Compare commits

..

168 Commits

Author SHA1 Message Date
Shawn Xie
fdbde0a5bc Cleanup code review comments (#7536) 2026-06-17 14:25:43 -04:00
Peter Chen
54e0f58424 Address Confidential Transfer Feedback and uncomment tests (#7541) 2026-06-17 13:32:18 -04:00
yinyiqian1
31a3a0db62 Re-randomize after each send (#7535) 2026-06-16 21:11:42 -04:00
Peter Chen
729bc70ec8 Merge pull request #7540 from PeterChen13579/mergeDevelop
Merge latest develop into Confidential MPT
2026-06-15 10:32:57 -07:00
Peter Chen
59d630ab09 Merge remote-tracking branch 'upstream/develop' into mergeDevelop 2026-06-15 08:48:34 -07:00
Peter Chen
5ec499245b Merge pull request #7372 from PeterChen13579/add_more_confidential_Mpt_test
add missing tests to Confidential MPT
2026-06-11 15:08:01 -07:00
Peter Chen
f79eac53bc Merge pull request #7419 from PeterChen13579/mergeLatestDevelop
Merge latest develop into Confidential MPT
2026-06-11 08:00:57 -07:00
Peter Chen
9854b3efc2 fix clang tidy 2026-06-10 13:51:45 -07:00
Peter Chen
e39356a75b fix clang tidy 2026-06-10 13:27:28 -07:00
Peter Chen
aca11d08ce fix clang tidy 2026-06-10 13:03:54 -07:00
Peter Chen
f118070075 Merge remote-tracking branch 'upstream/develop' into mergeLatestDevelop 2026-06-10 11:43:47 -07:00
Peter Chen
1aa983aac9 fix naming 2026-06-10 11:41:56 -07:00
Peter Chen
b1bcd41d7f fix conan lock 2026-06-10 11:31:16 -07:00
Peter Chen
e36927d4db fix lock 2026-06-10 11:05:42 -07:00
Peter Chen
ab730e791c update mpt-crypto and uncomment confidential transfer tests 2026-06-10 10:52:23 -07:00
Peter Chen
7b5f86c6ea Merge remote-tracking branch 'upstream/develop' into mergeLatestDevelop 2026-06-08 08:26:27 -07:00
Peter Chen
ecef6b6002 codegen 2026-06-06 07:32:47 -07:00
Peter Chen
617c56c9f9 Merge latest develop into Confidential MPT 2026-06-05 22:02:44 -07:00
Peter Chen
72ff8f9824 Revert "Merge develop into confidential MPT" (#7416) 2026-06-05 12:18:13 -07:00
Peter Chen
bf17bd6c58 Merge pull request #7384 from PeterChen13579/merge_develop_into_confidentialMPT
Merge develop into confidential MPT
2026-06-05 09:39:30 -07:00
Peter Chen
e64247e542 fix clang tidy 2026-06-02 15:40:01 -04:00
Peter Chen
b14078bff9 fix clang tidy 2026-06-02 14:08:16 -04:00
Peter Chen
7471bfb182 Merge develop into confidential MPT 2026-06-02 13:01:46 -04:00
Peter Chen
dacd108657 Refactor confidential Test into 2 files (#7351) 2026-05-29 14:14:41 -04:00
Peter Chen
5869c23ecd Add specific checks for COA and OA (#7275) 2026-05-26 13:44:36 -04:00
yinyiqian1
ca7da3600f Add Batch + Delegation + Confidential tests (#7279) 2026-05-15 14:08:58 -04:00
Peter Chen
bab0c48d01 add amm pseudo account test with confidential Transfer (#7276) 2026-05-15 10:26:43 -04:00
Peter Chen
71983a59b6 add public MPT txn after Clearing Confidential Flag (#7113) 2026-05-13 09:57:18 -04:00
yinyiqian1
70f03eba66 Update error code for transfer rate error (#7115) 2026-05-12 13:31:13 -04:00
yinyiqian1
d7158abb63 Enforce zero transfer rate for confidential MPT (#7106) 2026-05-11 11:35:44 -04:00
yinyiqian1
c5cfeefc44 refactor and check deposit preauth before ZK proof (#7085) 2026-05-07 11:13:11 -04:00
yinyiqian1
ed6de36cb4 Increase confidential transactions fee (#7063) 2026-05-06 17:15:39 -04:00
Shawn Xie
b831b661ed Merge develop into confidential-transfer
Merge `develop` into `confidential-transfer`
2026-05-04 20:45:24 -04:00
Shawn Xie
cd193bba31 exclude bugprone access 2026-05-04 19:40:27 -04:00
Shawn Xie
086d95aa2c exclude constness for ConfidentialTransfer test file 2026-05-04 18:03:51 -04:00
Shawn Xie
6960e3a234 exclude const clang tidy 2026-05-04 17:20:48 -04:00
Shawn Xie
d7d9e54f92 fix const 2026-05-04 16:08:55 -04:00
Shawn Xie
541c4afeca clang tidy 2026-05-04 15:45:12 -04:00
Shawn Xie
64b5c3cf8f update delegable test cnt 2026-05-04 15:12:18 -04:00
Shawn Xie
feb9782387 fix autogen 2026-05-04 14:53:46 -04:00
Shawn Xie
aa55d7e360 Merge branch 'ripple/confidential-transfer' into ct-merge-dev-may-4 2026-05-04 14:39:54 -04:00
Shawn Xie
f1de2738a3 Fix clang tidy and compile errors 2026-05-04 14:39:03 -04:00
yinyiqian1
cad129921d add IssuerEncryptedBalance check in ConvertBack (#7060)
Co-authored-by: Peter Chen <34582813+PeterChen13579@users.noreply.github.com>
2026-05-04 14:32:23 -04:00
Shawn Xie
5afa29cd39 Merge remote-tracking branch 'upstream/develop' into ct-merge-dev-may-4
# Conflicts:
#	include/xrpl/protocol/detail/features.macro
#	include/xrpl/protocol/detail/ledger_entries.macro
#	include/xrpl/protocol/detail/secp256k1.h
#	include/xrpl/protocol/detail/transactions.macro
#	src/libxrpl/ledger/helpers/TokenHelpers.cpp
#	src/libxrpl/protocol/PublicKey.cpp
#	src/libxrpl/tx/invariants/MPTInvariant.cpp
#	src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp
#	src/test/app/Delegate_test.cpp
#	src/test/jtx/impl/mpt.cpp
#	src/test/jtx/mpt.h
2026-05-04 13:51:58 -04:00
Shawn Xie
9a62a1e471 fix: add credential amendment check and more refactoring (#7041) 2026-05-01 12:25:01 -04:00
Peter Chen
cc58844d49 Merge pull request #7048 from PeterChen13579/add_defensive_checks
add defensive checks
2026-04-30 15:12:06 -04:00
Peter Chen
58337c61c5 Fix comments 2026-04-30 14:36:19 -04:00
Peter Chen
d09352af59 add another check 2026-04-29 13:36:20 -04:00
Peter Chen
7fad75d2b2 add test 2026-04-29 12:10:23 -04:00
Peter Chen
5f1c3ba4eb add defensive checks 2026-04-29 11:28:56 -04:00
Shawn Xie
9bfd78e2fc Add ZKP and ciphertext malleability tests (#6999) 2026-04-28 11:25:40 -04:00
Shawn Xie
5a643b1a7f fix: assorted safety checks (#7030) 2026-04-27 14:36:54 -04:00
yinyiqian1
8457b91b10 Test inconsistent C1 for ConfidentialMPTSend (#7017) 2026-04-24 15:03:26 -04:00
yinyiqian1
4520de22ef Merge pull request #7002 from yinyiqian1/merge-ct2
Merge develop into confidential-transfer
2026-04-23 12:24:22 -04:00
yinyiqian1
b0386622ca fix clang-tidy 2026-04-23 11:38:05 -04:00
yinyiqian1
c53d6aca08 resolve conflicts 2026-04-23 01:05:20 -04:00
yinyiqian1
be8de20a0b Merge branch 'develop' into merge-ct2 2026-04-23 00:41:44 -04:00
yinyiqian1
235547f460 Revert "Merge develop into ConfidentialTransfer (#6987)" (#7001) 2026-04-22 23:57:01 -04:00
yinyiqian1
1e73f3f3ff Merge develop into ConfidentialTransfer (#6987)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: chuanshanjida <chuanshanjida@outlook.com>
Co-authored-by: Ed Hennis <ed@ripple.com>
Co-authored-by: Bart <bthomee@users.noreply.github.com>
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
Co-authored-by: Zhiyuan Wang <96991820+Kassaking7@users.noreply.github.com>
Co-authored-by: Alex Kremer <akremer@ripple.com>
Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
Co-authored-by: Sergey Kuznetsov <skuznetsov@ripple.com>
Co-authored-by: JCW <a1q123456@users.noreply.github.com>
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Gregory Tsipenyuk <gregtatcam@users.noreply.github.com>
Co-authored-by: chuanshanjida <chuanshanjida@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
2026-04-22 23:34:03 -04:00
Shawn Xie
2b65b740d0 Add destination tag to ConfidentialMPTSend (#6988) 2026-04-22 17:07:31 -04:00
yinyiqian1
f08121de35 Address coding style comments (#6966) 2026-04-20 13:31:09 -04:00
yinyiqian1
09778f2fec Support compact AND-composed sigma proof (#6859) 2026-04-16 17:51:31 -04:00
Shawn Xie
5229ff5a45 fix: fix pipeline failures (#6850) 2026-04-10 13:42:59 -04:00
Shawn Xie
6a8016f288 Merge develop into confidential-transfer 2026-04-09 11:54:29 -04:00
Shawn Xie
5d38603f90 fix compile and test failure 2026-04-09 11:22:31 -04:00
Shawn Xie
6f4fddc1ae Merge remote-tracking branch 'upstream/develop' into ct-merge-mpt-dex 2026-04-09 11:05:31 -04:00
Peter Chen
4ae17f193f Add JS fields required for Clio mpt_holder_api (#6810) 2026-04-09 10:49:10 -04:00
Peter Chen
36c805692a add batch tests (#6598) 2026-04-08 14:45:11 -04:00
yinyiqian1
5bd7ce1dfd Add tests for confidential delegation with tickets (#6808) 2026-04-08 13:20:59 -04:00
yinyiqian1
0d7bf6948e Add delegation tests for Confidential Transfers (#6729) 2026-04-07 17:45:26 -04:00
Peter Chen
58e38e05f9 add non-curve point tests (#6695) 2026-04-07 13:50:08 -04:00
Shawn Xie
697786afab Merge develop into confidential-transfer
- Resolved conflicts from autogen files and modularization
2026-04-06 15:45:06 -04:00
Shawn Xie
834cb72515 Update autogen files and delegable count 2026-04-06 15:00:07 -04:00
Shawn Xie
261e7d7769 Resolve conflicts 2026-04-06 12:11:45 -04:00
Shawn Xie
042b09563b Merge remote-tracking branch 'upstream/develop' into ct-merge 2026-04-06 11:57:06 -04:00
yinyiqian1
cc2d6cbff6 Integrate mpt_utility SDK for tests (#6705) 2026-04-02 12:04:05 -04:00
yinyiqian1
6f1232140b remove dead functions due to integration (#6683) 2026-03-30 16:37:55 -04:00
Peter Chen
13b3a243a3 add tests: encrypt under wrong public key (#6650) 2026-03-30 10:38:03 -04:00
yinyiqian1
2472ef7df7 Integrate mpt-crypto SDK lib for on-chain verification (#6679) 2026-03-27 16:26:37 -04:00
Shawn Xie
b34ecc476a Add ProofReader and auth/lock and overflow edge case checks (#6651) 2026-03-26 13:16:51 -04:00
Shawn Xie
4c0e6012e3 Handle edge cases checks in flag setting and token deletion (#6596) 2026-03-24 11:41:27 -04:00
Peter Chen
3f749ecf76 tests: add ticket tests (#6576) 2026-03-20 10:52:18 -04:00
Shawn Xie
a43cf94ff7 update sfBlindingFactor to type uint256 and refactor helper functions to return std::optional 2026-03-19 14:04:03 -04:00
Shawn Xie
9f4cf28aea Rename privacy flag and public key names (#6550)
Corresponding spec change:
https://github.com/XRPLF/XRPL-Standards/pull/501

### Field Renames (SFields)

| Before | After |
|--------|-------|
| `sfIssuerElGamalPublicKey` | `sfIssuerEncryptionKey` |
| `sfHolderElGamalPublicKey` | `sfHolderEncryptionKey` |
| `sfAuditorElGamalPublicKey` | `sfAuditorEncryptionKey` |

### Flag Renames

#### Transaction Flags (`tf`)

| Before | After |
|--------|-------|
| `tfMPTCanPrivacy` | `tfMPTCanConfidentialAmount` |

#### Ledger State Flags (`lsf`)

| Before | After |
|--------|-------|
| `lsfMPTCanPrivacy` | `lsfMPTCanConfidentialAmount` |

#### Ledger State Mutable Flags (`lsmf`)

| Before | After |
|--------|-------|
| `lsmfMPTCannotMutatePrivacy` |
`lsmfMPTCannotMutateCanConfidentialAmount` |

#### Transaction Mutable Flags (`tmf`)

| Before | After |
|--------|-------|
| `tmfMPTCannotMutatePrivacy` |
`tmfMPTCannotMutateCanConfidentialAmount` |
| `tmfMPTSetPrivacy` | `tmfMPTSetCanConfidentialAmount` |
| `tmfMPTClearPrivacy` | `tmfMPTClearCanConfidentialAmount` |
2026-03-17 10:53:17 -04:00
Peter Chen
c45177b69e add homomorphic tests (#6490) 2026-03-16 11:27:36 -04:00
Shawn Xie
d615098849 refactor: improve readability and clean up code from review comments (#6544) 2026-03-16 10:47:39 -04:00
Shawn Xie
eeb0d15ea9 refactor: return optional buffers for helper functions (#6520) 2026-03-11 12:02:27 -04:00
Shawn Xie
84cc8599af chore: Apply clang-format width 100 (#6516) 2026-03-10 11:24:01 -04:00
Shawn Xie
36c1c5f3cd Merge develop into confidential-transfer 2026-03-09 16:50:01 -04:00
Shawn Xie
2c94e213c2 Resolve conflicts 2026-03-09 15:32:42 -04:00
Shawn Xie
cbcc75ff2d Merge remote-tracking branch 'upstream/develop' into ct-merge-dev-100col 2026-03-09 15:25:10 -04:00
Shawn Xie
da7698974c Merge commit '2c1fad102353e11293e3edde1c043224e7d3e983' into ct-merge-dev-100col 2026-03-09 14:24:26 -04:00
Shawn Xie
a3a82faa3d fix conflicts and update modularization 2026-03-09 14:23:22 -04:00
Shawn Xie
fff83c1d4a Merge commit '25cca465538a56cce501477f9e5e2c1c7ea2d84c' into ct-merge-dev-100col 2026-03-09 14:02:31 -04:00
Shawn Xie
803ab67fc5 fix: address auditor feedbacks (#6511) 2026-03-09 13:59:32 -04:00
yinyiqian1
734b11c0e3 Support shared r for ConfidentialMPTSend equality proof (#6496)
* Support shared r for send equality proof
2026-03-06 18:28:35 -05:00
Peter Chen
fae34d0f36 tests: add replay tests to confidential MPT (#6451) 2026-03-04 13:43:43 -05:00
yinyiqian1
c52d317810 Update hashing and support ticket (#6444) 2026-02-27 11:50:22 -05:00
Peter Chen
c2f8b91397 Add invariants and tests (#6403) 2026-02-25 16:40:44 -05:00
Peter Chen
1ea9312946 update crypto-lib (#6418) 2026-02-25 12:18:31 -05:00
yinyiqian1
6ad60d7141 Support Range Proof for ConfidentialMPTSend (#6404)
- proving send amount m is in the range [0, 2^64)
- proving remaining balance b-m is in the range [0, 2^64)
2026-02-20 14:18:34 -05:00
Shawn Xie
94e911ed69 Add Range Proof Verification to ConvertBack (#6377) 2026-02-19 19:22:48 -05:00
Shawn Xie
b2c434dd73 Compress ElGamal Public Keys and Pedersen Commitments + Add Validation (#6385) 2026-02-19 08:41:15 -05:00
Peter Chen
b6d1a8d62b Test Auditor for Confidential Send and revealed R (#6320) 2026-02-17 14:27:33 -05:00
Shawn Xie
9d0c854139 Remove hardcoded library functions in ConfidentialTransfer (#6365) 2026-02-12 14:18:36 -05:00
Ayaz Salikhov
3a6ca681ff chore: Use mpt-crypto library (#6362) 2026-02-12 12:59:36 -05:00
Shawn Xie
a216824c15 Merge develop into confidential-transfer 2026-02-12 11:40:07 -05:00
Shawn Xie
90cf86a920 remove newline 2026-02-12 11:33:34 -05:00
Shawn Xie
e69d3c9bd7 Merge remote-tracking branch 'upstream/develop' into ct-merge-develop-lib 2026-02-12 11:14:40 -05:00
Shawn Xie
fd390a4f1c Add doxygen comments for new transactions and helper functions (#6332) 2026-02-10 10:51:50 -05:00
Shawn Xie
3941283438 Prefix confidential transfer transaction names with "MPT" (#6312) 2026-02-02 12:13:18 -05:00
Shawn Xie
86af28d91d Apply clang-format due to new column size (#6311) 2026-02-02 11:15:39 -05:00
Shawn Xie
41f7102fb8 Merge develop into ripple/confidential-transfer
Merge `develop` into `ripple/confidential-transfer`
2026-02-02 09:46:25 -05:00
Shawn Xie
66ed0fa452 namespace rename 2026-01-30 12:36:15 -05:00
Shawn Xie
cad8fb328a Merge branch 'develop' into ct-merge-develop-new 2026-01-30 12:25:55 -05:00
Shawn Xie
31346425f0 Merge commit '5f638f55536def0d88b970d1018a465a238e55f4' into ct-merge-develop-new 2026-01-30 12:25:36 -05:00
Shawn Xie
40bfaa25d2 Merge commit '92046785d1fea5f9efe5a770d636792ea6cab78b' into ct-merge-develop-new 2026-01-30 12:25:16 -05:00
Peter Chen
c4916f1251 Add more Auditor Tests for Convert and ConvertBack (#6255) 2026-01-29 12:17:19 -05:00
yinyiqian1
fc8b7898c5 Support Pedersen-ElGamal linkage for ConfidentialSend (#6289)
* support Pedersen Amount commitment for ConfidentialSend
* support Pedersen Balance commitment for ConfidentialSend
2026-01-29 11:18:46 -05:00
Shawn Xie
446f9fbe6d Reuse getConfidentialRecipientCount (#6281) 2026-01-26 13:04:02 -05:00
yinyiqian1
1297385b7e Support ConfidentialSend equality proof (#6274)
* Support ConfidentialSend equality proof

* resolve conflicts

* Add version check in send
2026-01-26 12:39:35 -05:00
Shawn Xie
114adc0c57 Pedersen commitment with ConvertBack and basic test (#6243) 2026-01-22 13:00:19 -05:00
yinyiqian1
1d349c32c5 fix encrypt zero balance and remove improper throw (#6242) 2026-01-20 12:27:44 -05:00
Shawn Xie
a5f20c129d Copying over pedersen commitment from crypto lib (#6238) 2026-01-19 13:56:10 -05:00
yinyiqian1
75d143a2a0 support new design to reveal blinding factor (#6237)
* reveal blinding factor and optimize
* schnorr proof is added for registering holder pub key
* clean env.close that already closed
* clean up the lib functions
2026-01-19 13:07:19 -05:00
Shawn Xie
e3da98e310 Update unit test framework to use shared random factor (#6233) 2026-01-16 16:36:49 -05:00
Shawn Xie
ec6d7cb91d Add equality proof to ConvertBack and refactor to reduce redundancy (#6220) 2026-01-16 10:28:55 -05:00
Shawn Xie
fa055c2bd5 Add auditing feature across confidential transfer transactions (#6200) 2026-01-14 11:18:06 -05:00
Shawn Xie
6c38086f17 ConfidentialConvert with Equality Proof (#6177) 2026-01-07 16:17:07 -05:00
Shawn Xie
3e9dc276ed add back clawback hash (#6175) 2026-01-06 12:21:00 -05:00
Shawn Xie
abf7a62b1f Refactor proof (#6168) 2026-01-05 12:00:41 -05:00
yinyiqian1
bd3a6e1631 Support equality proof for confidential clawback (#6149) 2026-01-02 11:48:06 -05:00
yinyiqian1
7c0bd419a4 support mutability for MPTPrivacy (#6137)
Update lsfMPTNoConfidentialTransfer to lsfMPTPrivacy
Add flag lsmfMPTPrivacy to control the mutability of lsfMPTPrivacy.
disallow mutating lsfMPTPrivacy when lsfMPTPrivacy is not set.
disallow mutating lsfMPTPrivacy when there's confidential outstanding amount.
2025-12-10 17:10:33 -05:00
yinyiqian1
d3126959e7 Merge pull request #6123 from yinyiqian1/merge
Merge remote-tracking branch 'origin/develop' into 'ripple/confidential-transfer'
2025-12-09 10:49:11 -05:00
yinyiqian1
67e8e89e0f copyright fix 2025-12-08 18:36:34 -05:00
yinyiqian1
4e4326a174 trigger ci 2025-12-08 18:25:47 -05:00
yinyiqian1
5397bd6d6e fix naming 2025-12-08 17:58:32 -05:00
yinyiqian1
6dece25cc3 fix test failure 2025-12-08 17:34:20 -05:00
yinyiqian1
d9da8733be resolve pre-commit clang-format 2025-12-08 16:45:41 -05:00
yinyiqian1
f6f51451e7 Merge remote-tracking branch 'origin/develop' into merge 2025-12-08 15:09:04 -05:00
Shawn Xie
b94c95b3e9 Change err code (#6050) 2025-11-18 14:19:41 -05:00
yinyiqian1
8365148b5c feat: support ConfidentialClawback and add tests (#6023) 2025-11-13 14:24:40 -05:00
Shawn Xie
c03866bf0f Variable rename (#6028) 2025-11-12 11:58:05 -05:00
Shawn Xie
389afc5f06 Add deposit preauth and other checks (#6011) 2025-11-10 10:52:23 -05:00
Shawn Xie
7b04eaae81 ConvertBack preclaim tests (#6006) 2025-11-05 13:58:52 -05:00
Shawn Xie
1343019509 ConvertBack tests (#6005) 2025-11-05 13:52:55 -05:00
Shawn Xie
cd75e630a2 Change ConfidentialSend preflight error code (#5994) 2025-11-03 18:46:27 -05:00
Shawn Xie
ec57fbdc5f Merge remote-tracking branch 'upstream/develop' into confidential-transfer 2025-11-03 18:42:41 -05:00
Shawn Xie
4fe67f5715 ConvertBack preflight tests (#5991) 2025-11-03 15:58:32 -05:00
Shawn Xie
44d885e39b Basic ConvertBack test (#5979) 2025-10-31 11:46:24 -04:00
yinyiqian1
3af758145c Check auth for ConfidentialSend (#5968) 2025-10-30 11:02:46 -04:00
yinyiqian1
f3d4d4341b add ciphertext check for ConfidentialSend (#5964) 2025-10-29 12:10:48 -04:00
Shawn Xie
ddb518ad09 MergeInbox tests (#5949) 2025-10-28 13:21:11 -04:00
Shawn Xie
3899e3f36c Add auth checks for convert (#5937) 2025-10-24 11:42:43 -04:00
yinyiqian1
e4a8ba51f9 check lock in ConfidentialSend (#5933) 2025-10-23 12:58:38 -04:00
Shawn Xie
35e4fad557 Add ciphertext check (#5930) 2025-10-23 11:57:18 -04:00
yinyiqian1
8e9cb3c1da support ConfidentialSend (#5921) 2025-10-22 12:02:00 -04:00
Shawn Xie
18d92058e3 MergeInbox (#5922) 2025-10-22 11:30:44 -04:00
Shawn Xie
f24d584f29 ConfidentialConvert tests (#5911) 2025-10-20 14:39:16 -04:00
Shawn Xie
da3fbcd25b Remove unused header file (#5908) 2025-10-17 16:42:08 -04:00
Shawn Xie
daa1303b5a Update decryption test helper function (#5907) 2025-10-17 14:19:19 -04:00
Shawn Xie
a636fe5871 Update test framework for encryption (#5906) 2025-10-17 14:04:54 -04:00
Shawn Xie
bbc3071fd1 Update mpt-crypto with zero encryption (#5905) 2025-10-17 11:41:39 -04:00
Shawn Xie
8fdc639206 ConfidentialConvert (#5901)
ConfidentialConvert and some test framework update
2025-10-16 14:31:14 -04:00
Shawn Xie
5a89641d98 remove duplicate code 2025-10-07 15:52:18 -04:00
Shawn Xie
beefa248a6 Merge remote-tracking branch 'upstream/develop' into confidential-transfer 2025-10-07 15:00:14 -04:00
Shawn Xie
e919a25ecb Merge develop into ripple/confidential-transfer (#5835)
* Fix: Don't flag consensus as stalled prematurely (#5658)

Fix stalled consensus detection to prevent false positives in situations where there are no disputed transactions.

Stalled consensus detection was added to 2.5.0 in response to a network consensus halt that caused a round to run for over an hour. However, it has a flaw that makes it very easy to have false positives. Those false positives are usually mitigated by other checks that prevent them from having an effect, but there have been several instances of validators "running ahead" because there are circumstances where the other checks are "successful", allowing the stall state to be checked.

* Set version to 2.5.1

* fix: Skip processing transaction batch if the batch is empty (#5670)

Avoids an assertion failure in NetworkOPsImp::apply in the unlikely event that all incoming transactions are invalid.

* Fix: EscrowTokenV1 (#5571)

* resolves an accounting inconsistency in MPT escrows where transfer fees were not properly handled when unlocking escrowed tokens.

* refactor: Wrap GitHub CI conditionals in curly braces (#5796)

This change wraps all GitHub conditionals in `${{ .. }}`, both for consistency and to reduce unexpected failures, because it was previously noticed that not all conditionals work without those curly braces.

* Only notify clio for PRs targeting the release and master branches (#5794)

Clio should only be notified when releases are about to be made, instead of for all PR, so this change only notifies Clio when a PR targets the release or master branch.

* Support DynamicMPT XLS-94d (#5705)

* extends the functionality of the MPTokenIssuanceSet transaction, allowing the issuer to update fields or flags that were explicitly marked as mutable during creation.

* Bugfix: Adds graceful peer disconnection (#5669)

The XRPL establishes connections in three stages: first a TCP connection, then a TLS/SSL handshake to secure the connection, and finally an upgrade to the bespoke XRP Ledger peer-to-peer protocol. During connection termination, xrpld directly closes the TCP connection, bypassing the TLS/SSL shutdown handshake. This makes peer disconnection diagnostics more difficult - abrupt TCP termination appears as if the peer crashed rather than disconnected gracefully.

This change refactors the connection lifecycle with the following changes:
- Enhanced outgoing connection logic with granular timeouts for each connection stage (TCP, TLS, XRPL handshake) to improve diagnostic capabilities
- Updated both PeerImp and ConnectAttempt to use proper asynchronous TLS shutdown procedures for graceful connection termination

* Downgrade to boost 1.83

* Set version to 2.6.1-rc1

* chore: Use self hosted windows runners (#5780)

This changes switches from the GitHub-managed Windows runners to self-hosted runners to significantly reduce build time.

* Rename mutable flags (#5797)

This is a minor change on top of #5705

* fix(amendment): Add missing fields for keylets to ledger objects (#5646)

This change adds a fix amendment (`fixIncludeKeyletFields`) that adds:
* `sfSequence` to `Escrow` and `PayChannel`
* `sfOwner` to `SignerList`
* `sfOracleDocumentID` to `Oracle`

This ensures that all ledger entries hold all the information needed to determine their keylet.

* chore: Limits CI build and test parallelism to reduce resource contention (#5799)

GitHub runners have a limit on how many concurrent jobs they can actually process (even though they will try to run them all at the same time), and similarly the Conan remote cannot handle hundreds of concurrent requests. Previously, the Conan dependency uploading was already limited to max 10 jobs running in parallel, and this change makes the same change to the build+test workflow.

* chore: Build and test all configs for daily scheduled run (#5801)

This change re-enables building and testing all configurations, but only for the daily scheduled run. Previously all configurations were run for each merge into the develop branch, but that overwhelmed both the GitHub runners and the Conan remote, and thus they were limited to just a subset of configurations. Now that the number of jobs is limited via `max-parallel: 10`, we should be able to safely enable building all configurations again. However, building them all once a day instead of for each PR merge should be sufficient.

* chore: Add unit tests dir to code coverage excludes (#5803)

This change excludes unit test code from code coverage reporting.

* refactor: Modularise ledger (#5493)

This change moves the ledger code to libxrpl.

* Mark PermissionDelegation as unsupported

* Set version to 2.6.1-rc2

* Miscellaneous refactors and updates (#5590)

- Added a new Invariant: `ValidPseudoAccounts` which checks that all pseudo-accounts behave consistently through creation and updates, and that no "real" accounts look like pseudo-accounts (which means they don't have a 0 sequence). 
- `to_short_string(base_uint)`. Like `to_string`, but only returns the first 8 characters. (Similar to how a git commit ID can be abbreviated.) Used as a wrapped sink to prefix most transaction-related messages. More can be added later.
- `XRPL_ASSERT_PARTS`. Convenience wrapper for `XRPL_ASSERT`, which takes the `function` and `description` as separate parameters.
- `SField::sMD_PseudoAccount`. Metadata option for `SField` definitions to indicate that the field, if set in an `AccountRoot` indicates that account is a pseudo-account. Removes the need for hard-coded field lists all over the place. Added the flag to `AMMID` and `VaultID`.
- Added functionality to `SField` ctor to detect both code and name collisions using asserts. And require all SFields to have a name
- Convenience type aliases `STLedgerEntry::const_pointer` and `STLedgerEntry::const_ref`. (`SLE` is an alias to `STLedgerEntry`.)
- Generalized `feeunit.h` (`TaggedFee`) into `unit.h` (`ValueUnit`) and added new "BIPS"-related tags for future use. Also refactored the type restrictions to use Concepts.
- Restructured `transactions.macro` to do two big things
	1. Include the `#include` directives for transactor header files directly in the macro file. Removes the need to update `applySteps.cpp` and the resulting conflicts.
	2. Added a `privileges` parameter to the `TRANSACTION` macro, which specifies some of the operations a transaction is allowed to do. These `privileges` are enforced by invariant checks. Again, removed the need to update scattered lists of transaction types in various checks.
- Unit tests:
	1.  Moved more helper functions into `TestHelpers.h` and `.cpp`. 
	2. Cleaned up the namespaces to prevent / mitigate random collisions and ambiguous symbols, particularly in unity builds.
	3. Generalized `Env::balance` to add support for `MPTIssue` and `Asset`.
	4. Added a set of helper classes to simplify `Env` transaction parameter classes: `JTxField`, `JTxFieldWrapper`, and a bunch of classes derived or aliased from it. For an example of how awesome it is, check the changes `src/test/jtx/escrow.h` for how much simpler the definitions are for `finish_time`, `cancel_time`, `condition`, and `fulfillment`. 
	5. Generalized several of the amount-related helper classes to understand `Asset`s.
     6. `env.balance` for an MPT issuer will return a negative number (or 0) for consistency with IOUs.

* refactor: Simplify STParsedJSON with some helper functions (#5591)

- Add code coverage for STParsedJSON edge cases

Co-authored-by: Denis Angell <dangell@transia.co>

* test: Add STInteger and STParsedJSON tests (#5726)

This change is to improve code coverage (and to simplify #5720 and #5725); there is otherwise no change in functionality. The change adds basic tests for `STInteger` and `STParsedJSON`, so it becomes easier to test smaller changes to the types, as well as removes `STParsedJSONArray`, since it is not used anywhere (including in Clio).

* Revert "Update Conan dependencies: OpenSSL" (#5807)

This change reverts #5617, because it will require extensive testing that will take up more time than we have before the next scheduled release.

Reverting this change does not mean we are abandoning it. We aim to pick it back up once there's a sufficient time window to allow for testing on multiple distros running a mixture of OpenSSL 1.x and 3.x.

* docs: Add warning about using std::counting_semaphore (#5595)

This adds a comment to avoid using `std::counting_semaphore` until the minimum compiler versions of GCC and Clang have been updated to no longer contain the bug that is present in older compilers.

* Improve ValidatorList invalid UNL manifest logging (#5804)

This change raises logging severity from `INFO` to `WARN` when handling UNL manifest signed with an unexpected / invalid key. It also changes the internal error code for an invalid format of UNL manifest to `invalid` (from `untrusted`).

This is a follow up to problems experienced by an UNL node due to old manifest key configured in `validators.txt`, which would be easier to diagnose with improved logging.

It also replaces a log line with `UNREACHABLE` for an impossible situation when we match UNL manifest key against a configured key which has an invalid type (we cannot configure such a key because of checks when loading configured keys).

* chore: Pin all CI Docker tags (#5813)

To avoid surprises and ensure reproducibility, this change pins all CI Docker image tags to the latest version in the XRPLF/CI repo.

* change `fixPriceOracleOrder` to `Supported::yes` (#5749)

* fix: Address http header case sensitivity (#5767)

This change makes the regex in `HttpClient.cpp` that matches the content-length http header case insensitive to improve compatibility, as http headers are case insensitive.

* test: add more comprehensive tests for `FeeVote` (#5746)

This change adds more comprehensive tests for the `FeeVote` module, which previously only checked the basics, and not the more comprehensive flows in that class.

* ci: Call all reusable workflows reusable (#5818)

* Add `STInt32` as a new `SType` (#5788)

This change adds `STInt32` as a new `SType` under the `STInteger` umbrella, with `SType` value `12`. This is the first and only `STInteger` type that supports negative values.

* switch `fixIncludeKeyletFields` to `Supported::yes` (#5819)

* refactor: Restructure Transactor::preflight to reduce boilerplate (#5592)

* Restructures `Transactor::preflight` to create several functions that will remove the need for error-prone boilerplate code in derived classes' implementations of `preflight`.

* refactor: Add support for extra transaction signatures (#5594)

* Restructures Transactor signature checking code to be able to handle a `sigObject`, which may be the full transaction, or may be an object field containing a separate signature. Either way, the `sigObject` can be a single- or multi-sign signature.

* ci: Upload artifacts during build and test in a separate job (#5817)

* chore: Set free-form CI inputs as env vars (#5822)

This change moves CI values that could be user-provided into environment variables.

* Rename flags for DynamicMPT (#5820)

* Set version to 2.6.1

* fix: FD/handle guarding + exponential backoff (#5823)

* fix: Transaction sig checking functions do not get a full context (#5829)

Fixes a (currently harmless) bug introduced by PR #5594

* Remove bogus coverage warning (#5838)

* fix return type

---------

Co-authored-by: Ed Hennis <ed@ripple.com>
Co-authored-by: Jingchen <a1q123456@users.noreply.github.com>
Co-authored-by: Denis Angell <dangell@transia.co>
Co-authored-by: Bart <bthomee@users.noreply.github.com>
Co-authored-by: yinyiqian1 <yqian@ripple.com>
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
Co-authored-by: Bronek Kozicki <brok@incorrekt.com>
Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
Co-authored-by: Valentin Balaschenko <13349202+vlntb@users.noreply.github.com>
Co-authored-by: tequ <git@tequ.dev>
Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
2025-10-07 14:14:34 -04:00
Shawn Xie
c3fdbc0430 SFields and formats (#5795) 2025-10-01 17:02:11 +00:00
145 changed files with 22532 additions and 2982 deletions

View File

@@ -153,7 +153,6 @@ Checks: "-*,
readability-use-std-min-max
"
# ---
# bugprone-narrowing-conversions, # this will break a lot of code but we should enable it in the future because it can eliminate a lot of bugs
# readability-inconsistent-declaration-parameter-name, # In this codebase this check will break a lot of arg names
# readability-static-accessed-through-instance, # this check is probably unnecessary. It makes the code less readable
# ---

View File

@@ -14,6 +14,7 @@ libxrpl.ledger > xrpl.json
libxrpl.ledger > xrpl.ledger
libxrpl.ledger > xrpl.nodestore
libxrpl.ledger > xrpl.protocol
libxrpl.ledger > xrpl.server
libxrpl.ledger > xrpl.shamap
libxrpl.net > xrpl.basics
libxrpl.net > xrpl.net
@@ -220,6 +221,7 @@ xrpl.core > xrpl.protocol
xrpl.json > xrpl.basics
xrpl.ledger > xrpl.basics
xrpl.ledger > xrpl.protocol
xrpl.ledger > xrpl.server
xrpl.ledger > xrpl.shamap
xrpl.net > xrpl.basics
xrpl.nodestore > xrpl.basics

View File

@@ -20,6 +20,8 @@ _SANITIZER_SUFFIX: dict[str, str] = {
def get_cmake_args(build_type: str, extra_args: str) -> str:
"""Get the full list of CMake arguments for a config."""
args = _BASE_CMAKE_ARGS.copy()
if build_type == "Release":
args.append("-Dassert=ON")
if extra_args:
args.extend(extra_args.split())
return " ".join(args)

View File

@@ -1,5 +1,5 @@
{
"image_tag": "sha-fe4c8ae",
"image_tag": "sha-63ffdc3",
"configs": {
"ubuntu": [
{
@@ -68,7 +68,7 @@
"compiler": ["gcc"],
"build_type": ["Release"],
"arch": ["amd64"],
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-577d745"
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-63ffdc3"
}
],
@@ -77,7 +77,7 @@
"compiler": ["gcc"],
"build_type": ["Release"],
"arch": ["amd64"],
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-577d745"
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-63ffdc3"
}
]
}

View File

@@ -9,20 +9,12 @@ on:
- "flake.nix"
- "flake.lock"
- "nix/**"
- "!nix/docker/README.md"
- "!nix/devshell.nix"
- "bin/check-tools.sh"
- "bin/install-sanitizer-libs.sh"
pull_request:
paths:
- ".github/workflows/build-nix-images.yml"
- "flake.nix"
- "flake.lock"
- "nix/**"
- "!nix/docker/README.md"
- "!nix/devshell.nix"
- "bin/check-tools.sh"
- "bin/install-sanitizer-libs.sh"
workflow_dispatch:
concurrency:

View File

@@ -41,7 +41,7 @@ env:
jobs:
build:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-fe4c8ae
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-63ffdc3
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

View File

@@ -121,11 +121,6 @@ jobs:
if: ${{ inputs.ccache_enabled && runner.debug == '1' }}
run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >>"${GITHUB_ENV}"
- name: Check tools
env:
CHECK_TOOLS_SKIP_CLONE: "1"
run: ./bin/check-tools.sh
- name: Print build environment
uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574
@@ -169,27 +164,6 @@ jobs:
${CMAKE_ARGS} \
..
# Export the sanitizer options before any instrumented binary runs. The
# protocol code-gen and build steps below invoke instrumented dependency
# tools (protoc, grpc), so setting UBSAN_OPTIONS here lets the UBSan
# suppression list silence their diagnostics too, not just at test time.
# GITHUB_WORKSPACE (not the github.workspace context) is used so the path
# resolves correctly inside the container job.
- name: Set sanitizer options
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
env:
CONFIG_NAME: ${{ inputs.config_name }}
run: |
SUPP="${GITHUB_WORKSPACE}/sanitizers/suppressions"
ASAN_OPTS="include=${SUPP}/runtime-asan-options.txt:suppressions=${SUPP}/asan.supp"
if [[ "${CONFIG_NAME}" == *gcc* ]]; then
ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0"
fi
echo "ASAN_OPTIONS=${ASAN_OPTS}" >>${GITHUB_ENV}
echo "TSAN_OPTIONS=include=${SUPP}/runtime-tsan-options.txt:suppressions=${SUPP}/tsan.supp" >>${GITHUB_ENV}
echo "UBSAN_OPTIONS=include=${SUPP}/runtime-ubsan-options.txt:suppressions=${SUPP}/ubsan.supp" >>${GITHUB_ENV}
echo "LSAN_OPTIONS=include=${SUPP}/runtime-lsan-options.txt:suppressions=${SUPP}/lsan.supp" >>${GITHUB_ENV}
- name: Check protocol autogen files are up-to-date
working-directory: ${{ env.BUILD_DIR }}
env:
@@ -305,6 +279,20 @@ jobs:
run: |
./xrpld --version | grep libvoidstar
- name: Set sanitizer options
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
env:
CONFIG_NAME: ${{ inputs.config_name }}
run: |
ASAN_OPTS="include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp"
if [[ "${CONFIG_NAME}" == *gcc* ]]; then
ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0"
fi
echo "ASAN_OPTIONS=${ASAN_OPTS}" >>${GITHUB_ENV}
echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >>${GITHUB_ENV}
echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >>${GITHUB_ENV}
echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >>${GITHUB_ENV}
- name: Run the separate tests
if: ${{ !inputs.build_only }}
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}

View File

@@ -20,12 +20,9 @@ env:
BUILD_DIR: build
BUILD_TYPE: Debug # Debug so that ASSERTS and such participate in clang-tidy check
OUTPUT_FILE: /tmp/clang-tidy-output.txt
FILTERED_OUTPUT_FILE: /tmp/clang-tidy-filtered-output.txt
DIFF_FILE: /tmp/clang-tidy-git-diff.txt
ISSUE_FILE: /tmp/clang-tidy-issue.md
COMPILER: clang
OUTPUT_FILE: clang-tidy-output.txt
DIFF_FILE: clang-tidy-git-diff.txt
ISSUE_FILE: clang-tidy-issue.md
jobs:
determine-files:
@@ -39,7 +36,7 @@ jobs:
needs: [determine-files]
if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }}
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-fe4c8ae"
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-63ffdc3"
permissions:
contents: read
issues: write
@@ -62,7 +59,7 @@ jobs:
- name: Set compiler environment
uses: ./.github/actions/set-compiler-env
with:
compiler: ${{ env.COMPILER }}
compiler: clang
- name: Setup Conan
uses: ./.github/actions/setup-conan
@@ -153,21 +150,21 @@ jobs:
run: |
if [ -f "${OUTPUT_FILE}" ]; then
# Extract lines containing 'error:', 'warning:', or 'note:'
grep -E '(error:|warning:|note:)' "${OUTPUT_FILE}" >"${FILTERED_OUTPUT_FILE}" || true
grep -E '(error:|warning:|note:)' "${OUTPUT_FILE}" >filtered-output.txt || true
# If filtered output is empty, use original (might be a different error format)
if [ ! -s "${FILTERED_OUTPUT_FILE}" ]; then
cp "${OUTPUT_FILE}" "${FILTERED_OUTPUT_FILE}"
if [ ! -s filtered-output.txt ]; then
cp "${OUTPUT_FILE}" filtered-output.txt
fi
# Truncate if too large
head -c 60000 "${FILTERED_OUTPUT_FILE}" >>"${ISSUE_FILE}"
if [ "$(wc -c <"${FILTERED_OUTPUT_FILE}")" -gt 60000 ]; then
head -c 60000 filtered-output.txt >>"${ISSUE_FILE}"
if [ "$(wc -c <filtered-output.txt)" -gt 60000 ]; then
echo "" >>"${ISSUE_FILE}"
echo "... (output truncated, see artifacts for full output)" >>"${ISSUE_FILE}"
fi
rm "${FILTERED_OUTPUT_FILE}"
rm filtered-output.txt
else
echo "No output file found" >>"${ISSUE_FILE}"
fi

View File

@@ -40,7 +40,7 @@ defaults:
jobs:
upload:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-fe4c8ae
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-63ffdc3
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

391
BUILD.md
View File

@@ -1,57 +1,26 @@
| :warning: **WARNING** :warning: |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| These instructions assume you have a C++ development environment ready with Git, Python, Conan, CMake, and a C++ compiler. For help setting one up on Linux, macOS, or Windows, [see this guide](./docs/build/environment.md).<br><br>These instructions also assume a basic familiarity with Conan and CMake. If you are unfamiliar with Conan, you can read our [crash course](./docs/build/conan.md) or the official [Getting Started][conan-getting-started] walkthrough. |
| :warning: **WARNING** :warning: |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| These instructions assume you have a C++ development environment ready with Git, Python, Conan, CMake, and a C++ compiler. For help setting one up on Linux, macOS, or Windows, [see this guide](./docs/build/environment.md). |
## Minimum Requirements
> These instructions also assume a basic familiarity with Conan and CMake.
> If you are unfamiliar with Conan, you can read our
> [crash course](./docs/build/conan.md) or the official [Getting Started][3]
> walkthrough.
See [System Requirements](https://xrpl.org/system-requirements.html).
## Branches
Building xrpld generally requires Git, Python, Conan, CMake, and a C++
compiler.
- [Python](https://www.python.org/downloads/)
- [Conan](https://conan.io/downloads.html)
- [CMake](https://cmake.org/download/)
You can verify that the required tools are installed and runnable with:
For a stable release, choose the `master` branch or one of the [tagged
releases](https://github.com/XRPLF/rippled/releases).
```bash
./bin/check-tools.sh
git checkout master
```
`xrpld` is written in the C++23 dialect. The [tested compiler versions][cpp23-support] are:
For the latest release candidate, choose the `release` branch.
| Compiler | Version |
| ----------- | --------------- |
| GCC | 15.2 |
| Clang | 22 |
| Apple Clang | 17 |
| MSVC | 19.44[^windows] |
## Operating Systems
Please see the [environment setup guide](./docs/build/environment.md) for detailed instructions for all platforms.
### Linux
The Ubuntu Linux distribution has received the highest level of quality
assurance, testing, and support. We also support Red Hat and use Debian
internally.
Our Linux CI tooling is distro-independent and uses a Nix-based environment, so it should be possible to build on other Linux distributions as well, although we have not tested them.
### macOS
Many `xrpld` engineers use macOS for development.
### Windows
Windows is used by some engineers for development only.
[^windows]: Windows is not recommended for production use.
## Steps
### Branches
```bash
git checkout release
```
For the latest set of untested features, or to contribute, choose the `develop`
branch.
@@ -60,15 +29,55 @@ branch.
git checkout develop
```
For a release candidate, choose the relevant release branch, e.g.
`release/3.2.x`.
## Minimum Requirements
```bash
git checkout release/3.2.x
```
See [System Requirements](https://xrpl.org/system-requirements.html).
For a stable release, choose one of the [tagged
releases](https://github.com/XRPLF/rippled/releases).
Building xrpld generally requires git, Python, Conan, CMake, and a C++
compiler. Some guidance on setting up such a [C++ development environment can be
found here](./docs/build/environment.md).
- [Python 3.11](https://www.python.org/downloads/), or higher
- [Conan 2.17](https://conan.io/downloads.html)[^1], or higher
- [CMake 3.22](https://cmake.org/download/), or higher
[^1]:
It is possible to build with Conan 1.60+, but the instructions are
significantly different, which is why we are not recommending it.
`xrpld` is written in the C++23 dialect and includes the `<concepts>` header.
The [tested compiler versions][2] are:
| Compiler | Version |
| ----------- | --------- |
| GCC | 15 |
| Clang | 22 |
| Apple Clang | 17 |
| MSVC | 19.44[^3] |
### Linux
The Ubuntu Linux distribution has received the highest level of quality
assurance, testing, and support. We also support Red Hat and use Debian
internally.
Here are [sample instructions for setting up a C++ development environment on
Linux](./docs/build/environment.md#linux).
### Mac
Many xrpld engineers use macOS for development.
Here are [sample instructions for setting up a C++ development environment on
macOS](./docs/build/environment.md#macos).
### Windows
Windows is used by some engineers for development only.
[^3]: Windows is not recommended for production use.
## Steps
### Set Up Conan
@@ -77,11 +86,18 @@ Conan, CMake, and a C++ compiler, you may need to set up your Conan profile.
These instructions assume a basic familiarity with Conan and CMake. If you are
unfamiliar with Conan, then please read [this crash course](./docs/build/conan.md) or the official
[Getting Started][conan-getting-started] walkthrough.
[Getting Started][3] walkthrough.
#### Profiles
#### Conan lockfile
We recommend that you install our Conan profiles:
To achieve reproducible dependencies, we use a [Conan lockfile](https://docs.conan.io/2/tutorial/versioning/lockfiles.html),
which has to be updated every time dependencies change.
Please see the [instructions on how to regenerate the lockfile](conan/lockfile/README.md).
#### Default profile
We recommend that you import the provided `conan/profiles/default` profile:
```bash
conan config install conan/profiles/ -tf $(conan config home)/profiles/
@@ -93,15 +109,222 @@ You can check your Conan profile by running:
conan profile show
```
If the default profile is not suitable for your environment, you can create a custom profile and pass it to Conan.
More information on customizing Conan can be found in the [Advanced Conan configuration](./docs/build/advanced_conan.md).
#### Custom profile
#### Add xrplf remote
Run the following command to add the `xrplf` remote, which hosts some of our dependencies:
If the default profile does not work for you and you do not yet have a Conan
profile, you can create one by running:
```bash
conan remote add --index 0 --force xrplf https://conan.ripplex.io
conan profile detect
```
You may need to make changes to the profile to suit your environment. You can
refer to the provided `conan/profiles/default` profile for inspiration, and you
may also need to apply the required [tweaks](#conan-profile-tweaks) to this
default profile.
### Patched recipes
Occasionally, we need patched recipes or recipes not present in Conan Center.
We maintain a fork of the Conan Center Index
[here](https://github.com/XRPLF/conan-center-index/) containing the modified and newly added recipes.
To ensure our patched recipes are used, you must add our Conan remote at a
higher index than the default Conan Center remote, so it is consulted first. You
can do this by running:
```bash
conan remote add --index 0 xrplf https://conan.ripplex.io
```
Alternatively, you can pull our recipes from the repository and export them locally:
```bash
# Define which recipes to export.
recipes=('abseil' 'ed25519' 'mpt-crypto' 'openssl' 'secp256k1' 'snappy' 'soci' 'wasm-xrplf' 'wasmi')
# Selectively check out the recipes from our CCI fork.
cd external
mkdir -p conan-center-index
cd conan-center-index
git init
git remote add origin git@github.com:XRPLF/conan-center-index.git
git sparse-checkout init
for recipe in "${recipes[@]}"; do
echo "Checking out recipe '${recipe}'..."
git sparse-checkout add recipes/${recipe}
done
git fetch origin master
git checkout master
./export_all.sh
cd ../../
```
In the case we switch to a newer version of a dependency that still requires a
patch or add a new dependency, it will be necessary for you to pull in the changes and re-export the
updated dependencies with the newer version. However, if we switch to a newer
version that no longer requires a patch, no action is required on your part, as
the new recipe will be automatically pulled from the official Conan Center.
> [!NOTE]
> You might need to add `--lockfile=""` to your `conan install` command
> to avoid automatic use of the existing `conan.lock` file when you run
> `conan export` manually on your machine
>
> This is not recommended though, as you might end up using different revisions of recipes.
### Conan profile tweaks
#### Missing compiler version
If you see an error similar to the following after running `conan profile show`:
```text
ERROR: Invalid setting '17' is not a valid 'settings.compiler.version' value.
Possible values are ['5.0', '5.1', '6.0', '6.1', '7.0', '7.3', '8.0', '8.1',
'9.0', '9.1', '10.0', '11.0', '12.0', '13', '13.0', '13.1', '14', '14.0', '15',
'15.0', '16', '16.0']
Read "http://docs.conan.io/2/knowledge/faq.html#error-invalid-setting"
```
you need to add your compiler to the list of compiler versions in
`$(conan config home)/settings_user.yml`, by adding the required version number(s)
to the `version` array specific for your compiler. For example:
```yaml
compiler:
apple-clang:
version: ["17.0"]
```
#### Multiple compilers
If you have multiple compilers installed, make sure to select the one to use in
your default Conan configuration **before** running `conan profile detect`, by
setting the `CC` and `CXX` environment variables.
For example, if you are running MacOS and have [homebrew
LLVM@18](https://formulae.brew.sh/formula/llvm@18), and want to use it as a
compiler in the new Conan profile:
```bash
export CC=$(brew --prefix llvm@18)/bin/clang
export CXX=$(brew --prefix llvm@18)/bin/clang++
conan profile detect
```
You should also explicitly set the path to the compiler in the profile file,
which helps to avoid errors when `CC` and/or `CXX` are set and disagree with the
selected Conan profile. For example:
```text
[conf]
tools.build:compiler_executables={'c':'/usr/bin/gcc','cpp':'/usr/bin/g++'}
```
#### Multiple profiles
You can manage multiple Conan profiles in the directory
`$(conan config home)/profiles`, for example renaming `default` to a different
name and then creating a new `default` profile for a different compiler.
#### Select language
The default profile created by Conan will typically select different C++ dialect
than C++23 used by this project. You should set `23` in the profile line
starting with `compiler.cppstd=`. For example:
```bash
sed -i.bak -e 's|^compiler\.cppstd=.*$|compiler.cppstd=23|' $(conan config home)/profiles/default
```
#### Select standard library in Linux
**Linux** developers will commonly have a default Conan [profile][] that
compiles with GCC and links with libstdc++. If you are linking with libstdc++
(see profile setting `compiler.libcxx`), then you will need to choose the
`libstdc++11` ABI:
```bash
sed -i.bak -e 's|^compiler\.libcxx=.*$|compiler.libcxx=libstdc++11|' $(conan config home)/profiles/default
```
#### Select architecture and runtime in Windows
**Windows** developers may need to use the x64 native build tools. An easy way
to do that is to run the shortcut "x64 Native Tools Command Prompt" for the
version of Visual Studio that you have installed.
Windows developers must also build `xrpld` and its dependencies for the x64
architecture:
```bash
sed -i.bak -e 's|^arch=.*$|arch=x86_64|' $(conan config home)/profiles/default
```
**Windows** developers also must select static runtime:
```bash
sed -i.bak -e 's|^compiler\.runtime=.*$|compiler.runtime=static|' $(conan config home)/profiles/default
```
#### Clang workaround for grpc
If your compiler is clang, version 19 or later, or apple-clang, version 17 or
later, you may encounter a compilation error while building the `grpc`
dependency:
```text
In file included from .../lib/promise/try_seq.h:26:
.../lib/promise/detail/basic_seq.h:499:38: error: a template argument list is expected after a name prefixed by the template keyword [-Wmissing-template-arg-list-after-template-kw]
499 | Traits::template CallSeqFactory(f_, *cur_, std::move(arg)));
| ^
```
The workaround for this error is to add two lines to profile:
```text
[conf]
tools.build:cxxflags=['-Wno-missing-template-arg-list-after-template-kw']
```
#### Workaround for gcc 12
If your compiler is gcc, version 12, and you have enabled `werr` option, you may
encounter a compilation error such as:
```text
/usr/include/c++/12/bits/char_traits.h:435:56: error: 'void* __builtin_memcpy(void*, const void*, long unsigned int)' accessing 9223372036854775810 or more bytes at offsets [2, 9223372036854775807] and 1 may overlap up to 9223372036854775813 bytes at offset -3 [-Werror=restrict]
435 | return static_cast<char_type*>(__builtin_memcpy(__s1, __s2, __n));
| ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors
```
The workaround for this error is to add two lines to your profile:
```text
[conf]
tools.build:cxxflags=['-Wno-restrict']
```
#### Workaround for clang 16
If your compiler is clang, version 16, you may encounter compilation error such
as:
```text
In file included from .../boost/beast/websocket/stream.hpp:2857:
.../boost/beast/websocket/impl/read.hpp:695:17: error: call to 'async_teardown' is ambiguous
async_teardown(impl.role, impl.stream(),
^~~~~~~~~~~~~~
```
The workaround for this error is to add two lines to your profile:
```text
[conf]
tools.build:cxxflags=['-DBOOST_ASIO_DISABLE_CONCEPTS']
```
### Set Up Ccache
@@ -110,7 +333,14 @@ To speed up repeated compilations, we recommend that you install
[ccache](https://ccache.dev), a tool that wraps your compiler so that it can
cache build objects locally.
On Linux and macOS, `ccache` is included in the [Nix development shell](./docs/build/nix.md).
#### Linux
You can install it using the package manager, e.g. `sudo apt install ccache`
(Ubuntu) or `sudo dnf install ccache` (RHEL).
#### macOS
You can install it using Homebrew, i.e. `brew install ccache`.
#### Windows
@@ -319,7 +549,7 @@ See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
| Option | Default Value | Description |
| ---------- | ------------- | -------------------------------------------------------------- |
| `assert` | OFF | Force enabling assertions. |
| `assert` | OFF | Enable assertions. |
| `coverage` | OFF | Prepare the coverage report. |
| `tests` | OFF | Build tests. |
| `unity` | OFF | Configure a unity build. |
@@ -327,7 +557,7 @@ See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
| `werr` | OFF | Treat compilation warnings as errors |
| `wextra` | OFF | Enable additional compilation warnings |
[Unity builds][unity-build] may be faster for the first build (at the cost of much more
[Unity builds][5] may be faster for the first build (at the cost of much more
memory) since they concatenate sources into fewer translation units. Non-unity
builds may be faster for incremental builds, and can be helpful for detecting
`#include` omissions.
@@ -353,14 +583,14 @@ After any updates or changes to dependencies, you may need to do the following:
conan remove '*'
```
3. Re-run [conan export](./docs/build/advanced_conan.md#patched-recipes) if needed.
4. [Regenerate lockfile](./docs/build/advanced_conan.md#conan-lockfile).
3. Re-run [conan export](#patched-recipes) if needed.
4. [Regenerate lockfile](#conan-lockfile).
5. Re-run [conan install](#build-and-test).
#### ERROR: Package not resolved
If you're seeing an error like `ERROR: Package 'snappy/1.1.10' not resolved: Unable to find 'snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246' in remotes.`,
please [add `xrplf` remote](#add-xrplf-remote) or re-run `conan export` for [patched recipes](./docs/build/advanced_conan.md#patched-recipes).
please add `xrplf` remote or re-run `conan export` for [patched recipes](#patched-recipes).
### `protobuf/port_def.inc` file not found
@@ -380,9 +610,28 @@ For example, if you want to build Debug:
1. For conan install, pass `--settings build_type=Debug`
2. For cmake, pass `-DCMAKE_BUILD_TYPE=Debug`
[cpp23-support]: https://en.cppreference.com/w/cpp/compiler_support/23
[conan-getting-started]: https://docs.conan.io/en/latest/getting_started.html
[unity-build]: https://en.wikipedia.org/wiki/Unity_build
## Add a Dependency
If you want to experiment with a new package, follow these steps:
1. Search for the package on [Conan Center](https://conan.io/center/).
2. Modify [`conanfile.py`](./conanfile.py):
- Add a version of the package to the `requires` property.
- Change any default options for the package by adding them to the
`default_options` property (with syntax `'$package:$option': $value`).
3. Modify [`CMakeLists.txt`](./CMakeLists.txt):
- Add a call to `find_package($package REQUIRED)`.
- Link a library from the package to the target `xrpl_libs`
(search for the existing call to `target_link_libraries(xrpl_libs INTERFACE ...)`).
4. Start coding! Don't forget to include whatever headers you need from the package.
[1]: https://github.com/conan-io/conan-center-index/issues/13168
[2]: https://en.cppreference.com/w/cpp/compiler_support/20
[3]: https://docs.conan.io/en/latest/getting_started.html
[5]: https://en.wikipedia.org/wiki/Unity_build
[6]: https://github.com/boostorg/beast/issues/2648
[7]: https://github.com/boostorg/beast/issues/2661
[gcovr]: https://gcovr.com/en/stable/getting-started.html
[python-pip]: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/
[build_type]: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html
[profile]: https://docs.conan.io/en/latest/reference/profiles.html

View File

@@ -88,6 +88,7 @@ find_package(ed25519 REQUIRED)
find_package(gRPC REQUIRED)
find_package(LibArchive REQUIRED)
find_package(lz4 REQUIRED)
find_package(mpt-crypto REQUIRED)
find_package(nudb REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(secp256k1 REQUIRED)
@@ -100,6 +101,7 @@ target_link_libraries(
INTERFACE
ed25519::ed25519
lz4::lz4
mpt-crypto::mpt-crypto
OpenSSL::Crypto
OpenSSL::SSL
secp256k1::secp256k1

View File

@@ -14,9 +14,9 @@ The following branches exist in the main project repository:
- `develop`: The latest set of unreleased features, and the most common
starting point for contributions.
- `release/*` (e.g. `release/3.2.x`): Release branches, one per release line,
holding the latest release candidate, or stable release for that line.
Stable releases are published as [tagged releases](https://github.com/XRPLF/rippled/releases).
- `release`: The latest beta release or release candidate.
- `master`: The latest stable release.
- `gh-pages`: The documentation for this project, built by Doxygen.
The tip of each branch must be signed. In order for GitHub to sign a
squashed commit that it builds from your pull request, GitHub must know
@@ -130,9 +130,11 @@ tl;dr
## Pull requests
In general, pull requests use `develop` as the base branch.
The exceptions are
The exceptions are fixes, improvements, and hotfixes for an existing release,
which use that release's branch (e.g. `release/3.2.x`) as the base.
- Fixes and improvements to a release candidate use `release` as the
base.
- Hotfixes use `master` as the base.
If your changes are not quite ready, but you want to make it easily available
for preliminary examination or review, you can create a "Draft" pull request.
@@ -214,7 +216,7 @@ coherent rather than a set of _thou shalt not_ commandments.
## Formatting
All code must conform to `clang-format` version 22,
All code must conform to `clang-format` version 21,
according to the settings in [`.clang-format`](./.clang-format),
unless the result would be unreasonably difficult to read or maintain.
To demarcate lines that should be left as-is, surround them with comments like
@@ -259,7 +261,7 @@ This ensures that configuration changes don't introduce new warnings across the
### Installing clang-tidy
See the [environment setup guide](./docs/build/environment.md#clang-tidy) for how to get clang-tidy.
See the [environment setup guide](./docs/build/environment.md#clang-tidy) for platform-specific installation instructions.
### Running clang-tidy locally

View File

@@ -1,158 +0,0 @@
#!/usr/bin/env bash
#
# check-tools.sh — verify the xrpld development tooling is present and runnable.
#
# Works on Linux, macOS, and Windows (Git Bash / MSYS). For every expected tool
# it runs a version probe, collecting anything that is missing or fails to run,
# and prints a summary at the end (exiting non-zero if anything is missing).
#
# The tool set is platform-aware:
# - Linux: the full Nix CI environment (see nix/packages.nix, nix/ci-env.nix),
# with GCC, Clang and the sanitizer/coverage tooling. This script is
# run during the Nix Docker image build (nix/docker/Dockerfile), so
# the Linux list is kept in sync with that environment.
# - macOS: the same tooling, minus GCC/g++/gcov/mold
# - Windows: the core build tools only (CMake, Conan, Git, Python).
# MSVC is expected to be provided separately and is not checked here.
#
# Some tools (clang-format, doxygen, gcovr, gh, git-cliff, gpg, pre-commit,
# run-clang-tidy) are present in our Linux CI images and in local development
# setups, but not in the macOS CI environment. They are checked everywhere
# except when running in CI on macOS.
#
# Environment variables:
# CI if set, skip the tools above when on macOS.
# CHECK_TOOLS_SKIP_CLONE if set, skip the git-over-HTTPS connectivity check.
set -uo pipefail
missing=()
checked=0
# check <name> [probe-command...]
# Runs the probe (default: "<name> --version") quietly. Records <name> as
# missing if the command is not found or exits non-zero.
check() {
local name="$1"
shift
local -a probe=("$@")
if [ "${#probe[@]}" -eq 0 ]; then
probe=("${name}" --version)
fi
echo "Checking ${name}..."
checked=$((checked + 1))
if "${probe[@]}" | head -n 1; then
printf ' [ ok ] %s\n' "${name}"
else
printf ' [MISS] %s\n' "${name}"
missing+=("${name}")
fi
}
case "$(uname -s)" in
Linux*) os=linux ;;
Darwin*) os=macos ;;
MINGW* | MSYS* | CYGWIN*) os=windows ;;
*)
echo "Unknown OS: $(uname -s)" >&2
exit 1
;;
esac
echo "Detected OS: ${os} ($(uname -s) $(uname -m))"
echo
echo "Core build tools:"
check cmake
check conan
check git
if [ "${os}" = "windows" ]; then
check python python --version
else
check python3
fi
# The full development toolchain. Available from Nix on Linux and macOS; on
# Windows these are typically not installed, so they are skipped.
if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
echo
echo "Development tooling:"
check ccache
check clang
check clang++
check ClangBuildAnalyzer
check curl
check file
check less
check make
check netstat which netstat
check ninja
check perl
check pkg-config
check vim
# These tools are present in our Linux CI images and in local development
# setups, but not in the macOS CI environment. So check them everywhere
# except when running in CI on macOS.
if [ "${os}" = "linux" ] || [ -z "${CI:-}" ]; then
check clang-format
check doxygen
check gcovr
check gh
check git-cliff
check gpg
# pre-commit, or its alternative implementation prek
check pre-commit sh -c 'pre-commit --version || prek --version'
check run-clang-tidy run-clang-tidy --help
fi
fi
# GCC is the default compiler on Linux. macOS uses the system Apple Clang
# instead, so GCC/g++/gcov are not expected there.
if [ "${os}" = "linux" ]; then
echo
echo "GCC toolchain:"
check gcc
check g++
check gcov
echo
echo "Mold:"
check mold
fi
if [ "${os}" = "windows" ]; then
echo
echo "Note: on Windows the C++ compiler is MSVC, which is provided"
echo " separately (e.g. via Visual Studio) and is not checked here."
fi
# A simple test to verify that git can clone a repository over HTTPS
# (i.e. the CA bundle is wired up). Clone to a temp dir and clean up.
if [ -n "${CHECK_TOOLS_SKIP_CLONE:-}" ]; then
echo
echo "Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set)."
else
echo
echo "Connectivity check:"
checked=$((checked + 1))
tmp_clone="$(mktemp -d)"
if git clone --depth 1 https://github.com/XRPLF/actions.git "${tmp_clone}/actions" >/dev/null 2>&1; then
printf ' [ ok ] git clone over HTTPS\n'
else
printf ' [MISS] git clone over HTTPS\n'
missing+=("git-https-clone")
fi
rm -rf "${tmp_clone}"
fi
echo
if [ "${#missing[@]}" -eq 0 ]; then
echo "All ${checked} checked tools are present and runnable."
else
echo "Missing or non-functional tools (${#missing[@]} of ${checked}):" >&2
for tool in "${missing[@]}"; do
echo " - ${tool}" >&2
done
exit 1
fi

View File

@@ -1,9 +1,9 @@
{
"version": "0.5",
"requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503",
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1776096494.149",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1778091117.311",
"soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
"secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329",
@@ -12,10 +12,11 @@
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
"openssl/3.6.2#4789bbf131b77d0515d15e094c8f697f%1778071755.506",
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1775040983.408",
"mpt-crypto/0.3.0-rc5#536cb5bdf3cc7ce1b29373fa94d7eab9%1781638154.791",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1776147552.838",
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1778091117.848",
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
"grpc/1.78.1#b1a9e74b145cc471bed4dc64dc6eb2c1%1774467387.342",
@@ -23,11 +24,11 @@
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778050991.9",
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778091165.282",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
],
"build_requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503",
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964",
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707",
@@ -41,12 +42,18 @@
],
"python_requires": [],
"overrides": {
"openssl/[>=1.1 <4]": [
"openssl/3.6.2"
],
"protobuf/[>=5.27.0 <7]": [
"protobuf/6.33.5"
],
"lz4/1.9.4": [
"lz4/1.10.0"
],
"openssl/[>=3.5 <4]": [
"openssl/3.6.2#4789bbf131b77d0515d15e094c8f697f"
],
"boost/[>=1.83.0 <1.91.0]": [
"boost/1.91.0"
],

View File

@@ -30,8 +30,8 @@ class Xrpl(ConanFile):
"ed25519/2015.03",
"grpc/1.78.1",
"libarchive/3.8.7",
"mpt-crypto/0.3.0-rc5",
"nudb/2.0.9",
"openssl/3.6.2",
"secp256k1/0.7.1",
"soci/4.0.3",
"zlib/1.3.2",
@@ -133,6 +133,7 @@ class Xrpl(ConanFile):
self.requires("boost/1.91.0", force=True, transitive_headers=True)
self.requires("date/3.0.4", transitive_headers=True)
self.requires("lz4/1.10.0", force=True)
self.requires("openssl/3.6.2", force=True)
self.requires("protobuf/6.33.5", force=True)
self.requires("sqlite3/3.53.0", force=True)
if self.options.jemalloc:
@@ -208,6 +209,7 @@ class Xrpl(ConanFile):
"grpc::grpc++",
"libarchive::libarchive",
"lz4::lz4",
"mpt-crypto::mpt-crypto",
"nudb::nudb",
"openssl::crypto",
"protobuf::libprotobuf",

View File

@@ -60,6 +60,7 @@ words:
- autobridging
- bimap
- bindir
- blindings
- bookdir
- Bougalis
- Britto
@@ -95,6 +96,7 @@ words:
- daria
- dcmake
- dearmor
- decryptor
- dedented
- deleteme
- demultiplexer
@@ -106,10 +108,10 @@ words:
- distro
- doxyfile
- dxrpl
- elgamal
- enabled
- enablerepo
- endmacro
- envrc
- exceptioned
- EXPECT_STREQ
- Falco
@@ -119,6 +121,7 @@ words:
- fmtdur
- fsanitize
- funclets
- Gamal
- gcov
- gcovr
- ghead
@@ -216,6 +219,7 @@ words:
- partitioner
- paychan
- paychans
- Pedersen
- permdex
- perminute
- permissioned
@@ -239,6 +243,10 @@ words:
- Raphson
- rcflags
- replayer
- rerandomize
- rerandomization
- rerandomized
- rerandomizes
- rerere
- retriable
- RIPD
@@ -255,6 +263,7 @@ words:
- sahyadri
- Satoshi
- scons
- Schnorr
- secp
- sendq
- seqit
@@ -285,6 +294,7 @@ words:
- stvar
- stvector
- stxchainattestations
- summands
- superpeer
- superpeers
- takergets

View File

@@ -1,193 +0,0 @@
# Advanced Conan configuration
This document provides advanced instructions for setting up and configuring Conan for `xrpld` development: custom profiles, the lockfile, patched recipes, and profile tweaks.
## Custom profile
If the default profile does not work for you and you do not yet have a Conan
profile, you can create one by running:
```bash
conan profile detect
```
You may need to make changes to the profile to suit your environment. You can
refer to the provided `conan/profiles/default` profile for inspiration, and you
may also need to apply the required [tweaks](#conan-profile-tweaks) to this
default profile.
## Conan lockfile
To achieve reproducible dependencies, we use a [Conan lockfile](https://docs.conan.io/2/tutorial/versioning/lockfiles.html),
which has to be updated every time dependencies change.
Please see the [instructions on how to regenerate the lockfile](../../conan/lockfile/README.md).
## Patched recipes
Occasionally, we need patched recipes or recipes not present in Conan Center.
We maintain a fork of the Conan Center Index
[here](https://github.com/XRPLF/conan-center-index/) containing the modified and newly added recipes.
To ensure our patched recipes are used, you must add our Conan remote at a
higher index than the default Conan Center remote, so it is consulted first. You
can do this by running:
```bash
conan remote add --index 0 --force xrplf https://conan.ripplex.io
```
Alternatively, you can pull our recipes from the repository and export them locally:
```bash
# Define which recipes to export.
recipes=('abseil' 'ed25519' 'mpt-crypto' 'openssl' 'secp256k1' 'snappy' 'soci' 'wasm-xrplf' 'wasmi')
# Selectively check out the recipes from our CCI fork.
cd external
mkdir -p conan-center-index
cd conan-center-index
git init
git remote add origin git@github.com:XRPLF/conan-center-index.git
git sparse-checkout init
for recipe in "${recipes[@]}"; do
echo "Checking out recipe '${recipe}'..."
git sparse-checkout add recipes/${recipe}
done
git fetch origin master
git checkout master
./export_all.sh
cd ../../
```
In the case we switch to a newer version of a dependency that still requires a
patch or add a new dependency, it will be necessary for you to pull in the changes and re-export the
updated dependencies with the newer version. However, if we switch to a newer
version that no longer requires a patch, no action is required on your part, as
the new recipe will be automatically pulled from the official Conan Center.
> [!NOTE]
> You might need to add `--lockfile=""` to your `conan install` command
> to avoid automatic use of the existing `conan.lock` file when you run
> `conan export` manually on your machine
>
> This is not recommended though, as you might end up using different revisions of recipes.
## Conan profile tweaks
### Missing compiler version
If you see an error similar to the following after running `conan profile show`:
```text
ERROR: Invalid setting '17' is not a valid 'settings.compiler.version' value.
Possible values are ['5.0', '5.1', '6.0', '6.1', '7.0', '7.3', '8.0', '8.1',
'9.0', '9.1', '10.0', '11.0', '12.0', '13', '13.0', '13.1', '14', '14.0', '15',
'15.0', '16', '16.0']
Read "http://docs.conan.io/2/knowledge/faq.html#error-invalid-setting"
```
you need to create `$(conan config home)/settings_user.yml` file if it doesn't exist and add the required version number(s)
to the `version` array specific for your compiler. For example:
```yaml
compiler:
apple-clang:
version: ["17.0"]
```
### Multiple compilers
If you have multiple compilers installed, make sure to select the one to use in
your default Conan configuration **before** running `conan profile detect`, by
setting the `CC` and `CXX` environment variables.
For example, if you are running MacOS and have [homebrew
LLVM@18](https://formulae.brew.sh/formula/llvm@18), and want to use it as a
compiler in the new Conan profile:
```bash
export CC=$(brew --prefix llvm@18)/bin/clang
export CXX=$(brew --prefix llvm@18)/bin/clang++
conan profile detect
```
You should also explicitly set the path to the compiler in the profile file,
which helps to avoid errors when `CC` and/or `CXX` are set and disagree with the
selected Conan profile. For example:
```text
[conf]
tools.build:compiler_executables={'c':'/usr/bin/gcc','cpp':'/usr/bin/g++'}
```
### Multiple profiles
You can manage multiple Conan profiles in the directory
`$(conan config home)/profiles`, for example renaming `default` to a different
name and then creating a new `default` profile for a different compiler.
### Select language
The default profile created by Conan will typically select different C++ dialect
than C++23 used by this project. You should set `23` in the profile line
starting with `compiler.cppstd=`. For example:
```bash
sed -i.bak -e 's|^compiler\.cppstd=.*$|compiler.cppstd=23|' $(conan config home)/profiles/default
```
### Select standard library in Linux
**Linux** developers will commonly have a default Conan [profile][] that
compiles with GCC and links with libstdc++. If you are linking with libstdc++
(see profile setting `compiler.libcxx`), then you will need to choose the
`libstdc++11` ABI:
```bash
sed -i.bak -e 's|^compiler\.libcxx=.*$|compiler.libcxx=libstdc++11|' $(conan config home)/profiles/default
```
### Select architecture and runtime in Windows
**Windows** developers may need to use the x64 native build tools. An easy way
to do that is to run the shortcut "x64 Native Tools Command Prompt" for the
version of Visual Studio that you have installed.
Windows developers must also build `xrpld` and its dependencies for the x64
architecture:
```bash
sed -i.bak -e 's|^arch=.*$|arch=x86_64|' $(conan config home)/profiles/default
```
**Windows** developers also must select static runtime:
```bash
sed -i.bak -e 's|^compiler\.runtime=.*$|compiler.runtime=static|' $(conan config home)/profiles/default
```
## Add a Dependency
If you want to experiment with a new package, follow these steps:
1. Search for the package on [Conan Center](https://conan.io/center/).
2. Modify [`conanfile.py`](../../conanfile.py):
- Add a version of the package to the `requires` property.
- Change any default options for the package by adding them to the
`default_options` property (with syntax `'$package:$option': $value`).
3. Regenerate the [Conan lockfile](../../conan/lockfile/README.md) so the new
dependency is captured:
```bash
./conan/lockfile/regenerate.sh
```
4. Modify [`CMakeLists.txt`](../../CMakeLists.txt):
- Add a call to `find_package($package REQUIRED)`.
- Link a library from the package to the target `xrpl_libs`
(search for the existing call to `target_link_libraries(xrpl_libs INTERFACE ...)`).
5. Start coding! Don't forget to include whatever headers you need from the package.
[profile]: https://docs.conan.io/2/reference/config_files/profiles.html

2
docs/build/conan.md vendored
View File

@@ -115,7 +115,7 @@ By default, Conan will use the profile named "default".
[find_package]: https://cmake.org/cmake/help/latest/command/find_package.html
[pcf]: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#package-configuration-file
[prefix_path]: https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html
[profile]: https://docs.conan.io/2/reference/config_files/profiles.html
[profile]: https://docs.conan.io/en/latest/reference/profiles.html
[pvf]: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#package-version-file
[runtime]: https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html
[search]: https://cmake.org/cmake/help/latest/command/find_package.html#search-procedure

View File

@@ -1,73 +1,69 @@
Our [build instructions][BUILD.md] assume you have a C++ development
environment complete with Git, Python, Conan, CMake, and a C++ compiler.
This document explains how to set one up.
This document exists to help readers set one up on any of the Big Three
platforms: Linux, macOS, or Windows.
As an alternative to system packages, the Nix development shell can be used to provide a development environment. See [using nix development shell](./nix.md) for more details.
[BUILD.md]: ../../BUILD.md
## Tested compiler versions
## Linux
`xrpld` is built in the **C++23** dialect by default.
Make sure your toolchain is recent enough — the compiler versions currently tested in CI are:
Package ecosystems vary across Linux distributions,
so there is no one set of instructions that will work for every Linux user.
The instructions below are written for Debian 12 (Bookworm).
| Compiler | Version |
| ----------- | ------- |
| GCC | 15.2 |
| Clang | 22 |
| Apple Clang | 17 |
| MSVC | 19.44 |
```
export GCC_RELEASE=12
sudo apt update
sudo apt install --yes gcc-${GCC_RELEASE} g++-${GCC_RELEASE} python3-pip \
python-is-python3 python3-venv python3-dev curl wget ca-certificates \
git build-essential cmake ninja-build libc6-dev
sudo pip install --break-system-packages conan
LLVM tools (`clang-tidy` and `clang-format`) are also pinned to version 22.
Older compilers may fail to build the latest `develop` code: the codebase now
relies on C++23 features and has been adjusted for `clang-tidy`.
If the latest code doesn't build for you, update your build toolchain first.
## Linux and macOS
The **recommended way** to get a development environment on Linux and macOS is
the Nix development shell. It provides the exact tooling used in CI — `git`,
`python`, `conan`, `cmake`, `clang-tidy`, `clang-format`, and everything else —
with a single command and without installing anything system-wide:
```bash
nix --experimental-features 'nix-command flakes' develop
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-${GCC_RELEASE} 999
sudo update-alternatives --install \
/usr/bin/gcc gcc /usr/bin/gcc-${GCC_RELEASE} 100 \
--slave /usr/bin/g++ g++ /usr/bin/g++-${GCC_RELEASE} \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-${GCC_RELEASE} \
--slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-${GCC_RELEASE} \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-${GCC_RELEASE} \
--slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_RELEASE} \
--slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-${GCC_RELEASE} \
--slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-${GCC_RELEASE} \
--slave /usr/bin/lto-dump lto-dump /usr/bin/lto-dump-${GCC_RELEASE}
sudo update-alternatives --auto cc
sudo update-alternatives --auto gcc
```
On **Linux**, Nix also provides the compiler (GCC). On **macOS**, the shell uses
your **system-wide Apple Clang** as the compiler, so you still need to manage
its version (see below).
If you use different Linux distribution, hope the instruction above can guide
you in the right direction. We try to maintain compatibility with all recent
compiler releases, so if you use a rolling distribution like e.g. Arch or CentOS
then there is a chance that everything will "just work".
See [Using the Nix development shell](./nix.md) for installation and usage
details, including how to select a different compiler.
## macOS
> [!NOTE]
> Using Nix is not mandatory. Any custom environment (Homebrew packages or
> anything else) will continue to work, but then it is up to you to keep it in
> sync with the environment used in CI. Nix unifies the development environment
> for everyone and synchronizes updates, which is why we recommend it.
Open a Terminal and enter the below command to bring up a dialog to install
the command line developer tools.
Once it is finished, this command should return a version greater than the
minimum required (see [BUILD.md][]).
### macOS: managing the Apple Clang version
Because the Nix shell uses the system-wide Apple Clang on macOS, the compiler
version is whatever your installed Xcode (or Command Line Tools) provides. The
following command should return a version greater than or equal to the
[minimum required](#tested-compiler-versions):
```bash
```
clang --version
```
If you develop other applications using Xcode, you might be consistently
updating to the newest version of Apple Clang, which will likely cause issues
building xrpld. You may want to install and pin a specific version of Xcode:
### Install Xcode Specific Version (Optional)
If you develop other applications using XCode you might be consistently updating to the newest version of Apple Clang.
This will likely cause issues building xrpld. You may want to install a specific version of Xcode:
1. **Download Xcode**
- Visit [Apple Developer Downloads](https://developer.apple.com/download/more/)
- Sign in with your Apple Developer account
- Search for an Xcode version that includes the expected Apple Clang version
- Search for an Xcode version that includes **Apple Clang (Expected Version)**
- Download the `.xip` file
2. **Install and configure Xcode**
2. **Install and Configure Xcode**
```bash
# Extract the .xip file and rename for version management
@@ -83,28 +79,62 @@ building xrpld. You may want to install and pin a specific version of Xcode:
export DEVELOPER_DIR=/Applications/Xcode_16.2.app/Contents/Developer
```
## Windows
The command line developer tools should include Git too:
Nix is not available on Windows, so the required tools have to be installed
manually:
```
git --version
```
- [Visual Studio 2022](https://visualstudio.microsoft.com/) with the
**"Desktop development with C++"** workload — this provides MSVC and the
"x64 Native Tools Command Prompt".
- [Git for Windows](https://git-scm.com/download/win)
- [Python 3.11](https://www.python.org/downloads/), or higher
- [Conan 2.17](https://conan.io/downloads.html), or higher
- [CMake 3.22](https://cmake.org/download/), or higher
Install [Homebrew][],
use it to install [pyenv][],
use it to install Python,
and use it to install Conan:
> [!NOTE]
> Windows is used for development only and is not recommended for production.
[Homebrew]: https://brew.sh/
[pyenv]: https://github.com/pyenv/pyenv
```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew update
brew install xz
brew install pyenv
pyenv install 3.11
pyenv global 3.11
eval "$(pyenv init -)"
pip install 'conan'
```
Install CMake with Homebrew too:
```
brew install cmake
```
## Clang-tidy
`clang-tidy` is required to run static analysis checks locally (see
[CONTRIBUTING.md](../../CONTRIBUTING.md)). It is not required to build the
project. This project currently uses `clang-tidy` version 22.
Clang-tidy is required to run static analysis checks locally (see [CONTRIBUTING.md](../../CONTRIBUTING.md)).
It is not required to build the project. Currently this project uses clang-tidy version 21.
On Linux and macOS, the [Nix development shell](./nix.md) provides `clang-tidy`
22 out of the box — run it via `run-clang-tidy`. No separate installation is
needed.
### Linux
LLVM 21 is not available in the default Debian 12 (Bookworm) repositories.
Install it using the official LLVM apt installer:
```
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 21
sudo apt install --yes clang-tidy-21
```
Then use `run-clang-tidy-21` when running clang-tidy locally.
### macOS
Install LLVM 21 via Homebrew:
```
brew install llvm@21
```
Then use `run-clang-tidy` from the LLVM 21 Homebrew prefix when running clang-tidy locally.

45
docs/build/nix.md vendored
View File

@@ -2,12 +2,9 @@
This guide explains how to use Nix to set up a reproducible development environment for xrpld. Using Nix eliminates the need to manually install utilities and ensures consistent tooling across different machines.
**The Nix development shell is the recommended way to develop xrpld.** It unifies the development environment for everyone and synchronizes updates: the same tooling and compiler versions are used both here and in CI. Any custom environment (Homebrew packages or anything else) will continue to work, but then it is up to you to keep it in sync with the environment used in CI.
## Benefits of Using Nix
- **Reproducible environment**: Everyone gets the same versions of tools and compilers
- **Matches CI**: The Linux CI runs in Docker images built from this exact Nix environment
- **No system pollution**: Dependencies are isolated and don't affect your system packages
- **Multiple compiler versions**: Easily switch between different GCC and Clang versions
- **Quick setup**: Get started with a single command
@@ -31,22 +28,11 @@ This will:
- Download and set up all required development tools (CMake, Ninja, Conan, etc.)
- Configure the appropriate compiler for your platform:
- **Linux**: GCC 15.2 (provided by Nix)
- **macOS**: Apple Clang (your system compiler)
- **macOS**: Apple Clang (default system compiler)
- **Linux**: GCC 15
The first time you run this command, it will take a few minutes to download and build the environment. Subsequent runs will be much faster.
### Platform notes
- **Linux**: `nix develop` gives you a shell with all the tooling necessary to
develop xrpld and with GCC 15.2 (also provided by Nix). There are no caveats.
- **macOS**: `nix develop` gives you a full environment too. The compiler is
your system-wide Apple Clang, while every other tool — including Conan — is
provided by Nix. Conan has no binary in the Nix cache for macOS, so it is
built from source the first time you enter the shell, which makes the initial
setup slower (this is handled automatically; see
[`nix/devshell.nix`](../../nix/devshell.nix)).
> [!TIP]
> To avoid typing `--experimental-features 'nix-command flakes'` every time, you can permanently enable flakes by creating `~/.config/nix/nix.conf`:
>
@@ -65,7 +51,7 @@ The first time you run this command, it will take a few minutes to download and
A compiler can be chosen by providing its name with the `.#` prefix, e.g. `nix develop .#gcc15`.
Use `nix flake show` to see all the available development shells.
Use `nix develop .#no-compiler` to use the compiler from your system.
Use `nix develop .#no_compiler` to use the compiler from your system.
### Example Usage
@@ -82,28 +68,12 @@ nix develop
### Using a different shell
`nix develop` opens bash by default. To use another shell, pass it with the `-c` flag — this works with any shell, e.g. `zsh` or `fish`:
`nix develop` opens bash by default. If you want to use another shell this could be done by adding `-c` flag. For example:
```bash
# Use zsh
nix develop -c zsh
# Use fish
nix develop -c fish
# Use your login shell
nix develop -c "$SHELL"
```
> [!WARNING]
> Your shell's interactive startup files (e.g. `config.fish`, `.zshrc`) may prepend other directories — most commonly Homebrew — to `$PATH`, which can shadow the tools provided by the Nix shell. After entering, verify that tools resolve into the Nix store:
>
> ```bash
> command -v cmake # should print a /nix/store/... path
> ```
>
> If it doesn't, either adjust your shell configuration so it doesn't override `$PATH`, or use [direnv](#automatic-activation-with-direnv) (below), which loads the environment _after_ your shell config and so takes precedence regardless of the shell you use.
## Building xrpld with Nix
Once inside the Nix development shell, follow the standard [build instructions](../../BUILD.md#steps). The Nix shell provides all necessary tools (CMake, Ninja, Conan, etc.).
@@ -112,8 +82,6 @@ Once inside the Nix development shell, follow the standard [build instructions](
[direnv](https://direnv.net/) or [nix-direnv](https://github.com/nix-community/nix-direnv) can automatically activate the Nix development shell when you enter the repository directory.
This is also the most robust way to use the environment from **any shell** (bash, zsh, fish, …): direnv stays in your current shell and loads the environment _after_ your shell's startup files have run, so the Nix-provided tools take precedence over anything your shell configuration adds to `$PATH`. To use it, install direnv for your shell, then add an `.envrc` containing `use flake` at the repository root and run `direnv allow`.
## Conan and Prebuilt Packages
Please note that there is no guarantee that binaries from conan cache will work when using nix. If you encounter any errors, please use `--build '*'` to force conan to compile everything from source:
@@ -125,8 +93,3 @@ conan install .. --output-folder . --build '*' --settings build_type=Release
## Updating `flake.lock` file
To update `flake.lock` to the latest revision use `nix flake update` command.
## Troubleshooting
See [Troubleshooting Nix problems](./nix_troubleshooting.md) for common issues,
such as `nix develop` failing inside Git worktrees.

View File

@@ -1,61 +0,0 @@
# Troubleshooting Nix problems
Common issues encountered when using the [Nix development shell](./nix.md), and
how to resolve them.
## Git worktrees
If `nix develop` fails with an error like:
```
error:
… while fetching the input 'git+file:///path/to/rippled'
error: opening Git repository "/path/to/rippled": unsupported extension name extensions.relativeworktrees (libgit2 error code = 6)
```
then your Nix is linked against a libgit2 older than **1.9.4**. Git 2.48+ writes
the `extensions.relativeWorktrees` config entry when a worktree is created with
relative paths (`git worktree add --relative-paths`, or with
`worktree.useRelativePaths=true`), and older libgit2 versions refuse to open a
repository that uses it. Nix uses libgit2 to read the flake, so evaluation
fails.
> [!IMPORTANT]
> This entry is written to the **shared** repository config, so once any
> relative worktree exists, `nix develop` fails in the main checkout too — not
> just inside the worktree.
### Workarounds
These work today, with any Nix version:
- bypass libgit2 with a `path:` flakeref: `nix develop "path:$PWD"`
(note: this copies the working tree to the store and ignores `.gitignore`); or
- create worktrees with absolute paths (omit `--relative-paths`); or
- clear the extension if you don't need relative worktrees:
`git config --unset extensions.relativeWorktrees`.
### Permanent fix
The fix is in [libgit2 1.9.4](https://github.com/libgit2/libgit2/releases/tag/v1.9.4),
so the real solution is a Nix that links against libgit2 `1.9.4` or newer. Check
which version yours links against:
```bash
nix-store -qR "$(readlink -f "$(command -v nix)")" | grep libgit2
```
> [!WARNING]
> `nix upgrade-nix` does **not** help yet. It installs the build from the
> official [`nix-fallback-paths`](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/installer/tools/nix-fallback-paths.nix),
> which is still linked against libgit2 `1.9.2` — there is no new upstream Nix
> release with the fix. (On some systems that build is even the exact store path
> you already have, making the upgrade a no-op.)
nixpkgs has already rebuilt Nix against the fixed libgit2 (e.g. `nix-2.34.7+1`),
so the cleanest path is to reinstall Nix using your usual installation method
once it picks up that rebuild, then re-run the `grep libgit2` check above to
confirm it reports `1.9.4` or newer.
Until then, prefer the workarounds above.

View File

@@ -0,0 +1,29 @@
#pragma once
#if XRPL_ROCKSDB_AVAILABLE
// #include <rocksdb2/port/port_posix.h>
#include <rocksdb/cache.h>
#include <rocksdb/compaction_filter.h>
#include <rocksdb/comparator.h>
#include <rocksdb/convenience.h>
#include <rocksdb/db.h>
#include <rocksdb/env.h>
#include <rocksdb/filter_policy.h>
#include <rocksdb/flush_block_policy.h>
#include <rocksdb/iterator.h>
#include <rocksdb/memtablerep.h>
#include <rocksdb/merge_operator.h>
#include <rocksdb/options.h>
#include <rocksdb/perf_context.h>
#include <rocksdb/slice.h>
#include <rocksdb/slice_transform.h>
#include <rocksdb/statistics.h>
#include <rocksdb/status.h>
#include <rocksdb/table.h>
#include <rocksdb/table_properties.h>
#include <rocksdb/transaction_log.h>
#include <rocksdb/types.h>
#include <rocksdb/universal_compaction.h>
#include <rocksdb/write_batch.h>
#endif

View File

@@ -4,7 +4,7 @@
/*
ASAN flags some false positives with sudden jumps in control flow, like
exceptions, or when encountering coroutine stack switches. This macro can be used to disable ASAN
instrumentation for specific functions.
intrumentation for specific functions.
*/
#if defined(__GNUC__) || defined(__clang__)
#define XRPL_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address", "hwaddress")))

View File

@@ -0,0 +1,49 @@
#pragma once
#include <xrpl/protocol/MultiApiJson.h>
#include <xrpl/server/InfoSub.h>
#include <memory>
#include <mutex>
namespace xrpl {
/** Listen to public/subscribe messages from a book. */
class BookListeners
{
public:
using pointer = std::shared_ptr<BookListeners>;
BookListeners() = default;
/** Add a new subscription for this book
*/
void
addSubscriber(InfoSub::ref sub);
/** Stop publishing to a subscriber
*/
void
removeSubscriber(std::uint64_t sub);
/** Publish a transaction to subscribers
Publish a transaction to clients subscribed to changes on this book.
Uses havePublished to prevent sending duplicate transactions to clients
that have subscribed to multiple books.
@param jvObj JSON transaction data to publish
@param havePublished InfoSub sequence numbers that have already
published this transaction.
*/
void
publish(MultiApiJson const& jvObj, hash_set<std::uint64_t>& havePublished);
private:
std::recursive_mutex lock_;
hash_map<std::uint64_t, InfoSub::wptr> listeners_;
};
} // namespace xrpl

View File

@@ -1,11 +1,11 @@
#pragma once
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/AcceptedLedgerTx.h>
#include <xrpl/ledger/BookListeners.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Book.h>
#include <xrpl/protocol/MultiApiJson.h>
#include <xrpl/protocol/UintTypes.h>
#include <memory>
@@ -77,24 +77,34 @@ public:
*/
virtual bool
isBookToXRP(Asset const& asset, std::optional<Domain> const& domain = std::nullopt) = 0;
/**
* Process a transaction for order book tracking.
* @param ledger The ledger the transaction was applied to
* @param alTx The transaction to process
* @param jvObj The JSON object of the transaction
*/
virtual void
processTxn(
std::shared_ptr<ReadView const> const& ledger,
AcceptedLedgerTx const& alTx,
MultiApiJson const& jvObj) = 0;
/**
* Get the book listeners for a book.
* @param book The book to get the listeners for
* @return The book listeners for the book
*/
virtual BookListeners::pointer
getBookListeners(Book const&) = 0;
/**
* Create a new book listeners for a book.
* @param book The book to create the listeners for
* @return The new book listeners for the book
*/
virtual BookListeners::pointer
makeBookListeners(Book const&) = 0;
};
/** Extract the set of books affected by a transaction.
*
* Walks the transaction's metadata nodes and collects every order book
* whose offers were created, modified, or deleted. Used by NetworkOPs to
* fan transaction notifications out to book subscribers.
*
* @param alTx The accepted ledger transaction to inspect.
* @param j Journal used to log per-node parsing failures. Inspecting an
* offer node can throw if a required field is missing; in that
* case the bad node is skipped and a warn-level message is
* emitted via @p j. Other affected books in the same transaction
* are still returned.
* @return The set of books whose offers were created, modified, or
* deleted. May be empty for non-offer transactions.
*/
hash_set<Book>
affectedBooks(AcceptedLedgerTx const& alTx, beast::Journal const& j);
} // namespace xrpl

View File

@@ -13,57 +13,164 @@
namespace xrpl {
namespace credentials {
// These function will be used by the code that use DepositPreauth / Credentials
// (and any future pre-authorization modes) as part of authorization (all the
// transfer funds transactions)
// Check if credential sfExpiration field has passed ledger's parentCloseTime
/**
* @brief Check whether a credential has expired.
*
* @param sleCredential Credential ledger entry to inspect.
* @param closed Parent close time to compare against sfExpiration.
* @return true if sfExpiration is before closed; otherwise false.
*/
bool
checkExpired(SLE const& sleCredential, NetClock::time_point const& closed);
// Actually remove a credentials object from the ledger
/**
* @brief Remove a Credential ledger object from the view.
*
* @param view Mutable ledger view.
* @param sleCredential Credential ledger entry to delete.
* @param j Journal for diagnostics.
* @return tesSUCCESS on success, or an error code if ledger cleanup fails.
*/
[[nodiscard]] TER
deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j);
// Amendment and parameters checks for sfCredentialIDs field
/**
* @brief Check amendment and parameter rules for sfCredentialIDs.
*
* @param tx Transaction to validate.
* @param j Journal for diagnostics.
* @return tesSUCCESS if sfCredentialIDs is absent or well-formed; otherwise a
* malformed transaction code.
*/
NotTEC
checkFields(STTx const& tx, beast::Journal j);
// Accessing the ledger to check if provided credentials are valid. Do not use
// in doApply (only in preclaim) since it does not remove expired credentials.
// If you call it in preclaim, you also must call verifyDepositPreauth in
// doApply
/**
* @brief Check whether the transaction's provided credentials are valid.
*
* Use this only from preclaim because it does not remove expired credentials.
* If this is called from preclaim, verifyDepositPreauth() must also be called
* from doApply().
*
* @param tx Transaction containing optional sfCredentialIDs.
* @param view Read-only ledger view.
* @param src Source account that must own the credentials.
* @param j Journal for diagnostics.
* @return tesSUCCESS if credentials are valid, otherwise a credential error.
*/
TER
valid(STTx const& tx, ReadView const& view, AccountID const& src, beast::Journal j);
// Check if subject has any credential matching the given domain. If you call it
// in preclaim and it returns tecEXPIRED, you should call verifyValidDomain in
// doApply. This will ensure that expired credentials are deleted.
/**
* @brief Check whether an account has an accepted credential for a domain.
*
* If this is called from preclaim and returns tecEXPIRED, verifyValidDomain()
* should be called from doApply() so expired credentials are deleted.
*
* @param view Read-only ledger view.
* @param domainID PermissionedDomain ledger entry ID.
* @param subject Account whose credentials are checked.
* @return tesSUCCESS if a matching accepted credential exists, tecEXPIRED if
* only expired credentials match, or an authorization/object error.
*/
TER
validDomain(ReadView const& view, uint256 domainID, AccountID const& subject);
// This function is only called when we are about to return tecNO_PERMISSION
// because all the checks for the DepositPreauth authorization failed.
/**
* @brief Check DepositPreauth authorization using credential IDs.
*
* @param view Read-only ledger view.
* @param ctx Credential IDs supplied by the transaction.
* @param dst Destination account requiring deposit authorization.
* @return tesSUCCESS if credentials match a DepositPreauth object, otherwise
* an authorization or internal error.
*/
TER
authorizedDepositPreauth(ReadView const& view, STVector256 const& ctx, AccountID const& dst);
// Sort credentials array, return empty set if there are duplicates
/**
* @brief Sort credential issuer/type pairs.
*
* @param credentials Credential descriptors from a transaction.
* @return Sorted issuer/type pairs, or an empty set if duplicates exist.
*/
std::set<std::pair<AccountID, Slice>>
makeSorted(STArray const& credentials);
// Check credentials array passed to DepositPreauth/PermissionedDomainSet
// transactions
/**
* @brief Check a credentials array for DepositPreauth or PermissionedDomainSet.
*
* @param credentials Credential descriptors to validate.
* @param maxSize Maximum allowed array size.
* @param j Journal for diagnostics.
* @return tesSUCCESS if the array is well-formed, otherwise a malformed
* transaction code.
*/
NotTEC
checkArray(STArray const& credentials, unsigned maxSize, beast::Journal j);
} // namespace credentials
// Check expired credentials and for credentials matching DomainID of the ledger
// object
/**
* @brief Verify an account has a credential for a domain and delete expired
* credentials encountered while checking.
*
* @param view Mutable ledger view.
* @param account Account whose credentials are checked.
* @param domainID PermissionedDomain ledger entry ID.
* @param j Journal for diagnostics.
* @return tesSUCCESS if a matching accepted credential exists, tecEXPIRED if
* expired credentials were removed but no valid credential remains, or
* an authorization/object error.
*/
TER
verifyValidDomain(ApplyView& view, AccountID const& account, uint256 domainID, beast::Journal j);
// Check expired credentials and for existing DepositPreauth ledger object
/**
* @brief Check whether src is authorized to deposit to dst.
*
* @param tx Transaction containing optional credential IDs.
* @param view Read-only ledger view.
* @param src Source account.
* @param dst Destination account.
* @param sleDst Destination AccountRoot, if it exists.
* @param j Journal for diagnostics.
* @return tesSUCCESS if the deposit is allowed, otherwise an authorization
* error.
*/
TER
checkDepositPreauth(
STTx const& tx,
ReadView const& view,
AccountID const& src,
AccountID const& dst,
std::shared_ptr<SLE const> const& sleDst,
beast::Journal j);
/**
* @brief Remove expired credentials referenced by the transaction.
*
* @param tx Transaction containing optional sfCredentialIDs.
* @param view Mutable ledger view.
* @param j Journal for diagnostics.
* @return tesSUCCESS if no referenced credentials expired, tecEXPIRED if any
* were removed, or an error from credential deletion.
*/
TER
cleanupExpiredCredentials(STTx const& tx, ApplyView& view, beast::Journal j);
/**
* @brief Remove expired credentials, then check DepositPreauth authorization.
*
* @param tx Transaction containing optional credential IDs.
* @param view Mutable ledger view.
* @param src Source account.
* @param dst Destination account.
* @param sleDst Destination AccountRoot, if it exists.
* @param j Journal for diagnostics.
* @return tesSUCCESS if the deposit is authorized, otherwise an expiration,
* authorization, or deletion error.
*/
TER
verifyDepositPreauth(
STTx const& tx,

View File

@@ -23,9 +23,13 @@ checkTxPermission(SLE::const_ref delegate, STTx const& tx);
* @param delegate The delegate account.
* @param type Used to determine which granted granular permissions to load,
* based on the transaction type.
* @return the granted granular permissions tied to the transaction type.
* @param granularPermissions Granted granular permissions tied to the
* transaction type.
*/
std::unordered_set<GranularPermissionType>
getGranularPermission(SLE::const_ref delegate, TxType const& type);
void
loadGranularPermission(
SLE::const_ref delegate,
TxType const& type,
std::unordered_set<GranularPermissionType>& granularPermissions);
} // namespace xrpl

View File

@@ -5,45 +5,9 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>
#include <cstdint>
#include <memory>
#include <optional>
namespace xrpl {
/** Close a payment channel and return its remaining funds to the channel owner.
*
* @param slep The SLE for the PayChannel object to close.
* @param view The apply view in which ledger state modifications are made.
* @param key The ledger key identifying the PayChannel entry.
* @param j Journal used for fatal-level diagnostic messages.
* @return tesSUCCESS on success; tefBAD_LEDGER if a directory removal
* fails; tefINTERNAL if the source account SLE cannot be found.
*/
TER
closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal j);
/** Add two uint32_t values with saturation at UINT32_MAX.
*
* @param rules The current ledger rules used to check amendment status.
* @param lhs Left-hand operand.
* @param rhs Right-hand operand.
* @return @p lhs + @p rhs, saturated at UINT32_MAX when the amendment
* is active.
*/
uint32_t
saturatingAdd(Rules const& rules, uint32_t const lhs, uint32_t const rhs);
/** Determine whether a payment channel time field represents an expired time.
*
* @param view The apply view providing the parent close time and rules.
* @param timeField The optional expiry timestamp (seconds since the XRP
* Ledger epoch). If empty, the function returns false.
* @return @c true if @p timeField is set and the indicated time is
* in the past relative to the view's parent close time;
* @c false otherwise.
*/
bool
isChannelExpired(ApplyView const& view, std::optional<std::uint32_t> timeField);
} // namespace xrpl

View File

@@ -102,32 +102,25 @@ getAPIVersionNumber(json::Value const& jv, bool betaEnabled)
json::Value const maxVersion(
betaEnabled ? RPC::kApiBetaVersion : RPC::kApiMaximumSupportedVersion);
if (!jv.isObject() || !jv.isMember(jss::api_version))
return RPC::kApiVersionIfUnspecified;
try
if (jv.isObject())
{
auto const& rawVersion = jv[jss::api_version];
switch (rawVersion.type())
if (jv.isMember(jss::api_version))
{
case json::ValueType::Int:
if (rawVersion.asInt() < 0)
return RPC::kApiInvalidVersion;
[[fallthrough]];
case json::ValueType::UInt: {
auto const apiVersion = rawVersion.asUInt();
if (apiVersion < kMinVersion || apiVersion > maxVersion)
return RPC::kApiInvalidVersion;
return apiVersion;
}
default:
auto const specifiedVersion = jv[jss::api_version];
if (!specifiedVersion.isInt() && !specifiedVersion.isUInt())
{
return RPC::kApiInvalidVersion;
}
auto const specifiedVersionInt = specifiedVersion.asInt();
if (specifiedVersionInt < kMinVersion || specifiedVersionInt > maxVersion)
{
return RPC::kApiInvalidVersion;
}
return specifiedVersionInt;
}
}
catch (...)
{
return RPC::kApiInvalidVersion;
}
return RPC::kApiVersionIfUnspecified;
}
} // namespace RPC

View File

@@ -0,0 +1,417 @@
#pragma once
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/detail/secp256k1.h>
#include <secp256k1_mpt.h>
namespace xrpl {
/**
* @brief Bundles an ElGamal public key with its associated encrypted amount.
*
* Used to represent a recipient in confidential transfers, containing both
* the recipient's ElGamal public key and the ciphertext encrypting the
* transfer amount under that key.
*/
struct ConfidentialRecipient
{
/** @brief The recipient's ElGamal public key (size=xrpl::ecPubKeyLength). */
Slice publicKey;
/**
* @brief The encrypted amount ciphertext
* (size=xrpl::ecGamalEncryptedTotalLength).
*/
Slice encryptedAmount;
};
/**
* @brief Holds two secp256k1 public key components representing an ElGamal
* ciphertext (C1, C2).
*/
struct EcPair
{
/** @brief First ElGamal ciphertext component. */
secp256k1_pubkey c1;
/** @brief Second ElGamal ciphertext component. */
secp256k1_pubkey c2;
};
/**
* @brief Increments the confidential balance version counter on an MPToken.
*
* The version counter is used to prevent replay attacks by binding proofs
* to a specific state of the account's confidential balance. Wraps to 0
* on overflow (defined behavior for unsigned integers).
*
* @param mptoken The MPToken ledger entry to update.
*/
inline void
incrementConfidentialVersion(STObject& mptoken)
{
// Retrieve current version and increment.
// Unsigned integer overflow is defined behavior in C++ (wraps to 0),
// which is acceptable here.
mptoken[sfConfidentialBalanceVersion] = mptoken[~sfConfidentialBalanceVersion].valueOr(0u) + 1u;
}
/**
* @brief Generates the context hash for ConfidentialMPTSend transactions.
*
* Creates a unique 256-bit hash that binds the zero-knowledge proofs to
* this specific send transaction, preventing proof reuse across transactions.
*
* @param account The sender's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or ticket number.
* @param destination The destination account ID.
* @param version The sender's confidential balance version.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getSendContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& destination,
std::uint32_t version);
/**
* @brief Generates the context hash for ConfidentialMPTClawback transactions.
*
* Creates a unique 256-bit hash that binds the equality proof to this
* specific clawback transaction.
*
* @param account The issuer's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or ticket number.
* @param holder The holder's account ID being clawed back from.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getClawbackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& holder);
/**
* @brief Generates the context hash for ConfidentialMPTConvert transactions.
*
* Creates a unique 256-bit hash that binds the Schnorr proof (for key
* registration) to this specific convert transaction.
*
* @param account The holder's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or a ticket number.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence);
/**
* @brief Generates the context hash for ConfidentialMPTConvertBack transactions.
*
* Creates a unique 256-bit hash that binds the zero-knowledge proofs to
* this specific convert-back transaction.
*
* @param account The holder's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or a ticket number.
* @param version The holder's confidential balance version.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getConvertBackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
std::uint32_t version);
/**
* @brief Parses an ElGamal ciphertext into two secp256k1 public key components.
*
* Breaks a 66-byte encrypted amount (two 33-byte compressed EC points) into
* a pair containing (C1, C2) for use in cryptographic operations.
*
* @param buffer The 66-byte buffer containing the compressed ciphertext.
* @return The parsed pair (c1, c2) if successful, std::nullopt if the buffer is invalid.
*/
std::optional<EcPair>
makeEcPair(Slice const& buffer);
/**
* @brief Serializes an EcPair into compressed form.
*
* Converts an EcPair (C1, C2) back into a 66-byte buffer containing
* two 33-byte compressed EC points.
*
* @param pair The EcPair to serialize.
* @return The 66-byte buffer, or std::nullopt if serialization fails.
*/
std::optional<Buffer>
serializeEcPair(EcPair const& pair);
/**
* @brief Verifies that a buffer contains two valid, parsable EC public keys.
*
* @param buffer The input buffer containing two concatenated components.
* @return true if both components can be parsed successfully, false otherwise.
*/
bool
isValidCiphertext(Slice const& buffer);
/**
* @brief Verifies that a buffer contains a valid, parsable compressed EC point.
*
* Can be used to validate both compressed public keys and Pedersen commitments.
* Fails early if the prefix byte is not 0x02 or 0x03.
*
* @param buffer The input buffer containing a compressed EC point (33 bytes).
* @return true if the point can be parsed successfully, false otherwise.
*/
bool
isValidCompressedECPoint(Slice const& buffer);
/**
* @brief Homomorphically adds two ElGamal ciphertexts.
*
* Uses the additive homomorphic property of ElGamal encryption to compute
* Enc(a + b) from Enc(a) and Enc(b) without decryption.
*
* @param a The first ciphertext (66 bytes).
* @param b The second ciphertext (66 bytes).
* @return The resulting ciphertext Enc(a + b), or std::nullopt on failure.
*/
std::optional<Buffer>
homomorphicAdd(Slice const& a, Slice const& b);
/**
* @brief Homomorphically subtracts two ElGamal ciphertexts.
*
* Uses the additive homomorphic property of ElGamal encryption to compute
* Enc(a - b) from Enc(a) and Enc(b) without decryption.
*
* @param a The minuend ciphertext (66 bytes).
* @param b The subtrahend ciphertext (66 bytes).
* @return The resulting ciphertext Enc(a - b), or std::nullopt on failure.
*/
std::optional<Buffer>
homomorphicSubtract(Slice const& a, Slice const& b);
/**
* @brief Re-randomizes an ElGamal ciphertext without changing its plaintext.
*
* Adds Enc(0; randomness) under the supplied public key to the ciphertext.
* This is used when a public, deterministic scalar must perturb ciphertext
* randomness while preserving ledger reproducibility.
*
* @param ciphertext The ciphertext to re-randomize (66 bytes).
* @param pubKeySlice The ElGamal public key matching the ciphertext recipient.
* @param randomness The scalar used as zero-encryption randomness (32 bytes).
* @return The re-randomized ciphertext, or std::nullopt on failure.
*/
std::optional<Buffer>
rerandomizeCiphertext(Slice const& ciphertext, Slice const& pubKeySlice, Slice const& randomness);
/**
* @brief Encrypts an amount using ElGamal encryption.
*
* Produces a ciphertext C = (C1, C2) where C1 = r*G and C2 = m*G + r*Pk,
* using the provided blinding factor r.
*
* @param amt The plaintext amount to encrypt.
* @param pubKeySlice The recipient's ElGamal public key (size=xrpl::ecPubKeyLength).
* @param blindingFactor The randomness used as blinding factor r
* (size=xrpl::ecBlindingFactorLength).
* @return The ciphertext (size=xrpl::ecGamalEncryptedTotalLength), or std::nullopt on failure.
*/
std::optional<Buffer>
encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor);
/**
* @brief Generates the canonical zero encryption for a specific MPToken.
*
* Creates a deterministic encryption of zero that is unique to the account
* and MPT issuance. Used to initialize confidential balance fields.
*
* @param pubKeySlice The holder's ElGamal public key (size=xrpl::ecPubKeyLength).
* @param account The account ID of the token holder.
* @param mptId The MPToken Issuance ID.
* @return The canonical zero ciphertext (size=xrpl::ecGamalEncryptedTotalLength), or std::nullopt
* on failure.
*/
std::optional<Buffer>
encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId);
/**
* @brief Verifies a Schnorr proof of knowledge of an ElGamal private key.
*
* Proves that the submitter knows the secret key corresponding to the
* provided public key, without revealing the secret key itself.
*
* @param pubKeySlice The ElGamal public key (size=xrpl::ecPubKeyLength).
* @param proofSlice The Schnorr proof (size=xrpl::ecSchnorrProofLength).
* @param contextHash The 256-bit context hash binding the proof.
* @return tesSUCCESS if valid, or an error code otherwise.
*/
TER
verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash);
/**
* @brief Validates the format of encrypted amount fields in a transaction.
*
* Checks that all ciphertext fields in the transaction object have the
* correct length and contain valid EC points. This function is only used
* by ConfidentialMPTConvert and ConfidentialMPTConvertBack transactions.
*
* @param object The transaction object containing encrypted amount fields.
* @return tesSUCCESS if all formats are valid, temMALFORMED if required fields
* are missing, or temBAD_CIPHERTEXT if format validation fails.
*/
NotTEC
checkEncryptedAmountFormat(STObject const& object);
/**
* @brief Verifies revealed amount encryptions for all recipients.
*
* Validates that the same amount was correctly encrypted for the holder,
* issuer, and optionally the auditor using their respective public keys.
*
* @param amount The revealed plaintext amount.
* @param blindingFactor The blinding factor used in all encryptions
* (size=xrpl::ecBlindingFactorLength).
* @param holder The holder's public key and encrypted amount.
* @param issuer The issuer's public key and encrypted amount.
* @param auditor Optional auditor's public key and encrypted amount.
* @return tesSUCCESS if all encryptions are valid, or an error code otherwise.
*/
TER
verifyRevealedAmount(
uint64_t const amount,
Slice const& blindingFactor,
ConfidentialRecipient const& holder,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor);
/**
* @brief Returns the number of recipients in a confidential transfer.
*
* Returns 4 if an auditor is present (sender, destination, issuer, auditor),
* or 3 if no auditor (sender, destination, issuer).
*
* @param hasAuditor Whether the issuance has an auditor configured.
* @return The number of recipients (3 or 4).
*/
constexpr uint8_t
getConfidentialRecipientCount(bool hasAuditor)
{
return hasAuditor ? 4 : 3;
}
/**
* @brief Verifies a compact sigma clawback proof.
*
* Proves that the issuer knows the exact amount encrypted in the holder's
* balance ciphertext. Used in ConfidentialMPTClawback to verify the issuer
* can decrypt the balance using their private key.
*
* @param amount The revealed plaintext amount.
* @param proof The zero-knowledge proof bytes (ecClawbackProofLength).
* @param pubKeySlice The issuer's ElGamal public key (ecPubKeyLength bytes).
* @param ciphertext The issuer's encrypted balance on the holder's account
* (ecGamalEncryptedTotalLength bytes).
* @param contextHash The 256-bit context hash binding the proof.
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
*/
TER
verifyClawbackProof(
uint64_t const amount,
Slice const& proof,
Slice const& pubKeySlice,
Slice const& ciphertext,
uint256 const& contextHash);
/**
* @brief Generates a cryptographically secure 32-byte blinding factor.
*
* Produces random bytes suitable for use as an ElGamal blinding factor
* or Pedersen commitment randomness.
*
* @return A 32-byte buffer containing the random blinding factor.
*/
Buffer
generateBlindingFactor();
/**
* @brief Verifies all zero-knowledge proofs for a ConfidentialMPTSend transaction.
*
* This function calls mpt_verify_send_proof API in the mpt-crypto utility lib, which verifies the
* equality proof, amount linkage, balance linkage, and range proof.
* Equality proof: Proves the same value is encrypted for the sender, receiver, issuer, and auditor.
* Amount linkage: Proves the send amount matches the amount Pedersen commitment.
* Balance linkage: Proves the sender's balance matches the balance Pedersen
* commitment.
* Range proof: Proves the amount and the remaining balance are within range [0, 2^64-1].
*
* @param proof The full proof blob.
* @param sender The sender's public key and encrypted amount.
* @param destination The destination's public key and encrypted amount.
* @param issuer The issuer's public key and encrypted amount.
* @param auditor The auditor's public key and encrypted amount if present.
* @param spendingBalance The sender's current spending balance ciphertext.
* @param amountCommitment The Pedersen commitment to the send amount.
* @param balanceCommitment The Pedersen commitment to the sender's balance.
* @param contextHash The context hash binding the proof.
* @return tesSUCCESS if all proofs are valid, or an error code otherwise.
*/
TER
verifySendProof(
Slice const& proof,
ConfidentialRecipient const& sender,
ConfidentialRecipient const& destination,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor,
Slice const& spendingBalance,
Slice const& amountCommitment,
Slice const& balanceCommitment,
uint256 const& contextHash);
/**
* @brief Verifies all zero-knowledge proofs for a ConfidentialMPTConvertBack transaction.
*
* This function calls mpt_verify_convert_back_proof API in the mpt-crypto utility lib, which
* verifies the balance linkage proof and range proof. Balance linkage proof: proves the balance
* commitment matches the spending ciphertext. Range proof: proves the remaining balance after
* convert back is within range [0, 2^64-1].
*
* @param proof The full proof blob.
* @param pubKeySlice The holder's public key.
* @param spendingBalance The holder's spending balance ciphertext.
* @param balanceCommitment The Pedersen commitment to the balance.
* @param amount The amount being converted back to public.
* @param contextHash The context hash binding the proof.
* @return tesSUCCESS if all proofs are valid, or an error code otherwise.
*/
TER
verifyConvertBackProof(
Slice const& proof,
Slice const& pubKeySlice,
Slice const& spendingBalance,
Slice const& balanceCommitment,
uint64_t amount,
uint256 const& contextHash);
} // namespace xrpl

View File

@@ -177,7 +177,8 @@ enum LedgerEntryType : std::uint16_t {
LSF_FLAG(lsfMPTCanEscrow, 0x00000008) \
LSF_FLAG(lsfMPTCanTrade, 0x00000010) \
LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \
LSF_FLAG(lsfMPTCanClawback, 0x00000040)) \
LSF_FLAG(lsfMPTCanClawback, 0x00000040) \
LSF_FLAG(lsfMPTCanConfidentialAmount, 0x00000080)) \
\
LEDGER_OBJECT(MPTokenIssuanceMutable, \
LSF_FLAG(lsmfMPTCanMutateCanLock, 0x00000002) \
@@ -187,7 +188,8 @@ enum LedgerEntryType : std::uint16_t {
LSF_FLAG(lsmfMPTCanMutateCanTransfer, 0x00000020) \
LSF_FLAG(lsmfMPTCanMutateCanClawback, 0x00000040) \
LSF_FLAG(lsmfMPTCanMutateMetadata, 0x00010000) \
LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \
LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000) \
LSF_FLAG(lsmfMPTCannotMutateCanConfidentialAmount, 0x00040000)) \
\
LEDGER_OBJECT(MPToken, \
LSF_FLAG2(lsfMPTLocked, 0x00000001) \

View File

@@ -7,13 +7,8 @@
#include <optional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace xrpl {
class STTx;
/**
* We have both transaction type permissions and granular type permissions.
* Since we will reuse the TransactionFormats to parse the Transaction
@@ -24,15 +19,15 @@ class STTx;
// Macro-generated, complex
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum GranularPermissionType : std::uint32_t {
#pragma push_macro("GRANULAR_PERMISSION")
#undef GRANULAR_PERMISSION
#pragma push_macro("PERMISSION")
#undef PERMISSION
#define GRANULAR_PERMISSION(name, txType, value, ...) name = (value),
#define PERMISSION(type, txType, value) type = (value),
#include <xrpl/protocol/detail/permissions.macro>
#undef GRANULAR_PERMISSION
#pragma pop_macro("GRANULAR_PERMISSION")
#undef PERMISSION
#pragma pop_macro("PERMISSION")
};
// Injected bare enumerators (xrpl::delegable / xrpl::notDelegable) are required by preprocessor
@@ -45,30 +40,15 @@ class Permission
private:
Permission();
struct GranularPermissionEntry
{
std::string name;
TxType txType;
std::uint32_t permittedFlags;
SOTemplate permittedFields;
std::unordered_map<std::uint16_t, uint256> txFeatureMap_;
GranularPermissionEntry(
std::string name,
TxType txType,
std::uint32_t permittedFlags,
std::vector<SOElement> fields);
};
std::unordered_map<std::uint16_t, Delegation> delegableTx_;
struct TxDelegationEntry
{
uint256 amendment;
Delegation delegable{NotDelegable};
};
std::unordered_map<std::string, GranularPermissionType> granularPermissionMap_;
std::unordered_set<TxType> granularTxTypes_;
std::unordered_map<TxType, TxDelegationEntry> txDelegationMap_;
std::unordered_map<std::string, GranularPermissionType> granularPermissionsByName_;
std::unordered_map<GranularPermissionType, GranularPermissionEntry> granularPermissions_;
std::unordered_map<GranularPermissionType, std::string> granularNameMap_;
std::unordered_map<GranularPermissionType, TxType> granularTxTypeMap_;
public:
static Permission const&
@@ -79,52 +59,30 @@ public:
operator=(Permission const&) = delete;
[[nodiscard]] std::optional<std::string>
getPermissionName(std::uint32_t value) const;
getPermissionName(std::uint32_t const value) const;
[[nodiscard]] std::optional<std::uint32_t>
getGranularValue(std::string const& name) const;
[[nodiscard]] std::optional<std::string>
getGranularName(GranularPermissionType value) const;
getGranularName(GranularPermissionType const& value) const;
[[nodiscard]] std::optional<TxType>
getGranularTxType(GranularPermissionType gpType) const;
getGranularTxType(GranularPermissionType const& gpType) const;
// Returns a reference to avoid copying uint256 - 32 bytes. std::optional
// cannot hold references directly, so std::reference_wrapper is used.
[[nodiscard]] std::optional<std::reference_wrapper<uint256 const>>
getTxFeature(TxType txType) const;
[[nodiscard]] bool
isDelegable(std::uint32_t permissionValue, Rules const& rules) const;
[[nodiscard]] bool
hasGranularPermissions(TxType txType) const;
isDelegable(std::uint32_t const& permissionValue, Rules const& rules) const;
// for tx level permission, permission value is equal to tx type plus one
[[nodiscard]] static uint32_t
txToPermissionType(TxType type);
static uint32_t
txToPermissionType(TxType const& type);
// tx type value is permission value minus one
[[nodiscard]] static TxType
permissionToTxType(std::uint32_t value);
/**
* @brief Verifies a delegated transaction against its granular permission template.
*
* @note WARNING: Do not move this check before standard transaction-level
* format checks, which is in preclaim. This function assumes the transaction's
* base structural integrity (fees, sequence, signatures) has already been
* validated.
*
* @param tx The transaction to verify.
* @param heldPermissions The granular permissions that the sender hold.
* @return true if the transaction fields and flags comply with the granular template.
*/
[[nodiscard]] bool
checkGranularSandbox(
STTx const& tx,
std::unordered_set<GranularPermissionType> const& heldPermissions) const;
static TxType
permissionToTxType(uint32_t const& value);
};
} // namespace xrpl

View File

@@ -307,4 +307,54 @@ constexpr std::size_t kPermissionMaxSize = 10;
/** The maximum number of transactions that can be in a batch. */
constexpr std::size_t kMaxBatchTxCount = 8;
/** Length of one component of EC ElGamal ciphertext */
constexpr std::size_t kEcGamalEncryptedLength = 33;
/** EC ElGamal ciphertext length: two 33-byte components concatenated */
constexpr std::size_t kEcGamalEncryptedTotalLength = kEcGamalEncryptedLength * 2;
/** Length of EC point (compressed) */
constexpr std::size_t kCompressedEcPointLength = 33;
/** Length of EC public key (compressed) */
constexpr std::size_t kEcPubKeyLength = kCompressedEcPointLength;
/** Length of EC private key in bytes */
constexpr std::size_t kEcPrivKeyLength = 32;
/** Length of the EC blinding factor in bytes */
constexpr std::size_t kEcBlindingFactorLength = 32;
/** Length of Schnorr ZKProof for public key registration (compact form) in bytes */
constexpr std::size_t kEcSchnorrProofLength = 64;
/** Length of Pedersen Commitment (compressed) */
constexpr std::size_t kEcPedersenCommitmentLength = kCompressedEcPointLength;
/** Length of single bulletproof (range proof for 1 commitment) in bytes */
constexpr std::size_t kEcSingleBulletproofLength = 688;
/** Length of double bulletproof (range proof for 2 commitments) in bytes */
constexpr std::size_t kEcDoubleBulletproofLength = 754;
/** Length of the ZKProof for ConfidentialMPTSend.
* 192 bytes compact sigma proof + 754 bytes double bulletproof. */
constexpr std::size_t kEcSendProofLength = 946;
/** Length of the ZKProof for ConfidentialMPTConvertBack.
* 128 bytes compact sigma proof + 688 bytes single bulletproof. */
constexpr std::size_t kEcConvertBackProofLength = 816;
/** Length of the ZKProof for ConfidentialMPTClawback. */
constexpr std::size_t kEcClawbackProofLength = 64;
/** Extra base fee multiplier charged to confidential MPT transactions. */
constexpr std::uint32_t kConfidentialFeeMultiplier = 9;
/** Compressed EC point prefix for even y-coordinate */
constexpr std::uint8_t kEcCompressedPrefixEvenY = 0x02;
/** Compressed EC point prefix for odd y-coordinate */
constexpr std::uint8_t kEcCompressedPrefixOddY = 0x03;
} // namespace xrpl

View File

@@ -128,6 +128,7 @@ enum TEMcodes : TERUnderlyingType {
temBAD_TRANSFER_FEE,
temINVALID_INNER_BATCH,
temBAD_MPT,
temBAD_CIPHERTEXT,
};
//------------------------------------------------------------------------------
@@ -358,6 +359,11 @@ enum TECcodes : TERUnderlyingType {
tecLIMIT_EXCEEDED = 195,
tecPSEUDO_ACCOUNT = 196,
tecPRECISION_LOSS = 197,
// DEPRECATED: This error code tecNO_DELEGATE_PERMISSION is reserved for
// backward compatibility with historical data on non-prod networks, can be
// reclaimed after those networks reset.
tecNO_DELEGATE_PERMISSION = 198,
tecBAD_PROOF = 199,
};
//------------------------------------------------------------------------------

View File

@@ -140,7 +140,8 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
TF_FLAG(tfMPTCanEscrow, lsfMPTCanEscrow) \
TF_FLAG(tfMPTCanTrade, lsfMPTCanTrade) \
TF_FLAG(tfMPTCanTransfer, lsfMPTCanTransfer) \
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback), \
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback) \
TF_FLAG(tfMPTCanConfidentialAmount, lsfMPTCanConfidentialAmount), \
MASK_ADJ(0)) \
\
TRANSACTION(MPTokenAuthorize, \
@@ -349,10 +350,12 @@ inline constexpr FlagValue tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTrans
inline constexpr FlagValue tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback;
inline constexpr FlagValue tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata;
inline constexpr FlagValue tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee;
inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask =
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow |
tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback |
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
inline constexpr FlagValue tmfMPTCannotMutateCanConfidentialAmount =
lsmfMPTCannotMutateCanConfidentialAmount;
inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask = ~(
tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow |
tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback |
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee | tmfMPTCannotMutateCanConfidentialAmount);
// MPTokenIssuanceSet MutableFlags:
// Set or Clear flags.
@@ -369,10 +372,13 @@ inline constexpr FlagValue tmfMPTSetCanTransfer = 0x00000100;
inline constexpr FlagValue tmfMPTClearCanTransfer = 0x00000200;
inline constexpr FlagValue tmfMPTSetCanClawback = 0x00000400;
inline constexpr FlagValue tmfMPTClearCanClawback = 0x00000800;
inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask = ~(
tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTSetRequireAuth | tmfMPTClearRequireAuth |
tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | tmfMPTSetCanTrade | tmfMPTClearCanTrade |
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanClawback | tmfMPTClearCanClawback);
inline constexpr FlagValue tmfMPTSetCanConfidentialAmount = 0x00001000;
inline constexpr FlagValue tmfMPTClearCanConfidentialAmount = 0x00002000;
inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask =
~(tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTSetRequireAuth | tmfMPTClearRequireAuth |
tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | tmfMPTSetCanTrade | tmfMPTClearCanTrade |
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanClawback |
tmfMPTClearCanClawback | tmfMPTSetCanConfidentialAmount | tmfMPTClearCanConfidentialAmount);
// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between accounts allowed a
// TrustLine to be added to the issuer of that token without explicit permission from that issuer.

View File

@@ -14,14 +14,14 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.
XRPL_FEATURE(ConfidentialTransfer, Supported::No, VoteBehavior::DefaultNo)
XRPL_FIX (Cleanup3_3_0, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (Cleanup3_2_0, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(MPTokensV2, Supported::No, VoteBehavior::DefaultNo)
XRPL_FIX (Cleanup3_1_3, Supported::Yes, VoteBehavior::DefaultYes)
XRPL_FIX (BatchInnerSigs, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(LendingProtocol, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionDelegationV1_1, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionDelegationV1_1, Supported::No, VoteBehavior::DefaultNo)
XRPL_FIX (DirectoryLimit, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (IncludeKeyletFields, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(DynamicMPT, Supported::No, VoteBehavior::DefaultNo)

View File

@@ -401,19 +401,28 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfDomainID, SoeOptional},
{sfMutableFlags, SoeDefault},
{sfReferenceHolding, SoeOptional},
{sfIssuerEncryptionKey, SoeOptional},
{sfAuditorEncryptionKey, SoeOptional},
{sfConfidentialOutstandingAmount, SoeDefault},
}))
/** A ledger object which tracks MPToken
\sa keylet::mptoken
*/
LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
{sfAccount, SoeRequired},
{sfMPTokenIssuanceID, SoeRequired},
{sfMPTAmount, SoeDefault},
{sfLockedAmount, SoeOptional},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfAccount, SoeRequired},
{sfMPTokenIssuanceID, SoeRequired},
{sfMPTAmount, SoeDefault},
{sfLockedAmount, SoeOptional},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfConfidentialBalanceInbox, SoeOptional},
{sfConfidentialBalanceSpending, SoeOptional},
{sfConfidentialBalanceVersion, SoeDefault},
{sfIssuerEncryptedBalance, SoeOptional},
{sfAuditorEncryptedBalance, SoeOptional},
{sfHolderEncryptionKey, SoeOptional},
}))
/** A ledger object which tracks Oracle

View File

@@ -1,74 +1,49 @@
#if !defined(GRANULAR_PERMISSION)
#error "undefined macro: GRANULAR_PERMISSION"
#if !defined(PERMISSION)
#error "undefined macro: PERMISSION"
#endif
/**
* GRANULAR_PERMISSION(name, txType, value, allowedFlags, allowedFields)
* PERMISSION(name, type, txType, value)
*
* Defines a granular permission:
* name: the granular permission name.
* txType: the corresponding TxType for this permission.
* value: the uint32 numeric value for the enum type.
* allowedFlags: transaction flags permitted under this permission.
* allowedFields: transaction fields permitted under this permission.
* This macro defines a permission:
* name: the name of the permission.
* type: the GranularPermissionType enum.
* txType: the corresponding TxType for this permission.
* value: the uint32 numeric value for the enum type.
*/
/** Grants the ability to authorize a trustline. */
GRANULAR_PERMISSION(TrustlineAuthorize, ttTRUST_SET, 65537, tfUniversal | tfSetfAuth,
({{sfLimitAmount, SoeRequired}}))
/** This permission grants the delegated account the ability to authorize a trustline. */
PERMISSION(TrustlineAuthorize, ttTRUST_SET, 65537)
/** Grants the ability to freeze a trustline. */
GRANULAR_PERMISSION(TrustlineFreeze, ttTRUST_SET, 65538, tfUniversal | tfSetFreeze,
({{sfLimitAmount, SoeRequired}}))
/** This permission grants the delegated account the ability to freeze a trustline. */
PERMISSION(TrustlineFreeze, ttTRUST_SET, 65538)
/** Grants the ability to unfreeze a trustline. */
GRANULAR_PERMISSION(TrustlineUnfreeze, ttTRUST_SET, 65539, tfUniversal | tfClearFreeze,
({{sfLimitAmount, SoeRequired}}))
/** This permission grants the delegated account the ability to unfreeze a trustline. */
PERMISSION(TrustlineUnfreeze, ttTRUST_SET, 65539)
/** Grants the ability to set Domain. */
GRANULAR_PERMISSION(AccountDomainSet, ttACCOUNT_SET, 65540, tfUniversal,
({{sfDomain, SoeOptional}}))
/** This permission grants the delegated account the ability to set Domain. */
PERMISSION(AccountDomainSet, ttACCOUNT_SET, 65540)
/** Grants the ability to set EmailHash. */
GRANULAR_PERMISSION(AccountEmailHashSet, ttACCOUNT_SET, 65541, tfUniversal,
({{sfEmailHash, SoeOptional}}))
/** This permission grants the delegated account the ability to set EmailHashSet. */
PERMISSION(AccountEmailHashSet, ttACCOUNT_SET, 65541)
/** Grants the ability to set MessageKey. */
GRANULAR_PERMISSION(AccountMessageKeySet, ttACCOUNT_SET, 65542, tfUniversal,
({{sfMessageKey, SoeOptional}}))
/** This permission grants the delegated account the ability to set MessageKey. */
PERMISSION(AccountMessageKeySet, ttACCOUNT_SET, 65542)
/** Grants the ability to set TransferRate. */
GRANULAR_PERMISSION(AccountTransferRateSet, ttACCOUNT_SET, 65543, tfUniversal,
({{sfTransferRate, SoeOptional}}))
/** This permission grants the delegated account the ability to set TransferRate. */
PERMISSION(AccountTransferRateSet, ttACCOUNT_SET, 65543)
/** Grants the ability to set TickSize. */
GRANULAR_PERMISSION(AccountTickSizeSet, ttACCOUNT_SET, 65544, tfUniversal,
({{sfTickSize, SoeOptional}}))
/** This permission grants the delegated account the ability to set TickSize. */
PERMISSION(AccountTickSizeSet, ttACCOUNT_SET, 65544)
/** Grants the ability to mint payment (sending account is the issuer). Cross-currency payments are disallowed. */
GRANULAR_PERMISSION(PaymentMint, ttPAYMENT, 65545, tfUniversal,
({{sfDestination, SoeRequired},
{sfAmount, SoeRequired},
{sfSendMax, SoeOptional},
{sfInvoiceID, SoeOptional},
{sfDestinationTag, SoeOptional},
{sfCredentialIDs, SoeOptional}}))
/** This permission grants the delegated account the ability to mint payment, which means sending a payment for a currency where the sending account is the issuer. */
PERMISSION(PaymentMint, ttPAYMENT, 65545)
/** Grants the ability to burn payment (destination account is the issuer). Cross-currency payments are disallowed. */
GRANULAR_PERMISSION(PaymentBurn, ttPAYMENT, 65546, tfUniversal,
({{sfDestination, SoeRequired},
{sfAmount, SoeRequired},
{sfSendMax, SoeOptional},
{sfInvoiceID, SoeOptional},
{sfDestinationTag, SoeOptional},
{sfCredentialIDs, SoeOptional}}))
/** This permission grants the delegated account the ability to burn payment, which means sending a payment for a currency where the destination account is the issuer */
PERMISSION(PaymentBurn, ttPAYMENT, 65546)
/** Grants the ability to lock an MPToken. */
GRANULAR_PERMISSION(MPTokenIssuanceLock, ttMPTOKEN_ISSUANCE_SET, 65547, tfUniversal | tfMPTLock,
({{sfMPTokenIssuanceID, SoeRequired},
{sfHolder, SoeOptional}}))
/** This permission grants the delegated account the ability to lock MPToken. */
PERMISSION(MPTokenIssuanceLock, ttMPTOKEN_ISSUANCE_SET, 65547)
/** Grants the ability to unlock an MPToken. */
GRANULAR_PERMISSION(MPTokenIssuanceUnlock, ttMPTOKEN_ISSUANCE_SET, 65548, tfUniversal | tfMPTUnlock,
({{sfMPTokenIssuanceID, SoeRequired},
{sfHolder, SoeOptional}}))
/** This permission grants the delegated account the ability to unlock MPToken. */
PERMISSION(MPTokenIssuanceUnlock, ttMPTOKEN_ISSUANCE_SET, 65548)

View File

@@ -11,7 +11,10 @@ secp256k1Context()
struct Holder
{
secp256k1_context* impl;
Holder() : impl(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
// SECP256K1_CONTEXT_SIGN and SECP256K1_CONTEXT_VERIFY were deprecated.
// All contexts support both signing and verification, so
// SECP256K1_CONTEXT_NONE is the correct flag to use.
Holder() : impl(secp256k1_context_create(SECP256K1_CONTEXT_NONE))
{
}

View File

@@ -113,6 +113,7 @@ TYPED_SFIELD(sfInterestRate, UINT32, 65) // 1/10 basis points (bi
TYPED_SFIELD(sfLateInterestRate, UINT32, 66) // 1/10 basis points (bips)
TYPED_SFIELD(sfCloseInterestRate, UINT32, 67) // 1/10 basis points (bips)
TYPED_SFIELD(sfOverpaymentInterestRate, UINT32, 68) // 1/10 basis points (bips)
TYPED_SFIELD(sfConfidentialBalanceVersion, UINT32, 69)
// 64-bit integers (common)
TYPED_SFIELD(sfIndexNext, UINT64, 1)
@@ -146,6 +147,7 @@ TYPED_SFIELD(sfSubjectNode, UINT64, 28)
TYPED_SFIELD(sfLockedAmount, UINT64, 29, SField::kSmdBaseTen|SField::kSmdDefault)
TYPED_SFIELD(sfVaultNode, UINT64, 30)
TYPED_SFIELD(sfLoanBrokerNode, UINT64, 31)
TYPED_SFIELD(sfConfidentialOutstandingAmount, UINT64, 32, SField::kSmdBaseTen|SField::kSmdDefault)
// 128-bit
TYPED_SFIELD(sfEmailHash, UINT128, 1)
@@ -205,7 +207,8 @@ TYPED_SFIELD(sfParentBatchID, UINT256, 36)
TYPED_SFIELD(sfLoanBrokerID, UINT256, 37,
SField::kSmdPseudoAccount | SField::kSmdDefault)
TYPED_SFIELD(sfLoanID, UINT256, 38)
TYPED_SFIELD(sfReferenceHolding, UINT256, 39)
TYPED_SFIELD(sfBlindingFactor, UINT256, 39)
TYPED_SFIELD(sfReferenceHolding, UINT256, 40)
// number (common)
TYPED_SFIELD(sfNumber, NUMBER, 1)
@@ -299,6 +302,21 @@ TYPED_SFIELD(sfAssetClass, VL, 28)
TYPED_SFIELD(sfProvider, VL, 29)
TYPED_SFIELD(sfMPTokenMetadata, VL, 30)
TYPED_SFIELD(sfCredentialType, VL, 31)
TYPED_SFIELD(sfConfidentialBalanceInbox, VL, 32)
TYPED_SFIELD(sfConfidentialBalanceSpending, VL, 33)
TYPED_SFIELD(sfIssuerEncryptedBalance, VL, 34)
TYPED_SFIELD(sfIssuerEncryptionKey, VL, 35)
TYPED_SFIELD(sfHolderEncryptionKey, VL, 36)
TYPED_SFIELD(sfZKProof, VL, 37)
TYPED_SFIELD(sfHolderEncryptedAmount, VL, 38)
TYPED_SFIELD(sfIssuerEncryptedAmount, VL, 39)
TYPED_SFIELD(sfSenderEncryptedAmount, VL, 40)
TYPED_SFIELD(sfDestinationEncryptedAmount, VL, 41)
TYPED_SFIELD(sfAuditorEncryptedBalance, VL, 42)
TYPED_SFIELD(sfAuditorEncryptedAmount, VL, 43)
TYPED_SFIELD(sfAuditorEncryptionKey, VL, 44)
TYPED_SFIELD(sfAmountCommitment, VL, 45)
TYPED_SFIELD(sfBalanceCommitment, VL, 46)
// account (common)
TYPED_SFIELD(sfAccount, ACCOUNT, 1)

View File

@@ -735,6 +735,8 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
{sfMPTokenMetadata, SoeOptional},
{sfTransferFee, SoeOptional},
{sfMutableFlags, SoeOptional},
{sfIssuerEncryptionKey, SoeOptional},
{sfAuditorEncryptionKey, SoeOptional},
}))
/** This transaction type authorizes a MPToken instance */
@@ -1077,6 +1079,91 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay,
{sfAmount, SoeRequired, SoeMptSupported},
}))
/** This transaction type converts into confidential MPT balance. */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTConvert.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT, 85, ConfidentialMPTConvert,
Delegation::Delegable,
featureConfidentialTransfer,
NoPriv,
({
{sfMPTokenIssuanceID, SoeRequired},
{sfMPTAmount, SoeRequired},
{sfHolderEncryptionKey, SoeOptional},
{sfHolderEncryptedAmount, SoeRequired},
{sfIssuerEncryptedAmount, SoeRequired},
{sfAuditorEncryptedAmount, SoeOptional},
{sfBlindingFactor, SoeRequired},
{sfZKProof, SoeOptional},
}))
/** This transaction type merges MPT inbox. */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTMergeInbox.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_MERGE_INBOX, 86, ConfidentialMPTMergeInbox,
Delegation::Delegable,
featureConfidentialTransfer,
NoPriv,
({
{sfMPTokenIssuanceID, SoeRequired},
}))
/** This transaction type converts back into public MPT balance. */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTConvertBack.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT_BACK, 87, ConfidentialMPTConvertBack,
Delegation::Delegable,
featureConfidentialTransfer,
NoPriv,
({
{sfMPTokenIssuanceID, SoeRequired},
{sfMPTAmount, SoeRequired},
{sfHolderEncryptedAmount, SoeRequired},
{sfIssuerEncryptedAmount, SoeRequired},
{sfAuditorEncryptedAmount, SoeOptional},
{sfBlindingFactor, SoeRequired},
{sfZKProof, SoeRequired},
{sfBalanceCommitment, SoeRequired},
}))
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTSend.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_SEND, 88, ConfidentialMPTSend,
Delegation::Delegable,
featureConfidentialTransfer,
NoPriv,
({
{sfMPTokenIssuanceID, SoeRequired},
{sfDestination, SoeRequired},
{sfDestinationTag, SoeOptional},
{sfSenderEncryptedAmount, SoeRequired},
{sfDestinationEncryptedAmount, SoeRequired},
{sfIssuerEncryptedAmount, SoeRequired},
{sfAuditorEncryptedAmount, SoeOptional},
{sfZKProof, SoeRequired},
{sfAmountCommitment, SoeRequired},
{sfBalanceCommitment, SoeRequired},
{sfCredentialIDs, SoeOptional},
}))
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTClawback.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_CLAWBACK, 89, ConfidentialMPTClawback,
Delegation::Delegable,
featureConfidentialTransfer,
NoPriv,
({
{sfMPTokenIssuanceID, SoeRequired},
{sfHolder, SoeRequired},
{sfMPTAmount, SoeRequired},
{sfZKProof, SoeRequired},
}))
/** This system-generated transaction type is used to update the status of the various amendments.
For details, see: https://xrpl.org/amendments.html

View File

@@ -137,6 +137,7 @@ JSS(authorized_credentials); // in: ledger_entry DepositPreauth
JSS(auth_accounts); // out: amm_info
JSS(auth_change); // out: AccountInfo
JSS(auth_change_queued); // out: AccountInfo
JSS(auditor_encrypted_balance); // out: mpt_holders (confidential MPT)
JSS(available); // out: ValidatorList
JSS(avg_bps_recv); // out: Peers
JSS(avg_bps_sent); // out: Peers
@@ -161,9 +162,6 @@ JSS(build_path); // in: TransactionSign
JSS(build_version); // out: NetworkOPs
JSS(cancel_after); // out: AccountChannels
JSS(can_delete); // out: CanDelete
JSS(mpt_amount); // out: mpt_holders
JSS(mpt_issuance_id); // in: Payment, mpt_holders
JSS(mptoken_index); // out: mpt_holders
JSS(changes); // out: BookChanges
JSS(channel_id); // out: AccountChannels
JSS(channels); // out: AccountChannels
@@ -185,165 +183,170 @@ JSS(command); // in: RPCHandler
JSS(common); // out: RPC server_definitions
JSS(complete); // out: NetworkOPs, InboundLedger
JSS(complete_ledgers); // out: NetworkOPs, PeerImp
JSS(consensus); // out: NetworkOPs, LedgerConsensus
JSS(converge_time); // out: NetworkOPs
JSS(converge_time_s); // out: NetworkOPs
JSS(cookie); // out: NetworkOPs
JSS(count); // in: AccountTx*, ValidatorList
JSS(counters); // in/out: retrieve counters
JSS(credentials); // in: deposit_authorized
JSS(credential_type); // in: LedgerEntry DepositPreauth
JSS(ctid); // in/out: Tx RPC
JSS(currency_a); // out: BookChanges
JSS(currency_b); // out: BookChanges
JSS(currency); // in: paths/PathRequest, STAmount
// out: STPathSet, STAmount, AccountLines
JSS(current); // out: OwnerInfo
JSS(current_activities); //
JSS(current_ledger_size); // out: TxQ
JSS(current_queue_size); // out: TxQ
JSS(data); // out: LedgerData
JSS(date); // out: tx/Transaction, NetworkOPs
JSS(dbKBLedger); // out: getCounts
JSS(dbKBTotal); // out: getCounts
JSS(dbKBTransaction); // out: getCounts
JSS(debug_signing); // in: TransactionSign
JSS(deletion_blockers_only); // in: AccountObjects
JSS(delivered_amount); // out: insertDeliveredAmount
JSS(deposit_authorized); // out: deposit_authorized
JSS(deprecated); //
JSS(descending); // in: AccountTx*
JSS(description); // in/out: Reservations
JSS(destination); // in: nft_buy_offers, nft_sell_offers
JSS(destination_account); // in: PathRequest, RipplePathFind, account_lines
// out: AccountChannels
JSS(destination_amount); // in: PathRequest, RipplePathFind
JSS(destination_currencies); // in: PathRequest, RipplePathFind
JSS(destination_tag); // in: PathRequest
// out: AccountChannels
JSS(details); // out: Manifest, server_info
JSS(dir_entry); // out: DirectoryEntryIterator
JSS(dir_index); // out: DirectoryEntryIterator
JSS(dir_root); // out: DirectoryEntryIterator
JSS(discounted_fee); // out: amm_info
JSS(domain); // out: ValidatorInfo, Manifest
JSS(drops); // out: TxQ
JSS(duration_us); // out: NetworkOPs
JSS(effective); // out: ValidatorList
// in: UNL
JSS(enabled); // out: AmendmentTable
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
JSS(entire_set); // out: get_aggregate_price
JSS(ephemeral_key); // out: ValidatorInfo
// in/out: Manifest
JSS(error); // out: error
JSS(errored); //
JSS(error_code); // out: error
JSS(error_exception); // out: Submit
JSS(error_message); // out: error
JSS(expand); // in: handler/Ledger
JSS(expected_date); // out: any (warnings)
JSS(expected_date_UTC); // out: any (warnings)
JSS(expected_ledger_size); // out: TxQ
JSS(expiration); // out: AccountOffers, AccountChannels, ValidatorList, amm_info
JSS(fail_hard); // in: Sign, Submit
JSS(failed); // out: InboundLedger
JSS(feature); // in: Feature
JSS(features); // out: Feature
JSS(fee_base); // out: NetworkOPs
JSS(fee_div_max); // in: TransactionSign
JSS(fee_level); // out: AccountInfo
JSS(fee_mult_max); // in: TransactionSign
JSS(fee_ref); // out: NetworkOPs, DEPRECATED
JSS(fetch_pack); // out: NetworkOPs
JSS(FIELDS); // out: RPC server_definitions
// matches definitions.json format
JSS(first); // out: rpc/Version
JSS(finished); //
JSS(fix_txns); // in: LedgerCleaner
JSS(flags); // out: AccountOffers, NetworkOPs
JSS(forward); // in: AccountTx
JSS(freeze); // out: AccountLines
JSS(freeze_peer); // out: AccountLines
JSS(deep_freeze); // out: AccountLines
JSS(deep_freeze_peer); // out: AccountLines
JSS(frozen_balances); // out: GatewayBalances
JSS(full); // in: LedgerClearer, handlers/Ledger
JSS(full_reply); // out: PathFind
JSS(fullbelow_size); // out: GetCounts
JSS(git); // out: server_info
JSS(good); // out: RPCVersion
JSS(hash); // out: NetworkOPs, InboundLedger, LedgerToJson, STTx; field
JSS(have_header); // out: InboundLedger
JSS(have_state); // out: InboundLedger
JSS(have_transactions); // out: InboundLedger
JSS(high); // out: BookChanges
JSS(highest_sequence); // out: AccountInfo
JSS(highest_ticket); // out: AccountInfo
JSS(historical_perminute); // historical_perminute.
JSS(holders); // out: MPTHolders
JSS(hostid); // out: NetworkOPs
JSS(hotwallet); // in: GatewayBalances
JSS(id); // websocket.
JSS(ident); // in: AccountCurrencies, AccountInfo, OwnerInfo
JSS(ignore_default); // in: AccountLines
JSS(in); // out: OverlayImpl
JSS(inLedger); // out: tx/Transaction
JSS(inbound); // out: PeerImp
JSS(index); // in: LedgerEntry
// out: STLedgerEntry, LedgerEntry, TxHistory, LedgerData
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
JSS(initial_sync_duration_us); //
JSS(internal_command); // in: Internal
JSS(invalid_API_version); // out: Many, when a request has an invalid version
JSS(io_latency_ms); // out: NetworkOPs
JSS(ip); // in: Connect, out: OverlayImpl
JSS(is_burned); // out: nft_info (clio)
JSS(isSerialized); // out: RPC server_definitions
// matches definitions.json format
JSS(isSigningField); // out: RPC server_definitions
// matches definitions.json format
JSS(isVLEncoded); // out: RPC server_definitions
// matches definitions.json format
JSS(issuer); // in: RipplePathFind, Subscribe, Unsubscribe, BookOffers
// out: STPathSet, STAmount
JSS(job); //
JSS(job_queue); //
JSS(jobs); //
JSS(jsonrpc); // json version
JSS(jq_trans_overflow); // JobQueue transaction limit overflow.
JSS(kept); // out: SubmitTransaction
JSS(key); // out
JSS(key_type); // in/out: WalletPropose, TransactionSign
JSS(latency); // out: PeerImp
JSS(last); // out: RPCVersion
JSS(last_close); // out: NetworkOPs
JSS(last_refresh_time); // out: ValidatorSite
JSS(last_refresh_status); // out: ValidatorSite
JSS(last_refresh_message); // out: ValidatorSite
JSS(ledger); // in: NetworkOPs, LedgerCleaner, RPCHelpers
// out: NetworkOPs, PeerImp
JSS(ledger_current_index); // out: NetworkOPs, RPCHelpers, LedgerCurrent, LedgerAccept,
// AccountLines
JSS(ledger_data); // out: LedgerHeader
JSS(ledger_hash); // in: RPCHelpers, LedgerRequest, RipplePathFind,
// TransactionEntry, handlers/Ledger
// out: NetworkOPs, RPCHelpers, LedgerClosed, LedgerData,
// AccountLines
JSS(ledger_hit_rate); // out: GetCounts
JSS(ledger_index); // in/out: many
JSS(ledger_index_max); // in, out: AccountTx*
JSS(ledger_index_min); // in, out: AccountTx*
JSS(ledger_max); // in, out: AccountTx*
JSS(ledger_min); // in, out: AccountTx*
JSS(ledger_time); // out: NetworkOPs
JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions
// matches definitions.json format
JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions
JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions
JSS(levels); // LogLevels
JSS(confidential_balance_inbox); // out: mpt_holders (confidential MPT)
JSS(confidential_balance_spending); // out: mpt_holders (confidential MPT)
JSS(confidential_balance_version); // out: mpt_holders (confidential MPT)
JSS(consensus); // out: NetworkOPs, LedgerConsensus
JSS(converge_time); // out: NetworkOPs
JSS(converge_time_s); // out: NetworkOPs
JSS(cookie); // out: NetworkOPs
JSS(count); // in: AccountTx*, ValidatorList
JSS(counters); // in/out: retrieve counters
JSS(credentials); // in: deposit_authorized
JSS(credential_type); // in: LedgerEntry DepositPreauth
JSS(ctid); // in/out: Tx RPC
JSS(currency_a); // out: BookChanges
JSS(currency_b); // out: BookChanges
JSS(currency); // in: paths/PathRequest, STAmount
// out: STPathSet, STAmount, AccountLines
JSS(current); // out: OwnerInfo
JSS(current_activities); //
JSS(current_ledger_size); // out: TxQ
JSS(current_queue_size); // out: TxQ
JSS(data); // out: LedgerData
JSS(date); // out: tx/Transaction, NetworkOPs
JSS(dbKBLedger); // out: getCounts
JSS(dbKBTotal); // out: getCounts
JSS(dbKBTransaction); // out: getCounts
JSS(debug_signing); // in: TransactionSign
JSS(deletion_blockers_only); // in: AccountObjects
JSS(delivered_amount); // out: insertDeliveredAmount
JSS(deposit_authorized); // out: deposit_authorized
JSS(deprecated); //
JSS(descending); // in: AccountTx*
JSS(description); // in/out: Reservations
JSS(destination); // in: nft_buy_offers, nft_sell_offers
JSS(destination_account); // in: PathRequest, RipplePathFind, account_lines
// out: AccountChannels
JSS(destination_amount); // in: PathRequest, RipplePathFind
JSS(destination_currencies); // in: PathRequest, RipplePathFind
JSS(destination_tag); // in: PathRequest
// out: AccountChannels
JSS(details); // out: Manifest, server_info
JSS(dir_entry); // out: DirectoryEntryIterator
JSS(dir_index); // out: DirectoryEntryIterator
JSS(dir_root); // out: DirectoryEntryIterator
JSS(discounted_fee); // out: amm_info
JSS(domain); // out: ValidatorInfo, Manifest
JSS(drops); // out: TxQ
JSS(duration_us); // out: NetworkOPs
JSS(effective); // out: ValidatorList
// in: UNL
JSS(enabled); // out: AmendmentTable
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
JSS(entire_set); // out: get_aggregate_price
JSS(ephemeral_key); // out: ValidatorInfo
// in/out: Manifest
JSS(error); // out: error
JSS(errored); //
JSS(error_code); // out: error
JSS(error_exception); // out: Submit
JSS(error_message); // out: error
JSS(expand); // in: handler/Ledger
JSS(expected_date); // out: any (warnings)
JSS(expected_date_UTC); // out: any (warnings)
JSS(expected_ledger_size); // out: TxQ
JSS(expiration); // out: AccountOffers, AccountChannels, ValidatorList, amm_info
JSS(fail_hard); // in: Sign, Submit
JSS(failed); // out: InboundLedger
JSS(feature); // in: Feature
JSS(features); // out: Feature
JSS(fee_base); // out: NetworkOPs
JSS(fee_div_max); // in: TransactionSign
JSS(fee_level); // out: AccountInfo
JSS(fee_mult_max); // in: TransactionSign
JSS(fee_ref); // out: NetworkOPs, DEPRECATED
JSS(fetch_pack); // out: NetworkOPs
JSS(FIELDS); // out: RPC server_definitions
// matches definitions.json format
JSS(first); // out: rpc/Version
JSS(finished); //
JSS(fix_txns); // in: LedgerCleaner
JSS(flags); // out: AccountOffers, NetworkOPs
JSS(forward); // in: AccountTx
JSS(freeze); // out: AccountLines
JSS(freeze_peer); // out: AccountLines
JSS(deep_freeze); // out: AccountLines
JSS(deep_freeze_peer); // out: AccountLines
JSS(frozen_balances); // out: GatewayBalances
JSS(full); // in: LedgerClearer, handlers/Ledger
JSS(full_reply); // out: PathFind
JSS(fullbelow_size); // out: GetCounts
JSS(git); // out: server_info
JSS(good); // out: RPCVersion
JSS(hash); // out: NetworkOPs, InboundLedger, LedgerToJson, STTx; field
JSS(have_header); // out: InboundLedger
JSS(have_state); // out: InboundLedger
JSS(have_transactions); // out: InboundLedger
JSS(high); // out: BookChanges
JSS(highest_sequence); // out: AccountInfo
JSS(highest_ticket); // out: AccountInfo
JSS(historical_perminute); // historical_perminute.
JSS(holders); // out: MPTHolders
JSS(holder_encryption_key); // out: mpt_holders (confidential MPT)
JSS(hostid); // out: NetworkOPs
JSS(hotwallet); // in: GatewayBalances
JSS(id); // websocket.
JSS(ident); // in: AccountCurrencies, AccountInfo, OwnerInfo
JSS(ignore_default); // in: AccountLines
JSS(in); // out: OverlayImpl
JSS(inLedger); // out: tx/Transaction
JSS(inbound); // out: PeerImp
JSS(index); // in: LedgerEntry
// out: STLedgerEntry, LedgerEntry, TxHistory, LedgerData
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
JSS(initial_sync_duration_us); //
JSS(internal_command); // in: Internal
JSS(invalid_API_version); // out: Many, when a request has an invalid version
JSS(io_latency_ms); // out: NetworkOPs
JSS(ip); // in: Connect, out: OverlayImpl
JSS(is_burned); // out: nft_info (clio)
JSS(isSerialized); // out: RPC server_definitions
// matches definitions.json format
JSS(isSigningField); // out: RPC server_definitions
// matches definitions.json format
JSS(isVLEncoded); // out: RPC server_definitions
// matches definitions.json format
JSS(issuer); // in: RipplePathFind, Subscribe, Unsubscribe, BookOffers
// out: STPathSet, STAmount
JSS(issuer_encrypted_balance); // out: mpt_holders (confidential MPT)
JSS(job); //
JSS(job_queue); //
JSS(jobs); //
JSS(jsonrpc); // json version
JSS(jq_trans_overflow); // JobQueue transaction limit overflow.
JSS(kept); // out: SubmitTransaction
JSS(key); // out
JSS(key_type); // in/out: WalletPropose, TransactionSign
JSS(latency); // out: PeerImp
JSS(last); // out: RPCVersion
JSS(last_close); // out: NetworkOPs
JSS(last_refresh_time); // out: ValidatorSite
JSS(last_refresh_status); // out: ValidatorSite
JSS(last_refresh_message); // out: ValidatorSite
JSS(ledger); // in: NetworkOPs, LedgerCleaner, RPCHelpers
// out: NetworkOPs, PeerImp
JSS(ledger_current_index); // out: NetworkOPs, RPCHelpers, LedgerCurrent, LedgerAccept,
// AccountLines
JSS(ledger_data); // out: LedgerHeader
JSS(ledger_hash); // in: RPCHelpers, LedgerRequest, RipplePathFind,
// TransactionEntry, handlers/Ledger
// out: NetworkOPs, RPCHelpers, LedgerClosed, LedgerData,
// AccountLines
JSS(ledger_hit_rate); // out: GetCounts
JSS(ledger_index); // in/out: many
JSS(ledger_index_max); // in, out: AccountTx*
JSS(ledger_index_min); // in, out: AccountTx*
JSS(ledger_max); // in, out: AccountTx*
JSS(ledger_min); // in, out: AccountTx*
JSS(ledger_time); // out: NetworkOPs
JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions
// matches definitions.json format
JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions
JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions
JSS(levels); // LogLevels
JSS(limit); // in/out: AccountTx*, AccountOffers, AccountLines, AccountObjects
// in: LedgerData, BookOffers
JSS(limit_peer); // out: AccountLines
@@ -401,6 +404,9 @@ JSS(min_ledger); // in: LedgerCleaner
JSS(minimum_fee); // out: TxQ
JSS(minimum_level); // out: TxQ
JSS(missingCommand); // error
JSS(mpt_amount); // out: mpt_holders
JSS(mpt_issuance_id); // in: Payment, mpt_holders
JSS(mptoken_index); // out: mpt_holders
JSS(mpt_issuance_id_a); // out: BookChanges
JSS(mpt_issuance_id_b); // out: BookChanges
JSS(name); // out: AmendmentTableImpl, PeerImp

View File

@@ -147,6 +147,150 @@ public:
{
return this->sle_->at(sfPreviousTxnLgrSeq);
}
/**
* @brief Get sfConfidentialBalanceInbox (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getConfidentialBalanceInbox() const
{
if (hasConfidentialBalanceInbox())
return this->sle_->at(sfConfidentialBalanceInbox);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceInbox is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceInbox() const
{
return this->sle_->isFieldPresent(sfConfidentialBalanceInbox);
}
/**
* @brief Get sfConfidentialBalanceSpending (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getConfidentialBalanceSpending() const
{
if (hasConfidentialBalanceSpending())
return this->sle_->at(sfConfidentialBalanceSpending);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceSpending is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceSpending() const
{
return this->sle_->isFieldPresent(sfConfidentialBalanceSpending);
}
/**
* @brief Get sfConfidentialBalanceVersion (SoeDefault)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getConfidentialBalanceVersion() const
{
if (hasConfidentialBalanceVersion())
return this->sle_->at(sfConfidentialBalanceVersion);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceVersion is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceVersion() const
{
return this->sle_->isFieldPresent(sfConfidentialBalanceVersion);
}
/**
* @brief Get sfIssuerEncryptedBalance (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getIssuerEncryptedBalance() const
{
if (hasIssuerEncryptedBalance())
return this->sle_->at(sfIssuerEncryptedBalance);
return std::nullopt;
}
/**
* @brief Check if sfIssuerEncryptedBalance is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasIssuerEncryptedBalance() const
{
return this->sle_->isFieldPresent(sfIssuerEncryptedBalance);
}
/**
* @brief Get sfAuditorEncryptedBalance (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedBalance() const
{
if (hasAuditorEncryptedBalance())
return this->sle_->at(sfAuditorEncryptedBalance);
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedBalance is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedBalance() const
{
return this->sle_->isFieldPresent(sfAuditorEncryptedBalance);
}
/**
* @brief Get sfHolderEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getHolderEncryptionKey() const
{
if (hasHolderEncryptionKey())
return this->sle_->at(sfHolderEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfHolderEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasHolderEncryptionKey() const
{
return this->sle_->isFieldPresent(sfHolderEncryptionKey);
}
};
/**
@@ -270,6 +414,72 @@ public:
return *this;
}
/**
* @brief Set sfConfidentialBalanceInbox (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setConfidentialBalanceInbox(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfConfidentialBalanceInbox] = value;
return *this;
}
/**
* @brief Set sfConfidentialBalanceSpending (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setConfidentialBalanceSpending(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfConfidentialBalanceSpending] = value;
return *this;
}
/**
* @brief Set sfConfidentialBalanceVersion (SoeDefault)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setConfidentialBalanceVersion(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfConfidentialBalanceVersion] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedBalance (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setIssuerEncryptedBalance(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedBalance] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedBalance (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setAuditorEncryptedBalance(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedBalance] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setHolderEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptionKey] = value;
return *this;
}
/**
* @brief Build and return the completed MPToken wrapper.
* @param index The ledger entry index.

View File

@@ -302,6 +302,78 @@ public:
{
return this->sle_->isFieldPresent(sfReferenceHolding);
}
/**
* @brief Get sfIssuerEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getIssuerEncryptionKey() const
{
if (hasIssuerEncryptionKey())
return this->sle_->at(sfIssuerEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfIssuerEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasIssuerEncryptionKey() const
{
return this->sle_->isFieldPresent(sfIssuerEncryptionKey);
}
/**
* @brief Get sfAuditorEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptionKey() const
{
if (hasAuditorEncryptionKey())
return this->sle_->at(sfAuditorEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptionKey() const
{
return this->sle_->isFieldPresent(sfAuditorEncryptionKey);
}
/**
* @brief Get sfConfidentialOutstandingAmount (SoeDefault)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getConfidentialOutstandingAmount() const
{
if (hasConfidentialOutstandingAmount())
return this->sle_->at(sfConfidentialOutstandingAmount);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialOutstandingAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialOutstandingAmount() const
{
return this->sle_->isFieldPresent(sfConfidentialOutstandingAmount);
}
};
/**
@@ -504,6 +576,39 @@ public:
return *this;
}
/**
* @brief Set sfIssuerEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setIssuerEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setAuditorEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfConfidentialOutstandingAmount (SoeDefault)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setConfidentialOutstandingAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfConfidentialOutstandingAmount] = value;
return *this;
}
/**
* @brief Build and return the completed MPTokenIssuance wrapper.
* @param index The ledger entry index.

View File

@@ -0,0 +1,201 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTClawbackBuilder;
/**
* @brief Transaction: ConfidentialMPTClawback
*
* Type: ttCONFIDENTIAL_MPT_CLAWBACK (89)
* Delegable: Delegation::Delegable
* Amendment: featureConfidentialTransfer
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTClawbackBuilder to construct new transactions.
*/
class ConfidentialMPTClawback : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CLAWBACK;
/**
* @brief Construct a ConfidentialMPTClawback transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTClawback(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTClawback");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfHolder (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getHolder() const
{
return this->tx_->at(sfHolder);
}
/**
* @brief Get sfMPTAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getMPTAmount() const
{
return this->tx_->at(sfMPTAmount);
}
/**
* @brief Get sfZKProof (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getZKProof() const
{
return this->tx_->at(sfZKProof);
}
};
/**
* @brief Builder for ConfidentialMPTClawback transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTClawbackBuilder : public TransactionBuilderBase<ConfidentialMPTClawbackBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTClawbackBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param holder The sfHolder field value.
* @param mPTAmount The sfMPTAmount field value.
* @param zKProof The sfZKProof field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTClawbackBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_ACCOUNT::type::value_type> const& holder, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTClawbackBuilder>(ttCONFIDENTIAL_MPT_CLAWBACK, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setHolder(holder);
setMPTAmount(mPTAmount);
setZKProof(zKProof);
}
/**
* @brief Construct a ConfidentialMPTClawbackBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTClawbackBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CLAWBACK)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTClawbackBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfHolder (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setHolder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfHolder] = value;
return *this;
}
/**
* @brief Set sfMPTAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMPTAmount] = value;
return *this;
}
/**
* @brief Set sfZKProof (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTClawback wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTClawback
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTClawback{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -0,0 +1,336 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTConvertBuilder;
/**
* @brief Transaction: ConfidentialMPTConvert
*
* Type: ttCONFIDENTIAL_MPT_CONVERT (85)
* Delegable: Delegation::Delegable
* Amendment: featureConfidentialTransfer
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTConvertBuilder to construct new transactions.
*/
class ConfidentialMPTConvert : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CONVERT;
/**
* @brief Construct a ConfidentialMPTConvert transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTConvert(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvert");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfMPTAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getMPTAmount() const
{
return this->tx_->at(sfMPTAmount);
}
/**
* @brief Get sfHolderEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getHolderEncryptionKey() const
{
if (hasHolderEncryptionKey())
{
return this->tx_->at(sfHolderEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfHolderEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasHolderEncryptionKey() const
{
return this->tx_->isFieldPresent(sfHolderEncryptionKey);
}
/**
* @brief Get sfHolderEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getHolderEncryptedAmount() const
{
return this->tx_->at(sfHolderEncryptedAmount);
}
/**
* @brief Get sfIssuerEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getIssuerEncryptedAmount() const
{
return this->tx_->at(sfIssuerEncryptedAmount);
}
/**
* @brief Get sfAuditorEncryptedAmount (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedAmount() const
{
if (hasAuditorEncryptedAmount())
{
return this->tx_->at(sfAuditorEncryptedAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedAmount() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
}
/**
* @brief Get sfBlindingFactor (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT256::type::value_type
getBlindingFactor() const
{
return this->tx_->at(sfBlindingFactor);
}
/**
* @brief Get sfZKProof (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getZKProof() const
{
if (hasZKProof())
{
return this->tx_->at(sfZKProof);
}
return std::nullopt;
}
/**
* @brief Check if sfZKProof is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasZKProof() const
{
return this->tx_->isFieldPresent(sfZKProof);
}
};
/**
* @brief Builder for ConfidentialMPTConvert transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTConvertBuilder : public TransactionBuilderBase<ConfidentialMPTConvertBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTConvertBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param mPTAmount The sfMPTAmount field value.
* @param holderEncryptedAmount The sfHolderEncryptedAmount field value.
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
* @param blindingFactor The sfBlindingFactor field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTConvertBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& holderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_UINT256::type::value_type> const& blindingFactor, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTConvertBuilder>(ttCONFIDENTIAL_MPT_CONVERT, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setMPTAmount(mPTAmount);
setHolderEncryptedAmount(holderEncryptedAmount);
setIssuerEncryptedAmount(issuerEncryptedAmount);
setBlindingFactor(blindingFactor);
}
/**
* @brief Construct a ConfidentialMPTConvertBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTConvertBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CONVERT)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfMPTAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMPTAmount] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setHolderEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setHolderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedAmount (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfBlindingFactor (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setBlindingFactor(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfBlindingFactor] = value;
return *this;
}
/**
* @brief Set sfZKProof (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTConvert wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTConvert
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTConvert{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -0,0 +1,310 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTConvertBackBuilder;
/**
* @brief Transaction: ConfidentialMPTConvertBack
*
* Type: ttCONFIDENTIAL_MPT_CONVERT_BACK (87)
* Delegable: Delegation::Delegable
* Amendment: featureConfidentialTransfer
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTConvertBackBuilder to construct new transactions.
*/
class ConfidentialMPTConvertBack : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CONVERT_BACK;
/**
* @brief Construct a ConfidentialMPTConvertBack transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTConvertBack(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBack");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfMPTAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getMPTAmount() const
{
return this->tx_->at(sfMPTAmount);
}
/**
* @brief Get sfHolderEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getHolderEncryptedAmount() const
{
return this->tx_->at(sfHolderEncryptedAmount);
}
/**
* @brief Get sfIssuerEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getIssuerEncryptedAmount() const
{
return this->tx_->at(sfIssuerEncryptedAmount);
}
/**
* @brief Get sfAuditorEncryptedAmount (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedAmount() const
{
if (hasAuditorEncryptedAmount())
{
return this->tx_->at(sfAuditorEncryptedAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedAmount() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
}
/**
* @brief Get sfBlindingFactor (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT256::type::value_type
getBlindingFactor() const
{
return this->tx_->at(sfBlindingFactor);
}
/**
* @brief Get sfZKProof (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getZKProof() const
{
return this->tx_->at(sfZKProof);
}
/**
* @brief Get sfBalanceCommitment (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getBalanceCommitment() const
{
return this->tx_->at(sfBalanceCommitment);
}
};
/**
* @brief Builder for ConfidentialMPTConvertBack transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTConvertBackBuilder : public TransactionBuilderBase<ConfidentialMPTConvertBackBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTConvertBackBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param mPTAmount The sfMPTAmount field value.
* @param holderEncryptedAmount The sfHolderEncryptedAmount field value.
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
* @param blindingFactor The sfBlindingFactor field value.
* @param zKProof The sfZKProof field value.
* @param balanceCommitment The sfBalanceCommitment field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTConvertBackBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& holderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_UINT256::type::value_type> const& blindingFactor, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::decay_t<typename SF_VL::type::value_type> const& balanceCommitment, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTConvertBackBuilder>(ttCONFIDENTIAL_MPT_CONVERT_BACK, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setMPTAmount(mPTAmount);
setHolderEncryptedAmount(holderEncryptedAmount);
setIssuerEncryptedAmount(issuerEncryptedAmount);
setBlindingFactor(blindingFactor);
setZKProof(zKProof);
setBalanceCommitment(balanceCommitment);
}
/**
* @brief Construct a ConfidentialMPTConvertBackBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTConvertBackBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CONVERT_BACK)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBackBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfMPTAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMPTAmount] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setHolderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedAmount (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfBlindingFactor (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setBlindingFactor(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfBlindingFactor] = value;
return *this;
}
/**
* @brief Set sfZKProof (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Set sfBalanceCommitment (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setBalanceCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfBalanceCommitment] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTConvertBack wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTConvertBack
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTConvertBack{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -0,0 +1,129 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTMergeInboxBuilder;
/**
* @brief Transaction: ConfidentialMPTMergeInbox
*
* Type: ttCONFIDENTIAL_MPT_MERGE_INBOX (86)
* Delegable: Delegation::Delegable
* Amendment: featureConfidentialTransfer
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTMergeInboxBuilder to construct new transactions.
*/
class ConfidentialMPTMergeInbox : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_MERGE_INBOX;
/**
* @brief Construct a ConfidentialMPTMergeInbox transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTMergeInbox(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTMergeInbox");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
};
/**
* @brief Builder for ConfidentialMPTMergeInbox transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTMergeInboxBuilder : public TransactionBuilderBase<ConfidentialMPTMergeInboxBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTMergeInboxBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTMergeInboxBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTMergeInboxBuilder>(ttCONFIDENTIAL_MPT_MERGE_INBOX, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
}
/**
* @brief Construct a ConfidentialMPTMergeInboxBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTMergeInboxBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_MERGE_INBOX)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTMergeInboxBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTMergeInboxBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTMergeInbox wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTMergeInbox
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTMergeInbox{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -0,0 +1,408 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTSendBuilder;
/**
* @brief Transaction: ConfidentialMPTSend
*
* Type: ttCONFIDENTIAL_MPT_SEND (88)
* Delegable: Delegation::Delegable
* Amendment: featureConfidentialTransfer
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTSendBuilder to construct new transactions.
*/
class ConfidentialMPTSend : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_SEND;
/**
* @brief Construct a ConfidentialMPTSend transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTSend(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTSend");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfDestination (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getDestination() const
{
return this->tx_->at(sfDestination);
}
/**
* @brief Get sfDestinationTag (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getDestinationTag() const
{
if (hasDestinationTag())
{
return this->tx_->at(sfDestinationTag);
}
return std::nullopt;
}
/**
* @brief Check if sfDestinationTag is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasDestinationTag() const
{
return this->tx_->isFieldPresent(sfDestinationTag);
}
/**
* @brief Get sfSenderEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getSenderEncryptedAmount() const
{
return this->tx_->at(sfSenderEncryptedAmount);
}
/**
* @brief Get sfDestinationEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getDestinationEncryptedAmount() const
{
return this->tx_->at(sfDestinationEncryptedAmount);
}
/**
* @brief Get sfIssuerEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getIssuerEncryptedAmount() const
{
return this->tx_->at(sfIssuerEncryptedAmount);
}
/**
* @brief Get sfAuditorEncryptedAmount (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedAmount() const
{
if (hasAuditorEncryptedAmount())
{
return this->tx_->at(sfAuditorEncryptedAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedAmount() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
}
/**
* @brief Get sfZKProof (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getZKProof() const
{
return this->tx_->at(sfZKProof);
}
/**
* @brief Get sfAmountCommitment (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getAmountCommitment() const
{
return this->tx_->at(sfAmountCommitment);
}
/**
* @brief Get sfBalanceCommitment (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getBalanceCommitment() const
{
return this->tx_->at(sfBalanceCommitment);
}
/**
* @brief Get sfCredentialIDs (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VECTOR256::type::value_type>
getCredentialIDs() const
{
if (hasCredentialIDs())
{
return this->tx_->at(sfCredentialIDs);
}
return std::nullopt;
}
/**
* @brief Check if sfCredentialIDs is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasCredentialIDs() const
{
return this->tx_->isFieldPresent(sfCredentialIDs);
}
};
/**
* @brief Builder for ConfidentialMPTSend transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTSendBuilder : public TransactionBuilderBase<ConfidentialMPTSendBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTSendBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param destination The sfDestination field value.
* @param senderEncryptedAmount The sfSenderEncryptedAmount field value.
* @param destinationEncryptedAmount The sfDestinationEncryptedAmount field value.
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
* @param zKProof The sfZKProof field value.
* @param amountCommitment The sfAmountCommitment field value.
* @param balanceCommitment The sfBalanceCommitment field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTSendBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_ACCOUNT::type::value_type> const& destination, std::decay_t<typename SF_VL::type::value_type> const& senderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& destinationEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::decay_t<typename SF_VL::type::value_type> const& amountCommitment, std::decay_t<typename SF_VL::type::value_type> const& balanceCommitment, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTSendBuilder>(ttCONFIDENTIAL_MPT_SEND, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setDestination(destination);
setSenderEncryptedAmount(senderEncryptedAmount);
setDestinationEncryptedAmount(destinationEncryptedAmount);
setIssuerEncryptedAmount(issuerEncryptedAmount);
setZKProof(zKProof);
setAmountCommitment(amountCommitment);
setBalanceCommitment(balanceCommitment);
}
/**
* @brief Construct a ConfidentialMPTSendBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTSendBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_SEND)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTSendBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfDestination (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* @brief Set sfDestinationTag (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfDestinationTag] = value;
return *this;
}
/**
* @brief Set sfSenderEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setSenderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfSenderEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfDestinationEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setDestinationEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfDestinationEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedAmount (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfZKProof (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Set sfAmountCommitment (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setAmountCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAmountCommitment] = value;
return *this;
}
/**
* @brief Set sfBalanceCommitment (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setBalanceCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfBalanceCommitment] = value;
return *this;
}
/**
* @brief Set sfCredentialIDs (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setCredentialIDs(std::decay_t<typename SF_VECTOR256::type::value_type> const& value)
{
object_[sfCredentialIDs] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTSend wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTSend
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTSend{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -187,6 +187,58 @@ public:
{
return this->tx_->isFieldPresent(sfMutableFlags);
}
/**
* @brief Get sfIssuerEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getIssuerEncryptionKey() const
{
if (hasIssuerEncryptionKey())
{
return this->tx_->at(sfIssuerEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfIssuerEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasIssuerEncryptionKey() const
{
return this->tx_->isFieldPresent(sfIssuerEncryptionKey);
}
/**
* @brief Get sfAuditorEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptionKey() const
{
if (hasAuditorEncryptionKey())
{
return this->tx_->at(sfAuditorEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptionKey() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptionKey);
}
};
/**
@@ -297,6 +349,28 @@ public:
return *this;
}
/**
* @brief Set sfIssuerEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceSetBuilder&
setIssuerEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceSetBuilder&
setAuditorEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptionKey] = value;
return *this;
}
/**
* @brief Build and return the MPTokenIssuanceSet wrapper.
* @param publicKey The public key for signing.

View File

@@ -1,7 +1,6 @@
#pragma once
#include <xrpl/basics/CountedObject.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/Book.h>
#include <xrpl/protocol/ErrorCodes.h>
@@ -27,19 +26,6 @@ public:
};
/** Manages a client's subscription to data feeds.
*
* An InfoSub holds a non-owning reference to its `Source` (typically the
* process-wide `NetworkOPsImp`). The destructor reaches back into the
* `Source` to remove this subscriber from every server-side subscription
* map.
*
* @note Lifetime contract: every `InfoSub` instance MUST be destroyed
* before the backing `Source`. NetworkOPsImp shutdown drops all
* subscriber strong refs before its own teardown to satisfy this.
* @note Thread-safety: per-instance state is guarded by `lock_`. The
* destructor reads tracking sets without taking `lock_` because
* the strong-pointer ref-count is zero at destruction time, so
* no other thread can be calling the public mutators.
*/
class InfoSub : public CountedObject<InfoSub>
{
@@ -131,43 +117,8 @@ public:
virtual bool
subBook(ref ispListener, Book const&) = 0;
/**
* Remove a book subscription for a live subscriber.
*
* Clears the book from the subscriber's own tracking set
* (InfoSub::bookSubscriptions_) and then removes the server-side
* entry from subBook_. Call this from RPC unsubscribe handlers.
*
* @param ispListener The subscriber requesting removal.
* @param book The order book to unsubscribe from.
* @return true if the entry was present and removed, false if the
* subscriber was not subscribed to @p book.
*
* @note Thread-safety: acquires subLock_ internally.
* @note Do NOT call from ~InfoSub(). Use unsubBookInternal instead
* to avoid a redundant write-back to bookSubscriptions_ on a
* partially-destroyed object.
*/
virtual bool
unsubBook(ref ispListener, Book const&) = 0;
/**
* Remove a book subscription during InfoSub teardown.
*
* Removes only the server-side entry from subBook_. Does NOT touch
* InfoSub::bookSubscriptions_ because the InfoSub is being destroyed.
* Called by ~InfoSub() for each book in bookSubscriptions_.
*
* @param uListener The sequence number of the subscriber being torn down.
* @param book The order book entry to remove.
* @return true if the entry was present and removed, false otherwise
* (e.g., already removed by a concurrent RPC unsubscribe).
*
* @note Thread-safety: acquires subLock_ internally.
*/
virtual bool
unsubBookInternal(std::uint64_t uListener, Book const&) = 0;
unsubBook(std::uint64_t uListener, Book const&) = 0;
virtual bool
subTransactions(ref ispListener) = 0;
@@ -207,13 +158,6 @@ public:
addRpcSub(std::string const& strUrl, ref rspEntry) = 0;
virtual bool
tryRemoveRpcSub(std::string const& strUrl) = 0;
/** Journal used by InfoSub for diagnostics that occur after the
* owning subsystem (e.g. application-level Logs) is the only
* surviving sink — primarily destructor-time cleanup failures.
*/
[[nodiscard]] virtual beast::Journal const&
journal() const = 0;
};
public:
@@ -240,31 +184,6 @@ public:
void
deleteSubAccountInfo(AccountID const& account, bool rt);
/** Record that this subscriber is following @p book.
*
* Called by NetworkOPsImp::subBook so that ~InfoSub() can issue a
* matching unsubBook for every book this subscriber is tracking,
* keeping per-subscriber state symmetric with the server-side map.
*
* @param book The order book this subscriber has just subscribed to.
* @note Idempotent: re-inserting an already-tracked book is a no-op.
* @note Thread-safe: takes InfoSub::lock_.
*/
void
insertBookSubscription(Book const& book);
/** Stop tracking @p book for this subscriber.
*
* Called by the unsubscribe RPC handler so that the book is not
* re-unsubscribed by ~InfoSub(). Pairs with insertBookSubscription.
*
* @param book The order book to forget.
* @note No-op if @p book was not previously inserted.
* @note Thread-safe: takes InfoSub::lock_.
*/
void
deleteBookSubscription(Book const& book);
// return false if already subscribed to this account
bool
insertSubAccountHistory(AccountID const& account);
@@ -298,7 +217,6 @@ private:
std::shared_ptr<InfoSubRequest> request_;
std::uint64_t seq_;
hash_set<AccountID> accountHistorySubscriptions_;
hash_set<Book> bookSubscriptions_;
unsigned int apiVersion_ = 0;
static int

View File

@@ -249,19 +249,6 @@ public:
virtual void
stateAccounting(json::Value& obj) = 0;
/** Total number of (book, subscriber) entries currently tracked.
*
* Counts every weak_ptr stored across every book in subBook_, NOT the
* number of distinct subscribers and NOT the number of distinct
* books: a single subscriber following N books contributes N entries.
*
* @note Diagnostic accessor; intended for tests and operator visibility
* into per-book subscription state. The returned value is a
* snapshot under the subscription lock.
*/
virtual std::size_t
getBookSubscribersCount() = 0;
};
} // namespace xrpl

View File

@@ -222,63 +222,8 @@ public:
return tesSUCCESS;
}
/**
* This function can be overridden to introduce additional semantic constraints beyond the
* granular template validation for granular permissions. It is called by the base
* invokeCheckPermission method only after the transaction has successfully passed
* checkGranularSandbox.
*/
static NotTEC
checkGranularSemantics(
ReadView const& view,
STTx const& tx,
std::unordered_set<GranularPermissionType> const& heldGranularPermissions)
{
return tesSUCCESS;
}
/**
* Checks whether the transaction is authorized to be executed by the delegated account.
* This function enforces the strict permission check hierarchy. It is explicitly
* designed NOT to be overridden. Derived transactors must instead implement
* checkGranularSemantics to add custom validation logic for granular permissions.
*
* The evaluation proceeds as follows:
* - If transaction-level permission is granted, the function immediately returns tesSUCCESS.
* - If transaction-level permission is not granted, the function checks whether the transaction
* matches the granular permission template defined in permissions.macro. If it does, it then
* calls checkGranularSemantics to perform any additional, fine-grained validation.
*
*/
template <class T>
static NotTEC
invokeCheckPermission(ReadView const& view, STTx const& tx)
{
// heldGranularPermissions is passed by reference into checkPermission.
// It is populated with the senders granular permissions only when the sender
// lacks tx-level permission but has granular permissions that satisfy the
// granular permission template.
//
// - result is terNO_DELEGATE_PERMISSION: return immediately.
// - result is tesSUCCESS and heldGranularPermissions is empty: tx-level permission was
// granted, so we returned success before populating it.
// - result is tesSUCCESS and heldGranularPermissions is not empty: tx-level permission was
// not granted, but the held granular permissions passed checkGranularSandbox, so we proceed
// to checkGranularSemantics.
//
// WARNING: Do not simplify checkPermission to return only
// heldGranularPermissions or the ter code. Both the result and the
// populated set are required to enforce the strict permission hierarchy
// described above.
std::unordered_set<GranularPermissionType> heldGranularPermissions;
if (NotTEC const result = checkPermission(view, tx, heldGranularPermissions);
!isTesSuccess(result) || heldGranularPermissions.empty())
{
return result;
}
return T::checkGranularSemantics(view, tx, heldGranularPermissions);
}
checkPermission(ReadView const& view, STTx const& tx);
/////////////////////////////////////////////////////
// Interface used by AccountDelete
@@ -408,12 +353,6 @@ protected:
unit::ValueUnit<Unit, T> min = unit::ValueUnit<Unit, T>{});
private:
static NotTEC
checkPermission(
ReadView const& view,
STTx const& tx,
std::unordered_set<GranularPermissionType>& heldGranularPermissions);
std::pair<TER, XRPAmount>
reset(XRPAmount fee);

View File

@@ -413,6 +413,7 @@ using InvariantChecks = std::tuple<
ValidLoanBroker,
ValidLoan,
ValidVault,
ValidConfidentialMPToken,
ValidMPTPayment,
ValidAmounts,
ValidMPTTransfer>;

View File

@@ -36,17 +36,42 @@ class ValidMPTIssuance
std::vector<std::shared_ptr<SLE const>> deletedHoldings_;
public:
/**
* @brief Track MPT issuance and holding creations, deletions, and
* mutations.
*
* @param isDelete Whether the ledger entry is being deleted.
* @param before The ledger entry before transaction application.
* @param after The ledger entry after transaction application.
*/
void
visitEntry(bool, SLE::const_ref, SLE::const_ref);
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);
/**
* @brief Verify MPT issuance invariants after transaction application.
*
* @param tx The transaction being checked.
* @param result The transaction result code.
* @param fee The fee charged by the transaction.
* @param view The ledger view after transaction application.
* @param j Journal used for diagnostics.
* @return true if the invariant checks pass, otherwise false.
*/
[[nodiscard]] bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;
finalize(
STTx const& tx,
TER const result,
XRPAmount const fee,
ReadView const& view,
beast::Journal const& j) const;
};
/** Verify:
* - OutstandingAmount <= MaximumAmount for any MPT
* - OutstandingAmount after = OutstandingAmount before +
* sum (MPT after - MPT before) - this is total MPT credit/debit
/**
* @brief Verify public MPT amount and outstanding amount accounting.
*
* Checks that OutstandingAmount does not exceed MaximumAmount and that
* OutstandingAmount after application equals OutstandingAmount before
* application plus the net holder balance delta.
*/
class ValidMPTPayment
{
@@ -64,11 +89,102 @@ class ValidMPTPayment
hash_map<uint192, MPTData> data_;
public:
/**
* @brief Track MPT amount and outstanding amount changes.
*
* @param isDelete Whether the ledger entry is being deleted.
* @param before The ledger entry before transaction application.
* @param after The ledger entry after transaction application.
*/
void
visitEntry(bool, SLE::const_ref, SLE::const_ref);
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);
/**
* @brief Verify public MPT payment accounting invariants.
*
* @param tx The transaction being checked.
* @param result The transaction result code.
* @param fee The fee charged by the transaction.
* @param view The ledger view after transaction application.
* @param j Journal used for diagnostics.
* @return true if the invariant checks pass, otherwise false.
*/
bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
finalize(
STTx const& tx,
TER const result,
XRPAmount const fee,
ReadView const& view,
beast::Journal const& j);
};
/**
* @brief Invariants: Confidential MPToken consistency
*
* - Convert/ConvertBack symmetry:
* Regular MPToken balance change (±X) == COA (Confidential Outstanding Amount) change (∓X)
* - Cannot delete MPToken with non-zero confidential state:
* Cannot delete if sfIssuerEncryptedBalance exists
* Cannot delete if sfConfidentialBalanceInbox and sfConfidentialBalanceSpending exist
* - Privacy flag consistency:
* MPToken confidential balance fields can only be created or changed if
* lsfMPTCanConfidentialAmount is set on the issuance.
* - Encrypted field existence consistency:
* If sfConfidentialBalanceSpending/sfConfidentialBalanceInbox exists, then
* sfIssuerEncryptedBalance must also exist (and vice versa).
* - COA <= OutstandingAmount:
* Confidential outstanding balance cannot exceed total outstanding.
* - Verifies sfConfidentialBalanceVersion is changed whenever sfConfidentialBalanceSpending is
* modified on an MPToken.
*/
class ValidConfidentialMPToken
{
struct Changes
{
std::int64_t mptAmountDelta = 0;
std::int64_t coaDelta = 0;
std::int64_t outstandingDelta = 0;
SLE::const_pointer issuance;
bool deletedWithEncrypted = false;
bool badConsistency = false;
bool badCOA = false;
bool changesConfidentialFields = false;
bool badVersion = false;
};
std::map<uint192, Changes> changes_;
public:
/**
* @brief Track confidential MPT balance, issuance, and version changes.
*
* @param isDelete Whether the ledger entry is being deleted.
* @param before The ledger entry before transaction application.
* @param after The ledger entry after transaction application.
*/
void
visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after);
/**
* @brief Verify confidential MPT accounting and encrypted-field
* invariants.
*
* @param tx The transaction being checked.
* @param result The transaction result code.
* @param fee The fee charged by the transaction.
* @param view The ledger view after transaction application.
* @param j Journal used for diagnostics.
* @return true if the invariant checks pass, otherwise false.
*/
bool
finalize(
STTx const& tx,
TER const result,
XRPAmount const fee,
ReadView const& view,
beast::Journal const& j);
};
class ValidMPTTransfer
@@ -85,11 +201,36 @@ class ValidMPTTransfer
hash_map<uint256, bool> deletedAuthorized_;
public:
/**
* @brief Track MPT balance changes and deleted authorization state.
*
* @param isDelete Whether the ledger entry is being deleted.
* @param before The ledger entry before transaction application.
* @param after The ledger entry after transaction application.
*/
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after);
/**
* @brief Verify MPT transfer authorization invariants.
*
* @param tx The transaction being checked.
* @param result The transaction result code.
* @param fee The fee charged by the transaction.
* @param view The ledger view after transaction application.
* @param j Journal used for diagnostics.
* @return true if the invariant checks pass, otherwise false.
*/
bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
finalize(
STTx const& tx,
TER const result,
XRPAmount const fee,
ReadView const& view,
beast::Journal const& j);
private:
/**
@@ -99,7 +240,13 @@ private:
* finalize() runs, so their authorization state is captured during
* visitEntry() and stored in deletedAuthorized_. For deleted MPTokens,
* returns true if reqAuth is false or lsfMPTAuthorized was set at deletion.
* For existing MPTokens, returns the result of requireAuth()
* For existing MPTokens, returns the result of requireAuth().
*
* @param view The ledger view after transaction application.
* @param mptid The MPToken issuance ID.
* @param holder The holder account being checked.
* @param requireAuth Whether the issuance requires explicit authorization.
* @return true if the holder is authorized, otherwise false.
*/
[[nodiscard]] bool
isAuthorized(

View File

@@ -23,6 +23,9 @@ public:
static NotTEC
preflight(PreflightContext const& ctx);
static NotTEC
checkPermission(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);

View File

@@ -32,10 +32,7 @@ public:
preflight(PreflightContext const& ctx);
static NotTEC
checkGranularSemantics(
ReadView const& view,
STTx const& tx,
std::unordered_set<GranularPermissionType> const& heldGranularPermissions);
checkPermission(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);

View File

@@ -0,0 +1,59 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Allows an MPT issuer to clawback confidential balances from a holder.
*
* This transaction enables the issuer of an MPToken Issuance (with clawback
* enabled) to reclaim confidential tokens from a holder's account. Unlike
* regular clawback, the issuer cannot see the holder's balance directly.
* Instead, the issuer must provide a zero-knowledge proof that demonstrates
* they know the exact encrypted balance amount.
*
* @par Cryptographic Operations:
* - **Equality Proof Verification**: Verifies that the issuer's revealed
* amount matches the holder's encrypted balance using the issuer's
* ElGamal private key.
*
* @see ConfidentialMPTSend, ConfidentialMPTConvert
*/
class ConfidentialMPTClawback : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTClawback(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl

View File

@@ -0,0 +1,61 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Converts public (plaintext) MPT balance to confidential (encrypted)
* balance.
*
* This transaction allows a token holder to convert their publicly visible
* MPToken balance into an encrypted confidential balance. Once converted,
* the balance can only be spent using ConfidentialMPTSend transactions and
* remains hidden from public view on the ledger.
*
* @par Cryptographic Operations:
* - **Schnorr Proof Verification**: When registering a new ElGamal public key,
* verifies proof of knowledge of the corresponding private key.
* - **Revealed Amount Verification**: Verifies that the provided encrypted
* amounts (for holder, issuer, and optionally auditor) all encrypt the
* same plaintext amount using the provided blinding factor.
*
* @see ConfidentialMPTConvertBack, ConfidentialMPTSend
*/
class ConfidentialMPTConvert : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTConvert(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl

View File

@@ -0,0 +1,62 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Converts confidential (encrypted) MPT balance back to public
* (plaintext) balance.
*
* This transaction allows a token holder to convert their encrypted
* confidential balance back into a publicly visible MPToken balance. The
* holder must prove they have sufficient confidential balance without
* revealing the actual balance amount.
*
* @par Cryptographic Operations:
* - **Revealed Amount Verification**: Verifies that the provided encrypted
* amounts correctly encrypt the conversion amount.
* - **Pedersen Linkage Proof**: Verifies that the provided balance commitment
* correctly links to the holder's encrypted spending balance.
* - **Bulletproof Range Proof**: Verifies that the remaining balance (after
* conversion) is non-negative, ensuring the holder has sufficient funds.
*
* @see ConfidentialMPTConvert, ConfidentialMPTSend
*/
class ConfidentialMPTConvertBack : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTConvertBack(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl

View File

@@ -0,0 +1,63 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Merges the confidential inbox balance into the spending balance.
*
* In the confidential transfer system, incoming funds are deposited into an
* "inbox" balance that the recipient cannot immediately spend. This prevents
* front-running attacks where an attacker could invalidate a pending
* transaction by sending funds to the sender. This transaction merges the
* inbox into the spending balance, making those funds available for spending.
*
* @par Cryptographic Operations:
* - **Homomorphic Addition**: Adds the encrypted inbox balance to the
* encrypted spending balance using ElGamal homomorphic properties.
* - **Zero Encryption**: Resets the inbox to an encryption of zero.
*
* @note This transaction requires no zero-knowledge proofs because it only
* combines encrypted values that the holder already owns. The
* homomorphic properties of ElGamal encryption ensure correctness.
*
* @see ConfidentialMPTSend, ConfidentialMPTConvert
*/
class ConfidentialMPTMergeInbox : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTMergeInbox(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl

View File

@@ -0,0 +1,72 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Transfers confidential MPT tokens between holders privately.
*
* This transaction enables private token transfers where the transfer amount
* is hidden from public view. Both sender and recipient must have initialized
* confidential balances. The transaction provides encrypted amounts for all
* parties (sender, destination, issuer, and optionally auditor) along with
* zero-knowledge proofs that verify correctness without revealing the amount.
*
* @par Cryptographic Operations:
* - **Multi-Ciphertext Equality Proof**: Verifies that all encrypted amounts
* (sender, destination, issuer, auditor) encrypt the same plaintext value.
* - **Amount Pedersen Linkage Proof**: Verifies that the amount commitment
* correctly links to the sender's encrypted amount.
* - **Balance Pedersen Linkage Proof**: Verifies that the balance commitment
* correctly links to the sender's encrypted spending balance.
* - **Bulletproof Range Proof**: Verifies remaining balance and
* transfer amount are non-negative.
*
* @note Funds are deposited into the destination's inbox, not spending
* balance. The recipient must call ConfidentialMPTMergeInbox to make
* received funds spendable.
*
* @see ConfidentialMPTMergeInbox, ConfidentialMPTConvert,
* ConfidentialMPTConvertBack
*/
class ConfidentialMPTSend : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTSend(ApplyContext& ctx) : Transactor(ctx)
{
}
static bool
checkExtraFeatures(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl

View File

@@ -22,6 +22,9 @@ public:
static NotTEC
preflight(PreflightContext const& ctx);
static NotTEC
checkPermission(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);

View File

@@ -21,10 +21,7 @@ public:
preflight(PreflightContext const& ctx);
static NotTEC
checkGranularSemantics(
ReadView const& view,
STTx const& tx,
std::unordered_set<GranularPermissionType> const& heldGranularPermissions);
checkPermission(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);

View File

@@ -71,7 +71,7 @@ if [ ! -e "${target}" ]; then
fi
EOF
COPY bin/check-tools.sh /tmp/check-tools.sh
COPY nix/docker/check-tools.sh /tmp/check-tools.sh
RUN /tmp/check-tools.sh
# Sanity-check that the g++/clang++ are able to build binaries, including sanitizer-instrumented ones.
@@ -93,7 +93,7 @@ RUN if echo "${BASE_IMAGE}" | grep -qiE 'nixos'; then \
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
# Sanity-check that the built binaries run correctly in the vanilla base image, with the necessary sanitizer runtime libraries installed.
COPY bin/install-sanitizer-libs.sh /tmp/install-sanitizer-libs.sh
COPY nix/docker/install-sanitizer-libs.sh /tmp/install-sanitizer-libs.sh
COPY nix/docker/test_files/run-test-binaries.sh /tmp/run-test-binaries.sh
COPY --from=final /tmp/bins /tmp/bins

View File

@@ -1,90 +0,0 @@
# Nix CI Docker images
This directory builds the Docker images used by xrpld's Linux CI. Each image
bundles the **exact same toolchain that the Nix development shell provides**
(see [`docs/build/nix.md`](../../docs/build/nix.md)), so what runs in CI matches
what developers get locally from `nix develop`.
The toolchain (CMake, Ninja, Conan, GCC, Clang, clang-tidy, the
sanitizer/coverage tools, …) is defined in [`nix/packages.nix`](../packages.nix)
and assembled for CI by [`nix/ci-env.nix`](../ci-env.nix). The Docker build
turns that Nix environment into an ordinary container image layered on top of a
conventional base image (Ubuntu, Debian, RHEL, or `nixos/nix`).
## Images
The images are built by the [`build-nix-images.yml`](../../.github/workflows/build-nix-images.yml)
workflow and pushed to `ghcr.io/xrplf/xrpld/nix-<distro>`. The `<distro>` is
selected through the `BASE_IMAGE` build argument; the base images are the
**oldest supported version** of each distribution we target:
| Image | `BASE_IMAGE` | Notes |
| ------------ | -------------------------------------------- | -------------------------------------------------- |
| `nix-nixos` | `nixos/nix:latest` | Build/lint only; binaries are not run (see below). |
| `nix-ubuntu` | `ubuntu:20.04` | Oldest supported Ubuntu (glibc 2.31). |
| `nix-debian` | `debian:bookworm` | |
| `nix-rhel` | `registry.access.redhat.com/ubi9/ubi:latest` | |
All images carry the full toolchain on `PATH` (via `/nix/ci-env/bin`) plus the
CA bundle shipped in the Nix environment, so HTTPS clients (git, curl, Conan)
work without `ca-certificates` being installed in the base image.
## Build stages
[`Dockerfile`](./Dockerfile) is a multi-stage build:
1. **`builder`** — On a `nixos/nix` builder, evaluate the flake and build the
CI environment (`nix/ci-env.nix`). The resulting Nix store closure (the
complete set of store paths the toolchain depends on) is copied into a
staging directory.
2. **`final`** — Start from `BASE_IMAGE`, copy in the Nix store closure and the
`ci-env` symlink tree, and wire up `PATH` and the CA bundle. It then:
- installs the dynamic linker if the base image lacks one (see
[How libc is handled](#how-libc-is-handled)),
- runs [`bin/check-tools.sh`](../../bin/check-tools.sh) to verify every
expected tool is present and runnable, and
- compiles the C++ test programs in
[`test_files/`](./test_files) with both `g++` and `clang++`, and sanitizers.
3. **`tester`** — Start again from a clean `BASE_IMAGE` (no Nix toolchain),
install only the sanitizer runtime libraries
([`install-sanitizer-libs.sh`](./install-sanitizer-libs.sh)), and run the
binaries compiled in `final`. This proves the binaries built with the Nix
toolchain actually run on a vanilla base image. On `nixos/nix` this step is
skipped (the binaries are patched for a conventional FHS loader).
4. **Output** — The final image is gated on the tester succeeding: it copies a
sentinel file out of `tester`, so a failed test run fails the whole build.
## How libc is handled
The goal is for binaries built in these images to run on the **oldest supported
base image** (Ubuntu 20.04, glibc 2.31) and newer — without the developer's Nix
toolchain being present at runtime. Two pieces make that work:
- **Compilers linked against an old glibc.** The Nix CI environment does not use
nixpkgs' current glibc. Instead it pins a 2020 nixpkgs snapshot whose primary
glibc is **2.31** (matching Ubuntu 20.04), via the `nixpkgs-custom-glibc`
flake input. GCC, Clang, binutils and compiler-rt are all rebuilt/wrapped
against this custom glibc (see [`nix/ci-env.nix`](../ci-env.nix)). As a result
the libraries they emit (`libstdc++`, `libgcc_s`, the sanitizer runtimes)
reference only symbols available in glibc 2.31.
- **An expected dynamic linker in the image.**
Binaries built in Nix environments reference a dynamic linker from Nix store paths, which won't be present in the base image. However,
[`loader-path.sh`](./loader-path.sh) reports the expected loader path for the
current architecture, so we can patch the binaries to use the correct loader.
The build then verifies all of this end to end: the test programs in
`test_files/` (a regular binary plus ASan/TSan/UBSan variants) are compiled in
`final`, their `PT_INTERP` is patched to the target loader, and they are run in
the clean `tester` stage to confirm each emits the expected sanitizer
diagnostic on a stock base image.
## Files
| File | Purpose |
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| [`./Dockerfile`](./Dockerfile) | Multi-stage build described above. |
| [`./loader-path.sh`](./loader-path.sh) | Print the dynamic-linker (`PT_INTERP`) path for the current architecture. |
| [`./test_files/`](./test_files) | C++ sources and scripts to compile and run the sanitizer smoke tests. |
| [`/bin/check-tools.sh`](../../bin/check-tools.sh) | Verify every expected tools are present and runnable. |
| [`/bin/install-sanitizer-libs.sh`](../../bin/install-sanitizer-libs.sh) | Install `libasan`/`libtsan`/`libubsan` runtimes on the supported base images. |

38
nix/docker/check-tools.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/bash
# Verify that every tool expected in the Nix CI env is present and runnable.
set -euo pipefail
ccache --version
clang --version
clang++ --version
clang-format --version
cmake --version
conan --version
curl --version
doxygen --version
file --version
g++ --version
gcc --version
gcov --version
gcovr --version
gh --version
git --version
git-cliff --version
gpg --version
less --version
make --version
mold --version
netstat --version
ninja --version
perl --version
pkg-config --version
pre-commit --version
python3 --version
run-clang-tidy --help
vim --version
# A simple test to verify that git can clone a repository over HTTPS
# (i.e. the CA bundle is wired up). Clone to a temp dir and clean up.
tmp_clone="$(mktemp -d)"
git clone --depth 1 https://github.com/XRPLF/actions.git "${tmp_clone}/actions"
rm -rf "${tmp_clone}"

View File

@@ -9,7 +9,6 @@ in
{
commonPackages = with pkgs; [
ccache
clangbuildanalyzer
cmake
conan
curlMinimal # needed for codecov/codecov-action
@@ -19,10 +18,8 @@ in
gh
git
git-cliff
git-lfs
gnumake
gnupg # needed for signing commits & codecov/codecov-action
graphviz
llvmPackages_22.clang-tools
less # needed for git diff
mold
@@ -35,6 +32,5 @@ in
python3
runClangTidy
vim
zip
];
}

View File

@@ -15,6 +15,7 @@ package/
xrpld.sysusers sysusers.d config (used by both RPM and DEB)
xrpld.tmpfiles tmpfiles.d config (used by both RPM and DEB)
xrpld.logrotate logrotate config (installed to /etc/logrotate.d/xrpld)
update-xrpld auto-update script (installed to /usr/libexec/xrpld/, run by update-xrpld.timer)
```
## Prerequisites

View File

@@ -114,11 +114,10 @@ VER_BASE="${VERSION%%-*}"
VER_SUFFIX="${VERSION#*-}"
[[ "${VER_SUFFIX}" == "${VERSION}" ]] && VER_SUFFIX=""
# Reject multi-segment suffixes (e.g. "beta-1", "rc1-15-gabc123"). Neither an
# RPM Version nor a Debian upstream version may contain '-' (it's the NVR /
# version-revision separator), and the convention here is single-token
# suffixes like b1 or rc2. Fail early with a clear message rather than letting
# the package tooling blow up or silently mangle dashes.
# Reject multi-segment suffixes (e.g. "beta-1", "rc1-15-gabc123"). The RPM
# Release field forbids '-', and the convention here is single-token suffixes
# like b1 or rc2. Fail early with a clear message rather than letting either
# rpmbuild blow up or silently mangling dashes into dots.
if [[ "${VER_SUFFIX}" == *-* ]]; then
echo "build_pkg.sh: multi-segment pre-release in VERSION='${VERSION}' (suffix '${VER_SUFFIX}')." >&2
echo "Use single-token suffixes like 3.2.0-b1 or 3.2.0-rc2." >&2
@@ -143,6 +142,9 @@ stage_common() {
cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers"
cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles"
cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate"
cp "${SHARED}/update-xrpld" "${dest}/update-xrpld"
cp "${SHARED}/update-xrpld.service" "${dest}/update-xrpld.service"
cp "${SHARED}/update-xrpld.timer" "${dest}/update-xrpld.timer"
cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset"
}
@@ -154,18 +156,20 @@ build_rpm() {
cp "${SRC_DIR}/package/rpm/xrpld.spec" "${topdir}/SPECS/xrpld.spec"
stage_common "${topdir}/SOURCES"
# Pre-releases use the modern rpm '~' convention (rpm >= 4.10): the suffix
# goes in Version (e.g. 3.2.0~b1), which rpmvercmp sorts *before* the final
# 3.2.0 — identical semantics to Debian's '~'. Release is just the package
# release number. This replaces the older "0.<release>.<suffix>" Release
# hack and keeps the RPM and DEB version strings symmetric.
local rpm_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}"
# RPM Version can't contain '-'. A pre-release goes in Release with a
# leading "0." so 3.2.0-b1 sorts before the final 3.2.0-<pkg_release>.
# The order is "0.<pkg_release>.<suffix>" (e.g. 0.1.b6) — the Fedora/EPEL
# convention. Reversing to "0.<suffix>.<pkg_release>" (e.g. 0.b6.1) breaks
# rpmvercmp against the former because numeric segments outrank alphabetic
# ones, so "0.1.b5" would sort newer than "0.b6.1".
local rpm_release="${PKG_RELEASE}"
[[ -n "${VER_SUFFIX}" ]] && rpm_release="0.${PKG_RELEASE}.${VER_SUFFIX}"
set -x
rpmbuild -bb \
--define "_topdir ${topdir}" \
--define "xrpld_version ${rpm_version}" \
--define "xrpld_release ${PKG_RELEASE}" \
--define "xrpld_version ${VER_BASE}" \
--define "xrpld_release ${rpm_release}" \
"${topdir}/SPECS/xrpld.spec"
}
@@ -177,10 +181,13 @@ build_deb() {
stage_common "${staging}"
cp -r "${DEBIAN_DIR}" "${staging}/debian"
# Debhelper auto-discovers these only from debian/.
cp "${staging}/xrpld.service" "${staging}/debian/xrpld.service"
cp "${staging}/xrpld.sysusers" "${staging}/debian/xrpld.sysusers"
cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles"
cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate"
cp "${staging}/update-xrpld.service" "${staging}/debian/xrpld.update-xrpld.service"
cp "${staging}/update-xrpld.timer" "${staging}/debian/xrpld.update-xrpld.timer"
# Debian '~' marks a pre-release; 3.2.0~b1 sorts before 3.2.0.
local deb_full_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}-${PKG_RELEASE}"

View File

@@ -10,6 +10,7 @@ override_dh_auto_configure override_dh_auto_build override_dh_auto_test:
override_dh_installsystemd:
dh_installsystemd --no-stop-on-upgrade xrpld.service
dh_installsystemd --name=update-xrpld --no-enable --no-start update-xrpld.service update-xrpld.timer
execute_before_dh_installtmpfiles:
dh_installsysusers
@@ -20,6 +21,7 @@ override_dh_install:
install -D -m 0755 xrpld debian/xrpld/usr/bin/xrpld
install -D -m 0644 xrpld.cfg debian/xrpld/etc/xrpld/xrpld.cfg
install -D -m 0644 validators.txt debian/xrpld/etc/xrpld/validators.txt
install -D -m 0755 update-xrpld debian/xrpld/usr/libexec/xrpld/update-xrpld
override_dh_dwz:
@:

View File

@@ -1 +1,2 @@
README.md
LICENSE.md

View File

@@ -35,6 +35,8 @@ install -Dm0644 %{_sourcedir}/validators.txt %{buildroot}%{_sysconfdir}/%{
# systemd units, sysusers, tmpfiles, preset
install -Dm0644 %{_sourcedir}/xrpld.service %{buildroot}%{_unitdir}/xrpld.service
install -Dm0644 %{_sourcedir}/update-xrpld.service %{buildroot}%{_unitdir}/update-xrpld.service
install -Dm0644 %{_sourcedir}/update-xrpld.timer %{buildroot}%{_unitdir}/update-xrpld.timer
install -Dm0644 %{_sourcedir}/xrpld.sysusers %{buildroot}%{_sysusersdir}/xrpld.conf
install -Dm0644 %{_sourcedir}/xrpld.tmpfiles %{buildroot}%{_tmpfilesdir}/xrpld.conf
install -Dm0644 %{_sourcedir}/50-xrpld.preset %{buildroot}%{_presetdir}/50-xrpld.preset
@@ -42,6 +44,9 @@ install -Dm0644 %{_sourcedir}/50-xrpld.preset %{buildroot}%{_presetdir}/50-
# Logrotate config
install -Dm0644 %{_sourcedir}/xrpld.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
# Update helper
install -Dm0755 %{_sourcedir}/update-xrpld %{buildroot}%{_libexecdir}/%{name}/update-xrpld
# Docs
install -Dm0644 %{_sourcedir}/LICENSE.md %{buildroot}%{_docdir}/%{name}/LICENSE.md
install -Dm0644 %{_sourcedir}/README.md %{buildroot}%{_docdir}/%{name}/README.md
@@ -56,10 +61,10 @@ ln -s %{_bindir}/%{name} %{buildroot}/usr/local/bin/rippled
%post
systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || :
%systemd_post xrpld.service
%systemd_post xrpld.service update-xrpld.timer
%preun
%systemd_preun xrpld.service
%systemd_preun xrpld.service update-xrpld.timer
%postun
%systemd_postun_with_restart xrpld.service
@@ -69,6 +74,7 @@ systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || :
%doc %{_docdir}/%{name}/README.md
%dir %{_sysconfdir}/%{name}
%dir %{_libexecdir}/%{name}
%{_bindir}/%{name}
@@ -76,13 +82,18 @@ systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || :
%config(noreplace) %{_sysconfdir}/%{name}/validators.txt
%config(noreplace) %{_sysconfdir}/logrotate.d/%{name}
%{_libexecdir}/%{name}/update-xrpld
%{_unitdir}/xrpld.service
%{_unitdir}/update-xrpld.service
%{_unitdir}/update-xrpld.timer
%{_presetdir}/50-xrpld.preset
%{_sysusersdir}/xrpld.conf
%{_tmpfilesdir}/xrpld.conf
%ghost %dir /var/lib/xrpld
%ghost %dir /var/log/xrpld
%ghost %dir /var/lib/%{name}
%ghost %dir /var/log/%{name}
# Legacy compatibility for pre-FHS package layouts.
# TODO: remove after rippled fully deprecated.

View File

@@ -1,2 +1,4 @@
# /usr/lib/systemd/system-preset/50-xrpld.preset
enable xrpld.service
# Don't enable automatic updates
disable update-xrpld.timer

152
package/shared/update-xrpld Executable file
View File

@@ -0,0 +1,152 @@
#!/usr/bin/env bash
set -euo pipefail
# Optional: also write logs to a legacy file in addition to journald.
# By default, this script logs to systemd/journald, viewable via:
# journalctl -t update-xrpld
#
# Uncomment the line below if you need a flat file for compatibility with
# external tooling, manual inspection, or environments where journald logs
# are not persisted or easily accessible.
#
# Note: This duplicates all output (stdout/stderr) to both journald and the file.
# It is generally not needed on modern systems and may cause log file growth
# if left enabled long-term.
#
# Requires /var/log/xrpld/ to exist and be writable by the service (root).
#
# exec > >(tee -a /var/log/xrpld/update.log) 2>&1
PATH=/usr/sbin:/usr/bin:/sbin:/bin
PKG_NAME=${PKG_NAME:-xrpld}
log() {
# If running under systemd/journald, let it handle timestamps.
if [[ -n "${JOURNAL_STREAM:-}" ]]; then
printf '%s\n' "$*"
else
printf '%s %s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" "$*"
fi
}
require_root() {
if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
log "RESULT: failed reason=not-root"
exit 1
fi
}
get_installed_version() {
if command -v dpkg-query >/dev/null 2>&1; then
dpkg-query -W -f='${Version}' "$PKG_NAME" 2>/dev/null || printf 'unknown'
elif command -v rpm >/dev/null 2>&1; then
rpm -q --qf '%{VERSION}-%{RELEASE}' "$PKG_NAME" 2>/dev/null || printf 'unknown'
else
printf 'unknown'
fi
}
trap 'log "RESULT: failed reason=script-error exit_code=$?"' ERR
apt_can_update() {
apt-get update -qq
apt-get -s --only-upgrade install "$PKG_NAME" 2>/dev/null | grep -q "^Inst ${PKG_NAME}\b"
}
apt_apply_update() {
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
-o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confold" \
"$PKG_NAME"
}
get_rpm_pm() {
if command -v dnf >/dev/null 2>&1; then
printf 'dnf\n'
elif command -v yum >/dev/null 2>&1; then
printf 'yum\n'
else
return 1
fi
}
rpm_refresh_metadata() {
local pm=$1
if [[ "$pm" == "dnf" ]]; then
dnf makecache --refresh -q >/dev/null
else
yum clean expire-cache -q >/dev/null
fi
}
rpm_can_update() {
local pm=$1
rpm_refresh_metadata "$pm"
local rc=0
set +e
"$pm" check-update -q "$PKG_NAME" >/dev/null 2>&1
rc=$?
set -e
if [[ $rc -eq 100 ]]; then
return 0
elif [[ $rc -eq 0 ]]; then
return 1
else
log "$pm check-update failed with exit code ${rc}."
exit 1
fi
}
rpm_apply_update() {
local pm=$1
"$pm" update -y "$PKG_NAME"
}
restart_service() {
# Preserve the operator's prior service state: if xrpld was intentionally
# stopped before the update, don't bring it back up just because the
# auto-update timer fired.
if systemctl is-active --quiet "${PKG_NAME}.service"; then
systemctl restart "${PKG_NAME}.service"
log "${PKG_NAME} service restarted successfully."
else
log "${PKG_NAME} service was not running; skipping restart to preserve prior state."
fi
}
main() {
require_root
if command -v apt-get >/dev/null 2>&1; then
log "Checking for ${PKG_NAME} updates via apt"
if apt_can_update; then
log "Update available; installing."
apt_apply_update
restart_service
log "RESULT: updated ${PKG_NAME}=$(get_installed_version)"
else
log "RESULT: no-update ${PKG_NAME}=$(get_installed_version)"
fi
return
fi
local rpm_pm=""
if rpm_pm="$(get_rpm_pm)"; then
log "Checking for ${PKG_NAME} updates via ${rpm_pm}"
if rpm_can_update "$rpm_pm"; then
log "Update available; installing"
rpm_apply_update "$rpm_pm"
restart_service
log "RESULT: updated ${PKG_NAME}=$(get_installed_version)"
else
log "RESULT: no-update ${PKG_NAME}=$(get_installed_version)"
fi
return
fi
log "RESULT: failed reason=no-package-manager"
exit 1
}
main "$@"

View File

@@ -0,0 +1,16 @@
[Unit]
Description=Check for and install xrpld package updates
Documentation=man:systemd.service(5)
Wants=network-online.target
After=network-online.target
ConditionPathExists=/usr/libexec/xrpld/update-xrpld
ConditionPathExists=/usr/bin/xrpld
[Service]
Type=oneshot
ExecStart=/usr/bin/flock -n /run/lock/xrpld-update.lock /usr/libexec/xrpld/update-xrpld
StandardOutput=journal
StandardError=journal
SyslogIdentifier=update-xrpld
TimeoutStartSec=30min
PrivateTmp=true

View File

@@ -0,0 +1,10 @@
[Unit]
Description=Daily xrpld update check
[Timer]
OnCalendar=*-*-* 00:00:00
RandomizedDelaySec=4h
Persistent=true
[Install]
WantedBy=timers.target

View File

@@ -18,11 +18,6 @@ PrivateTmp=true
User=xrpld
Group=xrpld
LimitNOFILE=65536
SystemCallArchitectures=native
# Uncomment both lines to allow xrpld to bind to privileged ports (<1024)
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
#AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target

View File

@@ -1 +1 @@
halt_on_error=true
halt_on_error=false

View File

@@ -72,7 +72,7 @@ vptr:boost
# Google protobuf - intentional overflows in hash functions
undefined:protobuf
unsigned-integer-overflow:protobuf
unsigned-integer-overflow:google/protobuf/stubs/stringpiece.h
# gRPC intentional overflows in timer calculations
unsigned-integer-overflow:grpc
@@ -102,103 +102,47 @@ undefined:nudb
# Snappy compression library intentional overflows
unsigned-integer-overflow:snappy.cc
# Abseil intentional overflows in hashing, RNG and time arithmetic.
# Matched at library scope (like boost above): the wraparound is by design
# across many absl files (hash mixing, raw_hash_set probing, duration math,
# int128, uniform_int_distribution), so listing individual files just churns.
unsigned-integer-overflow:absl
# Abseil intentional overflows
unsigned-integer-overflow:absl/strings/numbers.cc
unsigned-integer-overflow:absl/strings/internal/cord_rep_flat.h
unsigned-integer-overflow:absl/base/internal/low_level_alloc.cc
unsigned-integer-overflow:absl/hash/internal/hash.h
unsigned-integer-overflow:absl/container/internal/raw_hash_set.h
# Standard library intentional overflows
unsigned-integer-overflow:basic_string.h
unsigned-integer-overflow:bits/align.h
unsigned-integer-overflow:bits/basic_string.tcc
unsigned-integer-overflow:bits/chrono.h
unsigned-integer-overflow:bits/random.h
unsigned-integer-overflow:bits/random.tcc
unsigned-integer-overflow:bits/stl_algobase.h
unsigned-integer-overflow:bits/string_view.tcc
unsigned-integer-overflow:bits/uniform_int_dist.h
unsigned-integer-overflow:string_view
unsigned-integer-overflow:__random/seed_seq.h
unsigned-integer-overflow:__charconv/traits.h
unsigned-integer-overflow:__chrono/duration.h
# libstdc++ <bit> (std::__bit_ceil etc.) negates an unsigned width; <bit> is a
# distinct header from the bits/ directory so it needs its own entry.
unsigned-integer-overflow:include/c++/*/bit
# =============================================================================
# Rippled code suppressions
# =============================================================================
# These suppressions are keyed by SOURCE FILE, not function name. This UBSan
# build runs without symbol information, so the runtime only knows the
# file:line of each report, never the enclosing function — function-name
# patterns silently never match. Each entry below is therefore scoped to the
# file whose arithmetic is intentional; the comment names the specific
# construct.
# Signed integer negation (-value) in amount types.
# INT64_MIN cannot occur in practice due to domain invariants (mantissa ranges
# are well within int64_t bounds), but UBSan flags the pattern as potential
# signed overflow. Narrowed to operator- to avoid suppressing unrelated
# overflows anywhere in a stack trace containing these type names.
signed-integer-overflow:operator-*IOUAmount*
signed-integer-overflow:operator-*XRPAmount*
signed-integer-overflow:operator-*MPTAmount*
signed-integer-overflow:operator-*STAmount*
# STAmount amount-type arithmetic. Unary negation of the mantissa in xrp()/
# iou()/mpt()/canonicalize() and getInt64Value, plus bounded +/- on amounts:
# INT64_MIN cannot occur because canonicalize() keeps the mantissa well within
# int64_t, and operands are bounded by total supply (~10^17 XRP, ~10^18 MPT).
signed-integer-overflow:protocol/STAmount.cpp
# STAmount::operator+ signed addition — operands are bounded by total supply
# (~10^17 for XRP, ~10^18 for MPT) so overflow cannot occur in practice.
signed-integer-overflow:operator+*STAmount*
# nft::cipheredTaxon uses intentional uint32 wraparound (LCG permutation);
# the helper lives in the generated protocol header nft.h.
unsigned-integer-overflow:protocol/nft.h
# STAmount::getRate uses unsigned shift and addition
unsigned-integer-overflow:*STAmount*getRate*
# STAmount::serialize uses unsigned bitwise operations
unsigned-integer-overflow:*STAmount*serialize*
# STPathElement::getHash multiplies/adds accumulators (non-secure, speed-first).
unsigned-integer-overflow:protocol/STPathSet.cpp
# beast XorShiftEngine PRNG and murmurhash3 mixing wrap by design.
unsigned-integer-overflow:beast/xor_shift_engine.h
# Number::normalizeToRange multiplies the mantissa by powers of ten; the result
# is intentionally allowed to wrap while searching for the in-range value.
unsigned-integer-overflow:basics/Number.h
# Counter / sequence arithmetic with intentional unsigned wraparound, each
# guarded by an explicit overflow or domain check at the call site:
# base_uint operator++/-- wrap by definition;
# ApplyView::insertPage ++page is asserted to wrap to 0 (page exhaustion);
# confineOwnerCount documents "overflow is well defined on unsigned";
# NFTokenMint checks tokenSeq + 1u == 0u; AmendmentTable does (seq - 1) / 256.
unsigned-integer-overflow:basics/base_uint.h
unsigned-integer-overflow:ledger/ApplyView.cpp
unsigned-integer-overflow:ledger/helpers/AccountRootHelpers.cpp
unsigned-integer-overflow:tx/transactors/nft/NFTokenMint.cpp
unsigned-integer-overflow:app/misc/detail/AmendmentTable.cpp
# Sentinel / bounded subtractions that wrap by design (loop counters, reverse
# iteration, "not found" sentinels, balance math bounded by issuance invariants,
# base58/base64 codec index math, hash-router and role bit math).
unsigned-integer-overflow:shamap/SHAMap.cpp
unsigned-integer-overflow:protocol/Permissions.cpp
unsigned-integer-overflow:protocol/tokens.cpp
unsigned-integer-overflow:basics/base64.cpp
unsigned-integer-overflow:json/json_value.cpp
unsigned-integer-overflow:app/misc/NetworkOPs.cpp
unsigned-integer-overflow:rpc/detail/Role.cpp
unsigned-integer-overflow:tx/transactors/oracle/OracleSet.cpp
unsigned-integer-overflow:ledger/helpers/MPTokenHelpers.cpp
unsigned-integer-overflow:crypto/RFC1751.cpp
unsigned-integer-overflow:tx/paths/detail/StrandFlow.h
unsigned-integer-overflow:protocol/STObject.h
# GetAggregatePrice negates an unsigned trim count to step a reverse iterator;
# trimCount is bounded by the price set size.
unsigned-integer-overflow:rpc/handlers/orderbook/GetAggregatePrice.cpp
# Test-only intentional overflow/underflow in fixture and unit-test arithmetic.
unsigned-integer-overflow:tests/libxrpl/basics/RangeSet.cpp
unsigned-integer-overflow:test/app/Batch_test.cpp
unsigned-integer-overflow:test/app/Invariants_test.cpp
unsigned-integer-overflow:test/app/Loan_test.cpp
unsigned-integer-overflow:test/app/NFToken_test.cpp
unsigned-integer-overflow:test/app/OfferMPT_test.cpp
unsigned-integer-overflow:test/app/Offer_test.cpp
unsigned-integer-overflow:test/app/Path_test.cpp
unsigned-integer-overflow:test/jtx/impl/acctdelete.cpp
unsigned-integer-overflow:test/ledger/SkipList_test.cpp
unsigned-integer-overflow:test/rpc/Subscribe_test.cpp
signed-integer-overflow:test/basics/XRPAmount_test.cpp
# nft::cipheredTaxon uses intentional uint32 wraparound (LCG permutation)
unsigned-integer-overflow:cipheredTaxon

View File

@@ -0,0 +1,55 @@
#include <xrpl/ledger/BookListeners.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/MultiApiJson.h>
#include <xrpl/server/InfoSub.h>
#include <cstdint>
#include <mutex>
namespace xrpl {
void
BookListeners::addSubscriber(InfoSub::ref sub)
{
std::scoped_lock const sl(lock_);
listeners_[sub->getSeq()] = sub;
}
void
BookListeners::removeSubscriber(std::uint64_t seq)
{
std::scoped_lock const sl(lock_);
listeners_.erase(seq);
}
void
BookListeners::publish(MultiApiJson const& jvObj, hash_set<std::uint64_t>& havePublished)
{
std::scoped_lock const sl(lock_);
auto it = listeners_.cbegin();
while (it != listeners_.cend())
{
InfoSub::pointer p = it->second.lock();
if (p)
{
// Only publish jvObj if this is the first occurrence
if (havePublished.emplace(p->getSeq()).second)
{
jvObj.visit(
p->getApiVersion(), //
[&](json::Value const& jv) { p->send(jv, true); });
}
++it;
}
else
{
it = listeners_.erase(it);
}
}
}
} // namespace xrpl

View File

@@ -346,9 +346,9 @@ verifyValidDomain(ApplyView& view, AccountID const& account, uint256 domainID, b
}
TER
verifyDepositPreauth(
checkDepositPreauth(
STTx const& tx,
ApplyView& view,
ReadView const& view,
AccountID const& src,
AccountID const& dst,
SLE::const_ref sleDst,
@@ -360,9 +360,27 @@ verifyDepositPreauth(
// 2. If src is deposit preauthorized by dst (either by account or by
// credentials).
bool const credentialsPresent = tx.isFieldPresent(sfCredentialIDs);
if (sleDst && ((sleDst->getFlags() & lsfDepositAuth) != 0u))
{
if (src != dst)
{
if (!view.exists(keylet::depositPreauth(dst, src)))
{
return !tx.isFieldPresent(sfCredentialIDs)
? tecNO_PERMISSION
: credentials::authorizedDepositPreauth(
view, tx.getFieldV256(sfCredentialIDs), dst);
}
}
}
if (credentialsPresent)
return tesSUCCESS;
}
TER
cleanupExpiredCredentials(STTx const& tx, ApplyView& view, beast::Journal j)
{
if (tx.isFieldPresent(sfCredentialIDs))
{
auto const foundExpired =
credentials::removeExpired(view, tx.getFieldV256(sfCredentialIDs), j);
@@ -372,20 +390,22 @@ verifyDepositPreauth(
return tecEXPIRED;
}
if (sleDst && sleDst->isFlag(lsfDepositAuth))
{
if (src != dst)
{
if (!view.exists(keylet::depositPreauth(dst, src)))
{
return !credentialsPresent ? tecNO_PERMISSION
: credentials::authorizedDepositPreauth(
view, tx.getFieldV256(sfCredentialIDs), dst);
}
}
}
return tesSUCCESS;
}
TER
verifyDepositPreauth(
STTx const& tx,
ApplyView& view,
AccountID const& src,
AccountID const& dst,
SLE::const_ref sleDst,
beast::Journal j)
{
if (auto const err = cleanupExpiredCredentials(tx, view, j); !isTesSuccess(err))
return err;
return checkDepositPreauth(tx, view, src, dst, sleDst, j);
}
} // namespace xrpl

View File

@@ -290,6 +290,15 @@ removeEmptyHolding(
(view.rules().enabled(fixCleanup3_1_3) && (*mptoken)[~sfLockedAmount].valueOr(0) != 0))
return tecHAS_OBLIGATIONS;
// Don't delete if the token still has confidential balances
if (mptoken->isFieldPresent(sfConfidentialBalanceInbox) ||
mptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
mptoken->isFieldPresent(sfIssuerEncryptedBalance) ||
mptoken->isFieldPresent(sfAuditorEncryptedBalance))
{
return tecHAS_OBLIGATIONS;
}
return authorizeMPToken(
view,
{}, // priorBalance

View File

@@ -5,20 +5,13 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
#include <algorithm>
#include <cstdint>
#include <limits>
#include <optional>
namespace xrpl {
TER
@@ -66,28 +59,4 @@ closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal
return tesSUCCESS;
}
uint32_t
saturatingAdd(Rules const& rules, uint32_t const lhs, uint32_t const rhs)
{
if (rules.enabled(fixCleanup3_2_0))
{
static constexpr auto kUint32Max =
static_cast<uint64_t>(std::numeric_limits<uint32_t>::max());
uint64_t const saturatedResult = std::min(uint64_t{lhs} + rhs, kUint32Max);
return static_cast<uint32_t>(saturatedResult);
}
return lhs + rhs;
}
bool
isChannelExpired(ApplyView const& view, std::optional<uint32_t> timeField)
{
if (!timeField)
return false;
if (view.rules().enabled(fixCleanup3_2_0))
return after(view.header().parentCloseTime, *timeField);
return view.header().parentCloseTime.time_since_epoch().count() >= *timeField;
}
} // namespace xrpl

View File

@@ -349,7 +349,8 @@ accountHolds(
// Only if auth check is needed, as it needs to do an additional read
// operation. Note featureSingleAssetVault will affect error codes.
if (zeroIfUnauthorized == AuthHandling::ZeroIfUnauthorized &&
view.rules().enabled(featureSingleAssetVault))
(view.rules().enabled(featureSingleAssetVault) ||
view.rules().enabled(featureConfidentialTransfer)))
{
if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth);
!isTesSuccess(err))

View File

@@ -45,10 +45,8 @@ ManagerImp::missingBackend()
// the Factory classes is an undefined behaviour.
void
registerNuDBFactory(Manager& manager);
#if XRPL_ROCKSDB_AVAILABLE
void
registerRocksDBFactory(Manager& manager);
#endif
void
registerNullFactory(Manager& manager);
void
@@ -57,9 +55,7 @@ registerMemoryFactory(Manager& manager);
ManagerImp::ManagerImp()
{
registerNuDBFactory(*this);
#if XRPL_ROCKSDB_AVAILABLE
registerRocksDBFactory(*this);
#endif
registerNullFactory(*this);
registerMemoryFactory(*this);
}

View File

@@ -1,23 +1,13 @@
#if XRPL_ROCKSDB_AVAILABLE
#include <xrpl/basics/ByteUtilities.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/core/CurrentThreadName.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/config/BasicConfig.h>
#include <xrpl/config/Constants.h>
#include <xrpl/nodestore/Backend.h>
#include <xrpl/nodestore/Factory.h>
#include <xrpl/nodestore/Manager.h>
#include <xrpl/nodestore/NodeObject.h>
#include <xrpl/nodestore/Scheduler.h>
#include <xrpl/nodestore/Types.h>
#include <xrpl/nodestore/detail/BatchWriter.h>
#include <xrpl/nodestore/detail/DecodedBlob.h>
#include <xrpl/nodestore/detail/EncodedBlob.h>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
@@ -35,14 +25,26 @@
#include <rocksdb/table.h>
#include <rocksdb/write_batch.h>
#include <atomic>
#include <bit>
#include <cstddef>
#include <functional>
#include <memory>
#include <stdexcept>
#include <string>
#if XRPL_ROCKSDB_AVAILABLE
#include <xrpl/basics/ByteUtilities.h>
#include <xrpl/basics/contract.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/core/CurrentThreadName.h>
#include <xrpl/nodestore/Factory.h>
#include <xrpl/nodestore/Manager.h>
#include <xrpl/nodestore/detail/BatchWriter.h>
#include <xrpl/nodestore/detail/DecodedBlob.h>
#include <xrpl/nodestore/detail/EncodedBlob.h>
#include <atomic>
#include <memory>
namespace xrpl::NodeStore {
class RocksDBEnv : public rocksdb::EnvWrapper

View File

@@ -23,7 +23,7 @@ namespace {
//------------------------------------------------------------------------------
// clang-format off
// NOLINTNEXTLINE(readability-identifier-naming)
char const* const versionString = "3.3.0-b0"
char const* const versionString = "3.2.0-rc3"
// clang-format on
;

View File

@@ -0,0 +1,486 @@
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/basics/Buffer.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>
#include <openssl/rand.h>
#include <utility/mpt_utility.h>
#include <mpt_protocol.h>
#include <secp256k1.h>
#include <secp256k1_mpt.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <optional>
#include <stdexcept>
#include <vector>
namespace xrpl {
namespace {
account_id
toAccountId(AccountID const& account)
{
account_id res;
std::memcpy(res.bytes, account.data(), kMPT_ACCOUNT_ID_SIZE);
return res;
}
mpt_issuance_id
toIssuanceId(uint192 const& issuance)
{
mpt_issuance_id res;
std::memcpy(res.bytes, issuance.data(), kMPT_ISSUANCE_ID_SIZE);
return res;
}
} // namespace
uint256
getSendContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& destination,
std::uint32_t version)
{
uint256 result;
mpt_get_send_context_hash(
toAccountId(account),
toIssuanceId(issuanceID),
sequence,
toAccountId(destination),
version,
result.data());
return result;
}
uint256
getClawbackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& holder)
{
uint256 result;
mpt_get_clawback_context_hash(
toAccountId(account),
toIssuanceId(issuanceID),
sequence,
toAccountId(holder),
result.data());
return result;
}
uint256
getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence)
{
uint256 result;
mpt_get_convert_context_hash(
toAccountId(account), toIssuanceId(issuanceID), sequence, result.data());
return result;
}
uint256
getConvertBackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
std::uint32_t version)
{
uint256 result;
mpt_get_convert_back_context_hash(
toAccountId(account), toIssuanceId(issuanceID), sequence, version, result.data());
return result;
}
std::optional<EcPair>
makeEcPair(Slice const& buffer)
{
if (buffer.length() != 2 * kEcGamalEncryptedLength)
return std::nullopt; // LCOV_EXCL_LINE
auto parsePubKey = [](Slice const& slice, secp256k1_pubkey& out) {
return secp256k1_ec_pubkey_parse(secp256k1Context(), &out, slice.data(), slice.length());
};
Slice const s1{buffer.data(), kEcGamalEncryptedLength};
Slice const s2{buffer.data() + kEcGamalEncryptedLength, kEcGamalEncryptedLength};
EcPair pair{};
if (parsePubKey(s1, pair.c1) != 1 || parsePubKey(s2, pair.c2) != 1)
return std::nullopt;
return pair;
}
std::optional<Buffer>
serializeEcPair(EcPair const& pair)
{
auto serializePubKey = [](secp256k1_pubkey const& pub, unsigned char* out) {
size_t outLen = kEcGamalEncryptedLength; // 33 bytes
auto const ret = secp256k1_ec_pubkey_serialize(
secp256k1Context(), out, &outLen, &pub, SECP256K1_EC_COMPRESSED);
return ret == 1 && outLen == kEcGamalEncryptedLength;
};
Buffer buffer(kEcGamalEncryptedTotalLength);
auto const ptr = buffer.data();
bool const res1 = serializePubKey(pair.c1, ptr);
bool const res2 = serializePubKey(pair.c2, ptr + kEcGamalEncryptedLength);
if (!res1 || !res2)
return std::nullopt;
return buffer;
}
bool
isValidCiphertext(Slice const& buffer)
{
return makeEcPair(buffer).has_value();
}
bool
isValidCompressedECPoint(Slice const& buffer)
{
if (buffer.size() != kCompressedEcPointLength)
return false;
// Compressed EC points must start with 0x02 or 0x03
if (buffer[0] != kEcCompressedPrefixEvenY && buffer[0] != kEcCompressedPrefixOddY)
return false;
secp256k1_pubkey point;
return secp256k1_ec_pubkey_parse(secp256k1Context(), &point, buffer.data(), buffer.size()) == 1;
}
std::optional<Buffer>
homomorphicAdd(Slice const& a, Slice const& b)
{
if (a.length() != kEcGamalEncryptedTotalLength || b.length() != kEcGamalEncryptedTotalLength)
return std::nullopt;
auto const pairA = makeEcPair(a);
auto const pairB = makeEcPair(b);
if (!pairA || !pairB)
return std::nullopt;
EcPair sum{};
if (auto res = secp256k1_elgamal_add(
secp256k1Context(), &sum.c1, &sum.c2, &pairA->c1, &pairA->c2, &pairB->c1, &pairB->c2);
res != 1)
{
return std::nullopt;
}
return serializeEcPair(sum);
}
std::optional<Buffer>
homomorphicSubtract(Slice const& a, Slice const& b)
{
if (a.length() != kEcGamalEncryptedTotalLength || b.length() != kEcGamalEncryptedTotalLength)
return std::nullopt;
auto const pairA = makeEcPair(a);
auto const pairB = makeEcPair(b);
if (!pairA || !pairB)
return std::nullopt;
EcPair diff{};
if (auto const res = secp256k1_elgamal_subtract(
secp256k1Context(), &diff.c1, &diff.c2, &pairA->c1, &pairA->c2, &pairB->c1, &pairB->c2);
res != 1)
{
return std::nullopt;
}
return serializeEcPair(diff);
}
std::optional<Buffer>
rerandomizeCiphertext(Slice const& ciphertext, Slice const& pubKeySlice, Slice const& randomness)
{
auto zero = encryptAmount(0, pubKeySlice, randomness);
if (!zero)
return std::nullopt;
return homomorphicAdd(ciphertext, *zero);
}
Buffer
generateBlindingFactor()
{
unsigned char blindingFactor[kEcBlindingFactorLength];
// todo: might need to be updated using another RNG
if (RAND_bytes(blindingFactor, kEcBlindingFactorLength) != 1)
Throw<std::runtime_error>("Failed to generate random number");
return Buffer(blindingFactor, kEcBlindingFactorLength);
}
std::optional<Buffer>
encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor)
{
if (blindingFactor.size() != kEcBlindingFactorLength || pubKeySlice.size() != kEcPubKeyLength)
return std::nullopt;
Buffer out(kEcGamalEncryptedTotalLength);
if (mpt_encrypt_amount(amt, pubKeySlice.data(), blindingFactor.data(), out.data()) != 0)
return std::nullopt;
return out;
}
std::optional<Buffer>
encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId)
{
if (pubKeySlice.size() != kEcPubKeyLength)
return std::nullopt; // LCOV_EXCL_LINE
EcPair pair{};
secp256k1_pubkey pubKey;
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), kEcPubKeyLength);
res != 1)
{
return std::nullopt; // LCOV_EXCL_LINE
}
if (auto res = generate_canonical_encrypted_zero(
secp256k1Context(), &pair.c1, &pair.c2, &pubKey, account.data(), mptId.data());
res != 1)
{
return std::nullopt; // LCOV_EXCL_LINE
}
return serializeEcPair(pair);
}
TER
verifyRevealedAmount(
uint64_t const amount,
Slice const& blindingFactor,
ConfidentialRecipient const& holder,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor)
{
if (blindingFactor.size() != kEcBlindingFactorLength ||
holder.publicKey.size() != kEcPubKeyLength ||
holder.encryptedAmount.size() != kEcGamalEncryptedTotalLength ||
issuer.publicKey.size() != kEcPubKeyLength ||
issuer.encryptedAmount.size() != kEcGamalEncryptedTotalLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
auto toParticipant = [](ConfidentialRecipient const& r) {
mpt_confidential_participant p;
std::memcpy(p.pubkey, r.publicKey.data(), kMPT_PUBKEY_SIZE);
std::memcpy(p.ciphertext, r.encryptedAmount.data(), kMPT_ELGAMAL_TOTAL_SIZE);
return p;
};
auto const holderP = toParticipant(holder);
auto const issuerP = toParticipant(issuer);
mpt_confidential_participant auditorP;
mpt_confidential_participant const* auditorPtr = nullptr;
if (auditor)
{
if (auditor->publicKey.size() != kEcPubKeyLength ||
auditor->encryptedAmount.size() != kEcGamalEncryptedTotalLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
auditorP = toParticipant(*auditor);
auditorPtr = &auditorP;
}
if (mpt_verify_revealed_amount(amount, blindingFactor.data(), &holderP, &issuerP, auditorPtr) !=
0)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
NotTEC
checkEncryptedAmountFormat(STObject const& object)
{
// Current usage of this function is only for ConfidentialMPTConvert and
// ConfidentialMPTConvertBack transactions, which already enforce that these fields
// are present.
if (!object.isFieldPresent(sfHolderEncryptedAmount) ||
!object.isFieldPresent(sfIssuerEncryptedAmount))
{
return temMALFORMED; // LCOV_EXCL_LINE
}
if (object[sfHolderEncryptedAmount].length() != kEcGamalEncryptedTotalLength ||
object[sfIssuerEncryptedAmount].length() != kEcGamalEncryptedTotalLength)
{
return temBAD_CIPHERTEXT;
}
bool const hasAuditor = object.isFieldPresent(sfAuditorEncryptedAmount);
if (hasAuditor && object[sfAuditorEncryptedAmount].length() != kEcGamalEncryptedTotalLength)
return temBAD_CIPHERTEXT;
if (!isValidCiphertext(object[sfHolderEncryptedAmount]) ||
!isValidCiphertext(object[sfIssuerEncryptedAmount]))
{
return temBAD_CIPHERTEXT;
}
if (hasAuditor && !isValidCiphertext(object[sfAuditorEncryptedAmount]))
return temBAD_CIPHERTEXT;
return tesSUCCESS;
}
TER
verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash)
{
if (proofSlice.size() != kEcSchnorrProofLength || pubKeySlice.size() != kEcPubKeyLength)
return tecINTERNAL; // LCOV_EXCL_LINE
if (mpt_verify_convert_proof(proofSlice.data(), pubKeySlice.data(), contextHash.data()) != 0)
return tecBAD_PROOF;
return tesSUCCESS;
}
TER
verifyClawbackProof(
uint64_t const amount,
Slice const& proof,
Slice const& pubKeySlice,
Slice const& ciphertext,
uint256 const& contextHash)
{
if (ciphertext.size() != kEcGamalEncryptedTotalLength ||
pubKeySlice.size() != kEcPubKeyLength || proof.size() != kEcClawbackProofLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
if (mpt_verify_clawback_proof(
proof.data(), amount, pubKeySlice.data(), ciphertext.data(), contextHash.data()) != 0)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
TER
verifySendProof(
Slice const& proof,
ConfidentialRecipient const& sender,
ConfidentialRecipient const& destination,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor,
Slice const& spendingBalance,
Slice const& amountCommitment,
Slice const& balanceCommitment,
uint256 const& contextHash)
{
auto const recipientCount = getConfidentialRecipientCount(auditor.has_value());
if (proof.size() != kEcSendProofLength || sender.publicKey.size() != kEcPubKeyLength ||
sender.encryptedAmount.size() != kEcGamalEncryptedTotalLength ||
destination.publicKey.size() != kEcPubKeyLength ||
destination.encryptedAmount.size() != kEcGamalEncryptedTotalLength ||
issuer.publicKey.size() != kEcPubKeyLength ||
issuer.encryptedAmount.size() != kEcGamalEncryptedTotalLength ||
spendingBalance.size() != kEcGamalEncryptedTotalLength ||
amountCommitment.size() != kEcPedersenCommitmentLength ||
balanceCommitment.size() != kEcPedersenCommitmentLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
auto makeParticipant = [](ConfidentialRecipient const& r) {
mpt_confidential_participant p;
std::memcpy(p.pubkey, r.publicKey.data(), kMPT_PUBKEY_SIZE);
std::memcpy(p.ciphertext, r.encryptedAmount.data(), kMPT_ELGAMAL_TOTAL_SIZE);
return p;
};
std::vector<mpt_confidential_participant> participants;
participants.reserve(recipientCount);
participants.push_back(makeParticipant(sender));
participants.push_back(makeParticipant(destination));
participants.push_back(makeParticipant(issuer));
if (auditor)
{
if (auditor->publicKey.size() != kEcPubKeyLength ||
auditor->encryptedAmount.size() != kEcGamalEncryptedTotalLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
participants.push_back(makeParticipant(*auditor));
}
if (participants.size() != recipientCount)
return tecINTERNAL; // LCOV_EXCL_LINE
if (mpt_verify_send_proof(
proof.data(),
participants.data(),
recipientCount,
spendingBalance.data(),
amountCommitment.data(),
balanceCommitment.data(),
contextHash.data()) != 0)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
TER
verifyConvertBackProof(
Slice const& proof,
Slice const& pubKeySlice,
Slice const& spendingBalance,
Slice const& balanceCommitment,
uint64_t amount,
uint256 const& contextHash)
{
if (proof.size() != kEcConvertBackProofLength || pubKeySlice.size() != kEcPubKeyLength ||
spendingBalance.size() != kEcGamalEncryptedTotalLength ||
balanceCommitment.size() != kEcPedersenCommitmentLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
if (mpt_verify_convert_back_proof(
proof.data(),
pubKeySlice.data(),
spendingBalance.data(),
balanceCommitment.data(),
amount,
contextHash.data()) != 0)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -1,136 +1,91 @@
#include <xrpl/protocol/Permissions.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/Feature.h> // IWYU pragma: keep
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/SOTemplate.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TxFlags.h> // IWYU pragma: keep
#include <xrpl/protocol/TxFormats.h>
#include <algorithm>
#include <cstdint>
#include <functional>
#include <optional>
#include <stdexcept>
#include <string>
#include <tuple>
#include <unordered_set>
#include <utility>
#include <vector>
namespace xrpl {
Permission::GranularPermissionEntry::GranularPermissionEntry(
std::string name,
TxType txType,
std::uint32_t permittedFlags,
std::vector<SOElement> permittedFields)
: name(std::move(name))
, txType(txType)
, permittedFlags(permittedFlags)
, permittedFields(std::move(permittedFields), TxFormats::getCommonFields())
{
}
Permission::Permission()
{
{
txFeatureMap_ = {
#pragma push_macro("TRANSACTION")
#undef TRANSACTION
#define TRANSACTION(tag, value, name, delegable, amendment, ...) \
txDelegationMap_[static_cast<TxType>(value)] = {amendment, delegable};
#define TRANSACTION(tag, value, name, delegable, amendment, ...) {value, amendment},
#include <xrpl/protocol/detail/transactions.macro>
#undef TRANSACTION
#pragma pop_macro("TRANSACTION")
}
granularPermissionsByName_ = {
#pragma push_macro("GRANULAR_PERMISSION")
#undef GRANULAR_PERMISSION
#define GRANULAR_PERMISSION(type, ...) {#type, type},
#include <xrpl/protocol/detail/permissions.macro>
#undef GRANULAR_PERMISSION
#pragma pop_macro("GRANULAR_PERMISSION")
};
{
#pragma push_macro("GRANULAR_PERMISSION")
#undef GRANULAR_PERMISSION
delegableTx_ = {
#pragma push_macro("TRANSACTION")
#undef TRANSACTION
// NOLINTBEGIN(bugprone-macro-parentheses)
#define GRANULAR_PERMISSION(type, txType, value, flags, fields) \
granularPermissions_.emplace( \
std::piecewise_construct, \
std::forward_as_tuple(GranularPermissionType::type), \
std::forward_as_tuple( \
#type, txType, static_cast<std::uint32_t>(flags), std::vector<SOElement> fields));
// NOLINTEND(bugprone-macro-parentheses)
#define TRANSACTION(tag, value, name, delegable, ...) {value, delegable},
#include <xrpl/protocol/detail/transactions.macro>
#undef TRANSACTION
#pragma pop_macro("TRANSACTION")
};
granularPermissionMap_ = {
#pragma push_macro("PERMISSION")
#undef PERMISSION
#define PERMISSION(type, txType, value) {#type, type},
#include <xrpl/protocol/detail/permissions.macro>
#undef GRANULAR_PERMISSION
#pragma pop_macro("GRANULAR_PERMISSION")
}
#undef PERMISSION
#pragma pop_macro("PERMISSION")
};
if (granularPermissionsByName_.size() != granularPermissions_.size())
granularNameMap_ = {
#pragma push_macro("PERMISSION")
#undef PERMISSION
#define PERMISSION(type, txType, value) {type, #type},
#include <xrpl/protocol/detail/permissions.macro>
#undef PERMISSION
#pragma pop_macro("PERMISSION")
};
granularTxTypeMap_ = {
#pragma push_macro("PERMISSION")
#undef PERMISSION
#define PERMISSION(type, txType, value) {type, txType},
#include <xrpl/protocol/detail/permissions.macro>
#undef PERMISSION
#pragma pop_macro("PERMISSION")
};
XRPL_ASSERT(
txFeatureMap_.size() == delegableTx_.size(),
"xrpl::Permission : txFeatureMap_ and delegableTx_ must have same "
"size");
for ([[maybe_unused]] auto const& permission : granularPermissionMap_)
{
// LCOV_EXCL_START
Throw<std::logic_error>(
"granularPermissionsByName_ and granularPermissions_ must have same size");
// LCOV_EXCL_STOP
}
for (auto const& [name, type] : granularPermissionsByName_)
{
if (type <= UINT16_MAX)
{
// LCOV_EXCL_START
Throw<std::logic_error>(
"Granular permission value must exceed the maximum uint16_t value: " + name);
// LCOV_EXCL_STOP
}
}
for (auto const& [type, entry] : granularPermissions_)
granularTxTypes_.insert(entry.txType);
// Validate that all fields listed in permissions.macro exist in the
// corresponding transaction type's format, catching typos at startup.
for (auto const& [type, entry] : granularPermissions_)
{
if (!txDelegationMap_.contains(entry.txType))
{
// LCOV_EXCL_START
Throw<std::logic_error>("Invalid granular permission txType in txDelegationMap_");
// LCOV_EXCL_STOP
}
auto const* fmt = TxFormats::getInstance().findByType(entry.txType);
if (fmt == nullptr)
{
// LCOV_EXCL_START
Throw<std::logic_error>("Invalid granular permission txType");
// LCOV_EXCL_STOP
}
for (auto const& field : entry.permittedFields)
{
if (fmt->getSOTemplate().getIndex(field.sField()) == -1)
{
// LCOV_EXCL_START
Throw<std::logic_error>("Invalid granular permission field");
// LCOV_EXCL_STOP
}
}
XRPL_ASSERT(
permission.second > UINT16_MAX,
"xrpl::Permission::granularPermissionMap_ : granular permission "
"value must not exceed the maximum uint16_t value.");
}
}
@@ -142,11 +97,8 @@ Permission::getInstance()
}
std::optional<std::string>
Permission::getPermissionName(std::uint32_t value) const
Permission::getPermissionName(std::uint32_t const value) const
{
if (value == 0)
return std::nullopt;
auto const permissionValue = static_cast<GranularPermissionType>(value);
if (auto const granular = getGranularName(permissionValue))
return granular;
@@ -162,131 +114,90 @@ Permission::getPermissionName(std::uint32_t value) const
std::optional<std::uint32_t>
Permission::getGranularValue(std::string const& name) const
{
auto const it = granularPermissionsByName_.find(name);
if (it != granularPermissionsByName_.end())
auto const it = granularPermissionMap_.find(name);
if (it != granularPermissionMap_.end())
return static_cast<uint32_t>(it->second);
return std::nullopt;
}
std::optional<std::string>
Permission::getGranularName(GranularPermissionType value) const
Permission::getGranularName(GranularPermissionType const& value) const
{
auto const it = granularPermissions_.find(value);
if (it != granularPermissions_.end())
return it->second.name;
auto const it = granularNameMap_.find(value);
if (it != granularNameMap_.end())
return it->second;
return std::nullopt;
}
std::optional<TxType>
Permission::getGranularTxType(GranularPermissionType gpType) const
Permission::getGranularTxType(GranularPermissionType const& gpType) const
{
auto const it = granularPermissions_.find(gpType);
if (it != granularPermissions_.end())
return it->second.txType;
auto const it = granularTxTypeMap_.find(gpType);
if (it != granularTxTypeMap_.end())
return it->second;
return std::nullopt;
}
bool
Permission::hasGranularPermissions(TxType txType) const
{
return granularTxTypes_.contains(txType);
}
std::optional<std::reference_wrapper<uint256 const>>
Permission::getTxFeature(TxType txType) const
{
auto const it = txDelegationMap_.find(txType);
auto const txFeaturesIt = txFeatureMap_.find(txType);
XRPL_ASSERT(
it != txDelegationMap_.end(),
"xrpl::Permission::getTxFeature : tx exists in txDelegationMap_");
txFeaturesIt != txFeatureMap_.end(),
"xrpl::Permissions::getTxFeature : tx exists in txFeatureMap_");
if (it->second.amendment == uint256{})
if (txFeaturesIt->second == uint256{})
return std::nullopt;
return std::optional{std::cref(it->second.amendment)};
return txFeaturesIt->second;
}
bool
Permission::isDelegable(std::uint32_t permissionValue, Rules const& rules) const
Permission::isDelegable(std::uint32_t const& permissionValue, Rules const& rules) const
{
if (permissionValue == 0)
return false; // LCOV_EXCL_LINE
auto const amendmentEnabled = [&rules](TxDelegationEntry const& entry) {
return entry.amendment == uint256{} || rules.enabled(entry.amendment);
};
// Granular permissions may authorize a limited subset of a tx type even
// when the full tx type is not delegable. They still require the
// underlying transaction amendment to be enabled.
if (auto const granularIt =
granularPermissions_.find(static_cast<GranularPermissionType>(permissionValue));
granularIt != granularPermissions_.end())
auto const granularPermission =
getGranularName(static_cast<GranularPermissionType>(permissionValue));
if (granularPermission)
{
auto const txIt = txDelegationMap_.find(granularIt->second.txType);
return txIt != txDelegationMap_.end() && amendmentEnabled(txIt->second);
// granular permissions are always allowed to be delegated
return true;
}
auto const txType = permissionToTxType(permissionValue);
auto const txIt = txDelegationMap_.find(txType);
auto const it = delegableTx_.find(txType);
// Tx-level permissions require the transaction type itself to be delegable, and
// the corresponding amendment enabled.
return txIt != txDelegationMap_.end() && txIt->second.delegable != NotDelegable &&
amendmentEnabled(txIt->second);
if (it == delegableTx_.end())
return false;
auto const txFeaturesIt = txFeatureMap_.find(txType);
XRPL_ASSERT(
txFeaturesIt != txFeatureMap_.end(),
"xrpl::Permissions::isDelegable : tx exists in txFeatureMap_");
// Delegation is only allowed if the required amendment for the transaction
// is enabled. For transactions that do not require an amendment, delegation
// is always allowed.
if (txFeaturesIt->second != uint256{} && !rules.enabled(txFeaturesIt->second))
return false;
if (it->second == Delegation::NotDelegable)
return false;
return true;
}
uint32_t
Permission::txToPermissionType(TxType const type)
Permission::txToPermissionType(TxType const& type)
{
return static_cast<uint32_t>(type) + 1;
}
TxType
Permission::permissionToTxType(uint32_t value)
Permission::permissionToTxType(uint32_t const& value)
{
XRPL_ASSERT(value > 0, "xrpl::Permission::permissionToTxType : value is greater than 0");
return static_cast<TxType>(value - 1);
}
bool
Permission::checkGranularSandbox(
STTx const& tx,
std::unordered_set<GranularPermissionType> const& heldPermissions) const
{
// Build union of flags upfront to enable an early exit. Fields are not stored and
// grouped in advance to avoid heap allocation.
std::uint32_t unionFlags = 0;
for (auto const& gp : heldPermissions)
{
auto const it = granularPermissions_.find(gp);
if (it != granularPermissions_.end())
unionFlags |= it->second.permittedFlags;
}
// Check if flags are permitted
if ((tx.getFlags() & ~unionFlags) != 0)
return false;
// Check if fields are permitted. Every present field must appear in at least one held
// permission's template. The common fields are included in the constructor.
for (auto const& field : tx)
{
if (field.getSType() == STI_NOTPRESENT)
continue;
if (!std::ranges::any_of(heldPermissions, [&](auto const& gp) {
auto const it = granularPermissions_.find(gp);
return it != granularPermissions_.end() &&
it->second.permittedFields.getIndex(field.getFName()) != -1;
}))
return false;
}
return true;
}
} // namespace xrpl

View File

@@ -5,6 +5,7 @@
#include <xrpl/basics/contract.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/KeyType.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/detail/secp256k1.h>
#include <xrpl/protocol/digest.h>
@@ -211,7 +212,7 @@ publicKeyType(Slice const& slice)
if (slice[0] == 0xED)
return KeyType::Ed25519;
if (slice[0] == 0x02 || slice[0] == 0x03)
if (slice[0] == kEcCompressedPrefixEvenY || slice[0] == kEcCompressedPrefixOddY)
return KeyType::Secp256k1;
}

View File

@@ -217,7 +217,7 @@ STTx::getFeePayer() const
{
// If sfDelegate is present, the delegate account is the payer
// note: if a delegate is specified, its authorization to act on behalf of the account is
// enforced in `Transactor::invokeCheckPermission`
// enforced in `Transactor::checkPermission`
// cryptographic signature validity is checked separately (e.g., in `Transactor::checkSign`)
if (isFieldPresent(sfDelegate))
return getAccountID(sfDelegate);

View File

@@ -106,6 +106,7 @@ transResults()
MAKE_ERROR(tecLIMIT_EXCEEDED, "Limit exceeded."),
MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."),
MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."),
MAKE_ERROR(tecBAD_PROOF, "Proof cannot be verified"),
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
@@ -199,6 +200,7 @@ transResults()
MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."),
MAKE_ERROR(temBAD_TRANSFER_FEE, "Malformed: Transfer fee is outside valid range."),
MAKE_ERROR(temINVALID_INNER_BATCH, "Malformed: Invalid inner batch transaction."),
MAKE_ERROR(temBAD_CIPHERTEXT, "Malformed: Invalid ciphertext."),
MAKE_ERROR(terRETRY, "Retry transaction."),
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),

View File

@@ -1,47 +1,15 @@
#include <xrpl/server/InfoSub.h>
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Book.h>
#include <xrpl/resource/Consumer.h>
#include <cstdint>
#include <exception>
#include <memory>
#include <mutex>
namespace xrpl {
namespace {
// Wraps a Source teardown call so that an exception from one cleanup
// step does not prevent the subsequent steps from running. Source methods
// acquire a lock and can throw std::system_error; a throw out of ~InfoSub
// during stack unwinding would terminate the process. Failures are
// reported through the Source's Journal so they reach the configured log
// sinks; JLOG itself cannot throw, so the noexcept guarantee holds.
template <typename F>
void
safeUnsub(std::uint64_t seq, F&& f, beast::Journal j) noexcept
{
try
{
f();
}
catch (std::exception const& e)
{
JLOG(j.warn()) << "~InfoSub[seq=" << seq << "]: cleanup step failed: " << e.what();
}
catch (...)
{
JLOG(j.warn()) << "~InfoSub[seq=" << seq << "]: cleanup step failed: unknown exception";
}
}
} // namespace
// This is the primary interface into the "client" portion of the program.
// Code that wants to do normal operations on the network such as
// creating and monitoring accounts, creating transactions, and so on
@@ -64,44 +32,25 @@ InfoSub::InfoSub(Source& source, Consumer consumer)
InfoSub::~InfoSub()
{
// Each Source teardown call below acquires a server-side lock and
// can throw. Wrap each independent call so partial failure does not
// skip the remaining teardown steps.
auto const& j = source_.journal();
safeUnsub(seq_, [&] { source_.unsubTransactions(seq_); }, j);
safeUnsub(seq_, [&] { source_.unsubRTTransactions(seq_); }, j);
safeUnsub(seq_, [&] { source_.unsubLedger(seq_); }, j);
safeUnsub(seq_, [&] { source_.unsubManifests(seq_); }, j);
safeUnsub(seq_, [&] { source_.unsubServer(seq_); }, j);
safeUnsub(seq_, [&] { source_.unsubValidations(seq_); }, j);
safeUnsub(seq_, [&] { source_.unsubPeerStatus(seq_); }, j);
safeUnsub(seq_, [&] { source_.unsubConsensus(seq_); }, j);
source_.unsubTransactions(seq_);
source_.unsubRTTransactions(seq_);
source_.unsubLedger(seq_);
source_.unsubManifests(seq_);
source_.unsubServer(seq_);
source_.unsubValidations(seq_);
source_.unsubPeerStatus(seq_);
source_.unsubConsensus(seq_);
// Use the internal unsubscribe so that it won't call
// back to us and modify its own parameter
if (!realTimeSubscriptions_.empty())
{
safeUnsub(
seq_, [&] { source_.unsubAccountInternal(seq_, realTimeSubscriptions_, true); }, j);
}
source_.unsubAccountInternal(seq_, realTimeSubscriptions_, true);
if (!normalSubscriptions_.empty())
{
safeUnsub(
seq_, [&] { source_.unsubAccountInternal(seq_, normalSubscriptions_, false); }, j);
}
source_.unsubAccountInternal(seq_, normalSubscriptions_, false);
for (auto const& account : accountHistorySubscriptions_)
{
safeUnsub(seq_, [&] { source_.unsubAccountHistoryInternal(seq_, account, false); }, j);
}
for (auto const& book : bookSubscriptions_)
{
safeUnsub(seq_, [&] { source_.unsubBookInternal(seq_, book); }, j);
}
source_.unsubAccountHistoryInternal(seq_, account, false);
}
Resource::Consumer&
@@ -165,20 +114,6 @@ InfoSub::deleteSubAccountHistory(AccountID const& account)
accountHistorySubscriptions_.erase(account);
}
void
InfoSub::insertBookSubscription(Book const& book)
{
std::scoped_lock const sl(lock_);
bookSubscriptions_.insert(book);
}
void
InfoSub::deleteBookSubscription(Book const& book)
{
std::scoped_lock const sl(lock_);
bookSubscriptions_.erase(book);
}
void
InfoSub::clearRequest()
{

View File

@@ -22,7 +22,6 @@
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Permissions.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/Rules.h>
@@ -47,7 +46,6 @@
#include <exception>
#include <optional>
#include <stdexcept>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -177,16 +175,6 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask)
if (ctx.tx[sfDelegate] == ctx.tx[sfAccount])
return temBAD_SIGNER;
auto const& perm = Permission::getInstance();
auto const txType = ctx.tx.getTxnType();
// If the transaction is not delegable and does not have granular permissions, fail earlier
// with temINVALID. This is to prevent transactions that are not delegable at all from
// being processed further in the invokeCheckPermission function.
if (!perm.isDelegable(Permission::txToPermissionType(txType), ctx.rules) &&
!perm.hasGranularPermissions(txType))
return temINVALID;
}
if (auto const ret = preflight0(ctx, flagMask))
@@ -307,33 +295,19 @@ Transactor::preflightSigValidated(PreflightContext const& ctx)
}
NotTEC
Transactor::checkPermission(
ReadView const& view,
STTx const& tx,
std::unordered_set<GranularPermissionType>& heldGranularPermissions)
Transactor::checkPermission(ReadView const& view, STTx const& tx)
{
auto const delegate = tx[~sfDelegate];
if (!delegate)
return tesSUCCESS;
auto const sle = view.read(keylet::delegate(tx[sfAccount], *delegate));
auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
auto const sle = view.read(delegateKey);
if (!sle)
return terNO_DELEGATE_PERMISSION;
if (isTesSuccess(checkTxPermission(sle, tx)))
return tesSUCCESS;
if (!Permission::getInstance().hasGranularPermissions(tx.getTxnType()))
return terNO_DELEGATE_PERMISSION;
heldGranularPermissions = getGranularPermission(sle, tx.getTxnType());
if (heldGranularPermissions.empty())
return terNO_DELEGATE_PERMISSION;
if (!Permission::getInstance().checkGranularSandbox(tx, heldGranularPermissions))
return terNO_DELEGATE_PERMISSION;
return tesSUCCESS;
return checkTxPermission(sle, tx);
}
XRPAmount

View File

@@ -181,8 +181,7 @@ invokePreclaim(PreclaimContext const& ctx)
if (NotTEC const result = T::checkPriorTxAndLastLedger(ctx))
return result;
if (NotTEC const result =
Transactor::invokeCheckPermission<T>(ctx.view, ctx.tx))
if (NotTEC const result = T::checkPermission(ctx.view, ctx.tx))
return result;
if (NotTEC const result = T::checkSign(ctx))

View File

@@ -1,7 +1,9 @@
#include <xrpl/tx/invariants/MPTInvariant.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -21,11 +23,22 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
namespace xrpl {
static constexpr auto kConfidentialMptTxTypes = std::to_array<TxType>({
ttCONFIDENTIAL_MPT_SEND,
ttCONFIDENTIAL_MPT_CONVERT,
ttCONFIDENTIAL_MPT_CONVERT_BACK,
ttCONFIDENTIAL_MPT_MERGE_INBOX,
ttCONFIDENTIAL_MPT_CLAWBACK,
});
void
ValidMPTIssuance::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
{
@@ -436,6 +449,16 @@ ValidMPTPayment::finalize(
{
if (isTesSuccess(result))
{
// Confidential transactions are validated by ValidConfidentialMPToken.
// They modify encrypted fields and sfConfidentialOutstandingAmount
// rather than sfMPTAmount/sfOutstandingAmount in the standard way,
// so ValidMPTPayment's accounting does not apply to them.
if (std::ranges::find(kConfidentialMptTxTypes, tx.getTxnType()) !=
kConfidentialMptTxTypes.end())
{
return true;
}
bool const invariantPasses = !view.rules().enabled(featureMPTokensV2);
if (overflow_)
{
@@ -466,6 +489,249 @@ ValidMPTPayment::finalize(
return true;
}
void
ValidConfidentialMPToken::visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after)
{
// Helper to get MPToken Issuance ID safely
auto const getMptID = [](std::shared_ptr<SLE const> const& sle) -> uint192 {
if (!sle)
return beast::kZero;
if (sle->getType() == ltMPTOKEN)
return sle->getFieldH192(sfMPTokenIssuanceID);
if (sle->getType() == ltMPTOKEN_ISSUANCE)
return makeMptID(sle->getFieldU32(sfSequence), sle->getAccountID(sfIssuer));
return beast::kZero;
};
if (before && before->getType() == ltMPTOKEN)
{
uint192 const id = getMptID(before);
changes_[id].mptAmountDelta -= before->getFieldU64(sfMPTAmount);
// Cannot delete MPToken with non-zero confidential state or non-zero public amount
if (isDelete)
{
bool const hasPublicBalance = before->getFieldU64(sfMPTAmount) > 0;
bool const hasEncryptedFields = before->isFieldPresent(sfConfidentialBalanceSpending) ||
before->isFieldPresent(sfConfidentialBalanceInbox) ||
before->isFieldPresent(sfIssuerEncryptedBalance);
if (hasPublicBalance || hasEncryptedFields)
changes_[id].deletedWithEncrypted = true;
}
}
if (after && after->getType() == ltMPTOKEN)
{
uint192 const id = getMptID(after);
changes_[id].mptAmountDelta += after->getFieldU64(sfMPTAmount);
// Encrypted field existence consistency
bool const hasIssuerBalance = after->isFieldPresent(sfIssuerEncryptedBalance);
bool const hasHolderInbox = after->isFieldPresent(sfConfidentialBalanceInbox);
bool const hasHolderSpending = after->isFieldPresent(sfConfidentialBalanceSpending);
// sfIssuerEncryptedBalance, sfConfidentialBalanceInbox, and sfConfidentialBalanceSpending
// must all exist or not exist same time.
if (hasHolderInbox != hasHolderSpending || hasHolderInbox != hasIssuerBalance)
changes_[id].badConsistency = true;
auto const confidentialBalanceFieldChanged = [&before, &after](auto const& field) {
auto const afterValue = (*after)[~field];
if (!afterValue)
return false;
if (!before || before->getType() != ltMPTOKEN)
return true; // LCOV_EXCL_LINE
return (*before)[~field] != afterValue;
};
if (confidentialBalanceFieldChanged(sfConfidentialBalanceInbox) ||
confidentialBalanceFieldChanged(sfConfidentialBalanceSpending) ||
confidentialBalanceFieldChanged(sfIssuerEncryptedBalance) ||
confidentialBalanceFieldChanged(sfAuditorEncryptedBalance))
{
changes_[id].changesConfidentialFields = true;
}
}
if (before && before->getType() == ltMPTOKEN_ISSUANCE)
{
uint192 const id = getMptID(before);
if (before->isFieldPresent(sfConfidentialOutstandingAmount))
changes_[id].coaDelta -= before->getFieldU64(sfConfidentialOutstandingAmount);
changes_[id].outstandingDelta -= before->getFieldU64(sfOutstandingAmount);
}
if (after && after->getType() == ltMPTOKEN_ISSUANCE)
{
uint192 const id = getMptID(after);
auto& change = changes_[id];
bool const hasCOA = after->isFieldPresent(sfConfidentialOutstandingAmount);
std::uint64_t const coa = (*after)[~sfConfidentialOutstandingAmount].value_or(0);
std::uint64_t const oa = after->getFieldU64(sfOutstandingAmount);
if (hasCOA)
change.coaDelta += coa;
change.outstandingDelta += oa;
change.issuance = after;
// COA <= OutstandingAmount
if (coa > oa)
change.badCOA = true;
}
if (before && after && before->getType() == ltMPTOKEN && after->getType() == ltMPTOKEN)
{
uint192 const id = getMptID(after);
// sfConfidentialBalanceVersion must change when spending changes
auto const spendingBefore = (*before)[~sfConfidentialBalanceSpending];
auto const spendingAfter = (*after)[~sfConfidentialBalanceSpending];
auto const versionBefore = (*before)[~sfConfidentialBalanceVersion];
auto const versionAfter = (*after)[~sfConfidentialBalanceVersion];
if (spendingBefore.has_value() && spendingBefore != spendingAfter)
{
if (versionBefore == versionAfter)
changes_[id].badVersion = true;
}
}
}
bool
ValidConfidentialMPToken::finalize(
STTx const& tx,
TER const result,
XRPAmount const,
ReadView const& view,
beast::Journal const& j)
{
if (result != tesSUCCESS)
return true;
for (auto const& [id, checks] : changes_)
{
// Find the MPTokenIssuance
auto const issuance = [&]() -> std::shared_ptr<SLE const> {
if (checks.issuance)
return checks.issuance;
return view.read(keylet::mptIssuance(id));
}();
// Skip all invariance checks if issuance doesn't exist because that means the MPT has been
// deleted
if (!issuance)
continue;
// Cannot delete MPToken with non-zero confidential state
if (checks.deletedWithEncrypted)
{
if ((*issuance)[~sfConfidentialOutstandingAmount].value_or(0) > 0)
{
JLOG(j.fatal())
<< "Invariant failed: MPToken deleted with encrypted fields while COA > 0";
return false;
}
}
// Encrypted field existence consistency
if (checks.badConsistency)
{
JLOG(j.fatal()) << "Invariant failed: MPToken encrypted field "
"existence inconsistency";
return false;
}
// COA <= OutstandingAmount
if (checks.badCOA)
{
JLOG(j.fatal()) << "Invariant failed: Confidential outstanding amount "
"exceeds total outstanding amount";
return false;
}
// Confidential balance fields may remain on a holder MPToken after all
// confidential balances have returned to zero. Only creating or
// changing those fields requires the issuance privacy flag.
if (checks.changesConfidentialFields)
{
if (!issuance->isFlag(lsfMPTCanConfidentialAmount))
{
JLOG(j.fatal()) << "Invariant failed: MPToken has encrypted "
"fields but Issuance does not have "
"lsfMPTCanConfidentialAmount set";
return false;
}
}
// We only enforce this when Confidential Outstanding Amount changes (Convert, ConvertBack,
// ConfidentialClawback). This avoids falsely failing on Escrow or AMM operations that lock
// public tokens outside of ltMPTOKEN. Convert / ConvertBack:
// - COA and MPTAmount must have opposite deltas, which cancel each other out to zero.
// - OA remains unchanged.
// - Therefore, the net delta on both sides of the equation is zero.
//
// Clawback:
// - MPTAmount remains unchanged.
// - COA and OA must have identical deltas (mirrored on each side).
// - The equation remains balanced as both sides have equal offsets.
if (checks.coaDelta != 0)
{
if (checks.mptAmountDelta + checks.coaDelta != checks.outstandingDelta)
{
JLOG(j.fatal()) << "Invariant failed: Token conservation "
"violation for MPT "
<< to_string(id);
return false;
}
}
else if (
std::ranges::find(kConfidentialMptTxTypes, tx.getTxnType()) !=
kConfidentialMptTxTypes.end())
{
// Confidential Txns should not modify public MPTAmount balance
// if Confidential Amount Delta is 0
if (checks.mptAmountDelta != 0)
{
JLOG(j.fatal()) << "Invariant failed: MPTAmount changed by confidential "
"transaction that should not modify this field."
<< to_string(id);
return false;
}
// Among confidential MPT transactions, only ConfidentialMPTSend and
// ConfidentialMPTMergeInbox leave coaDelta unmodified. Therefore, if a confidential MPT
// transaction reaches here, it must be one of these two types, neither of which will
// modify sfOutstandingAmount
if (checks.outstandingDelta != 0)
{
JLOG(j.fatal()) << "Invariant failed: OutstandingAmount changed "
"by confidential transaction that should not "
"modify it for MPT "
<< to_string(id);
return false;
}
}
if (checks.badVersion)
{
JLOG(j.fatal())
<< "Invariant failed: MPToken sfConfidentialBalanceVersion not updated when "
"sfConfidentialBalanceSpending changed";
return false;
}
}
return true;
}
void
ValidMPTTransfer::visitEntry(
bool isDelete,

View File

@@ -6,6 +6,7 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/DelegateHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -19,11 +20,13 @@
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <cstdint>
#include <unordered_set>
namespace xrpl {
@@ -165,6 +168,54 @@ AccountSet::preflight(PreflightContext const& ctx)
return tesSUCCESS;
}
NotTEC
AccountSet::checkPermission(ReadView const& view, STTx const& tx)
{
// AccountSet is prohibited to be granted on a transaction level,
// but some granular permissions are allowed.
auto const delegate = tx[~sfDelegate];
if (!delegate)
return tesSUCCESS;
auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
auto const sle = view.read(delegateKey);
if (!sle)
return terNO_DELEGATE_PERMISSION;
std::unordered_set<GranularPermissionType> granularPermissions;
loadGranularPermission(sle, ttACCOUNT_SET, granularPermissions);
auto const uSetFlag = tx.getFieldU32(sfSetFlag);
auto const uClearFlag = tx.getFieldU32(sfClearFlag);
// We don't support any flag based granular permission under
// AccountSet transaction. If any delegated account is trying to
// update the flag on behalf of another account, it is not
// authorized.
if (uSetFlag != 0 || uClearFlag != 0 || ((tx.getFlags() & tfUniversalMask) != 0u))
return terNO_DELEGATE_PERMISSION;
if (tx.isFieldPresent(sfEmailHash) && !granularPermissions.contains(AccountEmailHashSet))
return terNO_DELEGATE_PERMISSION;
if (tx.isFieldPresent(sfWalletLocator) || tx.isFieldPresent(sfNFTokenMinter))
return terNO_DELEGATE_PERMISSION;
if (tx.isFieldPresent(sfMessageKey) && !granularPermissions.contains(AccountMessageKeySet))
return terNO_DELEGATE_PERMISSION;
if (tx.isFieldPresent(sfDomain) && !granularPermissions.contains(AccountDomainSet))
return terNO_DELEGATE_PERMISSION;
if (tx.isFieldPresent(sfTransferRate) && !granularPermissions.contains(AccountTransferRateSet))
return terNO_DELEGATE_PERMISSION;
if (tx.isFieldPresent(sfTickSize) && !granularPermissions.contains(AccountTickSizeSet))
return terNO_DELEGATE_PERMISSION;
return tesSUCCESS;
}
TER
AccountSet::preclaim(PreclaimContext const& ctx)
{

View File

@@ -29,12 +29,14 @@ checkTxPermission(SLE::const_ref delegate, STTx const& tx)
return terNO_DELEGATE_PERMISSION;
}
std::unordered_set<GranularPermissionType>
getGranularPermission(SLE::const_ref delegate, TxType const& txType)
void
loadGranularPermission(
SLE::const_ref delegate,
TxType const& txType,
std::unordered_set<GranularPermissionType>& granularPermissions)
{
std::unordered_set<GranularPermissionType> granularPermissions;
if (!delegate)
return granularPermissions;
return;
auto const permissionArray = delegate->getFieldArray(sfPermissions);
for (auto const& permission : permissionArray)
@@ -45,8 +47,6 @@ getGranularPermission(SLE::const_ref delegate, TxType const& txType)
if (type && *type == txType)
granularPermissions.insert(granularValue);
}
return granularPermissions;
}
} // namespace xrpl

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