Compare commits

..

59 Commits

Author SHA1 Message Date
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
Ayaz Salikhov
a0e09187b9 chore: Add cmake-format pre-commit hook (#6279)
This change adds `cmake-format` as. a pre-commit hook. The style file closely matches that in Clio, and they will be made to be equivalent over time. For now, some files have been excluded, as those need some manual adjustments, which will be done in future changes.
2026-01-29 13:33:24 +00: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
63 changed files with 9483 additions and 838 deletions

236
.cmake-format.yaml Normal file
View File

@@ -0,0 +1,236 @@
_help_parse: Options affecting listfile parsing
parse:
_help_additional_commands:
- Specify structure for custom cmake functions
_help_override_spec:
- Override configurations per-command where available
override_spec: {}
_help_vartags:
- Specify variable tags.
vartags: []
_help_proptags:
- Specify property tags.
proptags: []
_help_format: Options affecting formatting.
format:
_help_disable:
- Disable formatting entirely, making cmake-format a no-op
disable: false
_help_line_width:
- How wide to allow formatted cmake files
line_width: 120
_help_tab_size:
- How many spaces to tab for indent
tab_size: 4
_help_use_tabchars:
- If true, lines are indented using tab characters (utf-8
- 0x09) instead of <tab_size> space characters (utf-8 0x20).
- In cases where the layout would require a fractional tab
- character, the behavior of the fractional indentation is
- governed by <fractional_tab_policy>
use_tabchars: false
_help_fractional_tab_policy:
- If <use_tabchars> is True, then the value of this variable
- indicates how fractional indentions are handled during
- whitespace replacement. If set to 'use-space', fractional
- indentation is left as spaces (utf-8 0x20). If set to
- "`round-up` fractional indentation is replaced with a single"
- tab character (utf-8 0x09) effectively shifting the column
- to the next tabstop
fractional_tab_policy: use-space
_help_max_subgroups_hwrap:
- If an argument group contains more than this many sub-groups
- (parg or kwarg groups) then force it to a vertical layout.
max_subgroups_hwrap: 4
_help_max_pargs_hwrap:
- If a positional argument group contains more than this many
- arguments, then force it to a vertical layout.
max_pargs_hwrap: 5
_help_max_rows_cmdline:
- If a cmdline positional group consumes more than this many
- lines without nesting, then invalidate the layout (and nest)
max_rows_cmdline: 2
_help_separate_ctrl_name_with_space:
- If true, separate flow control names from their parentheses
- with a space
separate_ctrl_name_with_space: true
_help_separate_fn_name_with_space:
- If true, separate function names from parentheses with a
- space
separate_fn_name_with_space: false
_help_dangle_parens:
- If a statement is wrapped to more than one line, than dangle
- the closing parenthesis on its own line.
dangle_parens: false
_help_dangle_align:
- If the trailing parenthesis must be 'dangled' on its on
- "line, then align it to this reference: `prefix`: the start"
- "of the statement, `prefix-indent`: the start of the"
- "statement, plus one indentation level, `child`: align to"
- the column of the arguments
dangle_align: prefix
_help_min_prefix_chars:
- If the statement spelling length (including space and
- parenthesis) is smaller than this amount, then force reject
- nested layouts.
min_prefix_chars: 18
_help_max_prefix_chars:
- If the statement spelling length (including space and
- parenthesis) is larger than the tab width by more than this
- amount, then force reject un-nested layouts.
max_prefix_chars: 10
_help_max_lines_hwrap:
- If a candidate layout is wrapped horizontally but it exceeds
- this many lines, then reject the layout.
max_lines_hwrap: 2
_help_line_ending:
- What style line endings to use in the output.
line_ending: unix
_help_command_case:
- Format command names consistently as 'lower' or 'upper' case
command_case: canonical
_help_keyword_case:
- Format keywords consistently as 'lower' or 'upper' case
keyword_case: unchanged
_help_always_wrap:
- A list of command names which should always be wrapped
always_wrap: []
_help_enable_sort:
- If true, the argument lists which are known to be sortable
- will be sorted lexicographicall
enable_sort: true
_help_autosort:
- If true, the parsers may infer whether or not an argument
- list is sortable (without annotation).
autosort: true
_help_require_valid_layout:
- By default, if cmake-format cannot successfully fit
- everything into the desired linewidth it will apply the
- last, most aggressive attempt that it made. If this flag is
- True, however, cmake-format will print error, exit with non-
- zero status code, and write-out nothing
require_valid_layout: false
_help_layout_passes:
- A dictionary mapping layout nodes to a list of wrap
- decisions. See the documentation for more information.
layout_passes: {}
_help_markup: Options affecting comment reflow and formatting.
markup:
_help_bullet_char:
- What character to use for bulleted lists
bullet_char: "-"
_help_enum_char:
- What character to use as punctuation after numerals in an
- enumerated list
enum_char: .
_help_first_comment_is_literal:
- If comment markup is enabled, don't reflow the first comment
- block in each listfile. Use this to preserve formatting of
- your copyright/license statements.
first_comment_is_literal: false
_help_literal_comment_pattern:
- If comment markup is enabled, don't reflow any comment block
- which matches this (regex) pattern. Default is `None`
- (disabled).
literal_comment_pattern: null
_help_fence_pattern:
- Regular expression to match preformat fences in comments
- default= ``r'^\s*([`~]{3}[`~]*)(.*)$'``
fence_pattern: ^\s*([`~]{3}[`~]*)(.*)$
_help_ruler_pattern:
- Regular expression to match rulers in comments default=
- '``r''^\s*[^\w\s]{3}.*[^\w\s]{3}$''``'
ruler_pattern: ^\s*[^\w\s]{3}.*[^\w\s]{3}$
_help_explicit_trailing_pattern:
- If a comment line matches starts with this pattern then it
- is explicitly a trailing comment for the preceding
- argument. Default is '#<'
explicit_trailing_pattern: "#<"
_help_hashruler_min_length:
- If a comment line starts with at least this many consecutive
- hash characters, then don't lstrip() them off. This allows
- for lazy hash rulers where the first hash char is not
- separated by space
hashruler_min_length: 10
_help_canonicalize_hashrulers:
- If true, then insert a space between the first hash char and
- remaining hash chars in a hash ruler, and normalize its
- length to fill the column
canonicalize_hashrulers: true
_help_enable_markup:
- enable comment markup parsing and reflow
enable_markup: true
_help_lint: Options affecting the linter
lint:
_help_disabled_codes:
- a list of lint codes to disable
disabled_codes: []
_help_function_pattern:
- regular expression pattern describing valid function names
function_pattern: "[0-9a-z_]+"
_help_macro_pattern:
- regular expression pattern describing valid macro names
macro_pattern: "[0-9A-Z_]+"
_help_global_var_pattern:
- regular expression pattern describing valid names for
- variables with global (cache) scope
global_var_pattern: "[A-Z][0-9A-Z_]+"
_help_internal_var_pattern:
- regular expression pattern describing valid names for
- variables with global scope (but internal semantic)
internal_var_pattern: _[A-Z][0-9A-Z_]+
_help_local_var_pattern:
- regular expression pattern describing valid names for
- variables with local scope
local_var_pattern: "[a-z][a-z0-9_]+"
_help_private_var_pattern:
- regular expression pattern describing valid names for
- privatedirectory variables
private_var_pattern: _[0-9a-z_]+
_help_public_var_pattern:
- regular expression pattern describing valid names for public
- directory variables
public_var_pattern: "[A-Z][0-9A-Z_]+"
_help_argument_var_pattern:
- regular expression pattern describing valid names for
- function/macro arguments and loop variables.
argument_var_pattern: "[a-z][a-z0-9_]+"
_help_keyword_pattern:
- regular expression pattern describing valid names for
- keywords used in functions or macros
keyword_pattern: "[A-Z][0-9A-Z_]+"
_help_max_conditionals_custom_parser:
- In the heuristic for C0201, how many conditionals to match
- within a loop in before considering the loop a parser.
max_conditionals_custom_parser: 2
_help_min_statement_spacing:
- Require at least this many newlines between statements
min_statement_spacing: 1
_help_max_statement_spacing:
- Require no more than this many newlines between statements
max_statement_spacing: 2
max_returns: 6
max_branches: 12
max_arguments: 5
max_localvars: 15
max_statements: 50
_help_encode: Options affecting file encoding
encode:
_help_emit_byteorder_mark:
- If true, emit the unicode byte-order mark (BOM) at the start
- of the file
emit_byteorder_mark: false
_help_input_encoding:
- Specify the encoding of the input file. Defaults to utf-8
input_encoding: utf-8
_help_output_encoding:
- Specify the encoding of the output file. Defaults to utf-8.
- Note that cmake only claims to support utf-8 so be careful
- when using anything else
output_encoding: utf-8
_help_misc: Miscellaneous configurations options.
misc:
_help_per_command:
- A dictionary containing any per-command configuration
- overrides. Currently only `command_case` is supported.
per_command: {}

View File

@@ -101,6 +101,7 @@ words:
- gpgcheck
- gpgkey
- hotwallet
- hwrap
- ifndef
- inequation
- insuf
@@ -114,6 +115,8 @@ words:
- keylet
- keylets
- keyvadb
- kwarg
- kwargs
- ledgerentry
- ledgerhash
- ledgerindex
@@ -163,6 +166,7 @@ words:
- nunl
- Nyffenegger
- ostr
- pargs
- partitioner
- paychan
- paychans

View File

@@ -26,6 +26,24 @@ repos:
args: [--style=file]
"types_or": [c++, c, proto]
- repo: https://github.com/cheshirekow/cmake-format-precommit
rev: e2c2116d86a80e72e7146a06e68b7c228afc6319 # frozen: v0.6.13
hooks:
- id: cmake-format
additional_dependencies: [PyYAML]
exclude: |
(?x)^(
cmake/CodeCoverage.cmake|
cmake/XrplCore.cmake|
cmake/XrplDocs.cmake|
cmake/XrplInstall.cmake|
cmake/add_module.cmake|
cmake/isolate_headers.cmake|
cmake/target_link_modules.cmake|
cmake/target_protobuf_sources.cmake|
tests/conan/CMakeLists.txt
)$
- repo: https://github.com/rbubley/mirrors-prettier
rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2
hooks:

View File

@@ -1,16 +1,16 @@
cmake_minimum_required(VERSION 3.16)
if(POLICY CMP0074)
cmake_policy(SET CMP0074 NEW)
endif()
if(POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif()
if (POLICY CMP0074)
cmake_policy(SET CMP0074 NEW)
endif ()
if (POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif ()
# Fix "unrecognized escape" issues when passing CMAKE_MODULE_PATH on Windows.
if(DEFINED CMAKE_MODULE_PATH)
file(TO_CMAKE_PATH "${CMAKE_MODULE_PATH}" CMAKE_MODULE_PATH)
endif()
if (DEFINED CMAKE_MODULE_PATH)
file(TO_CMAKE_PATH "${CMAKE_MODULE_PATH}" CMAKE_MODULE_PATH)
endif ()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
project(xrpl)
@@ -20,65 +20,65 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(CompilationEnv)
if(is_gcc)
if (is_gcc)
# GCC-specific fixes
add_compile_options(-Wno-unknown-pragmas -Wno-subobject-linkage)
# -Wno-subobject-linkage can be removed when we upgrade GCC version to at least 13.3
elseif(is_clang)
elseif (is_clang)
# Clang-specific fixes
add_compile_options(-Wno-unknown-warning-option) # Ignore unknown warning options
elseif(is_msvc)
elseif (is_msvc)
# MSVC-specific fixes
add_compile_options(/wd4068) # Ignore unknown pragmas
endif()
endif ()
# Enable ccache to speed up builds.
include(Ccache)
# make GIT_COMMIT_HASH define available to all sources
find_package(Git)
if(Git_FOUND)
if (Git_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
if(gch)
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
if (gch)
set(GIT_COMMIT_HASH "${gch}")
message(STATUS gch: ${GIT_COMMIT_HASH})
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
endif()
endif ()
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse --abbrev-ref HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gb)
if(gb)
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gb)
if (gb)
set(GIT_BRANCH "${gb}")
message(STATUS gb: ${GIT_BRANCH})
add_definitions(-DGIT_BRANCH="${GIT_BRANCH}")
endif()
endif() #git
endif ()
endif () # git
if(thread_safety_analysis)
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DXRPL_ENABLE_THREAD_SAFETY_ANNOTATIONS)
add_compile_options("-stdlib=libc++")
add_link_options("-stdlib=libc++")
endif()
if (thread_safety_analysis)
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS
-DXRPL_ENABLE_THREAD_SAFETY_ANNOTATIONS)
add_compile_options("-stdlib=libc++")
add_link_options("-stdlib=libc++")
endif ()
include (CheckCXXCompilerFlag)
include (FetchContent)
include (ExternalProject)
include (CMakeFuncs) # must come *after* ExternalProject b/c it overrides one function in EP
include(CheckCXXCompilerFlag)
include(FetchContent)
include(ExternalProject)
include(CMakeFuncs) # must come *after* ExternalProject b/c it overrides one function in EP
if (target)
message (FATAL_ERROR "The target option has been removed - use native cmake options to control build")
message(FATAL_ERROR "The target option has been removed - use native cmake options to control build")
endif ()
include(XrplSanity)
include(XrplVersion)
include(XrplSettings)
# this check has to remain in the top-level cmake
# because of the early return statement
# this check has to remain in the top-level cmake because of the early return statement
if (packages_only)
if (NOT TARGET rpm)
message (FATAL_ERROR "packages_only requested, but targets were not created - is docker installed?")
endif()
return ()
if (NOT TARGET rpm)
message(FATAL_ERROR "packages_only requested, but targets were not created - is docker installed?")
endif ()
return()
endif ()
include(XrplCompiler)
include(XrplSanitizers)
@@ -86,11 +86,9 @@ include(XrplInterface)
option(only_docs "Include only the docs target?" FALSE)
include(XrplDocs)
if(only_docs)
return()
endif()
###
if (only_docs)
return()
endif ()
include(deps/Boost)
@@ -107,45 +105,43 @@ find_package(SOCI REQUIRED)
find_package(SQLite3 REQUIRED)
find_package(xxHash REQUIRED)
target_link_libraries(xrpl_libs INTERFACE
ed25519::ed25519
lz4::lz4
OpenSSL::Crypto
OpenSSL::SSL
secp256k1::secp256k1
soci::soci
SQLite::SQLite3
)
target_link_libraries(
xrpl_libs
INTERFACE ed25519::ed25519
lz4::lz4
OpenSSL::Crypto
OpenSSL::SSL
secp256k1::secp256k1
soci::soci
SQLite::SQLite3)
option(rocksdb "Enable RocksDB" ON)
if(rocksdb)
if (rocksdb)
find_package(RocksDB REQUIRED)
set_target_properties(RocksDB::rocksdb PROPERTIES
INTERFACE_COMPILE_DEFINITIONS XRPL_ROCKSDB_AVAILABLE=1
)
set_target_properties(RocksDB::rocksdb PROPERTIES INTERFACE_COMPILE_DEFINITIONS XRPL_ROCKSDB_AVAILABLE=1)
target_link_libraries(xrpl_libs INTERFACE RocksDB::rocksdb)
endif()
endif ()
# Work around changes to Conan recipe for now.
if(TARGET nudb::core)
set(nudb nudb::core)
elseif(TARGET NuDB::nudb)
set(nudb NuDB::nudb)
else()
message(FATAL_ERROR "unknown nudb target")
endif()
if (TARGET nudb::core)
set(nudb nudb::core)
elseif (TARGET NuDB::nudb)
set(nudb NuDB::nudb)
else ()
message(FATAL_ERROR "unknown nudb target")
endif ()
target_link_libraries(xrpl_libs INTERFACE ${nudb})
if(coverage)
include(XrplCov)
endif()
if (coverage)
include(XrplCov)
endif ()
set(PROJECT_EXPORT_SET XrplExports)
include(XrplCore)
include(XrplInstall)
include(XrplValidatorKeys)
if(tests)
include(CTest)
add_subdirectory(src/tests/libxrpl)
endif()
if (tests)
include(CTest)
add_subdirectory(src/tests/libxrpl)
endif ()

View File

@@ -940,7 +940,23 @@
#
# path Location to store the database
#
# Optional keys for NuDB and RocksDB:
# Optional keys
#
# cache_size Size of cache for database records. Default is 16384.
# Setting this value to 0 will use the default value.
#
# cache_age Length of time in minutes to keep database records
# cached. Default is 5 minutes. Setting this value to
# 0 will use the default value.
#
# Note: if neither cache_size nor cache_age is
# specified, the cache for database records will not
# be created. If only one of cache_size or cache_age
# is specified, the cache will be created using the
# default value for the unspecified parameter.
#
# Note: the cache will not be created if online_delete
# is specified.
#
# fast_load Boolean. If set, load the last persisted ledger
# from disk upon process start before syncing to
@@ -948,6 +964,8 @@
# if sufficient IOPS capacity is available.
# Default 0.
#
# Optional keys for NuDB or RocksDB:
#
# earliest_seq The default is 32570 to match the XRP ledger
# network's earliest allowed sequence. Alternate
# networks may set this value. Minimum value of 1.

View File

@@ -1,30 +1,29 @@
macro (exclude_from_default target_)
set_target_properties (${target_} PROPERTIES EXCLUDE_FROM_ALL ON)
set_target_properties (${target_} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD ON)
set_target_properties(${target_} PROPERTIES EXCLUDE_FROM_ALL ON)
set_target_properties(${target_} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD ON)
endmacro ()
macro (exclude_if_included target_)
get_directory_property(has_parent PARENT_DIRECTORY)
if (has_parent)
exclude_from_default (${target_})
endif ()
get_directory_property(has_parent PARENT_DIRECTORY)
if (has_parent)
exclude_from_default(${target_})
endif ()
endmacro ()
find_package(Git)
function (git_branch branch_val)
if (NOT GIT_FOUND)
return ()
endif ()
set (_branch "")
execute_process (COMMAND ${GIT_EXECUTABLE} "rev-parse" "--abbrev-ref" "HEAD"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE _git_exit_code
OUTPUT_VARIABLE _temp_branch
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
if (_git_exit_code EQUAL 0)
set (_branch ${_temp_branch})
endif ()
set (${branch_val} "${_branch}" PARENT_SCOPE)
if (NOT GIT_FOUND)
return()
endif ()
set(_branch "")
execute_process(COMMAND ${GIT_EXECUTABLE} "rev-parse" "--abbrev-ref" "HEAD"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE _git_exit_code
OUTPUT_VARIABLE _temp_branch
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if (_git_exit_code EQUAL 0)
set(_branch ${_temp_branch})
endif ()
set(${branch_val} "${_branch}" PARENT_SCOPE)
endfunction ()

View File

@@ -15,18 +15,17 @@ endif ()
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio#usage-with-cmake.
if ("${CCACHE_PATH}" MATCHES "chocolatey")
message(DEBUG "Ccache path: ${CCACHE_PATH}")
# Chocolatey uses a shim executable that we cannot use directly, in which
# case we have to find the executable it points to. If we cannot find the
# target executable then we cannot use ccache.
# Chocolatey uses a shim executable that we cannot use directly, in which case we have to find the executable it
# points to. If we cannot find the target executable then we cannot use ccache.
find_program(BASH_PATH "bash")
if (NOT BASH_PATH)
message(WARNING "Could not find bash.")
return()
endif ()
execute_process(
COMMAND bash -c "export LC_ALL='en_US.UTF-8'; ${CCACHE_PATH} --shimgen-noop | grep -oP 'path to executable: \\K.+' | head -c -1"
OUTPUT_VARIABLE CCACHE_PATH)
execute_process(COMMAND bash -c
"export LC_ALL='en_US.UTF-8'; ${CCACHE_PATH} --shimgen-noop | grep -oP 'path to executable: \\K.+' | head -c -1"
OUTPUT_VARIABLE CCACHE_PATH)
if (NOT CCACHE_PATH)
message(WARNING "Could not find ccache target.")
@@ -37,21 +36,14 @@ endif ()
message(STATUS "Found ccache: ${CCACHE_PATH}")
# Tell cmake to use ccache for compiling with Visual Studio.
file(COPY_FILE
${CCACHE_PATH} ${CMAKE_BINARY_DIR}/cl.exe
ONLY_IF_DIFFERENT)
set(CMAKE_VS_GLOBALS
"CLToolExe=cl.exe"
"CLToolPath=${CMAKE_BINARY_DIR}"
"TrackFileAccess=false"
"UseMultiToolTask=true")
file(COPY_FILE ${CCACHE_PATH} ${CMAKE_BINARY_DIR}/cl.exe ONLY_IF_DIFFERENT)
set(CMAKE_VS_GLOBALS "CLToolExe=cl.exe" "CLToolPath=${CMAKE_BINARY_DIR}" "TrackFileAccess=false"
"UseMultiToolTask=true")
# By default Visual Studio generators will use /Zi to capture debug information,
# which is not compatible with ccache, so tell it to use /Z7 instead.
# By default Visual Studio generators will use /Zi to capture debug information, which is not compatible with ccache, so
# tell it to use /Z7 instead.
if (MSVC)
foreach (var_
CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE)
string (REPLACE "/Zi" "/Z7" ${var_} "${${var_}}")
foreach (var_ CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE)
string(REPLACE "/Zi" "/Z7" ${var_} "${${var_}}")
endforeach ()
endif ()

View File

@@ -1,8 +1,7 @@
# Shared detection of compiler, operating system, and architecture.
#
# This module centralizes environment detection so that other
# CMake modules can use the same variables instead of repeating
# checks on CMAKE_* and built-in platform variables.
# Shared detection of compiler, operating system, and architecture.
#
# This module centralizes environment detection so that other CMake modules can use the same variables instead of
# repeating checks on CMAKE_* and built-in platform variables.
# Only run once per configure step.
include_guard(GLOBAL)
@@ -15,21 +14,20 @@ set(is_gcc FALSE)
set(is_msvc FALSE)
set(is_xcode FALSE)
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang
set(is_clang TRUE)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(is_gcc TRUE)
elseif(MSVC)
set(is_msvc TRUE)
else()
message(FATAL_ERROR "Unsupported C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang
set(is_clang TRUE)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(is_gcc TRUE)
elseif (MSVC)
set(is_msvc TRUE)
else ()
message(FATAL_ERROR "Unsupported C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
endif ()
# Xcode generator detection
if(CMAKE_GENERATOR STREQUAL "Xcode")
set(is_xcode TRUE)
endif()
if (CMAKE_GENERATOR STREQUAL "Xcode")
set(is_xcode TRUE)
endif ()
# --------------------------------------------------------------------
# Operating system detection
@@ -38,23 +36,23 @@ set(is_linux FALSE)
set(is_windows FALSE)
set(is_macos FALSE)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(is_linux TRUE)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(is_windows TRUE)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(is_macos TRUE)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(is_linux TRUE)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(is_windows TRUE)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(is_macos TRUE)
endif ()
# --------------------------------------------------------------------
# Architecture
# --------------------------------------------------------------------
set(is_amd64 FALSE)
set(is_arm64 FALSE)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
set(is_amd64 TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
set(is_arm64 TRUE)
else()
message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
set(is_amd64 TRUE)
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
set(is_arm64 TRUE)
else ()
message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
endif ()

View File

@@ -1,25 +1,16 @@
include(isolate_headers)
function(xrpl_add_test name)
set(target ${PROJECT_NAME}.test.${name})
function (xrpl_add_test name)
set(target ${PROJECT_NAME}.test.${name})
file(GLOB_RECURSE sources CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp"
)
add_executable(${target} ${ARGN} ${sources})
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp")
add_executable(${target} ${ARGN} ${sources})
isolate_headers(
${target}
"${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/tests/${name}"
PRIVATE
)
isolate_headers(${target} "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/tests/${name}" PRIVATE)
# Make sure the test isn't optimized away in unity builds
set_target_properties(${target} PROPERTIES
UNITY_BUILD_MODE GROUP
UNITY_BUILD_BATCH_SIZE 0) # Adjust as needed
# Make sure the test isn't optimized away in unity builds
set_target_properties(${target} PROPERTIES UNITY_BUILD_MODE GROUP UNITY_BUILD_BATCH_SIZE 0) # Adjust as needed
add_test(NAME ${target} COMMAND ${target})
endfunction()
add_test(NAME ${target} COMMAND ${target})
endfunction ()

View File

@@ -8,153 +8,155 @@ include(CompilationEnv)
TODO some/most of these common settings belong in a
toolchain file, especially the ABI-impacting ones
#]=========================================================]
add_library (common INTERFACE)
add_library (Xrpl::common ALIAS common)
add_library(common INTERFACE)
add_library(Xrpl::common ALIAS common)
include(XrplSanitizers)
# add a single global dependency on this interface lib
link_libraries (Xrpl::common)
link_libraries(Xrpl::common)
# Respect CMAKE_POSITION_INDEPENDENT_CODE setting (may be set by Conan toolchain)
if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
set_target_properties (common
PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ${CMAKE_POSITION_INDEPENDENT_CODE})
if (NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif ()
set_target_properties(common PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ${CMAKE_POSITION_INDEPENDENT_CODE})
set(CMAKE_CXX_EXTENSIONS OFF)
target_compile_definitions (common
INTERFACE
$<$<CONFIG:Debug>:DEBUG _DEBUG>
#[===[
target_compile_definitions(
common
INTERFACE $<$<CONFIG:Debug>:DEBUG
_DEBUG>
#[===[
NOTE: CMAKE release builds already have NDEBUG defined, so no need to add it
explicitly except for the special case of (profile ON) and (assert OFF).
Presumably this is because we don't want profile builds asserting unless
asserts were specifically requested.
]===]
$<$<AND:$<BOOL:${profile}>,$<NOT:$<BOOL:${assert}>>>:NDEBUG>
# TODO: Remove once we have migrated functions from OpenSSL 1.x to 3.x.
OPENSSL_SUPPRESS_DEPRECATED
)
$<$<AND:$<BOOL:${profile}>,$<NOT:$<BOOL:${assert}>>>:NDEBUG>
# TODO: Remove once we have migrated functions from OpenSSL 1.x to 3.x.
OPENSSL_SUPPRESS_DEPRECATED)
if (MSVC)
# remove existing exception flag since we set it to -EHa
string (REGEX REPLACE "[-/]EH[a-z]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
# remove existing exception flag since we set it to -EHa
string(REGEX REPLACE "[-/]EH[a-z]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
foreach (var_
CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE)
foreach (var_ CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE)
# also remove dynamic runtime
string (REGEX REPLACE "[-/]MD[d]*" " " ${var_} "${${var_}}")
# also remove dynamic runtime
string(REGEX REPLACE "[-/]MD[d]*" " " ${var_} "${${var_}}")
# /ZI (Edit & Continue debugging information) is incompatible with Gy-
string (REPLACE "/ZI" "/Zi" ${var_} "${${var_}}")
# /ZI (Edit & Continue debugging information) is incompatible with Gy-
string(REPLACE "/ZI" "/Zi" ${var_} "${${var_}}")
# omit debug info completely under CI (not needed)
if (is_ci)
string (REPLACE "/Zi" " " ${var_} "${${var_}}")
string (REPLACE "/Z7" " " ${var_} "${${var_}}")
endif ()
endforeach ()
# omit debug info completely under CI (not needed)
if (is_ci)
string(REPLACE "/Zi" " " ${var_} "${${var_}}")
string(REPLACE "/Z7" " " ${var_} "${${var_}}")
endif ()
endforeach ()
target_compile_options (common
INTERFACE
-bigobj # Increase object file max size
-fp:precise # Floating point behavior
-Gd # __cdecl calling convention
-Gm- # Minimal rebuild: disabled
-Gy- # Function level linking: disabled
-MP # Multiprocessor compilation
-openmp- # pragma omp: disabled
-errorReport:none # No error reporting to Internet
-nologo # Suppress login banner
-wd4018 # Disable signed/unsigned comparison warnings
-wd4244 # Disable float to int possible loss of data warnings
-wd4267 # Disable size_t to T possible loss of data warnings
-wd4800 # Disable C4800(int to bool performance)
-wd4503 # Decorated name length exceeded, name was truncated
$<$<COMPILE_LANGUAGE:CXX>:
-EHa
-GR
>
$<$<CONFIG:Release>:-Ox>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:
-GS
-Zc:forScope
>
# static runtime
$<$<CONFIG:Debug>:-MTd>
$<$<NOT:$<CONFIG:Debug>>:-MT>
$<$<BOOL:${werr}>:-WX>
)
target_compile_definitions (common
INTERFACE
_WIN32_WINNT=0x6000
_SCL_SECURE_NO_WARNINGS
_CRT_SECURE_NO_WARNINGS
WIN32_CONSOLE
WIN32_LEAN_AND_MEAN
NOMINMAX
# TODO: Resolve these warnings, don't just silence them
_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:_CRTDBG_MAP_ALLOC>)
target_link_libraries (common
INTERFACE
-errorreport:none
-machine:X64)
target_compile_options(
common
INTERFACE # Increase object file max size
-bigobj
# Floating point behavior
-fp:precise
# __cdecl calling convention
-Gd
# Minimal rebuild: disabled
-Gm-
# Function level linking: disabled
-Gy-
# Multiprocessor compilation
-MP
# pragma omp: disabled
-openmp-
# No error reporting to Internet
-errorReport:none
# Suppress login banner
-nologo
# Disable signed/unsigned comparison warnings
-wd4018
# Disable float to int possible loss of data warnings
-wd4244
# Disable size_t to T possible loss of data warnings
-wd4267
# Disable C4800(int to bool performance)
-wd4800
# Decorated name length exceeded, name was truncated
-wd4503
$<$<COMPILE_LANGUAGE:CXX>:
-EHa
-GR
>
$<$<CONFIG:Release>:-Ox>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:
-GS
-Zc:forScope
>
# static runtime
$<$<CONFIG:Debug>:-MTd>
$<$<NOT:$<CONFIG:Debug>>:-MT>
$<$<BOOL:${werr}>:-WX>)
target_compile_definitions(
common
INTERFACE _WIN32_WINNT=0x6000
_SCL_SECURE_NO_WARNINGS
_CRT_SECURE_NO_WARNINGS
WIN32_CONSOLE
WIN32_LEAN_AND_MEAN
NOMINMAX
# TODO: Resolve these warnings, don't just silence them
_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:_CRTDBG_MAP_ALLOC>)
target_link_libraries(common INTERFACE -errorreport:none -machine:X64)
else ()
target_compile_options (common
INTERFACE
-Wall
-Wdeprecated
$<$<BOOL:${is_clang}>:-Wno-deprecated-declarations>
$<$<BOOL:${wextra}>:-Wextra -Wno-unused-parameter>
$<$<BOOL:${werr}>:-Werror>
-fstack-protector
-Wno-sign-compare
-Wno-unused-but-set-variable
$<$<NOT:$<CONFIG:Debug>>:-fno-strict-aliasing>
# tweak gcc optimization for debug
$<$<AND:$<BOOL:${is_gcc}>,$<CONFIG:Debug>>:-O0>
# Add debug symbols to release config
$<$<CONFIG:Release>:-g>)
target_link_libraries (common
INTERFACE
-rdynamic
$<$<BOOL:${is_linux}>:-Wl,-z,relro,-z,now,--build-id>
# link to static libc/c++ iff:
# * static option set and
# * NOT APPLE (AppleClang does not support static libc/c++) and
# * NOT SANITIZERS (sanitizers typically don't work with static libc/c++)
$<$<AND:$<BOOL:${static}>,$<NOT:$<BOOL:${APPLE}>>,$<NOT:$<BOOL:${SANITIZERS_ENABLED}>>>:
-static-libstdc++
-static-libgcc
>)
target_compile_options(
common
INTERFACE -Wall
-Wdeprecated
$<$<BOOL:${is_clang}>:-Wno-deprecated-declarations>
$<$<BOOL:${wextra}>:-Wextra
-Wno-unused-parameter>
$<$<BOOL:${werr}>:-Werror>
-fstack-protector
-Wno-sign-compare
-Wno-unused-but-set-variable
$<$<NOT:$<CONFIG:Debug>>:-fno-strict-aliasing>
# tweak gcc optimization for debug
$<$<AND:$<BOOL:${is_gcc}>,$<CONFIG:Debug>>:-O0>
# Add debug symbols to release config
$<$<CONFIG:Release>:-g>)
target_link_libraries(
common
INTERFACE -rdynamic
$<$<BOOL:${is_linux}>:-Wl,-z,relro,-z,now,--build-id>
# link to static libc/c++ iff: * static option set and * NOT APPLE (AppleClang does not support static
# libc/c++) and * NOT SANITIZERS (sanitizers typically don't work with static libc/c++)
$<$<AND:$<BOOL:${static}>,$<NOT:$<BOOL:${APPLE}>>,$<NOT:$<BOOL:${SANITIZERS_ENABLED}>>>:
-static-libstdc++
-static-libgcc
>)
endif ()
# Antithesis instrumentation will only be built and deployed using machines running Linux.
if (voidstar)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(FATAL_ERROR "Antithesis instrumentation requires Debug build type, aborting...")
elseif (NOT is_linux)
message(FATAL_ERROR "Antithesis instrumentation requires Linux, aborting...")
elseif (NOT (is_clang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0))
message(FATAL_ERROR "Antithesis instrumentation requires Clang version 16 or later, aborting...")
endif ()
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(FATAL_ERROR "Antithesis instrumentation requires Debug build type, aborting...")
elseif (NOT is_linux)
message(FATAL_ERROR "Antithesis instrumentation requires Linux, aborting...")
elseif (NOT (is_clang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0))
message(FATAL_ERROR "Antithesis instrumentation requires Clang version 16 or later, aborting...")
endif ()
endif ()
if (use_mold)
# use mold linker if available
execute_process (
COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=mold -Wl,--version
ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
if ("${LD_VERSION}" MATCHES "mold")
target_link_libraries (common INTERFACE -fuse-ld=mold)
endif ()
unset (LD_VERSION)
# use mold linker if available
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=mold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
if ("${LD_VERSION}" MATCHES "mold")
target_link_libraries(common INTERFACE -fuse-ld=mold)
endif ()
unset(LD_VERSION)
elseif (use_gold AND is_gcc)
# use gold linker if available
execute_process (
COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=gold -Wl,--version
ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
# use gold linker if available
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
#[=========================================================[
NOTE: THE gold linker inserts -rpath as DT_RUNPATH by
default instead of DT_RPATH, so you might have slightly
@@ -168,34 +170,31 @@ elseif (use_gold AND is_gcc)
disabling would be to figure out all the settings
required to make gold play nicely with jemalloc.
#]=========================================================]
if (("${LD_VERSION}" MATCHES "GNU gold") AND (NOT jemalloc))
target_link_libraries (common
INTERFACE
-fuse-ld=gold
-Wl,--no-as-needed
#[=========================================================[
if (("${LD_VERSION}" MATCHES "GNU gold") AND (NOT jemalloc))
target_link_libraries(
common
INTERFACE -fuse-ld=gold
-Wl,--no-as-needed
#[=========================================================[
see https://bugs.launchpad.net/ubuntu/+source/eglibc/+bug/1253638/comments/5
DT_RUNPATH does not work great for transitive
dependencies (of which boost has a few) - so just
switch to DT_RPATH if doing dynamic linking with gold
#]=========================================================]
$<$<NOT:$<BOOL:${static}>>:-Wl,--disable-new-dtags>)
endif ()
unset (LD_VERSION)
$<$<NOT:$<BOOL:${static}>>:-Wl,--disable-new-dtags>)
endif ()
unset(LD_VERSION)
elseif (use_lld)
# use lld linker if available
execute_process (
COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=lld -Wl,--version
ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
if ("${LD_VERSION}" MATCHES "LLD")
target_link_libraries (common INTERFACE -fuse-ld=lld)
endif ()
unset (LD_VERSION)
endif()
# use lld linker if available
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=lld -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
if ("${LD_VERSION}" MATCHES "LLD")
target_link_libraries(common INTERFACE -fuse-ld=lld)
endif ()
unset(LD_VERSION)
endif ()
if (assert)
foreach (var_ CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE)
STRING (REGEX REPLACE "[-/]DNDEBUG" "" ${var_} "${${var_}}")
endforeach ()
foreach (var_ CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE)
string(REGEX REPLACE "[-/]DNDEBUG" "" ${var_} "${${var_}}")
endforeach ()
endif ()

View File

@@ -1,54 +1,52 @@
include (CMakeFindDependencyMacro)
include(CMakeFindDependencyMacro)
# need to represent system dependencies of the lib here
#[=========================================================[
Boost
#]=========================================================]
if (static OR APPLE OR MSVC)
set (Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_LIBS ON)
endif ()
set (Boost_USE_MULTITHREADED ON)
set(Boost_USE_MULTITHREADED ON)
if (static OR MSVC)
set (Boost_USE_STATIC_RUNTIME ON)
set(Boost_USE_STATIC_RUNTIME ON)
else ()
set (Boost_USE_STATIC_RUNTIME OFF)
set(Boost_USE_STATIC_RUNTIME OFF)
endif ()
find_dependency (Boost
COMPONENTS
chrono
container
context
coroutine
date_time
filesystem
program_options
regex
system
thread)
find_dependency(Boost
COMPONENTS
chrono
container
context
coroutine
date_time
filesystem
program_options
regex
system
thread)
#[=========================================================[
OpenSSL
#]=========================================================]
if (NOT DEFINED OPENSSL_ROOT_DIR)
if (DEFINED ENV{OPENSSL_ROOT})
set (OPENSSL_ROOT_DIR $ENV{OPENSSL_ROOT})
elseif (APPLE)
find_program (homebrew brew)
if (homebrew)
execute_process (COMMAND ${homebrew} --prefix openssl
OUTPUT_VARIABLE OPENSSL_ROOT_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
if (DEFINED ENV{OPENSSL_ROOT})
set(OPENSSL_ROOT_DIR $ENV{OPENSSL_ROOT})
elseif (APPLE)
find_program(homebrew brew)
if (homebrew)
execute_process(COMMAND ${homebrew} --prefix openssl OUTPUT_VARIABLE OPENSSL_ROOT_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif ()
endif ()
endif ()
file (TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" OPENSSL_ROOT_DIR)
file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" OPENSSL_ROOT_DIR)
endif ()
if (static OR APPLE OR MSVC)
set (OPENSSL_USE_STATIC_LIBS ON)
set(OPENSSL_USE_STATIC_LIBS ON)
endif ()
set (OPENSSL_MSVC_STATIC_RT ON)
find_dependency (OpenSSL REQUIRED)
find_dependency (ZLIB)
find_dependency (date)
set(OPENSSL_MSVC_STATIC_RT ON)
find_dependency(OpenSSL REQUIRED)
find_dependency(ZLIB)
find_dependency(date)
if (TARGET ZLIB::ZLIB)
set_target_properties(OpenSSL::Crypto PROPERTIES
INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
set_target_properties(OpenSSL::Crypto PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
endif ()

View File

@@ -2,40 +2,51 @@
coverage report target
#]===================================================================]
if(NOT coverage)
message(FATAL_ERROR "Code coverage not enabled! Aborting ...")
endif()
if (NOT coverage)
message(FATAL_ERROR "Code coverage not enabled! Aborting ...")
endif ()
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
message(WARNING "Code coverage on Windows is not supported, ignoring 'coverage' flag")
return()
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
message(WARNING "Code coverage on Windows is not supported, ignoring 'coverage' flag")
return()
endif ()
include(ProcessorCount)
ProcessorCount(PROCESSOR_COUNT)
include(CodeCoverage)
# The instructions for these commands come from the `CodeCoverage` module,
# which was copied from https://github.com/bilke/cmake-modules, commit fb7d2a3,
# then locally changed (see CHANGES: section in `CodeCoverage.cmake`)
# The instructions for these commands come from the `CodeCoverage` module, which was copied from
# https://github.com/bilke/cmake-modules, commit fb7d2a3, then locally changed (see CHANGES: section in
# `CodeCoverage.cmake`)
set(GCOVR_ADDITIONAL_ARGS ${coverage_extra_args})
if(NOT GCOVR_ADDITIONAL_ARGS STREQUAL "")
separate_arguments(GCOVR_ADDITIONAL_ARGS)
endif()
if (NOT GCOVR_ADDITIONAL_ARGS STREQUAL "")
separate_arguments(GCOVR_ADDITIONAL_ARGS)
endif ()
list(APPEND GCOVR_ADDITIONAL_ARGS
--exclude-throw-branches
--exclude-noncode-lines
--exclude-unreachable-branches -s
-j ${PROCESSOR_COUNT})
list(APPEND
GCOVR_ADDITIONAL_ARGS
--exclude-throw-branches
--exclude-noncode-lines
--exclude-unreachable-branches
-s
-j
${PROCESSOR_COUNT})
setup_target_for_coverage_gcovr(
NAME coverage
FORMAT ${coverage_format}
EXCLUDE "src/test" "src/tests" "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
DEPENDENCIES xrpld xrpl.tests
)
NAME
coverage
FORMAT
${coverage_format}
EXCLUDE
"src/test"
"src/tests"
"include/xrpl/beast/test"
"include/xrpl/beast/unit_test"
"${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
DEPENDENCIES
xrpld
xrpl.tests)
add_code_coverage_to_target(opts INTERFACE)

View File

@@ -5,84 +5,79 @@
include(CompilationEnv)
# Set defaults for optional variables to avoid uninitialized variable warnings
if(NOT DEFINED voidstar)
set(voidstar OFF)
endif()
if (NOT DEFINED voidstar)
set(voidstar OFF)
endif ()
add_library (opts INTERFACE)
add_library (Xrpl::opts ALIAS opts)
target_compile_definitions (opts
INTERFACE
BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
BOOST_CONTAINER_FWD_BAD_DEQUE
HAS_UNCAUGHT_EXCEPTIONS=1
$<$<BOOL:${boost_show_deprecated}>:
BOOST_ASIO_NO_DEPRECATED
BOOST_FILESYSTEM_NO_DEPRECATED
>
$<$<NOT:$<BOOL:${boost_show_deprecated}>>:
BOOST_COROUTINES_NO_DEPRECATION_WARNING
BOOST_BEAST_ALLOW_DEPRECATED
BOOST_FILESYSTEM_DEPRECATED
>
$<$<BOOL:${beast_no_unit_test_inline}>:BEAST_NO_UNIT_TEST_INLINE=1>
$<$<BOOL:${beast_disable_autolink}>:BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES=1>
$<$<BOOL:${single_io_service_thread}>:XRPL_SINGLE_IO_SERVICE_THREAD=1>
$<$<BOOL:${voidstar}>:ENABLE_VOIDSTAR>)
target_compile_options (opts
INTERFACE
$<$<AND:$<BOOL:${is_gcc}>,$<COMPILE_LANGUAGE:CXX>>:-Wsuggest-override>
$<$<BOOL:${is_gcc}>:-Wno-maybe-uninitialized>
$<$<BOOL:${perf}>:-fno-omit-frame-pointer>
$<$<BOOL:${profile}>:-pg>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
add_library(opts INTERFACE)
add_library(Xrpl::opts ALIAS opts)
target_compile_definitions(
opts
INTERFACE BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
BOOST_CONTAINER_FWD_BAD_DEQUE
HAS_UNCAUGHT_EXCEPTIONS=1
$<$<BOOL:${boost_show_deprecated}>:
BOOST_ASIO_NO_DEPRECATED
BOOST_FILESYSTEM_NO_DEPRECATED
>
$<$<NOT:$<BOOL:${boost_show_deprecated}>>:
BOOST_COROUTINES_NO_DEPRECATION_WARNING
BOOST_BEAST_ALLOW_DEPRECATED
BOOST_FILESYSTEM_DEPRECATED
>
$<$<BOOL:${beast_no_unit_test_inline}>:BEAST_NO_UNIT_TEST_INLINE=1>
$<$<BOOL:${beast_disable_autolink}>:BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES=1>
$<$<BOOL:${single_io_service_thread}>:XRPL_SINGLE_IO_SERVICE_THREAD=1>
$<$<BOOL:${voidstar}>:ENABLE_VOIDSTAR>)
target_compile_options(
opts
INTERFACE $<$<AND:$<BOOL:${is_gcc}>,$<COMPILE_LANGUAGE:CXX>>:-Wsuggest-override>
$<$<BOOL:${is_gcc}>:-Wno-maybe-uninitialized> $<$<BOOL:${perf}>:-fno-omit-frame-pointer>
$<$<BOOL:${profile}>:-pg> $<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
target_link_libraries (opts
INTERFACE
$<$<BOOL:${profile}>:-pg>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
target_link_libraries(opts INTERFACE $<$<BOOL:${profile}>:-pg> $<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
if(jemalloc)
find_package(jemalloc REQUIRED)
target_compile_definitions(opts INTERFACE PROFILE_JEMALLOC)
target_link_libraries(opts INTERFACE jemalloc::jemalloc)
if (jemalloc)
find_package(jemalloc REQUIRED)
target_compile_definitions(opts INTERFACE PROFILE_JEMALLOC)
target_link_libraries(opts INTERFACE jemalloc::jemalloc)
endif ()
#[===================================================================[
xrpld transitive library deps via an interface library
#]===================================================================]
add_library (xrpl_syslibs INTERFACE)
add_library (Xrpl::syslibs ALIAS xrpl_syslibs)
target_link_libraries (xrpl_syslibs
INTERFACE
$<$<BOOL:${is_msvc}>:
legacy_stdio_definitions.lib
Shlwapi
kernel32
user32
gdi32
winspool
comdlg32
advapi32
shell32
ole32
oleaut32
uuid
odbc32
odbccp32
crypt32
>
$<$<NOT:$<BOOL:${is_msvc}>>:dl>
$<$<NOT:$<OR:$<BOOL:${is_msvc}>,$<BOOL:${is_macos}>>>:rt>)
add_library(xrpl_syslibs INTERFACE)
add_library(Xrpl::syslibs ALIAS xrpl_syslibs)
target_link_libraries(
xrpl_syslibs
INTERFACE $<$<BOOL:${is_msvc}>:
legacy_stdio_definitions.lib
Shlwapi
kernel32
user32
gdi32
winspool
comdlg32
advapi32
shell32
ole32
oleaut32
uuid
odbc32
odbccp32
crypt32
>
$<$<NOT:$<BOOL:${is_msvc}>>:dl>
$<$<NOT:$<OR:$<BOOL:${is_msvc}>,$<BOOL:${is_macos}>>>:rt>)
if (NOT is_msvc)
set (THREADS_PREFER_PTHREAD_FLAG ON)
find_package (Threads)
target_link_libraries (xrpl_syslibs INTERFACE Threads::Threads)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads)
target_link_libraries(xrpl_syslibs INTERFACE Threads::Threads)
endif ()
add_library (xrpl_libs INTERFACE)
add_library (Xrpl::libs ALIAS xrpl_libs)
target_link_libraries (xrpl_libs INTERFACE Xrpl::syslibs)
add_library(xrpl_libs INTERFACE)
add_library(Xrpl::libs ALIAS xrpl_libs)
target_link_libraries(xrpl_libs INTERFACE Xrpl::syslibs)

View File

@@ -44,23 +44,23 @@ include(CompilationEnv)
# Read environment variable
set(SANITIZERS "")
if(DEFINED ENV{SANITIZERS})
set(SANITIZERS "$ENV{SANITIZERS}")
endif()
if (DEFINED ENV{SANITIZERS})
set(SANITIZERS "$ENV{SANITIZERS}")
endif ()
# Set SANITIZERS_ENABLED flag for use in other modules
if(SANITIZERS MATCHES "address|thread|undefinedbehavior")
if (SANITIZERS MATCHES "address|thread|undefinedbehavior")
set(SANITIZERS_ENABLED TRUE)
else()
else ()
set(SANITIZERS_ENABLED FALSE)
return()
endif()
endif ()
# Sanitizers are not supported on Windows/MSVC
if(is_msvc)
if (is_msvc)
message(FATAL_ERROR "Sanitizers are not supported on Windows/MSVC. "
"Please unset the SANITIZERS environment variable.")
endif()
"Please unset the SANITIZERS environment variable.")
endif ()
message(STATUS "Configuring sanitizers: ${SANITIZERS}")
@@ -74,24 +74,24 @@ set(san_list "${SANITIZERS}")
string(REPLACE "," ";" san_list "${san_list}")
separate_arguments(san_list)
foreach(san IN LISTS san_list)
if(san STREQUAL "address")
foreach (san IN LISTS san_list)
if (san STREQUAL "address")
set(enable_asan TRUE)
elseif(san STREQUAL "thread")
elseif (san STREQUAL "thread")
set(enable_tsan TRUE)
elseif(san STREQUAL "undefinedbehavior")
elseif (san STREQUAL "undefinedbehavior")
set(enable_ubsan TRUE)
else()
else ()
message(FATAL_ERROR "Unsupported sanitizer type: ${san}"
"Supported: address, thread, undefinedbehavior and their combinations.")
endif()
endforeach()
"Supported: address, thread, undefinedbehavior and their combinations.")
endif ()
endforeach ()
# Validate sanitizer compatibility
if(enable_asan AND enable_tsan)
if (enable_asan AND enable_tsan)
message(FATAL_ERROR "AddressSanitizer and ThreadSanitizer are incompatible and cannot be enabled simultaneously. "
"Use 'address' or 'thread', optionally with 'undefinedbehavior'.")
endif()
"Use 'address' or 'thread', optionally with 'undefinedbehavior'.")
endif ()
# Frame pointer is required for meaningful stack traces. Sanitizers recommend minimum of -O1 for reasonable performance
set(SANITIZERS_COMPILE_FLAGS "-fno-omit-frame-pointer" "-O1")
@@ -99,31 +99,31 @@ set(SANITIZERS_COMPILE_FLAGS "-fno-omit-frame-pointer" "-O1")
# Build the sanitizer flags list
set(SANITIZER_TYPES)
if(enable_asan)
if (enable_asan)
list(APPEND SANITIZER_TYPES "address")
elseif(enable_tsan)
elseif (enable_tsan)
list(APPEND SANITIZER_TYPES "thread")
endif()
endif ()
if(enable_ubsan)
if (enable_ubsan)
# UB sanitizer flags
list(APPEND SANITIZER_TYPES "undefined" "float-divide-by-zero")
if(is_clang)
# Clang supports additional UB checks. More info here https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
if (is_clang)
# Clang supports additional UB checks. More info here
# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
list(APPEND SANITIZER_TYPES "unsigned-integer-overflow")
endif()
endif()
endif ()
endif ()
# Configure code model for GCC on amd64
# Use large code model for ASAN to avoid relocation errors
# Use medium code model for TSAN (large is not compatible with TSAN)
# Configure code model for GCC on amd64 Use large code model for ASAN to avoid relocation errors Use medium code model
# for TSAN (large is not compatible with TSAN)
set(SANITIZERS_RELOCATION_FLAGS)
# Compiler-specific configuration
if(is_gcc)
# Disable mold, gold and lld linkers for GCC with sanitizers
# Use default linker (bfd/ld) which is more lenient with mixed code models
# This is needed since the size of instrumented binary exceeds the limits set by mold, lld and gold linkers
if (is_gcc)
# Disable mold, gold and lld linkers for GCC with sanitizers Use default linker (bfd/ld) which is more lenient with
# mixed code models This is needed since the size of instrumented binary exceeds the limits set by mold, lld and
# gold linkers
set(use_mold OFF CACHE BOOL "Use mold linker" FORCE)
set(use_gold OFF CACHE BOOL "Use gold linker" FORCE)
set(use_lld OFF CACHE BOOL "Use lld linker" FORCE)
@@ -132,17 +132,17 @@ if(is_gcc)
# Suppress false positive warnings in GCC with stringop-overflow
list(APPEND SANITIZERS_COMPILE_FLAGS "-Wno-stringop-overflow")
if(is_amd64 AND enable_asan)
if (is_amd64 AND enable_asan)
message(STATUS " Using large code model (-mcmodel=large)")
list(APPEND SANITIZERS_COMPILE_FLAGS "-mcmodel=large")
list(APPEND SANITIZERS_RELOCATION_FLAGS "-mcmodel=large")
elseif(enable_tsan)
elseif (enable_tsan)
# GCC doesn't support atomic_thread_fence with tsan. Suppress warnings.
list(APPEND SANITIZERS_COMPILE_FLAGS "-Wno-tsan")
message(STATUS " Using medium code model (-mcmodel=medium)")
list(APPEND SANITIZERS_COMPILE_FLAGS "-mcmodel=medium")
list(APPEND SANITIZERS_RELOCATION_FLAGS "-mcmodel=medium")
endif()
endif ()
# Join sanitizer flags with commas for -fsanitize option
list(JOIN SANITIZER_TYPES "," SANITIZER_TYPES_STR)
@@ -151,13 +151,12 @@ if(is_gcc)
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
set(SANITIZERS_LINK_FLAGS "${SANITIZERS_RELOCATION_FLAGS}" "-fsanitize=${SANITIZER_TYPES_STR}")
elseif(is_clang)
# Add ignorelist for Clang (GCC doesn't support this)
# Use CMAKE_SOURCE_DIR to get the path to the ignorelist
elseif (is_clang)
# Add ignorelist for Clang (GCC doesn't support this) Use CMAKE_SOURCE_DIR to get the path to the ignorelist
set(IGNORELIST_PATH "${CMAKE_SOURCE_DIR}/sanitizers/suppressions/sanitizer-ignorelist.txt")
if(NOT EXISTS "${IGNORELIST_PATH}")
if (NOT EXISTS "${IGNORELIST_PATH}")
message(FATAL_ERROR "Sanitizer ignorelist not found: ${IGNORELIST_PATH}")
endif()
endif ()
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize-ignorelist=${IGNORELIST_PATH}")
message(STATUS " Using sanitizer ignorelist: ${IGNORELIST_PATH}")
@@ -168,34 +167,31 @@ elseif(is_clang)
# Add sanitizer to compile and link flags
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
set(SANITIZERS_LINK_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
endif()
endif ()
message(STATUS " Compile flags: ${SANITIZERS_COMPILE_FLAGS}")
message(STATUS " Link flags: ${SANITIZERS_LINK_FLAGS}")
# Apply the sanitizer flags to the 'common' interface library
# This is the same library used by XrplCompiler.cmake
target_compile_options(common INTERFACE
$<$<COMPILE_LANGUAGE:CXX>:${SANITIZERS_COMPILE_FLAGS}>
$<$<COMPILE_LANGUAGE:C>:${SANITIZERS_COMPILE_FLAGS}>
)
# Apply the sanitizer flags to the 'common' interface library This is the same library used by XrplCompiler.cmake
target_compile_options(common INTERFACE $<$<COMPILE_LANGUAGE:CXX>:${SANITIZERS_COMPILE_FLAGS}>
$<$<COMPILE_LANGUAGE:C>:${SANITIZERS_COMPILE_FLAGS}>)
# Apply linker flags
target_link_options(common INTERFACE ${SANITIZERS_LINK_FLAGS})
# Define SANITIZERS macro for BuildInfo.cpp
set(sanitizers_list)
if(enable_asan)
if (enable_asan)
list(APPEND sanitizers_list "ASAN")
endif()
if(enable_tsan)
endif ()
if (enable_tsan)
list(APPEND sanitizers_list "TSAN")
endif()
if(enable_ubsan)
endif ()
if (enable_ubsan)
list(APPEND sanitizers_list "UBSAN")
endif()
endif ()
if(sanitizers_list)
if (sanitizers_list)
list(JOIN sanitizers_list "." sanitizers_str)
target_compile_definitions(common INTERFACE SANITIZERS=${sanitizers_str})
endif()
endif ()

View File

@@ -6,40 +6,39 @@ include(CompilationEnv)
get_property(is_multiconfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
set (CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
if (NOT is_multiconfig)
if (NOT CMAKE_BUILD_TYPE)
message (STATUS "Build type not specified - defaulting to Release")
set (CMAKE_BUILD_TYPE Release CACHE STRING "build type" FORCE)
elseif (NOT (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL Release))
# for simplicity, these are the only two config types we care about. Limiting
# the build types simplifies dealing with external project builds especially
message (FATAL_ERROR " *** Only Debug or Release build types are currently supported ***")
endif ()
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "Build type not specified - defaulting to Release")
set(CMAKE_BUILD_TYPE Release CACHE STRING "build type" FORCE)
elseif (NOT (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL Release))
# for simplicity, these are the only two config types we care about. Limiting the build types simplifies dealing
# with external project builds especially
message(FATAL_ERROR " *** Only Debug or Release build types are currently supported ***")
endif ()
endif ()
if (is_clang) # both Clang and AppleClang
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
message (FATAL_ERROR "This project requires clang 16 or later")
endif ()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
message(FATAL_ERROR "This project requires clang 16 or later")
endif ()
elseif (is_gcc)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
message (FATAL_ERROR "This project requires GCC 12 or later")
endif ()
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
message(FATAL_ERROR "This project requires GCC 12 or later")
endif ()
endif ()
# check for in-source build and fail
if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
message (FATAL_ERROR "Builds (in-source) are not allowed in "
"${CMAKE_CURRENT_SOURCE_DIR}. Please remove CMakeCache.txt and the CMakeFiles "
"directory from ${CMAKE_CURRENT_SOURCE_DIR} and try building in a separate directory.")
message(FATAL_ERROR "Builds (in-source) are not allowed in "
"${CMAKE_CURRENT_SOURCE_DIR}. Please remove CMakeCache.txt and the CMakeFiles "
"directory from ${CMAKE_CURRENT_SOURCE_DIR} and try building in a separate directory.")
endif ()
if (MSVC AND CMAKE_GENERATOR_PLATFORM STREQUAL "Win32")
message (FATAL_ERROR "Visual Studio 32-bit build is not supported.")
message(FATAL_ERROR "Visual Studio 32-bit build is not supported.")
endif ()
if (APPLE AND NOT HOMEBREW)
find_program (HOMEBREW brew)
find_program(HOMEBREW brew)
endif ()

View File

@@ -5,125 +5,110 @@
include(CompilationEnv)
set(is_ci FALSE)
if(DEFINED ENV{CI})
if("$ENV{CI}" STREQUAL "true")
set(is_ci TRUE)
endif()
endif()
if (DEFINED ENV{CI})
if ("$ENV{CI}" STREQUAL "true")
set(is_ci TRUE)
endif ()
endif ()
get_directory_property(has_parent PARENT_DIRECTORY)
if(has_parent)
set(is_root_project OFF)
else()
set(is_root_project ON)
endif()
if (has_parent)
set(is_root_project OFF)
else ()
set(is_root_project ON)
endif ()
option(assert "Enables asserts, even in release builds" OFF)
option(xrpld "Build xrpld" ON)
option(tests "Build tests" ON)
if(tests)
# This setting allows making a separate workflow to test fees other than default 10
if(NOT UNIT_TEST_REFERENCE_FEE)
set(UNIT_TEST_REFERENCE_FEE "10" CACHE STRING "")
endif()
endif()
if (tests)
# This setting allows making a separate workflow to test fees other than default 10
if (NOT UNIT_TEST_REFERENCE_FEE)
set(UNIT_TEST_REFERENCE_FEE "10" CACHE STRING "")
endif ()
endif ()
option(unity "Creates a build using UNITY support in cmake." OFF)
if(unity)
if(NOT is_ci)
set(CMAKE_UNITY_BUILD_BATCH_SIZE 15 CACHE STRING "")
endif()
set(CMAKE_UNITY_BUILD ON CACHE BOOL "Do a unity build")
endif()
if (unity)
if (NOT is_ci)
set(CMAKE_UNITY_BUILD_BATCH_SIZE 15 CACHE STRING "")
endif ()
set(CMAKE_UNITY_BUILD ON CACHE BOOL "Do a unity build")
endif ()
if(is_clang AND is_linux)
option(voidstar "Enable Antithesis instrumentation." OFF)
endif()
if (is_clang AND is_linux)
option(voidstar "Enable Antithesis instrumentation." OFF)
endif ()
if(is_gcc OR is_clang)
include(ProcessorCount)
ProcessorCount(PROCESSOR_COUNT)
if (is_gcc OR is_clang)
include(ProcessorCount)
ProcessorCount(PROCESSOR_COUNT)
option(coverage "Generates coverage info." OFF)
option(profile "Add profiling flags" OFF)
set(coverage_format "html-details" CACHE STRING
"Output format of the coverage report.")
set(coverage_extra_args "" CACHE STRING
"Additional arguments to pass to gcovr.")
option(wextra "compile with extra gcc/clang warnings enabled" ON)
else()
set(profile OFF CACHE BOOL "gcc/clang only" FORCE)
set(coverage OFF CACHE BOOL "gcc/clang only" FORCE)
set(wextra OFF CACHE BOOL "gcc/clang only" FORCE)
endif()
option(coverage "Generates coverage info." OFF)
option(profile "Add profiling flags" OFF)
set(coverage_format "html-details" CACHE STRING "Output format of the coverage report.")
set(coverage_extra_args "" CACHE STRING "Additional arguments to pass to gcovr.")
option(wextra "compile with extra gcc/clang warnings enabled" ON)
else ()
set(profile OFF CACHE BOOL "gcc/clang only" FORCE)
set(coverage OFF CACHE BOOL "gcc/clang only" FORCE)
set(wextra OFF CACHE BOOL "gcc/clang only" FORCE)
endif ()
if(is_linux AND NOT SANITIZER)
option(BUILD_SHARED_LIBS "build shared xrpl libraries" OFF)
option(static "link protobuf, openssl, libc++, and boost statically" ON)
option(perf "Enables flags that assist with perf recording" OFF)
option(use_gold "enables detection of gold (binutils) linker" ON)
option(use_mold "enables detection of mold (binutils) linker" ON)
# Set a default value for the log flag based on the build type.
# This provides a sensible default (on for debug, off for release)
# while still allowing the user to override it for any build.
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(TRUNCATED_LOGS_DEFAULT ON)
else()
set(TRUNCATED_LOGS_DEFAULT OFF)
endif()
option(TRUNCATED_THREAD_NAME_LOGS
"Show warnings about truncated thread names on Linux."
${TRUNCATED_LOGS_DEFAULT}
)
if(TRUNCATED_THREAD_NAME_LOGS)
add_compile_definitions(TRUNCATED_THREAD_NAME_LOGS)
endif()
else()
# we are not ready to allow shared-libs on windows because it would require
# export declarations. On macos it's more feasible, but static openssl
# produces odd linker errors, thus we disable shared lib builds for now.
set(BUILD_SHARED_LIBS OFF CACHE BOOL "build shared xrpl libraries - OFF for win/macos" FORCE)
set(static ON CACHE BOOL "static link, linux only. ON for WIN/macos" FORCE)
set(perf OFF CACHE BOOL "perf flags, linux only" FORCE)
set(use_gold OFF CACHE BOOL "gold linker, linux only" FORCE)
set(use_mold OFF CACHE BOOL "mold linker, linux only" FORCE)
endif()
if (is_linux AND NOT SANITIZER)
option(BUILD_SHARED_LIBS "build shared xrpl libraries" OFF)
option(static "link protobuf, openssl, libc++, and boost statically" ON)
option(perf "Enables flags that assist with perf recording" OFF)
option(use_gold "enables detection of gold (binutils) linker" ON)
option(use_mold "enables detection of mold (binutils) linker" ON)
# Set a default value for the log flag based on the build type. This provides a sensible default (on for debug, off
# for release) while still allowing the user to override it for any build.
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(TRUNCATED_LOGS_DEFAULT ON)
else ()
set(TRUNCATED_LOGS_DEFAULT OFF)
endif ()
option(TRUNCATED_THREAD_NAME_LOGS "Show warnings about truncated thread names on Linux." ${TRUNCATED_LOGS_DEFAULT})
if (TRUNCATED_THREAD_NAME_LOGS)
add_compile_definitions(TRUNCATED_THREAD_NAME_LOGS)
endif ()
else ()
# we are not ready to allow shared-libs on windows because it would require export declarations. On macos it's more
# feasible, but static openssl produces odd linker errors, thus we disable shared lib builds for now.
set(BUILD_SHARED_LIBS OFF CACHE BOOL "build shared xrpl libraries - OFF for win/macos" FORCE)
set(static ON CACHE BOOL "static link, linux only. ON for WIN/macos" FORCE)
set(perf OFF CACHE BOOL "perf flags, linux only" FORCE)
set(use_gold OFF CACHE BOOL "gold linker, linux only" FORCE)
set(use_mold OFF CACHE BOOL "mold linker, linux only" FORCE)
endif ()
if(is_clang)
option(use_lld "enables detection of lld linker" ON)
else()
set(use_lld OFF CACHE BOOL "try lld linker, clang only" FORCE)
endif()
if (is_clang)
option(use_lld "enables detection of lld linker" ON)
else ()
set(use_lld OFF CACHE BOOL "try lld linker, clang only" FORCE)
endif ()
option(jemalloc "Enables jemalloc for heap profiling" OFF)
option(werr "treat warnings as errors" OFF)
option(local_protobuf
"Force a local build of protobuf instead of looking for an installed version." OFF)
option(local_grpc
"Force a local build of gRPC instead of looking for an installed version." OFF)
option(local_protobuf "Force a local build of protobuf instead of looking for an installed version." OFF)
option(local_grpc "Force a local build of gRPC instead of looking for an installed version." OFF)
# the remaining options are obscure and rarely used
option(beast_no_unit_test_inline
"Prevents unit test definitions from being inserted into global table"
OFF)
option(single_io_service_thread
"Restricts the number of threads calling io_context::run to one. \
This can be useful when debugging."
OFF)
option(boost_show_deprecated
"Allow boost to fail on deprecated usage. Only useful if you're trying\
to find deprecated calls."
OFF)
option(beast_no_unit_test_inline "Prevents unit test definitions from being inserted into global table" OFF)
option(single_io_service_thread "Restricts the number of threads calling io_context::run to one. \
This can be useful when debugging." OFF)
option(boost_show_deprecated "Allow boost to fail on deprecated usage. Only useful if you're trying\
to find deprecated calls." OFF)
if(WIN32)
option(beast_disable_autolink "Disables autolinking of system libraries on WIN32" OFF)
else()
set(beast_disable_autolink OFF CACHE BOOL "WIN32 only" FORCE)
endif()
if (WIN32)
option(beast_disable_autolink "Disables autolinking of system libraries on WIN32" OFF)
else ()
set(beast_disable_autolink OFF CACHE BOOL "WIN32 only" FORCE)
endif ()
if(coverage)
message(STATUS "coverage build requested - forcing Debug build")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "build type" FORCE)
endif()
if (coverage)
message(STATUS "coverage build requested - forcing Debug build")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "build type" FORCE)
endif ()

View File

@@ -1,20 +1,17 @@
option (validator_keys "Enables building of validator-keys tool as a separate target (imported via FetchContent)" OFF)
option(validator_keys "Enables building of validator-keys tool as a separate target (imported via FetchContent)" OFF)
if (validator_keys)
git_branch (current_branch)
# default to tracking VK master branch unless we are on release
if (NOT (current_branch STREQUAL "release"))
set (current_branch "master")
endif ()
message (STATUS "Tracking ValidatorKeys branch: ${current_branch}")
git_branch(current_branch)
# default to tracking VK master branch unless we are on release
if (NOT (current_branch STREQUAL "release"))
set(current_branch "master")
endif ()
message(STATUS "Tracking ValidatorKeys branch: ${current_branch}")
FetchContent_Declare (
validator_keys
GIT_REPOSITORY https://github.com/ripple/validator-keys-tool.git
GIT_TAG "${current_branch}"
)
FetchContent_MakeAvailable(validator_keys)
set_target_properties(validator-keys PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
install(TARGETS validator-keys RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
FetchContent_Declare(validator_keys GIT_REPOSITORY https://github.com/ripple/validator-keys-tool.git
GIT_TAG "${current_branch}")
FetchContent_MakeAvailable(validator_keys)
set_target_properties(validator-keys PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
install(TARGETS validator-keys RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif ()

View File

@@ -3,13 +3,13 @@
#]===================================================================]
file(STRINGS src/libxrpl/protocol/BuildInfo.cpp BUILD_INFO)
foreach(line_ ${BUILD_INFO})
if(line_ MATCHES "versionString[ ]*=[ ]*\"(.+)\"")
set(xrpld_version ${CMAKE_MATCH_1})
endif()
endforeach()
if(xrpld_version)
message(STATUS "xrpld version: ${xrpld_version}")
else()
message(FATAL_ERROR "unable to determine xrpld version")
endif()
foreach (line_ ${BUILD_INFO})
if (line_ MATCHES "versionString[ ]*=[ ]*\"(.+)\"")
set(xrpld_version ${CMAKE_MATCH_1})
endif ()
endforeach ()
if (xrpld_version)
message(STATUS "xrpld version: ${xrpld_version}")
else ()
message(FATAL_ERROR "unable to determine xrpld version")
endif ()

View File

@@ -1,20 +1,19 @@
# file(CREATE_SYMLINK) only works on Windows with administrator privileges.
# https://stackoverflow.com/a/61244115/618906
function(create_symbolic_link target link)
if(WIN32)
if(NOT IS_SYMLINK "${link}")
if(NOT IS_ABSOLUTE "${target}")
# Relative links work do not work on Windows.
set(target "${link}/../${target}")
endif()
file(TO_NATIVE_PATH "${target}" target)
file(TO_NATIVE_PATH "${link}" link)
execute_process(COMMAND cmd.exe /c mklink /J "${link}" "${target}")
endif()
else()
file(CREATE_LINK "${target}" "${link}" SYMBOLIC)
endif()
if(NOT IS_SYMLINK "${link}")
message(ERROR "failed to create symlink: <${link}>")
endif()
endfunction()
# file(CREATE_SYMLINK) only works on Windows with administrator privileges. https://stackoverflow.com/a/61244115/618906
function (create_symbolic_link target link)
if (WIN32)
if (NOT IS_SYMLINK "${link}")
if (NOT IS_ABSOLUTE "${target}")
# Relative links work do not work on Windows.
set(target "${link}/../${target}")
endif ()
file(TO_NATIVE_PATH "${target}" target)
file(TO_NATIVE_PATH "${link}" link)
execute_process(COMMAND cmd.exe /c mklink /J "${link}" "${target}")
endif ()
else ()
file(CREATE_LINK "${target}" "${link}" SYMBOLIC)
endif ()
if (NOT IS_SYMLINK "${link}")
message(ERROR "failed to create symlink: <${link}>")
endif ()
endfunction ()

View File

@@ -2,48 +2,43 @@ include(CompilationEnv)
include(XrplSanitizers)
find_package(Boost REQUIRED
COMPONENTS
chrono
container
coroutine
date_time
filesystem
json
program_options
regex
system
thread
)
COMPONENTS chrono
container
coroutine
date_time
filesystem
json
program_options
regex
system
thread)
add_library(xrpl_boost INTERFACE)
add_library(Xrpl::boost ALIAS xrpl_boost)
target_link_libraries(xrpl_boost
INTERFACE
Boost::headers
Boost::chrono
Boost::container
Boost::coroutine
Boost::date_time
Boost::filesystem
Boost::json
Boost::process
Boost::program_options
Boost::regex
Boost::thread)
if(Boost_COMPILER)
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
endif()
if(SANITIZERS_ENABLED AND is_clang)
# TODO: gcc does not support -fsanitize-blacklist...can we do something else
# for gcc ?
if(NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
get_target_property(Boost_INCLUDE_DIRS Boost::headers INTERFACE_INCLUDE_DIRECTORIES)
endif()
message(STATUS "Adding [${Boost_INCLUDE_DIRS}] to sanitizer blacklist")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt "src:${Boost_INCLUDE_DIRS}/*")
target_compile_options(opts
INTERFACE
# ignore boost headers for sanitizing
-fsanitize-blacklist=${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt)
endif()
target_link_libraries(
xrpl_boost
INTERFACE Boost::headers
Boost::chrono
Boost::container
Boost::coroutine
Boost::date_time
Boost::filesystem
Boost::json
Boost::process
Boost::program_options
Boost::regex
Boost::thread)
if (Boost_COMPILER)
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
endif ()
if (SANITIZERS_ENABLED AND is_clang)
# TODO: gcc does not support -fsanitize-blacklist...can we do something else for gcc ?
if (NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
get_target_property(Boost_INCLUDE_DIRS Boost::headers INTERFACE_INCLUDE_DIRECTORIES)
endif ()
message(STATUS "Adding [${Boost_INCLUDE_DIRS}] to sanitizer blacklist")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt "src:${Boost_INCLUDE_DIRS}/*")
target_compile_options(opts INTERFACE # ignore boost headers for sanitizing
-fsanitize-blacklist=${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt)
endif ()

View File

@@ -77,16 +77,16 @@ public:
If the object is not found or an error is encountered, the
result will indicate the condition.
@note This will be called concurrently.
@param hash The hash of the object.
@param key A pointer to the key data.
@param pObject [out] The created object if successful.
@return The result of the operation.
*/
virtual Status
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pObject) = 0;
fetch(void const* key, std::shared_ptr<NodeObject>* pObject) = 0;
/** Fetch a batch synchronously. */
virtual std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
fetchBatch(std::vector<uint256> const& hashes) = 0;
fetchBatch(std::vector<uint256 const*> const& hashes) = 0;
/** Store a single object.
Depending on the implementation this may happen immediately

View File

@@ -24,6 +24,32 @@ public:
beast::Journal j)
: Database(scheduler, readThreads, config, j), backend_(std::move(backend))
{
std::optional<int> cacheSize, cacheAge;
if (config.exists("cache_size"))
{
cacheSize = get<int>(config, "cache_size");
if (cacheSize.value() < 0)
{
Throw<std::runtime_error>("Specified negative value for cache_size");
}
}
if (config.exists("cache_age"))
{
cacheAge = get<int>(config, "cache_age");
if (cacheAge.value() < 0)
{
Throw<std::runtime_error>("Specified negative value for cache_age");
}
}
if (cacheSize != 0 || cacheAge != 0)
{
cache_ = std::make_shared<TaggedCache<uint256, NodeObject>>(
"DatabaseNodeImp", cacheSize.value_or(0), std::chrono::minutes(cacheAge.value_or(0)), stopwatch(), j);
}
XRPL_ASSERT(
backend_,
"xrpl::NodeStore::DatabaseNodeImp::DatabaseNodeImp : non-null "
@@ -82,6 +108,9 @@ public:
sweep() override;
private:
// Cache for database objects. This cache is not always initialized. Check
// for null before using.
std::shared_ptr<TaggedCache<uint256, NodeObject>> cache_;
// Persistent key/value storage
std::shared_ptr<Backend> backend_;

View File

@@ -0,0 +1,489 @@
#ifndef XRPL_PROTOCOL_CONFIDENTIALTRANSFER_H_INCLUDED
#define XRPL_PROTOCOL_CONFIDENTIALTRANSFER_H_INCLUDED
#include <xrpl/basics/Slice.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.h>
namespace xrpl {
// Helper struct to bundle the ElGamal Public Key and the associated Ciphertext
struct ConfidentialRecipient
{
Slice const publicKey;
Slice const encryptedAmount;
};
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].value_or(0u) + 1u;
}
void
addCommonZKPFields(
Serializer& s,
std::uint16_t txType,
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID);
uint256
getSendContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
AccountID const& destination,
std::uint32_t version);
uint256
getClawbackContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
std::uint64_t amount,
AccountID const& holder);
uint256
getConvertContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
std::uint64_t amount);
uint256
getConvertBackContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
std::uint64_t amount,
std::uint32_t version);
// breaks a 66-byte encrypted amount into two 33-byte components
// then parses each 33-byte component into 64-byte secp256k1_pubkey format
bool
makeEcPair(Slice const& buffer, secp256k1_pubkey& out1, secp256k1_pubkey& out2);
// serialize two secp256k1_pubkey components back into compressed 66-byte form
bool
serializeEcPair(secp256k1_pubkey const& in1, secp256k1_pubkey const& in2, Buffer& buffer);
/**
* @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);
TER
homomorphicAdd(Slice const& a, Slice const& b, Buffer& out);
TER
homomorphicSubtract(Slice const& a, Slice const& b, Buffer& out);
// returns ciphertext and the blinding factor used
std::optional<Buffer>
encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor);
std::optional<Buffer>
encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId);
TER
verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash);
TER
verifyElGamalEncryption(
std::uint64_t const amount,
Slice const& blindingFactor,
Slice const& pubKeySlice,
Slice const& ciphertext);
NotTEC
checkEncryptedAmountFormat(STObject const& object);
TER
verifyRevealedAmount(
std::uint64_t const amount,
Slice const& blindingFactor,
ConfidentialRecipient const& holder,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor);
constexpr std::size_t
getConfidentialRecipientCount(bool hasAuditor)
{
return hasAuditor ? 4 : 3;
}
std::size_t
getMultiCiphertextEqualityProofSize(std::size_t nRecipients);
TER
verifyMultiCiphertextEqualityProof(
Slice const& proof,
std::vector<ConfidentialRecipient> const& recipients,
std::size_t const nRecipients,
uint256 const& contextHash);
TER
verifyClawbackEqualityProof(
uint64_t const amount,
Slice const& proof,
Slice const& pubKeySlice,
Slice const& ciphertext,
uint256 const& contextHash);
// generates a 32 byte randomness factor to be used in encryption and proofs
Buffer
generateBlindingFactor();
/**
* @brief Verifies the cryptographic link between an ElGamal Ciphertext and a
* Pedersen Commitment for a transaction Amount.
*
* It proves that the ElGamal ciphertext `encAmt` encrypts the same value `m`
* as the Pedersen Commitment `pcmSlice`, using the randomness `r`.
* Proves Enc(m) <-> Pcm(m)
*
* @param proof The Zero Knowledge Proof bytes.
* @param encAmt The ElGamal ciphertext of the amount (C1, C2).
* @param pubKeySlice The sender's public key.
* @param pcmSlice The Pedersen Commitment to the amount.
* @param contextHash The unique context hash for this transaction.
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
*/
TER
verifyAmountPcmLinkage(
Slice const& proof,
Slice const& encAmt,
Slice const& pubKeySlice,
Slice const& pcmSlice,
uint256 const& contextHash);
/**
* @brief Verifies the cryptographic link between an ElGamal Ciphertext and a
* Pedersen Commitment for an account Balance.
*
* It proves that the ElGamal ciphertext `encAmt` encrypts the same value `b`
* as the Pedersen Commitment `pcmSlice`, using the secret key `s`.
* Proves Enc(b) <-> Pcm(b)
*
* Note: Swaps arguments (Pk <-> C1) to accommodate the different algebraic
* structure.
*
* @param proof The Zero Knowledge Proof bytes.
* @param encAmt The ElGamal ciphertext of the balance (C1, C2).
* @param pubKeySlice The sender's public key.
* @param pcmSlice The Pedersen Commitment to the balance.
* @param contextHash The unique context hash for this transaction.
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
*/
TER
verifyBalancePcmLinkage(
Slice const& proof,
Slice const& encAmt,
Slice const& pubKeySlice,
Slice const& pcmSlice,
uint256 const& contextHash);
// The following functions belong to the mpt-crypto library,
// they will be finally removed and we will use conan2 to manage the dependency.
/**
* @brief Generates a new secp256k1 key pair.
*/
SECP256K1_API int
secp256k1_elgamal_generate_keypair(secp256k1_context const* ctx, unsigned char* privkey, secp256k1_pubkey* pubkey);
/**
* @brief Encrypts a 64-bit amount using ElGamal.
*/
SECP256K1_API int
secp256k1_elgamal_encrypt(
secp256k1_context const* ctx,
secp256k1_pubkey* c1,
secp256k1_pubkey* c2,
secp256k1_pubkey const* pubkey_Q,
uint64_t amount,
unsigned char const* blinding_factor);
/**
* @brief Decrypts an ElGamal ciphertext to recover the amount.
*/
SECP256K1_API int
secp256k1_elgamal_decrypt(
secp256k1_context const* ctx,
uint64_t* amount,
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
unsigned char const* privkey);
/**
* @brief Homomorphically adds two ElGamal ciphertexts.
*/
SECP256K1_API int
secp256k1_elgamal_add(
secp256k1_context const* ctx,
secp256k1_pubkey* sum_c1,
secp256k1_pubkey* sum_c2,
secp256k1_pubkey const* a_c1,
secp256k1_pubkey const* a_c2,
secp256k1_pubkey const* b_c1,
secp256k1_pubkey const* b_c2);
/**
* @brief Homomorphically subtracts two ElGamal ciphertexts.
*/
SECP256K1_API int
secp256k1_elgamal_subtract(
secp256k1_context const* ctx,
secp256k1_pubkey* diff_c1,
secp256k1_pubkey* diff_c2,
secp256k1_pubkey const* a_c1,
secp256k1_pubkey const* a_c2,
secp256k1_pubkey const* b_c1,
secp256k1_pubkey const* b_c2);
/**
* @brief Generates the canonical encrypted zero for a given MPT token instance.
*
* This ciphertext represents a zero balance for a specific account's holding
* of a token defined by its MPTokenIssuanceID.
*
* @param[in] ctx A pointer to a valid secp256k1 context.
* @param[out] enc_zero_c1 The C1 component of the canonical ciphertext.
* @param[out] enc_zero_c2 The C2 component of the canonical ciphertext.
* @param[in] pubkey The ElGamal public key of the account holder.
* @param[in] account_id A pointer to the 20-byte AccountID.
* @param[in] mpt_issuance_id A pointer to the 24-byte MPTokenIssuanceID.
*
* @return 1 on success, 0 on failure.
*/
SECP256K1_API int
generate_canonical_encrypted_zero(
secp256k1_context const* ctx,
secp256k1_pubkey* enc_zero_c1,
secp256k1_pubkey* enc_zero_c2,
secp256k1_pubkey const* pubkey,
unsigned char const* account_id, // 20 bytes
unsigned char const* mpt_issuance_id // 24 bytes
);
/**
* Generates a cryptographically secure 32-byte scalar (private key).
* @return 1 on success, 0 on failure.
*/
SECP256K1_API int
generate_random_scalar(secp256k1_context const* ctx, unsigned char* scalar_bytes);
/**
* Computes the point M = amount * G.
* IMPORTANT: This function MUST NOT be called with amount = 0.
*/
SECP256K1_API int
compute_amount_point(secp256k1_context const* ctx, secp256k1_pubkey* mG, uint64_t amount);
/**
* Builds the challenge hash input for the NON-ZERO amount case.
* Output buffer must be 253 bytes.
*/
SECP256K1_API void
build_challenge_hash_input_nonzero(
unsigned char* hash_input,
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
secp256k1_pubkey const* pk,
secp256k1_pubkey const* mG,
secp256k1_pubkey const* T1,
secp256k1_pubkey const* T2,
unsigned char const* tx_context_id);
/**
* Builds the challenge hash input for the ZERO amount case.
* Output buffer must be 220 bytes.
*/
SECP256K1_API void
build_challenge_hash_input_zero(
unsigned char* hash_input,
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
secp256k1_pubkey const* pk,
secp256k1_pubkey const* T1,
secp256k1_pubkey const* T2,
unsigned char const* tx_context_id);
/**
* @brief Proves that a commitment (C1, C2) encrypts a specific plaintext
* 'amount'.
*/
SECP256K1_API int
secp256k1_equality_plaintext_prove(
secp256k1_context const* ctx,
unsigned char* proof,
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
secp256k1_pubkey const* pk_recipient,
uint64_t amount,
unsigned char const* randomness_r,
unsigned char const* tx_context_id);
/**
* @brief Verifies the proof generated by secp256k1_equality_plaintext_prove.
*/
SECP256K1_API int
secp256k1_equality_plaintext_verify(
secp256k1_context const* ctx,
unsigned char const* proof,
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
secp256k1_pubkey const* pk_recipient,
uint64_t amount,
unsigned char const* tx_context_id);
void
build_pok_challenge(
unsigned char* e,
secp256k1_context const* ctx,
secp256k1_pubkey const* pk,
secp256k1_pubkey const* T,
unsigned char const* context_id);
/** Proof of Knowledge of Secret Key for Registration */
int
secp256k1_mpt_pok_sk_prove(
secp256k1_context const* ctx,
unsigned char* proof, /* Expected size: 65 bytes */
secp256k1_pubkey const* pk,
unsigned char const* sk,
unsigned char const* context_id);
int
secp256k1_mpt_pok_sk_verify(
secp256k1_context const* ctx,
unsigned char const* proof, /* Expected size: 65 bytes */
secp256k1_pubkey const* pk,
unsigned char const* context_id);
/**
* Verifies that (c1, c2) is a valid ElGamal encryption of 'amount'
* for 'pubkey_Q' using the revealed 'blinding_factor'.
*/
int
secp256k1_elgamal_verify_encryption(
secp256k1_context const* ctx,
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
secp256k1_pubkey const* pubkey_Q,
uint64_t amount,
unsigned char const* blinding_factor);
/**
* @brief Proves the link between an ElGamal ciphertext and a Pedersen
* commitment.
* * Formal Statement: Knowledge of (m, r, rho) such that:
* C1 = r*G, C2 = m*G + r*Pk, and PCm = m*G + rho*H.
* * @param ctx Pointer to a secp256k1 context object.
* @param proof [OUT] Pointer to 195-byte buffer for the proof output.
* @param c1 Pointer to the ElGamal C1 point (r*G).
* @param c2 Pointer to the ElGamal C2 point (m*G + r*Pk).
* @param pk Pointer to the recipient's public key.
* @param pcm Pointer to the Pedersen Commitment (m*G + rho*H).
* @param amount The plaintext amount (m).
* @param r The 32-byte secret ElGamal blinding factor.
* @param rho The 32-byte secret Pedersen blinding factor.
* @param context_id 32-byte unique transaction context identifier.
* @return 1 on success, 0 on failure.
*/
int
secp256k1_elgamal_pedersen_link_prove(
secp256k1_context const* ctx,
unsigned char* proof,
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
secp256k1_pubkey const* pk,
secp256k1_pubkey const* pcm,
uint64_t amount,
unsigned char const* r,
unsigned char const* rho,
unsigned char const* context_id);
/**
* @brief Verifies the link proof between ElGamal and Pedersen commitments.
* * @return 1 if the proof is valid, 0 otherwise.
*/
int
secp256k1_elgamal_pedersen_link_verify(
secp256k1_context const* ctx,
unsigned char const* proof,
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
secp256k1_pubkey const* pk,
secp256k1_pubkey const* pcm,
unsigned char const* context_id);
/**
* Compute a Pedersen Commitment: PC = m*G + rho*H
* Returns 1 on success, 0 on failure.
*/
int
secp256k1_mpt_pedersen_commit(
secp256k1_context const* ctx,
secp256k1_pubkey* commitment,
uint64_t amount,
unsigned char const* blinding_factor_rho /* 32 bytes */
);
// Multi-proof for same plaintexts
void
build_hash_input(
unsigned char* hash_out, // Output: 32-byte hash
size_t n,
secp256k1_pubkey const* R,
secp256k1_pubkey const* S,
secp256k1_pubkey const* Pk,
secp256k1_pubkey const* T_m,
secp256k1_pubkey const* T_rG,
secp256k1_pubkey const* T_rP,
unsigned char const* tx_id);
size_t
secp256k1_mpt_prove_same_plaintext_multi_size(size_t n);
int
secp256k1_mpt_prove_same_plaintext_multi(
secp256k1_context const* ctx,
unsigned char* proof_out,
size_t* proof_len,
uint64_t amount_m,
size_t n,
secp256k1_pubkey const* R,
secp256k1_pubkey const* S,
secp256k1_pubkey const* Pk,
unsigned char const* r_array,
unsigned char const* tx_id);
int
secp256k1_mpt_verify_same_plaintext_multi(
secp256k1_context const* ctx,
unsigned char const* proof,
size_t proof_len,
size_t n,
secp256k1_pubkey const* R,
secp256k1_pubkey const* S,
secp256k1_pubkey const* Pk,
unsigned char const* tx_id);
} // namespace xrpl
#endif

View File

@@ -168,6 +168,7 @@ enum LedgerSpecificFlags {
lsfMPTCanTrade = 0x00000010,
lsfMPTCanTransfer = 0x00000020,
lsfMPTCanClawback = 0x00000040,
lsfMPTCanPrivacy = 0x00000080,
lsmfMPTCanMutateCanLock = 0x00000002,
lsmfMPTCanMutateRequireAuth = 0x00000004,
@@ -177,6 +178,8 @@ enum LedgerSpecificFlags {
lsmfMPTCanMutateCanClawback = 0x00000040,
lsmfMPTCanMutateMetadata = 0x00010000,
lsmfMPTCanMutateTransferFee = 0x00020000,
// if set, lsfMPTCanPrivacy can not be mutated
lsmfMPTCannotMutatePrivacy = 0x00040000,
// ltMPTOKEN
lsfMPTAuthorized = 0x00000002,

View File

@@ -297,6 +297,36 @@ std::size_t constexpr permissionMaxSize = 10;
/** The maximum number of transactions that can be in a batch. */
std::size_t constexpr maxBatchTxCount = 8;
/** EC ElGamal ciphertext length 33-byte */
std::size_t constexpr ecGamalEncryptedLength = 33;
/** EC ElGamal ciphertext length: two 33-byte components concatenated */
std::size_t constexpr ecGamalEncryptedTotalLength = 66;
/** Length of equality ZKProof */
std::size_t constexpr ecEqualityProofLength = 98;
/** Length of EC public key */
std::size_t constexpr ecPubKeyLength = 64;
/** Length of EC private key */
std::size_t constexpr ecPrivKeyLength = 32;
/** Length of the EC blinding factor */
std::size_t constexpr ecBlindingFactorLength = 32;
/** Length of Schnorr ZKProof for public key registration */
std::size_t constexpr ecSchnorrProofLength = 65;
/** Length of ElGamal ciphertext equality proof */
std::size_t constexpr ecCiphertextEqualityProofLength = 261;
/** Length of ElGamal Pedersen linkage proof */
std::size_t constexpr ecPedersenProofLength = 195;
/** Length of Pedersen Commitment proof */
std::size_t constexpr ecPedersenCommitmentLength = 64;
} // namespace xrpl
#endif

View File

@@ -122,6 +122,7 @@ enum TEMcodes : TERUnderlyingType {
temARRAY_TOO_LARGE,
temBAD_TRANSFER_FEE,
temINVALID_INNER_BATCH,
temBAD_CIPHERTEXT,
};
//------------------------------------------------------------------------------
@@ -347,6 +348,7 @@ enum TECcodes : TERUnderlyingType {
// 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

@@ -132,8 +132,9 @@ constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow;
constexpr std::uint32_t const tfMPTCanTrade = lsfMPTCanTrade;
constexpr std::uint32_t const tfMPTCanTransfer = lsfMPTCanTransfer;
constexpr std::uint32_t const tfMPTCanClawback = lsfMPTCanClawback;
constexpr std::uint32_t const tfMPTCanPrivacy = lsfMPTCanPrivacy;
constexpr std::uint32_t const tfMPTokenIssuanceCreateMask =
~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback);
~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback | tfMPTCanPrivacy);
// MPTokenIssuanceCreate MutableFlags:
// Indicating specific fields or flags may be changed after issuance.
@@ -145,9 +146,13 @@ constexpr std::uint32_t const tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTr
constexpr std::uint32_t const tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback;
constexpr std::uint32_t const tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata;
constexpr std::uint32_t const tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee;
// Issuer can mutate lsfMPTPrivacy by default unless lsmfMPTCannotMutatePrivacy is set.
constexpr std::uint32_t const tmfMPTCannotMutatePrivacy = lsmfMPTCannotMutatePrivacy;
constexpr std::uint32_t const tmfMPTokenIssuanceCreateMutableMask =
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow | tmfMPTCanMutateCanTrade
| tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
| tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee
| tmfMPTCannotMutatePrivacy);
// MPTokenAuthorize flags:
constexpr std::uint32_t const tfMPTUnauthorize = 0x00000001;
@@ -173,10 +178,12 @@ constexpr std::uint32_t const tmfMPTSetCanTransfer = 0x00000100;
constexpr std::uint32_t const tmfMPTClearCanTransfer = 0x00000200;
constexpr std::uint32_t const tmfMPTSetCanClawback = 0x00000400;
constexpr std::uint32_t const tmfMPTClearCanClawback = 0x00000800;
constexpr std::uint32_t const tmfMPTSetPrivacy = 0x00001000;
constexpr std::uint32_t const tmfMPTClearPrivacy = 0x00002000;
constexpr std::uint32_t const tmfMPTokenIssuanceSetMutableMask = ~(tmfMPTSetCanLock | tmfMPTClearCanLock |
tmfMPTSetRequireAuth | tmfMPTClearRequireAuth | tmfMPTSetCanEscrow | tmfMPTClearCanEscrow |
tmfMPTSetCanTrade | tmfMPTClearCanTrade | tmfMPTSetCanTransfer | tmfMPTClearCanTransfer |
tmfMPTSetCanClawback | tmfMPTClearCanClawback);
tmfMPTSetCanClawback | tmfMPTClearCanClawback | tmfMPTSetPrivacy | tmfMPTClearPrivacy);
// MPTokenIssuanceDestroy flags:
constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal;

View File

@@ -16,6 +16,7 @@
// 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 (BatchInnerSigs, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionDelegationV1_1, Supported::no, VoteBehavior::DefaultNo)

View File

@@ -398,6 +398,9 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfPreviousTxnLgrSeq, soeREQUIRED},
{sfDomainID, soeOPTIONAL},
{sfMutableFlags, soeDEFAULT},
{sfIssuerElGamalPublicKey, soeOPTIONAL},
{sfAuditorElGamalPublicKey, soeOPTIONAL},
{sfConfidentialOutstandingAmount, soeDEFAULT},
}))
/** A ledger object which tracks MPToken
@@ -411,6 +414,12 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
{sfOwnerNode, soeREQUIRED},
{sfPreviousTxnID, soeREQUIRED},
{sfPreviousTxnLgrSeq, soeREQUIRED},
{sfConfidentialBalanceInbox, soeOPTIONAL},
{sfConfidentialBalanceSpending, soeOPTIONAL},
{sfConfidentialBalanceVersion, soeDEFAULT},
{sfIssuerEncryptedBalance, soeOPTIONAL},
{sfAuditorEncryptedBalance, soeOPTIONAL},
{sfHolderElGamalPublicKey, soeOPTIONAL},
}))
/** A ledger object which tracks Oracle

View File

@@ -114,6 +114,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)
@@ -147,6 +148,7 @@ TYPED_SFIELD(sfSubjectNode, UINT64, 28)
TYPED_SFIELD(sfLockedAmount, UINT64, 29, SField::sMD_BaseTen|SField::sMD_Default)
TYPED_SFIELD(sfVaultNode, UINT64, 30)
TYPED_SFIELD(sfLoanBrokerNode, UINT64, 31)
TYPED_SFIELD(sfConfidentialOutstandingAmount, UINT64, 32, SField::sMD_BaseTen|SField::sMD_Default)
// 128-bit
TYPED_SFIELD(sfEmailHash, UINT128, 1)
@@ -297,6 +299,22 @@ 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(sfIssuerElGamalPublicKey, VL, 35)
TYPED_SFIELD(sfHolderElGamalPublicKey, 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(sfAuditorElGamalPublicKey, VL, 44)
TYPED_SFIELD(sfBlindingFactor, VL, 45)
TYPED_SFIELD(sfAmountCommitment, VL, 46)
TYPED_SFIELD(sfBalanceCommitment, VL, 47)
// account (common)
TYPED_SFIELD(sfAccount, ACCOUNT, 1)

View File

@@ -722,6 +722,8 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
{sfMPTokenMetadata, soeOPTIONAL},
{sfTransferFee, soeOPTIONAL},
{sfMutableFlags, soeOPTIONAL},
{sfIssuerElGamalPublicKey, soeOPTIONAL},
{sfAuditorElGamalPublicKey, soeOPTIONAL},
}))
/** This transaction type authorizes a MPToken instance */
@@ -1058,6 +1060,90 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay,
{sfAmount, soeREQUIRED, soeMPTSupported},
}))
/** This transaction type converts into confidential MPT balance. */
#if TRANSACTION_INCLUDE
#include <xrpld/app/tx/detail/ConfidentialMPTConvert.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT, 85, ConfidentialMPTConvert,
Delegation::delegable,
featureConfidentialTransfer,
noPriv,
({
{sfMPTokenIssuanceID, soeREQUIRED},
{sfMPTAmount, soeREQUIRED},
{sfHolderElGamalPublicKey, soeOPTIONAL},
{sfHolderEncryptedAmount, soeREQUIRED},
{sfIssuerEncryptedAmount, soeREQUIRED},
{sfAuditorEncryptedAmount, soeOPTIONAL},
{sfBlindingFactor, soeREQUIRED},
{sfZKProof, soeOPTIONAL},
}))
/** This transaction type merges MPT inbox. */
#if TRANSACTION_INCLUDE
#include <xrpld/app/tx/detail/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 <xrpld/app/tx/detail/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 <xrpld/app/tx/detail/ConfidentialMPTSend.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_SEND, 88, ConfidentialMPTSend,
Delegation::delegable,
featureConfidentialTransfer,
noPriv,
({
{sfMPTokenIssuanceID, soeREQUIRED},
{sfDestination, soeREQUIRED},
{sfSenderEncryptedAmount, soeREQUIRED},
{sfDestinationEncryptedAmount, soeREQUIRED},
{sfIssuerEncryptedAmount, soeREQUIRED},
{sfAuditorEncryptedAmount, soeOPTIONAL},
{sfZKProof, soeREQUIRED},
{sfAmountCommitment, soeREQUIRED},
{sfBalanceCommitment, soeREQUIRED},
{sfCredentialIDs, soeOPTIONAL},
}))
#if TRANSACTION_INCLUDE
#include <xrpld/app/tx/detail/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

@@ -468,7 +468,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 == ahZERO_IF_UNAUTHORIZED && view.rules().enabled(featureSingleAssetVault))
if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED &&
(view.rules().enabled(featureSingleAssetVault) || view.rules().enabled(featureConfidentialTransfer)))
{
if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth); !isTesSuccess(err))
amount.clear(mptIssue);

View File

@@ -10,6 +10,11 @@ DatabaseNodeImp::store(NodeObjectType type, Blob&& data, uint256 const& hash, st
auto obj = NodeObject::createObject(type, std::move(data), hash);
backend_->store(obj);
if (cache_)
{
// After the store, replace a negative cache entry if there is one
cache_->canonicalize(hash, obj, [](std::shared_ptr<NodeObject> const& n) { return n->getType() == hotDUMMY; });
}
}
void
@@ -18,41 +23,77 @@ DatabaseNodeImp::asyncFetch(
std::uint32_t ledgerSeq,
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback)
{
if (cache_)
{
std::shared_ptr<NodeObject> obj = cache_->fetch(hash);
if (obj)
{
callback(obj->getType() == hotDUMMY ? nullptr : obj);
return;
}
}
Database::asyncFetch(hash, ledgerSeq, std::move(callback));
}
void
DatabaseNodeImp::sweep()
{
if (cache_)
cache_->sweep();
}
std::shared_ptr<NodeObject>
DatabaseNodeImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate)
{
std::shared_ptr<NodeObject> nodeObject = nullptr;
Status status;
std::shared_ptr<NodeObject> nodeObject = cache_ ? cache_->fetch(hash) : nullptr;
try
if (!nodeObject)
{
status = backend_->fetch(hash, &nodeObject);
}
catch (std::exception const& e)
{
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": Exception fetching from backend: " << e.what();
Rethrow();
}
JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record not " << (cache_ ? "cached" : "found");
switch (status)
Status status;
try
{
status = backend_->fetch(hash.data(), &nodeObject);
}
catch (std::exception const& e)
{
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": Exception fetching from backend: " << e.what();
Rethrow();
}
switch (status)
{
case ok:
if (cache_)
{
if (nodeObject)
cache_->canonicalize_replace_client(hash, nodeObject);
else
{
auto notFound = NodeObject::createObject(hotDUMMY, {}, hash);
cache_->canonicalize_replace_client(hash, notFound);
if (notFound->getType() != hotDUMMY)
nodeObject = notFound;
}
}
break;
case notFound:
break;
case dataCorrupt:
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted";
break;
default:
JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " << status;
break;
}
}
else
{
case ok:
case notFound:
break;
case dataCorrupt:
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted";
break;
default:
JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " << status;
break;
JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record found in cache";
if (nodeObject->getType() == hotDUMMY)
nodeObject.reset();
}
if (nodeObject)
@@ -64,22 +105,66 @@ DatabaseNodeImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport
std::vector<std::shared_ptr<NodeObject>>
DatabaseNodeImp::fetchBatch(std::vector<uint256> const& hashes)
{
std::vector<std::shared_ptr<NodeObject>> results{hashes.size()};
using namespace std::chrono;
auto const before = steady_clock::now();
std::vector<std::shared_ptr<NodeObject>> results{hashes.size()};
results = backend_->fetchBatch(hashes).first;
for (size_t i = 0; i < results.size(); ++i)
std::unordered_map<uint256 const*, size_t> indexMap;
std::vector<uint256 const*> cacheMisses;
uint64_t hits = 0;
uint64_t fetches = 0;
for (size_t i = 0; i < hashes.size(); ++i)
{
if (!results[i])
auto const& hash = hashes[i];
// See if the object already exists in the cache
auto nObj = cache_ ? cache_->fetch(hash) : nullptr;
++fetches;
if (!nObj)
{
JLOG(j_.error()) << "fetchBatch - "
<< "record not found in db. hash = " << strHex(hashes[i]);
// Try the database
indexMap[&hash] = i;
cacheMisses.push_back(&hash);
}
else
{
results[i] = nObj->getType() == hotDUMMY ? nullptr : nObj;
// It was in the cache.
++hits;
}
}
JLOG(j_.debug()) << "fetchBatch - cache hits = " << (hashes.size() - cacheMisses.size())
<< " - cache misses = " << cacheMisses.size();
auto dbResults = backend_->fetchBatch(cacheMisses).first;
for (size_t i = 0; i < dbResults.size(); ++i)
{
auto nObj = std::move(dbResults[i]);
size_t index = indexMap[cacheMisses[i]];
auto const& hash = hashes[index];
if (nObj)
{
// Ensure all threads get the same object
if (cache_)
cache_->canonicalize_replace_client(hash, nObj);
}
else
{
JLOG(j_.error()) << "fetchBatch - "
<< "record not found in db or cache. hash = " << strHex(hash);
if (cache_)
{
auto notFound = NodeObject::createObject(hotDUMMY, {}, hash);
cache_->canonicalize_replace_client(hash, notFound);
if (notFound->getType() != hotDUMMY)
nObj = std::move(notFound);
}
}
results[index] = std::move(nObj);
}
auto fetchDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now() - before).count();
updateFetchMetrics(hashes.size(), 0, fetchDurationUs);
updateFetchMetrics(fetches, hits, fetchDurationUs);
return results;
}

View File

@@ -107,7 +107,7 @@ DatabaseRotatingImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchRe
std::shared_ptr<NodeObject> nodeObject;
try
{
status = backend->fetch(hash, &nodeObject);
status = backend->fetch(hash.data(), &nodeObject);
}
catch (std::exception const& e)
{

View File

@@ -115,9 +115,10 @@ public:
//--------------------------------------------------------------------------
Status
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pObject) override
fetch(void const* key, std::shared_ptr<NodeObject>* pObject) override
{
XRPL_ASSERT(db_, "xrpl::NodeStore::MemoryBackend::fetch : non-null database");
uint256 const hash(uint256::fromVoid(key));
std::lock_guard _(db_->mutex);
@@ -132,14 +133,14 @@ public:
}
std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
fetchBatch(std::vector<uint256> const& hashes) override
fetchBatch(std::vector<uint256 const*> const& hashes) override
{
std::vector<std::shared_ptr<NodeObject>> results;
results.reserve(hashes.size());
for (auto const& h : hashes)
{
std::shared_ptr<NodeObject> nObj;
Status status = fetch(h, &nObj);
Status status = fetch(h->begin(), &nObj);
if (status != ok)
results.push_back({});
else

View File

@@ -177,17 +177,17 @@ public:
}
Status
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pno) override
fetch(void const* key, std::shared_ptr<NodeObject>* pno) override
{
Status status;
pno->reset();
nudb::error_code ec;
db_.fetch(
hash.data(),
[hash, pno, &status](void const* data, std::size_t size) {
key,
[key, pno, &status](void const* data, std::size_t size) {
nudb::detail::buffer bf;
auto const result = nodeobject_decompress(data, size, bf);
DecodedBlob decoded(hash.data(), result.first, result.second);
DecodedBlob decoded(key, result.first, result.second);
if (!decoded.wasOk())
{
status = dataCorrupt;
@@ -205,14 +205,14 @@ public:
}
std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
fetchBatch(std::vector<uint256> const& hashes) override
fetchBatch(std::vector<uint256 const*> const& hashes) override
{
std::vector<std::shared_ptr<NodeObject>> results;
results.reserve(hashes.size());
for (auto const& h : hashes)
{
std::shared_ptr<NodeObject> nObj;
Status status = fetch(h, &nObj);
Status status = fetch(h->begin(), &nObj);
if (status != ok)
results.push_back({});
else

View File

@@ -36,13 +36,13 @@ public:
}
Status
fetch(uint256 const&, std::shared_ptr<NodeObject>*) override
fetch(void const*, std::shared_ptr<NodeObject>*) override
{
return notFound;
}
std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
fetchBatch(std::vector<uint256> const& hashes) override
fetchBatch(std::vector<uint256 const*> const& hashes) override
{
return {};
}

View File

@@ -237,7 +237,7 @@ public:
//--------------------------------------------------------------------------
Status
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pObject) override
fetch(void const* key, std::shared_ptr<NodeObject>* pObject) override
{
XRPL_ASSERT(m_db, "xrpl::NodeStore::RocksDBBackend::fetch : non-null database");
pObject->reset();
@@ -245,7 +245,7 @@ public:
Status status(ok);
rocksdb::ReadOptions const options;
rocksdb::Slice const slice(reinterpret_cast<char const*>(hash.data()), m_keyBytes);
rocksdb::Slice const slice(static_cast<char const*>(key), m_keyBytes);
std::string string;
@@ -253,7 +253,7 @@ public:
if (getStatus.ok())
{
DecodedBlob decoded(hash.data(), string.data(), string.size());
DecodedBlob decoded(key, string.data(), string.size());
if (decoded.wasOk())
{
@@ -288,14 +288,14 @@ public:
}
std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
fetchBatch(std::vector<uint256> const& hashes) override
fetchBatch(std::vector<uint256 const*> const& hashes) override
{
std::vector<std::shared_ptr<NodeObject>> results;
results.reserve(hashes.size());
for (auto const& h : hashes)
{
std::shared_ptr<NodeObject> nObj;
Status status = fetch(h, &nObj);
Status status = fetch(h->begin(), &nObj);
if (status != ok)
results.push_back({});
else

File diff suppressed because it is too large Load Diff

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."),
@@ -198,6 +199,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."),

File diff suppressed because it is too large Load Diff

View File

@@ -507,7 +507,8 @@ class MPToken_test : public beast::unit_test::suite
// (2)
mptAlice.set({.account = alice, .flags = 0x00000008, .err = temINVALID_FLAG});
if (!features[featureSingleAssetVault] && !features[featureDynamicMPT])
if (!features[featureSingleAssetVault] && !features[featureDynamicMPT] &&
!features[featureConfidentialTransfer])
{
// test invalid flags - nothing is being changed
mptAlice.set({.account = alice, .flags = 0x00000000, .err = tecNO_PERMISSION});
@@ -2550,6 +2551,7 @@ class MPToken_test : public beast::unit_test::suite
tmfMPTSetCanTrade | tmfMPTClearCanTrade,
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer,
tmfMPTSetCanClawback | tmfMPTClearCanClawback,
tmfMPTSetPrivacy | tmfMPTClearPrivacy,
tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTClearCanTrade,
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanEscrow | tmfMPTClearCanClawback};

View File

@@ -490,8 +490,19 @@ public:
Env env(*this, envconfig(onlineDelete));
/////////////////////////////////////////////////////////////
// Create NodeStore with two backends to allow online deletion of data.
// Normally, SHAMapStoreImp handles all these details.
// Create the backend. Normally, SHAMapStoreImp handles all these
// details
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
// Provide default values:
if (!nscfg.exists("cache_size"))
nscfg.set(
"cache_size", std::to_string(env.app().config().getValueFor(SizedItem::treeCacheSize, std::nullopt)));
if (!nscfg.exists("cache_age"))
nscfg.set(
"cache_age", std::to_string(env.app().config().getValueFor(SizedItem::treeCacheAge, std::nullopt)));
NodeStoreScheduler scheduler(env.app().getJobQueue());
std::string const writableDb = "write";
@@ -499,8 +510,9 @@ public:
auto writableBackend = makeBackendRotating(env, scheduler, writableDb);
auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb);
// Create NodeStore with two backends to allow online deletion of
// data
constexpr int readThreads = 4;
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
auto dbr = std::make_unique<NodeStore::DatabaseRotatingImp>(
scheduler,
readThreads,

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,11 @@
#include <test/jtx/ter.h>
#include <test/jtx/txflags.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/UintTypes.h>
#include <cstdint>
namespace xrpl {
namespace test {
namespace jtx {
@@ -94,6 +97,7 @@ struct MPTCreate
struct MPTInit
{
Holders holders = {};
std::optional<Account> auditor = std::nullopt;
PrettyAmount const xrp = XRP(10'000);
PrettyAmount const xrpHolders = XRP(10'000);
bool fund = true;
@@ -108,6 +112,7 @@ struct MPTInitDef
Env& env;
Account issuer;
Holders holders = {};
std::optional<Account> auditor = std::nullopt;
std::uint16_t transferFee = 0;
std::optional<std::uint64_t> pay = std::nullopt;
std::uint32_t flags = MPTDEXFlags;
@@ -152,18 +157,131 @@ struct MPTSet
std::optional<std::string> metadata = std::nullopt;
std::optional<Account> delegate = std::nullopt;
std::optional<uint256> domainID = std::nullopt;
std::optional<Buffer> issuerPubKey = std::nullopt;
std::optional<Buffer> auditorPubKey = std::nullopt;
std::optional<TER> err = std::nullopt;
};
struct MPTConvert
{
std::optional<Account> account = std::nullopt;
std::optional<MPTID> id = std::nullopt;
std::optional<std::uint64_t> amt = std::nullopt;
std::optional<std::string> proof = std::nullopt;
std::optional<bool> fillAuditorEncryptedAmt = true;
// indicates whether to autofill schnorr proof.
// default : auto generate proof if holderPubKey is present.
// true: force proof generation.
// false: force proof omission.
std::optional<bool> fillSchnorrProof = std::nullopt;
std::optional<Buffer> holderPubKey = std::nullopt;
std::optional<Buffer> holderEncryptedAmt = std::nullopt;
std::optional<Buffer> issuerEncryptedAmt = std::nullopt;
std::optional<Buffer> auditorEncryptedAmt = std::nullopt;
std::optional<Buffer> blindingFactor = std::nullopt;
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<TER> err = std::nullopt;
};
struct MPTMergeInbox
{
std::optional<Account> account = std::nullopt;
std::optional<MPTID> id = std::nullopt;
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<TER> err = std::nullopt;
};
struct MPTConfidentialSend
{
std::optional<Account> account = std::nullopt;
std::optional<Account> dest = std::nullopt;
std::optional<MPTID> id = std::nullopt;
// amt is to generate encrypted amounts for testing purposes
std::optional<std::uint64_t> amt = std::nullopt;
std::optional<std::string> proof = std::nullopt;
std::optional<Buffer> senderEncryptedAmt = std::nullopt;
std::optional<Buffer> destEncryptedAmt = std::nullopt;
std::optional<Buffer> issuerEncryptedAmt = std::nullopt;
std::optional<Buffer> auditorEncryptedAmt = std::nullopt;
std::optional<std::vector<std::string>> credentials = std::nullopt;
// not an txn param, only used for autofilling
std::optional<Buffer> blindingFactor = std::nullopt;
std::optional<Buffer> amountCommitment = std::nullopt;
std::optional<Buffer> balanceCommitment = std::nullopt;
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<TER> err = std::nullopt;
};
struct MPTConvertBack
{
std::optional<Account> account = std::nullopt;
std::optional<MPTID> id = std::nullopt;
std::optional<std::uint64_t> amt = std::nullopt;
std::optional<Buffer> proof = std::nullopt;
std::optional<Buffer> holderEncryptedAmt = std::nullopt;
std::optional<Buffer> issuerEncryptedAmt = std::nullopt;
std::optional<Buffer> auditorEncryptedAmt = std::nullopt;
std::optional<bool> fillAuditorEncryptedAmt = true;
// not an txn param, only used for autofilling
std::optional<Buffer> blindingFactor = std::nullopt;
std::optional<Buffer> pedersenCommitment = std::nullopt;
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<TER> err = std::nullopt;
};
struct MPTConfidentialClawback
{
std::optional<Account> account = std::nullopt;
std::optional<Account> holder = std::nullopt;
std::optional<MPTID> id = std::nullopt;
std::optional<std::uint64_t> amt = std::nullopt;
std::optional<std::string> proof = std::nullopt;
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<TER> err = std::nullopt;
};
/**
* @brief Stores the parameterss that are exclusively used to generate a
* pedersen linkage proof
*/
struct PedersenProofParams
{
Buffer const pedersenCommitment;
uint64_t const amt; // either spending balance or value to be transferred
Buffer const encryptedAmt;
Buffer const blindingFactor;
};
class MPTTester
{
Env& env_;
Account const issuer_;
std::unordered_map<std::string, Account> const holders_;
std::optional<Account> const auditor_;
std::optional<MPTID> id_;
bool close_;
std::unordered_map<AccountID, Buffer> pubKeys;
std::unordered_map<AccountID, Buffer> privKeys;
public:
enum EncryptedBalanceType {
ISSUER_ENCRYPTED_BALANCE,
HOLDER_ENCRYPTED_INBOX,
HOLDER_ENCRYPTED_SPENDING,
AUDITOR_ENCRYPTED_BALANCE,
};
MPTTester(Env& env, Account const& issuer, MPTInit const& constr = {});
MPTTester(MPTInitDef const& constr);
MPTTester(
@@ -201,6 +319,21 @@ public:
static Json::Value
setjv(MPTSet const& set = {});
void
convert(MPTConvert const& arg = MPTConvert{});
void
mergeInbox(MPTMergeInbox const& arg = MPTMergeInbox{});
void
send(MPTConfidentialSend const& arg = MPTConfidentialSend{});
void
convertBack(MPTConvertBack const& arg = MPTConvertBack{});
void
confidentialClaw(MPTConfidentialClawback const& arg = MPTConfidentialClawback{});
[[nodiscard]] bool
checkDomainID(std::optional<uint256> expected) const;
@@ -210,6 +343,9 @@ public:
[[nodiscard]] bool
checkMPTokenOutstandingAmount(std::int64_t expectedAmount) const;
[[nodiscard]] bool
checkIssuanceConfidentialBalance(std::int64_t expectedAmount) const;
[[nodiscard]] bool
checkFlags(uint32_t const expectedFlags, std::optional<Account> const& holder = std::nullopt) const;
@@ -230,6 +366,7 @@ public:
{
return issuer_;
}
Account const&
holder(std::string const& h) const;
@@ -257,6 +394,12 @@ public:
std::int64_t
getBalance(Account const& account) const;
std::int64_t
getIssuanceConfidentialBalance() const;
std::optional<Buffer>
getEncryptedBalance(Account const& account, EncryptedBalanceType option = HOLDER_ENCRYPTED_INBOX) const;
MPT
operator[](std::string const& name) const;
@@ -265,6 +408,79 @@ public:
operator Asset() const;
bool
printMPT(Account const& holder_) const;
void
generateKeyPair(Account const& account);
std::optional<Buffer>
getPubKey(Account const& account) const;
std::optional<Buffer>
getPrivKey(Account const& account) const;
Buffer
encryptAmount(Account const& account, uint64_t const amt, Buffer const& blindingFactor) const;
std::optional<uint64_t>
decryptAmount(Account const& account, Buffer const& amt) const;
std::optional<uint64_t>
getDecryptedBalance(Account const& account, EncryptedBalanceType balanceType) const;
std::int64_t
getIssuanceOutstandingBalance() const;
std::optional<Buffer>
getClawbackProof(Account const& holder, std::uint64_t amount, Buffer const& privateKey, uint256 const& txHash)
const;
std::optional<Buffer>
getSchnorrProof(Account const& account, uint256 const& ctxHash) const;
std::optional<Buffer>
getConfidentialSendProof(
Account const& sender,
std::uint64_t const amount,
std::vector<ConfidentialRecipient> const& recipients,
Slice const& blindingFactor,
std::size_t const nRecipients,
uint256 const& contextHash,
PedersenProofParams const& amountParams,
PedersenProofParams const& balanceParams) const;
Buffer
getConvertBackProof(
Account const& holder,
std::uint64_t const amount,
uint256 const& contextHash,
Buffer const& holderCiphertext,
Buffer const& issuerCiphertext,
std::optional<Buffer> const& auditorCiphertext,
Buffer const& blindingFactor,
PedersenProofParams const& pcParams) const;
std::uint32_t
getMPTokenVersion(Account const account) const;
Buffer
getAmountLinkageProof(
Buffer const& pubKey,
Buffer const& blindingFactor,
uint256 const& contextHash,
PedersenProofParams const& params) const;
Buffer
getBalanceLinkageProof(
Account const& account,
uint256 const& contextHash,
Buffer const& pubKey,
PedersenProofParams const& params) const;
Buffer
getPedersenCommitment(std::uint64_t const amount, Buffer const& pedersenBlindingFactor);
private:
using SLEP = SLE::const_pointer;
bool
@@ -294,6 +510,16 @@ private:
std::uint32_t
getFlags(std::optional<Account> const& holder) const;
template <typename T>
void
fillConversionCiphertexts(
T const& arg,
Json::Value& jv,
Buffer& holderCiphertext,
Buffer& issuerCiphertext,
std::optional<Buffer>& auditorCiphertext,
Buffer& blindingFactor) const;
};
} // namespace jtx

View File

@@ -138,7 +138,7 @@ public:
{
std::shared_ptr<NodeObject> object;
Status const status = backend.fetch(batch[i]->getHash(), &object);
Status const status = backend.fetch(batch[i]->getHash().cbegin(), &object);
BEAST_EXPECT(status == ok);
@@ -158,7 +158,7 @@ public:
{
std::shared_ptr<NodeObject> object;
Status const status = backend.fetch(batch[i]->getHash(), &object);
Status const status = backend.fetch(batch[i]->getHash().cbegin(), &object);
BEAST_EXPECT(status == notFound);
}

View File

@@ -313,7 +313,7 @@ public:
std::shared_ptr<NodeObject> obj;
std::shared_ptr<NodeObject> result;
obj = seq1_.obj(dist_(gen_));
backend_.fetch(obj->getHash(), &result);
backend_.fetch(obj->getHash().data(), &result);
suite_.expect(result && isSame(result, obj));
}
catch (std::exception const& e)
@@ -371,9 +371,9 @@ public:
{
try
{
auto const hash = seq2_.key(i);
auto const key = seq2_.key(i);
std::shared_ptr<NodeObject> result;
backend_.fetch(hash, &result);
backend_.fetch(key.data(), &result);
suite_.expect(!result);
}
catch (std::exception const& e)
@@ -438,9 +438,9 @@ public:
{
if (rand_(gen_) < missingNodePercent)
{
auto const hash = seq2_.key(dist_(gen_));
auto const key = seq2_.key(dist_(gen_));
std::shared_ptr<NodeObject> result;
backend_.fetch(hash, &result);
backend_.fetch(key.data(), &result);
suite_.expect(!result);
}
else
@@ -448,7 +448,7 @@ public:
std::shared_ptr<NodeObject> obj;
std::shared_ptr<NodeObject> result;
obj = seq1_.obj(dist_(gen_));
backend_.fetch(obj->getHash(), &result);
backend_.fetch(obj->getHash().data(), &result);
suite_.expect(result && isSame(result, obj));
}
}
@@ -525,7 +525,7 @@ public:
auto const j = older_(gen_);
obj = seq1_.obj(j);
std::shared_ptr<NodeObject> result1;
backend_.fetch(obj->getHash(), &result);
backend_.fetch(obj->getHash().data(), &result);
suite_.expect(result != nullptr);
suite_.expect(isSame(result, obj));
}
@@ -543,7 +543,7 @@ public:
std::shared_ptr<NodeObject> result;
auto const j = recent_(gen_);
obj = seq1_.obj(j);
backend_.fetch(obj->getHash(), &result);
backend_.fetch(obj->getHash().data(), &result);
suite_.expect(!result || isSame(result, obj));
break;
}

View File

@@ -8,12 +8,8 @@ add_custom_target(xrpl.tests)
# Test helpers
add_library(xrpl.helpers.test STATIC)
target_sources(xrpl.helpers.test PRIVATE
helpers/TestSink.cpp
)
target_include_directories(xrpl.helpers.test PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_sources(xrpl.helpers.test PRIVATE helpers/TestSink.cpp)
target_include_directories(xrpl.helpers.test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(xrpl.helpers.test PRIVATE xrpl.libxrpl)
# Common library dependencies for the rest of the tests.
@@ -34,8 +30,8 @@ target_link_libraries(xrpl.test.json PRIVATE xrpl.imports.test)
add_dependencies(xrpl.tests xrpl.test.json)
# Network unit tests are currently not supported on Windows
if(NOT WIN32)
xrpl_add_test(net)
target_link_libraries(xrpl.test.net PRIVATE xrpl.imports.test)
add_dependencies(xrpl.tests xrpl.test.net)
endif()
if (NOT WIN32)
xrpl_add_test(net)
target_link_libraries(xrpl.test.net PRIVATE xrpl.imports.test)
add_dependencies(xrpl.tests xrpl.test.net)
endif ()

View File

@@ -130,6 +130,14 @@ std::unique_ptr<NodeStore::Database>
SHAMapStoreImp::makeNodeStore(int readThreads)
{
auto nscfg = app_.config().section(ConfigSection::nodeDatabase());
// Provide default values:
if (!nscfg.exists("cache_size"))
nscfg.set("cache_size", std::to_string(app_.config().getValueFor(SizedItem::treeCacheSize, std::nullopt)));
if (!nscfg.exists("cache_age"))
nscfg.set("cache_age", std::to_string(app_.config().getValueFor(SizedItem::treeCacheAge, std::nullopt)));
std::unique_ptr<NodeStore::Database> db;
if (deleteInterval_)
@@ -218,6 +226,8 @@ SHAMapStoreImp::run()
LedgerIndex lastRotated = state_db_.getState().lastRotated;
netOPs_ = &app_.getOPs();
ledgerMaster_ = &app_.getLedgerMaster();
fullBelowCache_ = &(*app_.getNodeFamily().getFullBelowCache());
treeNodeCache_ = &(*app_.getNodeFamily().getTreeNodeCache());
if (advisoryDelete_)
canDelete_ = state_db_.getCanDelete();
@@ -480,12 +490,16 @@ void
SHAMapStoreImp::clearCaches(LedgerIndex validatedSeq)
{
ledgerMaster_->clearLedgerCachePrior(validatedSeq);
fullBelowCache_->clear();
}
void
SHAMapStoreImp::freshenCaches()
{
freshenCache(*app_.getNodeFamily().getTreeNodeCache()) && freshenCache(app_.getMasterTransaction().getCache());
if (freshenCache(*treeNodeCache_))
return;
if (freshenCache(app_.getMasterTransaction().getCache()))
return;
}
void

View File

@@ -94,6 +94,8 @@ private:
// as of run() or before
NetworkOPs* netOPs_ = nullptr;
LedgerMaster* ledgerMaster_ = nullptr;
FullBelowCache* fullBelowCache_ = nullptr;
TreeNodeCache* treeNodeCache_ = nullptr;
static constexpr auto nodeStoreName_ = "NodeStore";

View File

@@ -0,0 +1,160 @@
#include <xrpld/app/tx/detail/ConfidentialMPTClawback.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
namespace xrpl {
NotTEC
ConfidentialMPTClawback::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
auto const account = ctx.tx[sfAccount];
// Only issuer can clawback
if (account != MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer())
return temMALFORMED;
// Cannot clawback from self
if (account == ctx.tx[sfHolder])
return temMALFORMED;
// Check invalid claw amount
auto const clawAmount = ctx.tx[sfMPTAmount];
if (clawAmount == 0 || clawAmount > maxMPTokenAmount)
return temBAD_AMOUNT;
// Verify proof length
if (ctx.tx[sfZKProof].length() != ecEqualityProofLength)
return temMALFORMED;
return tesSUCCESS;
}
TER
ConfidentialMPTClawback::preclaim(PreclaimContext const& ctx)
{
// Check if sender account exists
auto const account = ctx.tx[sfAccount];
if (!ctx.view.exists(keylet::account(account)))
return terNO_ACCOUNT;
// Check if holder account exists
auto const holder = ctx.tx[sfHolder];
if (!ctx.view.exists(keylet::account(holder)))
return tecNO_TARGET;
// Check if MPT issuance exists
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
// Sanity check: issuer must be the same as account
if (sleIssuance->getAccountID(sfIssuer) != account)
return tefINTERNAL; // LCOV_EXCL_LINE
// Check if issuance has issuer ElGamal public key
if (!sleIssuance->isFieldPresent(sfIssuerElGamalPublicKey))
return tecNO_PERMISSION;
// Check if clawback is allowed
if (!sleIssuance->isFlag(lsfMPTCanClawback))
return tecNO_PERMISSION;
// Check holder's MPToken
auto const sleHolderMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, holder));
if (!sleHolderMPToken)
return tecOBJECT_NOT_FOUND;
// Check if holder has confidential balances to claw back
if (!sleHolderMPToken->isFieldPresent(sfIssuerEncryptedBalance))
return tecNO_PERMISSION;
// Sanity check: claw amount can not exceed confidential outstanding amount
auto const amount = ctx.tx[sfMPTAmount];
if (amount > (*sleIssuance)[~sfConfidentialOutstandingAmount].value_or(0))
return tecINSUFFICIENT_FUNDS;
auto const contextHash = getClawbackContextHash(account, ctx.tx[sfSequence], mptIssuanceID, amount, holder);
// Verify the revealed confidential amount by the issuer matches the exact
// confidential balance of the holder.
return verifyClawbackEqualityProof(
amount,
ctx.tx[sfZKProof],
(*sleIssuance)[sfIssuerElGamalPublicKey],
(*sleHolderMPToken)[sfIssuerEncryptedBalance],
contextHash);
}
TER
ConfidentialMPTClawback::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto const holder = ctx_.tx[sfHolder];
auto sleIssuance = view().peek(keylet::mptIssuance(mptIssuanceID));
auto sleHolderMPToken = view().peek(keylet::mptoken(mptIssuanceID, holder));
if (!sleIssuance || !sleHolderMPToken)
return tecINTERNAL;
auto const clawAmount = ctx_.tx[sfMPTAmount];
Slice const holderPubKey = (*sleHolderMPToken)[sfHolderElGamalPublicKey];
Slice const issuerPubKey = (*sleIssuance)[sfIssuerElGamalPublicKey];
// After clawback, the balance should be encrypted zero.
auto const encZeroForHolder = encryptCanonicalZeroAmount(holderPubKey, holder, mptIssuanceID);
if (!encZeroForHolder)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const encZeroForIssuer = encryptCanonicalZeroAmount(issuerPubKey, holder, mptIssuanceID);
if (!encZeroForIssuer)
return tecINTERNAL; // LCOV_EXCL_LINE
// Set holder's confidential balances to encrypted zero
(*sleHolderMPToken)[sfConfidentialBalanceInbox] = *encZeroForHolder;
(*sleHolderMPToken)[sfConfidentialBalanceSpending] = *encZeroForHolder;
(*sleHolderMPToken)[sfIssuerEncryptedBalance] = *encZeroForIssuer;
(*sleHolderMPToken)[sfConfidentialBalanceVersion] = 0;
if (sleHolderMPToken->isFieldPresent(sfAuditorEncryptedBalance))
{
// Sanity check: the issuance must have an auditor public key if
// auditing is enabled.
if (!sleIssuance->isFieldPresent(sfAuditorElGamalPublicKey))
return tecINTERNAL; // LCOV_EXCL_LINE
Slice const auditorPubKey = (*sleIssuance)[sfAuditorElGamalPublicKey];
auto const encZeroForAuditor = encryptCanonicalZeroAmount(auditorPubKey, holder, mptIssuanceID);
if (!encZeroForAuditor)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleHolderMPToken)[sfAuditorEncryptedBalance] = *encZeroForAuditor;
}
// Decrease Global Confidential Outstanding Amount
auto const oldCOA = (*sleIssuance)[sfConfidentialOutstandingAmount];
(*sleIssuance)[sfConfidentialOutstandingAmount] = oldCOA - clawAmount;
// Decrease Global Total Outstanding Amount
auto const oldOA = (*sleIssuance)[sfOutstandingAmount];
(*sleIssuance)[sfOutstandingAmount] = oldOA - clawAmount;
view().update(sleHolderMPToken);
view().update(sleIssuance);
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -0,0 +1,29 @@
#ifndef XRPL_TX_CONFIDENTIALCLAWSBACK_H_INCLUDED
#define XRPL_TX_CONFIDENTIALCLAWSBACK_H_INCLUDED
#include <xrpld/app/tx/detail/Transactor.h>
namespace xrpl {
class ConfidentialMPTClawback : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ConfidentialMPTClawback(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace xrpl
#endif

View File

@@ -0,0 +1,249 @@
#include <xrpld/app/tx/detail/ConfidentialMPTConvert.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
namespace xrpl {
NotTEC
ConfidentialMPTConvert::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
// issuer cannot convert
if (MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer() == ctx.tx[sfAccount])
return temMALFORMED;
if (ctx.tx[sfMPTAmount] > maxMPTokenAmount)
return temBAD_AMOUNT;
if (ctx.tx[sfBlindingFactor].size() != ecBlindingFactorLength)
return temMALFORMED;
if (ctx.tx.isFieldPresent(sfHolderElGamalPublicKey))
{
if (ctx.tx[sfHolderElGamalPublicKey].length() != ecPubKeyLength)
return temMALFORMED;
// proof of knowledge of the secret key corresponding to the provided
// public key is needed when holder ec public key is being set.
if (!ctx.tx.isFieldPresent(sfZKProof))
return temMALFORMED;
// verify schnorr proof length when registerring holder ec public key
if (ctx.tx[sfZKProof].size() != ecSchnorrProofLength)
return temMALFORMED;
}
else
{
// zkp should not be present if public key was already set
if (ctx.tx.isFieldPresent(sfZKProof))
return temMALFORMED;
}
// check encrypted amount format after the above basic checks
// this check is more expensive so put it at the end
if (auto const res = checkEncryptedAmountFormat(ctx.tx); !isTesSuccess(res))
return res;
return tesSUCCESS;
}
TER
ConfidentialMPTConvert::preclaim(PreclaimContext const& ctx)
{
auto const account = ctx.tx[sfAccount];
auto const issuanceID = ctx.tx[sfMPTokenIssuanceID];
auto const amount = ctx.tx[sfMPTAmount];
// ensure that issuance exists
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(issuanceID));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
if (!sleIssuance->isFlag(lsfMPTCanPrivacy))
return tecNO_PERMISSION;
// already checked in preflight, but should also check that issuer on the
// issuance isn't the account either
if (sleIssuance->getAccountID(sfIssuer) == account)
return tefINTERNAL; // LCOV_EXCL_LINE
// issuer has not uploaded their pub key yet
if (!sleIssuance->isFieldPresent(sfIssuerElGamalPublicKey))
return tecNO_PERMISSION;
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorElGamalPublicKey);
// tx must include auditor ciphertext if the issuance has enabled
// auditing, and must not include it if auditing is not enabled
if (requiresAuditor != hasAuditor)
return tecNO_PERMISSION;
auto const sleMptoken = ctx.view.read(keylet::mptoken(issuanceID, account));
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
auto const mptIssue = MPTIssue{issuanceID};
STAmount const mptAmount = STAmount(MPTAmount{static_cast<MPTAmount::value_type>(amount)}, mptIssue);
if (accountHolds(
ctx.view,
account,
mptIssue,
FreezeHandling::fhZERO_IF_FROZEN,
AuthHandling::ahZERO_IF_UNAUTHORIZED,
ctx.j) < mptAmount)
{
return tecINSUFFICIENT_FUNDS;
}
auto const hasHolderKeyOnLedger = sleMptoken->isFieldPresent(sfHolderElGamalPublicKey);
auto const hasHolderKeyInTx = ctx.tx.isFieldPresent(sfHolderElGamalPublicKey);
// must have pk to convert
if (!hasHolderKeyOnLedger && !hasHolderKeyInTx)
return tecNO_PERMISSION;
// can't update if there's already a pk
if (hasHolderKeyOnLedger && hasHolderKeyInTx)
return tecDUPLICATE;
Slice holderPubKey;
if (hasHolderKeyInTx)
{
holderPubKey = ctx.tx[sfHolderElGamalPublicKey];
auto const contextHash = getConvertContextHash(account, ctx.tx[sfSequence], issuanceID, amount);
// when register new pk, verify through schnorr proof
if (!isTesSuccess(verifySchnorrProof(holderPubKey, ctx.tx[sfZKProof], contextHash)))
{
return tecBAD_PROOF;
}
}
else
{
holderPubKey = (*sleMptoken)[sfHolderElGamalPublicKey];
}
std::optional<ConfidentialRecipient> auditor;
if (hasAuditor)
{
auditor.emplace(
ConfidentialRecipient{(*sleIssuance)[sfAuditorElGamalPublicKey], ctx.tx[sfAuditorEncryptedAmount]});
}
return verifyRevealedAmount(
amount,
ctx.tx[sfBlindingFactor],
{holderPubKey, ctx.tx[sfHolderEncryptedAmount]},
{(*sleIssuance)[sfIssuerElGamalPublicKey], ctx.tx[sfIssuerEncryptedAmount]},
auditor);
}
TER
ConfidentialMPTConvert::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, account_));
if (!sleMptoken)
return tecINTERNAL;
auto sleIssuance = view().peek(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tecINTERNAL;
auto const amtToConvert = ctx_.tx[sfMPTAmount];
auto const amt = (*sleMptoken)[~sfMPTAmount].value_or(0);
if (ctx_.tx.isFieldPresent(sfHolderElGamalPublicKey))
(*sleMptoken)[sfHolderElGamalPublicKey] = ctx_.tx[sfHolderElGamalPublicKey];
(*sleMptoken)[sfMPTAmount] = amt - amtToConvert;
(*sleIssuance)[sfConfidentialOutstandingAmount] =
(*sleIssuance)[~sfConfidentialOutstandingAmount].value_or(0) + amtToConvert;
Slice const holderEc = ctx_.tx[sfHolderEncryptedAmount];
Slice const issuerEc = ctx_.tx[sfIssuerEncryptedAmount];
auto const auditorEc = ctx_.tx[~sfAuditorEncryptedAmount];
// todo: we should check sfConfidentialBalanceSpending depending on
// if we encrypt zero amount
if (sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) &&
sleMptoken->isFieldPresent(sfConfidentialBalanceSpending))
{
// homomorphically add holder's encrypted balance
{
Buffer sum(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(holderEc, (*sleMptoken)[sfConfidentialBalanceInbox], sum);
!isTesSuccess(ter))
return tecINTERNAL;
(*sleMptoken)[sfConfidentialBalanceInbox] = sum;
}
// homomorphically add issuer's encrypted balance
{
Buffer sum(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(issuerEc, (*sleMptoken)[sfIssuerEncryptedBalance], sum);
!isTesSuccess(ter))
return tecINTERNAL;
(*sleMptoken)[sfIssuerEncryptedBalance] = sum;
}
// homomorphically add auditor's encrypted balance
if (auditorEc)
{
Buffer sum(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(*auditorEc, (*sleMptoken)[sfAuditorEncryptedBalance], sum);
!isTesSuccess(ter))
return tecINTERNAL;
(*sleMptoken)[sfAuditorEncryptedBalance] = sum;
}
}
else if (
!sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
!sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) &&
!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending))
{
(*sleMptoken)[sfConfidentialBalanceInbox] = holderEc;
(*sleMptoken)[sfIssuerEncryptedBalance] = issuerEc;
(*sleMptoken)[sfConfidentialBalanceVersion] = 0;
if (auditorEc)
(*sleMptoken)[sfAuditorEncryptedBalance] = *auditorEc;
// encrypt sfConfidentialBalanceSpending with zero balance
auto const zeroBalance =
encryptCanonicalZeroAmount((*sleMptoken)[sfHolderElGamalPublicKey], account_, mptIssuanceID);
if (!zeroBalance)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceSpending] = *zeroBalance;
}
else
{
// both sfIssuerEncryptedBalance and sfConfidentialBalanceInbox should
// exist together
return tecINTERNAL; // LCOV_EXCL_LINE
}
view().update(sleIssuance);
view().update(sleMptoken);
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -0,0 +1,29 @@
#ifndef XRPL_TX_CONFIDENTIALCONVERT_H_INCLUDED
#define XRPL_TX_CONFIDENTIALCONVERT_H_INCLUDED
#include <xrpld/app/tx/detail/Transactor.h>
namespace xrpl {
class ConfidentialMPTConvert : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ConfidentialMPTConvert(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace xrpl
#endif

View File

@@ -0,0 +1,230 @@
#include <xrpld/app/tx/detail/ConfidentialMPTConvertBack.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <cstddef>
namespace xrpl {
NotTEC
ConfidentialMPTConvertBack::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
// issuer cannot convert back
if (MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer() == ctx.tx[sfAccount])
return temMALFORMED;
if (ctx.tx[sfMPTAmount] == 0 || ctx.tx[sfMPTAmount] > maxMPTokenAmount)
return temBAD_AMOUNT;
if (ctx.tx[sfBlindingFactor].size() != ecBlindingFactorLength)
return temMALFORMED;
if (ctx.tx[sfBalanceCommitment].size() != ecPedersenCommitmentLength)
return temMALFORMED;
// check encrypted amount format after the above basic checks
// this check is more expensive so put it at the end
if (auto const res = checkEncryptedAmountFormat(ctx.tx); !isTesSuccess(res))
return res;
return tesSUCCESS;
}
TER
verifyProofs(STTx const& tx, std::shared_ptr<SLE const> const& issuance, std::shared_ptr<SLE const> const& mptoken)
{
if (!mptoken->isFieldPresent(sfHolderElGamalPublicKey))
return tecINTERNAL; // LCOV_EXCL_LINE
auto const mptIssuanceID = tx[sfMPTokenIssuanceID];
auto const account = tx[sfAccount];
auto const amount = tx[sfMPTAmount];
auto const blindingFactor = tx[sfBlindingFactor];
auto const holderPubKey = (*mptoken)[sfHolderElGamalPublicKey];
auto const contextHash = getConvertBackContextHash(
account, tx[sfSequence], mptIssuanceID, amount, (*mptoken)[~sfConfidentialBalanceVersion].value_or(0));
// Prepare Auditor Info
std::optional<ConfidentialRecipient> auditor;
bool const hasAuditor = issuance->isFieldPresent(sfAuditorElGamalPublicKey);
if (hasAuditor)
{
auditor.emplace(ConfidentialRecipient{(*issuance)[sfAuditorElGamalPublicKey], tx[sfAuditorEncryptedAmount]});
}
if (auto const ter = verifyRevealedAmount(
amount,
blindingFactor,
{holderPubKey, tx[sfHolderEncryptedAmount]},
{(*issuance)[sfIssuerElGamalPublicKey], tx[sfIssuerEncryptedAmount]},
auditor);
!isTesSuccess(ter))
{
return ter;
}
// Use a pointer to parse each proof component
Buffer zkps = Buffer(tx[sfZKProof].data(), tx[sfZKProof].size());
std::uint8_t* ptr = zkps.data();
// verify el gamal pedersen linkage
{
Buffer const pedersen{ptr, ecPedersenProofLength};
if (auto const ter = verifyBalancePcmLinkage(
pedersen,
(*mptoken)[sfConfidentialBalanceSpending],
holderPubKey,
tx[sfBalanceCommitment],
contextHash);
!isTesSuccess(ter))
{
return ter;
}
// increment pointer
ptr += ecPedersenProofLength;
}
return tesSUCCESS;
}
TER
ConfidentialMPTConvertBack::preclaim(PreclaimContext const& ctx)
{
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
auto const account = ctx.tx[sfAccount];
auto const amount = ctx.tx[sfMPTAmount];
// ensure that issuance exists
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
if (!sleIssuance->isFlag(lsfMPTCanPrivacy))
return tecNO_PERMISSION;
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorElGamalPublicKey);
// tx must include auditor ciphertext if the issuance has enabled
// auditing
if (requiresAuditor && !hasAuditor)
return tecNO_PERMISSION;
// if auditing is not supported then user should not upload auditor
// ciphertext
if (!requiresAuditor && hasAuditor)
return tecNO_PERMISSION;
// already checked in preflight, but should also check that issuer on
// the issuance isn't the account either
if (sleIssuance->getAccountID(sfIssuer) == account)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const sleMptoken = ctx.view.read(keylet::mptoken(mptIssuanceID, account));
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
if (!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
!sleMptoken->isFieldPresent(sfHolderElGamalPublicKey))
{
return tecNO_PERMISSION;
}
// if the total circulating confidential balance is smaller than what the
// holder is trying to convert back, we know for sure this txn should
// fail
if ((*sleIssuance)[~sfConfidentialOutstandingAmount].value_or(0) < amount)
{
return tecINSUFFICIENT_FUNDS;
}
// Check lock
MPTIssue const mptIssue(mptIssuanceID);
if (auto const ter = checkFrozen(ctx.view, account, mptIssue); !isTesSuccess(ter))
return ter;
// Check auth
if (auto const ter = requireAuth(ctx.view, mptIssue, account); !isTesSuccess(ter))
return ter;
if (TER const res = verifyProofs(ctx.tx, sleIssuance, sleMptoken); !isTesSuccess(res))
return res;
return tesSUCCESS;
}
TER
ConfidentialMPTConvertBack::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, account_));
if (!sleMptoken)
return tecINTERNAL;
auto sleIssuance = view().peek(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tecINTERNAL;
auto const amtToConvertBack = ctx_.tx[sfMPTAmount];
auto const amt = (*sleMptoken)[~sfMPTAmount].value_or(0);
(*sleMptoken)[sfMPTAmount] = amt + amtToConvertBack;
(*sleIssuance)[sfConfidentialOutstandingAmount] =
(*sleIssuance)[sfConfidentialOutstandingAmount] - amtToConvertBack;
std::optional<Slice> const auditorEc = ctx_.tx[~sfAuditorEncryptedAmount];
// homomorphically subtract holder's encrypted balance
{
Buffer res(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicSubtract(
(*sleMptoken)[sfConfidentialBalanceSpending], ctx_.tx[sfHolderEncryptedAmount], res);
!isTesSuccess(ter))
return tecINTERNAL;
(*sleMptoken)[sfConfidentialBalanceSpending] = res;
}
// homomorphically subtract issuer's encrypted balance
{
Buffer res(ecGamalEncryptedTotalLength);
if (TER const ter =
homomorphicSubtract((*sleMptoken)[sfIssuerEncryptedBalance], ctx_.tx[sfIssuerEncryptedAmount], res);
!isTesSuccess(ter))
return tecINTERNAL;
(*sleMptoken)[sfIssuerEncryptedBalance] = res;
}
if (auditorEc)
{
Buffer res(ecGamalEncryptedTotalLength);
if (TER const ter =
homomorphicSubtract((*sleMptoken)[sfAuditorEncryptedBalance], ctx_.tx[sfAuditorEncryptedAmount], res);
!isTesSuccess(ter))
return tecINTERNAL;
(*sleMptoken)[sfAuditorEncryptedBalance] = res;
}
// increment version
incrementConfidentialVersion(*sleMptoken);
view().update(sleIssuance);
view().update(sleMptoken);
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -0,0 +1,29 @@
#ifndef XRPL_TX_CONFIDENTIALCONVERTBACK_H_INCLUDED
#define XRPL_TX_CONFIDENTIALCONVERTBACK_H_INCLUDED
#include <xrpld/app/tx/detail/Transactor.h>
namespace xrpl {
class ConfidentialMPTConvertBack : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ConfidentialMPTConvertBack(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace xrpl
#endif

View File

@@ -0,0 +1,92 @@
#include <xrpld/app/tx/detail/ConfidentialMPTMergeInbox.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
namespace xrpl {
NotTEC
ConfidentialMPTMergeInbox::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
// issuer cannot merge
if (MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer() == ctx.tx[sfAccount])
return temMALFORMED;
return tesSUCCESS;
}
TER
ConfidentialMPTMergeInbox::preclaim(PreclaimContext const& ctx)
{
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID]));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
if (!sleIssuance->isFlag(lsfMPTCanPrivacy))
return tecNO_PERMISSION;
// already checked in preflight, but should also check that issuer on the
// issuance isn't the account either
if (sleIssuance->getAccountID(sfIssuer) == ctx.tx[sfAccount])
return tefINTERNAL; // LCOV_EXCL_LINE
auto const sleMptoken = ctx.view.read(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], ctx.tx[sfAccount]));
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
if (!sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) ||
!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
!sleMptoken->isFieldPresent(sfHolderElGamalPublicKey))
return tecNO_PERMISSION;
return tesSUCCESS;
}
TER
ConfidentialMPTMergeInbox::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, account_));
if (!sleMptoken)
return tecINTERNAL;
// sanity check
if (!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
!sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) ||
!sleMptoken->isFieldPresent(sfHolderElGamalPublicKey))
{
return tecINTERNAL;
}
// homomorphically add holder's encrypted balance
Buffer sum(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(
(*sleMptoken)[sfConfidentialBalanceSpending], (*sleMptoken)[sfConfidentialBalanceInbox], sum);
!isTesSuccess(ter))
return tecINTERNAL;
(*sleMptoken)[sfConfidentialBalanceSpending] = sum;
auto const zeroEncryption =
encryptCanonicalZeroAmount((*sleMptoken)[sfHolderElGamalPublicKey], account_, mptIssuanceID);
if (!zeroEncryption)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceInbox] = *zeroEncryption;
// it's fine if it reaches max uint32, it just resets to 0
(*sleMptoken)[sfConfidentialBalanceVersion] = (*sleMptoken)[~sfConfidentialBalanceVersion].value_or(0u) + 1u;
view().update(sleMptoken);
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -0,0 +1,29 @@
#ifndef XRPL_TX_CONFIDENTIALMERGEINBOX_H_INCLUDED
#define XRPL_TX_CONFIDENTIALMERGEINBOX_H_INCLUDED
#include <xrpld/app/tx/detail/Transactor.h>
namespace xrpl {
class ConfidentialMPTMergeInbox : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ConfidentialMPTMergeInbox(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace xrpl
#endif

View File

@@ -0,0 +1,361 @@
#include <xrpld/app/tx/detail/ConfidentialMPTSend.h>
#include <xrpl/ledger/CredentialHelpers.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
namespace xrpl {
NotTEC
ConfidentialMPTSend::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
auto const account = ctx.tx[sfAccount];
auto const issuer = MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer();
// ConfidentialMPTSend only allows holder to holder, holder to second account,
// and second account to holder transfers. So issuer cannot be the sender.
if (account == issuer)
return temMALFORMED;
// Can not send to self
if (account == ctx.tx[sfDestination])
return temMALFORMED;
// Check the length of the encrypted amounts
if (ctx.tx[sfSenderEncryptedAmount].length() != ecGamalEncryptedTotalLength ||
ctx.tx[sfDestinationEncryptedAmount].length() != ecGamalEncryptedTotalLength ||
ctx.tx[sfIssuerEncryptedAmount].length() != ecGamalEncryptedTotalLength)
return temBAD_CIPHERTEXT;
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
if (hasAuditor && ctx.tx[sfAuditorEncryptedAmount].length() != ecGamalEncryptedTotalLength)
return temBAD_CIPHERTEXT;
// Check the length of the ZKProof
auto const recipientCount = getConfidentialRecipientCount(hasAuditor);
auto const sizeEquality = getMultiCiphertextEqualityProofSize(recipientCount);
auto const sizePedersenLinkage = 2 * ecPedersenProofLength;
if (ctx.tx[sfZKProof].length() != sizeEquality + sizePedersenLinkage)
return temMALFORMED;
// Check the length of Pedersen commitments
if (ctx.tx[sfBalanceCommitment].size() != ecPedersenCommitmentLength ||
ctx.tx[sfAmountCommitment].size() != ecPedersenCommitmentLength)
return temMALFORMED;
// Check the encrypted amount formats, this is more expensive so put it at
// the end
if (!isValidCiphertext(ctx.tx[sfSenderEncryptedAmount]) ||
!isValidCiphertext(ctx.tx[sfDestinationEncryptedAmount]) || !isValidCiphertext(ctx.tx[sfIssuerEncryptedAmount]))
return temBAD_CIPHERTEXT;
if (hasAuditor && !isValidCiphertext(ctx.tx[sfAuditorEncryptedAmount]))
return temBAD_CIPHERTEXT;
return tesSUCCESS;
}
TER
verifySendProofs(
PreclaimContext const& ctx,
std::shared_ptr<SLE const> const& sleSenderMPToken,
std::shared_ptr<SLE const> const& sleDestinationMPToken,
std::shared_ptr<SLE const> const& sleIssuance)
{
// Sanity check
if (!sleSenderMPToken || !sleDestinationMPToken || !sleIssuance)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
auto const recipientCount = getConfidentialRecipientCount(hasAuditor);
auto const proof = ctx.tx[sfZKProof];
size_t remainingLength = proof.size();
size_t currentOffset = 0;
// Extract equality proof
auto const sizeEquality = getMultiCiphertextEqualityProofSize(recipientCount);
if (remainingLength < sizeEquality)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const equalityProof = proof.substr(currentOffset, sizeEquality);
currentOffset += sizeEquality;
remainingLength -= sizeEquality;
// Extract Pedersen linkage proof for amount commitment
if (remainingLength < ecPedersenProofLength)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const amountLinkageProof = proof.substr(currentOffset, ecPedersenProofLength);
currentOffset += ecPedersenProofLength;
remainingLength -= ecPedersenProofLength;
// Extract Pedersen linkage proof for balance commitment
if (remainingLength < ecPedersenProofLength)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const balanceLinkageProof = proof.substr(currentOffset, ecPedersenProofLength);
currentOffset += ecPedersenProofLength;
remainingLength -= ecPedersenProofLength;
// todo: Extract range proof once the lib is ready
if (remainingLength != 0)
return tecINTERNAL; // LCOV_EXCL_LINE
// Prepare receipient list
std::vector<ConfidentialRecipient> recipients;
recipients.reserve(recipientCount);
recipients.push_back({(*sleSenderMPToken)[sfHolderElGamalPublicKey], ctx.tx[sfSenderEncryptedAmount]});
recipients.push_back({(*sleDestinationMPToken)[sfHolderElGamalPublicKey], ctx.tx[sfDestinationEncryptedAmount]});
recipients.push_back({(*sleIssuance)[sfIssuerElGamalPublicKey], ctx.tx[sfIssuerEncryptedAmount]});
if (hasAuditor)
{
recipients.push_back({(*sleIssuance)[sfAuditorElGamalPublicKey], ctx.tx[sfAuditorEncryptedAmount]});
}
// Prepare the context hash
auto const contextHash = getSendContextHash(
ctx.tx[sfAccount],
ctx.tx[sfSequence],
ctx.tx[sfMPTokenIssuanceID],
ctx.tx[sfDestination],
(*sleSenderMPToken)[~sfConfidentialBalanceVersion].value_or(0));
// Verify the multi-ciphertext equality proof
if (auto const ter = verifyMultiCiphertextEqualityProof(equalityProof, recipients, recipientCount, contextHash);
!isTesSuccess(ter))
{
JLOG(ctx.j.trace()) << "ConfidentialMPTSend: Equality proof failed.";
return ter;
}
// Verify amount linkage
if (auto const ter = verifyAmountPcmLinkage(
amountLinkageProof,
ctx.tx[sfSenderEncryptedAmount],
(*sleSenderMPToken)[sfHolderElGamalPublicKey],
ctx.tx[sfAmountCommitment],
contextHash);
!isTesSuccess(ter))
{
JLOG(ctx.j.trace()) << "ConfidentialMPTSend: Amount linkage proof failed.";
return ter;
}
// Verify balance linkage
if (auto const ter = verifyBalancePcmLinkage(
balanceLinkageProof,
(*sleSenderMPToken)[sfConfidentialBalanceSpending],
(*sleSenderMPToken)[sfHolderElGamalPublicKey],
ctx.tx[sfBalanceCommitment],
contextHash);
!isTesSuccess(ter))
{
JLOG(ctx.j.trace()) << "ConfidentialMPTSend: Balance linkage proof failed.";
return ter;
}
return tesSUCCESS;
}
TER
ConfidentialMPTSend::preclaim(PreclaimContext const& ctx)
{
// Check if sender account exists
auto const account = ctx.tx[sfAccount];
if (!ctx.view.exists(keylet::account(account)))
return terNO_ACCOUNT;
// Check if destination account exists
auto const destination = ctx.tx[sfDestination];
if (!ctx.view.exists(keylet::account(destination)))
return tecNO_TARGET;
// Check if MPT issuance exists
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
// Check if the issuance allows transfer
if (!sleIssuance->isFlag(lsfMPTCanTransfer))
return tecNO_AUTH;
// Check if issuance allows confidential transfer
if (!sleIssuance->isFlag(lsfMPTCanPrivacy))
return tecNO_PERMISSION;
// Check if issuance has issuer ElGamal public key
if (!sleIssuance->isFieldPresent(sfIssuerElGamalPublicKey))
return tecNO_PERMISSION;
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorElGamalPublicKey);
// Tx must include auditor ciphertext if the issuance has enabled
// auditing, and must not include it if auditing is not enabled
if (requiresAuditor != hasAuditor)
return tecNO_PERMISSION;
// Sanity check: issuer isn't the sender
if (sleIssuance->getAccountID(sfIssuer) == ctx.tx[sfAccount])
return tefINTERNAL; // LCOV_EXCL_LINE
// Check sender's MPToken existence
auto const sleSenderMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, account));
if (!sleSenderMPToken)
return tecOBJECT_NOT_FOUND;
// Check sender's MPToken has necessary fields for confidential send
if (!sleSenderMPToken->isFieldPresent(sfHolderElGamalPublicKey) ||
!sleSenderMPToken->isFieldPresent(sfConfidentialBalanceSpending) ||
!sleSenderMPToken->isFieldPresent(sfIssuerEncryptedBalance))
return tecNO_PERMISSION;
// Sanity check: MPToken's auditor field must be present if auditing is
// enabled
if (requiresAuditor && !sleSenderMPToken->isFieldPresent(sfAuditorEncryptedBalance))
return tefINTERNAL;
// Check destination's MPToken existence
auto const sleDestinationMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, destination));
if (!sleDestinationMPToken)
return tecOBJECT_NOT_FOUND;
// Check destination's MPToken has necessary fields for confidential send
if (!sleDestinationMPToken->isFieldPresent(sfHolderElGamalPublicKey) ||
!sleDestinationMPToken->isFieldPresent(sfConfidentialBalanceInbox) ||
!sleDestinationMPToken->isFieldPresent(sfIssuerEncryptedBalance))
return tecNO_PERMISSION;
// Check lock
MPTIssue const mptIssue(mptIssuanceID);
if (auto const ter = checkFrozen(ctx.view, account, mptIssue); !isTesSuccess(ter))
return ter;
if (auto const ter = checkFrozen(ctx.view, destination, mptIssue); !isTesSuccess(ter))
return ter;
// Check auth
if (auto const ter = requireAuth(ctx.view, mptIssue, account); !isTesSuccess(ter))
return ter;
if (auto const ter = requireAuth(ctx.view, mptIssue, destination); !isTesSuccess(ter))
return ter;
return verifySendProofs(ctx, sleSenderMPToken, sleDestinationMPToken, sleIssuance);
}
TER
ConfidentialMPTSend::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto const destination = ctx_.tx[sfDestination];
auto sleSenderMPToken = view().peek(keylet::mptoken(mptIssuanceID, account_));
auto sleDestinationMPToken = view().peek(keylet::mptoken(mptIssuanceID, destination));
auto sleDestAcct = view().peek(keylet::account(destination));
if (!sleSenderMPToken || !sleDestinationMPToken || !sleDestAcct)
return tecINTERNAL;
if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, destination, sleDestAcct, ctx_.journal);
!isTesSuccess(err))
return err;
Slice const senderEc = ctx_.tx[sfSenderEncryptedAmount];
Slice const destEc = ctx_.tx[sfDestinationEncryptedAmount];
Slice const issuerEc = ctx_.tx[sfIssuerEncryptedAmount];
auto const auditorEc = ctx_.tx[~sfAuditorEncryptedAmount];
// Subtract from sender's spending balance
{
Slice const curSpending = (*sleSenderMPToken)[sfConfidentialBalanceSpending];
Buffer newSpending(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicSubtract(curSpending, senderEc, newSpending); !isTesSuccess(ter))
return tecINTERNAL;
(*sleSenderMPToken)[sfConfidentialBalanceSpending] = newSpending;
}
// Subtract from issuer's balance
{
Slice const curIssuerEnc = (*sleSenderMPToken)[sfIssuerEncryptedBalance];
Buffer newIssuerEnc(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicSubtract(curIssuerEnc, issuerEc, newIssuerEnc); !isTesSuccess(ter))
return tecINTERNAL;
(*sleSenderMPToken)[sfIssuerEncryptedBalance] = newIssuerEnc;
}
// Subtract from auditor's balance if present
if (auditorEc)
{
Slice const curAuditorEnc = (*sleSenderMPToken)[sfAuditorEncryptedBalance];
Buffer newAuditorEnc(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicSubtract(curAuditorEnc, *auditorEc, newAuditorEnc); !isTesSuccess(ter))
return tecINTERNAL;
(*sleSenderMPToken)[sfAuditorEncryptedBalance] = newAuditorEnc;
}
// Add to destination's inbox balance
{
Slice const curInbox = (*sleDestinationMPToken)[sfConfidentialBalanceInbox];
Buffer newInbox(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(curInbox, destEc, newInbox); !isTesSuccess(ter))
return tecINTERNAL;
(*sleDestinationMPToken)[sfConfidentialBalanceInbox] = newInbox;
}
// Add to issuer's balance
{
Slice const curIssuerEnc = (*sleDestinationMPToken)[sfIssuerEncryptedBalance];
Buffer newIssuerEnc(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(curIssuerEnc, issuerEc, newIssuerEnc); !isTesSuccess(ter))
return tecINTERNAL;
(*sleDestinationMPToken)[sfIssuerEncryptedBalance] = newIssuerEnc;
}
// Add to auditor's balance if present
if (auditorEc)
{
Slice const curAuditorEnc = (*sleDestinationMPToken)[sfAuditorEncryptedBalance];
Buffer newAuditorEnc(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(curAuditorEnc, *auditorEc, newAuditorEnc); !isTesSuccess(ter))
return tecINTERNAL;
(*sleDestinationMPToken)[sfAuditorEncryptedBalance] = newAuditorEnc;
}
// increment version
incrementConfidentialVersion(*sleSenderMPToken);
incrementConfidentialVersion(*sleDestinationMPToken);
view().update(sleSenderMPToken);
view().update(sleDestinationMPToken);
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -0,0 +1,29 @@
#ifndef XRPL_TX_CONFIDENTIALSEND_H_INCLUDED
#define XRPL_TX_CONFIDENTIALSEND_H_INCLUDED
#include <xrpld/app/tx/detail/Transactor.h>
namespace xrpl {
class ConfidentialMPTSend : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ConfidentialMPTSend(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace xrpl
#endif

View File

@@ -70,6 +70,23 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx)
if (ctx.view.rules().enabled(featureSingleAssetVault) && sleMpt->isFlag(lsfMPTLocked))
return tecNO_PERMISSION;
if (ctx.view.rules().enabled(featureConfidentialTransfer))
{
auto const sleMptIssuance = ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID]));
// if there still existing encrypted balances of MPT in
// circulation
if (sleMptIssuance && (*sleMptIssuance)[~sfConfidentialOutstandingAmount].value_or(0) != 0)
{
// this MPT still has encrypted balance, since we don't know
// if it's non-zero or not, we won't allow deletion of
// MPToken
if (sleMpt->isFieldPresent(sfConfidentialBalanceInbox) ||
sleMpt->isFieldPresent(sfConfidentialBalanceSpending))
return tecHAS_OBLIGATIONS;
}
}
return tesSUCCESS;
}

View File

@@ -16,6 +16,14 @@ MPTokenIssuanceCreate::checkExtraFeatures(PreflightContext const& ctx)
if (ctx.tx.isFieldPresent(sfMutableFlags) && !ctx.rules.enabled(featureDynamicMPT))
return false;
if (ctx.tx.isFlag(tfMPTCanPrivacy) && !ctx.rules.enabled(featureConfidentialTransfer))
return false;
// can not set tmfMPTCannotMutatePrivacy without featureConfidentialTransfer
auto const mutableFlags = ctx.tx[~sfMutableFlags];
if (mutableFlags && (*mutableFlags & tmfMPTCannotMutatePrivacy) && !ctx.rules.enabled(featureConfidentialTransfer))
return false;
return true;
}

View File

@@ -27,16 +27,19 @@ struct MPTMutabilityFlags
{
std::uint32_t setFlag;
std::uint32_t clearFlag;
std::uint32_t canMutateFlag;
std::uint32_t mutabilityFlag;
std::uint32_t targetFlag;
bool isCannotMutate = false; // if true, cannot mutate by default.
};
static constexpr std::array<MPTMutabilityFlags, 6> mptMutabilityFlags = {
{{tmfMPTSetCanLock, tmfMPTClearCanLock, lsmfMPTCanMutateCanLock},
{tmfMPTSetRequireAuth, tmfMPTClearRequireAuth, lsmfMPTCanMutateRequireAuth},
{tmfMPTSetCanEscrow, tmfMPTClearCanEscrow, lsmfMPTCanMutateCanEscrow},
{tmfMPTSetCanTrade, tmfMPTClearCanTrade, lsmfMPTCanMutateCanTrade},
{tmfMPTSetCanTransfer, tmfMPTClearCanTransfer, lsmfMPTCanMutateCanTransfer},
{tmfMPTSetCanClawback, tmfMPTClearCanClawback, lsmfMPTCanMutateCanClawback}}};
static constexpr std::array<MPTMutabilityFlags, 7> mptMutabilityFlags = {
{{tmfMPTSetCanLock, tmfMPTClearCanLock, lsmfMPTCanMutateCanLock, lsfMPTCanLock},
{tmfMPTSetRequireAuth, tmfMPTClearRequireAuth, lsmfMPTCanMutateRequireAuth, lsfMPTRequireAuth},
{tmfMPTSetCanEscrow, tmfMPTClearCanEscrow, lsmfMPTCanMutateCanEscrow, lsfMPTCanEscrow},
{tmfMPTSetCanTrade, tmfMPTClearCanTrade, lsmfMPTCanMutateCanTrade, lsfMPTCanTrade},
{tmfMPTSetCanTransfer, tmfMPTClearCanTransfer, lsmfMPTCanMutateCanTransfer, lsfMPTCanTransfer},
{tmfMPTSetCanClawback, tmfMPTClearCanClawback, lsmfMPTCanMutateCanClawback, lsfMPTCanClawback},
{tmfMPTSetPrivacy, tmfMPTClearPrivacy, lsmfMPTCannotMutatePrivacy, lsfMPTCanPrivacy, true}}};
NotTEC
MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
@@ -45,14 +48,27 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
auto const metadata = ctx.tx[~sfMPTokenMetadata];
auto const transferFee = ctx.tx[~sfTransferFee];
auto const isMutate = mutableFlags || metadata || transferFee;
auto const hasIssuerElGamalKey = ctx.tx.isFieldPresent(sfIssuerElGamalPublicKey);
auto const hasAuditorElGamalKey = ctx.tx.isFieldPresent(sfAuditorElGamalPublicKey);
auto const txFlags = ctx.tx.getFlags();
auto const mutatePrivacy = mutableFlags && ((*mutableFlags & (tmfMPTSetPrivacy | tmfMPTClearPrivacy)));
auto const hasDomain = ctx.tx.isFieldPresent(sfDomainID);
auto const hasHolder = ctx.tx.isFieldPresent(sfHolder);
if (isMutate && !ctx.rules.enabled(featureDynamicMPT))
return temDISABLED;
if (ctx.tx.isFieldPresent(sfDomainID) && ctx.tx.isFieldPresent(sfHolder))
if ((hasIssuerElGamalKey || hasAuditorElGamalKey || mutatePrivacy) &&
!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
if (hasDomain && hasHolder)
return temMALFORMED;
auto const txFlags = ctx.tx.getFlags();
if (mutatePrivacy && hasHolder)
return temMALFORMED;
// fails if both flags are set
if ((txFlags & tfMPTLock) && (txFlags & tfMPTUnlock))
@@ -63,10 +79,11 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
if (holderID && accountID == holderID)
return temMALFORMED;
if (ctx.rules.enabled(featureSingleAssetVault) || ctx.rules.enabled(featureDynamicMPT))
if (ctx.rules.enabled(featureSingleAssetVault) || ctx.rules.enabled(featureDynamicMPT) ||
ctx.rules.enabled(featureConfidentialTransfer))
{
// Is this transaction actually changing anything ?
if (txFlags == 0 && !ctx.tx.isFieldPresent(sfDomainID) && !isMutate)
if (txFlags == 0 && !hasDomain && !hasIssuerElGamalKey && !hasAuditorElGamalKey && !isMutate)
return temMALFORMED;
}
@@ -104,6 +121,18 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
}
}
if (hasHolder && (hasIssuerElGamalKey || hasAuditorElGamalKey))
return temMALFORMED;
if (hasAuditorElGamalKey && !hasIssuerElGamalKey)
return temMALFORMED;
if (hasIssuerElGamalKey && ctx.tx[sfIssuerElGamalPublicKey].length() != ecPubKeyLength)
return temMALFORMED;
if (hasAuditorElGamalKey && ctx.tx[sfAuditorElGamalPublicKey].length() != ecPubKeyLength)
return temMALFORMED;
return tesSUCCESS;
}
@@ -193,13 +222,26 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool { return currentMutableFlags & mutableFlag; };
if (auto const mutableFlags = ctx.tx[~sfMutableFlags])
auto const mutableFlags = ctx.tx[~sfMutableFlags];
if (mutableFlags)
{
if (std::any_of(
mptMutabilityFlags.begin(), mptMutabilityFlags.end(), [mutableFlags, &isMutableFlag](auto const& f) {
return !isMutableFlag(f.canMutateFlag) && ((*mutableFlags & (f.setFlag | f.clearFlag)));
bool const canMutate =
f.isCannotMutate ? isMutableFlag(f.mutabilityFlag) : !isMutableFlag(f.mutabilityFlag);
return canMutate && (*mutableFlags & (f.setFlag | f.clearFlag));
}))
return tecNO_PERMISSION;
if ((*mutableFlags & tmfMPTSetPrivacy) || (*mutableFlags & tmfMPTClearPrivacy))
{
std::uint64_t const confidentialOA = (*sleMptIssuance)[~sfConfidentialOutstandingAmount].value_or(0);
// If there's any confidential outstanding amount, disallow toggling
// the lsfMPTCanPrivacy flag
if (confidentialOA > 0)
return tecNO_PERMISSION;
}
}
if (!isMutableFlag(lsmfMPTCanMutateMetadata) && ctx.tx.isFieldPresent(sfMPTokenMetadata))
@@ -218,6 +260,35 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
return tecNO_PERMISSION;
}
// cannot update issuer public key
if (ctx.tx.isFieldPresent(sfIssuerElGamalPublicKey) && sleMptIssuance->isFieldPresent(sfIssuerElGamalPublicKey))
{
return tecNO_PERMISSION;
}
// cannot update auditor public key
if (ctx.tx.isFieldPresent(sfAuditorElGamalPublicKey) && sleMptIssuance->isFieldPresent(sfAuditorElGamalPublicKey))
{
return tecNO_PERMISSION; // LCOV_EXCL_LINE
}
if (ctx.tx.isFieldPresent(sfIssuerElGamalPublicKey) && !sleMptIssuance->isFlag(lsfMPTCanPrivacy))
{
return tecNO_PERMISSION;
}
if (ctx.tx.isFieldPresent(sfAuditorElGamalPublicKey) && !sleMptIssuance->isFlag(lsfMPTCanPrivacy))
{
return tecNO_PERMISSION;
}
// cannot upload key if there's circulating supply of COA
if ((ctx.tx.isFieldPresent(sfIssuerElGamalPublicKey) || ctx.tx.isFieldPresent(sfAuditorElGamalPublicKey)) &&
sleMptIssuance->isFieldPresent(sfConfidentialOutstandingAmount))
{
return tecNO_PERMISSION; // LCOV_EXCL_LINE
}
return tesSUCCESS;
}
@@ -251,9 +322,9 @@ MPTokenIssuanceSet::doApply()
for (auto const& f : mptMutabilityFlags)
{
if (mutableFlags & f.setFlag)
flagsOut |= f.canMutateFlag;
flagsOut |= f.targetFlag;
else if (mutableFlags & f.clearFlag)
flagsOut &= ~f.canMutateFlag;
flagsOut &= ~f.targetFlag;
}
if (mutableFlags & tmfMPTClearCanTransfer)
@@ -303,6 +374,22 @@ MPTokenIssuanceSet::doApply()
}
}
if (auto const pubKey = ctx_.tx[~sfIssuerElGamalPublicKey])
{
// This is enforced in preflight.
XRPL_ASSERT(sle->getType() == ltMPTOKEN_ISSUANCE, "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
sle->setFieldVL(sfIssuerElGamalPublicKey, *pubKey);
}
if (auto const pubKey = ctx_.tx[~sfAuditorElGamalPublicKey])
{
// This is enforced in preflight.
XRPL_ASSERT(sle->getType() == ltMPTOKEN_ISSUANCE, "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
sle->setFieldVL(sfAuditorElGamalPublicKey, *pubKey);
}
view().update(sle);
return tesSUCCESS;