Compare commits

...

199 Commits

Author SHA1 Message Date
Some Random Crypto Guy 861df8283c fixed 'make depends' building 2025-07-18 09:42:24 +01:00
akildemir c7a739d885 Merge pull request #25 from salvium/premine-scan
fixed premine scanning using Carrot
2025-07-16 11:29:01 +03:00
Some Random Crypto Guy 608962068a fixed premine scan 2025-07-15 22:04:47 +01:00
Some Random Crypto Guy e7615b4c08 Merge branch 'carrot-integration' of https://github.com/salvium/salvium into carrot-integration 2025-07-15 16:05:31 +01:00
Some Random Crypto Guy 5fa5ff8585 bumped RC version 2025-07-15 12:10:18 +01:00
akildemir 28a9338ab7 Merge pull request #24 from salvium/audit-spend-fix
fixed subaddress_map issues allowing construction of TX for spending AUDIT and STAKE post-Carrot-HF
2025-07-15 13:48:51 +03:00
Some Random Crypto Guy 1d3747e9cd fixed subaddress_map issues allowing construction of TX for spending AUDIT and STAKE post-Carrot-HF 2025-07-15 10:50:03 +01:00
akildemir c1c70ddace fix finding wrong change bug (#23) 2025-07-14 16:47:21 +01:00
akildemir 7ea690614e Merge pull request #22 from salvium/scanning-fixes
fix scanning for pre-Carrot STAKE + AUDIT TXs
2025-07-14 17:42:43 +03:00
Some Random Crypto Guy 2909fd2505 Fixed pre-scanning for AUDIT TXs 2025-07-14 14:25:28 +01:00
Some Random Crypto Guy 7dd5450419 Fixed pre-scanning for STAKE TXs 2025-07-14 13:18:54 +01:00
akildemir e3a03e9d98 add carrot subaddress derive type when inserting new subaddresses 2025-07-14 10:43:41 +03:00
akildemir dd39f94ca6 all pre-carrot txs work; Add support for return txs post-carrot. 2025-07-11 12:08:44 +03:00
akildemir 78232ff3a8 misc fixes 2025-07-07 16:55:26 +03:00
Some Random Crypto Guy aa02928620 fixed keys for pre+post Carrot mining rewards 2025-07-04 16:03:57 +01:00
Some Random Crypto Guy 52736ea0a3 bumped version number for RC 2025-07-04 10:01:32 +01:00
Some Random Crypto Guy b7cc8e8100 fixed Carrot mining; added support for dedicated Carrot wallet addresses with SC1 prefix 2025-07-03 23:08:24 +01:00
akildemir 520d4b4eab implement sparc return address functionality 2025-07-02 14:25:25 +03:00
Some Random Crypto Guy 11a296acde bumped version - rc2 2025-07-02 10:45:15 +01:00
Some Random Crypto Guy 8b1796147a fixed Carrot transfer functionality getting correct spendkeys 2025-07-01 23:12:13 +01:00
Some Random Crypto Guy bf9d0e837e fixed detection of received Carrot payments 2025-07-01 21:27:54 +01:00
Some Random Crypto Guy a9301f5573 migrated wallet_scanning tests to using carrot_and_legacy_account instead of keystore 2025-07-01 13:56:52 +01:00
Some Random Crypto Guy e6e644e600 migrated to using carrot_and_legacy_account instead of keystore 2025-07-01 13:33:53 +01:00
akildemir 130975cebd clean up previous commit 2025-06-29 15:38:14 +03:00
Some Random Crypto Guy 5306d4b94a interim checkin - working RCT Carrot TX 2025-06-27 16:05:08 +01:00
akildemir 14a6c6b8ef generate & scan correct carrot pubkeys 2025-06-24 13:19:42 +03:00
akildemir 2b31c8ad69 compile unit tests 2025-06-24 13:18:08 +03:00
akildemir ae02dcbe9a post-carrot tx scanning bug fixes 2025-06-20 11:40:46 +03:00
Some Random Crypto Guy e235199ff3 fixed crash due to accessing member based on HF version rather than TX version 2025-06-18 08:46:06 +01:00
akildemir 87ceee6c2d fully integrate carrot keys into wallet2 2025-06-17 15:40:12 +03:00
Some Random Crypto Guy 41e3e85159 fixed missing references to new RCT type RCTTypeSalviumOne 2025-06-17 13:27:02 +01:00
Some Random Crypto Guy b570a4938c added method to simple_wallet 2025-06-17 10:03:43 +01:00
Some Random Crypto Guy cbde471d91 fixed GCC issue with fields initialised in wrong order 2025-06-12 16:54:08 +01:00
Some Random Crypto Guy 1e81092e59 Merge branch 'carrot-integration' of https://github.com/salvium/salvium into carrot-integration 2025-06-12 14:35:39 +01:00
Some Random Crypto Guy 65db5c3782 fixed detection of return_payment outputs; fixed spending of return_payment outputs 2025-06-12 14:35:35 +01:00
akildemir a289de023a completed first successful tx from old keys to old keys post carrot fork 2025-06-12 16:32:16 +03:00
Some Random Crypto Guy cad1618bbd updated Carrot hard fork for testnet; merged treasury mint into Carrot HF 2025-06-09 11:33:57 +01:00
Some Random Crypto Guy d53a0669c9 fixed building issues; added carrot key initialisation to wallet2 2025-06-09 11:18:51 +01:00
Some Random Crypto Guy adcb66d268 fixed wallet API function usage 2025-06-05 14:13:19 +01:00
Some Random Crypto Guy ec2015e5a4 updated SA_Proof functions to use domain separator hash key 2025-06-05 13:40:29 +01:00
akildemir d163d6892c add missing change output parameters for genRctSimpleCarrot 2025-06-05 15:14:12 +03:00
Some Random Crypto Guy 8eac4bce5f fixed genRctSimpleCarrot params; added carrot_and_legacy_account to wallet2 2025-06-05 12:29:08 +01:00
Some Random Crypto Guy ea19899914 moved the Carrot account functionality from cryptonote_basic to carrot_impl 2025-06-04 15:21:07 +01:00
Some Random Crypto Guy 7fe9ee0990 updated RCT functions to support SalviumOne and T-CLSAG use 2025-06-04 13:05:20 +01:00
akildemir c8314f7b56 complete the parameter list for genRctSimple 2025-06-04 13:27:12 +03:00
Some Random Crypto Guy b07d2ce901 added Carrot keys to account_keys etc 2025-06-03 16:32:22 +01:00
akildemir 474bca538b initial post-carrot tx construction 2025-06-03 16:03:07 +03:00
Some Random Crypto Guy a958bdb0e0 fixed multisig issues from monero_c integration 2025-06-02 17:40:10 +01:00
Some Random Crypto Guy 1adc1b0f8d added full set of CLSAG tests for T-CLSAG 2025-05-29 19:07:12 +01:00
Some Random Crypto Guy f7be7b843b fixed scalarmultKey() calls in Carrot-specific CLSAG code; added new signing function for T terms in CLSAG 2025-05-29 14:12:54 +01:00
akildemir 3878e389fe initial clsag implementation for carrot 2025-05-28 17:50:16 +03:00
Some Random Crypto Guy a3a05a24e4 Merge branch 'carrot-integration' of https://github.com/salvium/salvium into carrot-integration 2025-05-21 12:58:49 +01:00
akildemir 1bf4f40b79 test compile 2025-05-21 14:33:58 +03:00
Some Random Crypto Guy 4a909bda2d merged construct_protocol_tx into latest carrot changes from Akil 2025-05-21 12:31:35 +01:00
jeffro256 6f6a17d923 carrot+fcmp: fix unit tests for payment_id_t type separation 2025-05-21 14:26:03 +03:00
jeffro256 d1b7abfc69 carrot_core: fix unit tests for payment_id_t type separation 2025-05-21 14:25:05 +03:00
Some Random Crypto Guy 33535531ec added Carrot support to construct_protocol_tx 2025-05-21 12:25:01 +01:00
jeffro256 bd34433be2 carrot_impl: refactor for encrypted_payment_id_t type separation 2025-05-21 14:23:00 +03:00
jeffro256 daffdd764b carrot_core: separate payment_id_t and encrypted_payment_id_t 2025-05-21 14:18:35 +03:00
jeffro256 4942f848f8 carrot_impl: fix sweep_all to create multiple txs with n_outputs *each* 2025-05-21 14:17:49 +03:00
jeffro256 fa3ecd03df carrot+fcmp: PR #32 j-berman review 1 2025-05-21 14:15:18 +03:00
akildemir 9c08143015 make last commit compile 2025-05-21 14:10:15 +03:00
jeffro256 9c2fa466a0 carrot+fcmp: carrot construction integration into wallet2 2025-05-21 14:05:27 +03:00
akildemir 4ee14df3c6 compile commits so far 2025-05-21 14:04:02 +03:00
jeffro256 c4ca673074 carrot+fcmp: changes to pending_tx for Carrot construction 2025-05-21 12:36:43 +03:00
jeffro256 724e2739d4 carrot_impl: document CarrotTransactionProposalV1 fields 2025-05-20 15:25:19 +03:00
j-berman d483b9872b jeffro comments 2025-05-20 15:15:30 +03:00
akildemir f2f21a49b1 make pre-carrot construction & carrot receiving work. 2025-05-20 14:47:13 +03:00
Some Random Crypto Guy 5f6122d045 fixed pre-Carrot TX construction; removed currently-extraneous RCTTypeFcmpPlusPlus 2025-05-20 10:06:33 +01:00
Some Random Crypto Guy 53db771c35 removed serialisation of unlock_time for txout_to_carrot_v1 output type 2025-05-19 12:17:00 +01:00
Some Random Crypto Guy 59b167f0f3 Merge branch 'sparc-integration' into carrot-integration 2025-05-19 11:50:08 +01:00
Some Random Crypto Guy 60b3f22376 fixed field ordering to allow GCC to compile; removed unlock_time field from CARROT/SPARC TX outputs 2025-05-19 11:13:01 +01:00
Some Random Crypto Guy e3e35f6dde Merge branch 'carrot-integration' into sparc-integration 2025-05-19 10:30:41 +01:00
Some Random Crypto Guy c145285dcf finished SPARC library prototype - anonymised return payments now pass unit tests; removed extraneous code 2025-05-16 21:06:20 +01:00
Some Random Crypto Guy 2550591cf1 added unit test for full verification of Anonymised Returns process from SPARC 2025-05-15 10:46:50 +01:00
akildemir 18ec1c331b make scanning pre-carrot and carrot coinbase txs work 2025-05-14 16:02:35 +03:00
Some Random Crypto Guy 0520fc4fe6 working make_return_tx() code 2025-05-14 12:04:50 +01:00
Some Random Crypto Guy f5729a6109 Merge branch 'sparc-integration' of https://github.com/salvium/salvium into sparc-integration 2025-05-14 11:22:23 +01:00
Some Random Crypto Guy da99d88bce initial import of SPARC integration into latest Carrot 2025-05-13 14:12:08 +01:00
Some Random Crypto Guy 87be655738 restored the missing params from generate_key_image_helper() calls; fixed linux build with missing vector include 2025-05-13 14:08:47 +01:00
akildemir 7a05f26810 tests compiles 2025-05-13 15:46:47 +03:00
Some Random Crypto Guy 96fff8abc3 initial import of SPARC integration into latest Carrot 2025-05-13 12:57:05 +01:00
jeffro256 f1e48c9094 carrot_impl: more documentation and rename CarrotPreSelectedInput 2025-05-13 13:57:28 +03:00
jeffro256 02e8fea2fe carrot_impl: file reorganization 2025-05-13 13:56:59 +03:00
jeffro256 266c41aeab carrot_impl: input selection overhaul
* Split subsetting into `form_preferred_input_candidate_subsets` fuction
* Use `get_input_counts_in_preferred_order` directly from `make_single_transfer_input_selector`
* Input selection policies are passed number of inputs to select for, instead of dynamic
* Filter dust before calling policies
* Throw more descriptive errors when not enough money (could still be improved for not enough input case)
* Replace multiprecision::int128_t with multiprecision::uint128_t for simplicity and cryptonote::print_money
* More documentation
* Remove "select two, prefer oldest" policy for now
2025-05-13 13:42:46 +03:00
jeffro256 410a9313dc carrot_core: better exception handling 2025-05-13 13:38:28 +03:00
jeffro256 870203e6fe carrot_impl: correct subaddr account index for change 2025-05-13 13:38:09 +03:00
jeffro256 8f699871d3 carrot_impl: sweep all below 2025-05-13 13:35:03 +03:00
jeffro256 650b6b8bd4 carrot_impl: classify 2-out dummys as change and make unique derivs for 2-selfsend 2-outs 2025-05-13 13:34:48 +03:00
jeffro256 19593db002 carrot_impl: fix 2-out selfsend sweep carving 2025-05-13 13:16:53 +03:00
jeffro256 d89d20ab0e carrot_impl: make_carrot_transaction_proposals_wallet2_sweep_all 2025-05-13 13:16:07 +03:00
jeffro256 b982d4c5a6 carrot_impl: make multiple sweep tx proposals at once 2025-05-13 13:13:06 +03:00
jeffro256 c4dbc4ea7b carrot_impl: remove device parameters from tx proposal making functions 2025-05-13 12:47:22 +03:00
jeffro256 29f9a4d679 carrot_impl: make multiple tx transfer proposals at once 2025-05-13 12:45:21 +03:00
jeffro256 8ad1727ea7 carrot_impl: merge make_carrot_transaction_proposal_v1_transfer functions 2025-05-12 16:11:54 +03:00
jeffro256 79429b6870 upstream: add vector erase if container helper 2025-05-12 16:10:56 +03:00
jeffro256 1057317147 mx25519: ARM machines select portable impl as default 2025-05-12 16:10:21 +03:00
jeffro256 c79e9d0e71 carrot_impl: integrate Carrot into construct_miner_tx() 2025-05-12 16:08:38 +03:00
jeffro256 d4a61dbb66 carrot_impl: make_single_enote_carrot_coinbase_transaction_v1 2025-05-12 15:58:30 +03:00
jeffro256 4989c4cece msys2 build: include int headers 2025-05-12 15:56:15 +03:00
jeffro256 818aac3c73 upstream: tighten definition of reverse_erase 2025-05-12 15:50:14 +03:00
akildemir 04208fdcd3 compile commits 2025-05-12 15:50:08 +03:00
jeffro256 673661bdb9 carrot_impl: real tx.extra size calculation 2025-05-12 15:46:56 +03:00
jeffro256 b7ea8d0b22 carrot_impl: store extra_nonce to coinbase txs 2025-05-12 15:46:16 +03:00
jeffro256 70bde9353b carrot_impl: rename make_pruned_transaction_from_proposal_v1 2025-05-12 15:45:06 +03:00
jeffro256 0bfa68beda carrot_impl: use get_output_enote_proposals directly to get payment proposal order 2025-05-12 15:41:27 +03:00
jeffro256 4a0242d905 carrot_impl: scan_key_image fails password cb once per batch and doesn't block other instances 2025-05-12 15:40:39 +03:00
jeffro256 1ee4d3d66e carrot_impl: propogate error::password_needed instead of generic exception when applicable 2025-05-12 15:39:59 +03:00
jeffro256 c72543bb63 carrot_impl: propogate exceptions in scan_key_image instead of swallowing them 2025-05-12 15:39:25 +03:00
jeffro256 ec46d14fe5 carrot_impl: more debug statements for input selection 2025-05-12 15:37:48 +03:00
akildemir fa0a05a01c compile commit so far 2025-05-12 15:36:39 +03:00
jeffro256 3cc4bb2b9a upstream: wallet2: get_subaddress_map_ref 2025-05-12 15:05:14 +03:00
jeffro256 89c60d57ec carrot+fcmp: tx_builder use select_greedy_aging 2025-05-12 15:04:54 +03:00
jeffro256 606cdd43f2 carrot_impl: trace stmt 2025-05-12 15:04:14 +03:00
jeffro256 1f3255e1e8 carrot_impl: unit tests for select_greedy_aging 2025-05-12 15:03:47 +03:00
jeffro256 8f4ceba0cc carrot_impl: new input selection policy: select_greedy_aging 2025-05-12 15:03:13 +03:00
jeffro256 88e104423f carrot_core: get_enote_ephemeral_privkey from normal payment proposal 2025-05-12 15:02:16 +03:00
jeffro256 09b886b324 carrot+fcmp: tx builder wallet2 overloads 2025-05-12 15:00:35 +03:00
jeffro256 4d891d61c1 carrot_core: output_set_finalization: get order on non-coinbase payment proposals 2025-05-12 14:45:21 +03:00
jeffro256 c4dc67cfc5 carrot_impl: use std::variant in scanning_tools 2025-05-12 14:43:45 +03:00
jeffro256 b7488f7735 upstream: remove serialization for tools::variant and add std::variant 2025-05-12 14:43:05 +03:00
jeffro256 871a687b52 carrot_impl: add serialization impl for more Carrot types 2025-05-12 14:38:20 +03:00
jeffro256 149a22bee1 carrot_core: refactor from tools:variant -> std::variant 2025-05-12 14:34:54 +03:00
jeffro256 9ca8361824 upstream: add serialiation for std::optional 2025-05-12 14:34:07 +03:00
jeffro256 29f9451b27 carrot_impl: silence aggr init warnings 2025-05-12 14:33:38 +03:00
jeffro256 f7c2cc6550 upstream: add serialization for mx25519_pubkey 2025-05-12 14:32:43 +03:00
jeffro256 3b671cb0bf upstream: container helper to remove using reverse iterator 2025-05-12 14:19:42 +03:00
jeffro256 e1d3086a24 carrot_impl: silence addr init warnings in carrot_impl.cpp 2025-05-12 14:19:16 +03:00
jeffro256 b259bc45e8 carrot_core: change in/out limits 2025-05-12 14:18:48 +03:00
jeffro256 638034c35f upstream: sync against wallet_keys_unlocker PR #9860 2025-05-12 14:17:53 +03:00
jeffro256 66552f14ca upstream: change upstream code for Boost serialization shims 2025-05-12 12:54:53 +03:00
jeffro256 7d4122151e upstream: Boost serialization vendoring for std C++17 types 2025-05-12 12:54:14 +03:00
jeffro256 4659b6b6f1 carrot_core: janus protection tests 2025-05-12 12:48:09 +03:00
jeffro256 b529887e88 carrot+fcmp: rework for Carrot scanning changes 2025-05-12 12:28:21 +03:00
jeffro256 c607f23a60 upstream: add is_out_to_acc_precomp span overload 2025-05-12 11:46:47 +03:00
jeffro256 3a21213153 carrot_impl: load enotes directly from transactions 2025-05-12 11:44:19 +03:00
jeffro256 2194b853d5 carrot_core: unbind K^j_v in d_e and overhaul scanning functions 2025-05-12 11:29:32 +03:00
jeffro256 ce9c845d26 carrot_impl: fix key image scanning in view-only and burned zombie outputs 2025-05-07 16:22:22 +03:00
jeffro256 fec249d598 tx_construction_helpers: improve RingCT input detection 2025-05-07 16:21:57 +03:00
jeffro256 ff85622450 carrot_impl: fix wallet scanning with cold signing and other input-less txs 2025-05-07 16:20:43 +03:00
jeffro256 a204844b8e carrot_impl: fix background sync key images 2025-05-07 16:20:01 +03:00
jeffro256 154806402b carrot_impl: fix payment ID scanning in scanning_tools 2025-05-07 16:19:31 +03:00
jeffro256 ee263314ef fix wallet2::check_tx_hey_helper
And fix compilation bug in wallet_tx_builder and reorganize unit tests.
2025-05-07 16:17:30 +03:00
jeffro256 cc83158ccc unit tests/crypto: test genesis output for torsion 2025-05-07 16:09:57 +03:00
jeffro256 ad1eb77d50 carrot_impl: input_count_for_max_usable_money fix 2025-05-07 16:07:26 +03:00
akildemir 316aacbc62 carrot_impl: format utils bug fixes with ephemeral count 2025-05-07 16:06:45 +03:00
jeffro256 d9846bba74 crypto: make ec_point comparable 2025-05-07 16:03:39 +03:00
jeffro256 c69c3e35fd carrot_impl: fix password fetching in wallet2::scan_key_image 2025-05-07 15:58:59 +03:00
jeffro256 13793610af carrot_impl: refactor fake_pruned_blockchain into header 2025-05-07 15:58:28 +03:00
jeffro256 4eecccee04 carrot_impl: add consensus rule for unique output pubkeys in tx
Required by Carrot to mitigate burning bugs, described in section 4.3 of the Carrot spec:
https://github.com/jeffro256/carrot/blob/master/carrot.md#43-transaction-model

Also remove 0-out check in `check_output_types()`, which I added in and technically constitutes a retroactive network split.

Co-authored-by: j-berman <justinberman@protonmail.com>
2025-05-07 15:53:44 +03:00
jeffro256 939fd068c5 carrot_impl: make_signable_tx_hash_from_carrot_transaction_proposal_v1 and bug fixes 2025-05-07 15:41:43 +03:00
jeffro256 4f33613628 carrot_impl: tx format utils load extra correctly 2025-05-07 15:38:49 +03:00
jeffro256 33aa5607a4 carrot_core: device documentation improvements 2025-05-07 15:37:52 +03:00
jeffro256 4eb13de957 upstream: is_coinbase() for prefix 2025-05-07 15:35:52 +03:00
j-berman ada1fa0e5e Fix debian build attempt 2025-05-07 15:34:32 +03:00
tobtoht 164985ce16 cmake: add mx25519 static define 2025-05-07 15:33:46 +03:00
akildemir a6733fcf2b carrot_impl compilation 2025-05-07 13:52:52 +03:00
jeffro256 44e70b4765 carrot_impl 3/27/25 [WIP] 2025-05-06 15:38:34 +03:00
akildemir 22cc08c107 carrot core completion 2025-05-06 13:52:34 +03:00
jeffro256 ed030081b7 carrot_core [3/27/2025] 2025-05-06 12:48:29 +03:00
akildemir 9292f88a57 Merge pull request #18 from salvium/update-hardcoded-sal-to-sa1
update hardcoded sal assets to sal1
2025-05-06 12:07:47 +03:00
akildemir 4095e9f80f Merge pull request #17 from salvium/add-sal1-treasury-mint
sal1 treasury mint
2025-05-06 12:01:47 +03:00
akildemir 02fd790788 Merge pull request #16 from salvium/fix-get-outs-money-amount-fn
only sum SAL1 in tx outs
2025-05-06 11:52:40 +03:00
akildemir 6d2960612f update hardcoded sal assets to sal1 2025-04-15 14:21:38 +03:00
akildemir 2d9b302137 sal1 treasury mint 2025-04-14 19:27:11 +03:00
akildemir a7753897b2 only sum SAL1 in tx outs 2025-04-02 16:58:05 +03:00
Some Random Crypto Guy fe3297271f added fast sync checkpoints; bumped RC version number 2025-04-02 10:47:34 +01:00
Some Random Crypto Guy 9d9471d314 fixed a couple of unit_test issues; bumped RC version 2025-04-01 12:18:56 +01:00
Some Random Crypto Guy 5da798a7c5 Merge remote-tracking branch 'origin/fix-all-unit-tests' into develop 2025-03-31 11:31:49 +01:00
akildemir 0f97ec9ea7 fix unit-tests 2025-03-31 10:49:39 +03:00
Some Random Crypto Guy 0418bfee30 fixed asio-related tests 2025-03-27 11:08:33 +00:00
Some Random Crypto Guy 488d5f1e9b updated fast sync checkpoints 2025-03-19 11:09:50 +00:00
Some Random Crypto Guy 771b8ea606 Merge branch 'rebase-v0.18' into develop 2025-03-19 09:43:51 +00:00
Some Random Crypto Guy 28262a30fe added unit tests for protocol_tx and check_output_types; bumped version to v0.9.6-rc1 2025-03-19 09:30:02 +00:00
Some Random Crypto Guy aa4276c39e Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-03-18 12:21:15 +00:00
Some Random Crypto Guy 1f49c178ee fixed build on Linux after background-sync patch applied and broke it! 2025-03-17 11:43:49 +00:00
j-berman f087b3807b wallet: background sync with just the view key
- When background syncing, the wallet wipes the spend key
from memory and processes all new transactions. The wallet saves
all receives, spends, and "plausible" spends of receives the
wallet does not know key images for.
- When background sync disabled, the wallet processes all
background synced txs and then clears the background sync cache.
- Adding "plausible" spends to the background sync cache ensures
that the wallet does not need to query the daemon to see if any
received outputs were spent while background sync was enabled.
This would harm privacy especially for users of 3rd party daemons.
- To enable the feature in the CLI wallet, the user can set
background-sync to reuse-wallet-password or
custom-background-password and the wallet automatically syncs in
the background when the wallet locks, then processes all
background synced txs when the wallet is unlocked.
- The custom-background-password option enables the user to
open a distinct background wallet that only has a view key saved
and can be opened/closed/synced separately from the main wallet.
When the main wallet opens, it processes the background wallet's
cache.
- To enable the feature in the RPC wallet, there is a new
`/setup_background_sync` endpoint.
- HW, multsig and view-only wallets cannot background sync.
2025-03-17 11:27:36 +00:00
Some Random Crypto Guy cb6cdac603 fixed sorting of yield_info output in CLI 2025-03-17 11:22:21 +00:00
akildemir 082976a3f0 Merge pull request #15 from salvium/fix-build
merge pr https://github.com/monero-project/monero/pull/9462
2025-03-17 10:40:18 +03:00
Some Random Crypto Guy 420824005e updates to allow building of all platforms using 'make depends' 2025-03-14 05:24:56 +00:00
Some Random Crypto Guy 98ded9b9c4 interim checkin of the rebased code (added files) 2025-03-13 17:58:18 +00:00
Some Random Crypto Guy 9baeb750ac interim checkin of the rebased code 2025-03-13 17:57:28 +00:00
akildemir 1cf81058b6 merge pr https://github.com/monero-project/monero/pull/9462 2025-03-12 15:34:51 +03:00
Some Random Crypto Guy 9570c7a910 Merge branch 'hotfix-v0.9.5a' 2025-03-02 22:26:32 +00:00
Some Random Crypto Guy 23756691b7 Hotfix release to address 2 wallet issues:
1. new wallet "set" parameter "send-change-back-to-subaddress [1|0]"
   The change for a transaction can now be sent back to the main address for the wallet (default)
   or to the sending subaddress
   (* please note that for AUDIT TX type, it will ALWAYS be sent back to the subaddress)

2. fixed erroneous code setting "source_asset" in wallet2::process_new_transaction()
   This bug caused change entries to appear in all TXs when querying using "get_transfer_by_txid"
   RPC method as "in" output types. Whilst this is fully conformant to the Monero Docs that
   describe the method in question, it did not conform to the experience of the RPC method users
   or to traditional Monero RPC output.
2025-03-02 22:15:50 +00:00
Some Random Crypto Guy d5f6cdc0cd tweaked build of audit tool; added asset_type to destinations in RPC get_* calls 2025-02-28 11:51:35 +00:00
Some Random Crypto Guy 378ec75a6e Merge branch 'main' into develop 2025-02-26 13:16:27 +00:00
Some Random Crypto Guy 3eb986fc51 fixed debug build without audit tool 2025-02-25 15:54:50 +00:00
Some Random Crypto Guy b713a08a81 bumped version, ready for fork; fixed up wallet API method for auditing 2025-02-25 13:04:29 +00:00
Some Random Crypto Guy 5bd079af4a updated HF9 block height 2025-02-24 11:50:08 +00:00
Some Random Crypto Guy cd2c7c939c added audit2 as phase 3 2025-02-24 10:57:18 +00:00
Some Random Crypto Guy 2f707e30c6 bumped version number in preparation for hardforks 2025-02-24 10:53:32 +00:00
Some Random Crypto Guy f9726354b8 Merge branch 'develop' 2025-02-24 10:52:28 +00:00
Some Random Crypto Guy fcd78eea7e merged SAL1 support into the wallet API functions 2025-02-24 10:40:13 +00:00
akildemir db740fa037 make a separate tx per subaddress 2025-02-24 13:30:31 +03:00
Some Random Crypto Guy 5971f39e7a added 2nd phase of audit; bumped version 2025-02-19 17:49:38 +00:00
akildemir 02f2eb5ee9 add SAL1 support to wallet api 2025-02-18 12:05:12 +03:00
Some Random Crypto Guy 02ef77bbcc Merge branch 'main' into develop 2025-02-17 23:48:42 +00:00
Some Random Crypto Guy aa64124c28 fix for supply_info command with SAL supply going negative 2025-02-14 13:28:22 +00:00
314 changed files with 34971 additions and 4678 deletions
+24 -8
View File
@@ -372,6 +372,7 @@ if(NOT MANUAL_SUBMODULES)
check_submodule(external/trezor-common)
check_submodule(external/randomx)
check_submodule(external/supercop)
check_submodule(external/mx25519)
endif()
endif()
@@ -458,7 +459,7 @@ elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
set(BSDI TRUE)
endif()
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include)
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/mx25519/include)
if(APPLE)
cmake_policy(SET CMP0042 NEW)
@@ -506,6 +507,7 @@ if(STATIC)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZMQ_STATIC")
add_definitions("-DMX25519_STATIC")
endif()
option(SANITIZE "Use ASAN memory sanitizer" OFF)
@@ -531,14 +533,14 @@ add_definitions("-DBLOCKCHAIN_DB=${BLOCKCHAIN_DB}")
# Can't install hook in static build on OSX, because OSX linker does not support --wrap
# On ARM, having libunwind package (with .so's only) installed breaks static link.
# When possible, avoid stack tracing using libunwind in favor of using easylogging++.
if (APPLE OR NETBSD)
if (APPLE)
set(DEFAULT_STACK_TRACE OFF)
set(LIBUNWIND_LIBRARIES "")
elseif (DEPENDS AND NOT LINUX)
set(DEFAULT_STACK_TRACE OFF)
set(LIBUNWIND_LIBRARIES "")
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT MINGW)
set(DEFAULT_STACK_TRACE OFF)
set(DEFAULT_STACK_TRACE ON)
set(STACK_TRACE_LIB "easylogging++") # for diag output only
set(LIBUNWIND_LIBRARIES "")
elseif (ARM)
@@ -547,7 +549,7 @@ elseif (ARM)
else()
find_package(Libunwind)
if(LIBUNWIND_FOUND)
set(DEFAULT_STACK_TRACE OFF)
set(DEFAULT_STACK_TRACE ON)
set(STACK_TRACE_LIB "libunwind") # for diag output only
else()
set(DEFAULT_STACK_TRACE OFF)
@@ -1084,6 +1086,10 @@ endif()
find_package(Boost 1.58 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS})
add_definitions(-DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION)
add_definitions(-DBOOST_NO_AUTO_PTR)
add_definitions(-DBOOST_UUID_DISABLE_ALIGNMENT) # This restores UUID's std::has_unique_object_representations property
# Boost has two conflicting save/load impls for `std::variant`, one in serialization/variant.hpp,
# and one in serialization/std_variant.hpp. This macro disables the one in variant.hpp.
add_definitions(-DBOOST_NO_CXX17_HDR_VARIANT)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES})
if(NOT Boost_FOUND)
@@ -1107,7 +1113,9 @@ include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
if(MINGW)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj")
set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt)
if(NOT DEPENDS)
if(DEPENDS)
set(ICU_LIBRARIES icuio icui18n icuuc icudata icutu iconv)
else()
set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv)
endif()
elseif(APPLE OR OPENBSD OR ANDROID)
@@ -1182,7 +1190,6 @@ endif()
if(STATIC)
set(sodium_USE_STATIC_LIBS ON)
endif()
find_package(Sodium REQUIRED)
find_path(ZMQ_INCLUDE_PATH zmq.h)
find_library(ZMQ_LIB zmq)
@@ -1190,6 +1197,7 @@ find_library(PGM_LIBRARY pgm)
find_library(NORM_LIBRARY norm)
find_library(GSSAPI_LIBRARY gssapi_krb5)
find_library(PROTOLIB_LIBRARY protolib)
find_library(SODIUM_LIBRARY sodium)
find_library(BSD_LIBRARY bsd)
find_library(MD_LIBRARY md)
find_library(PROTOKIT_LIBRARY protokit)
@@ -1212,8 +1220,16 @@ endif()
if(PROTOLIB_LIBRARY)
set(ZMQ_LIB "${ZMQ_LIB};${PROTOLIB_LIBRARY}")
endif()
if(Sodium_FOUND)
set(ZMQ_LIB "${ZMQ_LIB};${sodium_LIBRARIES}")
if(SODIUM_LIBRARY)
message(STATUS "ZMQ_LIB: ${ZMQ_LIB};${SODIUM_LIBRARY}")
set(ZMQ_LIB "${ZMQ_LIB};${SODIUM_LIBRARY}")
find_path(SODIUM_INCLUDE_PATH sodium/crypto_verify_32.h)
if (SODIUM_INCLUDE_PATH)
message(STATUS "SODIUM_INCLUDE_PATH: ${SODIUM_INCLUDE_PATH}")
include_directories(${SODIUM_INCLUDE_PATH})
else()
message(FATAL_ERROR "Could not find required sodium/crypto_verify_32.h")
endif()
endif()
if(BSD_LIBRARY)
set(ZMQ_LIB "${ZMQ_LIB};${BSD_LIBRARY}")
+5 -5
View File
@@ -1,4 +1,4 @@
# Salvium Zero v0.9.2
# Salvium Zero v0.9.9
Copyright (c) 2023-2024, Salvium
Portions Copyright (c) 2014-2023, The Monero Project
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
```bash
cd salvium
git checkout v0.9.2
git checkout v0.9.9
make
```
@@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```bash
git clone https://github.com/salvium/salvium
cd salvium
git checkout v0.9.2
git checkout v0.9.9
```
* Build:
@@ -370,10 +370,10 @@ application.
cd salvium
```
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v0.9.2'. 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. 'v0.9.9'. If you don't care about the version and just want binaries from master, skip this step:
```bash
git checkout v0.9.2
git checkout v0.9.9
```
* If you are on a 64-bit system, run:
+66
View File
@@ -0,0 +1,66 @@
depends_prefix="`dirname ${ac_site_file}`/.."
cross_compiling=maybe
host_alias=@HOST@
ac_tool_prefix=${host_alias}-
if test -z $with_boost; then
with_boost=$depends_prefix
fi
if test x@host_os@ = xdarwin; then
BREW=no
PORT=no
fi
PATH=$depends_prefix/native/bin:$PATH
PKG_CONFIG="`which pkg-config` --static"
# These two need to remain exported because pkg-config does not see them
# otherwise. That means they must be unexported at the end of configure.ac to
# avoid ruining the cache. Sigh.
export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig:$depends_prefix/lib/pkgconfig
if test -z "@allow_host_packages@"; then
export PKGCONFIG_LIBDIR=
fi
CPPFLAGS="-I$depends_prefix/include/ $CPPFLAGS"
LDFLAGS="-L$depends_prefix/lib $LDFLAGS"
CC="@CC@"
CXX="@CXX@"
OBJC="${CC}"
CCACHE=$depends_prefix/native/bin/ccache
PYTHONPATH=$depends_prefix/native/lib/python/dist-packages:$PYTHONPATH
if test -n "@AR@"; then
AR=@AR@
ac_cv_path_ac_pt_AR=${AR}
fi
if test -n "@RANLIB@"; then
RANLIB=@RANLIB@
ac_cv_path_ac_pt_RANLIB=${RANLIB}
fi
if test -n "@NM@"; then
NM=@NM@
ac_cv_path_ac_pt_NM=${NM}
fi
if test -n "@debug@"; then
enable_reduce_exports=no
fi
if test -n "@CFLAGS@"; then
CFLAGS="@CFLAGS@ $CFLAGS"
fi
if test -n "@CXXFLAGS@"; then
CXXFLAGS="@CXXFLAGS@ $CXXFLAGS"
fi
if test -n "@CPPFLAGS@"; then
CPPFLAGS="@CPPFLAGS@ $CPPFLAGS"
fi
if test -n "@LDFLAGS@"; then
LDFLAGS="@LDFLAGS@ $LDFLAGS"
fi
+1 -1
View File
@@ -1,4 +1,4 @@
OSX_MIN_VERSION=10.13
OSX_MIN_VERSION=11.0
LD64_VERSION=609
ifeq (aarch64, $(host_arch))
CC_target=arm64-apple-$(host_os)
+1 -1
View File
@@ -1,4 +1,4 @@
mingw32_CFLAGS=-pipe -pthread
mingw32_CFLAGS=-pipe
mingw32_CXXFLAGS=$(mingw32_CFLAGS)
mingw32_ARFLAGS=cr
+11 -13
View File
@@ -1,18 +1,17 @@
package=boost
$(package)_version=1_64_0
$(package)_download_path=https://downloads.sourceforge.net/project/boost/boost/1.64.0/
$(package)_file_name=$(package)_$($(package)_version).tar.bz2
$(package)_sha256_hash=7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332
$(package)_patches=fix_aroptions.patch fix_arm_arch.patch
package=boost
$(package)_version=1.84.0
$(package)_download_path=https://archives.boost.io/release/$($(package)_version)/source/
$(package)_file_name=$(package)_$(subst .,_,$($(package)_version)).tar.bz2
$(package)_sha256_hash=cc4b893acf645c9d4b698e9a0f08ca8846aa5d6c68275c14c3e7949c24109454
define $(package)_set_vars
$(package)_config_opts_release=variant=release
$(package)_config_opts_debug=variant=debug
$(package)_config_opts=--layout=tagged --build-type=complete --user-config=user-config.jam
$(package)_config_opts+=--layout=system --user-config=user-config.jam variant=release
$(package)_config_opts+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1
$(package)_config_opts_linux=threadapi=pthread runtime-link=shared
$(package)_config_opts_android=threadapi=pthread runtime-link=static target-os=android
$(package)_config_opts_darwin=--toolset=darwin runtime-link=shared
$(package)_config_opts_darwin=--toolset=darwin runtime-link=shared target-os=darwin
$(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static
$(package)_config_opts_x86_64_mingw32=address-model=64
$(package)_config_opts_i686_mingw32=address-model=32
@@ -23,14 +22,13 @@ $(package)_toolset_darwin=darwin
$(package)_archiver_darwin=$($(package)_libtool)
$(package)_config_libraries_$(host_os)="chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization"
$(package)_config_libraries_mingw32="chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale"
$(package)_cxxflags=-std=c++11
$(package)_cxxflags_linux=-fPIC
$(package)_cxxflags_freebsd=-fPIC
$(package)_cxxflags=-std=c++17
$(package)_cxxflags_linux+=-fPIC
$(package)_cxxflags_freebsd+=-fPIC
$(package)_cxxflags_darwin+=-ffile-prefix-map=$($(package)_extract_dir)=/usr
endef
define $(package)_preprocess_cmds
patch -p1 < $($(package)_patch_dir)/fix_aroptions.patch &&\
patch -p1 < $($(package)_patch_dir)/fix_arm_arch.patch &&\
echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : <cxxflags>\"$($(package)_cxxflags) $($(package)_cppflags)\" <linkflags>\"$($(package)_ldflags)\" <archiver>\"$(boost_archiver_$(host_os))\" <arflags>\"$($(package)_arflags)\" <striper>\"$(host_STRIP)\" <ranlib>\"$(host_RANLIB)\" <rc>\"$(host_WINDRES)\" : ;" > user-config.jam
endef
+27
View File
@@ -0,0 +1,27 @@
package=icu4c
$(package)_version=55.2
$(package)_download_path=https://github.com/unicode-org/icu/releases/download/release-55-2/
$(package)_file_name=$(package)-55_2-src.tgz
$(package)_sha256_hash=eda2aa9f9c787748a2e2d310590720ca8bcc6252adf6b4cfb03b65bef9d66759
$(package)_patches=icu-001-dont-build-static-dynamic-twice.patch
define $(package)_set_vars
$(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -DU_USING_ICU_NAMESPACE=0 -DU_STATIC_IMPLEMENTATION -DU_COMBINED_IMPLEMENTATION -fPIC -DENABLE_STATIC=YES -DPGKDATA_MODE=static"
endef
define $(package)_config_cmds
patch -p1 < $($(package)_patch_dir)/icu-001-dont-build-static-dynamic-twice.patch &&\
mkdir builda &&\
mkdir buildb &&\
cd builda &&\
sh ../source/runConfigureICU Linux &&\
make &&\
cd ../buildb &&\
sh ../source/runConfigureICU MinGW --enable-static=yes --disable-shared --disable-layout --disable-layoutex --disable-tests --disable-samples --prefix=$(host_prefix) --with-cross-build=`pwd`/../builda &&\
$(MAKE) $($(package)_build_opts)
endef
define $(package)_stage_cmds
cd buildb &&\
$(MAKE) $($(package)_build_opts) DESTDIR=$($(package)_staging_dir) install lib/*
endef
+35
View File
@@ -0,0 +1,35 @@
package=libiconv
$(package)_version=1.15
$(package)_download_path=https://ftp.gnu.org/gnu/libiconv
$(package)_file_name=libiconv-$($(package)_version).tar.gz
$(package)_sha256_hash=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
$(package)_patches=fix-whitespace.patch
define $(package)_set_vars
$(package)_config_opts=--disable-nls
$(package)_config_opts=--enable-static
$(package)_config_opts=--disable-shared
$(package)_config_opts_linux=--with-pic
$(package)_config_opts_freebsd=--with-pic
endef
define $(package)_preprocess_cmds
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux/ &&\
patch -p1 < $($(package)_patch_dir)/fix-whitespace.patch
endef
define $(package)_config_cmds
$($(package)_autoconf) AR_FLAGS=$($(package)_arflags)
endef
define $(package)_build_cmds
$(MAKE)
endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
define $(package)_postprocess_cmds
rm lib/*.la
endef
+25
View File
@@ -0,0 +1,25 @@
package=native_ccache
$(package)_version=3.3.4
$(package)_download_path=https://samba.org/ftp/ccache
$(package)_file_name=ccache-$($(package)_version).tar.bz2
$(package)_sha256_hash=fa9d7f38367431bc86b19ad107d709ca7ecf1574fdacca01698bdf0a47cd8567
define $(package)_set_vars
$(package)_config_opts=
endef
define $(package)_config_cmds
$($(package)_autoconf)
endef
define $(package)_build_cmds
$(MAKE)
endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
define $(package)_postprocess_cmds
rm -rf lib include
endef
+4 -3
View File
@@ -5,17 +5,18 @@ $(package)_download_file=$($(package)_version).tar.gz
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=70a7189418c2086d20c299c5d59250cf5940782c778892ccc899c66516ed240e
$(package)_build_subdir=cctools
$(package)_dependencies=native_clang native_libtapi
$(package)_patches=no-build-date.patch
$(package)_dependencies=native_libtapi
define $(package)_set_vars
$(package)_config_opts=--target=$(host) --disable-lto-support --with-libtapi=$(host_prefix)
$(package)_ldflags+=-Wl,-rpath=\\$$$$$$$$\$$$$$$$$ORIGIN/../lib
$(package)_cc=$(host_prefix)/native/bin/clang
$(package)_cxx=$(host_prefix)/native/bin/clang++
$(package)_cc=$(clang_prog)
$(package)_cxx=$(clangxx_prog)
endef
define $(package)_preprocess_cmds
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub cctools && \
patch -p1 < $($(package)_patch_dir)/no-build-date.patch
endef
+11 -5
View File
@@ -1,9 +1,15 @@
package=native_clang
$(package)_version=9.0.0
$(package)_download_path=https://releases.llvm.org/$($(package)_version)
$(package)_download_file=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
$(package)_file_name=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
$(package)_sha256_hash=a23b082b30c128c9831dbdd96edad26b43f56624d0ad0ea9edec506f5385038d
#$(package)_version=9.0.0
#$(package)_download_path=https://releases.llvm.org/$($(package)_version)
#$(package)_download_file=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
#$(package)_file_name=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
#$(package)_sha256_hash=a23b082b30c128c9831dbdd96edad26b43f56624d0ad0ea9edec506f5385038d
$(package)_version=12.0.0
$(package)_download_path=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version)
$(package)_download_file=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-20.04.tar.xz
$(package)_file_name=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-20.04.tar.xz
$(package)_sha256_hash=a9ff205eb0b73ca7c86afc6432eed1c2d49133bd0d49e47b15be59bbf0dd292e
define $(package)_extract_cmds
echo $($(package)_sha256_hash) $($(package)_source) | sha256sum -c &&\
+2 -16
View File
@@ -4,30 +4,16 @@ $(package)_download_path=https://github.com/tpoechtrager/apple-libtapi/archive
$(package)_download_file=$($(package)_version).tar.gz
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=62e419c12d1c9fad67cc1cd523132bc00db050998337c734c15bc8d73cc02b61
$(package)_build_subdir=build
$(package)_dependencies=native_clang
$(package)_patches=no_embed_git_rev.patch
define $(package)_preprocess_cmds
patch -p1 -i $($(package)_patch_dir)/no_embed_git_rev.patch
endef
define $(package)_config_cmds
echo -n $(build_prefix) > INSTALLPREFIX; \
CC=$(host_prefix)/native/bin/clang CXX=$(host_prefix)/native/bin/clang++ \
cmake -DCMAKE_INSTALL_PREFIX=$(build_prefix) \
-DLLVM_INCLUDE_TESTS=OFF \
-DCMAKE_BUILD_TYPE=RELEASE \
-DTAPI_REPOSITORY_STRING="1100.0.11" \
-DTAPI_FULL_VERSION="11.0.0" \
-DCMAKE_CXX_FLAGS="-I $($(package)_extract_dir)/src/llvm/projects/clang/include -I $($(package)_build_dir)/projects/clang/include" \
$($(package)_extract_dir)/src/llvm
endef
define $(package)_build_cmds
$(MAKE) clangBasic && $(MAKE) libtapi
CC=$(clang_prog) CXX=$(clangxx_prog) INSTALLPREFIX=$($(package)_staging_prefix_dir) ./build.sh
endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install-libtapi install-tapi-headers
./install.sh
endef
+1
View File
@@ -46,6 +46,7 @@ define $(package)_set_vars
endef
define $(package)_preprocess_cmds
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub . && \
cp $($(package)_patch_dir)/fallback.c ncurses
endef
+28 -22
View File
@@ -1,28 +1,34 @@
packages:=boost openssl zeromq expat unbound sodium
native_packages := native_protobuf
packages := boost openssl zeromq unbound sodium protobuf
hardware_packages := hidapi protobuf libusb
hardware_native_packages := native_protobuf
android_native_packages = android_ndk $(hardware_native_packages)
android_packages = ncurses readline protobuf
darwin_native_packages = $(hardware_native_packages)
darwin_packages = ncurses readline $(hardware_packages)
# not really native...
freebsd_native_packages = freebsd_base $(hardware_native_packages)
freebsd_packages = ncurses readline protobuf libusb
linux_packages = eudev ncurses readline $(hardware_packages)
linux_native_packages = $(hardware_native_packages)
ifeq ($(build_tests),ON)
packages += gtest
ifneq ($(host_os),android)
packages += libusb
endif
mingw32_packages = $(hardware_packages)
mingw32_native_packages = $(hardware_native_packages)
ifneq ($(host_os),freebsd)
ifneq ($(host_os),android)
packages += hidapi
endif
endif
ifneq ($(host_os),mingw32)
packages += ncurses readline
endif
mingw32_native_packages :=
mingw32_packages = icu4c libiconv
linux_native_packages :=
linux_packages :=
freebsd_native_packages := freebsd_base
freebsd_packages :=
ifneq ($(build_os),darwin)
darwin_native_packages += darwin_sdk native_clang native_cctools native_libtapi
darwin_native_packages := darwin_sdk native_clang native_cctools native_libtapi
endif
darwin_packages :=
android_native_packages := android_ndk
android_packages :=
+13 -9
View File
@@ -1,26 +1,26 @@
package=unbound
$(package)_version=1.19.1
$(package)_version=1.22.0
$(package)_download_path=https://www.nlnetlabs.nl/downloads/$(package)/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=bc1d576f3dd846a0739adc41ffaa702404c6767d2b6082deb9f2f97cbb24a3a9
$(package)_dependencies=openssl expat
$(package)_patches=disable-glibc-reallocarray.patch
$(package)_sha256_hash=c5dd1bdef5d5685b2cedb749158dd152c52d44f65529a34ac15cd88d4b1b3d43
$(package)_dependencies=openssl
$(package)_patches=no-expat.patch
define $(package)_set_vars
$(package)_config_opts=--disable-shared --enable-static --without-pyunbound --prefix=$(host_prefix)
$(package)_config_opts+=--with-libexpat=$(host_prefix) --with-ssl=$(host_prefix) --with-libevent=no
$(package)_config_opts+=--with-libexpat=no --with-ssl=$(host_prefix) --with-libevent=no
$(package)_config_opts+=--without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only
$(package)_config_opts_linux=--with-pic
$(package)_config_opts_w64=--enable-static-exe --sysconfdir=/etc --prefix=$(host_prefix) --target=$(host_prefix)
$(package)_config_opts_x86_64_darwin=ac_cv_func_SHA384_Init=yes
$(package)_build_opts_mingw32=LDFLAGS="$($(package)_ldflags) -lpthread"
$(package)_cflags_mingw32+="-D_WIN32_WINNT=0x600"
endef
# Remove blobs
define $(package)_preprocess_cmds
patch -p1 < $($(package)_patch_dir)/disable-glibc-reallocarray.patch &&\
autoconf
patch -p1 < $($(package)_patch_dir)/no-expat.patch &&\
rm configure~ doc/*.odp doc/*.pdf contrib/*.tar.gz contrib/*.tar.bz2 &&\
rm -rf testdata dnscrypt/testdata
endef
define $(package)_config_cmds
@@ -34,3 +34,7 @@ endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
define $(package)_postprocess_cmds
rm -rf share
endef
+29
View File
@@ -0,0 +1,29 @@
package=unwind
$(package)_version=1.5.0
$(package)_download_path=https://download.savannah.nongnu.org/releases/libunwind
$(package)_file_name=lib$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=90337653d92d4a13de590781371c604f9031cdb50520366aa1e3a91e1efb1017
$(package)_patches=fix_obj_order.patch
define $(package)_preprocess_cmds
patch -p0 < $($(package)_patch_dir)/fix_obj_order.patch
endef
define $(package)_config_cmds
cp -f $(BASEDIR)/config.guess config/config.guess &&\
cp -f $(BASEDIR)/config.sub config/config.sub &&\
$($(package)_autoconf) --disable-shared --enable-static --disable-tests --disable-documentation AR_FLAGS=$($(package)_arflags)
endef
define $(package)_build_cmds
$(MAKE)
endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
define $(package)_postprocess_cmds
rm lib/*.la
endef
@@ -1,11 +0,0 @@
--- boost_1_64_0/tools/build/src/tools/darwin.jam.O 2017-04-17 03:22:26.000000000 +0100
+++ boost_1_64_0/tools/build/src/tools/darwin.jam 2022-05-04 17:26:29.984464447 +0000
@@ -505,7 +505,7 @@
if $(instruction-set) {
options = -arch$(_)$(instruction-set) ;
} else {
- options = -arch arm ;
+# options = -arch arm ;
}
}
}
@@ -1,28 +0,0 @@
--- boost_1_64_0/tools/build/src/tools/gcc.jam.O 2017-04-17 03:22:26.000000000 +0100
+++ boost_1_64_0/tools/build/src/tools/gcc.jam 2019-11-15 15:46:16.957937137 +0000
@@ -243,6 +243,8 @@
{
ECHO notice: using gcc archiver :: $(condition) :: $(archiver[1]) ;
}
+ local arflags = [ feature.get-values <arflags> : $(options) ] ;
+ toolset.flags gcc.archive .ARFLAGS $(condition) : $(arflags) ;
# - Ranlib.
local ranlib = [ common.get-invocation-command gcc
@@ -970,6 +972,7 @@
# logic in intel-linux, but that is hardly worth the trouble as on Linux, 'ar'
# is always available.
.AR = ar ;
+.ARFLAGS = rc ;
.RANLIB = ranlib ;
toolset.flags gcc.archive AROPTIONS <archiveflags> ;
@@ -1011,7 +1014,7 @@
#
actions piecemeal archive
{
- "$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)"
+ "$(.AR)" $(AROPTIONS) $(.ARFLAGS) "$(<)" "$(>)"
"$(.RANLIB)" "$(<)"
}
@@ -0,0 +1,37 @@
Don't build object files twice
When passed --enable-static and --enable-shared, icu will generate
both a shared and a static version of its libraries.
However, in order to do so, it builds each and every object file
twice: once with -fPIC (for the shared library), and once without
-fPIC (for the static library). While admittedly building -fPIC for a
static library generates a slightly suboptimal code, this is what all
the autotools-based project are doing. They build each object file
once, and they use it for both the static and shared libraries.
icu builds the object files for the shared library as .o files, and
the object files for static library as .ao files. By simply changing
the suffix of object files used for static libraries to ".o", we tell
icu to use the ones built for the shared library (i.e, with -fPIC),
and avoid the double build of icu.
On a fast build server, this brings the target icu build from
3m41.302s down to 1m43.926s (approximate numbers: some other builds
are running on the system at the same time).
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Index: b/source/config/mh-linux
===================================================================
--- a/source/config/mh-linux
+++ b/source/config/mh-linux
@@ -38,7 +38,7 @@
## Shared object suffix
SO = so
## Non-shared intermediate object suffix
-STATIC_O = ao
+STATIC_O = o
## Compilation rules
%.$(STATIC_O): $(srcdir)/%.c
@@ -0,0 +1,13 @@
diff --git a/preload/configure b/preload/configure
index aab5c77..e20b8f0 100755
--- a/preload/configure
+++ b/preload/configure
@@ -588,7 +588,7 @@ MAKEFLAGS=
PACKAGE_NAME='libiconv'
PACKAGE_TARNAME='libiconv'
PACKAGE_VERSION='0'
-PACKAGE_STRING='libiconv 0'
+PACKAGE_STRING='libiconv0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -1,14 +0,0 @@
diff --git a/configure.ac b/configure.ac
index 5c7da197..e2b25288 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1702,6 +1702,9 @@ AC_LINK_IFELSE([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT
#ifndef _OPENBSD_SOURCE
#define _OPENBSD_SOURCE 1
#endif
+#ifdef __linux__
+# error reallocarray() is currently disabled on Linux to support glibc < 2.26
+#endif
#include <stdlib.h>
int main(void) {
void* p = reallocarray(NULL, 10, 100);
@@ -0,0 +1,20 @@
diff --git a/configure b/configure
index a41e3e1e..7d6a58f0 100755
--- a/configure
+++ b/configure
@@ -22053,6 +22053,7 @@ else $as_nop
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
+if test x_$withval = x_yes -o x_$withval != x_no; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libexpat" >&5
printf %s "checking for libexpat... " >&6; }
found_libexpat="no"
@@ -22090,6 +22091,7 @@ else $as_nop
ac_have_decl=0
fi
printf "%s\n" "#define HAVE_DECL_XML_STOPPARSER $ac_have_decl" >>confdefs.h
+fi
# hiredis (redis C client for cachedb)
@@ -0,0 +1,11 @@
--- config/ltmain.sh.0 2020-11-10 17:25:26.000000000 +0100
+++ config/ltmain.sh 2021-09-11 19:39:36.000000000 +0200
@@ -10768,6 +10768,8 @@
fi
func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
tool_oldlib=$func_to_tool_file_result
+ oldobjs=`for obj in $oldobjs; do echo $obj; done | sort`
+ oldobjs=" `echo $oldobjs`"
eval cmds=\"$old_archive_cmds\"
func_len " $cmds"
+1 -1
View File
@@ -91,7 +91,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
SET(BREW OFF)
SET(PORT OFF)
SET(CMAKE_OSX_SYSROOT "@prefix@/native/SDK/")
SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
SET(CMAKE_OSX_DEPLOYMENT_TARGET "11.0")
SET(CMAKE_CXX_STANDARD 17)
SET(LLVM_ENABLE_PIC OFF)
SET(LLVM_ENABLE_PIE OFF)
+2
View File
@@ -74,6 +74,7 @@ namespace epee
public:
using char_type = std::uint8_t;
using Ch = char_type;
using value_type = char_type;
//! Increase internal buffer by at least `byte_stream_increase` bytes.
byte_stream() noexcept
@@ -86,6 +87,7 @@ namespace epee
~byte_stream() noexcept = default;
byte_stream& operator=(byte_stream&& rhs) noexcept;
std::uint8_t* data() noexcept { return buffer_.get(); }
const std::uint8_t* data() const noexcept { return buffer_.get(); }
std::uint8_t* tellp() const noexcept { return next_write_; }
std::size_t available() const noexcept { return end_ - next_write_; }
+42 -22
View File
@@ -47,6 +47,7 @@
#include <condition_variable>
#include <boost/asio.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/steady_timer.hpp>
@@ -64,6 +65,7 @@
#define MONERO_DEFAULT_LOG_CATEGORY "net"
#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 1000
#define ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT 100 * 1024 * 1024
namespace epee
{
@@ -76,6 +78,13 @@ namespace net_utils
protected:
virtual ~i_connection_filter(){}
};
struct i_connection_limit
{
virtual bool is_host_limit(const epee::net_utils::network_address &address)=0;
protected:
virtual ~i_connection_limit(){}
};
/************************************************************************/
@@ -100,8 +109,8 @@ namespace net_utils
using ec_t = boost::system::error_code;
using handshake_t = boost::asio::ssl::stream_base::handshake_type;
using io_context_t = boost::asio::io_service;
using strand_t = boost::asio::io_service::strand;
using io_context_t = boost::asio::io_context;
using strand_t = io_context_t::strand;
using socket_t = boost::asio::ip::tcp::socket;
using network_throttle_t = epee::net_utils::network_throttle;
@@ -162,6 +171,7 @@ namespace net_utils
} read;
struct {
std::deque<epee::byte_slice> queue;
std::size_t total_bytes;
bool wait_consume;
} write;
};
@@ -260,20 +270,28 @@ namespace net_utils
struct shared_state : connection_basic_shared_state, t_protocol_handler::config_type
{
shared_state()
: connection_basic_shared_state(), t_protocol_handler::config_type(), pfilter(nullptr), stop_signal_sent(false)
: connection_basic_shared_state(),
t_protocol_handler::config_type(),
pfilter(nullptr),
plimit(nullptr),
response_soft_limit(ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT),
stop_signal_sent(false)
{}
i_connection_filter* pfilter;
i_connection_limit* plimit;
std::size_t response_soft_limit;
bool stop_signal_sent;
};
/// Construct a connection with the given io_service.
explicit connection( boost::asio::io_service& io_service,
/// Construct a connection with the given io_context.
explicit connection( io_context_t& io_context,
std::shared_ptr<shared_state> state,
t_connection_type connection_type,
epee::net_utils::ssl_support_t ssl_support);
explicit connection( boost::asio::ip::tcp::socket&& sock,
explicit connection( io_context_t& io_context,
boost::asio::ip::tcp::socket&& sock,
std::shared_ptr<shared_state> state,
t_connection_type connection_type,
epee::net_utils::ssl_support_t ssl_support);
@@ -306,7 +324,7 @@ namespace net_utils
virtual bool close();
virtual bool call_run_once_service_io();
virtual bool request_callback();
virtual boost::asio::io_service& get_io_service();
virtual io_context_t& get_io_context();
virtual bool add_ref();
virtual bool release();
//------------------------------------------------------
@@ -336,7 +354,7 @@ namespace net_utils
/// serve up files from the given directory.
boosted_tcp_server(t_connection_type connection_type);
explicit boosted_tcp_server(boost::asio::io_service& external_io_service, t_connection_type connection_type);
explicit boosted_tcp_server(boost::asio::io_context& external_io_context, t_connection_type connection_type);
~boosted_tcp_server();
std::map<std::string, t_connection_type> server_type_map;
@@ -349,7 +367,7 @@ namespace net_utils
const std::string port_ipv6 = "", const std::string address_ipv6 = "::", bool use_ipv6 = false, bool require_ipv4 = true,
ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
/// Run the server's io_service loop.
/// Run the server's io_context loop.
bool run_server(size_t threads_count, bool wait = true, const boost::thread::attributes& attrs = boost::thread::attributes());
/// wait for service workers stop
@@ -369,6 +387,8 @@ namespace net_utils
size_t get_threads_count(){return m_threads_count;}
void set_connection_filter(i_connection_filter* pfilter);
void set_connection_limit(i_connection_limit* plimit);
void set_response_soft_limit(std::size_t limit);
void set_default_remote(epee::net_utils::network_address remote)
{
@@ -409,7 +429,7 @@ namespace net_utils
return connections_count;
}
boost::asio::io_service& get_io_service(){return io_service_;}
boost::asio::io_context& get_io_context(){return io_context_;}
struct idle_callback_conext_base
{
@@ -417,7 +437,7 @@ namespace net_utils
virtual bool call_handler(){return true;}
idle_callback_conext_base(boost::asio::io_service& io_serice):
idle_callback_conext_base(boost::asio::io_context& io_serice):
m_timer(io_serice)
{}
boost::asio::deadline_timer m_timer;
@@ -426,7 +446,7 @@ namespace net_utils
template <class t_handler>
struct idle_callback_conext: public idle_callback_conext_base
{
idle_callback_conext(boost::asio::io_service& io_serice, t_handler& h, uint64_t period):
idle_callback_conext(boost::asio::io_context& io_serice, t_handler& h, uint64_t period):
idle_callback_conext_base(io_serice),
m_handler(h)
{this->m_period = period;}
@@ -442,7 +462,7 @@ namespace net_utils
template<class t_handler>
bool add_idle_handler(t_handler t_callback, uint64_t timeout_ms)
{
boost::shared_ptr<idle_callback_conext<t_handler>> ptr(new idle_callback_conext<t_handler>(io_service_, t_callback, timeout_ms));
boost::shared_ptr<idle_callback_conext<t_handler>> ptr(new idle_callback_conext<t_handler>(io_context_, t_callback, timeout_ms));
//needed call handler here ?...
ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period));
ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler<t_handler>, this, ptr));
@@ -461,14 +481,14 @@ namespace net_utils
}
template<class t_handler>
bool async_call(t_handler t_callback)
bool async_call(t_handler&& t_callback)
{
io_service_.post(t_callback);
boost::asio::post(io_context_, std::forward<t_handler>(t_callback));
return true;
}
private:
/// Run the server's io_service loop.
/// Run the server's io_context loop.
bool worker_thread();
/// Handle completion of an asynchronous accept operation.
void handle_accept_ipv4(const boost::system::error_code& e);
@@ -479,18 +499,18 @@ namespace net_utils
const std::shared_ptr<typename connection<t_protocol_handler>::shared_state> m_state;
/// The io_service used to perform asynchronous operations.
/// The io_context used to perform asynchronous operations.
struct worker
{
worker()
: io_service(), work(io_service)
: io_context(), work(io_context.get_executor())
{}
boost::asio::io_service io_service;
boost::asio::io_service::work work;
boost::asio::io_context io_context;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work;
};
std::unique_ptr<worker> m_io_service_local_instance;
boost::asio::io_service& io_service_;
std::unique_ptr<worker> m_io_context_local_instance;
boost::asio::io_context& io_context_;
/// Acceptor used to listen for incoming connections.
boost::asio::ip::tcp::acceptor acceptor_;
+175 -114
View File
@@ -31,11 +31,12 @@
//
#include <boost/asio/post.hpp>
#include <boost/foreach.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/chrono.hpp>
#include <boost/utility/value_init.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> // TODO
#include <boost/thread/condition_variable.hpp> // TODO
@@ -145,23 +146,19 @@ namespace net_utils
if (m_state.timers.general.wait_expire) {
m_state.timers.general.cancel_expire = true;
m_state.timers.general.reset_expire = true;
ec_t ec;
m_timers.general.expires_from_now(
m_timers.general.expires_after(
std::min(
duration + (add ? m_timers.general.expires_from_now() : duration_t{}),
duration + (add ? (m_timers.general.expiry() - std::chrono::steady_clock::now()) : duration_t{}),
get_default_timeout()
),
ec
)
);
}
else {
ec_t ec;
m_timers.general.expires_from_now(
m_timers.general.expires_after(
std::min(
duration + (add ? m_timers.general.expires_from_now() : duration_t{}),
duration + (add ? (m_timers.general.expiry() - std::chrono::steady_clock::now()) : duration_t{}),
get_default_timeout()
),
ec
)
);
async_wait_timer();
}
@@ -202,8 +199,7 @@ namespace net_utils
return;
m_state.timers.general.cancel_expire = true;
m_state.timers.general.reset_expire = false;
ec_t ec;
m_timers.general.cancel(ec);
m_timers.general.cancel();
}
template<typename T>
@@ -225,7 +221,8 @@ namespace net_utils
m_state.data.read.buffer.size()
),
boost::asio::transfer_exactly(epee::net_utils::get_ssl_magic_size()),
m_strand.wrap(
boost::asio::bind_executor(
m_strand,
[this, self](const ec_t &ec, size_t bytes_transferred){
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.socket.wait_read = false;
@@ -246,7 +243,8 @@ namespace net_utils
) {
m_state.ssl.enabled = false;
m_state.socket.handle_read = true;
connection_basic::strand_.post(
boost::asio::post(
connection_basic::strand_,
[this, self, bytes_transferred]{
bool success = m_handler.handle_recv(
reinterpret_cast<char *>(m_state.data.read.buffer.data()),
@@ -304,7 +302,8 @@ namespace net_utils
static_cast<shared_state&>(
connection_basic::get_state()
).ssl_options().configure(connection_basic::socket_, handshake);
m_strand.post(
boost::asio::post(
m_strand,
[this, self, on_handshake]{
connection_basic::socket_.async_handshake(
handshake,
@@ -313,7 +312,7 @@ namespace net_utils
m_state.ssl.forced ? 0 :
epee::net_utils::get_ssl_magic_size()
),
m_strand.wrap(on_handshake)
boost::asio::bind_executor(m_strand, on_handshake)
);
}
);
@@ -328,7 +327,7 @@ namespace net_utils
return;
}
auto self = connection<T>::shared_from_this();
if (m_connection_type != e_connection_type_RPC) {
if (speed_limit_is_enabled()) {
auto calc_duration = []{
CRITICAL_REGION_LOCAL(
network_throttle_manager_t::m_lock_get_global_throttle_in
@@ -345,8 +344,7 @@ namespace net_utils
};
const auto duration = calc_duration();
if (duration > duration_t{}) {
ec_t ec;
m_timers.throttle.in.expires_from_now(duration, ec);
m_timers.throttle.in.expires_after(duration);
m_state.timers.throttle.in.wait_expire = true;
m_timers.throttle.in.async_wait([this, self](const ec_t &ec){
std::lock_guard<std::mutex> guard(m_state.lock);
@@ -382,7 +380,7 @@ namespace net_utils
m_conn_context.m_max_speed_down,
speed
);
{
if (speed_limit_is_enabled()) {
CRITICAL_REGION_LOCAL(
network_throttle_manager_t::m_lock_get_global_throttle_in
);
@@ -401,7 +399,8 @@ namespace net_utils
// writes until the connection terminates without deadlocking waiting
// for handle_recv.
m_state.socket.handle_read = true;
connection_basic::strand_.post(
boost::asio::post(
connection_basic::strand_,
[this, self, bytes_transferred]{
bool success = m_handler.handle_recv(
reinterpret_cast<char *>(m_state.data.read.buffer.data()),
@@ -428,17 +427,18 @@ namespace net_utils
m_state.data.read.buffer.data(),
m_state.data.read.buffer.size()
),
m_strand.wrap(on_read)
boost::asio::bind_executor(m_strand, on_read)
);
else
m_strand.post(
boost::asio::post(
m_strand,
[this, self, on_read]{
connection_basic::socket_.async_read_some(
boost::asio::buffer(
m_state.data.read.buffer.data(),
m_state.data.read.buffer.size()
),
m_strand.wrap(on_read)
boost::asio::bind_executor(m_strand, on_read)
);
}
);
@@ -454,7 +454,7 @@ namespace net_utils
return;
}
auto self = connection<T>::shared_from_this();
if (m_connection_type != e_connection_type_RPC) {
if (speed_limit_is_enabled()) {
auto calc_duration = [this]{
CRITICAL_REGION_LOCAL(
network_throttle_manager_t::m_lock_get_global_throttle_out
@@ -473,8 +473,7 @@ namespace net_utils
};
const auto duration = calc_duration();
if (duration > duration_t{}) {
ec_t ec;
m_timers.throttle.out.expires_from_now(duration, ec);
m_timers.throttle.out.expires_after(duration);
m_state.timers.throttle.out.wait_expire = true;
m_timers.throttle.out.async_wait([this, self](const ec_t &ec){
std::lock_guard<std::mutex> guard(m_state.lock);
@@ -498,10 +497,12 @@ namespace net_utils
if (m_state.socket.cancel_write) {
m_state.socket.cancel_write = false;
m_state.data.write.queue.clear();
m_state.data.write.total_bytes = 0;
state_status_check();
}
else if (ec.value()) {
m_state.data.write.queue.clear();
m_state.data.write.total_bytes = 0;
interrupt();
}
else {
@@ -513,7 +514,7 @@ namespace net_utils
m_conn_context.m_max_speed_down,
speed
);
{
if (speed_limit_is_enabled()) {
CRITICAL_REGION_LOCAL(
network_throttle_manager_t::m_lock_get_global_throttle_out
);
@@ -526,8 +527,11 @@ namespace net_utils
start_timer(get_default_timeout(), true);
}
assert(bytes_transferred == m_state.data.write.queue.back().size());
const std::size_t byte_count = m_state.data.write.queue.back().size();
assert(bytes_transferred == byte_count);
m_state.data.write.queue.pop_back();
m_state.data.write.total_bytes -=
std::min(m_state.data.write.total_bytes, byte_count);
m_state.condition.notify_all();
start_write();
}
@@ -539,10 +543,11 @@ namespace net_utils
m_state.data.write.queue.back().data(),
m_state.data.write.queue.back().size()
),
m_strand.wrap(on_write)
boost::asio::bind_executor(m_strand, on_write)
);
else
m_strand.post(
boost::asio::post(
m_strand,
[this, self, on_write]{
boost::asio::async_write(
connection_basic::socket_,
@@ -550,7 +555,7 @@ namespace net_utils
m_state.data.write.queue.back().data(),
m_state.data.write.queue.back().size()
),
m_strand.wrap(on_write)
boost::asio::bind_executor(m_strand, on_write)
);
}
);
@@ -587,10 +592,11 @@ namespace net_utils
terminate();
}
};
m_strand.post(
boost::asio::post(
m_strand,
[this, self, on_shutdown]{
connection_basic::socket_.async_shutdown(
m_strand.wrap(on_shutdown)
boost::asio::bind_executor(m_strand, on_shutdown)
);
}
);
@@ -605,15 +611,13 @@ namespace net_utils
wait_socket = m_state.socket.cancel_handshake = true;
if (m_state.timers.throttle.in.wait_expire) {
m_state.timers.throttle.in.cancel_expire = true;
ec_t ec;
m_timers.throttle.in.cancel(ec);
m_timers.throttle.in.cancel();
}
if (m_state.socket.wait_read)
wait_socket = m_state.socket.cancel_read = true;
if (m_state.timers.throttle.out.wait_expire) {
m_state.timers.throttle.out.cancel_expire = true;
ec_t ec;
m_timers.throttle.out.cancel(ec);
m_timers.throttle.out.cancel();
}
if (m_state.socket.wait_write)
wait_socket = m_state.socket.cancel_write = true;
@@ -671,8 +675,9 @@ namespace net_utils
return;
if (m_state.timers.throttle.out.wait_expire)
return;
if (m_state.socket.wait_write)
return;
// \NOTE See on_terminating() comments
//if (m_state.socket.wait_write)
// return;
if (m_state.socket.wait_shutdown)
return;
if (m_state.protocol.wait_init)
@@ -730,8 +735,13 @@ namespace net_utils
return;
if (m_state.timers.throttle.out.wait_expire)
return;
if (m_state.socket.wait_write)
return;
// Writes cannot be canceled due to `async_write` being a "composed"
// handler. ASIO has new cancellation routines, not available in 1.66, to
// handle this situation. The problem is that if cancel is called after an
// intermediate handler is queued, the op will not check the cancel flag in
// our code, and will instead queue up another write.
//if (m_state.socket.wait_write)
// return;
if (m_state.socket.wait_shutdown)
return;
if (m_state.protocol.wait_init)
@@ -758,6 +768,8 @@ namespace net_utils
std::lock_guard<std::mutex> guard(m_state.lock);
if (m_state.status != status_t::RUNNING || m_state.socket.wait_handshake)
return false;
if (std::numeric_limits<std::size_t>::max() - m_state.data.write.total_bytes < message.size())
return false;
// Wait for the write queue to fall below the max. If it doesn't after a
// randomized delay, drop the connection.
@@ -775,7 +787,14 @@ namespace net_utils
std::uniform_int_distribution<>(5000, 6000)(rng)
);
};
if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT)
// The bytes check intentionally does not include incoming message size.
// This allows for a soft overflow; a single http response will never fail
// this check, but multiple responses could. Clients can avoid this case
// by reading the entire response before making another request. P2P
// should never hit the MAX_BYTES check (when using default values).
if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
m_state.data.write.total_bytes <= static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit)
return true;
m_state.data.write.wait_consume = true;
bool success = m_state.condition.wait_for(
@@ -784,14 +803,23 @@ namespace net_utils
[this]{
return (
m_state.status != status_t::RUNNING ||
m_state.data.write.queue.size() <=
ABSTRACT_SERVER_SEND_QUE_MAX_COUNT
(
m_state.data.write.queue.size() <=
ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
m_state.data.write.total_bytes <=
static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit
)
);
}
);
m_state.data.write.wait_consume = false;
if (!success) {
terminate();
// synchronize with intermediate writes on `m_strand`
auto self = connection<T>::shared_from_this();
boost::asio::post(m_strand, [this, self] {
std::lock_guard<std::mutex> guard(m_state.lock);
terminate();
});
return false;
}
else
@@ -817,7 +845,9 @@ namespace net_utils
) {
if (!wait_consume())
return false;
const std::size_t byte_count = message.size();
m_state.data.write.queue.emplace_front(std::move(message));
m_state.data.write.total_bytes += byte_count;
start_write();
}
else {
@@ -827,6 +857,7 @@ namespace net_utils
m_state.data.write.queue.emplace_front(
message.take_slice(CHUNK_SIZE)
);
m_state.data.write.total_bytes += m_state.data.write.queue.front().size();
start_write();
}
}
@@ -860,7 +891,7 @@ namespace net_utils
ipv4_network_address{
uint32_t{
boost::asio::detail::socket_ops::host_to_network_long(
endpoint.address().to_v4().to_ulong()
endpoint.address().to_v4().to_uint()
)
},
endpoint.port()
@@ -873,6 +904,13 @@ namespace net_utils
).pfilter;
if (filter && !filter->is_remote_host_allowed(*real_remote))
return false;
auto *limit = static_cast<shared_state&>(
connection_basic::get_state()
).plimit;
if (limit && limit->is_host_limit(*real_remote))
return false;
ec_t ec;
#if !defined(_WIN32) || !defined(__i686)
connection_basic::socket_.next_layer().set_option(
@@ -938,7 +976,8 @@ namespace net_utils
ssl_support_t ssl_support
):
connection(
std::move(socket_t{io_context}),
io_context,
socket_t{io_context},
std::move(shared_state),
connection_type,
ssl_support
@@ -948,15 +987,16 @@ namespace net_utils
template<typename T>
connection<T>::connection(
io_context_t &io_context,
socket_t &&socket,
std::shared_ptr<shared_state> shared_state,
t_connection_type connection_type,
ssl_support_t ssl_support
):
connection_basic(std::move(socket), shared_state, ssl_support),
connection_basic(io_context, std::move(socket), shared_state, ssl_support),
m_handler(this, *shared_state, m_conn_context),
m_connection_type(connection_type),
m_io_context{GET_IO_SERVICE(connection_basic::socket_)},
m_io_context{io_context},
m_strand{m_io_context},
m_timers{m_io_context}
{
@@ -1022,7 +1062,7 @@ namespace net_utils
template<typename T>
bool connection<T>::speed_limit_is_enabled() const
{
return m_connection_type != e_connection_type_RPC;
return m_connection_type == e_connection_type_P2P;
}
template<typename T>
@@ -1075,7 +1115,7 @@ namespace net_utils
return false;
auto self = connection<T>::shared_from_this();
++m_state.protocol.wait_callback;
connection_basic::strand_.post([this, self]{
boost::asio::post(connection_basic::strand_, [this, self]{
m_handler.handle_qued_callback();
std::lock_guard<std::mutex> guard(m_state.lock);
--m_state.protocol.wait_callback;
@@ -1088,7 +1128,7 @@ namespace net_utils
}
template<typename T>
typename connection<T>::io_context_t &connection<T>::get_io_service()
typename connection<T>::io_context_t &connection<T>::get_io_context()
{
return m_io_context;
}
@@ -1128,10 +1168,10 @@ namespace net_utils
template<class t_protocol_handler>
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server( t_connection_type connection_type ) :
m_state(std::make_shared<typename connection<t_protocol_handler>::shared_state>()),
m_io_service_local_instance(new worker()),
io_service_(m_io_service_local_instance->io_service),
acceptor_(io_service_),
acceptor_ipv6(io_service_),
m_io_context_local_instance(new worker()),
io_context_(m_io_context_local_instance->io_context),
acceptor_(io_context_),
acceptor_ipv6(io_context_),
default_remote(),
m_stop_signal_sent(false), m_port(0),
m_threads_count(0),
@@ -1145,11 +1185,11 @@ namespace net_utils
}
template<class t_protocol_handler>
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server(boost::asio::io_service& extarnal_io_service, t_connection_type connection_type) :
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server(boost::asio::io_context& extarnal_io_context, t_connection_type connection_type) :
m_state(std::make_shared<typename connection<t_protocol_handler>::shared_state>()),
io_service_(extarnal_io_service),
acceptor_(io_service_),
acceptor_ipv6(io_service_),
io_context_(extarnal_io_context),
acceptor_(io_context_),
acceptor_ipv6(io_context_),
default_remote(),
m_stop_signal_sent(false), m_port(0),
m_threads_count(0),
@@ -1196,24 +1236,27 @@ namespace net_utils
std::string ipv4_failed = "";
std::string ipv6_failed = "";
boost::asio::ip::tcp::resolver resolver(io_context_);
try
{
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port), boost::asio::ip::tcp::resolver::query::canonical_name);
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
acceptor_.open(endpoint.protocol());
const auto results = resolver.resolve(
address, boost::lexical_cast<std::string>(port), boost::asio::ip::tcp::resolver::canonical_name
);
acceptor_.open(results.begin()->endpoint().protocol());
#if !defined(_WIN32)
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
#endif
acceptor_.bind(endpoint);
acceptor_.bind(*results.begin());
acceptor_.listen();
boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint();
m_port = binded_endpoint.port();
MDEBUG("start accept (IPv4)");
new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
new_connection_.reset(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, m_state->ssl_options().support));
acceptor_.async_accept(new_connection_->socket(),
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4, this,
boost::asio::placeholders::error));
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4, this,
boost::asio::placeholders::error));
}
catch (const std::exception &e)
{
@@ -1234,23 +1277,25 @@ namespace net_utils
try
{
if (port_ipv6 == 0) port_ipv6 = port; // default arg means bind to same port as ipv4
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(address_ipv6, boost::lexical_cast<std::string>(port_ipv6), boost::asio::ip::tcp::resolver::query::canonical_name);
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
acceptor_ipv6.open(endpoint.protocol());
const auto results = resolver.resolve(
address_ipv6, boost::lexical_cast<std::string>(port_ipv6), boost::asio::ip::tcp::resolver::canonical_name
);
acceptor_ipv6.open(results.begin()->endpoint().protocol());
#if !defined(_WIN32)
acceptor_ipv6.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
#endif
acceptor_ipv6.set_option(boost::asio::ip::v6_only(true));
acceptor_ipv6.bind(endpoint);
acceptor_ipv6.bind(*results.begin());
acceptor_ipv6.listen();
boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_ipv6.local_endpoint();
m_port_ipv6 = binded_endpoint.port();
MDEBUG("start accept (IPv6)");
new_connection_ipv6.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
new_connection_ipv6.reset(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, m_state->ssl_options().support));
acceptor_ipv6.async_accept(new_connection_ipv6->socket(),
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6, this,
boost::asio::placeholders::error));
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6, this,
boost::asio::placeholders::error));
}
catch (const std::exception &e)
{
@@ -1314,7 +1359,7 @@ namespace net_utils
{
try
{
io_service_.run();
io_context_.run();
return true;
}
catch(const std::exception& ex)
@@ -1349,6 +1394,20 @@ namespace net_utils
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
void boosted_tcp_server<t_protocol_handler>::set_connection_limit(i_connection_limit* plimit)
{
assert(m_state != nullptr); // always set in constructor
m_state->plimit = plimit;
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
void boosted_tcp_server<t_protocol_handler>::set_response_soft_limit(const std::size_t limit)
{
assert(m_state != nullptr); // always set in constructor
m_state->response_soft_limit = limit;
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::run_server(size_t threads_count, bool wait, const boost::thread::attributes& attrs)
{
TRY_ENTRY();
@@ -1358,7 +1417,7 @@ namespace net_utils
while(!m_stop_signal_sent)
{
// Create a pool of threads to run all of the io_services.
// Create a pool of threads to run all of the io_contexts.
CRITICAL_REGION_BEGIN(m_threads_lock);
for (std::size_t i = 0; i < threads_count; ++i)
{
@@ -1450,7 +1509,7 @@ namespace net_utils
}
connections_.clear();
connections_mutex.unlock();
io_service_.stop();
io_context_.stop();
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::send_stop_signal()", void());
}
//---------------------------------------------------------------------------------
@@ -1497,7 +1556,7 @@ namespace net_utils
(*current_new_connection)->setRpcStation(); // hopefully this is not needed actually
}
connection_ptr conn(std::move((*current_new_connection)));
(*current_new_connection).reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, conn->get_ssl_support()));
(*current_new_connection).reset(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, conn->get_ssl_support()));
current_acceptor->async_accept((*current_new_connection)->socket(),
boost::bind(accept_function_pointer, this,
boost::asio::placeholders::error));
@@ -1532,7 +1591,7 @@ namespace net_utils
assert(m_state != nullptr); // always set in constructor
_erro("Some problems at accept: " << e.message() << ", connections_count = " << m_state->sock_count);
misc_utils::sleep_no_w(100);
(*current_new_connection).reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, (*current_new_connection)->get_ssl_support()));
(*current_new_connection).reset(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, (*current_new_connection)->get_ssl_support()));
current_acceptor->async_accept((*current_new_connection)->socket(),
boost::bind(accept_function_pointer, this,
boost::asio::placeholders::error));
@@ -1541,9 +1600,9 @@ namespace net_utils
template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote, epee::net_utils::ssl_support_t ssl_support)
{
if(std::addressof(get_io_service()) == std::addressof(GET_IO_SERVICE(sock)))
if(std::addressof(get_io_context()) == std::addressof(sock.get_executor().context()))
{
connection_ptr conn(new connection<t_protocol_handler>(std::move(sock), m_state, m_connection_type, ssl_support));
connection_ptr conn(new connection<t_protocol_handler>(io_context_, std::move(sock), m_state, m_connection_type, ssl_support));
if(conn->start(false, 1 < m_threads_count, std::move(real_remote)))
{
conn->get_context(out);
@@ -1553,7 +1612,7 @@ namespace net_utils
}
else
{
MWARNING(out << " was not added, socket/io_service mismatch");
MWARNING(out << " was not added, socket/io_context mismatch");
}
return false;
}
@@ -1566,7 +1625,7 @@ namespace net_utils
sock_.open(remote_endpoint.protocol());
if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
{
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0);
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::make_address(bind_ip), 0);
boost::system::error_code ec;
sock_.bind(local_endpoint, ec);
if (ec)
@@ -1661,7 +1720,7 @@ namespace net_utils
{
TRY_ENTRY();
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) );
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support) );
connections_mutex.lock();
connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size());
@@ -1671,14 +1730,16 @@ namespace net_utils
bool try_ipv6 = false;
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
boost::asio::ip::tcp::resolver resolver(io_context_);
boost::asio::ip::tcp::resolver::results_type results{};
boost::system::error_code resolve_error;
boost::asio::ip::tcp::resolver::iterator iterator;
try
{
//resolving ipv4 address as ipv6 throws, catch here and move on
iterator = resolver.resolve(query, resolve_error);
results = resolver.resolve(
boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error
);
}
catch (const boost::system::system_error& e)
{
@@ -1696,8 +1757,7 @@ namespace net_utils
std::string bind_ip_to_use;
boost::asio::ip::tcp::resolver::iterator end;
if(iterator == end)
if(results.empty())
{
if (!m_use_ipv6)
{
@@ -1717,11 +1777,11 @@ namespace net_utils
if (try_ipv6)
{
boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
results = resolver.resolve(
boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error
);
iterator = resolver.resolve(query6, resolve_error);
if(iterator == end)
if(results.empty())
{
_erro("Failed to resolve " << adr);
return false;
@@ -1741,6 +1801,8 @@ namespace net_utils
}
const auto iterator = results.begin();
MDEBUG("Trying to connect to " << adr << ":" << port << ", bind_ip = " << bind_ip_to_use);
//boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
@@ -1767,7 +1829,6 @@ namespace net_utils
if (r)
{
new_connection_l->get_context(conn_context);
//new_connection_l.reset(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_pfilter));
}
else
{
@@ -1786,7 +1847,7 @@ namespace net_utils
bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, const t_callback &cb, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support)
{
TRY_ENTRY();
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) );
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support) );
connections_mutex.lock();
connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size());
@@ -1796,14 +1857,16 @@ namespace net_utils
bool try_ipv6 = false;
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
boost::asio::ip::tcp::resolver resolver(io_context_);
boost::asio::ip::tcp::resolver::results_type results{};
boost::system::error_code resolve_error;
boost::asio::ip::tcp::resolver::iterator iterator;
try
{
//resolving ipv4 address as ipv6 throws, catch here and move on
iterator = resolver.resolve(query, resolve_error);
results = resolver.resolve(
boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error
);
}
catch (const boost::system::system_error& e)
{
@@ -1819,8 +1882,7 @@ namespace net_utils
throw;
}
boost::asio::ip::tcp::resolver::iterator end;
if(iterator == end)
if(results.empty())
{
if (!try_ipv6)
{
@@ -1835,24 +1897,23 @@ namespace net_utils
if (try_ipv6)
{
boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
results = resolver.resolve(
boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error
);
iterator = resolver.resolve(query6, resolve_error);
if(iterator == end)
if(results.empty())
{
_erro("Failed to resolve " << adr);
return false;
}
}
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
boost::asio::ip::tcp::endpoint remote_endpoint(*results.begin());
sock_.open(remote_endpoint.protocol());
if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
{
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0);
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::make_address(bind_ip.c_str()), 0);
boost::system::error_code ec;
sock_.bind(local_endpoint, ec);
if (ec)
@@ -1864,7 +1925,7 @@ namespace net_utils
}
}
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_service_));
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_context_));
//start deadline
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
sh_deadline->async_wait([=](const boost::system::error_code& error)
@@ -112,21 +112,20 @@ class connection_basic { // not-templated base class for rapid developmet of som
std::deque<byte_slice> m_send_que;
volatile bool m_is_multithreaded;
/// Strand to ensure the connection's handlers are not called concurrently.
boost::asio::io_service::strand strand_;
boost::asio::io_context::strand strand_;
/// Socket for the connection.
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
ssl_support_t m_ssl_support;
public:
// first counter is the ++/-- count of current sockets, the other socket_number is only-increasing ++ number generator
connection_basic(boost::asio::ip::tcp::socket&& socket, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support);
connection_basic(boost::asio::io_service &io_service, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support);
connection_basic(boost::asio::io_context &context, boost::asio::ip::tcp::socket&& sock, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support);
connection_basic(boost::asio::io_context &context, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support);
virtual ~connection_basic() noexcept(false);
//! \return `shared_state` object passed in construction (ptr never changes).
connection_basic_shared_state& get_state() noexcept { return *m_state; /* verified in constructor */ }
connection_basic(boost::asio::io_service& io_service, std::atomic<long> &ref_sock_count, std::atomic<long> &sock_number, ssl_support_t ssl);
boost::asio::ip::tcp::socket& socket() { return socket_.next_layer(); }
ssl_support_t get_ssl_support() const { return m_ssl_support; }
@@ -135,7 +134,7 @@ class connection_basic { // not-templated base class for rapid developmet of som
bool handshake(boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer = {})
{
//m_state != nullptr verified in constructor
return m_state->ssl_options().handshake(socket_, type, buffer);
return m_state->ssl_options().handshake(strand_.context(), socket_, type, buffer);
}
template<typename MutableBufferSequence, typename ReadHandler>
@@ -32,6 +32,7 @@
#include <boost/optional/optional.hpp>
#include <string>
#include <unordered_map>
#include "net_utils_base.h"
#include "http_auth.h"
#include "http_base.h"
@@ -54,8 +55,13 @@ namespace net_utils
{
std::string m_folder;
std::vector<std::string> m_access_control_origins;
std::unordered_map<std::string, std::size_t> m_connections;
boost::optional<login> m_user;
size_t m_max_content_length{std::numeric_limits<size_t>::max()};
std::size_t m_connection_count{0};
std::size_t m_max_public_ip_connections{3};
std::size_t m_max_private_ip_connections{25};
std::size_t m_max_connections{100};
critical_section m_lock;
};
@@ -70,7 +76,7 @@ namespace net_utils
typedef http_server_config config_type;
simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context);
virtual ~simple_http_connection_handler(){}
virtual ~simple_http_connection_handler();
bool release_protocol()
{
@@ -86,10 +92,7 @@ namespace net_utils
{
return true;
}
bool after_init_connection()
{
return true;
}
bool after_init_connection();
virtual bool handle_recv(const void* ptr, size_t cb);
virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response);
@@ -146,6 +149,7 @@ namespace net_utils
protected:
i_service_endpoint* m_psnd_hndlr;
t_connection_context& m_conn_context;
bool m_initialized;
};
template<class t_connection_context>
@@ -212,10 +216,6 @@ namespace net_utils
}
void handle_qued_callback()
{}
bool after_init_connection()
{
return true;
}
private:
//simple_http_connection_handler::config_type m_stub_config;
@@ -208,11 +208,46 @@ namespace net_utils
m_newlines(0),
m_bytes_read(0),
m_psnd_hndlr(psnd_hndlr),
m_conn_context(conn_context)
m_conn_context(conn_context),
m_initialized(false)
{
}
//--------------------------------------------------------------------------------------------
template<class t_connection_context>
simple_http_connection_handler<t_connection_context>::~simple_http_connection_handler()
{
try
{
if (m_initialized)
{
CRITICAL_REGION_LOCAL(m_config.m_lock);
if (m_config.m_connection_count)
--m_config.m_connection_count;
auto elem = m_config.m_connections.find(m_conn_context.m_remote_address.host_str());
if (elem != m_config.m_connections.end())
{
if (elem->second == 1 || elem->second == 0)
m_config.m_connections.erase(elem);
else
--(elem->second);
}
}
}
catch (...)
{}
}
//--------------------------------------------------------------------------------------------
template<class t_connection_context>
bool simple_http_connection_handler<t_connection_context>::after_init_connection()
{
CRITICAL_REGION_LOCAL(m_config.m_lock);
++m_config.m_connections[m_conn_context.m_remote_address.host_str()];
++m_config.m_connection_count;
m_initialized = true;
return true;
}
//--------------------------------------------------------------------------------------------
template<class t_connection_context>
bool simple_http_connection_handler<t_connection_context>::set_ready_state()
{
@@ -71,7 +71,7 @@
else if((query_info.m_URI == s_pattern) && (cond)) \
{ \
handled = true; \
uint64_t ticks = misc_utils::get_tick_count(); \
uint64_t ticks = epee::misc_utils::get_tick_count(); \
boost::value_initialized<command_type::request> req; \
bool parse_res = epee::serialization::load_t_from_json(static_cast<command_type::request&>(req), query_info.m_body); \
if (!parse_res) \
@@ -107,7 +107,7 @@
else if(query_info.m_URI == s_pattern) \
{ \
handled = true; \
uint64_t ticks = misc_utils::get_tick_count(); \
uint64_t ticks = epee::misc_utils::get_tick_count(); \
boost::value_initialized<command_type::request> req; \
bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), epee::strspan<uint8_t>(query_info.m_body)); \
if (!parse_res) \
@@ -117,7 +117,7 @@
response_info.m_response_comment = "Bad request"; \
return true; \
} \
uint64_t ticks1 = misc_utils::get_tick_count(); \
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
boost::value_initialized<command_type::response> resp;\
MINFO(m_conn_context << "calling " << s_pattern); \
bool res = false; \
@@ -129,7 +129,7 @@
response_info.m_response_comment = "Internal Server Error"; \
return true; \
} \
uint64_t ticks2 = misc_utils::get_tick_count(); \
uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
epee::byte_slice buffer; \
epee::serialization::store_t_to_binary(static_cast<command_type::response&>(resp), buffer, 64 * 1024); \
uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
@@ -171,6 +171,13 @@
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
return true; \
} \
epee::serialization::storage_entry params_; \
params_ = epee::serialization::storage_entry(epee::serialization::section()); \
if(!ps.get_value("params", params_, nullptr)) \
{ \
epee::serialization::section params_section; \
ps.set_value("params", std::move(params_section), nullptr); \
} \
if(false) return true; //just a stub to have "else if"
@@ -33,6 +33,7 @@
#include <boost/thread.hpp>
#include <boost/bind/bind.hpp>
#include "cryptonote_config.h"
#include "net/abstract_tcp_server2.h"
#include "http_protocol_handler.h"
#include "net/http_server_handlers_map2.h"
@@ -44,7 +45,8 @@ namespace epee
{
template<class t_child_class, class t_connection_context = epee::net_utils::connection_context_base>
class http_server_impl_base: public net_utils::http::i_http_server_handler<t_connection_context>
class http_server_impl_base: public net_utils::http::i_http_server_handler<t_connection_context>,
net_utils::i_connection_limit
{
public:
@@ -52,7 +54,7 @@ namespace epee
: m_net_server(epee::net_utils::e_connection_type_RPC)
{}
explicit http_server_impl_base(boost::asio::io_service& external_io_service)
explicit http_server_impl_base(boost::asio::io_context& external_io_service)
: m_net_server(external_io_service)
{}
@@ -60,8 +62,16 @@ namespace epee
const std::string& bind_ipv6_address = "::", bool use_ipv6 = false, bool require_ipv4 = true,
std::vector<std::string> access_control_origins = std::vector<std::string>(),
boost::optional<net_utils::http::login> user = boost::none,
net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect)
net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect,
const std::size_t max_public_ip_connections = DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP,
const std::size_t max_private_ip_connections = DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP,
const std::size_t max_connections = DEFAULT_RPC_MAX_CONNECTIONS,
const std::size_t response_soft_limit = DEFAULT_RPC_SOFT_LIMIT_SIZE)
{
if (max_connections < max_public_ip_connections)
throw std::invalid_argument{"Max public IP connections cannot be more than max connections"};
if (max_connections < max_private_ip_connections)
throw std::invalid_argument{"Max private IP connections cannot be more than max connections"};
//set self as callback handler
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
@@ -75,6 +85,11 @@ namespace epee
m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins);
m_net_server.get_config_object().m_user = std::move(user);
m_net_server.get_config_object().m_max_public_ip_connections = max_public_ip_connections;
m_net_server.get_config_object().m_max_private_ip_connections = max_private_ip_connections;
m_net_server.get_config_object().m_max_connections = max_connections;
m_net_server.set_response_soft_limit(response_soft_limit);
m_net_server.set_connection_limit(this);
MGINFO("Binding on " << bind_ip << " (IPv4):" << bind_port);
if (use_ipv6)
@@ -131,6 +146,26 @@ namespace epee
}
protected:
virtual bool is_host_limit(const net_utils::network_address& na) override final
{
auto& config = m_net_server.get_config_object();
CRITICAL_REGION_LOCAL(config.m_lock);
if (config.m_max_connections <= config.m_connection_count)
return true;
const bool is_private = na.is_loopback() || na.is_local();
const auto elem = config.m_connections.find(na.host_str());
if (elem != config.m_connections.end())
{
if (is_private)
return config.m_max_private_ip_connections <= elem->second;
else
return config.m_max_public_ip_connections <= elem->second;
}
return false;
}
net_utils::boosted_tcp_server<net_utils::http::http_custom_handler<t_connection_context> > m_net_server;
};
}
@@ -200,7 +200,7 @@ public:
struct anvoke_handler: invoke_response_handler_base
{
anvoke_handler(const callback_t& cb, uint64_t timeout, async_protocol_handler& con, int command)
:m_cb(cb), m_timeout(timeout), m_con(con), m_timer(con.m_pservice_endpoint->get_io_service()), m_timer_started(false),
:m_cb(cb), m_timeout(timeout), m_con(con), m_timer(con.m_pservice_endpoint->get_io_context()), m_timer_started(false),
m_cancel_timer_called(false), m_timer_cancelled(false), m_command(command)
{
if(m_con.start_outer_call())
+14 -127
View File
@@ -34,7 +34,7 @@
#include <atomic>
#include <string>
#include <boost/version.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/ssl.hpp>
@@ -158,11 +158,11 @@ namespace net_utils
inline
try_connect_result_t try_connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout)
{
m_deadline.expires_from_now(timeout);
m_deadline.expires_after(timeout);
boost::unique_future<boost::asio::ip::tcp::socket> connection = m_connector(addr, port, m_deadline);
for (;;)
{
m_io_service.reset();
m_io_service.restart();
m_io_service.run_one();
if (connection.is_ready())
@@ -178,7 +178,7 @@ namespace net_utils
// SSL Options
if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
{
if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout))
if (!m_ssl_options.handshake(m_io_service, *m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout))
{
if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
{
@@ -285,7 +285,7 @@ namespace net_utils
try
{
m_deadline.expires_from_now(timeout);
m_deadline.expires_after(timeout);
// Set up the variable that receives the result of the asynchronous
// operation. The error code is set to would_block to signal that the
@@ -303,7 +303,7 @@ namespace net_utils
// Block until the asynchronous operation has completed.
while (ec == boost::asio::error::would_block)
{
m_io_service.reset();
m_io_service.restart();
m_io_service.run_one();
}
@@ -409,7 +409,7 @@ namespace net_utils
// Set a deadline for the asynchronous operation. Since this function uses
// a composed operation (async_read_until), the deadline applies to the
// entire operation, rather than individual reads from the socket.
m_deadline.expires_from_now(timeout);
m_deadline.expires_after(timeout);
// Set up the variable that receives the result of the asynchronous
// operation. The error code is set to would_block to signal that the
@@ -436,7 +436,7 @@ namespace net_utils
// Block until the asynchronous operation has completed.
while (ec == boost::asio::error::would_block && !m_shutdowned)
{
m_io_service.reset();
m_io_service.restart();
m_io_service.run_one();
}
@@ -495,7 +495,7 @@ namespace net_utils
// Set a deadline for the asynchronous operation. Since this function uses
// a composed operation (async_read_until), the deadline applies to the
// entire operation, rather than individual reads from the socket.
m_deadline.expires_from_now(timeout);
m_deadline.expires_after(timeout);
// Set up the variable that receives the result of the asynchronous
// operation. The error code is set to would_block to signal that the
@@ -580,7 +580,7 @@ namespace net_utils
return true;
}
boost::asio::io_service& get_io_service()
boost::asio::io_context& get_io_service()
{
return m_io_service;
}
@@ -607,7 +607,7 @@ namespace net_utils
// Check whether the deadline has passed. We compare the deadline against
// the current time since a new asynchronous operation may have moved the
// deadline before this actor had a chance to run.
if (m_deadline.expires_at() <= std::chrono::steady_clock::now())
if (m_deadline.expiry() <= std::chrono::steady_clock::now())
{
// The deadline has passed. The socket is closed so that any outstanding
// asynchronous operations are cancelled. This allows the blocked
@@ -628,11 +628,11 @@ namespace net_utils
void shutdown_ssl() {
// ssl socket shutdown blocks if server doesn't respond. We close after 2 secs
boost::system::error_code ec = boost::asio::error::would_block;
m_deadline.expires_from_now(std::chrono::milliseconds(2000));
m_deadline.expires_after(std::chrono::milliseconds(2000));
m_ssl_socket->async_shutdown(boost::lambda::var(ec) = boost::lambda::_1);
while (ec == boost::asio::error::would_block)
{
m_io_service.reset();
m_io_service.restart();
m_io_service.run_one();
}
// Ignore "short read" error
@@ -676,7 +676,7 @@ namespace net_utils
}
protected:
boost::asio::io_service m_io_service;
boost::asio::io_context m_io_service;
boost::asio::ssl::context m_ctx;
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_ssl_socket;
std::function<connect_func> m_connector;
@@ -688,119 +688,6 @@ namespace net_utils
std::atomic<uint64_t> m_bytes_sent;
std::atomic<uint64_t> m_bytes_received;
};
/************************************************************************/
/* */
/************************************************************************/
class async_blocked_mode_client: public blocked_mode_client
{
public:
async_blocked_mode_client():m_send_deadline(blocked_mode_client::m_io_service)
{
// No deadline is required until the first socket operation is started. We
// set the deadline to positive infinity so that the actor takes no action
// until a specific deadline is set.
m_send_deadline.expires_at(boost::posix_time::pos_infin);
// Start the persistent actor that checks for deadline expiry.
check_send_deadline();
}
~async_blocked_mode_client()
{
m_send_deadline.cancel();
}
bool shutdown()
{
blocked_mode_client::shutdown();
m_send_deadline.cancel();
return true;
}
inline
bool send(const void* data, size_t sz)
{
try
{
/*
m_send_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
// Set up the variable that receives the result of the asynchronous
// operation. The error code is set to would_block to signal that the
// operation is incomplete. Asio guarantees that its asynchronous
// operations will never fail with would_block, so any other value in
// ec indicates completion.
boost::system::error_code ec = boost::asio::error::would_block;
// Start the asynchronous operation itself. The boost::lambda function
// object is used as a callback and will update the ec variable when the
// operation completes. The blocking_udp_client.cpp example shows how you
// can use boost::bind rather than boost::lambda.
boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
// Block until the asynchronous operation has completed.
while(ec == boost::asio::error::would_block)
{
m_io_service.run_one();
}*/
boost::system::error_code ec;
size_t writen = write(data, sz, ec);
if (!writen || ec)
{
LOG_PRINT_L3("Problems at write: " << ec.message());
return false;
}else
{
m_send_deadline.expires_at(boost::posix_time::pos_infin);
}
}
catch(const boost::system::system_error& er)
{
LOG_ERROR("Some problems at connect, message: " << er.what());
return false;
}
catch(...)
{
LOG_ERROR("Some fatal problems.");
return false;
}
return true;
}
private:
boost::asio::deadline_timer m_send_deadline;
void check_send_deadline()
{
// Check whether the deadline has passed. We compare the deadline against
// the current time since a new asynchronous operation may have moved the
// deadline before this actor had a chance to run.
if (m_send_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
{
// The deadline has passed. The socket is closed so that any outstanding
// asynchronous operations are cancelled. This allows the blocked
// connect(), read_line() or write_line() functions to return.
LOG_PRINT_L3("Timed out socket");
m_ssl_socket->next_layer().close();
// There is no longer an active deadline. The expiry is set to positive
// infinity so that the actor takes no action until a new deadline is set.
m_send_deadline.expires_at(boost::posix_time::pos_infin);
}
// Put the actor back to sleep.
m_send_deadline.async_wait(boost::bind(&async_blocked_mode_client::check_send_deadline, this));
}
};
}
}
+3
View File
@@ -34,6 +34,7 @@
#include <string>
#include <vector>
#include <boost/utility/string_ref.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/filesystem/path.hpp>
@@ -125,6 +126,7 @@ namespace net_utils
\note It is strongly encouraged that clients using `system_ca`
verification provide a non-empty `host` for rfc2818 verification.
\param io_context associated with `socket`.
\param socket Used in SSL handshake and verification
\param type Client or server
\param host This parameter is only used when
@@ -136,6 +138,7 @@ namespace net_utils
\return True if the SSL handshake completes with peer verification
settings. */
bool handshake(
boost::asio::io_context& io_context,
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket,
boost::asio::ssl::stream_base::handshake_type type,
boost::asio::const_buffer buffer = {},
+6 -4
View File
@@ -30,7 +30,7 @@
#define _NET_UTILS_BASE_H_
#include <boost/uuid/uuid.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/address_v6.hpp>
#include <typeinfo>
#include <type_traits>
@@ -47,10 +47,12 @@
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(((uint32_t)a4)<<24))
#endif
/* Use the below function carefully. The executor and io_context are slightly
different concepts. */
#if BOOST_VERSION >= 107000
#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context())
#define MONERO_GET_EXECUTOR(type) type . get_executor()
#else
#define GET_IO_SERVICE(s) ((s).get_io_service())
#define MONERO_GET_EXECUTOR(type) type . get_io_context()
#endif
namespace net
@@ -443,7 +445,7 @@ namespace net_utils
virtual bool send_done()=0;
virtual bool call_run_once_service_io()=0;
virtual bool request_callback()=0;
virtual boost::asio::io_service& get_io_service()=0;
virtual boost::asio::io_context& get_io_context()=0;
//protect from deletion connection object(with protocol instance) during external call "invoke"
virtual bool add_ref()=0;
virtual bool release()=0;
@@ -46,13 +46,13 @@ namespace net_utils
class network_throttle : public i_network_throttle {
private:
public:
struct packet_info {
size_t m_size; // octets sent. Summary for given small-window (e.g. for all packaged in 1 second)
packet_info();
};
private:
network_speed_bps m_target_speed;
size_t m_network_add_cost; // estimated add cost of headers
size_t m_network_minimal_segment; // estimated minimal cost of sending 1 byte to round up to
@@ -98,16 +98,18 @@ public: \
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \
epee::serialization::selector<is_store>::serialize_t_val_as_blob(this_ref.varialble, stg, hparent_section, val_name);
#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, val_name) \
static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name)
#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(variable, val_name) \
static_assert(std::is_trivially_copyable<decltype(this_ref.variable)>(), "t_type must be a trivially copyable type."); \
static_assert(std::is_standard_layout<decltype(this_ref.variable)>(), "t_type must be a standard layout type."); \
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(variable, val_name)
#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, val_name, default_value) \
#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(variable, val_name, default_value) \
do { \
static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name); \
static_assert(std::is_trivially_copyable<decltype(this_ref.variable)>(), "t_type must be a trivially copyable type."); \
static_assert(std::is_standard_layout<decltype(this_ref.variable)>(), "t_type must be a standard layout type."); \
bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(variable, val_name) \
if (!ret) \
epee::serialize_default(this_ref.varialble, default_value); \
epee::serialize_default(this_ref.variable, default_value); \
} while(0);
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \
@@ -118,7 +120,7 @@ public: \
#define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble)
#define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble)
#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(varialble, def) KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, #varialble, def)
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_trivially_copyable and is_standard_layout compile time check
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble)
#define KV_SERIALIZE_OPT(variable,default_value) KV_SERIALIZE_OPT_N(variable, #variable, default_value)
@@ -26,6 +26,8 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <set>
#include <list>
#include <vector>
+13 -10
View File
@@ -133,17 +133,14 @@ namespace epee
return {src.data(), src.size()};
}
template<typename T>
constexpr bool has_padding() noexcept
{
return !std::is_standard_layout<T>() || alignof(T) != 1;
}
//! \return Cast data from `src` as `span<const std::uint8_t>`.
template<typename T>
span<const std::uint8_t> to_byte_span(const span<const T> src) noexcept
{
static_assert(!has_padding<T>(), "source type may have padding");
static_assert(!std::is_empty<T>(), "empty value types will not work -> sizeof == 1");
static_assert(std::is_standard_layout<T>(), "type must have standard layout");
static_assert(std::is_trivially_copyable<T>(), "type must be trivially copyable");
static_assert(alignof(T) == 1, "type may have padding");
return {reinterpret_cast<const std::uint8_t*>(src.data()), src.size_bytes()};
}
@@ -153,7 +150,9 @@ namespace epee
{
using value_type = typename T::value_type;
static_assert(!std::is_empty<value_type>(), "empty value types will not work -> sizeof == 1");
static_assert(!has_padding<value_type>(), "source value type may have padding");
static_assert(std::is_standard_layout<value_type>(), "value type must have standard layout");
static_assert(std::is_trivially_copyable<value_type>(), "value type must be trivially copyable");
static_assert(alignof(value_type) == 1, "value type may have padding");
return {reinterpret_cast<std::uint8_t*>(src.data()), src.size() * sizeof(value_type)};
}
@@ -162,7 +161,9 @@ namespace epee
span<const std::uint8_t> as_byte_span(const T& src) noexcept
{
static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
static_assert(!has_padding<T>(), "source type may have padding");
static_assert(std::is_standard_layout<T>(), "type must have standard layout");
static_assert(std::is_trivially_copyable<T>(), "type must be trivially copyable");
static_assert(alignof(T) == 1, "type may have padding");
return {reinterpret_cast<const std::uint8_t*>(std::addressof(src)), sizeof(T)};
}
@@ -171,7 +172,9 @@ namespace epee
span<std::uint8_t> as_mut_byte_span(T& src) noexcept
{
static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
static_assert(!has_padding<T>(), "source type may have padding");
static_assert(std::is_standard_layout<T>(), "type must have standard layout");
static_assert(std::is_trivially_copyable<T>(), "type must be trivially copyable");
static_assert(alignof(T) == 1, "type may have padding");
return {reinterpret_cast<std::uint8_t*>(std::addressof(src)), sizeof(T)};
}
@@ -38,6 +38,7 @@
#include <boost/numeric/conversion/bounds.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/numeric/conversion/bounds.hpp>
#include <typeinfo>
#include <iomanip>
+8 -10
View File
@@ -31,6 +31,7 @@
#include "mlocker.h"
#include <boost/utility/string_ref.hpp>
#include <boost/algorithm/string.hpp>
#include <sstream>
#include <string>
#include <cstdint>
@@ -69,23 +70,17 @@ namespace string_tools
#ifdef _WIN32
std::string get_current_module_path();
#endif
bool set_module_name_and_folder(const std::string& path_to_process_);
bool trim_left(std::string& str);
bool trim_right(std::string& str);
void set_module_name_and_folder(const std::string& path_to_process_);
//----------------------------------------------------------------------------
inline std::string& trim(std::string& str)
{
trim_left(str);
trim_right(str);
boost::trim(str);
return str;
}
//----------------------------------------------------------------------------
inline std::string trim(const std::string& str_)
inline std::string trim(const std::string& str)
{
std::string str = str_;
trim_left(str);
trim_right(str);
return str;
return boost::trim_copy(str);
}
std::string pad_string(std::string s, size_t n, char c = ' ', bool prepend = false);
@@ -94,6 +89,7 @@ namespace string_tools
std::string pod_to_hex(const t_pod_type& s)
{
static_assert(std::is_standard_layout<t_pod_type>(), "expected standard layout type");
static_assert(alignof(t_pod_type) == 1, "type may have padding");
return to_hex::string(as_byte_span(s));
}
//----------------------------------------------------------------------------
@@ -101,6 +97,8 @@ namespace string_tools
bool hex_to_pod(const boost::string_ref hex_str, t_pod_type& s)
{
static_assert(std::is_standard_layout<t_pod_type>(), "expected standard layout type");
static_assert(alignof(t_pod_type) == 1, "type may have padding");
static_assert(std::is_trivially_copyable<t_pod_type>(), "type must be trivially copyable");
return from_hex::to_buffer(as_mut_byte_span(s), hex_str);
}
//----------------------------------------------------------------------------
@@ -135,6 +135,13 @@ namespace http
http::url_content parsed{};
const bool r = parse_url(address, parsed);
CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address);
if (parsed.port == 0)
{
if (parsed.schema == "http")
parsed.port = 80;
else if (parsed.schema == "https")
parsed.port = 443;
}
set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), std::move(ssl_options));
return true;
}
+6 -2
View File
@@ -152,7 +152,11 @@ namespace epee
{
std::size_t space_needed = 0;
for (const auto& source : sources)
{
if (std::numeric_limits<std::size_t>::max() - space_needed < source.size())
throw std::bad_alloc{};
space_needed += source.size();
}
if (space_needed)
{
@@ -162,9 +166,9 @@ namespace epee
for (const auto& source : sources)
{
assert(source.size() <= out.size()); // see check above
std::memcpy(out.data(), source.data(), source.size());
if (out.remove_prefix(source.size()) < source.size())
throw std::bad_alloc{}; // size_t overflow on space_needed
out.remove_prefix(source.size());
}
storage_ = std::move(storage);
}
+6 -12
View File
@@ -46,12 +46,6 @@
// TODO:
#include "net/network_throttle-detail.hpp"
#if BOOST_VERSION >= 107000
#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context())
#else
#define GET_IO_SERVICE(s) ((s).get_io_service())
#endif
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.conn"
@@ -127,12 +121,12 @@ connection_basic_pimpl::connection_basic_pimpl(const std::string &name) : m_thro
int connection_basic_pimpl::m_default_tos;
// methods:
connection_basic::connection_basic(boost::asio::ip::tcp::socket&& sock, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support)
connection_basic::connection_basic(boost::asio::io_context &io_context, boost::asio::ip::tcp::socket&& sock, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support)
:
m_state(std::move(state)),
mI( new connection_basic_pimpl("peer") ),
strand_(GET_IO_SERVICE(sock)),
socket_(GET_IO_SERVICE(sock), get_context(m_state.get())),
strand_(io_context),
socket_(io_context, get_context(m_state.get())),
m_want_close_connection(false),
m_was_shutdown(false),
m_is_multithreaded(false),
@@ -152,12 +146,12 @@ connection_basic::connection_basic(boost::asio::ip::tcp::socket&& sock, std::sha
_note("Spawned connection #"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_state->sock_count);
}
connection_basic::connection_basic(boost::asio::io_service &io_service, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support)
connection_basic::connection_basic(boost::asio::io_context &io_context, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support)
:
m_state(std::move(state)),
mI( new connection_basic_pimpl("peer") ),
strand_(io_service),
socket_(io_service, get_context(m_state.get())),
strand_(io_context),
socket_(io_context, get_context(m_state.get())),
m_want_close_connection(false),
m_was_shutdown(false),
m_is_multithreaded(false),
+3 -2
View File
@@ -176,11 +176,12 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s
std::vector<boost::filesystem::path> found_files;
const boost::filesystem::directory_iterator end_itr;
const boost::filesystem::path filename_base_path(filename_base);
const std::string filename_base_name = filename_base_path.filename().string();
const boost::filesystem::path parent_path = filename_base_path.has_parent_path() ? filename_base_path.parent_path() : ".";
for (boost::filesystem::directory_iterator iter(parent_path); iter != end_itr; ++iter)
{
const std::string filename = iter->path().string();
if (filename.size() >= filename_base.size() && std::memcmp(filename.data(), filename_base.data(), filename_base.size()) == 0)
const std::string filename = iter->path().filename().string();
if (filename.size() >= filename_base_name.size() && std::memcmp(filename.data(), filename_base_name.data(), filename_base_name.size()) == 0)
{
found_files.push_back(iter->path());
}
+29 -20
View File
@@ -4,22 +4,38 @@ namespace epee
{
namespace net_utils
{
namespace
{
struct new_connection
{
boost::promise<boost::asio::ip::tcp::socket> result_;
boost::asio::ip::tcp::socket socket_;
template<typename T>
explicit new_connection(T&& executor)
: result_(), socket_(std::forward<T>(executor))
{}
};
}
boost::unique_future<boost::asio::ip::tcp::socket>
direct_connect::operator()(const std::string& addr, const std::string& port, boost::asio::steady_timer& timeout) const
{
// Get a list of endpoints corresponding to the server name.
//////////////////////////////////////////////////////////////////////////
boost::asio::ip::tcp::resolver resolver(GET_IO_SERVICE(timeout));
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
boost::asio::ip::tcp::resolver resolver(MONERO_GET_EXECUTOR(timeout));
bool try_ipv6 = false;
boost::asio::ip::tcp::resolver::iterator iterator;
boost::asio::ip::tcp::resolver::iterator end;
boost::asio::ip::tcp::resolver::results_type results{};
boost::system::error_code resolve_error;
try
{
iterator = resolver.resolve(query, resolve_error);
if(iterator == end) // Documentation states that successful call is guaranteed to be non-empty
results = resolver.resolve(
boost::asio::ip::tcp::v4(), addr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error
);
if (results.empty())
{
// if IPv4 resolution fails, try IPv6. Unintentional outgoing IPv6 connections should only
// be possible if for some reason a hostname was given and that hostname fails IPv4 resolution,
@@ -37,27 +53,20 @@ namespace net_utils
}
try_ipv6 = true;
}
if (try_ipv6)
{
boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
iterator = resolver.resolve(query6);
if (iterator == end)
results = resolver.resolve(
boost::asio::ip::tcp::v6(), addr, port, boost::asio::ip::tcp::resolver::canonical_name
);
if (results.empty())
throw boost::system::system_error{boost::asio::error::fault, "Failed to resolve " + addr};
}
//////////////////////////////////////////////////////////////////////////
struct new_connection
{
boost::promise<boost::asio::ip::tcp::socket> result_;
boost::asio::ip::tcp::socket socket_;
explicit new_connection(boost::asio::io_service& io_service)
: result_(), socket_(io_service)
{}
};
const auto shared = std::make_shared<new_connection>(GET_IO_SERVICE(timeout));
const auto shared = std::make_shared<new_connection>(MONERO_GET_EXECUTOR(timeout));
timeout.async_wait([shared] (boost::system::error_code error)
{
if (error != boost::system::errc::operation_canceled && shared && shared->socket_.is_open())
@@ -66,7 +75,7 @@ namespace net_utils
shared->socket_.close();
}
});
shared->socket_.async_connect(*iterator, [shared] (boost::system::error_code error)
shared->socket_.async_connect(*results.begin(), [shared] (boost::system::error_code error)
{
if (shared)
{
+15 -7
View File
@@ -29,6 +29,7 @@
#include <string.h>
#include <thread>
#include <boost/asio/post.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/cerrno.hpp>
#include <boost/filesystem/operations.hpp>
@@ -45,6 +46,13 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.ssl"
#if BOOST_VERSION >= 107300
#define MONERO_HOSTNAME_VERIFY boost::asio::ssl::host_name_verification
#else
#define MONERO_HOSTNAME_VERIFY boost::asio::ssl::rfc2818_verification
#endif
// openssl genrsa -out /tmp/KEY 4096
// openssl req -new -key /tmp/KEY -out /tmp/REQ
// openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
@@ -526,7 +534,7 @@ void ssl_options_t::configure(
// preverified means it passed system or user CA check. System CA is never loaded
// when fingerprints are whitelisted.
const bool verified = preverified &&
(verification != ssl_verification_t::system_ca || host.empty() || boost::asio::ssl::rfc2818_verification(host)(preverified, ctx));
(verification != ssl_verification_t::system_ca || host.empty() || MONERO_HOSTNAME_VERIFY(host)(preverified, ctx));
if (!verified && !has_fingerprint(ctx))
{
@@ -544,6 +552,7 @@ void ssl_options_t::configure(
}
bool ssl_options_t::handshake(
boost::asio::io_context& io_context,
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket,
boost::asio::ssl::stream_base::handshake_type type,
boost::asio::const_buffer buffer,
@@ -555,12 +564,11 @@ bool ssl_options_t::handshake(
auto start_handshake = [&]{
using ec_t = boost::system::error_code;
using timer_t = boost::asio::steady_timer;
using strand_t = boost::asio::io_service::strand;
using strand_t = boost::asio::io_context::strand;
using socket_t = boost::asio::ip::tcp::socket;
auto &io_context = GET_IO_SERVICE(socket);
if (io_context.stopped())
io_context.reset();
io_context.restart();
strand_t strand(io_context);
timer_t deadline(io_context, timeout);
@@ -595,13 +603,13 @@ bool ssl_options_t::handshake(
state.result = ec;
if (!state.cancel_handshake) {
state.cancel_timer = true;
ec_t ec;
deadline.cancel(ec);
deadline.cancel();
}
};
deadline.async_wait(on_timer);
strand.post(
boost::asio::post(
strand,
[&]{
socket.async_handshake(
type,
+20 -8
View File
@@ -46,7 +46,7 @@
#include "misc_log_ex.h"
#include <boost/chrono.hpp>
#include "misc_language.h"
#include <sstream>
#include <fstream>
#include <iomanip>
#include <algorithm>
@@ -186,6 +186,23 @@ void network_throttle::handle_trafic_exact(size_t packet_size)
_handle_trafic_exact(packet_size, packet_size);
}
namespace
{
struct output_history
{
const boost::circular_buffer< network_throttle::packet_info >& history;
};
std::ostream& operator<<(std::ostream& out, const output_history& source)
{
out << '[';
for (auto sample: source.history)
out << sample.m_size << ' ';
out << ']';
return out;
}
}
void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_size)
{
tick();
@@ -196,14 +213,11 @@ void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_s
m_total_packets++;
m_total_bytes += packet_size;
std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends;
std::string history_str = oss.str();
MTRACE("Throttle " << m_name << ": packet of ~"<<packet_size<<"b " << " (from "<<orginal_size<<" b)"
<< " Speed AVG=" << std::setw(4) << ((long int)(cts .average/1024)) <<"[w="<<cts .window<<"]"
<< " " << std::setw(4) << ((long int)(cts2.average/1024)) <<"[w="<<cts2.window<<"]"
<<" / " << " Limit="<< ((long int)(m_target_speed/1024)) <<" KiB/sec "
<< " " << history_str
<< " " << output_history{m_history}
);
}
@@ -289,8 +303,6 @@ void network_throttle::calculate_times(size_t packet_size, calculate_times_struc
}
if (dbg) {
std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends;
std::string history_str = oss.str();
MTRACE((cts.delay > 0 ? "SLEEP" : "")
<< "dbg " << m_name << ": "
<< "speed is A=" << std::setw(8) <<cts.average<<" vs "
@@ -300,7 +312,7 @@ void network_throttle::calculate_times(size_t packet_size, calculate_times_struc
<< "E="<< std::setw(8) << E << " (Enow="<<std::setw(8)<<Enow<<") "
<< "M=" << std::setw(8) << M <<" W="<< std::setw(8) << cts.window << " "
<< "R=" << std::setw(8) << cts.recomendetDataSize << " Wgood" << std::setw(8) << Wgood << " "
<< "History: " << std::setw(8) << history_str << " "
<< "History: " << std::setw(8) << output_history{m_history} << " "
<< "m_last_sample_time=" << std::setw(8) << m_last_sample_time
);
+26 -55
View File
@@ -38,9 +38,12 @@
#include <cstdlib>
#include <string>
#include <type_traits>
#include <system_error>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/filesystem.hpp>
#include "misc_log_ex.h"
#include "storages/parserse_base_utils.h"
#include "hex.h"
@@ -157,46 +160,20 @@ namespace string_tools
return pname;
}
#endif
bool set_module_name_and_folder(const std::string& path_to_process_)
{
std::string path_to_process = path_to_process_;
void set_module_name_and_folder(const std::string& path_to_process_)
{
boost::filesystem::path path_to_process = path_to_process_;
#ifdef _WIN32
path_to_process = get_current_module_path();
#endif
std::string::size_type a = path_to_process.rfind( '\\' );
if(a == std::string::npos )
{
a = path_to_process.rfind( '/' );
}
if ( a != std::string::npos )
{
get_current_module_name() = path_to_process.substr(a+1, path_to_process.size());
get_current_module_folder() = path_to_process.substr(0, a);
return true;
}else
return false;
}
get_current_module_name() = path_to_process.filename().string();
get_current_module_folder() = path_to_process.parent_path().string();
}
//----------------------------------------------------------------------------
bool trim_left(std::string& str)
{
for(std::string::iterator it = str.begin(); it!= str.end() && isspace(static_cast<unsigned char>(*it));)
str.erase(str.begin());
return true;
}
//----------------------------------------------------------------------------
bool trim_right(std::string& str)
{
for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast<unsigned char>(*it));)
str.erase( --((it++).base()));
return true;
}
//----------------------------------------------------------------------------
std::string pad_string(std::string s, size_t n, char c, bool prepend)
{
if (s.size() < n)
@@ -209,28 +186,22 @@ namespace string_tools
return s;
}
std::string get_extension(const std::string& str)
{
std::string res;
std::string::size_type pos = str.rfind('.');
if(std::string::npos == pos)
return res;
res = str.substr(pos+1, str.size()-pos);
return res;
}
//----------------------------------------------------------------------------
std::string cut_off_extension(const std::string& str)
{
std::string res;
std::string::size_type pos = str.rfind('.');
if(std::string::npos == pos)
return str;
std::string get_extension(const std::string& str)
{
std::string ext_with_dot = boost::filesystem::path(str).extension().string();
if (ext_with_dot.empty())
return {};
return ext_with_dot.erase(0, 1);
}
//----------------------------------------------------------------------------
std::string cut_off_extension(const std::string& str)
{
return boost::filesystem::path(str).replace_extension("").string();
}
res = str.substr(0, pos);
return res;
}
//----------------------------------------------------------------------------
#ifdef _WIN32
std::wstring utf8_to_utf16(const std::string& str)
{
+1
View File
@@ -70,3 +70,4 @@ add_subdirectory(db_drivers)
add_subdirectory(easylogging++)
add_subdirectory(qrcodegen)
add_subdirectory(randomx EXCLUDE_FROM_ALL)
add_subdirectory(mx25519)
+4
View File
@@ -0,0 +1,4 @@
# Boost Serialization Vendoring
* optional_shim.hpp - Compare to file include/boost/serialization/optional.hpp in the Boost serialization repository, checked out at commit ff0a5f9f4a4040c6f992581245fed48d9634acfe. This is the newest version of the file at time of writing. Once minimum required Boost version is >= 1.84, then we can drop this header.
* std_variant_shim.hpp - Compare to file include/boost/serialization/std_variant.hpp in the Boost serialization repository, checked out at commit 2805bc3faa8f8fb7ce80ec518158563c5fcf26f6. This is the newest version of the file at time of writing. Once minimum required Boost version includes commit 042c590a7ac84933413243d8e9c3b06a791803a7 in the multiprecision repository, then we can drop this header. Commit 042c590a7ac84933413243d8e9c3b06a791803a7 removes a namespace clash in Boost's multiprecision library with the std_variant.hpp header.
+177
View File
@@ -0,0 +1,177 @@
#include <boost/version.hpp>
#ifndef BOOST_VERSION
#error "Missing Boost version"
#endif
#if BOOST_VERSION >= 108400
#include <boost/serialization/optional.hpp>
#else
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// (C) Copyright 2002-4 Pavel Vozenilek .
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// Provides non-intrusive serialization for boost::optional.
#ifndef BOOST_SERIALIZATION_OPTIONAL_HPP
#define BOOST_SERIALIZATION_OPTIONAL_HPP
#if defined(_MSC_VER)
# pragma once
#endif
#include <boost/config.hpp>
#include <boost/optional.hpp>
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
#include <optional>
#endif
#include <boost/serialization/item_version_type.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/serialization/detail/is_default_constructible.hpp>
// function specializations must be defined in the appropriate
// namespace - boost::serialization
namespace boost {
namespace serialization {
namespace detail {
// OT is of the form optional<T>
template<class Archive, class OT>
void save_impl(
Archive & ar,
const OT & ot
){
// It is an inherent limitation to the serialization of optional.hpp
// that the underlying type must be either a pointer or must have a
// default constructor. It's possible that this could change sometime
// in the future, but for now, one will have to work around it. This can
// be done by serialization the optional<T> as optional<T *>
#ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS
BOOST_STATIC_ASSERT(
boost::serialization::detail::is_default_constructible<typename OT::value_type>::value
|| boost::is_pointer<typename OT::value_type>::value
);
#endif
const bool tflag(ot);
ar << boost::serialization::make_nvp("initialized", tflag);
if (tflag){
ar << boost::serialization::make_nvp("value", *ot);
}
}
// OT is of the form optional<T>
template<class Archive, class OT>
void load_impl(
Archive & ar,
OT & ot,
const unsigned int version
){
bool tflag;
ar >> boost::serialization::make_nvp("initialized", tflag);
if(! tflag){
ot.reset();
return;
}
if(0 == version){
boost::serialization::item_version_type item_version(0);
auto library_version(
ar.get_library_version()
);
if(decltype(library_version)(3) < library_version){
ar >> BOOST_SERIALIZATION_NVP(item_version);
}
}
typename OT::value_type t;
ar >> boost::serialization::make_nvp("value",t);
ot = t;
}
} // detail
template<class Archive, class T>
void save(
Archive & ar,
const boost::optional< T > & ot,
const unsigned int /*version*/
){
detail::save_impl(ar, ot);
}
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
template<class Archive, class T>
void save(
Archive & ar,
const std::optional< T > & ot,
const unsigned int /*version*/
){
detail::save_impl(ar, ot);
}
#endif
template<class Archive, class T>
void load(
Archive & ar,
boost::optional< T > & ot,
const unsigned int version
){
detail::load_impl(ar, ot, version);
}
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
template<class Archive, class T>
void load(
Archive & ar,
std::optional< T > & ot,
const unsigned int version
){
detail::load_impl(ar, ot, version);
}
#endif
template<class Archive, class T>
void serialize(
Archive & ar,
boost::optional< T > & ot,
const unsigned int version
){
boost::serialization::split_free(ar, ot, version);
}
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
template<class Archive, class T>
void serialize(
Archive & ar,
std::optional< T > & ot,
const unsigned int version
){
boost::serialization::split_free(ar, ot, version);
}
#endif
template<class T>
struct version<boost::optional<T> >{
BOOST_STATIC_CONSTANT(int, value = 1);
};
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
template<class T>
struct version<std::optional<T> >{
BOOST_STATIC_CONSTANT(int, value = 1);
};
#endif
} // serialization
} // boost
#endif // BOOST_SERIALIZATION_OPTIONAL_HPP
#endif //BOOST_VERSION < 108400
+204
View File
@@ -0,0 +1,204 @@
#ifndef BOOST_SERIALIZATION_STD_VARIANT_HPP
#define BOOST_SERIALIZATION_STD_VARIANT_HPP
// MS compatible compilers support #pragma once
#if defined(_MSC_VER)
# pragma once
#endif
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// variant.hpp - non-intrusive serialization of variant types
//
// copyright (c) 2019 Samuel Debionne, ESRF
//
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org for updates, documentation, and revision history.
//
// Widely inspired form boost::variant serialization
//
#include <boost/serialization/throw_exception.hpp>
#include <variant>
#include <boost/archive/archive_exception.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
namespace boost {
namespace serialization {
template<class Archive>
struct std_variant_save_visitor
{
std_variant_save_visitor(Archive& ar) :
m_ar(ar)
{}
template<class T>
void operator()(T const & value) const
{
m_ar << BOOST_SERIALIZATION_NVP(value);
}
private:
Archive & m_ar;
};
template<class Archive>
struct std_variant_load_visitor
{
std_variant_load_visitor(Archive& ar) :
m_ar(ar)
{}
template<class T>
void operator()(T & value) const
{
m_ar >> BOOST_SERIALIZATION_NVP(value);
}
private:
Archive & m_ar;
};
template<class Archive, class ...Types>
void save(
Archive & ar,
std::variant<Types...> const & v,
unsigned int /*version*/
){
const std::size_t which = v.index();
ar << BOOST_SERIALIZATION_NVP(which);
std_variant_save_visitor<Archive> visitor(ar);
std::visit(visitor, v);
}
// Minimalist metaprogramming for handling parameter pack
namespace mp_shim {
namespace detail {
template <typename Seq>
struct front_impl;
template <template <typename...> class Seq, typename T, typename... Ts>
struct front_impl<Seq<T, Ts...>> {
using type = T;
};
template <typename Seq>
struct pop_front_impl;
template <template <typename...> class Seq, typename T, typename... Ts>
struct pop_front_impl<Seq<T, Ts...>> {
using type = Seq<Ts...>;
};
} //namespace detail
template <typename... Ts>
struct typelist {};
template <typename Seq>
using front = typename detail::front_impl<Seq>::type;
template <typename Seq>
using pop_front = typename detail::pop_front_impl<Seq>::type;
} // namespace mp_shim
template<std::size_t N, class Seq>
struct variant_impl_shim
{
template<class Archive, class V>
static void load (
Archive & ar,
std::size_t which,
V & v,
const unsigned int version
){
if(which == 0){
// note: A non-intrusive implementation (such as this one)
// necessary has to copy the value. This wouldn't be necessary
// with an implementation that de-serialized to the address of the
// aligned storage included in the variant.
using type = mp_shim::front<Seq>;
type value;
ar >> BOOST_SERIALIZATION_NVP(value);
v = std::move(value);
type * new_address = & std::get<type>(v);
ar.reset_object_address(new_address, & value);
return;
}
//typedef typename mpl::pop_front<S>::type type;
using types = mp_shim::pop_front<Seq>;
variant_impl_shim<N - 1, types>::load(ar, which - 1, v, version);
}
};
template<class Seq>
struct variant_impl_shim<0, Seq>
{
template<class Archive, class V>
static void load (
Archive & /*ar*/,
std::size_t /*which*/,
V & /*v*/,
const unsigned int /*version*/
){}
};
template<class Archive, class... Types>
void load(
Archive & ar,
std::variant<Types...>& v,
const unsigned int version
){
std::size_t which;
ar >> BOOST_SERIALIZATION_NVP(which);
if(which >= sizeof...(Types))
// this might happen if a type was removed from the list of variant types
boost::serialization::throw_exception(
boost::archive::archive_exception(
boost::archive::archive_exception::unsupported_version
)
);
variant_impl_shim<sizeof...(Types), mp_shim::typelist<Types...>>::load(ar, which, v, version);
}
template<class Archive,class... Types>
inline void serialize(
Archive & ar,
std::variant<Types...> & v,
const unsigned int file_version
){
split_free(ar,v,file_version);
}
// Specialization for std::monostate
template<class Archive>
void serialize(Archive &, std::monostate &, const unsigned int /*version*/)
{}
} // namespace serialization
} // namespace boost
//template<typename T0_, BOOST_VARIANT_ENUM_SHIFTED_PARAMS(typename T)>
#include <boost/serialization/tracking.hpp>
namespace boost {
namespace serialization {
template<class... Types>
struct tracking_level<
std::variant<Types...>
>{
typedef mpl::integral_c_tag tag;
typedef mpl::int_< ::boost::serialization::track_always> type;
BOOST_STATIC_CONSTANT(int, value = type::value);
};
} // namespace serialization
} // namespace boost
#endif //BOOST_SERIALIZATION_VARIANT_HPP
+3
View File
@@ -83,6 +83,8 @@ endfunction ()
include(Version)
monero_add_library(version SOURCES ${CMAKE_BINARY_DIR}/version.cpp DEPENDS genversion)
add_subdirectory(carrot_core)
add_subdirectory(carrot_impl)
add_subdirectory(common)
add_subdirectory(crypto)
add_subdirectory(ringct)
@@ -97,6 +99,7 @@ add_subdirectory(hardforks)
add_subdirectory(blockchain_db)
add_subdirectory(mnemonics)
add_subdirectory(rpc)
add_subdirectory(seraphis_crypto)
if(NOT IOS)
add_subdirectory(serialization)
endif()
+16 -12
View File
@@ -34,6 +34,7 @@
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "profile_tools.h"
#include "ringct/rctOps.h"
#include "ringct/rctSigs.h"
#include "lmdb/db_lmdb.h"
@@ -240,7 +241,10 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
if (miner_tx && tx.version == 2)
{
cryptonote::tx_out vout = tx.vout[i];
rct::key commitment = rct::zeroCommit(vout.amount);
// TODO: avoid multiple expensive zeroCommitVartime call here + get_outs_by_last_locked_block + ver_non_input_consensus
rct::key commitment;
if (!rct::getCommitment(tx, i, commitment))
throw std::runtime_error("Failed to get miner tx commitment, aborting");
vout.amount = 0;
amount_output_indices[i] = add_output(tx_hash, vout, i, unlock_time,
&commitment);
@@ -296,7 +300,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
blobdata protocol_bd = tx_to_blob(blk.protocol_tx);
add_transaction(blk_hash, std::make_pair(blk.protocol_tx, blobdata_ref(protocol_bd)));
if (blk.miner_tx.version == 2)
if (blk.miner_tx.version >= 2)
{
num_rct_outs += blk.miner_tx.vout.size();
@@ -311,7 +315,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
std::map<std::string, int64_t> slippage_counts;
uint64_t audit_total = 0, yield_total = 0;
if (blk.protocol_tx.version == 2)
if (blk.protocol_tx.version >= 2)
{
num_rct_outs += blk.protocol_tx.vout.size();
@@ -356,16 +360,16 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
slippage_counts[asset_type] = 0;
slippage_counts[asset_type] += tx.first.amount_burnt;
}
}
// Is this an AUDIT TX?
if (tx.first.type == cryptonote::transaction_type::AUDIT) {
audit_total += tx.first.amount_burnt;
}
// Is this a STAKE TX?
if (tx.first.type == cryptonote::transaction_type::STAKE) {
yield_total += tx.first.amount_burnt;
}
// Is this an AUDIT TX?
if (tx.first.type == cryptonote::transaction_type::AUDIT) {
audit_total += tx.first.amount_burnt;
}
// Is this a STAKE TX?
if (tx.first.type == cryptonote::transaction_type::STAKE) {
yield_total += tx.first.amount_burnt;
}
++tx_i;
}
+73 -5
View File
@@ -29,14 +29,18 @@
#include "db_lmdb.h"
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/format.hpp>
#include <boost/circular_buffer.hpp>
#include <memory> // std::unique_ptr
#include <cstring> // memcpy
#ifdef WIN32
#include <winioctl.h>
#endif
#include "string_tools.h"
#include "file_io_utils.h"
#include "common/util.h"
#include "common/pruning.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
@@ -2083,6 +2087,54 @@ BlockchainLMDB::BlockchainLMDB(bool batch_transactions): BlockchainDB()
m_hardfork = nullptr;
}
#ifdef WIN32
static bool disable_ntfs_compression(const boost::filesystem::path& filepath)
{
DWORD file_attributes = ::GetFileAttributesW(filepath.c_str());
if (file_attributes == INVALID_FILE_ATTRIBUTES)
{
MERROR("Failed to get " << filepath.string() << " file attributes. Error: " << ::GetLastError());
return false;
}
if (!(file_attributes & FILE_ATTRIBUTE_COMPRESSED))
return true; // not compressed
LOG_PRINT_L1("Disabling NTFS compression for " << filepath.string());
HANDLE file_handle = ::CreateFileW(
filepath.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
boost::filesystem::is_directory(filepath) ? FILE_FLAG_BACKUP_SEMANTICS : 0, // Needed to open handles to directories
nullptr
);
if (file_handle == INVALID_HANDLE_VALUE)
{
MERROR("Failed to open handle: " << filepath.string() << ". Error: " << ::GetLastError());
return false;
}
USHORT compression_state = COMPRESSION_FORMAT_NONE;
DWORD bytes_returned;
BOOL ok = ::DeviceIoControl(
file_handle,
FSCTL_SET_COMPRESSION,
&compression_state,
sizeof(compression_state),
nullptr,
0,
&bytes_returned,
nullptr
);
::CloseHandle(file_handle);
return ok;
}
#endif
void BlockchainLMDB::open(const std::string& filename, const int db_flags)
{
int result;
@@ -2109,6 +2161,18 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
throw DB_ERROR("Database could not be opened");
}
#ifdef WIN32
// ensure NTFS compression is disabled on the directory and database file to avoid corruption of the blockchain
if (!disable_ntfs_compression(filename))
LOG_PRINT_L0("Failed to disable NTFS compression on folder: " << filename << ". Error: " << ::GetLastError());
boost::filesystem::path datafile(filename);
datafile /= CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
if (!boost::filesystem::exists(datafile))
boost::filesystem::ofstream(datafile).close(); // create the file to see if NTFS compression is enabled beforehand
if (!disable_ntfs_compression(datafile))
throw DB_ERROR("Database file is NTFS compressed and compression could not be disabled");
#endif
boost::optional<bool> is_hdd_result = tools::is_hdd(filename.c_str());
if (is_hdd_result)
{
@@ -3778,6 +3842,11 @@ std::map<std::string,uint64_t> BlockchainLMDB::get_circulating_supply() const
//amount += m_coinbase;
}
if (amount < 0) {
// Negative number can't be converted to a 64-bit UINT, so return 0, but retain -ve number privately
LOG_PRINT_L2("BlockchainLMDB::" << __func__ << " - supply of " << currency_label << " is negative (" << amount << ") but outputting zero");
amount = 0;
}
circulating_supply[currency_label] = amount.convert_to<uint64_t>();
}
@@ -5467,12 +5536,11 @@ bool BlockchainLMDB::is_read_only() const
uint64_t BlockchainLMDB::get_database_size() const
{
uint64_t size = 0;
boost::filesystem::path datafile(m_folder);
datafile /= CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
if (!epee::file_io_utils::get_file_size(datafile.string(), size))
size = 0;
return size;
boost::system::error_code ec{};
const boost::uintmax_t size = boost::filesystem::file_size(datafile, ec);
return (ec ? 0 : static_cast<uint64_t>(size));
}
#define RENAME_DB(name) do { \
+37 -43
View File
@@ -142,23 +142,20 @@ set(blockchain_scanner_private_headers)
monero_private_headers(blockchain_scanner
${blockchain_scanner_private_headers})
if (BUILD_TAG)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
set(blockchain_audit_sources
blockchain_audit.cpp
threadpool_boost.cpp
)
set(blockchain_audit_private_headers
threadpool_boost.h
)
monero_private_headers(blockchain_audit
${blockchain_audit_private_headers})
else()
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
set(blockchain_audit_sources
blockchain_audit.cpp
threadpool_boost.cpp
)
set(blockchain_audit_private_headers
threadpool_boost.h
)
monero_private_headers(blockchain_audit
${blockchain_audit_private_headers})
else()
message(STATUS "blockchain_audit.cpp not found - not building the audit tool")
endif()
message(STATUS "blockchain_audit.cpp not found - not building the audit tool")
endif()
monero_add_executable(blockchain_import
@@ -330,36 +327,33 @@ set_property(TARGET blockchain_scanner
OUTPUT_NAME "salvium-blockchain-scanner")
install(TARGETS blockchain_scanner DESTINATION bin)
if (BUILD_TAG)
else()
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
monero_add_executable(blockchain_audit
${blockchain_audit_sources}
${blockchain_audit_private_headers})
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
monero_add_executable(blockchain_audit
${blockchain_audit_sources}
${blockchain_audit_private_headers})
target_include_directories(blockchain_audit PRIVATE /usr/include/mysql-cppconn/jdbc)
target_include_directories(blockchain_audit PRIVATE /usr/include/mysql-cppconn/jdbc)
target_link_libraries(blockchain_audit
PRIVATE
wallet
crypto
cncrypto
cryptonote_core
blockchain_db
version
epee
mysqlcppconn
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
target_link_libraries(blockchain_audit
PRIVATE
wallet
crypto
cncrypto
cryptonote_core
blockchain_db
version
epee
mysqlcppconn
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET blockchain_audit
PROPERTY
OUTPUT_NAME "salvium-blockchain-audit")
install(TARGETS blockchain_audit DESTINATION bin)
endif()
set_property(TARGET blockchain_audit
PROPERTY
OUTPUT_NAME "salvium-blockchain-audit")
install(TARGETS blockchain_audit DESTINATION bin)
endif()
monero_add_executable(blockchain_stats
@@ -220,8 +220,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
uint32_t txhr[24] = {0};
unsigned int i;
const std::map<uint8_t, std::pair<std::string, std::string>> audit_hard_forks = get_config(net_type).AUDIT_HARD_FORKS;
const uint64_t audit_lock_period = get_config(net_type).AUDIT_LOCK_PERIOD;
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;
for (uint64_t h = block_start; h < block_stop; ++h)
{
@@ -300,7 +299,7 @@ skip:
}
protocol_tx_assets.insert(asset_type);
if (protocol_tx_vout.amount > 25000000000000) {
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.protocol_tx.hash << "" << delimiter << "large protocol TX amount detected from height " << (h-audit_lock_period) << delimiter << "amount:" << protocol_tx_vout.amount << std::endl;
//std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.protocol_tx.hash << "" << delimiter << "large protocol TX amount detected from height " << (h-audit_lock_period) << delimiter << "amount:" << protocol_tx_vout.amount << std::endl;
}
crypto::public_key key;
cryptonote::get_output_public_key(protocol_tx_vout, key);
Binary file not shown.
+65
View File
@@ -0,0 +1,65 @@
# Copyright (c) 2024, The Monero Project
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of
# conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
# of conditions and the following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(carrot_core_sources
account_secrets.cpp
address_utils.cpp
carrot_enote_types.cpp
core_types.cpp
destination.cpp
device_ram_borrowed.cpp
enote_utils.cpp
hash_functions.cpp
lazy_amount_commitment.cpp
output_set_finalization.cpp
payment_proposal.cpp
scan.cpp
scan_unsafe.cpp
sparc.cpp)
monero_find_all_headers(carrot_core_headers, "${CMAKE_CURRENT_SOURCE_DIR}")
monero_add_library(carrot_core
${carrot_core_sources}
${carrot_core_headers})
target_link_libraries(carrot_core
PUBLIC
cncrypto
epee
mx25519_static
ringct_basic
seraphis_crypto
PRIVATE
${EXTRA_LIBRARIES})
target_include_directories(carrot_core
PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}"
PRIVATE
${Boost_INCLUDE_DIRS})
+102
View File
@@ -0,0 +1,102 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//paired header
#include "account_secrets.h"
//local headers
#include "config.h"
#include "crypto/generators.h"
#include "hash_functions.h"
#include "ringct/rctOps.h"
#include "transcript_fixed.h"
//third party headers
//standard headers
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_provespend_key(const crypto::secret_key &s_master,
crypto::secret_key &k_prove_spend_out)
{
// k_ps = H_n(s_m)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_PROVE_SPEND_KEY>();
derive_scalar(transcript.data(), transcript.size(), &s_master, to_bytes(k_prove_spend_out));
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_viewbalance_secret(const crypto::secret_key &s_master,
crypto::secret_key &s_view_balance_out)
{
// s_vb = H_32(s_m)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_VIEW_BALANCE_SECRET>();
derive_bytes_32(transcript.data(), transcript.size(), &s_master, to_bytes(s_view_balance_out));
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_generateimage_key(const crypto::secret_key &s_view_balance,
crypto::secret_key &k_generate_image_out)
{
// k_gi = H_n(s_vb)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_GENERATE_IMAGE_KEY>();
derive_scalar(transcript.data(), transcript.size(), &s_view_balance, to_bytes(k_generate_image_out));
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_viewincoming_key(const crypto::secret_key &s_view_balance,
crypto::secret_key &k_view_out)
{
// k_v = H_n(s_vb)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_INCOMING_VIEW_KEY>();
derive_scalar(transcript.data(), transcript.size(), &s_view_balance, to_bytes(k_view_out));
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_generateaddress_secret(const crypto::secret_key &s_view_balance,
crypto::secret_key &s_generate_address_out)
{
// s_ga = H_32(s_vb)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_GENERATE_ADDRESS_SECRET>();
derive_bytes_32(transcript.data(), transcript.size(), &s_view_balance, to_bytes(s_generate_address_out));
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_spend_pubkey(const crypto::secret_key &k_generate_image,
const crypto::secret_key &k_prove_spend,
crypto::public_key &spend_pubkey_out)
{
// k_ps T
rct::key tmp;
rct::scalarmultKey(tmp, rct::pk2rct(crypto::get_T()), rct::sk2rct(k_prove_spend));
// K_s = k_gi G + k_ps T
rct::addKeys1(tmp, rct::sk2rct(k_generate_image), tmp);
spend_pubkey_out = rct::rct2pk(tmp);
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+103
View File
@@ -0,0 +1,103 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
////
// Core implementation details for making Carrot privkeys, secrets, and pubkeys.
// - Carrot is a specification for FCMP-RingCT compatible addressing
//
// references:
// * https://github.com/jeffro256/carrot/blob/master/carrot.md
///
#pragma once
//local headers
#include "crypto/crypto.h"
//third party headers
//standard headers
//forward declarations
namespace carrot
{
/**
* brief: make_carrot_provespend_key - prove-spend key, for signing input proofs to spend enotes
* k_ps = H_n(s_m)
* param: s_master - s_m
* outparam: k_prove_spend_out - k_ps
*/
void make_carrot_provespend_key(const crypto::secret_key &s_master,
crypto::secret_key &k_prove_spend_out);
/**
* brief: make_carrot_viewbalance_secret - view-balance secret, for viewing all balance information
* s_vb = H_n(s_m)
* param: s_master - s_m
* outparam: s_view_balance_out - s_vb
*/
void make_carrot_viewbalance_secret(const crypto::secret_key &s_master,
crypto::secret_key &s_view_balance_out);
/**
* brief: make_carrot_generateimage_key - generate-image key, for identifying enote spends
* k_gi = H_n(s_vb)
* param: s_view_balance - s_vb
* outparam: k_generate_image_out - k_gi
*/
void make_carrot_generateimage_key(const crypto::secret_key &s_view_balance,
crypto::secret_key &k_generate_image_out);
/**
* brief: make_carrot_viewincoming_key - view-incoming key, for identifying received external enotes
* k_v = H_n(s_vb)
* param: s_view_balance - s_vb
* outparam: k_view_out - k_v
*/
void make_carrot_viewincoming_key(const crypto::secret_key &s_view_balance,
crypto::secret_key &k_view_out);
/**
* brief: make_carrot_generateaddress_secret - generate-address secret, for generating addresses
* s_ga = H_32(s_vb)
* param: s_view_balance - s_vb
* outparam: s_generate_address_out - s_ga
*/
void make_carrot_generateaddress_secret(const crypto::secret_key &s_view_balance,
crypto::secret_key &s_generate_address_out);
/**
* brief: make_carrot_spend_pubkey - base public spendkey for rerandomizable RingCT
* K_s = k_gi G + k_ps T
* param: k_generate_image - k_gi
* param: k_prove_spend - k_ps
* outparam: spend_pubkey_out - K_s
*/
void make_carrot_spend_pubkey(const crypto::secret_key &k_generate_image,
const crypto::secret_key &k_prove_spend,
crypto::public_key &spend_pubkey_out);
} //namespace carrot
+85
View File
@@ -0,0 +1,85 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//paired header
#include "address_utils.h"
//local headers
#include "config.h"
#include "hash_functions.h"
#include "ringct/rctOps.h"
#include "transcript_fixed.h"
//third party headers
//standard headers
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_index_extension_generator(const crypto::secret_key &s_generate_address,
const std::uint32_t j_major,
const std::uint32_t j_minor,
crypto::secret_key &address_generator_out)
{
// s^j_gen = H_32[s_ga](j_major, j_minor)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ADDRESS_INDEX_GEN>(j_major, j_minor);
derive_bytes_32(transcript.data(), transcript.size(), &s_generate_address, &address_generator_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_subaddress_scalar(const crypto::public_key &account_spend_pubkey,
const crypto::secret_key &s_address_generator,
const std::uint32_t j_major,
const std::uint32_t j_minor,
crypto::secret_key &subaddress_scalar_out)
{
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_SUBADDRESS_SCALAR>(
account_spend_pubkey, j_major, j_minor);
derive_scalar(transcript.data(), transcript.size(), &s_address_generator, subaddress_scalar_out.data);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_address_spend_pubkey(const crypto::public_key &account_spend_pubkey,
const crypto::secret_key &s_generate_address,
const std::uint32_t j_major,
const std::uint32_t j_minor,
crypto::public_key &address_spend_pubkey_out)
{
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
crypto::secret_key subaddress_scalar;
make_carrot_subaddress_scalar(account_spend_pubkey, s_generate_address, j_major, j_minor, subaddress_scalar);
// K^j_s = k^j_subscal * K_s
address_spend_pubkey_out = rct::rct2pk(rct::scalarmultKey(
rct::pk2rct(account_spend_pubkey), rct::sk2rct(subaddress_scalar)));
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+95
View File
@@ -0,0 +1,95 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Utilities for building carrot addresses.
#pragma once
//local headers
#include "crypto/crypto.h"
//third party headers
//standard headers
//forward declarations
namespace carrot
{
/**
* brief: is_main_address_index - determine whether j=(j_major, j_minor) represents the main address
*/
static constexpr bool is_main_address_index(const std::uint32_t j_major, const std::uint32_t j_minor)
{
return !(j_major || j_minor);
}
/**
* brief: make_carrot_index_extension_generator - s^j_gen
* s^j_gen = H_32[s_ga](j_major, j_minor)
* param: s_generate_address - s_ga
* param: j_major -
* param: j_minor -
* outparam: address_generator_out - s^j_gen
*/
void make_carrot_index_extension_generator(const crypto::secret_key &s_generate_address,
const std::uint32_t j_major,
const std::uint32_t j_minor,
crypto::secret_key &address_generator_out);
/**
* brief: make_carrot_address_privkey - d^j_a
* k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
* param: account_spend_pubkey - K_s = k_vb X + k_m U
* param: s_address_generator - s^j_gen
* param: j_major -
* param: j_minor -
* outparam: subaddress_scalar_out - k^j_subscal
*/
void make_carrot_subaddress_scalar(const crypto::public_key &account_spend_pubkey,
const crypto::secret_key &s_address_generator,
const std::uint32_t j_major,
const std::uint32_t j_minor,
crypto::secret_key &subaddress_scalar_out);
/**
* brief: make_carrot_address_spend_pubkey - K^j_s
* K^j_s = k^j_subscal * K_s
* param: account_spend_pubkey - K_s = k_gi G + k_ps U
* param: s_generate_address - s_ga
* param: j_major -
* param: j_minor -
* outparam: address_spend_pubkey_out - K^j_s
*/
void make_carrot_address_spend_pubkey(const crypto::public_key &account_spend_pubkey,
const crypto::secret_key &s_generate_address,
const std::uint32_t j_major,
const std::uint32_t j_minor,
crypto::public_key &address_spend_pubkey_out);
} //namespace carrot
+75
View File
@@ -0,0 +1,75 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Utilities for scanning carrot enotes
//paired header
#include "carrot_enote_types.h"
//local headers
//third party headers
//standard headers
/*
onetime address
// - amount commitment
// - encrypted amount
// - encrypted janus anchor
// - view tag
// - ephemeral pubkey
// - tx first key image*/
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const CarrotEnoteV1 &a, const CarrotEnoteV1 &b)
{
return a.onetime_address == b.onetime_address &&
a.amount_commitment == b.amount_commitment &&
a.amount_enc == b.amount_enc &&
a.anchor_enc == b.anchor_enc &&
a.view_tag == b.view_tag &&
a.tx_first_key_image == b.tx_first_key_image &&
a.return_enc == b.return_enc &&
a.asset_type == b.asset_type &&
memcmp(a.enote_ephemeral_pubkey.data, b.enote_ephemeral_pubkey.data, sizeof(mx25519_pubkey)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const CarrotCoinbaseEnoteV1 &a, const CarrotCoinbaseEnoteV1 &b)
{
return a.onetime_address == b.onetime_address &&
a.amount == b.amount &&
a.anchor_enc == b.anchor_enc &&
a.view_tag == b.view_tag &&
a.block_index == b.block_index &&
memcmp(a.enote_ephemeral_pubkey.data, b.enote_ephemeral_pubkey.data, sizeof(mx25519_pubkey)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+115
View File
@@ -0,0 +1,115 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Seraphis core types.
#pragma once
//local headers
#include "core_types.h"
#include "mx25519.h"
#include "ringct/rctTypes.h"
//third party headers
//standard headers
//forward declarations
namespace carrot
{
////
// CarrotEnoteV1
// - onetime address
// - amount commitment
// - encrypted amount
// - encrypted janus anchor
// - view tag
// - ephemeral pubkey
// - tx first key image
///
struct CarrotEnoteV1 final
{
/// K_o
crypto::public_key onetime_address;
/// C_a
rct::key amount_commitment;
/// asset type
std::string asset_type;
/// a_enc
encrypted_amount_t amount_enc;
/// anchor_enc
encrypted_janus_anchor_t anchor_enc;
/// return_enc
encrypted_return_pubkey_t return_enc;
/// view_tag
view_tag_t view_tag;
/// D_e
mx25519_pubkey enote_ephemeral_pubkey;
/// L_0
crypto::key_image tx_first_key_image;
};
/// equality operators
bool operator==(const CarrotEnoteV1 &a, const CarrotEnoteV1 &b);
static inline bool operator!=(const CarrotEnoteV1 &a, const CarrotEnoteV1 &b) { return !(a == b); }
////
// CarrotCoinbaseEnoteV1
// - onetime address
// - cleartext amount
// - encrypted janus anchor
// - view tag
// - ephemeral pubkey
// - block index
///
struct CarrotCoinbaseEnoteV1 final
{
/// K_o
crypto::public_key onetime_address;
/// a
rct::xmr_amount amount;
/// asset type
std::string asset_type;
/// anchor_enc
encrypted_janus_anchor_t anchor_enc;
/// view_tag
view_tag_t view_tag;
/// D_e
mx25519_pubkey enote_ephemeral_pubkey;
/// block_index
std::uint64_t block_index;
};
/// equality operators
bool operator==(const CarrotCoinbaseEnoteV1 &a, const CarrotCoinbaseEnoteV1 &b);
static inline bool operator!=(const CarrotCoinbaseEnoteV1 &a, const CarrotCoinbaseEnoteV1 &b) { return !(a == b); }
} //namespace carrot
+79
View File
@@ -0,0 +1,79 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Constants used in Carrot
#pragma once
//local headers
//third party headers
//standard headers
//forward declarations
namespace carrot
{
// Carrot addressing protocol domain separators
static constexpr const unsigned char CARROT_DOMAIN_SEP_AMOUNT_BLINDING_FACTOR[] = "Carrot commitment mask";
static constexpr const unsigned char CARROT_DOMAIN_SEP_ONETIME_EXTENSION_G[] = "Carrot key extension G";
static constexpr const unsigned char CARROT_DOMAIN_SEP_ONETIME_EXTENSION_T[] = "Carrot key extension T";
static constexpr const unsigned char CARROT_DOMAIN_SEP_ENCRYPTION_MASK_ANCHOR[] = "Carrot encryption mask anchor";
static constexpr const unsigned char CARROT_DOMAIN_SEP_ENCRYPTION_MASK_AMOUNT[] = "Carrot encryption mask a";
static constexpr const unsigned char CARROT_DOMAIN_SEP_ENCRYPTION_MASK_PAYMENT_ID[] = "Carrot encryption mask pid";
static constexpr const unsigned char CARROT_DOMAIN_SEP_JANUS_ANCHOR_SPECIAL[] = "Carrot janus anchor special";
static constexpr const unsigned char CARROT_DOMAIN_SEP_EPHEMERAL_PRIVKEY[] = "Carrot sending key normal";
static constexpr const unsigned char CARROT_DOMAIN_SEP_VIEW_TAG[] = "Carrot view tag";
static constexpr const unsigned char CARROT_DOMAIN_SEP_SENDER_RECEIVER_SECRET[] = "Carrot sender-receiver secret";
static constexpr const unsigned char CARROT_DOMAIN_SEP_INPUT_CONTEXT_COINBASE = 'C';
static constexpr const unsigned char CARROT_DOMAIN_SEP_INPUT_CONTEXT_RINGCT = 'R';
// Carrot account secret domain separators
static constexpr const unsigned char CARROT_DOMAIN_SEP_PROVE_SPEND_KEY[] = "Carrot prove-spend key";
static constexpr const unsigned char CARROT_DOMAIN_SEP_VIEW_BALANCE_SECRET[] = "Carrot view-balance secret";
static constexpr const unsigned char CARROT_DOMAIN_SEP_GENERATE_IMAGE_KEY[] = "Carrot generate-image key";
static constexpr const unsigned char CARROT_DOMAIN_SEP_INCOMING_VIEW_KEY[] = "Carrot incoming view key";
static constexpr const unsigned char CARROT_DOMAIN_SEP_GENERATE_ADDRESS_SECRET[] = "Carrot generate-address secret";
// Carrot address domain separators
static constexpr const unsigned char CARROT_DOMAIN_SEP_ADDRESS_INDEX_GEN[] = "Carrot address index generator";
static constexpr const unsigned char CARROT_DOMAIN_SEP_SUBADDRESS_SCALAR[] = "Carrot subaddress scalar";
// Carrot misc constants
static constexpr const unsigned int CARROT_MIN_TX_OUTPUTS = 2;
static constexpr const unsigned int CARROT_MAX_TX_OUTPUTS = 8;
static constexpr const unsigned int CARROT_MIN_TX_INPUTS = 1;
static constexpr const unsigned int CARROT_MAX_TX_INPUTS = 128;
// 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";
} //namespace carrot
+167
View File
@@ -0,0 +1,167 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//paired header
#include "core_types.h"
//local headers
#include "crypto/crypto.h"
extern "C"
{
#include "crypto/crypto-ops.h"
}
//third party headers
#include <cstring>
//standard headers
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
template <std::size_t Sz>
static void xor_bytes(const unsigned char(&a)[Sz], const unsigned char(&b)[Sz], unsigned char(&c_out)[Sz])
{
for (std::size_t i{0}; i < Sz; ++i)
c_out[i] = a[i] ^ b[i];
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
template <typename T>
static T xor_bytes(const T &a, const T &b)
{
T temp;
xor_bytes(a.bytes, b.bytes, temp.bytes);
return temp;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const janus_anchor_t &a, const janus_anchor_t &b)
{
return memcmp(&a, &b, sizeof(janus_anchor_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
janus_anchor_t operator^(const janus_anchor_t &a, const janus_anchor_t &b)
{
return xor_bytes(a, b);
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const encrypted_amount_t &a, const encrypted_amount_t &b)
{
return memcmp(&a, &b, sizeof(encrypted_amount_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
encrypted_amount_t operator^(const encrypted_amount_t &a, const encrypted_amount_t &b)
{
return xor_bytes(a, b);
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b)
{
return memcmp(&a, &b, sizeof(encrypted_return_pubkey_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
encrypted_return_pubkey_t operator^(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b)
{
return xor_bytes(a, b);
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const payment_id_t &a, const payment_id_t &b)
{
return memcmp(&a, &b, sizeof(payment_id_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
payment_id_t operator^(const payment_id_t &a, const payment_id_t &b)
{
return xor_bytes(a, b);
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const encrypted_payment_id_t &a, const encrypted_payment_id_t &b)
{
return memcmp(&a, &b, sizeof(encrypted_payment_id_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
encrypted_payment_id_t operator^(const encrypted_payment_id_t &a, const encrypted_payment_id_t &b)
{
return xor_bytes(a, b);
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const input_context_t &a, const input_context_t &b)
{
return memcmp(&a, &b, sizeof(input_context_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const view_tag_t &a, const view_tag_t &b)
{
return memcmp(&a, &b, sizeof(view_tag_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
janus_anchor_t gen_janus_anchor()
{
return crypto::rand<janus_anchor_t>();
}
//-------------------------------------------------------------------------------------------------------------------
payment_id_t gen_payment_id()
{
while (true)
{
const payment_id_t res = crypto::rand<payment_id_t>();
if (res != null_payment_id)
return res;
}
}
//-------------------------------------------------------------------------------------------------------------------
encrypted_payment_id_t gen_encrypted_payment_id()
{
return crypto::rand<encrypted_payment_id_t>();
}
//-------------------------------------------------------------------------------------------------------------------
view_tag_t gen_view_tag()
{
return crypto::rand<view_tag_t>();
}
//-------------------------------------------------------------------------------------------------------------------
input_context_t gen_input_context()
{
return crypto::rand<input_context_t>();
}
//-------------------------------------------------------------------------------------------------------------------
mx25519_pubkey gen_x25519_pubkey()
{
unsigned char sc64[64];
crypto::rand(sizeof(sc64), sc64);
sc_reduce(sc64);
ge_p3 P;
ge_scalarmult_base(&P, sc64);
mx25519_pubkey P_x25519;
ge_p3_to_x25519(P_x25519.data, &P);
return P_x25519;
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+155
View File
@@ -0,0 +1,155 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Supporting types for Carrot (anchor, view tag, etc.).
#pragma once
//local headers
#include "mx25519.h"
//third party headers
//standard headers
#include <cstdint>
#include <cstddef>
//forward declarations
namespace carrot
{
constexpr std::size_t JANUS_ANCHOR_BYTES{16};
/// either encodes randomness the private key of, or an HMAC of, the ephemeral pubkey
struct janus_anchor_t final
{
unsigned char bytes[JANUS_ANCHOR_BYTES];
};
/// carrot janus anchor XORd with a user-defined secret
using encrypted_janus_anchor_t = janus_anchor_t;
/// carrot enote types
enum class CarrotEnoteType : unsigned char
{
PAYMENT = 0,
CHANGE = 1
};
/// carrot encrypted amount
constexpr std::size_t ENCRYPTED_AMOUNT_BYTES{8};
struct encrypted_amount_t final
{
unsigned char bytes[ENCRYPTED_AMOUNT_BYTES];
};
/// legacy payment ID
constexpr std::size_t PAYMENT_ID_BYTES{8};
struct payment_id_t final
{
unsigned char bytes[PAYMENT_ID_BYTES];
};
static constexpr payment_id_t null_payment_id{{0}};
/// legacy encrypted payment ID
struct encrypted_payment_id_t final
{
unsigned char bytes[PAYMENT_ID_BYTES];
};
/// carrot view tags
constexpr std::size_t VIEW_TAG_BYTES{3};
struct view_tag_t final
{
unsigned char bytes[VIEW_TAG_BYTES];
};
static_assert(sizeof(view_tag_t) < 32, "uint8_t cannot index all view tag bits");
/// carrot input context
constexpr std::size_t INPUT_CONTEXT_BYTES{1 + 32};
struct input_context_t final
{
unsigned char bytes[INPUT_CONTEXT_BYTES];
};
// SPARC encrypted return public key
constexpr std::size_t ENCRYPTED_RETURN_PUBKEY_BYTES{32};
struct encrypted_return_pubkey_t final
{
unsigned char bytes[ENCRYPTED_RETURN_PUBKEY_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); }
janus_anchor_t operator^(const janus_anchor_t &a, const janus_anchor_t &b);
/// overloaded operators: encrypted amount
bool operator==(const encrypted_amount_t &a, const encrypted_amount_t &b);
static inline bool operator!=(const encrypted_amount_t &a, const encrypted_amount_t &b) { return !(a == b); }
encrypted_amount_t operator^(const encrypted_amount_t &a, const encrypted_amount_t &b);
/// overloaded operators: payment ID
bool operator==(const payment_id_t &a, const payment_id_t &b);
static inline bool operator!=(const payment_id_t &a, const payment_id_t &b) { return !(a == b); }
payment_id_t operator^(const payment_id_t &a, const payment_id_t &b);
/// overloaded operators: encrypted payment ID
bool operator==(const encrypted_payment_id_t &a, const encrypted_payment_id_t &b);
static inline bool operator!=(const encrypted_payment_id_t &a, const encrypted_payment_id_t &b) { return !(a == b); }
encrypted_payment_id_t operator^(const encrypted_payment_id_t &a, const encrypted_payment_id_t &b);
/// overloaded operators: input context
bool operator==(const input_context_t &a, const input_context_t &b);
static inline bool operator!=(const input_context_t &a, const input_context_t &b) { return !(a == b); }
/// overloaded operators: view tag
bool operator==(const view_tag_t &a, const view_tag_t &b);
static inline bool operator!=(const view_tag_t &a, const view_tag_t &b) { return !(a == b); }
/// overloaded operators: encrypted return pubkey
bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b);
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);
/// generate a random janus anchor
janus_anchor_t gen_janus_anchor();
/// generate a random (non-null) payment ID
payment_id_t gen_payment_id();
/// generate a random encrypted payment ID
encrypted_payment_id_t gen_encrypted_payment_id();
/// generate a random view tag
view_tag_t gen_view_tag();
/// generate a random input context
input_context_t gen_input_context();
/// generate a random X25519 pubkey (unclamped)
mx25519_pubkey gen_x25519_pubkey();
} //namespace carrot
+150
View File
@@ -0,0 +1,150 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//paired header
#include "destination.h"
//local headers
#include "address_utils.h"
#include "exceptions.h"
#include "misc_log_ex.h"
#include "ringct/rctOps.h"
//third party headers
//standard headers
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot.dest"
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const CarrotDestinationV1 &a, const CarrotDestinationV1 &b)
{
return a.address_spend_pubkey == b.address_spend_pubkey &&
a.address_view_pubkey == b.address_view_pubkey &&
a.is_subaddress == b.is_subaddress &&
a.payment_id == b.payment_id;
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_main_address_v1(const crypto::public_key &account_spend_pubkey,
const crypto::public_key &primary_address_view_pubkey,
CarrotDestinationV1 &destination_out)
{
destination_out = CarrotDestinationV1{
.address_spend_pubkey = account_spend_pubkey,
.address_view_pubkey = primary_address_view_pubkey,
.is_subaddress = false,
.payment_id = null_payment_id
};
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_subaddress_v1(const crypto::public_key &account_spend_pubkey,
const crypto::public_key &account_view_pubkey,
const generate_address_secret_device &s_generate_address_dev,
const std::uint32_t j_major,
const std::uint32_t j_minor,
CarrotDestinationV1 &destination_out)
{
CARROT_CHECK_AND_THROW(j_major || j_minor,
bad_address_type, "j cannot be 0 for a subaddress, only for main addresses");
// s^j_gen = H_32[s_ga](j_major, j_minor)
crypto::secret_key address_index_generator;
s_generate_address_dev.make_index_extension_generator(j_major, j_minor, address_index_generator);
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
crypto::secret_key subaddress_scalar;
make_carrot_subaddress_scalar(account_spend_pubkey, address_index_generator, j_major, j_minor, subaddress_scalar);
// K^j_s = k^j_subscal * K_s
const rct::key address_spend_pubkey =
rct::scalarmultKey(rct::pk2rct(account_spend_pubkey), rct::sk2rct(subaddress_scalar));
// K^j_v = k^j_subscal * K_v
const rct::key address_view_pubkey =
rct::scalarmultKey(rct::pk2rct(account_view_pubkey), rct::sk2rct(subaddress_scalar));
destination_out = CarrotDestinationV1{
.address_spend_pubkey = rct::rct2pk(address_spend_pubkey),
.address_view_pubkey = rct::rct2pk(address_view_pubkey),
.is_subaddress = true,
.payment_id = null_payment_id
};
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_integrated_address_v1(const crypto::public_key &account_spend_pubkey,
const crypto::public_key &primary_address_view_pubkey,
const payment_id_t payment_id,
CarrotDestinationV1 &destination_out)
{
destination_out = CarrotDestinationV1{
.address_spend_pubkey = account_spend_pubkey,
.address_view_pubkey = primary_address_view_pubkey,
.is_subaddress = false,
.payment_id = payment_id
};
}
//-------------------------------------------------------------------------------------------------------------------
CarrotDestinationV1 gen_carrot_main_address_v1()
{
return CarrotDestinationV1{
.address_spend_pubkey = rct::rct2pk(rct::pkGen()),
.address_view_pubkey = rct::rct2pk(rct::pkGen()),
.is_subaddress = false,
.payment_id = null_payment_id
};
}
//-------------------------------------------------------------------------------------------------------------------
CarrotDestinationV1 gen_carrot_subaddress_v1()
{
return CarrotDestinationV1{
.address_spend_pubkey = rct::rct2pk(rct::pkGen()),
.address_view_pubkey = rct::rct2pk(rct::pkGen()),
.is_subaddress = true,
.payment_id = null_payment_id
};
}
//-------------------------------------------------------------------------------------------------------------------
CarrotDestinationV1 gen_carrot_integrated_address_v1()
{
// force generate non-zero payment id
payment_id_t payment_id{gen_payment_id()};
while (payment_id == null_payment_id)
payment_id = gen_payment_id();
return CarrotDestinationV1{
.address_spend_pubkey = rct::rct2pk(rct::pkGen()),
.address_view_pubkey = rct::rct2pk(rct::pkGen()),
.is_subaddress = false,
.payment_id = payment_id
};
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+117
View File
@@ -0,0 +1,117 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A 'payment proposal' is a proposal to make an enote sending funds to a Carrot address.
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
#pragma once
//local headers
#include "core_types.h"
#include "crypto/crypto.h"
#include "device.h"
//third party headers
//standard headers
//forward declarations
namespace carrot
{
////
// CarrotDestinationV1
// - for creating an output proposal to send an amount to someone
///
struct CarrotDestinationV1 final
{
/// K^j_s
crypto::public_key address_spend_pubkey;
/// K^j_v
crypto::public_key address_view_pubkey;
/// is a subaddress?
bool is_subaddress;
/// legacy payment id pid: null for main addresses and subaddresses
payment_id_t payment_id;
};
/// equality operators
bool operator==(const CarrotDestinationV1 &a, const CarrotDestinationV1 &b);
static inline bool operator!=(const CarrotDestinationV1 &a, const CarrotDestinationV1 &b) { return !(a == b); }
/**
* brief: make_carrot_main_address_v1 - make a destination address
* param: account_spend_pubkey - K_s
* param: primary_address_view_pubkey - K^0_v = k_v G
* outparam: destination_out - the full main address
*/
void make_carrot_main_address_v1(const crypto::public_key &account_spend_pubkey,
const crypto::public_key &primary_address_view_pubkey,
CarrotDestinationV1 &destination_out);
/**
* brief: make_carrot_subaddress_v1 - make a destination address
* param: account_spend_pubkey - K_s
* param: account_view_pubkey - K_v = k_v K_s
* param: s_generate_address_dev - device for s_ga
* param: j_major -
* param: j_minor -
* outparam: destination_out - the full subaddress
*/
void make_carrot_subaddress_v1(const crypto::public_key &account_spend_pubkey,
const crypto::public_key &account_view_pubkey,
const generate_address_secret_device &s_generate_address_dev,
const std::uint32_t j_major,
const std::uint32_t j_minor,
CarrotDestinationV1 &destination_out);
/**
* brief: make_carrot_integrated_address_v1 - make a destination address
* param: account_spend_pubkey - K_s
* param: primary_address_view_pubkey - K^0_v = k_v G
* param: payment_id - pid
* outparam: destination_out - the full main address
*/
void make_carrot_integrated_address_v1(const crypto::public_key &account_spend_pubkey,
const crypto::public_key &primary_address_view_pubkey,
const payment_id_t payment_id,
CarrotDestinationV1 &destination_out);
/**
* brief: gen_carrot_main_address_v1 - generate a random main address
*/
CarrotDestinationV1 gen_carrot_main_address_v1();
/**
* brief: gen_carrot_main_address_v1 - generate a random subaddress
*/
CarrotDestinationV1 gen_carrot_subaddress_v1();
/**
* brief: gen_carrot_main_address_v1 - generate a random integrated address
*/
CarrotDestinationV1 gen_carrot_integrated_address_v1();
} //namespace carrot
+194
View File
@@ -0,0 +1,194 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Abstract interfaces for performing scanning without revealing account keys
#pragma once
//local headers
#include "core_types.h"
#include "crypto/crypto.h"
#include "mx25519.h"
//third party headers
//standard headers
#include <cstdio>
#include <stdexcept>
#include <string>
//forward declarations
/**
* These device interfaces were written primarily to be as lean as possible, for ease of the
* implementor, so long as account keys don't leak. As such, the interfaces do not shield
* sender-receiver secrets, and thus temporary access to this device interface can expose
* transaction content permanently in a provable manner. The device interface currently used in
* Monero (hw::device) also exposes transaction content, which can be saved permanently, but it
* wouldn't necessarily be provable. Thus, in the case of a breach, the original user has some
* plausible deniability with (hw::device), which cannot be said of the interfaces in this file.
* It's not impossible to make carrot scanning happen completely on-device, but it is significantly
* more involved.
*/
namespace carrot
{
/**
* brief: base exception type for reporting carrot device errors.
* note: devices should only throw this exception or derived classes
*/
struct device_error: public std::runtime_error
{
/**
* param: dev_make - e.g. "Trezor", "Ledger"
* param: dev_model - e.g. "Model T", "Nano X"
* param: func_called - verbatim device interface method name, e.g. "view_key_scalar_mult_x25519"
* param: msg - arbitrary error message
* param: code - arbitrary error code
*/
device_error(std::string &&dev_make,
std::string &&dev_model,
std::string &&func_called,
std::string &&msg,
const int code)
: std::runtime_error(make_formatted_message(dev_make, dev_model, func_called, msg, code)),
dev_make(dev_make), dev_model(dev_model), func_called(func_called), msg(msg), code(code)
{}
static std::string make_formatted_message(const std::string &dev_make,
const std::string &dev_model,
const std::string &func_called,
const std::string &msg,
const int code)
{
char buf[384];
snprintf(buf, sizeof(buf),
"%s %s device error (%d), at %s(): %s",
dev_make.c_str(), dev_model.c_str(), code, func_called.c_str(), msg.c_str());
return {buf};
}
const std::string dev_make;
const std::string dev_model;
const std::string func_called;
const std::string msg;
const int code;
};
struct view_incoming_key_device
{
/**
* brief: view_key_scalar_mult_ed25519 - do an Ed25519 scalar mult against the incoming view key
* kvP = k_v * P
* param: P - Ed25519 base point
* outparam: kvP
* return: true on success, false on failure (e.g. unable to decompress point)
*/
virtual bool view_key_scalar_mult_ed25519(const crypto::public_key &P, crypto::public_key &kvP) const = 0;
/**
* brief: view_key_scalar_mult_x25519 - do an X25519 scalar mult and cofactor clear against the incoming view key
* kvD = k_v * D
* param: D - X25519 base point
* outparam: kvD
* return: true on success, false on failure (e.g. unable to decompress point)
*/
virtual bool view_key_scalar_mult_x25519(const mx25519_pubkey &D, mx25519_pubkey &kvD) const = 0;
/**
* brief: make_janus_anchor_special - make a janus anchor for "special" enotes
* anchor_sp = H_16(D_e, input_context, Ko, k_v)
* param: enote_ephemeral_pubkey - D_e
* param: input_context - input_context
* param: account_spend_pubkey - K_s
* outparam: anchor_special_out - anchor_sp
*/
virtual void make_janus_anchor_special(const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const crypto::public_key &onetime_address,
janus_anchor_t &anchor_special_out) const = 0;
virtual ~view_incoming_key_device() = default;
};
struct view_balance_secret_device
{
/**
* brief: make_internal_view_tag - make an internal view tag, given non-secret data
* vt = H_3(s_vb || input_context || Ko)
* param: input_context - input_context
* param: onetime_address - Ko
* outparam: view_tag_out - vt
*/
virtual void make_internal_view_tag(const input_context_t &input_context,
const crypto::public_key &onetime_address,
view_tag_t &view_tag_out) const = 0;
/**
* brief: make_internal_sender_receiver_secret - make internal sender-receiver secret, given non-secret data
* s^ctx_sr = H_32(s_sr, D_e, input_context)
* param: enote_ephemeral_pubkey - D_e
* param: input_context - input_context
* outparam: s_sender_receiver_out - s^ctx_sr
*/
virtual void make_internal_sender_receiver_secret(const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
crypto::hash &s_sender_receiver_out) const = 0;
/**
* brief: make_internal_return_privkey - make internal return private key, given non-secret data
* k_return = H_32(s_vb || input_context || Ko)
* param: input_context - input_context
* param: onetime_address - Ko
* outparam: return_privkey_out - k_return
*/
virtual void make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const = 0;
virtual ~view_balance_secret_device() = default;
};
struct generate_address_secret_device
{
/**
* brief: make_index_extension_generator - make carrot index extension generator s^j_gen
* s^j_gen = H_32[s_ga](j_major, j_minor)
* param: major_index - j_major
* param: minor_index - j_minor
* outparam: address_generator_out - s^j_gen
*/
virtual void make_index_extension_generator(const std::uint32_t major_index,
const std::uint32_t minor_index,
crypto::secret_key &address_generator_out) const = 0;
virtual ~generate_address_secret_device() = default;
};
} //namespace carrot
+104
View File
@@ -0,0 +1,104 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//pair header
#include "device_ram_borrowed.h"
//local headers
#include "address_utils.h"
#include "enote_utils.h"
#include "ringct/rctOps.h"
//third party headers
//standard headers
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
bool view_incoming_key_ram_borrowed_device::view_key_scalar_mult_ed25519(const crypto::public_key &P,
crypto::public_key &kvP) const
{
kvP = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(P), rct::sk2rct(m_k_view_incoming)));
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool view_incoming_key_ram_borrowed_device::view_key_scalar_mult_x25519(const mx25519_pubkey &D,
mx25519_pubkey &kvD) const
{
return make_carrot_uncontextualized_shared_key_receiver(m_k_view_incoming, D, kvD);
}
//-------------------------------------------------------------------------------------------------------------------
void view_incoming_key_ram_borrowed_device::make_janus_anchor_special(
const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const crypto::public_key &onetime_address,
janus_anchor_t &anchor_special_out) const
{
return make_carrot_janus_anchor_special(enote_ephemeral_pubkey,
input_context,
onetime_address,
m_k_view_incoming,
anchor_special_out);
}
//-------------------------------------------------------------------------------------------------------------------
void view_balance_secret_ram_borrowed_device::make_internal_view_tag(const input_context_t &input_context,
const crypto::public_key &onetime_address,
view_tag_t &view_tag_out) const
{
make_carrot_view_tag(to_bytes(m_s_view_balance), input_context, onetime_address, view_tag_out);
}
//-------------------------------------------------------------------------------------------------------------------
void view_balance_secret_ram_borrowed_device::make_internal_sender_receiver_secret(
const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
crypto::hash &s_sender_receiver_out) const
{
make_carrot_sender_receiver_secret(to_bytes(m_s_view_balance),
enote_ephemeral_pubkey,
input_context,
s_sender_receiver_out);
}
//-------------------------------------------------------------------------------------------------------------------
void view_balance_secret_ram_borrowed_device::make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const
{
make_sparc_return_privkey(to_bytes(m_s_view_balance), input_context, onetime_address, return_privkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
void generate_address_secret_ram_borrowed_device::make_index_extension_generator(
const std::uint32_t major_index,
const std::uint32_t minor_index,
crypto::secret_key &address_generator_out) const
{
make_carrot_index_extension_generator(m_s_generate_address, major_index, minor_index, address_generator_out);
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+103
View File
@@ -0,0 +1,103 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Carrot device implementations for in-memory keys & secrets
#pragma once
//local headers
#include "device.h"
//third party headers
//standard headers
//forward declarations
namespace carrot
{
class view_incoming_key_ram_borrowed_device: virtual public view_incoming_key_device
{
public:
view_incoming_key_ram_borrowed_device(const crypto::secret_key &k_view_incoming):
m_k_view_incoming(k_view_incoming) {}
bool view_key_scalar_mult_ed25519(const crypto::public_key &P,
crypto::public_key &kvP) const override;
bool view_key_scalar_mult_x25519(const mx25519_pubkey &D,
mx25519_pubkey &kvD) const override;
void make_janus_anchor_special(const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const crypto::public_key &onetime_address,
janus_anchor_t &anchor_special_out) const override;
protected:
const crypto::secret_key &m_k_view_incoming;
};
class view_balance_secret_ram_borrowed_device: public view_balance_secret_device
{
public:
view_balance_secret_ram_borrowed_device(const crypto::secret_key &s_view_balance):
m_s_view_balance(s_view_balance) {}
void make_internal_view_tag(const input_context_t &input_context,
const crypto::public_key &onetime_address,
view_tag_t &view_tag_out) const override;
void make_internal_sender_receiver_secret(const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
crypto::hash &s_sender_receiver_out) const override;
void make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const override;
protected:
const crypto::secret_key &m_s_view_balance;
};
class generate_address_secret_ram_borrowed_device: public generate_address_secret_device
{
public:
generate_address_secret_ram_borrowed_device(const crypto::secret_key &s_generate_address):
m_s_generate_address(s_generate_address) {}
void make_index_extension_generator(const std::uint32_t major_index,
const std::uint32_t minor_index,
crypto::secret_key &address_generator_out) const override;
protected:
const crypto::secret_key &m_s_generate_address;
};
} //namespace carrot
+565
View File
@@ -0,0 +1,565 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//paired header
#include "enote_utils.h"
//local headers
#include "config.h"
extern "C"
{
#include "crypto/crypto-ops.h"
}
#include "crypto/generators.h"
#include "crypto/wallet/crypto.h"
#include "hash_functions.h"
#include "int-util.h"
#include "misc_language.h"
#include "ringct/rctOps.h"
#include "transcript_fixed.h"
//third party headers
//standard headers
#include <mutex>
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
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;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static encrypted_amount_t enc_amount(const rct::xmr_amount amount, const encrypted_amount_t &mask)
{
static_assert(sizeof(rct::xmr_amount) == sizeof(encrypted_amount_t), "");
// little_endian(amount) XOR H_8(q, Ko)
encrypted_amount_t amount_LE;
memcpy_swap64le(amount_LE.bytes, &amount, 1);
return amount_LE ^ mask;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static rct::xmr_amount dec_amount(const encrypted_amount_t &encrypted_amount, const encrypted_amount_t &mask)
{
static_assert(sizeof(rct::xmr_amount) == sizeof(encrypted_amount_t), "");
// system_endian(encrypted_amount XOR H_8(q, Ko))
const encrypted_amount_t decryptd_amount{encrypted_amount ^ mask};
rct::xmr_amount amount;
memcpy_swap64le(&amount, &decryptd_amount, 1);
return amount;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
template <typename Pid,
typename OtherPid = std::conditional_t<std::is_same_v<Pid, payment_id_t>, encrypted_payment_id_t, payment_id_t>>
static OtherPid convert_payment_id(const Pid &v)
{
static_assert(sizeof(Pid) == PAYMENT_ID_BYTES);
OtherPid conv;
memcpy(&conv, &v, PAYMENT_ID_BYTES);
return conv;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_enote_ephemeral_privkey(const janus_anchor_t &anchor_norm,
const input_context_t &input_context,
const crypto::public_key &address_spend_pubkey,
const payment_id_t payment_id,
crypto::secret_key &enote_ephemeral_privkey_out)
{
// k_e = (H_64(anchor_norm, input_context, K^j_s, pid)) mod l
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_EPHEMERAL_PRIVKEY>(
anchor_norm, input_context, address_spend_pubkey, payment_id);
derive_scalar(transcript.data(), transcript.size(), nullptr, &enote_ephemeral_privkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_enote_ephemeral_pubkey_cryptonote(const crypto::secret_key &enote_ephemeral_privkey,
mx25519_pubkey &enote_ephemeral_pubkey_out)
{
// D_e = d_e B
mx25519_scmul_base(get_mx25519_impl(),
&enote_ephemeral_pubkey_out,
reinterpret_cast<const mx25519_privkey*>(&enote_ephemeral_privkey));
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &enote_ephemeral_privkey,
const crypto::public_key &address_spend_pubkey,
mx25519_pubkey &enote_ephemeral_pubkey_out)
{
// deserialize K^j_s
ge_p3 address_spend_pubkey_p3;
ge_frombytes_vartime(&address_spend_pubkey_p3, to_bytes(address_spend_pubkey));
// K_e = d_e K^j_s
ge_p3 D_e_in_ed25519;
ge_scalarmult_p3(&D_e_in_ed25519, to_bytes(enote_ephemeral_privkey), &address_spend_pubkey_p3);
// D_e = ConvertPointE(K_e)
ge_p3_to_x25519(enote_ephemeral_pubkey_out.data, &D_e_in_ed25519);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_enote_ephemeral_pubkey(const crypto::secret_key &enote_ephemeral_privkey,
const crypto::public_key &address_spend_pubkey,
const bool is_subaddress,
mx25519_pubkey &enote_ephemeral_pubkey_out)
{
if (is_subaddress)
{
// D_e = d_e ConvertPointE(K^j_s)
make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey,
address_spend_pubkey,
enote_ephemeral_pubkey_out);
}
else // !is_subaddress
{
// D_e = d_e B
make_carrot_enote_ephemeral_pubkey_cryptonote(enote_ephemeral_privkey, enote_ephemeral_pubkey_out);
}
}
//-------------------------------------------------------------------------------------------------------------------
bool make_carrot_uncontextualized_shared_key_receiver(const crypto::secret_key &k_view,
const mx25519_pubkey &enote_ephemeral_pubkey,
mx25519_pubkey &s_sender_receiver_unctx_out)
{
// s_sr = k_v D_e
mx25519_scmul_key(get_mx25519_impl(),
&s_sender_receiver_unctx_out,
reinterpret_cast<const mx25519_privkey*>(&k_view),
&enote_ephemeral_pubkey);
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool make_carrot_uncontextualized_shared_key_sender(const crypto::secret_key &enote_ephemeral_privkey,
const crypto::public_key &address_view_pubkey,
mx25519_pubkey &s_sender_receiver_unctx_out)
{
// if K^j_v not in prime order subgroup, then FAIL
ge_p3 address_view_pubkey_p3;
if (!rct::toPointCheckOrder(&address_view_pubkey_p3, to_bytes(address_view_pubkey)))
return false;
// D^j_v = ConvertPointE(K^j_v)
mx25519_pubkey address_view_pubkey_x25519;
ge_p3_to_x25519(address_view_pubkey_x25519.data, &address_view_pubkey_p3);
// s_sr = d_e D^j_v
mx25519_scmul_key(get_mx25519_impl(),
&s_sender_receiver_unctx_out,
reinterpret_cast<const mx25519_privkey*>(&enote_ephemeral_privkey),
&address_view_pubkey_x25519);
return true;
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
view_tag_t &view_tag_out)
{
// vt = H_3(s_sr || input_context || Ko)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_VIEW_TAG>(input_context, onetime_address);
derive_bytes_3(transcript.data(), transcript.size(), s_sender_receiver_unctx, &view_tag_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out)
{
// k_return = H_32(s_sr || input_context || Ko)
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_ADDRESS_SCALAR>(input_context, onetime_address);
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_privkey_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,
encrypted_return_pubkey_t &return_pubkey_mask_out)
{
// m_return = H_32(s_sr || input_context || Ko)
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_PUBKEY_ENCRYPTION_MASK>(input_context, onetime_address);
derive_bytes_32(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_pubkey_mask_out);
}
//-------------------------------------------------------------------------------------------------------------------
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)
{
// K_return = k_return G ^ m_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;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
input_context_t make_carrot_input_context_coinbase(const std::uint64_t block_index)
{
// input_context = "C" || IntToBytes256(block_index)
input_context_t input_context{};
input_context.bytes[0] = CARROT_DOMAIN_SEP_INPUT_CONTEXT_COINBASE;
memcpy_swap64le(input_context.bytes + 1, &block_index, 1);
return input_context;
}
//-------------------------------------------------------------------------------------------------------------------
input_context_t make_carrot_input_context(const crypto::key_image &first_rct_key_image)
{
// input_context = "R" || KI_1
input_context_t input_context{};
input_context.bytes[0] = CARROT_DOMAIN_SEP_INPUT_CONTEXT_RINGCT;
memcpy(input_context.bytes + 1, first_rct_key_image.data, sizeof(crypto::key_image));
return input_context;
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_sender_receiver_secret(const unsigned char s_sender_receiver_unctx[32],
const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
crypto::hash &s_sender_receiver_out)
{
// s^ctx_sr = H_32(s_sr, D_e, input_context)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_SENDER_RECEIVER_SECRET>(
enote_ephemeral_pubkey, input_context);
derive_bytes_32(transcript.data(), transcript.size(), s_sender_receiver_unctx, &s_sender_receiver_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_onetime_address_extension_g(const crypto::hash &s_sender_receiver,
const rct::key &amount_commitment,
crypto::secret_key &sender_extension_out)
{
// k^o_g = H_n("..g..", s^ctx_sr, C_a)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ONETIME_EXTENSION_G>(amount_commitment);
derive_scalar(transcript.data(), transcript.size(), &s_sender_receiver, &sender_extension_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_onetime_address_extension_t(const crypto::hash &s_sender_receiver,
const rct::key &amount_commitment,
crypto::secret_key &sender_extension_out)
{
// k^o_t = H_n("..t..", s^ctx_sr, C_a)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ONETIME_EXTENSION_T>(amount_commitment);
derive_scalar(transcript.data(), transcript.size(), &s_sender_receiver, &sender_extension_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_onetime_address_extension_pubkey(const crypto::hash &s_sender_receiver,
const rct::key &amount_commitment,
crypto::public_key &sender_extension_pubkey_out)
{
// k^o_g = H_n("..g..", s^ctx_sr, C_a)
crypto::secret_key sender_extension_g;
make_carrot_onetime_address_extension_g(s_sender_receiver, amount_commitment, sender_extension_g);
// k^o_t = H_n("..t..", s^ctx_sr, C_a)
crypto::secret_key sender_extension_t;
make_carrot_onetime_address_extension_t(s_sender_receiver, amount_commitment, sender_extension_t);
// K^o_ext = k^o_g G + k^o_t T
rct::key sender_extension_pubkey_tmp;
rct::addKeys2(sender_extension_pubkey_tmp,
rct::sk2rct(sender_extension_g),
rct::sk2rct(sender_extension_t),
rct::pk2rct(crypto::get_T()));
sender_extension_pubkey_out = rct::rct2pk(sender_extension_pubkey_tmp);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_onetime_address(const crypto::public_key &address_spend_pubkey,
const crypto::hash &s_sender_receiver,
const rct::key &amount_commitment,
crypto::public_key &onetime_address_out)
{
// K^o_ext = k^o_g G + k^o_t T
crypto::public_key sender_extension_pubkey;
make_carrot_onetime_address_extension_pubkey(s_sender_receiver, amount_commitment, sender_extension_pubkey);
// Ko = K^j_s + K^o_ext
onetime_address_out = rct::rct2pk(rct::addKeys(
rct::pk2rct(address_spend_pubkey), rct::pk2rct(sender_extension_pubkey)));
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_amount_blinding_factor(const crypto::hash &s_sender_receiver,
const rct::xmr_amount amount,
const crypto::public_key &address_spend_pubkey,
const CarrotEnoteType enote_type,
crypto::secret_key &amount_blinding_factor_out)
{
// k_a = H_n(s^ctx_sr, a, K^j_s, enote_type)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_AMOUNT_BLINDING_FACTOR>(
amount, address_spend_pubkey, static_cast<unsigned char>(enote_type));
derive_scalar(transcript.data(), transcript.size(), &s_sender_receiver, &amount_blinding_factor_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_anchor_encryption_mask(const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address,
encrypted_janus_anchor_t &anchor_encryption_mask_out)
{
// m_anchor = H_16(s^ctx_sr, Ko)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_ANCHOR>(onetime_address);
derive_bytes_16(transcript.data(), transcript.size(), &s_sender_receiver, &anchor_encryption_mask_out);
}
//-------------------------------------------------------------------------------------------------------------------
encrypted_janus_anchor_t encrypt_carrot_anchor(const janus_anchor_t &anchor,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address)
{
// m_anchor = H_16(s^ctx_sr, Ko)
encrypted_janus_anchor_t mask;
make_carrot_anchor_encryption_mask(s_sender_receiver, onetime_address, mask);
// anchor_enc = anchor XOR m_anchor
return anchor ^ mask;
}
//-------------------------------------------------------------------------------------------------------------------
janus_anchor_t decrypt_carrot_anchor(const encrypted_janus_anchor_t &encrypted_anchor,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address)
{
// m_anchor = H_16(s^ctx_sr, Ko)
encrypted_janus_anchor_t mask;
make_carrot_anchor_encryption_mask(s_sender_receiver, onetime_address, mask);
// anchor = anchor_enc XOR m_anchor
return encrypted_anchor ^ mask;
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_amount_encryption_mask(const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address,
encrypted_amount_t &amount_encryption_mask_out)
{
// m_a = H_8(s^ctx_sr, Ko)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_AMOUNT>(onetime_address);
derive_bytes_8(transcript.data(), transcript.size(), &s_sender_receiver, &amount_encryption_mask_out);
}
//-------------------------------------------------------------------------------------------------------------------
encrypted_amount_t encrypt_carrot_amount(const rct::xmr_amount amount,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address)
{
// m_a = H_8(s^ctx_sr, Ko)
encrypted_amount_t mask;
make_carrot_amount_encryption_mask(s_sender_receiver, onetime_address, mask);
// a_enc = a XOR m_a [paying attention to system endianness]
return enc_amount(amount, mask);
}
//-------------------------------------------------------------------------------------------------------------------
rct::xmr_amount decrypt_carrot_amount(const encrypted_amount_t encrypted_amount,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address)
{
// m_a = H_8(s^ctx_sr, Ko)
encrypted_amount_t mask;
make_carrot_amount_encryption_mask(s_sender_receiver, onetime_address, mask);
// a = a_enc XOR m_a [paying attention to system endianness]
return dec_amount(encrypted_amount, mask);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_payment_id_encryption_mask(const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address,
encrypted_payment_id_t &payment_id_encryption_mask_out)
{
// m_pid = H_8(s^ctx_sr, Ko)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_PAYMENT_ID>(onetime_address);
derive_bytes_8(transcript.data(), transcript.size(), &s_sender_receiver, &payment_id_encryption_mask_out);
}
//-------------------------------------------------------------------------------------------------------------------
encrypted_payment_id_t encrypt_legacy_payment_id(const payment_id_t payment_id,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address)
{
// m_pid = H_8(s^ctx_sr, Ko)
encrypted_payment_id_t mask;
make_carrot_payment_id_encryption_mask(s_sender_receiver, onetime_address, mask);
// pid_enc = pid XOR m_pid
return convert_payment_id(payment_id) ^ mask;
}
//-------------------------------------------------------------------------------------------------------------------
payment_id_t decrypt_legacy_payment_id(const encrypted_payment_id_t encrypted_payment_id,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address)
{
// m_pid = H_8(s^ctx_sr, Ko)
encrypted_payment_id_t mask;
make_carrot_payment_id_encryption_mask(s_sender_receiver, onetime_address, mask);
// pid = pid_enc XOR m_pid
return convert_payment_id(encrypted_payment_id ^ mask);
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_janus_anchor_special(const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const crypto::secret_key &k_view,
janus_anchor_t &anchor_special_out)
{
// anchor_sp = H_16(D_e, input_context, Ko, k_v)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_JANUS_ANCHOR_SPECIAL>(
enote_ephemeral_pubkey, input_context, onetime_address);
derive_bytes_16(transcript.data(), transcript.size(), &k_view, &anchor_special_out);
}
//-------------------------------------------------------------------------------------------------------------------
void recover_address_spend_pubkey(const crypto::public_key &onetime_address,
const crypto::hash &s_sender_receiver,
const rct::key &amount_commitment,
crypto::public_key &address_spend_key_out)
{
// K^o_ext = k^o_g G + k^o_t T
crypto::public_key sender_extension_pubkey;
make_carrot_onetime_address_extension_pubkey(s_sender_receiver, amount_commitment, sender_extension_pubkey);
// K^j_s = Ko - K^o_ext
rct::key res_tmp;
rct::subKeys(res_tmp, rct::pk2rct(onetime_address), rct::pk2rct(sender_extension_pubkey));
address_spend_key_out = rct::rct2pk(res_tmp);
}
//-------------------------------------------------------------------------------------------------------------------
bool test_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
const input_context_t input_context,
const crypto::public_key &onetime_address,
const view_tag_t view_tag)
{
// 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;
}
//-------------------------------------------------------------------------------------------------------------------
bool try_recompute_carrot_amount_commitment(const crypto::hash &s_sender_receiver,
const rct::xmr_amount nominal_amount,
const crypto::public_key &nominal_address_spend_pubkey,
const CarrotEnoteType nominal_enote_type,
const rct::key &amount_commitment,
crypto::secret_key &amount_blinding_factor_out)
{
// k_a' = H_n(s^ctx_sr, a', K^j_s', enote_type')
make_carrot_amount_blinding_factor(s_sender_receiver,
nominal_amount,
nominal_address_spend_pubkey,
nominal_enote_type,
amount_blinding_factor_out);
// C_a' = k_a' G + a' H
const rct::key nominal_amount_commitment = rct::commit(nominal_amount, rct::sk2rct(amount_blinding_factor_out));
// C_a' ?= C_a
return nominal_amount_commitment == amount_commitment;
}
//-------------------------------------------------------------------------------------------------------------------
bool try_get_carrot_amount(const crypto::hash &s_sender_receiver,
const encrypted_amount_t &encrypted_amount,
const crypto::public_key &onetime_address,
const crypto::public_key &address_spend_pubkey,
const rct::key &amount_commitment,
CarrotEnoteType &enote_type_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out)
{
// a' = a_enc XOR m_a
amount_out = decrypt_carrot_amount(encrypted_amount, s_sender_receiver, onetime_address);
// set enote_type <- "payment"
enote_type_out = CarrotEnoteType::PAYMENT;
// if C_a ?= k_a' G + a' H, then PASS
if (try_recompute_carrot_amount_commitment(s_sender_receiver,
amount_out,
address_spend_pubkey,
enote_type_out,
amount_commitment,
amount_blinding_factor_out))
return true;
// set enote_type <- "change"
enote_type_out = CarrotEnoteType::CHANGE;
// if C_a ?= k_a' G + a' H, then PASS
if (try_recompute_carrot_amount_commitment(s_sender_receiver,
amount_out,
address_spend_pubkey,
enote_type_out,
amount_commitment,
amount_blinding_factor_out))
return true;
// neither attempt at recomputing passed: so FAIL
return false;
}
//-------------------------------------------------------------------------------------------------------------------
bool verify_carrot_normal_janus_protection(const janus_anchor_t &nominal_anchor,
const input_context_t &input_context,
const crypto::public_key &nominal_address_spend_pubkey,
const bool is_subaddress,
const payment_id_t nominal_payment_id,
const mx25519_pubkey &enote_ephemeral_pubkey)
{
// d_e' = H_n(anchor_norm, input_context, K^j_s, pid))
crypto::secret_key nominal_enote_ephemeral_privkey;
make_carrot_enote_ephemeral_privkey(nominal_anchor,
input_context,
nominal_address_spend_pubkey,
nominal_payment_id,
nominal_enote_ephemeral_privkey);
// recompute D_e' for d_e' and address type
mx25519_pubkey nominal_enote_ephemeral_pubkey;
make_carrot_enote_ephemeral_pubkey(nominal_enote_ephemeral_privkey,
nominal_address_spend_pubkey,
is_subaddress,
nominal_enote_ephemeral_pubkey);
// D_e' ?= D_e
return 0 == memcmp(&nominal_enote_ephemeral_pubkey, &enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+438
View File
@@ -0,0 +1,438 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// @file Utilities for making and handling enotes with carrot.
#pragma once
//local headers
#include "crypto/crypto.h"
#include "core_types.h"
#include "mx25519.h"
#include "ringct/rctTypes.h"
#include "device.h"
//third party headers
//standard headers
//forward declarations
namespace carrot
{
/**
* brief: make_carrot_enote_ephemeral_privkey - enote ephemeral privkey k_e for Carrot enotes
* d_e = H_n(anchor_norm, input_context, K^j_s, pid))
* param: anchor_norm - anchor_norm
* param: input_context - input_context
* param: address_spend_pubkey - K^j_s
* param: payment_id - pid
* outparam: enote_ephemeral_privkey_out - k_e
*/
void make_carrot_enote_ephemeral_privkey(const janus_anchor_t &anchor_norm,
const input_context_t &input_context,
const crypto::public_key &address_spend_pubkey,
const payment_id_t payment_id,
crypto::secret_key &enote_ephemeral_privkey_out);
/**
* brief: make_carrot_enote_ephemeral_pubkey_main - make enote ephemeral pubkey D_e for a main address
* D_e = d_e B
* param: enote_ephemeral_privkey - d_e
* outparam: enote_ephemeral_pubkey_out - D_e
*/
void make_carrot_enote_ephemeral_pubkey_cryptonote(const crypto::secret_key &enote_ephemeral_privkey,
mx25519_pubkey &enote_ephemeral_pubkey_out);
/**
* brief: make_carrot_enote_ephemeral_pubkey_subaddress - make enote ephemeral pubkey D_e for a subaddress
* D_e = d_e ConvertPointE(K^j_s)
* param: enote_ephemeral_privkey - d_e
* param: address_spend_pubkey - K^j_s
* outparam: enote_ephemeral_pubkey_out - D_e
*/
void make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &enote_ephemeral_privkey,
const crypto::public_key &address_spend_pubkey,
mx25519_pubkey &enote_ephemeral_pubkey_out);
/**
* brief: make_carrot_enote_ephemeral_pubkey - make enote ephemeral pubkey D_e for either either address type
* [is_subaddress]: D_e = d_e ConvertPointE(K^j_s)
* [!is_subaddress]: D_e = d_e B
* param: enote_ephemeral_privkey - d_e
* param: address_spend_pubkey - K^j_s
* param: is_subaddress -
* outparam: enote_ephemeral_pubkey_out - D_e
*/
void make_carrot_enote_ephemeral_pubkey(const crypto::secret_key &enote_ephemeral_privkey,
const crypto::public_key &address_spend_pubkey,
const bool is_subaddress,
mx25519_pubkey &enote_ephemeral_pubkey_out);
/**
* brief: make_carrot_uncontextualized_shared_key_receiver - perform the receiver-side ECDH exchange for Carrot enotes
* s_sr = k_v D_e
* param: k_view - k_v
* param: enote_ephemeral_pubkey - D_e
* outparam: s_sender_receiver_unctx_out - s_sr
* return: true if successful, false if a failure occurred in point decompression
*/
bool make_carrot_uncontextualized_shared_key_receiver(const crypto::secret_key &k_view,
const mx25519_pubkey &enote_ephemeral_pubkey,
mx25519_pubkey &s_sender_receiver_unctx_out);
/**
* brief: make_carrot_uncontextualized_shared_key_sender - perform the sender-side ECDH exchange for Carrot enotes
* s_sr = d_e ConvertPointE(K^j_v)
* param: enote_ephemeral_privkey - d_e
* param: address_view_pubkey - K^j_v
* outparam: s_sender_receiver_unctx_out - s_sr
* return: true if successful, false if a failure occurred in point decompression
*/
bool make_carrot_uncontextualized_shared_key_sender(const crypto::secret_key &enote_ephemeral_privkey,
const crypto::public_key &address_view_pubkey,
mx25519_pubkey &s_sender_receiver_unctx_out);
/**
* brief: make_carrot_view_tag - used for optimized identification of enotes
* vt = H_3(s_sr || input_context || Ko)
* param: s_sender_receiver_unctx - s_sr
* param: input_context - input_context
* param: onetime_address - Ko
* outparam: view_tag_out - vt
*/
void make_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
view_tag_t &view_tag_out);
/**
* brief: make_sparc_return_privkey - return private key, given non-secret data
* k_return = H_32(s_sr || input_context || Ko)
* param: s_sender_receiver_unctx - s_sr
* param: input_context - input_context
* param: onetime_address - Ko
* outparam: return_privkey_out - k_return
*/
void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_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
* param: input_context - input_context
* param: onetime_address - Ko
* outparam: return_pubkey_mask_out - m_return
*/
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,
encrypted_return_pubkey_t &return_pubkey_mask_out);
/**
* brief: make_sparc_return_pubkey - construct the return pubkey
* param: s_sender_receiver_unctx - s_sr
* param: input_context - input_context
* param: s_view_balance_dev - s_vb
* param: onetime_address - Ko
* 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);
/**
* brief: make_carrot_input_context_coinbase - input context for a sender-receiver secret (coinbase txs)
* input_context = "C" || IntToBytes256(block_index)
* param: block_index - block index of the coinbase tx
* return: input_context
*/
input_context_t make_carrot_input_context_coinbase(const std::uint64_t block_index);
/**
* brief: make_carrot_input_context - input context for a sender-receiver secret (standard RingCT txs)
* input_context = "R" || KI_1
* param: first_rct_key_image - KI_1, the first spent RingCT key image in a tx
* return: input_context
*/
input_context_t make_carrot_input_context(const crypto::key_image &first_rct_key_image);
/**
* brief: make_carrot_sender_receiver_secret - contextualized sender-receiver secret s^ctx_sr
* s^ctx_sr = H_32(s_sr, D_e, input_context)
* param: s_sender_receiver_unctx - s_sr
* param: enote_ephemeral_pubkey - D_e
* param: input_context - [standard: KI_1] [coinbase: block index]
* outparam: s_sender_receiver_out - s^ctx_sr
* - note: this is 'crypto::hash' instead of 'crypto::secret_key' for better performance in multithreaded environments
*/
void make_carrot_sender_receiver_secret(const unsigned char s_sender_receiver_unctx[32],
const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
crypto::hash &s_sender_receiver_out);
/**
* brief: make_carrot_onetime_address_extension_g - extension for transforming a receiver's spendkey into an
* enote one-time address
* k^o_g = H_n("..g..", s^ctx_sr, C_a)
* param: s_sender_receiver - s^ctx_sr
* param: amount_commitment - C_a
* outparam: sender_extension_out - k^o_g
*/
void make_carrot_onetime_address_extension_g(const crypto::hash &s_sender_receiver,
const rct::key &amount_commitment,
crypto::secret_key &sender_extension_out);
/**
* brief: make_carrot_onetime_address_extension_t - extension for transforming a receiver's spendkey into an
* enote one-time address
* k^o_t = H_n("..t..", s^ctx_sr, C_a)
* param: s_sender_receiver - s^ctx_sr
* param: amount_commitment - C_a
* outparam: sender_extension_out - k^o_t
*/
void make_carrot_onetime_address_extension_t(const crypto::hash &s_sender_receiver,
const rct::key &amount_commitment,
crypto::secret_key &sender_extension_out);
/**
* brief: make_carrot_onetime_address_extension_pubkey - create a FCMP++ onetime address extension pubkey
* K^o_ext = k^o_g G + k^o_t T
* param: s_sender_receiver - s^ctx_sr
* param: amount_commitment - C_a
* outparam: sender_extension_pubkey_out - K^o_ext
*/
void make_carrot_onetime_address_extension_pubkey(const crypto::hash &s_sender_receiver,
const rct::key &amount_commitment,
crypto::public_key &sender_extension_pubkey_out);
/**
* brief: make_carrot_onetime_address - create a FCMP++ onetime address
* Ko = K^j_s + K^o_ext = K^j_s + (k^o_g G + k^o_t T)
* param: address_spend_pubkey - K^j_s
* param: s_sender_receiver - s^ctx_sr
* param: amount_commitment - C_a
* outparam: onetime_address_out - Ko
*/
void make_carrot_onetime_address(const crypto::public_key &address_spend_pubkey,
const crypto::hash &s_sender_receiver,
const rct::key &amount_commitment,
crypto::public_key &onetime_address_out);
/**
* brief: make_carrot_amount_blinding_factor - create blinding factor for enote's amount commitment C_a
* k_a = H_n(s^ctx_sr, a, K^j_s, enote_type)
* param: s_sender_receiver - s^ctx_sr
* param: amount - a
* param: address_spend_pubkey - K^j_s
* param: enote_type - enote_type
* outparam: amount_blinding_factor_out - k_a
*/
void make_carrot_amount_blinding_factor(const crypto::hash &s_sender_receiver,
const rct::xmr_amount amount,
const crypto::public_key &address_spend_pubkey,
const CarrotEnoteType enote_type,
crypto::secret_key &amount_blinding_factor_out);
/**
* brief: make_carrot_anchor_encryption_mask - create XOR encryption mask for enote's anchor
* m_anchor = H_16(s^ctx_sr, Ko)
* param: s_sender_receiver - s^ctx_sr
* param: onetime_address - Ko
* outparam: anchor_encryption_mask_out - m_anchor
*/
void make_carrot_anchor_encryption_mask(const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address,
encrypted_janus_anchor_t &anchor_encryption_mask_out);
/**
* brief: encrypt_carrot_anchor - encrypt a Janus anchor for an enote
* anchor_enc = anchor XOR m_anchor
* param: anchor -
* param: s_sender_receiver - s^ctx_sr
* param: onetime_address - Ko
* return: anchor_enc
*/
encrypted_janus_anchor_t encrypt_carrot_anchor(const janus_anchor_t &anchor,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address);
/**
* brief: decrypt_carrot_address_tag - decrypt a Janus anchor from an enote
* anchor = anchor_enc XOR m_anchor
* param: encrypted_anchor - anchor_enc
* param: s_sender_receiver - s^ctx_sr
* param: onetime_address - Ko
* return: anchor
*/
janus_anchor_t decrypt_carrot_anchor(const encrypted_janus_anchor_t &encrypted_anchor,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address);
/**
* brief: make_carrot_amount_encryption_mask - create XOR encryption mask for enote's amount
* m_a = H_8(s^ctx_sr, Ko)
* param: s_sender_receiver - s^ctx_sr
* param: onetime_address - Ko
* outparam: amount_encryption_mask_out - m_a
*/
void make_carrot_amount_encryption_mask(const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address,
encrypted_amount_t &amount_encryption_mask_out);
/**
* brief: encrypt_carrot_amount - encrypt an amount for an enote
* a_enc = a XOR m_a
* param: amount - a
* param: s_sender_receiver - s^ctx_sr
* param: onetime_address - Ko
* return: a_enc
*/
encrypted_amount_t encrypt_carrot_amount(const rct::xmr_amount amount,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address);
/**
* brief: decrypt_carrot_amount - decrypt an amount from an enote
* a = a_enc XOR m_a
* param: encrypted_amount - a_enc
* param: s_sender_receiver - s^ctx_sr
* param: onetime_address - Ko
* return: a
*/
rct::xmr_amount decrypt_carrot_amount(const encrypted_amount_t encrypted_amount,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address);
/**
* brief: make_carrot_payment_id_encryption_mask - create XOR encryption mask for enote's payment ID
* m_pid = H_8(s^ctx_sr, Ko)
* param: s_sender_receiver - s^ctx_sr
* param: onetime_address - Ko
* outparam: payment_id_encryption_mask_out - m_pid
*/
void make_carrot_payment_id_encryption_mask(const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address,
encrypted_payment_id_t &payment_id_encryption_mask_out);
/**
* brief: encrypt_legacy_payment_id - encrypt a payment ID from an enote
* pid_enc = pid XOR m_pid
* param: payment_id - pid
* param: s_sender_receiver - s^ctx_sr
* param: onetime_address - Ko
* return: pid_enc
*/
encrypted_payment_id_t encrypt_legacy_payment_id(const payment_id_t payment_id,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address);
/**
* brief: decrypt_legacy_payment_id - decrypt a payment ID from an enote
* pid = pid_enc XOR m_pid
* param: encrypted_payment_id - pid_enc
* param: s_sender_receiver - s^ctx_sr
* param: onetime_address - Ko
* return: pid
*/
payment_id_t decrypt_legacy_payment_id(const encrypted_payment_id_t encrypted_payment_id,
const crypto::hash &s_sender_receiver,
const crypto::public_key &onetime_address);
/**
* brief: make_carrot_janus_anchor_special - make a janus anchor for "special" enotes
* anchor_sp = H_16(D_e, input_context, Ko, k_v)
* param: enote_ephemeral_pubkey - D_e
* param: input_context -
* param: onetime_address - Ko
* param: k_view - k_v
* outparam: anchor_special_out - anchor_sp
*/
void make_carrot_janus_anchor_special(const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const crypto::secret_key &k_view,
janus_anchor_t &anchor_special_out);
/**
* brief: recover_address_spend_pubkey - get the receiver's spend key for which this RingCT onetime address
* can be reconstructed as 'owned' by
* K^j_s = Ko - K^o_ext = Ko - (k^o_g G + k^o_t U)
* param: onetime_address - Ko
* param: s_sender_receiver - s^ctx_sr
* param: amount_commitment - C_a
* outparam: address_spend_key_out: - K^j_s
*/
void recover_address_spend_pubkey(const crypto::public_key &onetime_address,
const crypto::hash &s_sender_receiver,
const rct::key &amount_commitment,
crypto::public_key &address_spend_key_out);
/**
* brief: test_carrot_view_tag - test carrot view tag
* param: s_sender_receiver_unctx - s_sr
* param: input_context -
* param: onetime_address - Ko
* param: view_tag - vt
* return: true if successfully recomputed the view tag
*/
bool test_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
const input_context_t input_context,
const crypto::public_key &onetime_address,
const view_tag_t view_tag);
/**
* brief: try_recompute_carrot_amount_commitment - test recreating the amount commitment for given enote_type and amount
* param: s_sender_receiver - s^ctx_sr
* param: nominal_amount - a'
* param: nominal_address_spend_pubkey - K^j_s'
* param: nominal_enote_type - enote_type'
* param: amount_commitment - C_a
* outparam: amount_blinding_factor_out - k_a' = H_n(s^ctx_sr, enote_type')
* return: true if successfully recomputed the amount commitment (C_a ?= k_a' G + a' H)
*/
bool try_recompute_carrot_amount_commitment(const crypto::hash &s_sender_receiver,
const rct::xmr_amount nominal_amount,
const crypto::public_key &nominal_address_spend_pubkey,
const CarrotEnoteType nominal_enote_type,
const rct::key &amount_commitment,
crypto::secret_key &amount_blinding_factor_out);
/**
* brief: try_get_amount - test decrypting the amount and recomputing the amount commitment
* param: s_sender_receiver - s^ctx_sr
* param: encrypted_amount - a_enc
* param: onetime_address - Ko
* param: address_spend_pubkey - K^j_s
* param: amount_commitment - C_a
* outparam: enote_type_out - enote_type'
* outparam: amount_out - a' = a_enc XOR m_a
* outparam: amount_blinding_factor_out - k_a' = H_n(s^ctx_sr, enote_type')
* return: true if successfully recomputed the amount commitment (C_a ?= k_a' G + a' H)
*/
bool try_get_carrot_amount(const crypto::hash &s_sender_receiver,
const encrypted_amount_t &encrypted_amount,
const crypto::public_key &onetime_address,
const crypto::public_key &address_spend_pubkey,
const rct::key &amount_commitment,
CarrotEnoteType &enote_type_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out);
/**
* brief: verify_carrot_normal_janus_protection - check normal external enote is Janus safe (i.e. can recompute D_e)
* param: nominal_anchor - anchor'
* param: input_context -
* param: nominal_address_spend_pubkey - K^j_s'
* param: is_subaddress -
* param: nominal_payment_id - pid'
* param: enote_ephemeral_pubkey - D_e
* return: true if this normal external enote is safe from Janus attacks
*/
bool verify_carrot_normal_janus_protection(const janus_anchor_t &nominal_anchor,
const input_context_t &input_context,
const crypto::public_key &nominal_address_spend_pubkey,
const bool is_subaddress,
const payment_id_t nominal_payment_id,
const mx25519_pubkey &enote_ephemeral_pubkey);
} //namespace carrot
+76
View File
@@ -0,0 +1,76 @@
// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Exceptions thrown by Carrot
#pragma once
//local headers
//third party headers
//standard headers
#include <stdexcept>
//forward declarations
namespace carrot
{
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: b { using b::b; };
class carrot_logic_error: 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)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(invalid_point, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(missing_components, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(missing_randomness, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_inputs, carrot_logic_error)
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; };
CARROT_DEFINE_SIMPLE_ERROR_TYPE(crypto_function_failed, carrot_runtime_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_money, carrot_runtime_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_usable_money, carrot_runtime_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(unexpected_scan_failure, carrot_runtime_error)
/// one needs to include misc_log_ex.h to use the following macros
#define CARROT_THROW(errtype, message) { \
std::stringstream ss; \
ss << message; \
const std::string msg_str = ss.str(); \
LOG_ERROR(msg_str); \
throw errtype(msg_str); \
}
#define CARROT_CHECK_AND_THROW(expr, errtype, message) if (!(expr)) { CARROT_THROW(errtype, message) }
#undef CARROT_DEFINE_SIMPLE_ERROR_TYPE
} //namespace carrot
+110
View File
@@ -0,0 +1,110 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//paired header
#include "hash_functions.h"
//local headers
extern "C"
{
#include "crypto/crypto-ops.h"
}
#include "crypto/blake2b.h"
#include "exceptions.h"
#include "misc_log_ex.h"
//third party headers
//standard headers
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
// H_x[k](data)
// - if derivation_key == nullptr, then the hash is NOT keyed
//-------------------------------------------------------------------------------------------------------------------
static void hash_base(const void *derivation_key, //32 bytes
const void *data,
const std::size_t data_length,
void *hash_out,
const std::size_t out_length)
{
CARROT_CHECK_AND_THROW(blake2b(hash_out,
out_length,
data,
data_length,
derivation_key,
derivation_key ? 32 : 0) == 0,
crypto_function_failed, "carrot hash base: blake2b failed");
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
void derive_bytes_3(const void *data, const std::size_t data_length, const void *key, void *hash_out)
{
// H_3(x): 2-byte output
hash_base(key, data, data_length, hash_out, 3);
}
//-------------------------------------------------------------------------------------------------------------------
void derive_bytes_8(const void *data, const std::size_t data_length, const void *key, void *hash_out)
{
// H_8(x): 8-byte output
hash_base(key, data, data_length, hash_out, 8);
}
//-------------------------------------------------------------------------------------------------------------------
void derive_bytes_16(const void *data, const std::size_t data_length, const void *key, void *hash_out)
{
// H_16(x): 16-byte output
hash_base(key, data, data_length, hash_out, 16);
}
//-------------------------------------------------------------------------------------------------------------------
void derive_bytes_32(const void *data, const std::size_t data_length, const void *key, void *hash_out)
{
// H_32(x): 32-byte output
hash_base(key, data, data_length, hash_out, 32);
}
//-------------------------------------------------------------------------------------------------------------------
void derive_bytes_64(const void *data, const std::size_t data_length, const void *key, void *hash_out)
{
// H_64(x): 64-byte output
hash_base(key, data, data_length, hash_out, 64);
}
//-------------------------------------------------------------------------------------------------------------------
void derive_scalar(const void *data, const std::size_t data_length, const void *key, void *hash_out)
{
// H_n(x): Ed25519 group scalar output (32 bytes)
// note: hash to 64 bytes then mod l
unsigned char temp[64];
hash_base(key, data, data_length, temp, 64);
sc_reduce(temp); //mod l
memcpy(hash_out, temp, 32);
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+59
View File
@@ -0,0 +1,59 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Core hash functions for Seraphis (note: this implementation satisfies the Jamtis specification).
#pragma once
//local headers
//third party headers
//standard headers
#include <cstddef>
//forward declarations
namespace carrot
{
/// H_3(x): 3-byte output
void derive_bytes_3(const void *data, const std::size_t data_length, const void *key, void *hash_out);
/// H_8(x): 8-byte output
void derive_bytes_8(const void *data, const std::size_t data_length, const void* key, void *hash_out);
/// H_16(x): 16-byte output
void derive_bytes_16(const void *data, const std::size_t data_length, const void *key, void *hash_out);
/// H_32(x): 32-byte output
void derive_bytes_32(const void *data, const std::size_t data_length, const void *key, void *hash_out);
/// H_64(x): 64-byte output
void derive_bytes_64(const void *data, const std::size_t data_length, const void *key, void *hash_out);
/// H_n(x): unclamped Curve25519/Ed25519 group scalar output (32 bytes)
void derive_scalar(const void *data, const std::size_t data_length, const void *key, void *hash_out);
} //namespace carrot
@@ -0,0 +1,58 @@
// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//paired header
#include "lazy_amount_commitment.h"
//local headers
#include "ringct/rctOps.h"
//third party headers
//standard headers
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
rct::key calculate_amount_commitment(const lazy_amount_commitment_t &lazy_amount_commitment)
{
struct lazy_amount_commitment_visitor
{
rct::key operator()(const rct::key &C) const { return C; }
rct::key operator()(const std::pair<rct::xmr_amount, rct::key> &op) const
{ return rct::commit(op.first, op.second); }
rct::key operator()(const rct::xmr_amount &a) const { return rct::zeroCommit(a); }
};
return std::visit(lazy_amount_commitment_visitor{}, lazy_amount_commitment);
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+53
View File
@@ -0,0 +1,53 @@
// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
//local headers
#include "ringct/rctTypes.h"
//third party headers
//standard headers
#include <utility>
#include <variant>
//forward declarations
namespace carrot
{
using lazy_amount_commitment_t = std::variant<
rct::key, // C
std::pair<rct::xmr_amount, rct::key>, // (a, z) s.t. C = z G + a H
rct::xmr_amount // a s.t. C = G + a H
>;
rct::key calculate_amount_commitment(const lazy_amount_commitment_t &lazy_amount_commitment);
} //namespace carrot
+447
View File
@@ -0,0 +1,447 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//paired header
#include "output_set_finalization.h"
//local headers
#include "common/container_helpers.h"
#include "enote_utils.h"
#include "exceptions.h"
#include "misc_log_ex.h"
#include "ringct/rctOps.h"
//third party headers
//standard headers
#include <set>
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot.osf"
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
template <typename T>
struct compare_memcmp{ bool operator()(const T &a, const T &b) const { return memcmp(&a, &b, sizeof(T)) < 0; } };
template <typename T>
using memcmp_set = std::set<T, compare_memcmp<T>>;
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_outgoing,
const size_t num_selfsend,
const bool need_change_output,
const bool have_payment_type_selfsend)
{
const size_t num_outputs = num_outgoing + num_selfsend;
const bool already_completed = num_outputs >= 2 && num_selfsend >= 1 && !need_change_output;
if (num_outputs == 0)
{
CARROT_THROW(too_few_outputs, "set contains 0 outputs");
}
else if (already_completed)
{
return std::nullopt;
}
else if (num_outputs == 1)
{
if (num_selfsend == 0)
{
return AdditionalOutputType::CHANGE_SHARED;
}
else if (!need_change_output)
{
return AdditionalOutputType::DUMMY;
}
else // num_selfsend == 1 && need_change_output
{
if (have_payment_type_selfsend)
{
return AdditionalOutputType::CHANGE_SHARED;
}
else
{
return AdditionalOutputType::PAYMENT_SHARED;
}
}
}
else if (num_outputs < CARROT_MAX_TX_OUTPUTS)
{
return AdditionalOutputType::CHANGE_UNIQUE;
}
else // num_outputs >= CARROT_MAX_TX_OUTPUTS
{
CARROT_THROW(too_many_outputs, "set needs finalization but already contains too many outputs");
}
}
//-------------------------------------------------------------------------------------------------------------------
tools::optional_variant<CarrotPaymentProposalV1, CarrotPaymentProposalSelfSendV1> get_additional_output_proposal(
const size_t num_outgoing,
const size_t num_selfsend,
const rct::xmr_amount needed_change_amount,
const bool have_payment_type_selfsend,
const crypto::public_key &change_address_spend_pubkey)
{
const std::optional<AdditionalOutputType> additional_output_type = get_additional_output_type(
num_outgoing,
num_selfsend,
needed_change_amount,
have_payment_type_selfsend
);
if (!additional_output_type)
return {};
switch (*additional_output_type)
{
case AdditionalOutputType::PAYMENT_SHARED:
return CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = change_address_spend_pubkey,
.amount = needed_change_amount,
.enote_type = CarrotEnoteType::PAYMENT,
.enote_ephemeral_pubkey = std::nullopt
};
case AdditionalOutputType::CHANGE_SHARED:
return CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = change_address_spend_pubkey,
.amount = needed_change_amount,
.enote_type = CarrotEnoteType::CHANGE,
.enote_ephemeral_pubkey = std::nullopt
};
case AdditionalOutputType::CHANGE_UNIQUE:
return CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = change_address_spend_pubkey,
.amount = needed_change_amount,
.enote_type = CarrotEnoteType::CHANGE,
.enote_ephemeral_pubkey = std::nullopt
};
case AdditionalOutputType::DUMMY:
return CarrotPaymentProposalV1{
.destination = gen_carrot_main_address_v1(),
.amount = 0,
.randomness = gen_janus_anchor()
};
}
CARROT_THROW(std::invalid_argument, "unrecognized additional output type");
}
//-------------------------------------------------------------------------------------------------------------------
void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
const std::vector<CarrotPaymentProposalSelfSendV1> &selfsend_payment_proposals,
const std::optional<encrypted_payment_id_t> &dummy_encrypted_payment_id,
const view_balance_secret_device *s_view_balance_dev,
const view_incoming_key_device *k_view_dev,
const crypto::key_image &tx_first_key_image,
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
encrypted_payment_id_t &encrypted_payment_id_out,
cryptonote::transaction_type tx_type,
size_t &change_index_out,
std::unordered_map<crypto::public_key, size_t> &payments_indices_out,
std::vector<std::pair<bool, std::size_t>> *payment_proposal_order_out)
{
output_enote_proposals_out.clear();
encrypted_payment_id_out = {{0}};
if (payment_proposal_order_out)
payment_proposal_order_out->clear();
// assert payment proposals numbers
const size_t num_selfsend_proposals = selfsend_payment_proposals.size();
const size_t num_proposals = normal_payment_proposals.size() + num_selfsend_proposals;
CARROT_CHECK_AND_THROW(num_proposals >= CARROT_MIN_TX_OUTPUTS, too_few_outputs, "too few payment proposals");
CARROT_CHECK_AND_THROW(num_proposals <= CARROT_MAX_TX_OUTPUTS, too_many_outputs, "too many payment proposals");
CARROT_CHECK_AND_THROW(num_selfsend_proposals, too_few_outputs, "no selfsend payment proposal");
// assert there is a max of 1 integrated address payment proposals
size_t num_integrated = 0;
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
if (normal_payment_proposal.destination.payment_id != null_payment_id)
++num_integrated;
CARROT_CHECK_AND_THROW(num_integrated <= 1,
bad_address_type, "only one integrated address is allowed per tx output set");
// assert anchor_norm != 0 for payments
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
CARROT_CHECK_AND_THROW(normal_payment_proposal.randomness != janus_anchor_t{},
missing_randomness, "normal payment proposal has unset anchor_norm AKA randomness");
// assert uniqueness of randomness for each payment
memcmp_set<janus_anchor_t> randomnesses;
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
randomnesses.insert(normal_payment_proposal.randomness);
const bool has_unique_randomness = randomnesses.size() == normal_payment_proposals.size();
CARROT_CHECK_AND_THROW(has_unique_randomness,
missing_randomness, "normal payment proposals contain duplicate anchor_norm AKA randomness");
// for each output: (enote proposal , is ss?, payment idx )
std::vector<std::pair<RCTOutputEnoteProposal, std::pair<bool, size_t>>> sortable_data;
sortable_data.reserve(num_proposals);
// D^other_e
std::optional<mx25519_pubkey> other_enote_ephemeral_pubkey;
// map destinations to output keys in order to find the indices of these outputs in tx
std::unordered_map<crypto::public_key, crypto::public_key> output_destinations_to_keys;
// construct normal enotes
for (size_t i = 0; i < normal_payment_proposals.size(); ++i)
{
auto &output_entry = tools::add_element(sortable_data);
output_entry.second = {false, i};
encrypted_payment_id_t encrypted_payment_id;
if (tx_type == cryptonote::transaction_type::RETURN) {
get_output_proposal_return_v1(normal_payment_proposals[i],
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,
s_view_balance_dev,
output_entry.first,
encrypted_payment_id);
}
// if 1 normal, and 2 self-send, set D^other_e equal to this D_e
if (num_proposals == 2)
other_enote_ephemeral_pubkey = output_entry.first.enote.enote_ephemeral_pubkey;
// set pid_enc from integrated address proposal pic_enc
const bool is_integrated = normal_payment_proposals[i].destination.payment_id != null_payment_id;
if (is_integrated)
encrypted_payment_id_out = encrypted_payment_id;
// save the one time key for this destination
output_destinations_to_keys.insert(
{output_entry.first.enote.onetime_address, normal_payment_proposals[i].destination.address_spend_pubkey}
);
}
// in the case that there is no required pid_enc, set it to the provided dummy
if (0 == num_integrated)
{
CARROT_CHECK_AND_THROW(dummy_encrypted_payment_id,
missing_components, "missing encrypted payment ID: no integrated address nor provided dummy");
encrypted_payment_id_out = *dummy_encrypted_payment_id;
}
// if 0 normal, and 2 self-send, set D^other_e equal to whichever *has* a D_e
if (num_proposals == 2 && num_selfsend_proposals == 2)
{
const size_t present_ephem_pk_index = selfsend_payment_proposals.at(0).enote_ephemeral_pubkey ? 0 : 1;
other_enote_ephemeral_pubkey = selfsend_payment_proposals.at(present_ephem_pk_index).enote_ephemeral_pubkey;
}
// construct selfsend enotes, preferring internal enotes over special enotes when possible
crypto::public_key change_address;
for (size_t i = 0; i < num_selfsend_proposals; ++i)
{
const CarrotPaymentProposalSelfSendV1 &selfsend_payment_proposal = selfsend_payment_proposals.at(i);
auto &output_entry = tools::add_element(sortable_data);
output_entry.second = {true, i};
if (s_view_balance_dev != nullptr)
{
get_output_proposal_internal_v1(selfsend_payment_proposal,
*s_view_balance_dev,
tx_first_key_image,
other_enote_ephemeral_pubkey,
output_entry.first);
}
else if (k_view_dev != nullptr)
{
get_output_proposal_special_v1(selfsend_payment_proposal,
*k_view_dev,
tx_first_key_image,
other_enote_ephemeral_pubkey,
output_entry.first);
}
else // neither k_v nor s_vb device passed
{
CARROT_THROW(std::invalid_argument, "neither a view-balance nor view-incoming device was provided");
}
// save the change one time key
if (selfsend_payment_proposal.enote_type == CarrotEnoteType::CHANGE)
{
change_address = output_entry.first.enote.onetime_address;
}
// save the one time key for this destination
output_destinations_to_keys.insert(
{output_entry.first.enote.onetime_address, selfsend_payment_proposal.destination_address_spend_pubkey}
);
}
// sort enotes by K_o
const auto sort_output_enote_proposal = [](const auto &a, const auto &b) -> bool
{ return a.first.enote.onetime_address < b.first.enote.onetime_address; };
std::sort(sortable_data.begin(), sortable_data.end(), sort_output_enote_proposal);
// get the change index
for (size_t i = 0; i < sortable_data.size(); ++i)
{
if (sortable_data[i].first.enote.onetime_address == change_address)
{
change_index_out = i;
}
// map destinations to output indices
const auto spend_key = output_destinations_to_keys[sortable_data[i].first.enote.onetime_address];
payments_indices_out.insert(
{spend_key, i}
);
}
// collect output_enote_proposals_out and payment_proposal_order_out
output_enote_proposals_out.reserve(num_proposals);
if (payment_proposal_order_out)
payment_proposal_order_out->reserve(num_proposals);
for (const auto &output_entry : sortable_data)
{
output_enote_proposals_out.push_back(output_entry.first);
if (payment_proposal_order_out)
payment_proposal_order_out->push_back(output_entry.second);
}
// assert uniqueness of D_e if >2-out, shared otherwise. also check D_e is not trivial
memcmp_set<mx25519_pubkey> ephemeral_pubkeys;
for (const RCTOutputEnoteProposal &p : output_enote_proposals_out)
{
const bool trivial_enote_ephemeral_pubkey = memcmp(p.enote.enote_ephemeral_pubkey.data,
mx25519_pubkey{}.data,
sizeof(mx25519_pubkey)) == 0;
CARROT_CHECK_AND_THROW(!trivial_enote_ephemeral_pubkey, missing_randomness,
"this set contains enote ephemeral pubkeys with x=0");
ephemeral_pubkeys.insert(p.enote.enote_ephemeral_pubkey);
}
const bool has_unique_ephemeral_pubkeys = ephemeral_pubkeys.size() == output_enote_proposals_out.size();
CARROT_CHECK_AND_THROW(!(num_proposals == 2 && has_unique_ephemeral_pubkeys),
component_out_of_order, "this 2-out set needs to share their ephemeral pubkey");
CARROT_CHECK_AND_THROW(!(num_proposals != 2 && !has_unique_ephemeral_pubkeys),
missing_randomness, "this >2-out set contains duplicate enote ephemeral pubkeys");
// assert uniqueness of K_o
CARROT_CHECK_AND_THROW(tools::is_sorted_and_unique(sortable_data, sort_output_enote_proposal),
component_out_of_order, "this set contains duplicate onetime addresses");
// assert all K_o lie in prime order subgroup
for (const RCTOutputEnoteProposal &output_enote_proposal : output_enote_proposals_out)
{
CARROT_CHECK_AND_THROW(rct::isInMainSubgroup(rct::pk2rct(output_enote_proposal.enote.onetime_address)),
invalid_point, "this set contains an invalid onetime address");
}
// assert unique and non-trivial k_a
memcmp_set<crypto::secret_key> amount_blinding_factors;
for (const RCTOutputEnoteProposal &output_enote_proposal : output_enote_proposals_out)
{
CARROT_CHECK_AND_THROW(output_enote_proposal.amount_blinding_factor != crypto::null_skey,
missing_randomness, "this set contains a trivial amount blinding factor");
amount_blinding_factors.insert(output_enote_proposal.amount_blinding_factor);
}
CARROT_CHECK_AND_THROW(amount_blinding_factors.size() == num_proposals, missing_randomness,
"this set contains duplicate amount blinding factors");
}
//-------------------------------------------------------------------------------------------------------------------
void get_coinbase_output_enotes(const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
const uint64_t block_index,
std::vector<CarrotCoinbaseEnoteV1> &output_coinbase_enotes_out)
{
output_coinbase_enotes_out.clear();
// assert payment proposals numbers
const size_t num_proposals = normal_payment_proposals.size();
// assert there is a max of 1 integrated address payment proposals
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
{
const CarrotDestinationV1 &destination = normal_payment_proposal.destination;
CARROT_CHECK_AND_THROW(destination.payment_id == null_payment_id && !destination.is_subaddress,
bad_address_type, "get coinbase output enotes: no integrated addresses or subaddresses allowed");
}
// assert anchor_norm != 0 for payments
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
CARROT_CHECK_AND_THROW(normal_payment_proposal.randomness != janus_anchor_t{},
missing_randomness, "normal payment proposal has unset anchor_norm AKA randomness");
// assert uniqueness of randomness for each payment
memcmp_set<janus_anchor_t> randomnesses;
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
randomnesses.insert(normal_payment_proposal.randomness);
const bool has_unique_randomness = randomnesses.size() == normal_payment_proposals.size();
CARROT_CHECK_AND_THROW(has_unique_randomness,
missing_randomness, "normal payment proposals contain duplicate anchor_norm AKA randomness");
// construct normal enotes
output_coinbase_enotes_out.reserve(num_proposals);
for (size_t i = 0; i < normal_payment_proposals.size(); ++i)
{
get_coinbase_output_proposal_v1(normal_payment_proposals[i],
block_index,
tools::add_element(output_coinbase_enotes_out));
}
// assert uniqueness and non-trivial-ness of D_e
memcmp_set<mx25519_pubkey> ephemeral_pubkeys;
for (const CarrotCoinbaseEnoteV1 &enote : output_coinbase_enotes_out)
{
const bool trivial_enote_ephemeral_pubkey = memcmp(enote.enote_ephemeral_pubkey.data,
mx25519_pubkey{}.data,
sizeof(mx25519_pubkey)) == 0;
CARROT_CHECK_AND_THROW(!trivial_enote_ephemeral_pubkey,
missing_randomness, "this set contains enote ephemeral pubkeys with x=0");
ephemeral_pubkeys.insert(enote.enote_ephemeral_pubkey);
}
const bool has_unique_ephemeral_pubkeys = ephemeral_pubkeys.size() == output_coinbase_enotes_out.size();
CARROT_CHECK_AND_THROW(has_unique_ephemeral_pubkeys,
missing_randomness, "a coinbase enote set needs unique ephemeral pubkeys, but this set doesn't");
// sort enotes by K_o
const auto sort_output_enote_proposal = [](const CarrotCoinbaseEnoteV1 &a, const CarrotCoinbaseEnoteV1 &b)
-> bool { return a.onetime_address < b.onetime_address; };
std::sort(output_coinbase_enotes_out.begin(), output_coinbase_enotes_out.end(), sort_output_enote_proposal);
// assert uniqueness of K_o
CARROT_CHECK_AND_THROW(tools::is_sorted_and_unique(output_coinbase_enotes_out, sort_output_enote_proposal),
component_out_of_order, "this set contains duplicate onetime addresses");
// assert all K_o lie in prime order subgroup
for (const CarrotCoinbaseEnoteV1 &output_enote : output_coinbase_enotes_out)
{
CARROT_CHECK_AND_THROW(rct::isInMainSubgroup(rct::pk2rct(output_enote.onetime_address)),
invalid_point, "this set contains an invalid onetime address");
}
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+128
View File
@@ -0,0 +1,128 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Utilities for constructing output proposal sets that adhere to Carrot rules
#pragma once
//local headers
#include "carrot_enote_types.h"
#include "common/variant.h"
#include "config.h"
#include "payment_proposal.h"
#include "ringct/rctTypes.h"
//third party headers
//standard headers
#include <optional>
#include <vector>
//forward declarations
namespace carrot
{
enum class AdditionalOutputType
{
PAYMENT_SHARED, // selfsend proposal with enote_type="payment" with a shared D_e
CHANGE_SHARED, // selfsend proposal with enote_type="change" with a shared D_e
CHANGE_UNIQUE, // selfsend proposal with enote_type="change" with a unique D_e
DUMMY // outgoing proposal to a random address
};
/**
* brief: get_additional_output_type - get the type of the additional enote needed to finalize an output set
* param: num_outgoing - number of outgoing transfers
* param: num_selfsend - number of selfsend transfers
* param: need_change_output - whether an additional change output needs to be included for balance
* param: have_payment_type_selfsend - true if the enote set has a selfsend enote with enote_type="payment"
* return: AdditionalOutputType if need an additional enote, else std::nullopt
* throw: std::runtime_error if the output set is in a state where it cannot be finalized
*/
std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_outgoing,
const size_t num_selfsend,
const bool need_change_output,
const bool have_payment_type_selfsend);
/**
* brief: get_additional_output_proposal - get an additional output proposal to complete an output set
* param: num_outgoing - number of outgoing transfers
* param: num_selfsend - number of selfsend transfers
* param: needed_change_amount - the amount of leftover change needed to be included
* param: have_payment_type_selfsend - true if the enote set has a selfsend enote with enote_type="payment"
* param: change_address_spend_pubkey - K^j_s of our change address
* return: an output proposal if need an additional enote, else none
* throw: std::runtime_error if the output set is in a state where it cannot be finalized
*/
tools::optional_variant<CarrotPaymentProposalV1, CarrotPaymentProposalSelfSendV1> get_additional_output_proposal(
const size_t num_outgoing,
const size_t num_selfsend,
const rct::xmr_amount needed_change_amount,
const bool have_payment_type_selfsend,
const crypto::public_key &change_address_spend_pubkey);
/**
* brief: get_output_enote_proposals - convert a *finalized* set of payment proposals into output enote proposals
* param: normal_payment_proposals -
* param: selfsend_payment_proposals -
* param: dummy_encrypted_payment_id - random pid_enc, required if no integrated addresses in normal payment proposals
* param: s_view_balance_dev - pointer to view-balance device (OPTIONAL)
* param: k_view_dev - pointer to view-incoming device (OPTIONAL)
* param: tx_first_key_image - KI_1
* outparam: output_enote_proposals_out -
* outparam: encrypted_payment_id_out - pid_enc
* outparam: payment_proposal_order_out - (is self-send, payment idx) pairs which specify order of proposals
* converted to output enotes (OPTIONAL)
* throw: std::runtime_error if the payment proposals do not represent a valid tx output set, or if no devices
*
* If s_view_balance_dev is not NULL, then the selfsend payments are converted into *internal* enotes.
* Otherwise, if k_view_dev is not NULL, then the selfsend payments are converted into *external* enotes.
*/
void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
const std::vector<CarrotPaymentProposalSelfSendV1> &selfsend_payment_proposals,
const std::optional<encrypted_payment_id_t> &dummy_encrypted_payment_id,
const view_balance_secret_device *s_view_balance_dev,
const view_incoming_key_device *k_view_dev,
const crypto::key_image &tx_first_key_image,
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
encrypted_payment_id_t &encrypted_payment_id_out,
cryptonote::transaction_type tx_type,
size_t &change_index_out,
std::unordered_map<crypto::public_key, size_t> &payments_indices_out,
std::vector<std::pair<bool, std::size_t>> *payment_proposal_order_out = nullptr);
/**
* brief: get_coinbase_output_enotes - convert a *finalized* set of payment proposals into coinbase output enotes
* param: normal_payment_proposals -
* param: block_index -
* outparam: output_coinbase_enotes_out -
* throw: std::runtime_error if the payment proposals do not represent a valid tx output set, or if no devices
*/
void get_coinbase_output_enotes(const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
const uint64_t block_index,
std::vector<CarrotCoinbaseEnoteV1> &output_coinbase_enotes_out);
} //namespace carrot
+585
View File
@@ -0,0 +1,585 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//paired header
#include "payment_proposal.h"
//local headers
#include "int-util.h"
#include "enote_utils.h"
#include "exceptions.h"
#include "misc_language.h"
#include "misc_log_ex.h"
#include "ringct/rctOps.h"
//third party headers
//standard headers
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot.pp"
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static const janus_anchor_t null_anchor{{0}};
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
template <typename T>
static auto auto_wiper(T &obj)
{
static_assert(std::is_trivially_copyable<T>());
return epee::misc_utils::create_scope_leave_handler([&]{ memwipe(&obj, sizeof(T)); });
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static crypto::secret_key get_enote_ephemeral_privkey(const janus_anchor_t randomness,
const CarrotDestinationV1 &destination,
const input_context_t &input_context)
{
// d_e = H_n(anchor_norm, input_context, K^j_s, pid))
crypto::secret_key enote_ephemeral_privkey;
make_carrot_enote_ephemeral_privkey(randomness,
input_context,
destination.address_spend_pubkey,
destination.payment_id,
enote_ephemeral_privkey);
return enote_ephemeral_privkey;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static mx25519_pubkey get_enote_ephemeral_pubkey(const janus_anchor_t randomness,
const CarrotDestinationV1 &destination,
const input_context_t &input_context)
{
// d_e = H_n(anchor_norm, input_context, K^j_s, pid))
const crypto::secret_key enote_ephemeral_privkey{get_enote_ephemeral_privkey(randomness,
destination,
input_context)};
mx25519_pubkey enote_ephemeral_pubkey;
if (destination.is_subaddress)
// D_e = d_e ConvertPointE(K^j_s)
make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey,
destination.address_spend_pubkey,
enote_ephemeral_pubkey);
else
// D_e = d_e B
make_carrot_enote_ephemeral_pubkey_cryptonote(enote_ephemeral_privkey,
enote_ephemeral_pubkey);
return enote_ephemeral_pubkey;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static void get_normal_proposal_ecdh_parts(const CarrotPaymentProposalV1 &proposal,
const input_context_t &input_context,
mx25519_pubkey &enote_ephemeral_pubkey_out,
mx25519_pubkey &s_sender_receiver_unctx_out)
{
// 1. d_e = H_n(anchor_norm, input_context, K^j_s, pid))
const crypto::secret_key enote_ephemeral_privkey = get_enote_ephemeral_privkey(proposal.randomness,
proposal.destination,
input_context);
// 2. make D_e
enote_ephemeral_pubkey_out = get_enote_ephemeral_pubkey(proposal, input_context);
// 3. s_sr = d_e ConvertPointE(K^j_v)
make_carrot_uncontextualized_shared_key_sender(enote_ephemeral_privkey,
proposal.destination.address_view_pubkey,
s_sender_receiver_unctx_out);
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static void get_output_proposal_parts(const crypto::hash &s_sender_receiver,
const crypto::public_key &destination_spend_pubkey,
const payment_id_t payment_id,
const rct::xmr_amount amount,
const CarrotEnoteType enote_type,
const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const bool coinbase_amount_commitment,
crypto::secret_key &amount_blinding_factor_out,
rct::key &amount_commitment_out,
crypto::public_key &onetime_address_out,
encrypted_amount_t &encrypted_amount_out,
encrypted_payment_id_t &encrypted_payment_id_out)
{
// 1. k_a = H_n(s^ctx_sr, a, K^j_s, enote_type) if !coinbase, else 1
if (coinbase_amount_commitment)
sc_1(to_bytes(amount_blinding_factor_out));
else
make_carrot_amount_blinding_factor(s_sender_receiver,
amount,
destination_spend_pubkey,
enote_type,
amount_blinding_factor_out);
// 2. C_a = k_a G + a H
amount_commitment_out = rct::commit(amount, rct::sk2rct(amount_blinding_factor_out));
// 3. Ko = K^j_s + K^o_ext = K^j_s + (k^o_g G + k^o_t T)
make_carrot_onetime_address(destination_spend_pubkey,
s_sender_receiver,
amount_commitment_out,
onetime_address_out);
// 4. a_enc = a XOR m_a
encrypted_amount_out = encrypt_carrot_amount(amount,
s_sender_receiver,
onetime_address_out);
// 5. pid_enc = pid XOR m_pid
encrypted_payment_id_out = encrypt_legacy_payment_id(payment_id, s_sender_receiver, onetime_address_out);
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static void get_external_output_proposal_parts(const mx25519_pubkey &s_sender_receiver_unctx,
const crypto::public_key &destination_spend_pubkey,
const payment_id_t payment_id,
const rct::xmr_amount amount,
const CarrotEnoteType enote_type,
const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const view_balance_secret_device *s_view_balance_dev,
const bool coinbase_amount_commitment,
crypto::hash &s_sender_receiver_out,
crypto::secret_key &amount_blinding_factor_out,
rct::key &amount_commitment_out,
crypto::public_key &onetime_address_out,
encrypted_amount_t &encrypted_amount_out,
encrypted_payment_id_t &encrypted_payment_id_out,
view_tag_t &view_tag_out,
encrypted_return_pubkey_t return_pubkey_out)
{
// 1. s^ctx_sr = H_32(s_sr, D_e, input_context)
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
enote_ephemeral_pubkey,
input_context,
s_sender_receiver_out);
// 2. get other parts: k_a, C_a, Ko, a_enc, pid_enc
get_output_proposal_parts(s_sender_receiver_out,
destination_spend_pubkey,
payment_id,
amount,
enote_type,
enote_ephemeral_pubkey,
input_context,
coinbase_amount_commitment,
amount_blinding_factor_out,
amount_commitment_out,
onetime_address_out,
encrypted_amount_out,
encrypted_payment_id_out);
// 3. vt = H_3(s_sr || input_context || Ko)
make_carrot_view_tag(s_sender_receiver_unctx.data, input_context, onetime_address_out, view_tag_out);
// 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);
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1 &b)
{
return a.destination == b.destination &&
a.amount == b.amount &&
a.randomness == b.randomness;
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentProposalSelfSendV1 &b)
{
return a.destination_address_spend_pubkey == b.destination_address_spend_pubkey &&
a.amount == b.amount &&
a.enote_type == b.enote_type &&
a.internal_message == b.internal_message &&
0 == memcmp(&a.enote_ephemeral_pubkey, &b.enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
}
//-------------------------------------------------------------------------------------------------------------------
crypto::secret_key get_enote_ephemeral_privkey(const CarrotPaymentProposalV1 &proposal,
const input_context_t &input_context)
{
return get_enote_ephemeral_privkey(proposal.randomness, proposal.destination, input_context);
}
//-------------------------------------------------------------------------------------------------------------------
mx25519_pubkey get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
const input_context_t &input_context)
{
return get_enote_ephemeral_pubkey(proposal.randomness, proposal.destination, input_context);
}
//-------------------------------------------------------------------------------------------------------------------
void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
const std::uint64_t block_index,
CarrotCoinbaseEnoteV1 &output_enote_out)
{
// 1. sanity checks
CARROT_CHECK_AND_THROW(proposal.randomness != null_anchor,
missing_randomness, "invalid randomness for janus anchor (zero).");
CARROT_CHECK_AND_THROW(!proposal.destination.is_subaddress,
bad_address_type, "subaddresses aren't allowed as destinations of coinbase outputs");
CARROT_CHECK_AND_THROW(proposal.destination.payment_id == null_payment_id,
bad_address_type, "integrated addresses aren't allowed as destinations of coinbase outputs");
// 2. coinbase input context
const input_context_t input_context= make_carrot_input_context_coinbase(block_index);
// 3. make D_e and do external ECDH
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_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_out;
crypto::secret_key dummy_amount_blinding_factor;
rct::key dummy_amount_commitment;
encrypted_amount_t dummy_encrypted_amount;
encrypted_payment_id_t dummy_encrypted_payment_id;
get_external_output_proposal_parts(s_sender_receiver_unctx,
proposal.destination.address_spend_pubkey,
null_payment_id,
proposal.amount,
CarrotEnoteType::PAYMENT,
output_enote_out.enote_ephemeral_pubkey,
input_context,
nullptr, // s_view_balance_dev
true, // coinbase_amount_commitment
s_sender_receiver,
dummy_amount_blinding_factor,
dummy_amount_commitment,
output_enote_out.onetime_address,
dummy_encrypted_amount,
dummy_encrypted_payment_id,
output_enote_out.view_tag,
return_pubkey_out);
// 5. anchor_enc = anchor XOR m_anchor
output_enote_out.anchor_enc = encrypt_carrot_anchor(proposal.randomness,
s_sender_receiver,
output_enote_out.onetime_address);
// 6. save the amount and block index and asset type
output_enote_out.amount = proposal.amount;
output_enote_out.asset_type = proposal.asset_type;
output_enote_out.block_index = block_index;
}
//-------------------------------------------------------------------------------------------------------------------
void get_output_proposal_normal_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)
{
// 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);
// 3. make D_e and do external ECDH
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);
// 5. 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);
// 6. save the amount and first key image
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;
output_enote_out.enote.return_enc = return_pubkey;
}
//-------------------------------------------------------------------------------------------------------------------
void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
const view_incoming_key_device &k_view_dev,
const crypto::key_image &tx_first_key_image,
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
RCTOutputEnoteProposal &output_enote_out)
{
// 1. sanity checks
CARROT_CHECK_AND_THROW(!proposal.internal_message,
component_out_of_order, "internal messages are only for internal selfsends, not special selfsends");
// 2. input context: input_context = "R" || KI_1
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
// 3. D_e
const bool missing_enote_ephemeral_pubkeys = !proposal.enote_ephemeral_pubkey && !other_enote_ephemeral_pubkey;
const bool mismatched_enote_ephemeral_pubkeys = proposal.enote_ephemeral_pubkey &&
other_enote_ephemeral_pubkey &&
memcmp(&*proposal.enote_ephemeral_pubkey, &*other_enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
CARROT_CHECK_AND_THROW(!missing_enote_ephemeral_pubkeys,
missing_components, "no enote ephemeral pubkey provided");
CARROT_CHECK_AND_THROW(!mismatched_enote_ephemeral_pubkeys,
component_out_of_order, "mismatched enote ephemeral pubkeys provided");
const mx25519_pubkey enote_ephemeral_pubkey = proposal.enote_ephemeral_pubkey.value_or(
other_enote_ephemeral_pubkey.value_or(mx25519_pubkey{}));
// 4. s_sr = k_v D_e
mx25519_pubkey s_sender_receiver_unctx; auto ecdh_wiper = auto_wiper(s_sender_receiver_unctx);
CARROT_CHECK_AND_THROW(k_view_dev.view_key_scalar_mult_x25519(enote_ephemeral_pubkey, s_sender_receiver_unctx),
crypto_function_failed, "HW device failed to perform ECDH with ephemeral pubkey");
// 5. 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_out;
encrypted_payment_id_t dummy_encrypted_payment_id;
get_external_output_proposal_parts(s_sender_receiver_unctx,
proposal.destination_address_spend_pubkey,
null_payment_id,
proposal.amount,
proposal.enote_type,
enote_ephemeral_pubkey,
input_context,
nullptr,
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,
dummy_encrypted_payment_id,
output_enote_out.enote.view_tag,
return_pubkey_out);
// 6. make special janus anchor: anchor_sp = H_16(D_e, input_context, Ko, k_v)
janus_anchor_t janus_anchor_special;
k_view_dev.make_janus_anchor_special(enote_ephemeral_pubkey,
input_context,
output_enote_out.enote.onetime_address,
janus_anchor_special);
// 7. encrypt special anchor: anchor_enc = anchor XOR m_anchor
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(janus_anchor_special,
s_sender_receiver,
output_enote_out.enote.onetime_address);
// 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.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
output_enote_out.amount = proposal.amount;
}
//-------------------------------------------------------------------------------------------------------------------
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)
{
// 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);
// 3. make D_e and do external ECDH
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
);
// 5. Override the values that change because of the enote onetime address (K_o) changing
// i.e. {K_o, vt, m_a, a_enc, m_anchor, anchor_enc, m_pid, pid_enc}
// Override the onetime address
output_enote_out.enote.onetime_address = rct::rct2pk(rct::addKeys(rct::pk2rct(proposal.destination.address_spend_pubkey), rct::pk2rct(proposal.destination.address_view_pubkey)));
// 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_internal_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
const view_balance_secret_device &s_view_balance_dev,
const crypto::key_image &tx_first_key_image,
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
RCTOutputEnoteProposal &output_enote_out)
{
// 1. sanity checks
// @TODO
// 2. input_context = "R" || KI_1
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
// 3. D_e
const bool missing_enote_ephemeral_pubkeys = !proposal.enote_ephemeral_pubkey && !other_enote_ephemeral_pubkey;
const bool mismatched_enote_ephemeral_pubkeys = proposal.enote_ephemeral_pubkey &&
other_enote_ephemeral_pubkey &&
memcmp(&*proposal.enote_ephemeral_pubkey, &*other_enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
CARROT_CHECK_AND_THROW(!missing_enote_ephemeral_pubkeys,
missing_components, "no enote ephemeral pubkey provided");
CARROT_CHECK_AND_THROW(!mismatched_enote_ephemeral_pubkeys,
component_out_of_order, "mismatched enote ephemeral pubkeys provided");
const mx25519_pubkey enote_ephemeral_pubkey = proposal.enote_ephemeral_pubkey.value_or(
other_enote_ephemeral_pubkey.value_or(mx25519_pubkey{}));
// 4. s^ctx_sr = H_32(s_vb, D_e, input_context)
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
s_view_balance_dev.make_internal_sender_receiver_secret(enote_ephemeral_pubkey,
input_context,
s_sender_receiver);
// 5. build the output enote address pieces
encrypted_payment_id_t dummy_encrypted_payment_id;
get_output_proposal_parts(s_sender_receiver,
proposal.destination_address_spend_pubkey,
null_payment_id,
proposal.amount,
proposal.enote_type,
enote_ephemeral_pubkey,
input_context,
false, // coinbase_amount_commitment
output_enote_out.amount_blinding_factor,
output_enote_out.enote.amount_commitment,
output_enote_out.enote.onetime_address,
output_enote_out.enote.amount_enc,
dummy_encrypted_payment_id);
// 6. vt = H_3(s_vb || input_context || Ko)
s_view_balance_dev.make_internal_view_tag(input_context,
output_enote_out.enote.onetime_address,
output_enote_out.enote.view_tag);
// 7. anchor = given message OR 0s, if not available
const janus_anchor_t anchor = proposal.internal_message.value_or(janus_anchor_t{});
// 8. encrypt anchor: anchor_enc = anchor XOR m_anchor
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(anchor,
s_sender_receiver,
output_enote_out.enote.onetime_address);
// 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.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
output_enote_out.amount = proposal.amount;
}
//-------------------------------------------------------------------------------------------------------------------
CarrotPaymentProposalV1 gen_carrot_payment_proposal_v1(const bool is_subaddress,
const bool has_payment_id,
const rct::xmr_amount amount,
const std::size_t num_random_memo_elements)
{
CarrotPaymentProposalV1 temp;
if (is_subaddress)
temp.destination = gen_carrot_subaddress_v1();
else if (has_payment_id)
temp.destination = gen_carrot_integrated_address_v1();
else
temp.destination = gen_carrot_main_address_v1();
temp.amount = amount;
temp.randomness = gen_janus_anchor();
return temp;
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+190
View File
@@ -0,0 +1,190 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A 'payment proposal' is a proposal to make an enote sending funds to a Carrot address.
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
#pragma once
//local headers
#include "carrot_enote_types.h"
#include "destination.h"
#include "device.h"
#include "ringct/rctTypes.h"
#include "common/variant.h"
//third party headers
//standard headers
#include <optional>
//forward declarations
namespace carrot
{
////
// CarrotPaymentProposalV1
// - for creating an output proposal to send an amount to someone
///
struct CarrotPaymentProposalV1 final
{
/// user address
CarrotDestinationV1 destination;
/// b
rct::xmr_amount amount;
/// asset type
std::string asset_type;
/// anchor_norm: secret 16-byte randomness for Janus anchor
janus_anchor_t randomness;
};
////
// CarrotPaymentProposalSelfSendV1
// - for creating an output proposal to send an change to yourself
///
struct CarrotPaymentProposalSelfSendV1 final
{
/// one of our own address spend pubkeys: K^j_s
crypto::public_key destination_address_spend_pubkey;
/// a
rct::xmr_amount amount;
/// enote_type
CarrotEnoteType enote_type;
/// enote ephemeral pubkey: D_e
std::optional<mx25519_pubkey> enote_ephemeral_pubkey;
/// anchor: arbitrary, pre-encrypted message for _internal_ selfsends
std::optional<janus_anchor_t> internal_message;
};
struct RCTOutputEnoteProposal
{
CarrotEnoteV1 enote;
// we need this opening information to make amount range proofs
rct::xmr_amount amount;
crypto::secret_key amount_blinding_factor;
};
/// equality operators
bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1 &b);
/// equality operators
bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentProposalSelfSendV1 &b);
/**
* brief: get_enote_ephemeral_privkey - get the proposal's enote ephemeral privkey d_e
* param: proposal -
* param: input_context -
* return: d_e
*/
crypto::secret_key get_enote_ephemeral_privkey(const CarrotPaymentProposalV1 &proposal,
const input_context_t &input_context);
/**
* brief: get_enote_ephemeral_pubkey - get the proposal's enote ephemeral pubkey D_e
* param: proposal -
* param: input_context -
* return: D_e
*/
mx25519_pubkey get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
const input_context_t &input_context);
/**
* brief: get_coinbase_output_proposal_v1 - convert the carrot proposal to a coinbase output proposal
* param: proposal -
* param: block_index - index of the coinbase tx's block
* outparam: output_enote_out -
* outparam: partial_memo_out -
*/
void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
const std::uint64_t block_index,
CarrotCoinbaseEnoteV1 &output_enote_out);
/**
* brief: get_output_proposal_normal_v1 - convert the carrot proposal to an output proposal
* param: proposal -
* param: tx_first_key_image -
* outparam: output_enote_out -
* outparam: encrypted_payment_id_out - pid_enc
*/
void get_output_proposal_normal_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);
/**
* brief: get_output_proposal_return_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_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);
/**
* brief: get_output_proposal_special_v1 - convert the carrot proposal to an output proposal (external selfsend)
* param: proposal -
* param: k_view_dev -
* param: tx_first_key_image -
* param: other_enote_ephemeral_pubkey -
* outparam: output_enote_out -
*/
void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
const view_incoming_key_device &k_view_dev,
const crypto::key_image &tx_first_key_image,
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
RCTOutputEnoteProposal &output_enote_out);
/**
* brief: get_output_proposal_internal_v1 - convert the carrot proposal to an output proposal (internal)
* param: proposal -
* param: s_view_balance_dev -
* param: account_spend_pubkey -
* param: tx_first_key_image -
* param: other_enote_ephemeral_pubkey -
* outparam: output_enote_out -
*/
void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
const view_balance_secret_device &s_view_balance_dev,
const crypto::key_image &tx_first_key_image,
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
RCTOutputEnoteProposal &output_enote_out);
/**
* brief: gen_jamtis_payment_proposal_v1 - generate a random proposal
* param: is_subaddress - whether to generate a proposal to subaddress
* param: has_payment_id - true to generate non-zero payment ID, false for null payment ID
* param: amount -
* return: a random proposal
*/
CarrotPaymentProposalV1 gen_carrot_payment_proposal_v1(const bool is_subaddress,
const bool has_payment_id,
const rct::xmr_amount amount);
} //namespace carrot
+403
View File
@@ -0,0 +1,403 @@
// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Utilities for scanning carrot enotes
//paired header
#include "scan.h"
//local headers
#include "destination.h"
#include "enote_utils.h"
#include "ringct/rctOps.h"
#include "scan_unsafe.h"
//third party headers
//standard headers
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static bool is_main_address_spend_pubkey(const crypto::public_key &address_spend_pubkey,
const epee::span<const crypto::public_key> main_address_spend_pubkeys)
{
for (const crypto::public_key &main_address_spend_pubkey : main_address_spend_pubkeys)
if (address_spend_pubkey == main_address_spend_pubkey)
return true;
return false;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static crypto::secret_key make_enote_ephemeral_privkey_sender(const janus_anchor_t &anchor_norm,
const CarrotDestinationV1 &destination,
const input_context_t &input_context)
{
// d_e = H_n(anchor_norm, input_context, K^j_s, pid))
crypto::secret_key enote_ephemeral_privkey;
make_carrot_enote_ephemeral_privkey(anchor_norm,
input_context,
destination.address_spend_pubkey,
destination.payment_id,
enote_ephemeral_privkey);
return enote_ephemeral_privkey;
}
//-------------------------------------------------------------------------------------------------------------------
static bool try_scan_carrot_coinbase_enote_checked(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx,
const epee::span<const crypto::public_key> main_addresss_spend_pubkeys,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out)
{
// s^ctx_sr, k^g_o, k^g_t, K^j_s, pid, anchor
janus_anchor_t nominal_janus_anchor;
if (!try_scan_carrot_coinbase_enote_no_janus(enote,
s_sender_receiver_unctx,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
nominal_janus_anchor))
return false;
if (!is_main_address_spend_pubkey(address_spend_pubkey_out, main_addresss_spend_pubkeys))
return false;
return verify_carrot_normal_janus_protection(nominal_janus_anchor,
make_carrot_input_context_coinbase(enote.block_index),
address_spend_pubkey_out,
/*is_subaddress=*/false,
null_payment_id,
enote.enote_ephemeral_pubkey);
}
//-------------------------------------------------------------------------------------------------------------------
static bool try_scan_carrot_enote_external_normal_checked(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const mx25519_pubkey &s_sender_receiver_unctx,
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
payment_id_t &payment_id_out,
CarrotEnoteType &enote_type_out,
janus_anchor_t &nominal_janus_anchor_out,
bool &verified_normal_janus)
{
if (!try_scan_carrot_enote_external_no_janus(enote,
encrypted_payment_id,
s_sender_receiver_unctx,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
amount_out,
amount_blinding_factor_out,
payment_id_out,
enote_type_out,
nominal_janus_anchor_out))
return false;
verified_normal_janus = verify_carrot_normal_janus_protection(
make_carrot_input_context(enote.tx_first_key_image),
address_spend_pubkey_out,
!is_main_address_spend_pubkey(address_spend_pubkey_out, main_address_spend_pubkeys),
enote.enote_ephemeral_pubkey,
nominal_janus_anchor_out,
payment_id_out);
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool make_carrot_uncontextualized_shared_key_receiver(
const view_incoming_key_device &k_view_dev,
const mx25519_pubkey &enote_ephemeral_pubkey,
mx25519_pubkey &s_sender_receiver_unctx_out)
{
return k_view_dev.view_key_scalar_mult_x25519(enote_ephemeral_pubkey, s_sender_receiver_unctx_out);
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_coinbase_enote_sender(
const CarrotCoinbaseEnoteV1 &enote,
const CarrotDestinationV1 &destination,
const janus_anchor_t &anchor_norm,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out)
{
const crypto::secret_key enote_ephemeral_privkey = make_enote_ephemeral_privkey_sender(anchor_norm,
destination,
make_carrot_input_context_coinbase(enote.block_index));
return try_scan_carrot_coinbase_enote_sender(enote,
destination,
enote_ephemeral_privkey,
sender_extension_g_out,
sender_extension_t_out);
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_coinbase_enote_sender(
const CarrotCoinbaseEnoteV1 &enote,
const CarrotDestinationV1 &destination,
const crypto::secret_key &enote_ephemeral_privkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out)
{
// s_sr = d_e ConvertPointE(K^j_v)
mx25519_pubkey s_sender_receiver_unctx;
make_carrot_uncontextualized_shared_key_sender(enote_ephemeral_privkey,
destination.address_view_pubkey,
s_sender_receiver_unctx);
crypto::public_key dummy_main_address_spend_pubkey;
if (!try_scan_carrot_coinbase_enote_checked(enote,
s_sender_receiver_unctx,
{&destination.address_spend_pubkey, 1},
sender_extension_g_out,
sender_extension_t_out,
dummy_main_address_spend_pubkey))
return false;
// this should've already been checked, but just for good measure...
return dummy_main_address_spend_pubkey == destination.address_spend_pubkey;
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_coinbase_enote_receiver(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx,
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &main_address_spend_pubkey_out)
{
return try_scan_carrot_coinbase_enote_checked(enote,
s_sender_receiver_unctx,
main_address_spend_pubkeys,
sender_extension_g_out,
sender_extension_t_out,
main_address_spend_pubkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_coinbase_enote_receiver(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx,
const crypto::public_key &main_address_spend_pubkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out)
{
crypto::public_key dummy_main_address_spend_pubkey;
return try_scan_carrot_coinbase_enote_receiver(
enote,
s_sender_receiver_unctx,
{&main_address_spend_pubkey, 1},
sender_extension_g_out,
sender_extension_t_out,
dummy_main_address_spend_pubkey);
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const CarrotDestinationV1 &destination,
const janus_anchor_t &anchor_norm,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
const bool check_pid)
{
const crypto::secret_key enote_ephemeral_privkey = make_enote_ephemeral_privkey_sender(anchor_norm,
destination,
make_carrot_input_context(enote.tx_first_key_image));
return try_scan_carrot_enote_external_sender(enote,
encrypted_payment_id,
destination,
enote_ephemeral_privkey,
sender_extension_g_out,
sender_extension_t_out,
amount_out,
amount_blinding_factor_out,
enote_type_out,
check_pid);
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const CarrotDestinationV1 &destination,
const crypto::secret_key &enote_ephemeral_privkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
const bool check_pid)
{
// s_sr = d_e ConvertPointE(K^j_v)
mx25519_pubkey s_sender_receiver_unctx;
make_carrot_uncontextualized_shared_key_sender(enote_ephemeral_privkey,
destination.address_view_pubkey,
s_sender_receiver_unctx);
return try_scan_carrot_enote_external_sender(enote,
encrypted_payment_id,
destination,
s_sender_receiver_unctx,
sender_extension_g_out,
sender_extension_t_out,
amount_out,
amount_blinding_factor_out,
enote_type_out,
check_pid);
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const CarrotDestinationV1 &destination,
const mx25519_pubkey &s_sender_receiver_unctx,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
const bool check_pid)
{
crypto::public_key recovered_address_spend_pubkey;
payment_id_t recovered_payment_id;
CarrotEnoteType recovered_enote_type;
janus_anchor_t dummy_janus_anchor;
bool verified_normal_janus = false;
if (!try_scan_carrot_enote_external_normal_checked(enote,
encrypted_payment_id,
s_sender_receiver_unctx,
{&destination.address_spend_pubkey, 1},
sender_extension_g_out,
sender_extension_t_out,
recovered_address_spend_pubkey,
amount_out,
amount_blinding_factor_out,
recovered_payment_id,
recovered_enote_type,
dummy_janus_anchor,
verified_normal_janus))
return false;
else if (!verified_normal_janus)
return false;
else if (recovered_address_spend_pubkey != destination.address_spend_pubkey)
return false;
else if (check_pid && recovered_payment_id != destination.payment_id)
return false;
else if (recovered_enote_type != CarrotEnoteType::PAYMENT)
return false;
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_enote_external_receiver(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const mx25519_pubkey &s_sender_receiver_unctx,
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
const view_incoming_key_device &k_view_dev,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
payment_id_t &payment_id_out,
CarrotEnoteType &enote_type_out)
{
janus_anchor_t nominal_janus_anchor;
bool verified_normal_janus = false;
if (!try_scan_carrot_enote_external_normal_checked(enote,
encrypted_payment_id,
s_sender_receiver_unctx,
main_address_spend_pubkeys,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
amount_out,
amount_blinding_factor_out,
payment_id_out,
enote_type_out,
nominal_janus_anchor,
verified_normal_janus))
return false;
if (!verified_normal_janus && !verify_carrot_special_janus_protection(enote.tx_first_key_image,
enote.enote_ephemeral_pubkey,
enote.onetime_address,
k_view_dev,
nominal_janus_anchor))
return false;
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
const view_balance_secret_device &s_view_balance_dev,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
janus_anchor_t &internal_message_out)
{
// input_context
const input_context_t input_context = make_carrot_input_context(enote.tx_first_key_image);
// vt = H_3(s_sr || input_context || Ko)
view_tag_t nominal_view_tag;
s_view_balance_dev.make_internal_view_tag(input_context, enote.onetime_address, nominal_view_tag);
// test view tag
if (nominal_view_tag != enote.view_tag)
return false;
// s^ctx_sr = H_32(s_vb, D_e, input_context)
crypto::hash s_sender_receiver;
s_view_balance_dev.make_internal_sender_receiver_secret(enote.enote_ephemeral_pubkey,
input_context,
s_sender_receiver);
return try_scan_carrot_enote_internal_burnt(enote,
s_sender_receiver,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
amount_out,
amount_blinding_factor_out,
enote_type_out,
internal_message_out);
// janus protection checks are not needed for internal scans
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+191
View File
@@ -0,0 +1,191 @@
// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Utilities for scanning carrot enotes
/**
* The "scan process" mentioned in this file is described in Section 8.1 "Enote Scan" of the Carrot specification:
* https://github.com/jeffro256/carrot/blob/master/carrot.md#81-enote-scan
*/
#pragma once
//local headers
#include "carrot_enote_types.h"
#include "device.h"
#include "span.h"
//third party headers
//standard headers
#include <optional>
//forward declarations
namespace carrot { struct CarrotDestinationV1; }
namespace carrot
{
/**
* brief: make_carrot_uncontextualized_shared_key_receiver - perform the receiver-side ECDH exchange for Carrot enotes
* s_sr = k_v D_e
* param: k_view_dev -
* param: enote_ephemeral_pubkey - D_e
* outparam: s_sender_receiver_unctx_out - s_sr
* return: true if successful, false if a failure occurred in point decompression
*/
bool make_carrot_uncontextualized_shared_key_receiver(
const view_incoming_key_device &k_view_dev,
const mx25519_pubkey &enote_ephemeral_pubkey,
mx25519_pubkey &s_sender_receiver_unctx_out);
/**
* brief: try_scan_carrot_coinbase_enote_[sender/receiver] - attempt scan process on coinbase enote
* param: enote -
* param: destination - (is_subaddress, K^j_s, K^j_v, pid)
* param: anchor_norm - anchor_norm
* param: enote_ephemeral_privkey - d_e
* param: s_sender_receiver_unctx - s_sr
* param: main_address_spend_pubkeys - {K^0_s, ...}
* outparam: sender_extension_g_out - k^g_o
* outparam: sender_extension_g_out - k^t_o
* outparam: main_address_spend_pubkey_out - K^0_s, which will be one of main_address_spend_pubkeys
* return: true iff the scan process succeeded
*/
bool try_scan_carrot_coinbase_enote_sender(
const CarrotCoinbaseEnoteV1 &enote,
const CarrotDestinationV1 &destination,
const janus_anchor_t &anchor_norm,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out);
bool try_scan_carrot_coinbase_enote_sender(
const CarrotCoinbaseEnoteV1 &enote,
const CarrotDestinationV1 &destination,
const crypto::secret_key &enote_ephemeral_privkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out);
bool try_scan_carrot_coinbase_enote_receiver(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx,
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &main_address_spend_pubkey_out);
bool try_scan_carrot_coinbase_enote_receiver(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx,
const crypto::public_key &main_address_spend_pubkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out);
/**
* brief: try_scan_carrot_enote_external_[sender/receiver] - attempt scan process on external enote
* param: enote -
* param: encrypted_payment_id - pid_enc
* param: destination - (is_subaddress, K^j_s, K^j_v, pid)
* param: anchor_norm - anchor_norm
* param: enote_ephemeral_privkey - d_e
* param: s_sender_receiver_unctx - s_sr
* param: main_address_spend_pubkeys - {K^0_s, ...}
* param: k_view_dev -
* outparam: sender_extension_g_out - k^g_o
* outparam: sender_extension_g_out - k^t_o
* outparam: address_spend_pubkey_out - K^j_s
* outparam: amount_out - a
* outparam: amount_blinding_factor_out - k_a
* outparam: payment_id_out - pid
* outparam: enote_type_out - enote_type
* return: true iff the scan process succeeded
*
* warning: the _sender overloads cannot check for Janus protection on special enotes
*/
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const CarrotDestinationV1 &destination,
const janus_anchor_t &anchor_norm,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
const bool check_pid = true);
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const CarrotDestinationV1 &destination,
const crypto::secret_key &enote_ephemeral_privkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
const bool check_pid = true);
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const CarrotDestinationV1 &destination,
const mx25519_pubkey &s_sender_receiver_unctx,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
const bool check_pid = true);
bool try_scan_carrot_enote_external_receiver(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const mx25519_pubkey &s_sender_receiver_unctx,
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
const view_incoming_key_device &k_view_dev,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
payment_id_t &payment_id_out,
CarrotEnoteType &enote_type_out);
/**
* brief: try_scan_carrot_enote_internal_receiver - attempt scan process on internal enote
* param: enote -
* param: s_view_balance_dev -
* outparam: sender_extension_g_out - k^g_o
* outparam: sender_extension_g_out - k^t_o
* outparam: address_spend_pubkey_out - K^j_s
* outparam: amount_out - a
* outparam: amount_blinding_factor_out - k_a
* outparam: enote_type_out - enote_type
* outparam: internal_message_out - anchor'
* return: true iff the scan process succeeded
*/
bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
const view_balance_secret_device &s_view_balance_dev,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
janus_anchor_t &internal_message_out);
//! @TODO: try_scan_carrot_enote_internal_sender(): can't validate burning w/o passing s_sr = s_vb
} //namespace carrot
+287
View File
@@ -0,0 +1,287 @@
// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Utilities for scanning carrot enotes
//paired header
#include "scan_unsafe.h"
//local headers
#include "enote_utils.h"
#include "ringct/rctOps.h"
//third party headers
//standard headers
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static void scan_carrot_dest_info(const crypto::public_key &onetime_address,
const rct::key &amount_commitment,
const encrypted_janus_anchor_t &encrypted_janus_anchor,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const crypto::hash &s_sender_receiver,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
payment_id_t &nominal_payment_id_out,
janus_anchor_t &nominal_janus_anchor_out)
{
// k^o_g = H_n("..g..", s^ctx_sr, C_a)
make_carrot_onetime_address_extension_g(s_sender_receiver,
amount_commitment,
sender_extension_g_out);
// k^o_t = H_n("..t..", s^ctx_sr, C_a)
make_carrot_onetime_address_extension_t(s_sender_receiver,
amount_commitment,
sender_extension_t_out);
// K^j_s = Ko - K^o_ext = Ko - (k^o_g G + k^o_t T)
recover_address_spend_pubkey(onetime_address,
s_sender_receiver,
amount_commitment,
address_spend_pubkey_out);
// pid = pid_enc XOR m_pid, if applicable
if (encrypted_payment_id)
nominal_payment_id_out = decrypt_legacy_payment_id(*encrypted_payment_id, s_sender_receiver, onetime_address);
else
nominal_payment_id_out = null_payment_id;
// anchor = anchor_enc XOR m_anchor
nominal_janus_anchor_out = decrypt_carrot_anchor(encrypted_janus_anchor,
s_sender_receiver,
onetime_address);
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static bool try_scan_carrot_external_noamount(const crypto::public_key &onetime_address,
const lazy_amount_commitment_t &lazy_amount_commitment,
const encrypted_janus_anchor_t &encrypted_janus_anchor,
const view_tag_t view_tag,
const mx25519_pubkey &enote_ephemeral_pubkey,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const input_context_t &input_context,
const mx25519_pubkey &s_sender_receiver_unctx,
crypto::hash &s_sender_receiver_out,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
payment_id_t &nominal_payment_id_out,
janus_anchor_t &janus_anchor_out)
{
// if vt' != vt, then FAIL
if (!test_carrot_view_tag(s_sender_receiver_unctx.data, input_context, onetime_address, view_tag))
return false;
// s^ctx_sr = H_32(s_sr, D_e, input_context)
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
enote_ephemeral_pubkey,
input_context,
s_sender_receiver_out);
// get C_a
const rct::key amount_commitment = calculate_amount_commitment(lazy_amount_commitment);
// k^g_o, k^t_o, K^j_s', pid', anchor'
scan_carrot_dest_info(onetime_address,
amount_commitment,
encrypted_janus_anchor,
encrypted_payment_id,
s_sender_receiver_out,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
nominal_payment_id_out,
janus_anchor_out);
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_coinbase_enote_no_janus(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &nominal_address_spend_pubkey_out,
janus_anchor_t &nominal_janus_anchor_out)
{
// input_context
const input_context_t input_context = make_carrot_input_context_coinbase(enote.block_index);
// s^ctx_sr, k^g_o, k^g_t, K^j_s, pid, anchor
crypto::hash dummy_s_sender_receiver;
payment_id_t dummy_payment_id;
if (!try_scan_carrot_external_noamount(enote.onetime_address,
enote.amount,
enote.anchor_enc,
enote.view_tag,
enote.enote_ephemeral_pubkey,
std::nullopt,
input_context,
s_sender_receiver_unctx,
dummy_s_sender_receiver,
sender_extension_g_out,
sender_extension_t_out,
nominal_address_spend_pubkey_out,
dummy_payment_id,
nominal_janus_anchor_out))
return false;
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_enote_external_no_janus(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const mx25519_pubkey &s_sender_receiver_unctx,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
payment_id_t &nominal_payment_id_out,
CarrotEnoteType &enote_type_out,
janus_anchor_t &nominal_janus_anchor_out)
{
// input_context
const input_context_t input_context = make_carrot_input_context(enote.tx_first_key_image);
// s^ctx_sr, k^g_o, k^g_t, K^j_s, pid, and Janus verification
crypto::hash s_sender_receiver;
if (!try_scan_carrot_external_noamount(enote.onetime_address,
enote.amount_commitment,
enote.anchor_enc,
enote.view_tag,
enote.enote_ephemeral_pubkey,
encrypted_payment_id,
input_context,
s_sender_receiver_unctx,
s_sender_receiver,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
nominal_payment_id_out,
nominal_janus_anchor_out))
return false;
// enote_type, a, z
return try_get_carrot_amount(s_sender_receiver,
enote.amount_enc,
enote.onetime_address,
address_spend_pubkey_out,
enote.amount_commitment,
enote_type_out,
amount_out,
amount_blinding_factor_out);
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_enote_internal_burnt(const CarrotEnoteV1 &enote,
const crypto::hash &s_sender_receiver,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
janus_anchor_t &internal_message_out)
{
// k^g_o, k^t_o, K^j_s', pid', anchor'
payment_id_t dummy_payment_id;
scan_carrot_dest_info(enote.onetime_address,
enote.amount_commitment,
enote.anchor_enc,
std::nullopt,
s_sender_receiver,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
dummy_payment_id,
internal_message_out);
// enote_type, a, z
return try_get_carrot_amount(s_sender_receiver,
enote.amount_enc,
enote.onetime_address,
address_spend_pubkey_out,
enote.amount_commitment,
enote_type_out,
amount_out,
amount_blinding_factor_out);
}
//-------------------------------------------------------------------------------------------------------------------
bool verify_carrot_normal_janus_protection(const input_context_t &input_context,
const crypto::public_key &nominal_address_spend_pubkey,
const bool is_subaddress,
const mx25519_pubkey &enote_ephemeral_pubkey,
const janus_anchor_t &nominal_janus_anchor,
payment_id_t &nominal_payment_id_inout)
{
// if can recompute D_e with pid', then PASS
if (verify_carrot_normal_janus_protection(nominal_janus_anchor,
input_context,
nominal_address_spend_pubkey,
is_subaddress,
nominal_payment_id_inout,
enote_ephemeral_pubkey))
return true;
// if can recompute D_e with null pid, then PASS
nominal_payment_id_inout = null_payment_id;
return verify_carrot_normal_janus_protection(nominal_janus_anchor,
input_context,
nominal_address_spend_pubkey,
is_subaddress,
nominal_payment_id_inout,
enote_ephemeral_pubkey);
}
//-------------------------------------------------------------------------------------------------------------------
bool verify_carrot_special_janus_protection(const crypto::key_image &tx_first_key_image,
const mx25519_pubkey &enote_ephemeral_pubkey,
const crypto::public_key &onetime_address,
const view_incoming_key_device &k_view_dev,
const janus_anchor_t &nominal_janus_anchor)
{
// input_context = "R" || KI_1
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
// anchor_sp = H_16(D_e, input_context, Ko, k_v)
janus_anchor_t expected_special_anchor;
k_view_dev.make_janus_anchor_special(enote_ephemeral_pubkey,
input_context,
onetime_address,
expected_special_anchor);
// attempt special janus check: anchor_sp ?= anchor'
return expected_special_anchor == nominal_janus_anchor;
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+155
View File
@@ -0,0 +1,155 @@
// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Low-level helper utilities for scanning carrot enotes
/**
* These functions should not be used directly unless you know what you're doing. Using these
* functions incorrectly can result in Janus attacks, incorrect PIDs, and view tag skips. For
* safer high-level scanning functions, see the scan.h header.
*/
#pragma once
//local headers
#include "carrot_enote_types.h"
#include "device.h"
#include "lazy_amount_commitment.h"
#include "span.h"
//third party headers
//standard headers
#include <optional>
//forward declarations
namespace carrot
{
/**
* brief: try_scan_carrot_coinbase_enote_no_janus - attempt scan process on coinbase enote w/o Janus protection
* param: enote -
* param: s_sender_receiver_unctx - s_sr
* outparam: sender_extension_g_out - k^g_o
* outparam: sender_extension_g_out - k^t_o
* outparam: nominal_address_spend_pubkey - K^j_s'
* outparam: nominal_janus_anchor_out - anchor'
* return: true iff the scan process (w/o Janus checks) succeeded
*/
bool try_scan_carrot_coinbase_enote_no_janus(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &nominal_address_spend_pubkey_out,
janus_anchor_t &nominal_janus_anchor_out);
/**
* brief: try_scan_carrot_enote_external_no_janus - attempt scan process on external enote,
* w/o Janus protection nor correct PID
* param: enote -
* param: encrypted_payment_id - pid_enc
* param: s_sender_receiver_unctx - s_sr
* param: k_view_dev -
* outparam: sender_extension_g_out - k^g_o
* outparam: sender_extension_g_out - k^t_o
* outparam: address_spend_pubkey_out - K^j_s
* outparam: amount_out - a
* outparam: amount_blinding_factor_out - k_a
* outparam: payment_id_out - pid
* outparam: enote_type_out - enote_type
* outparam: nominal_janus_anchor_out - anchor'
* return: true iff the scan process (w/o Janus or PID checks) succeeded
*/
bool try_scan_carrot_enote_external_no_janus(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const mx25519_pubkey &s_sender_receiver_unctx,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
payment_id_t &nominal_payment_id_out,
CarrotEnoteType &enote_type_out,
janus_anchor_t &nominal_janus_anchor_out);
/**
* brief: try_scan_carrot_enote_internal_burnt - attempt scan process on internal enote w/o
* regular burning bug check or view tag check
* param: enote -
* param: s_sender_receiver - s^ctx_sr
* outparam: sender_extension_g_out - k^g_o
* outparam: sender_extension_g_out - k^t_o
* outparam: address_spend_pubkey_out - K^j_s
* outparam: amount_out - a
* outparam: amount_blinding_factor_out - k_a
* outparam: enote_type_out - enote_type
* outparam: internal_message_out - anchor'
* return: true iff the scan process (w/o view tag check) succeeded
*/
bool try_scan_carrot_enote_internal_burnt(const CarrotEnoteV1 &enote,
const crypto::hash &s_sender_receiver,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
janus_anchor_t &internal_message_out);
/**
* brief: verify_carrot_normal_janus_protection - verify that scanned normal enote is not attempting a
* Janus attack, and check pid
* param: input_context - input_context
* param: nominal_address_spend_pubkey - K^j_s'
* param: is_subaddress - true iff K^j_s' corresponds to a subaddress
* param: enote_ephemeral_pubkey - D_e
* param: nominal_janus_anchor - anchor'
* outparam: nominal_payment_id_inout - takes pid', sets to nullpid if not associated to this enote
* return: true iff it is computationally intractable for a sender to succeed at a Janus attack
*/
bool verify_carrot_normal_janus_protection(const input_context_t &input_context,
const crypto::public_key &nominal_address_spend_pubkey,
const bool is_subaddress,
const mx25519_pubkey &enote_ephemeral_pubkey,
const janus_anchor_t &nominal_janus_anchor,
payment_id_t &nominal_payment_id_inout);
/**
* brief: verify_carrot_special_janus_protection - verify that scanned special enote is not attempting a Janus attack
* param: tx_first_key_image - KI_1
* param: enote_ephemeral_pubkey - D_e
* param: onetime_address - K_o
* param: k_view_dev -
* param: nominal_janus_anchor - anchor'
* return: true iff it is computationally intractable for a sender to succeed at a Janus attack
*/
bool verify_carrot_special_janus_protection(const crypto::key_image &tx_first_key_image,
const mx25519_pubkey &enote_ephemeral_pubkey,
const crypto::public_key &onetime_address,
const view_incoming_key_device &k_view_dev,
const janus_anchor_t &nominal_janus_anchor);
} //namespace carrot
+135
View File
@@ -0,0 +1,135 @@
// Copyright (c) 2024, Salvium (author: SRCG)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A 'return address' is a proposal to facilitate pseudonymous transfers of received funds
// back to the originating wallet, as per the previously-published "Return Address Scheme"
// published by knaccc at https://github.com/monero-project/research-lab/issues/53
// This code is designed to implement the F point management and zero-knowledge proofs
// required to support the "Return Address Scheme" in Carrot.
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
//paired header
#include "sparc.h"
//local headers
#include "crypto/crypto.h"
#include "crypto/generators.h"
#include "ringct/rctOps.h"
#include "ringct/rctTypes.h"
#include "crypto/hash.h"
#include "address_utils.h"
#include "misc_log_ex.h"
#include <vector>
#include <array>
namespace carrot {
// Optimized function to hash a vector of keys into a scalar
rct::key hash_to_scalar(std::vector<rct::key>& keys) {
// Create a fixed-size buffer large enough to hold all keys and a domain separator
const char* domain_separator = "sparc_spend_authority_proof";
const size_t domain_separator_length = strlen(domain_separator);
size_t total_size = keys.size() * sizeof(rct::key) + domain_separator_length;
std::vector<uint8_t> data(total_size);
// Copy the keys into the buffer
size_t offset = 0;
for (const auto& key : keys) {
std::memcpy(data.data() + offset, key.bytes, sizeof(rct::key));
offset += sizeof(rct::key);
}
// Add the domain separator "sparc_spend_authority_proof" at the _end_ of the buffer
std::memcpy(data.data() + offset, domain_separator, domain_separator_length);
// Hash the concatenated data into a fixed-size hash
rct::key hash_output;
keccak((const uint8_t *)data.data(), total_size, hash_output.bytes, sizeof(rct::key));
sc_reduce32(hash_output.bytes); // Reduce to valid scalar
return hash_output;
}
// Function to generate the zero-knowledge proof
void make_sparc_spend_authority_proof(const rct::key &x, const rct::key &y, const rct::key &K_o, rct::zk_proof &proof_out) {
// Step 1: Generate random scalars r_x and r_y
rct::key r_x = rct::skGen(); // Random scalar for G commitment
rct::key r_y = rct::skGen(); // Random scalar for T commitment
// Step 2: Calculate commitment by summing terms for G and T
rct::key commitment;
rct::key commitment_G = rct::scalarmultBase(r_x); // r_x * G
rct::key commitment_T = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), r_y); // r_y * T (using T generator)
commitment = rct::addKeys(commitment_G, commitment_T); // R = r_xG + r_yT
// Step 3: Calculate the challenge scalar
std::vector<rct::key> keys{commitment, K_o};
rct::key challenge = rct::hash_to_scalar(keys); // c = H(R || K_o)
// Step 4: Calculate responses
rct::key response_x;
sc_muladd(response_x.bytes, challenge.bytes, x.bytes, r_x.bytes); // z_x = r_x + c * x
sc_reduce32(response_x.bytes);
rct::key response_y;
sc_muladd(response_y.bytes, challenge.bytes, y.bytes, r_y.bytes); // z_y = r_y + c * y
sc_reduce32(response_y.bytes);
// Step 5: Construct and return the proof
proof_out.R = commitment;
proof_out.z1 = response_x;
proof_out.z2 = response_y;
// Step 6: Sanity checks
rct::key resC = rct::addKeys(commitment, rct::scalarmultKey(K_o, challenge));
rct::key resZ = rct::addKeys(rct::scalarmultBase(response_x), rct::scalarmultKey(rct::pk2rct(crypto::get_T()), response_y));
if (!rct::equalKeys(resZ, resC)) assert(false);
}
// Function to verify the zero-knowledge proof
bool verify_sparc_spend_authority_proof(const rct::zk_proof &proof, const rct::key &K_o) {
// Step 1: calculate the challenge
std::vector<rct::key> keys{proof.R, K_o};
rct::key recomputed_challenge = rct::hash_to_scalar(keys);
// Step 2: Calculate z_xG + x_yT
rct::key z_xG = rct::scalarmultBase(proof.z1); // z1 * G
rct::key x_yT = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), proof.z2); // z2 * T
rct::key resZ = rct::addKeys(z_xG, x_yT); // z_xG + x_yT
// Step 3: Calculate R + cK_o
rct::key resC = rct::addKeys(proof.R, rct::scalarmultKey(K_o, recomputed_challenge)); // R + cK_o
// Step 4: verify z_xG + x_yT ?= R + cK_o
return rct::equalKeys(resZ, resC);
}
} // namespace carrot
+59
View File
@@ -0,0 +1,59 @@
// Copyright (c) 2024, Salvium (author: SRCG)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A 'return address' is a proposal to facilitate pseudonymous transfers of received funds
// back to the originating wallet, as per the previously-published "Return Address Scheme"
// published by knaccc at https://github.com/monero-project/research-lab/issues/53
// This code is designed to implement the F point management and zero-knowledge proofs
// required to support the "Return Address Scheme" in Carrot.
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
#pragma once
#include "ringct/rctTypes.h"
namespace carrot
{
/**
* brief: make_sparc_spend_authority_proof - generate the zero-knowledge proof for spend authority for SPARC output pubkey
* param: x - G-term for the given output key
* param: y - T-term for the given output key
* param: K_o - output key to generate spend authority proof for
* outparam: proof_out - the complete spend authority proof
*/
void make_sparc_spend_authority_proof(const rct::key &x, const rct::key &y, const rct::key &K_o, rct::zk_proof &proof_out);
/**
* brief: verify_sparc_spend_authority_proof - verify the zero-knowledge proof for spend authority for SPARC output pubkey
* param: proof - the zero-knowledge proof to verify
* param: K_o - output key to verify the spend authority proof for
* return: true if proof is valid, false otherwise
*/
bool verify_sparc_spend_authority_proof(const rct::zk_proof &proof, const rct::key &K_o);
} //namespace carrot
+185
View File
@@ -0,0 +1,185 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Transcript class for assembling data that needs to be hashed.
#pragma once
//local headers
#include "int-util.h"
#include "memwipe.h"
//third party headers
//standard headers
#include <cstddef>
#include <string>
#include <type_traits>
//forward declarations
namespace sp
{
namespace detail
{
template <typename... Ts>
constexpr size_t sizeof_sum()
{
return (sizeof(Ts) + ...);
}
template <>
constexpr size_t sizeof_sum<>()
{
return 0;
}
} //namespace detail
////
// SpFixedTranscript
// - build a transcript of a fixed bytesize and input types, enforced at compile time
// - written to be the simplest correct transcript of data possible
// - requires domain separators at compile-time as well
// - ensures that no two transcripts with different domain separators will ever be equal
// - does not use dynamic allocation
// - unsigned integers are added to the transcript in little-endian form
// - signed integers are not allowed
// - domain separator is length-prefixed with a single unsigned byte at the beginning
// - passed domain separator can be null terminated or not, null bytes and after will be dropped
///
template <std::size_t N, const unsigned char domain_sep[N], typename... Ts>
class SpFixedTranscript final
{
public:
//constructors
/// normal constructor
SpFixedTranscript(const Ts&... args)
{
// copy domain separator length prefix
m_transcript[0] = static_cast<unsigned char>(domain_sep_size());
// copy domain separator
memcpy(m_transcript + 1, domain_sep, domain_sep_size());
// copy types into buffer
append<1 + domain_sep_size()>(args...);
}
//overloaded operators
/// disable copy/move
SpFixedTranscript& operator=(const SpFixedTranscript&) = delete;
SpFixedTranscript& operator=(SpFixedTranscript&&) = delete;
//member functions
constexpr const void* data() const noexcept { return m_transcript; }
static constexpr std::size_t size()
{
return 1 + domain_sep_size() + detail::sizeof_sum<Ts...>();
}
//destructors
~SpFixedTranscript()
{
// wipe the buffer on leave in case it contains sensitive data
memwipe(m_transcript, sizeof(m_transcript));
}
private:
//member functions
template <std::size_t>
void append() {}
template <std::size_t offset, typename U0, typename... Us>
void append(const U0 &arg0, const Us&... args)
{
// write current argument to buffer
write<offset>(arg0);
// call append for next argument
static constexpr size_t new_offset = offset + sizeof(arg0);
append<new_offset>(args...);
}
template <std::size_t offset, typename U>
void write(const U &val)
{
static_assert(std::has_unique_object_representations_v<U>);
static_assert(std::is_standard_layout_v<U>);
static_assert(!std::is_signed_v<U> || std::is_same_v<U, char>);
static_assert(alignof(U) == 1);
static_assert(!std::is_pointer_v<U>);
memcpy(m_transcript + offset, &val, sizeof(val));
}
template <std::size_t offset>
void write(std::uint16_t val)
{
val = SWAP16LE(val);
memcpy(m_transcript + offset, &val, sizeof(val));
}
template <std::size_t offset>
void write(std::uint32_t val)
{
val = SWAP32LE(val);
memcpy(m_transcript + offset, &val, sizeof(val));
}
template <std::size_t offset>
void write(std::uint64_t val)
{
val = SWAP64LE(val);
memcpy(m_transcript + offset, &val, sizeof(val));
}
static constexpr std::size_t domain_sep_size()
{
for (std::size_t i = 0; i < N; ++i)
if (domain_sep[i] == '\0')
return i;
return N;
}
static_assert(domain_sep_size() <= 255, "domain separator must be less than 256 characters long");
//member variables
/// the transcript buffer
unsigned char m_transcript[size()];
};
template <const auto & domain_sep, typename... Ts>
auto make_fixed_transcript(const Ts&... args)
{
return SpFixedTranscript<std::size(domain_sep), domain_sep, Ts...>(args...);
}
} //namespace sp
+56
View File
@@ -0,0 +1,56 @@
# Copyright (c) 2024, The Monero Project
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of
# conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
# of conditions and the following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(carrot_impl_sources
account.cpp
address_device_ram_borrowed.cpp
address_utils_compat.cpp
format_utils.cpp
input_selection.cpp
tx_builder_outputs.cpp
tx_proposal_utils.cpp
)
monero_find_all_headers(carrot_impl_headers, "${CMAKE_CURRENT_SOURCE_DIR}")
monero_add_library(carrot_impl
${carrot_impl_sources}
${carrot_impl_headers})
target_link_libraries(carrot_impl
PUBLIC
carrot_core
cryptonote_format_utils_basic
PRIVATE
${EXTRA_LIBRARIES})
target_include_directories(carrot_impl
PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}"
PRIVATE
${Boost_INCLUDE_DIRS})
+355
View File
@@ -0,0 +1,355 @@
// Copyright (c) 2014-2022, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <fstream>
#include "include_base_utils.h"
#include "account.h"
#include "warnings.h"
#include "crypto/crypto.h"
#include "crypto/generators.h"
extern "C"
{
#include "crypto/keccak.h"
}
#include "cryptonote_basic/account.h"
#include "cryptonote_config.h"
#include "ringct/rctOps.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot_impl_account"
using namespace std;
DISABLE_VS_WARNINGS(4244 4345)
namespace carrot
{
//----------------------------------------------------------------------------------------------------------------------
CarrotDestinationV1 carrot_and_legacy_account::cryptonote_address(const payment_id_t payment_id,
const AddressDeriveType derive_type) const
{
CarrotDestinationV1 addr;
switch (resolve_derive_type(derive_type))
{
case AddressDeriveType::Carrot:
make_carrot_integrated_address_v1(get_keys().m_carrot_main_address.m_spend_public_key,
get_keys().m_carrot_main_address.m_view_public_key,
payment_id,
addr);
break;
case AddressDeriveType::PreCarrot:
make_carrot_integrated_address_v1(get_keys().m_account_address.m_spend_public_key,
get_keys().m_account_address.m_view_public_key,
payment_id,
addr);
break;
default:
throw std::runtime_error("address derive type not recognized");
}
return addr;
}
//----------------------------------------------------------------------------------------------------------------------
CarrotDestinationV1 carrot_and_legacy_account::subaddress(const subaddress_index_extended &subaddress_index) const
{
if (!subaddress_index.index.is_subaddress())
return cryptonote_address(null_payment_id, subaddress_index.derive_type);
const cryptonote::account_keys &keys = get_keys();
CarrotDestinationV1 addr;
cryptonote::account_public_address cnaddr;
switch (resolve_derive_type(subaddress_index.derive_type))
{
case AddressDeriveType::Carrot:
make_carrot_subaddress_v1(keys.m_carrot_account_address.m_spend_public_key,
keys.m_carrot_account_address.m_view_public_key,
s_generate_address_dev,
subaddress_index.index.major,
subaddress_index.index.minor,
addr);
break;
case AddressDeriveType::PreCarrot:
cnaddr =
keys.m_device->get_subaddress(keys, {subaddress_index.index.major, subaddress_index.index.minor});
addr = CarrotDestinationV1{
.address_spend_pubkey = cnaddr.m_spend_public_key,
.address_view_pubkey = cnaddr.m_view_public_key,
.is_subaddress = true,
.payment_id = null_payment_id
};
break;
default:
throw std::runtime_error("address derive type not recognized");
}
return addr;
}
//----------------------------------------------------------------------------------------------------------------------
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> carrot_and_legacy_account::get_subaddress_map_cn() const
{
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> res;
for (const auto &p : subaddress_map)
res.emplace(p.first, cryptonote::subaddress_index{p.second.index.major, p.second.index.minor});
CHECK_AND_ASSERT_THROW_MES(!res.empty(),
"carrot_and_legacy_account::get_subaddress_map: subaddress map does not contain subaddresses");
return res;
}
//----------------------------------------------------------------------------------------------------------------------
std::unordered_map<crypto::public_key, subaddress_index_extended>& carrot_and_legacy_account::get_subaddress_map_ref() {
return subaddress_map;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::opening_for_subaddress(const subaddress_index_extended &subaddress_index,
crypto::secret_key &address_privkey_g_out,
crypto::secret_key &address_privkey_t_out,
crypto::public_key &address_spend_pubkey_out) const
{
const bool is_subaddress = subaddress_index.index.is_subaddress();
const uint32_t major_index = subaddress_index.index.major;
const uint32_t minor_index = subaddress_index.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;
switch (resolve_derive_type(subaddress_index.derive_type))
{
case AddressDeriveType::Carrot:
// 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_out), to_bytes(keys.k_generate_image), to_bytes(subaddress_scalar));
// k^t_a = k_ps * k^j_subscal
sc_mul(to_bytes(address_privkey_t_out), to_bytes(keys.k_prove_spend), to_bytes(subaddress_scalar));
break;
case AddressDeriveType::PreCarrot:
// m = Hn(k_v || j_major || j_minor) if subaddress else 0
subaddress_extension = is_subaddress
? keys.get_device().get_subaddress_secret_key(keys.m_view_secret_key, {major_index, minor_index})
: crypto::null_skey;
// k^g_a = k_s + m
sc_add(to_bytes(address_privkey_g_out), to_bytes(keys.m_spend_secret_key), to_bytes(subaddress_extension));
// k^t_a = 0
memset(address_privkey_t_out.data, 0, sizeof(address_privkey_t_out));
break;
default:
throw std::runtime_error("address derive type not recognized");
}
// perform sanity check
const CarrotDestinationV1 addr = subaddress(subaddress_index);
rct::key recomputed_address_spend_pubkey;
rct::addKeys2(recomputed_address_spend_pubkey,
rct::sk2rct(address_privkey_g_out),
rct::sk2rct(address_privkey_t_out),
rct::pk2rct(crypto::get_T()));
CHECK_AND_ASSERT_THROW_MES(rct::rct2pk(recomputed_address_spend_pubkey) == addr.address_spend_pubkey,
"carrot and legacy account: opening for subaddress: failed sanity check");
address_spend_pubkey_out = addr.address_spend_pubkey;
}
//----------------------------------------------------------------------------------------------------------------------
bool carrot_and_legacy_account::try_searching_for_opening_for_subaddress(const crypto::public_key &address_spend_pubkey,
crypto::secret_key &address_privkey_g_out,
crypto::secret_key &address_privkey_t_out) const
{
const auto it = subaddress_map.find(address_spend_pubkey);
if (it == subaddress_map.cend())
return false;
crypto::public_key recomputed_address_spend_pubkey;
opening_for_subaddress(it->second,
address_privkey_g_out,
address_privkey_t_out,
recomputed_address_spend_pubkey);
return address_spend_pubkey == recomputed_address_spend_pubkey;
}
bool carrot_and_legacy_account::try_searching_for_opening_for_onetime_address(const crypto::public_key &address_spend_pubkey,
const crypto::secret_key &sender_extension_g,
const crypto::secret_key &sender_extension_t,
crypto::secret_key &x_out,
crypto::secret_key &y_out) const
{
// k^{j,g}_addr, k^{j,t}_addr
crypto::secret_key address_privkey_g;
crypto::secret_key address_privkey_t;
if (!try_searching_for_opening_for_subaddress(address_spend_pubkey,
address_privkey_g,
address_privkey_t))
return false;
// x = k^{j,g}_addr + k^g_o
sc_add(to_bytes(x_out), to_bytes(address_privkey_g), to_bytes(sender_extension_g));
// y = k^{j,t}_addr + k^t_o
sc_add(to_bytes(y_out), to_bytes(address_privkey_t), to_bytes(sender_extension_t));
return true;
}
//----------------------------------------------------------------------------------------------------------------------
bool carrot_and_legacy_account::can_open_fcmp_onetime_address(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
{
crypto::secret_key x, y;
if (!try_searching_for_opening_for_onetime_address(address_spend_pubkey,
sender_extension_g,
sender_extension_t,
x,
y))
return false;
// O' = x G + y T
rct::key recomputed_onetime_address;
rct::addKeys2(recomputed_onetime_address,
rct::sk2rct(x),
rct::sk2rct(y),
rct::pk2rct(crypto::get_T()));
// O' ?= O
return 0 == memcmp(&recomputed_onetime_address, &onetime_address, sizeof(rct::key));
}
//----------------------------------------------------------------------------------------------------------------------
crypto::key_image carrot_and_legacy_account::derive_key_image(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
{
CHECK_AND_ASSERT_THROW_MES(can_open_fcmp_onetime_address(
address_spend_pubkey,
sender_extension_g,
sender_extension_t,
onetime_address),
"carrot and legacy account: derive key image: cannot open onetime address");
crypto::secret_key x, y;
try_searching_for_opening_for_onetime_address(address_spend_pubkey,
sender_extension_g,
sender_extension_t,
x,
y);
crypto::key_image L;
crypto::generate_key_image(onetime_address, x, L);
return L;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::generate_subaddress_map()
{
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 minor_index = 0; minor_index <= MAX_SUBADDRESS_MINOR_INDEX; ++minor_index)
{
for (const AddressDeriveType derive_type : derive_types)
{
const subaddress_index_extended subaddr_index{{major_index, minor_index}, derive_type, false};
const CarrotDestinationV1 addr = subaddress(subaddr_index);
subaddress_map.insert({addr.address_spend_pubkey, subaddr_index});
}
}
}
}
//----------------------------------------------------------------------------------------------------------------------
crypto::secret_key carrot_and_legacy_account::generate(
const crypto::secret_key& recovery_key,
bool recover,
bool two_random,
const AddressDeriveType default_derive_type)
{
// generate the legacy keys
crypto::secret_key retval = cryptonote::account_base::generate(recovery_key, recover, two_random);
// generate carrot keys
set_carrot_keys(default_derive_type);
return retval;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type)
{
// top level keys
m_keys.s_master = m_keys.m_spend_secret_key;
make_carrot_provespend_key(m_keys.s_master, m_keys.k_prove_spend);
make_carrot_viewbalance_secret(m_keys.s_master, m_keys.s_view_balance);
// view balance keys
make_carrot_viewincoming_key(m_keys.s_view_balance, m_keys.k_view_incoming);
make_carrot_generateimage_key(m_keys.s_view_balance, m_keys.k_generate_image);
make_carrot_generateaddress_secret(m_keys.s_view_balance, m_keys.s_generate_address);
// carrot account address
make_carrot_spend_pubkey(m_keys.k_generate_image, m_keys.k_prove_spend, m_keys.m_carrot_account_address.m_spend_public_key);
k_view_incoming_dev.view_key_scalar_mult_ed25519(
m_keys.m_carrot_account_address.m_spend_public_key,
m_keys.m_carrot_account_address.m_view_public_key
);
// carrot main wallet address
m_keys.m_carrot_main_address.m_spend_public_key = m_keys.m_carrot_account_address.m_spend_public_key;
k_view_incoming_dev.view_key_scalar_mult_ed25519(
crypto::get_G(),
m_keys.m_carrot_main_address.m_view_public_key
);
this->default_derive_type = 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}});
}
//----------------------------------------------------------------------------------------------------------------------
AddressDeriveType carrot_and_legacy_account::resolve_derive_type(const AddressDeriveType derive_type) const
{
return derive_type == AddressDeriveType::Auto ? default_derive_type : derive_type;
}
}
//-------------------------------------------------------------------------------------------------------------------
+117
View File
@@ -0,0 +1,117 @@
// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "carrot_core/account_secrets.h"
#include "carrot_core/address_utils.h"
#include "carrot_core/destination.h"
#include "carrot_core/device_ram_borrowed.h"
#include "carrot_core/enote_utils.h"
#include "carrot_impl/subaddress_index.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/subaddress_index.h"
//----------------------------------------------------------------------------------------------------------------------
static constexpr std::uint32_t MAX_SUBADDRESS_MAJOR_INDEX = 5;
static constexpr std::uint32_t MAX_SUBADDRESS_MINOR_INDEX = 20;
namespace carrot
{
class carrot_and_legacy_account : public cryptonote::account_base
{
public:
view_incoming_key_ram_borrowed_device k_view_incoming_dev;
view_balance_secret_ram_borrowed_device s_view_balance_dev;
generate_address_secret_ram_borrowed_device s_generate_address_dev;
AddressDeriveType default_derive_type;
carrot_and_legacy_account(): k_view_incoming_dev(get_keys().k_view_incoming),
s_view_balance_dev(get_keys().s_view_balance),
s_generate_address_dev(get_keys().s_generate_address)
{}
carrot_and_legacy_account(const carrot_and_legacy_account &k) = delete;
carrot_and_legacy_account(carrot_and_legacy_account&&) = delete;
carrot_and_legacy_account& operator=(const carrot_and_legacy_account&) = delete;
carrot_and_legacy_account& operator=(carrot_and_legacy_account&&) = delete;
CarrotDestinationV1 cryptonote_address(const payment_id_t payment_id = null_payment_id,
const AddressDeriveType derive_type = AddressDeriveType::Auto) const;
CarrotDestinationV1 subaddress(const subaddress_index_extended &subaddress_index) const;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> get_subaddress_map_cn() const;
std::unordered_map<crypto::public_key, subaddress_index_extended>& get_subaddress_map_ref();
// brief: opening_for_subaddress - return (k^g_a, k^t_a) for j s.t. K^j_s = (k^g_a * G + k^t_a * T)
void opening_for_subaddress(const subaddress_index_extended &subaddress_index,
crypto::secret_key &address_privkey_g_out,
crypto::secret_key &address_privkey_t_out,
crypto::public_key &address_spend_pubkey_out) const;
bool try_searching_for_opening_for_subaddress(const crypto::public_key &address_spend_pubkey,
crypto::secret_key &address_privkey_g_out,
crypto::secret_key &address_privkey_t_out) const;
bool try_searching_for_opening_for_onetime_address(const crypto::public_key &address_spend_pubkey,
const crypto::secret_key &sender_extension_g,
const crypto::secret_key &sender_extension_t,
crypto::secret_key &x_out,
crypto::secret_key &y_out) const;
bool can_open_fcmp_onetime_address(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;
crypto::key_image derive_key_image(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();
crypto::secret_key generate(
const crypto::secret_key& recovery_key = crypto::secret_key(),
bool recover = false,
bool two_random = false,
const AddressDeriveType default_derive_type = AddressDeriveType::Carrot
);
void set_carrot_keys(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot);
void insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map);
AddressDeriveType resolve_derive_type(const AddressDeriveType derive_type) const;
private:
std::unordered_map<crypto::public_key, subaddress_index_extended> subaddress_map;
};
}
+105
View File
@@ -0,0 +1,105 @@
// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
//local headers
#include "carrot_core/device.h"
#include "subaddress_index.h"
//third party headers
//standard headers
//forward declarations
namespace carrot
{
static constexpr const int E_UNSUPPORTED_ADDRESS_TYPE = 1;
struct cryptonote_hierarchy_address_device: virtual public view_incoming_key_device
{
/**
* brief: get_cryptonote_account_spend_pubkey - K_s
* return: K_s
*/
virtual crypto::public_key get_cryptonote_account_spend_pubkey() const = 0;
/**
* brief: make_legacy_subaddress_extension - k^j_subext
* k^j_subext = ScalarDeriveLegacy("SubAddr" || IntToBytes8(0) || k_v || IntToBytes32(j_major) || IntToBytes32(j_minor))
* param: major_index - j_major
* param: minor_index - j_minor
* outparam: legacy_subaddress_extension_out - k^j_subext
* throw: std::invalid_argument if major_index == minor_index == 0
*/
virtual void make_legacy_subaddress_extension(const std::uint32_t major_index,
const std::uint32_t minor_index,
crypto::secret_key &legacy_subaddress_extension_out) const = 0;
};
struct carrot_hierarchy_address_device: public generate_address_secret_device
{
/**
* brief: get_carrot_account_spend_pubkey - K_s = K^0_s
* return: K_s = K^0_s
*/
virtual crypto::public_key get_carrot_account_spend_pubkey() const = 0;
/**
* brief: get_carrot_account_view_pubkey - K_v
* return: K_v
*/
virtual crypto::public_key get_carrot_account_view_pubkey() const = 0;
/**
* brief: get_carrot_main_address_view_pubkey - K^0_v
* return: K^0_v
*/
virtual crypto::public_key get_carrot_main_address_view_pubkey() const = 0;
};
struct hybrid_hierarchy_address_device
{
virtual bool supports_address_derivation_type(AddressDeriveType derive_type) const = 0;
virtual cryptonote_hierarchy_address_device &access_cryptonote_hierarchy_device() const = 0;
virtual carrot_hierarchy_address_device &access_carrot_hierarchy_device() const = 0;
virtual ~hybrid_hierarchy_address_device() = default;
};
struct interactive_address_device
{
virtual void request_explicit_on_device_address_confirmation(
const subaddress_index_extended &index) = 0;
virtual ~interactive_address_device() = default;
};
} //namespace carrot
@@ -0,0 +1,56 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//paired header
#include "address_device_ram_borrowed.h"
//local headers
#include "address_utils_compat.h"
//third party headers
//standard headers
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot_impl"
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
void cryptonote_hierarchy_address_device_ram_borrowed::make_legacy_subaddress_extension(
const std::uint32_t major_index,
const std::uint32_t minor_index,
crypto::secret_key &legacy_subaddress_extension_out) const
{
return carrot::make_legacy_subaddress_extension(m_k_view_incoming,
major_index,
minor_index,
legacy_subaddress_extension_out);
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
@@ -0,0 +1,66 @@
// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
//local headers
#include "address_device.h"
#include "carrot_core/device_ram_borrowed.h"
//third party headers
//standard headers
//forward declarations
namespace carrot
{
struct cryptonote_hierarchy_address_device_ram_borrowed:
public cryptonote_hierarchy_address_device,
public view_incoming_key_ram_borrowed_device
{
cryptonote_hierarchy_address_device_ram_borrowed(
const crypto::public_key &cryptonote_account_spend_pubkey,
const crypto::secret_key &k_view_incoming):
view_incoming_key_ram_borrowed_device(k_view_incoming),
m_cryptonote_account_spend_pubkey(cryptonote_account_spend_pubkey)
{}
crypto::public_key get_cryptonote_account_spend_pubkey() const override
{
return m_cryptonote_account_spend_pubkey;
}
void make_legacy_subaddress_extension(const std::uint32_t major_index,
const std::uint32_t minor_index,
crypto::secret_key &legacy_subaddress_extension_out) const override;
protected:
const crypto::public_key &m_cryptonote_account_spend_pubkey;
};
} //namespace carrot

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