Compare commits

...

144 Commits

Author SHA1 Message Date
JCW
4001748ee9 Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-29 09:57:31 +01:00
JCW
f1482d332c Fix errors
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 16:49:11 +01:00
JCW
373121ed78 Fix levelisation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 15:54:56 +01:00
JCW
17c10de2ea Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 15:46:10 +01:00
JCW
6de7802001 Remove unrelated changes
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 15:30:49 +01:00
JCW
56a45506eb Optimise
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:08:06 +01:00
JCW
23029ab2b6 Optimise
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:52 +01:00
JCW
d1fe8ed31d Fix error
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:52 +01:00
JCW
129166cda5 Fix build error
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:52 +01:00
JCW
6376f10df7 Fix unit tests
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:52 +01:00
JCW
acafed7376 Performance test
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:52 +01:00
JCW
4feaa7b279 WIP
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:52 +01:00
JCW
45a4f44dc1 Performance test
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:51 +01:00
JCW
211d90dadd Performance test
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:51 +01:00
JCW
a4498f084e Performance test
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:51 +01:00
JCW
294dae5766 Performance test
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:51 +01:00
JCW
d2f01eb755 Performance test
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:51 +01:00
JCW
a854a78107 Fix test error
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:51 +01:00
JCW
c4047690e2 Fix test cases
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:50 +01:00
JCW
893632d330 Performance test
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:50 +01:00
JCW
f44d53be16 Fix test error
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:50 +01:00
JCW
d33691da84 Log size optimise
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:50 +01:00
JCW
1dc3b256e0 Bugfix
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:50 +01:00
JCW
2c2936fa93 Fix issues
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:50 +01:00
JCW
dcec5a0bbc Revert unrelated changes & performance optimisation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:20 +01:00
JCW
ce5a6aec7b Revert unneeded changes
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:06:18 +01:00
JCW
79c3a83088 Fix build error
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:03:16 +01:00
JCW
bd91ec7242 Optimise
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:03:14 +01:00
JCW
bb787e3995 Optimisation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:55 +01:00
JCW
0223443452 Hardcode the logstyle as json
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:54 +01:00
JCW
79e8c6a158 Add additional check
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:54 +01:00
JCW
44aa394e1e Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:54 +01:00
JCW
5e16b3df62 Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:54 +01:00
JCW
2f6d133169 Improve test coverage
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:54 +01:00
JCW
06c212495d Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:54 +01:00
JCW
9543ccf8e1 Set module name in json
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:53 +01:00
JCW
816089eab7 Fix error
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:53 +01:00
JCW
fa0cff3532 Fix build error
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:53 +01:00
JCW
3ec7596170 Fix build error
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:53 +01:00
JCW
28ad89ca20 Fix build error
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:53 +01:00
JCW
e6c5f8338b Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:53 +01:00
JCW
4d0c0ca5c7 Polish code
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:53 +01:00
JCW
4f63747f33 Polish code
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:52 +01:00
JCW
1a2b7e9b94 Fix issues
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:52 +01:00
JCW
c2aae2d846 Optimisation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:52 +01:00
JCW
458bd8a3bd Revert unrelated changes
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:52 +01:00
JCW
cd8d5d97d1 Fix issues
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:52 +01:00
JCW
bd7b098409 Fix levelisation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:52 +01:00
JCW
addfae1213 Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:51 +01:00
JCW
89ebb6b495 Fix issues
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:24 +01:00
JCW
67aa3d5ac9 Remove hardcoded logstyle
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:24 +01:00
JCW
3b2edce813 Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:23 +01:00
JCW
1d3d0c6774 Optimise
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:23 +01:00
JCW
f50f76788b Fix issues
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:23 +01:00
JCW
feae1d6e15 Optimisation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:23 +01:00
JCW
7debf3e9f4 Optimisation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:23 +01:00
JCW
90f970be46 Improve performance
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:23 +01:00
JCW
5e060a9e7b Fix
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:22 +01:00
JCW
dca000a60f Optimisation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:22 +01:00
JCW
7500d635bb Fix issues
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:22 +01:00
JCW
3181042f15 Fix issues
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:22 +01:00
JCW
157aa367f2 Fix issues
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:22 +01:00
JCW
48cf042258 Optimisation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:22 +01:00
JCW
61ff2ba0e7 Fix issues
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:21 +01:00
JCW
e19d770b86 performance optimisation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 14:02:21 +01:00
JCW
a128571ab5 Fix issues 2025-09-26 14:02:21 +01:00
JCW
76bb517eb8 Fix issues 2025-09-26 14:02:21 +01:00
JCW
dc221de60c Fix issues 2025-09-26 14:02:21 +01:00
JCW
cdf1109558 Performance improvement 2025-09-26 14:02:18 +01:00
JCW
0fe8f3f62d Hardcode the log style as json
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:55 +01:00
JCW
ab9e6563e4 Fix PR comments
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:54 +01:00
Jingchen
d0f0789490 Update include/xrpl/basics/Log.h
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
2025-09-26 13:55:54 +01:00
JCW
d36ef0cd18 Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:54 +01:00
JCW
a90bf169bf Improve coverage
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:54 +01:00
JCW
b3f389d918 Remove unneeded file
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:54 +01:00
JCW
d68f87f968 Fix errors
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:54 +01:00
JCW
34127593e6 Fix errors
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:53 +01:00
JCW
9e09595db0 Fix errors
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:53 +01:00
JCW
856b36d0a5 Fix errors
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:53 +01:00
JCW
9edba67e64 Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:53 +01:00
JCW
0e4f9a7ccf Fix errors
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:51 +01:00
JCW
eda9bf1f1a Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:41 +01:00
JCW
43c6e202af Fix to_string error
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:41 +01:00
JCW
e4db80f61d Fix errors
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:39 +01:00
JCW
af9dde4f75 Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:19 +01:00
JCW
f6d7b90b70 Support structured logs
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:18 +01:00
JCW
1774769226 Support structured logs
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:18 +01:00
JCW
92312801f1 Support structured logs
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-26 13:55:16 +01:00
Jingchen
cfd26f444c 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.
2025-09-26 11:40:43 +00:00
tequ
2c3024716b change fixPriceOracleOrder to Supported::yes (#5749) 2025-09-26 12:07:48 +01:00
Bart
a12f5de68d 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.
2025-09-25 16:08:07 +00:00
Bronek Kozicki
51c5f2bfc9 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).
2025-09-25 16:14:29 +02:00
Valentin Balaschenko
73ff54143d 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.
2025-09-23 13:26:26 +02:00
Bart
08b136528e 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.
2025-09-22 18:27:02 +00:00
Mayukha Vadari
6b8a589447 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).
2025-09-22 20:00:31 +02:00
Ed Hennis
ffeabc9642 refactor: Simplify STParsedJSON with some helper functions (#5591)
- Add code coverage for STParsedJSON edge cases

Co-authored-by: Denis Angell <dangell@transia.co>
2025-09-18 19:04:40 +00:00
Ed Hennis
3cbdf818a7 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.
2025-09-18 17:55:49 +00:00
Ed Hennis
bd834c87e0 Merge tag '2.6.1-rc1' into ximinez/merge-261rc1
2.6.1-rc1

* tag '2.6.1-rc1':
  Set version to 2.6.1-rc1
  Downgrade to boost 1.83
2025-09-18 11:46:22 -04:00
Jingchen
dc8b37a524 refactor: Modularise ledger (#5493)
This change moves the ledger code to libxrpl.
2025-09-18 11:12:24 -04:00
Bronek Kozicki
617a895af5 chore: Add unit tests dir to code coverage excludes (#5803)
This change excludes unit test code from code coverage reporting.
2025-09-18 06:30:34 -04:00
Bart
1af1048c58 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.
2025-09-17 19:17:48 -04:00
Ed Hennis
f07ba87e51 Merge tag '2.5.1' into upstream--develop
- Ensures the commits don't get orphaned, even though the relevant code
  changes are already included.

* tag '2.5.1':
  Set version to 2.5.1
  Fix: Don't flag consensus as stalled prematurely (#5658)
2025-09-17 19:05:14 -04:00
Bart
e66558a883 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.
2025-09-17 22:55:00 +00:00
Mayukha Vadari
510314d344 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.
2025-09-17 21:34:47 +00:00
yinyiqian1
37b951859c Rename mutable flags (#5797)
This is a minor change on top of #5705
2025-09-17 21:43:04 +01:00
Jingchen
9494fc9668 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.
2025-09-17 09:29:15 -04:00
Bronek Kozicki
8d01f35eb9 Set version to 2.6.1-rc1 2025-09-16 15:35:54 -04:00
Bronek Kozicki
1020a32d76 Downgrade to boost 1.83 2025-09-16 15:35:47 -04:00
Vito Tumas
17a2606591 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
2025-09-16 10:51:55 +01:00
yinyiqian1
ccb9f1e42d 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.
2025-09-15 19:42:36 +00:00
Bart
3e4e9a2ddc 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.
2025-09-15 13:28:47 -04:00
Bart
4caebfbd0e 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.
2025-09-15 16:26:08 +00:00
Denis Angell
37c377a1b6 Fix: EscrowTokenV1 (#5571)
* resolves an accounting inconsistency in MPT escrows where transfer fees were not properly handled when unlocking escrowed tokens.
2025-09-15 14:48:47 +00:00
Jingchen
bd182c0a3e 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.
2025-09-15 13:51:19 +00:00
Ayaz Salikhov
406c26cc72 ci: Fix conan secrets in upload-conan-deps (#5785)
- Accounts for some variables that were changed and missed when the reusable workflow was removed.
2025-09-12 17:09:42 +00:00
Jingchen
9bd1ce436a Fix code coverage error (#5765)
* Fix the issue where COVERAGE_CXX_COMPILER_FLAGS is never used
2025-09-12 15:13:27 +00:00
Ayaz Salikhov
f69ad4eff6 docs: Add remote to conan lock create command (#5770)
* docs: Add remote to `conan lock create` command
* Document error resolution for conan package issues
* Update BUILD.md
* Add more info about lockfiles
2025-09-11 15:42:27 +00:00
Mayukha Vadari
6fe0599cc2 refactor: clean up CTID.h (#5681) 2025-09-11 14:49:26 +00:00
tequ
e6f8bc720f Add additional metadata to simulate response (#5754) 2025-09-11 15:17:06 +01:00
Ayaz Salikhov
fbd60fc000 ci: Use pre-commit reusable workflow (#5772) 2025-09-11 13:58:11 +01:00
yinyiqian1
61d628d654 fix: Add restrictions to Permission Delegation: fixDelegateV1_1 (#5650)
- Amendment: fixDelegateV1_1
- In DelegateSet, disallow invalid PermissionValues like 0, and transaction values when the transaction's amendment is not enabled. Acts as if the transaction doesn't exist, which is the same thing older versions without the amendment will do.
- Payment burn/mint should disallow DEX currency exchange.
- Support MPT for Payment burn/mint.
2025-09-10 17:47:33 +00:00
Ayaz Salikhov
3d92375d12 ci: Add missing dependencies to workflows (#5783) 2025-09-10 08:20:45 +00:00
Ayaz Salikhov
cdbe70b2a7 ci: Use default conan install format (#5784) 2025-09-10 07:35:58 +00:00
Bronek Kozicki
f6426ca183 Switch CI pipeline bookworm:gcc-13 from arm64 to amd64 (#5779) 2025-09-09 21:23:07 +00:00
Ayaz Salikhov
e5f7a8442d ci: Change upload-conan-deps workflow is run (#5782)
- Don't run upload-conan-deps in PRs, unless the PR changes the workflow file.
- Change cron schedule for uploading Conan dependencies to run after work hours for most dev.
2025-09-09 16:21:12 -04:00
Ayaz Salikhov
e67e0395df ci: Limit number of parallel jobs in "upload-conan-deps" (#5781)
- This should prevent Artifactory from being overloaded by too many requests at a time.
- Uses "max-parallel" to limit the build job to 10 simultaneous instances.
- Only run the minimal matrix on PRs.
2025-09-09 19:47:06 +00:00
Ed Hennis
148f669a25 chore: "passed" fails if any previous jobs fail or are cancelled (#5776)
For the purposes of being able to merge a PR, Github Actions jobs count as passed if they ran and passed, or were skipped.

With this change, if any of the jobs that "passed" depends on fail or are cancelled, then "passed" will fail. If they all succeed or are skipped, then "passed" is skipped, which does not prevent a merge.

This saves spinning up a runner in the usual case where things work, and will simplify our branch protection rules, so that only "passed" will need to be checked.
2025-09-09 18:07:04 +00:00
yinyiqian1
f1eaa6a264 enable fixAMMClawbackRounding (#5750) 2025-09-09 15:57:28 +00:00
Ayaz Salikhov
da4c8c9550 ci: Only run build-test/notify-clio if should-run indicates to (#5777)
- Fixes an issue introduced by #5762 which removed the transitive `should-run` check from these two jobs.
2025-09-09 11:25:41 -04:00
Wo Jake
bcde2790a4 Update old links & descriptions in README.md (#4701) 2025-09-08 18:03:20 +00:00
Ayaz Salikhov
9ebeb413e4 feat: Implement separate upload workflow (#5762)
* feat: Implement separate upload workflow
* Use cleanup-workspace
* Name some workflows reusable
* Add dependencies
2025-09-08 15:15:59 +00:00
Bronek Kozicki
6d40b882a4 Switch on-trigger to minimal build (#5773) 2025-09-08 13:54:50 +00:00
tzchenxixi
9fe0a154f1 chore: remove redundant word in comment (#5752) 2025-09-08 13:13:32 +00:00
Ayaz Salikhov
cb52c9af00 fix: Remove extra @ in notify-clio.yml (#5771) 2025-09-05 14:08:17 +01:00
Mayukha Vadari
6bf8338038 chore: Add conan.lock to workflow file checks (#5769)
* Add conan.lock to workflow file checks
* Add conan.lock to on-trigger.yml
2025-09-04 22:32:23 +00:00
Ayaz Salikhov
b0f4174e47 chore: Use tooling provided by pre-commit (#5753) 2025-09-04 20:30:54 +00:00
Ayaz Salikhov
3865dde0b8 fix: Add missing info to notify-clio workflow (#5761)
* Add missing info to notify-clio workflow, as conan_ref
2025-09-04 19:26:57 +00:00
Ayaz Salikhov
811c980821 ci: Use cleanup-workspace action (#5763)
* ci: Use cleanup-workspace action
* Use latest version
2025-09-04 16:27:30 +01:00
Bronek Kozicki
cf5f65b68e Add Scale to SingleAssetVault (#5652)
* Add and Scale to VaultCreate
* Add round-trip calculation to VaultDeposit VaultWithdraw and VaultClawback
* Implement Number::truncate() for VaultClawback
* Add rounding to DepositWithdraw
* Disallow zero shares withdraw or deposit with tecPRECISION_LOSS
* Return tecPATH_DRY on overflow when converting shares/assets
* Remove empty shares MPToken in clawback or withdraw (except for vault owner)
* Implicitly create shares MPToken for vault owner in VaultCreate
* Review feedback: defensive checks in shares/assets calculations

---------

Co-authored-by: Ed Hennis <ed@ripple.com>
2025-09-04 08:54:24 +00:00
Jingchen
c38f2a3f2e Fix coverage parameter (#5760) 2025-09-03 16:08:02 +00:00
Ed Hennis
16c2ff97cc Set version to 2.5.1 2025-09-03 10:20:12 -04:00
Ed Hennis
32043463a8 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.
2025-09-03 10:12:30 -04:00
Ayaz Salikhov
724e9b1313 chore: Use conan lockfile (#5751)
* chore: Use conan lockfile
* Add windows-specific dependencies as well
* Add more info about lockfiles
* Update lockfile to latest version
* Update BUILD.md with conan install note
2025-09-03 10:24:07 +00:00
Bronek Kozicki
2e6f00aef2 Add required disable_ccache option (#5756) 2025-09-03 09:25:52 +01:00
301 changed files with 13133 additions and 3182 deletions

View File

@@ -33,5 +33,6 @@ slack_app: false
ignore:
- "src/test/"
- "src/tests/"
- "include/xrpl/beast/test/"
- "include/xrpl/beast/unit_test/"

View File

@@ -1,7 +1,5 @@
# This action installs and optionally uploads Conan dependencies to a remote
# repository. The dependencies will only be uploaded if the credentials are
# provided.
name: Build Conan dependencies
description: "Install Conan dependencies, optionally forcing a rebuild of all dependencies."
# Note that actions do not support 'type' and all inputs are strings, see
# https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs.
@@ -12,28 +10,10 @@ inputs:
build_type:
description: 'The build type to use ("Debug", "Release").'
required: true
conan_remote_name:
description: "The name of the Conan remote to use."
required: true
conan_remote_url:
description: "The URL of the Conan endpoint to use."
required: true
conan_remote_username:
description: "The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded."
required: false
default: ""
conan_remote_password:
description: "The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded."
required: false
default: ""
force_build:
description: 'Force building of all dependencies ("true", "false").'
required: false
default: "false"
force_upload:
description: 'Force uploading of all dependencies ("true", "false").'
required: false
default: "false"
runs:
using: composite
@@ -50,13 +30,4 @@ runs:
--options:host '&:tests=True' \
--options:host '&:xrpld=True' \
--settings:all build_type=${{ inputs.build_type }} \
--format=json ..
- name: Upload Conan dependencies
if: ${{ inputs.conan_remote_username != '' && inputs.conan_remote_password != '' }}
shell: bash
working-directory: ${{ inputs.build_dir }}
run: |
echo "Logging into Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}."
conan remote login ${{ inputs.conan_remote_name }} "${{ inputs.conan_remote_username }}" --password "${{ inputs.conan_remote_password }}"
echo 'Uploading dependencies.'
conan upload '*' --confirm --check ${{ inputs.force_upload == 'true' && '--force' || '' }} --remote=${{ inputs.conan_remote_name }}
..

View File

@@ -1,6 +1,7 @@
# This action build and tests the binary. The Conan dependencies must have
# already been installed (see the build-deps action).
name: Build and Test
description: "Build and test the binary."
# Note that actions do not support 'type' and all inputs are strings, see
# https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs.

43
.github/actions/setup-conan/action.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Setup Conan
description: "Set up Conan configuration, profile, and remote."
inputs:
conan_remote_name:
description: "The name of the Conan remote to use."
required: false
default: xrplf
conan_remote_url:
description: "The URL of the Conan endpoint to use."
required: false
default: https://conan.ripplex.io
runs:
using: composite
steps:
- name: Set up Conan configuration
shell: bash
run: |
echo 'Installing configuration.'
cat conan/global.conf ${{ runner.os == 'Linux' && '>>' || '>' }} $(conan config home)/global.conf
echo 'Conan configuration:'
conan config show '*'
- name: Set up Conan profile
shell: bash
run: |
echo 'Installing profile.'
conan config install conan/profiles/default -tf $(conan config home)/profiles/
echo 'Conan profile:'
conan profile show
- name: Set up Conan remote
shell: bash
run: |
echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}."
conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }}
echo 'Listing Conan remotes.'
conan remote list

View File

@@ -7,9 +7,6 @@ Loop: test.jtx test.unit_test
Loop: xrpld.app xrpld.core
xrpld.app > xrpld.core
Loop: xrpld.app xrpld.ledger
xrpld.app > xrpld.ledger
Loop: xrpld.app xrpld.overlay
xrpld.overlay > xrpld.app

View File

@@ -2,6 +2,10 @@ libxrpl.basics > xrpl.basics
libxrpl.crypto > xrpl.basics
libxrpl.json > xrpl.basics
libxrpl.json > xrpl.json
libxrpl.ledger > xrpl.basics
libxrpl.ledger > xrpl.json
libxrpl.ledger > xrpl.ledger
libxrpl.ledger > xrpl.protocol
libxrpl.net > xrpl.basics
libxrpl.net > xrpl.net
libxrpl.protocol > xrpl.basics
@@ -21,11 +25,11 @@ test.app > test.unit_test
test.app > xrpl.basics
test.app > xrpld.app
test.app > xrpld.core
test.app > xrpld.ledger
test.app > xrpld.nodestore
test.app > xrpld.overlay
test.app > xrpld.rpc
test.app > xrpl.json
test.app > xrpl.ledger
test.app > xrpl.protocol
test.app > xrpl.resource
test.basics > test.jtx
@@ -44,8 +48,8 @@ test.consensus > test.unit_test
test.consensus > xrpl.basics
test.consensus > xrpld.app
test.consensus > xrpld.consensus
test.consensus > xrpld.ledger
test.consensus > xrpl.json
test.consensus > xrpl.ledger
test.core > test.jtx
test.core > test.toplevel
test.core > test.unit_test
@@ -63,9 +67,9 @@ test.json > xrpl.json
test.jtx > xrpl.basics
test.jtx > xrpld.app
test.jtx > xrpld.core
test.jtx > xrpld.ledger
test.jtx > xrpld.rpc
test.jtx > xrpl.json
test.jtx > xrpl.ledger
test.jtx > xrpl.net
test.jtx > xrpl.protocol
test.jtx > xrpl.resource
@@ -75,7 +79,7 @@ test.ledger > test.toplevel
test.ledger > xrpl.basics
test.ledger > xrpld.app
test.ledger > xrpld.core
test.ledger > xrpld.ledger
test.ledger > xrpl.ledger
test.ledger > xrpl.protocol
test.nodestore > test.jtx
test.nodestore > test.toplevel
@@ -134,7 +138,10 @@ test.toplevel > test.csf
test.toplevel > xrpl.json
test.unit_test > xrpl.basics
tests.libxrpl > xrpl.basics
tests.libxrpl > xrpl.net
xrpl.json > xrpl.basics
xrpl.ledger > xrpl.basics
xrpl.ledger > xrpl.protocol
xrpl.net > xrpl.basics
xrpl.protocol > xrpl.basics
xrpl.protocol > xrpl.json
@@ -151,6 +158,7 @@ xrpld.app > xrpld.consensus
xrpld.app > xrpld.nodestore
xrpld.app > xrpld.perflog
xrpld.app > xrpl.json
xrpld.app > xrpl.ledger
xrpld.app > xrpl.net
xrpld.app > xrpl.protocol
xrpld.app > xrpl.resource
@@ -163,9 +171,6 @@ xrpld.core > xrpl.basics
xrpld.core > xrpl.json
xrpld.core > xrpl.net
xrpld.core > xrpl.protocol
xrpld.ledger > xrpl.basics
xrpld.ledger > xrpl.json
xrpld.ledger > xrpl.protocol
xrpld.nodestore > xrpl.basics
xrpld.nodestore > xrpld.core
xrpld.nodestore > xrpld.unity
@@ -186,9 +191,9 @@ xrpld.perflog > xrpl.basics
xrpld.perflog > xrpl.json
xrpld.rpc > xrpl.basics
xrpld.rpc > xrpld.core
xrpld.rpc > xrpld.ledger
xrpld.rpc > xrpld.nodestore
xrpld.rpc > xrpl.json
xrpld.rpc > xrpl.ledger
xrpld.rpc > xrpl.net
xrpld.rpc > xrpl.protocol
xrpld.rpc > xrpl.resource

47
.github/scripts/strategy-matrix/generate.py vendored Normal file → Executable file
View File

@@ -2,7 +2,17 @@
import argparse
import itertools
import json
import re
from pathlib import Path
from dataclasses import dataclass
THIS_DIR = Path(__file__).parent.resolve()
@dataclass
class Config:
architecture: list[dict]
os: list[dict]
build_type: list[str]
cmake_args: list[str]
'''
Generate a strategy matrix for GitHub Actions CI.
@@ -18,9 +28,9 @@ We will further set additional CMake arguments as follows:
- Certain Debian Bookworm configurations will change the reference fee, enable
codecov, and enable voidstar in PRs.
'''
def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict], build_type: list[str], cmake_args: list[str]) -> dict:
def generate_strategy_matrix(all: bool, config: Config) -> list:
configurations = []
for architecture, os, build_type, cmake_args in itertools.product(architecture, os, build_type, cmake_args):
for architecture, os, build_type, cmake_args in itertools.product(config.architecture, config.os, config.build_type, config.cmake_args):
# The default CMake target is 'all' for Linux and MacOS and 'install'
# for Windows, but it can get overridden for certain configurations.
cmake_target = 'install' if os["distro_name"] == 'windows' else 'all'
@@ -35,7 +45,7 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict]
# Only generate a subset of configurations in PRs.
if not all:
# Debian:
# - Bookworm using GCC 13: Release and Unity on linux/arm64, set
# - Bookworm using GCC 13: Release and Unity on linux/amd64, set
# the reference fee to 500.
# - Bookworm using GCC 15: Debug and no Unity on linux/amd64, enable
# code coverage (which will be done below).
@@ -47,7 +57,7 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict]
if os['distro_name'] == 'debian':
skip = True
if os['distro_version'] == 'bookworm':
if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-13' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/arm64':
if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-13' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/amd64':
cmake_args = f'-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}'
skip = False
if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-15' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/amd64':
@@ -158,21 +168,30 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict]
'architecture': architecture,
})
return {'include': configurations}
return configurations
def read_config(file: Path) -> Config:
config = json.loads(file.read_text())
if config['architecture'] is None or config['os'] is None or config['build_type'] is None or config['cmake_args'] is None:
raise Exception('Invalid configuration file.')
return Config(**config)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--all', help='Set to generate all configurations (generally used when merging a PR) or leave unset to generate a subset of configurations (generally used when committing to a PR).', action="store_true")
parser.add_argument('-c', '--config', help='Path to the JSON file containing the strategy matrix configurations.', required=True, type=str)
parser.add_argument('-c', '--config', help='Path to the JSON file containing the strategy matrix configurations.', required=False, type=Path)
args = parser.parse_args()
# Load the JSON configuration file.
config = None
with open(args.config, 'r') as f:
config = json.load(f)
if config['architecture'] is None or config['os'] is None or config['build_type'] is None or config['cmake_args'] is None:
raise Exception('Invalid configuration file.')
matrix = []
if args.config is None or args.config == '':
matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "linux.json"))
matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "macos.json"))
matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "windows.json"))
else:
matrix += generate_strategy_matrix(args.all, read_config(args.config))
# Generate the strategy matrix.
print(f'matrix={json.dumps(generate_strategy_matrix(args.all, config['architecture'], config['os'], config['build_type'], config['cmake_args']))}')
print(f'matrix={json.dumps({"include": matrix})}')

View File

@@ -2,7 +2,7 @@
"architecture": [
{
"platform": "windows/amd64",
"runner": ["windows-latest"]
"runner": ["self-hosted", "Windows", "devbox"]
}
],
"os": [

View File

@@ -13,14 +13,6 @@ on:
required: false
type: string
default: ".build"
conan_remote_name:
description: "The name of the Conan remote to use."
required: true
type: string
conan_remote_url:
description: "The URL of the Conan endpoint to use."
required: true
type: string
dependencies_force_build:
description: "Force building of all dependencies."
required: false
@@ -45,12 +37,6 @@ on:
codecov_token:
description: "The Codecov token to use for uploading coverage reports."
required: false
conan_remote_username:
description: "The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded."
required: false
conan_remote_password:
description: "The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded."
required: false
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.os }}
@@ -63,20 +49,10 @@ defaults:
jobs:
# Generate the strategy matrix to be used by the following job.
generate-matrix:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: 3.13
- name: Generate strategy matrix
working-directory: .github/scripts/strategy-matrix
id: generate
run: python generate.py ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} --config=${{ inputs.os }}.json >> "${GITHUB_OUTPUT}"
outputs:
matrix: ${{ steps.generate.outputs.matrix }}
uses: ./.github/workflows/reusable-strategy-matrix.yml
with:
os: ${{ inputs.os }}
strategy_matrix: ${{ inputs.strategy_matrix }}
# Build and test the binary.
build-test:
@@ -85,8 +61,9 @@ jobs:
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
max-parallel: 10
runs-on: ${{ matrix.architecture.runner }}
container: ${{ inputs.os == 'linux' && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }}
container: ${{ inputs.os == 'linux' && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-5dd7158', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }}
steps:
- name: Check strategy matrix
run: |
@@ -102,21 +79,16 @@ jobs:
echo 'CMake target: ${{ matrix.cmake_target }}'
echo 'Config name: ${{ matrix.config_name }}'
- name: Clean workspace (MacOS)
if: ${{ inputs.os == 'macos' }}
run: |
WORKSPACE=${{ github.workspace }}
echo "Cleaning workspace '${WORKSPACE}'."
if [ -z "${WORKSPACE}" ] || [ "${WORKSPACE}" = "/" ]; then
echo "Invalid working directory '${WORKSPACE}'."
exit 1
fi
find "${WORKSPACE}" -depth 1 | xargs rm -rfv
- name: Cleanup workspace
if: ${{ runner.os == 'macOS' }}
uses: XRPLF/actions/.github/actions/cleanup-workspace@3f044c7478548e3c32ff68980eeb36ece02b364e
- name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- name: Prepare runner
uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5
with:
disable_ccache: false
- name: Check configuration (Windows)
if: ${{ inputs.os == 'windows' }}
@@ -153,40 +125,16 @@ jobs:
echo 'Checking nproc version.'
nproc --version
- name: Set up Conan configuration
run: |
echo 'Installing configuration.'
cat conan/global.conf ${{ inputs.os == 'linux' && '>>' || '>' }} $(conan config home)/global.conf
echo 'Conan configuration:'
conan config show '*'
- name: Set up Conan profile
run: |
echo 'Installing profile.'
conan config install conan/profiles/default -tf $(conan config home)/profiles/
echo 'Conan profile:'
conan profile show
- name: Set up Conan remote
shell: bash
run: |
echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}."
conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }}
echo 'Listing Conan remotes.'
conan remote list
- name: Setup Conan
uses: ./.github/actions/setup-conan
- name: Build dependencies
uses: ./.github/actions/build-deps
with:
build_dir: ${{ inputs.build_dir }}
build_type: ${{ matrix.build_type }}
conan_remote_name: ${{ inputs.conan_remote_name }}
conan_remote_url: ${{ inputs.conan_remote_url }}
conan_remote_username: ${{ secrets.conan_remote_username }}
conan_remote_password: ${{ secrets.conan_remote_password }}
force_build: ${{ inputs.dependencies_force_build }}
force_upload: ${{ inputs.dependencies_force_upload }}
- name: Build and test binary
uses: ./.github/actions/build-test
with:

View File

@@ -1,75 +0,0 @@
# This workflow checks if the code is properly formatted.
name: Check format
# This workflow can only be triggered by other workflows.
on: workflow_call
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-format
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
pre-commit:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/ci/tools-rippled-pre-commit
steps:
# The $GITHUB_WORKSPACE and ${{ github.workspace }} might not point to the
# same directory for jobs running in containers. The actions/checkout step
# is *supposed* to checkout into $GITHUB_WORKSPACE and then add it to
# safe.directory (see instructions at https://github.com/actions/checkout)
# but that is apparently not happening for some container images. We
# therefore preemptively add both directories to safe.directory. See also
# https://github.com/actions/runner/issues/2058 for more details.
- name: Configure git safe.directory
run: |
git config --global --add safe.directory $GITHUB_WORKSPACE
git config --global --add safe.directory ${{ github.workspace }}
- name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- name: Check configuration
run: |
echo 'Checking path.'
echo ${PATH} | tr ':' '\n'
echo 'Checking environment variables.'
env | sort
echo 'Checking pre-commit version.'
pre-commit --version
echo 'Checking clang-format version.'
clang-format --version
echo 'Checking NPM version.'
npm --version
echo 'Checking Node.js version.'
node --version
echo 'Checking prettier version.'
prettier --version
- name: Format code
run: pre-commit run --show-diff-on-failure --color=always --all-files
- name: Check for differences
env:
MESSAGE: |
One or more files did not conform to the formatting. Maybe you did
not run 'pre-commit' before committing, or your version of
'clang-format' or 'prettier' has an incompatibility with the ones
used here (see the "Check configuration" step above).
Run 'pre-commit run --all-files' in your repo, and then commit and
push the changes.
run: |
DIFF=$(git status --porcelain)
if [ -n "${DIFF}" ]; then
# Print the files that changed to give the contributor a hint about
# what to expect when running pre-commit on their own machine.
git status
echo "${MESSAGE}"
exit 1
fi

View File

@@ -9,12 +9,14 @@ on:
inputs:
conan_remote_name:
description: "The name of the Conan remote to use."
required: true
required: false
type: string
default: xrplf
conan_remote_url:
description: "The URL of the Conan endpoint to use."
required: true
required: false
type: string
default: https://conan.ripplex.io
secrets:
clio_notify_token:
description: "The GitHub token to notify Clio about new versions."
@@ -38,7 +40,7 @@ jobs:
upload:
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13
container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13-sha-5dd7158
steps:
- name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
@@ -50,21 +52,25 @@ jobs:
echo "channel=pr_${{ github.event.pull_request.number }}" >> "${GITHUB_OUTPUT}"
echo 'Extracting version.'
echo "version=$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" >> "${GITHUB_OUTPUT}"
- name: Add Conan remote
- name: Calculate conan reference
id: conan_ref
run: |
echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}."
conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }}
echo 'Listing Conan remotes.'
conan remote list
echo "conan_ref=${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/${{ steps.generate.outputs.channel }}" >> "${GITHUB_OUTPUT}"
- name: Set up Conan
uses: ./.github/actions/setup-conan
with:
conan_remote_name: ${{ inputs.conan_remote_name }}
conan_remote_url: ${{ inputs.conan_remote_url }}
- name: Log into Conan remote
run: conan remote login ${{ inputs.conan_remote_name }} "${{ secrets.conan_remote_username }}" --password "${{ secrets.conan_remote_password }}"
- name: Upload package
run: |
conan export --user=${{ steps.generate.outputs.user }} --channel=${{ steps.generate.outputs.channel }} .
conan upload --confirm --check --remote=${{ inputs.conan_remote_name }} xrpl/${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/${{ steps.generate.outputs.channel }}
conan upload --confirm --check --remote=${{ inputs.conan_remote_name }} xrpl/${{ steps.conan_ref.outputs.conan_ref }}
outputs:
channel: ${{ steps.generate.outputs.channel }}
version: ${{ steps.generate.outputs.version }}
conan_ref: ${{ steps.conan_ref.outputs.conan_ref }}
notify:
needs: upload
@@ -76,5 +82,5 @@ jobs:
run: |
gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
/repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \
-F "client_payload[version]=${{ needs.upload.outputs.version }}@${{ needs.upload.outputs.user }}/${{ needs.upload.outputs.channel }}" \
-F "client_payload[pr]=${{ github.event.pull_request.number }}"
-F "client_payload[conan_ref]=${{ needs.upload.outputs.conan_ref }}" \
-F "client_payload[pr_url]=${{ github.event.pull_request.html_url }}"

View File

@@ -23,10 +23,6 @@ defaults:
run:
shell: bash
env:
CONAN_REMOTE_NAME: xrplf
CONAN_REMOTE_URL: https://conan.ripplex.io
jobs:
# This job determines whether the rest of the workflow should run. It runs
# when the PR is not a draft (which should also cover merge-group) or
@@ -54,18 +50,17 @@ jobs:
files: |
# These paths are unique to `on-pr.yml`.
.github/scripts/levelization/**
.github/workflows/check-format.yml
.github/workflows/check-levelization.yml
.github/workflows/notify-clio.yml
.github/workflows/on-pr.yml
.clang-format
.pre-commit-config.yaml
# Keep the paths below in sync with those in `on-trigger.yml`.
.github/actions/build-deps/**
.github/actions/build-test/**
.github/actions/setup-conan/**
.github/scripts/strategy-matrix/**
.github/workflows/build-test.yml
.github/workflows/reusable-strategy-matrix.yml
.codecov.yml
cmake/**
conan/**
@@ -75,6 +70,7 @@ jobs:
tests/**
CMakeLists.txt
conanfile.py
conan.lock
- name: Check whether to run
# This step determines whether the rest of the workflow should
# run. The rest of the workflow will run if this job runs AND at
@@ -94,61 +90,40 @@ jobs:
outputs:
go: ${{ steps.go.outputs.go == 'true' }}
check-format:
needs: should-run
if: needs.should-run.outputs.go == 'true'
uses: ./.github/workflows/check-format.yml
check-levelization:
needs: should-run
if: needs.should-run.outputs.go == 'true'
if: ${{ needs.should-run.outputs.go == 'true' }}
uses: ./.github/workflows/check-levelization.yml
# This job works around the limitation that GitHub Actions does not support
# using environment variables as inputs for reusable workflows.
generate-outputs:
needs: should-run
if: needs.should-run.outputs.go == 'true'
runs-on: ubuntu-latest
steps:
- name: No-op
run: true
outputs:
conan_remote_name: ${{ env.CONAN_REMOTE_NAME }}
conan_remote_url: ${{ env.CONAN_REMOTE_URL }}
build-test:
needs: generate-outputs
needs: should-run
if: ${{ needs.should-run.outputs.go == 'true' }}
uses: ./.github/workflows/build-test.yml
strategy:
matrix:
os: [linux, macos, windows]
with:
conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }}
conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }}
os: ${{ matrix.os }}
secrets:
codecov_token: ${{ secrets.CODECOV_TOKEN }}
notify-clio:
needs:
- generate-outputs
- should-run
- build-test
if: ${{ needs.should-run.outputs.go == 'true' && contains(fromJSON('["release", "master"]'), github.ref_name) }}
uses: ./.github/workflows/notify-clio.yml
with:
conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }}
conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }}
secrets:
clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }}
conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
passed:
if: failure() || cancelled()
needs:
- build-test
- check-format
- check-levelization
runs-on: ubuntu-latest
steps:
- name: No-op
run: true
- name: Fail
run: false

View File

@@ -21,8 +21,10 @@ on:
# Keep the paths below in sync with those in `on-pr.yml`.
- ".github/actions/build-deps/**"
- ".github/actions/build-test/**"
- ".github/actions/setup-conan/**"
- ".github/scripts/strategy-matrix/**"
- ".github/workflows/build-test.yml"
- ".github/workflows/reusable-strategy-matrix.yml"
- ".codecov.yml"
- "cmake/**"
- "conan/**"
@@ -32,6 +34,7 @@ on:
- "tests/**"
- "CMakeLists.txt"
- "conanfile.py"
- "conan.lock"
# Run at 06:32 UTC on every day of the week from Monday through Friday. This
# will force all dependencies to be rebuilt, which is useful to verify that
@@ -65,54 +68,18 @@ defaults:
run:
shell: bash
env:
CONAN_REMOTE_NAME: xrplf
CONAN_REMOTE_URL: https://conan.ripplex.io
jobs:
check-missing-commits:
if: ${{ github.event_name == 'push' && github.ref_type == 'branch' && contains(fromJSON('["develop", "release"]'), github.ref_name) }}
uses: ./.github/workflows/check-missing-commits.yml
# This job works around the limitation that GitHub Actions does not support
# using environment variables as inputs for reusable workflows. It also sets
# outputs that depend on the event that triggered the workflow.
generate-outputs:
runs-on: ubuntu-latest
steps:
- name: Check inputs and set outputs
id: generate
run: |
if [[ '${{ github.event_name }}' == 'push' ]]; then
echo 'dependencies_force_build=false' >> "${GITHUB_OUTPUT}"
echo 'dependencies_force_upload=false' >> "${GITHUB_OUTPUT}"
elif [[ '${{ github.event_name }}' == 'schedule' ]]; then
echo 'dependencies_force_build=true' >> "${GITHUB_OUTPUT}"
echo 'dependencies_force_upload=false' >> "${GITHUB_OUTPUT}"
else
echo 'dependencies_force_build=${{ inputs.dependencies_force_build }}' >> "${GITHUB_OUTPUT}"
echo 'dependencies_force_upload=${{ inputs.dependencies_force_upload }}' >> "${GITHUB_OUTPUT}"
fi
outputs:
conan_remote_name: ${{ env.CONAN_REMOTE_NAME }}
conan_remote_url: ${{ env.CONAN_REMOTE_URL }}
dependencies_force_build: ${{ steps.generate.outputs.dependencies_force_build }}
dependencies_force_upload: ${{ steps.generate.outputs.dependencies_force_upload }}
build-test:
needs: generate-outputs
uses: ./.github/workflows/build-test.yml
strategy:
matrix:
os: [linux, macos, windows]
with:
conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }}
conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }}
dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build == 'true' }}
dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload == 'true' }}
os: ${{ matrix.os }}
strategy_matrix: "all"
strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }}
secrets:
codecov_token: ${{ secrets.CODECOV_TOKEN }}
conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}

15
.github/workflows/pre-commit.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Run pre-commit hooks
on:
pull_request:
push:
branches: [develop, release, master]
workflow_dispatch:
jobs:
# Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks.
run-hooks:
uses: XRPLF/actions/.github/workflows/pre-commit.yml@af1b0f0d764cda2e5435f5ac97b240d4bd4d95d3
with:
runs_on: ubuntu-latest
container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-d1496b8" }'

View File

@@ -27,7 +27,7 @@ env:
jobs:
publish:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/ci/tools-rippled-documentation
container: ghcr.io/xrplf/ci/tools-rippled-documentation:sha-d1496b8
permissions:
contents: write
steps:

View File

@@ -0,0 +1,38 @@
name: Generate strategy matrix
on:
workflow_call:
inputs:
os:
description: 'The operating system to use for the build ("linux", "macos", "windows").'
required: false
type: string
strategy_matrix:
# TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations.
description: 'The strategy matrix to use for generating the configurations ("minimal", "all").'
required: false
type: string
default: "minimal"
outputs:
matrix:
description: "The generated strategy matrix."
value: ${{ jobs.generate-matrix.outputs.matrix }}
jobs:
generate-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.generate.outputs.matrix }}
steps:
- name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: 3.13
- name: Generate strategy matrix
working-directory: .github/scripts/strategy-matrix
id: generate
run: ./generate.py ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} >> "${GITHUB_OUTPUT}"

91
.github/workflows/upload-conan-deps.yml vendored Normal file
View File

@@ -0,0 +1,91 @@
name: Upload Conan Dependencies
on:
schedule:
- cron: "0 3 * * 2-6"
workflow_dispatch:
inputs:
force_source_build:
description: "Force source build of all dependencies"
required: false
default: false
type: boolean
force_upload:
description: "Force upload of all dependencies"
required: false
default: false
type: boolean
pull_request:
branches: [develop]
paths:
# This allows testing changes to the upload workflow in a PR
- .github/workflows/upload-conan-deps.yml
push:
branches: [develop]
paths:
- .github/workflows/upload-conan-deps.yml
- .github/workflows/reusable-strategy-matrix.yml
- .github/actions/build-deps/action.yml
- .github/actions/setup-conan/action.yml
- ".github/scripts/strategy-matrix/**"
- conanfile.py
- conan.lock
env:
CONAN_REMOTE_NAME: xrplf
CONAN_REMOTE_URL: https://conan.ripplex.io
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
generate-matrix:
uses: ./.github/workflows/reusable-strategy-matrix.yml
with:
strategy_matrix: ${{ github.event_name == 'pull_request' && 'minimal' || 'all' }}
run-upload-conan-deps:
needs:
- generate-matrix
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
max-parallel: 10
runs-on: ${{ matrix.architecture.runner }}
container: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-5dd7158', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }}
steps:
- name: Cleanup workspace
if: ${{ runner.os == 'macOS' }}
uses: XRPLF/actions/.github/actions/cleanup-workspace@3f044c7478548e3c32ff68980eeb36ece02b364e
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- name: Prepare runner
uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5
with:
disable_ccache: false
- name: Setup Conan
uses: ./.github/actions/setup-conan
with:
conan_remote_name: ${{ env.CONAN_REMOTE_NAME }}
conan_remote_url: ${{ env.CONAN_REMOTE_URL }}
- name: Build dependencies
uses: ./.github/actions/build-deps
with:
build_dir: .build
build_type: ${{ matrix.build_type }}
force_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }}
- name: Log into Conan remote
if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' }}
run: conan remote login ${{ env.CONAN_REMOTE_NAME }} "${{ secrets.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}"
- name: Upload Conan packages
if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule' }}
run: conan upload "*" -r=${{ env.CONAN_REMOTE_NAME }} --confirm ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }}

View File

@@ -1,18 +1,5 @@
# To run pre-commit hooks, first install pre-commit:
# - `pip install pre-commit==${PRE_COMMIT_VERSION}`
# - `pip install pre-commit-hooks==${PRE_COMMIT_HOOKS_VERSION}`
#
# Depending on your system, you can use `brew install` or `apt install` as well
# for installing the pre-commit package, but `pip` is needed to install the
# hooks; you can also use `pipx` if you prefer.
# Next, install the required formatters:
# - `pip install clang-format==${CLANG_VERSION}`
# - `npm install prettier@${PRETTIER_VERSION}`
#
# See https://github.com/XRPLF/ci/blob/main/.github/workflows/tools-rippled.yml
# for the versions used in the CI pipeline. You will need to have the exact same
# versions of the tools installed on your system to produce the same results as
# the pipeline.
#
# Then, run the following command to install the git hook scripts:
# - `pre-commit install`
@@ -20,45 +7,33 @@
# - `pre-commit run --all-files`
# To manually run a specific hook, use:
# - `pre-commit run <hook_id> --all-files`
# To run the hooks against only the files changed in the current commit, use:
# To run the hooks against only the staged files, use:
# - `pre-commit run`
repos:
- repo: local
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: mixed-line-ending
- id: check-merge-conflict
args: [--assume-in-merge]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 7d85583be209cb547946c82fbe51f4bc5dd1d017 # frozen: v18.1.8
hooks:
- id: clang-format
name: clang-format
language: system
entry: clang-format -i
files: '\.(cpp|hpp|h|ipp|proto)$'
- id: trailing-whitespace
name: trailing-whitespace
entry: trailing-whitespace-fixer
language: system
types: [text]
- id: end-of-file
name: end-of-file
entry: end-of-file-fixer
language: system
types: [text]
- id: mixed-line-ending
name: mixed-line-ending
entry: mixed-line-ending
language: system
types: [text]
- id: check-merge-conflict
name: check-merge-conflict
entry: check-merge-conflict --assume-in-merge
language: system
types: [text]
- repo: local
args: [--style=file]
"types_or": [c++, c, proto]
- repo: https://github.com/rbubley/mirrors-prettier
rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2
hooks:
- id: prettier
name: prettier
language: system
entry: prettier --ignore-unknown --write
exclude: |
(?x)^(
external/.*|
.github/scripts/levelization/results/.*\.txt
.github/scripts/levelization/results/.*\.txt|
conan\.lock
)$

View File

@@ -132,7 +132,7 @@ higher index than the default Conan Center remote, so it is consulted first. You
can do this by running:
```bash
conan remote add --index 0 xrplf "https://conan.ripplex.io"
conan remote add --index 0 xrplf https://conan.ripplex.io
```
Alternatively, you can pull the patched recipes into the repository and use them
@@ -158,6 +158,10 @@ updated dependencies with the newer version. However, if we switch to a newer
version that no longer requires a patch, no action is required on your part, as
the new recipe will be automatically pulled from the official Conan Center.
> [!NOTE]
> You might need to add `--lockfile=""` to your `conan install` command
> to avoid automatic use of the existing `conan.lock` file when you run `conan export` manually on your machine
### Conan profile tweaks
#### Missing compiler version
@@ -466,6 +470,33 @@ tools.build:cxxflags=['-DBOOST_ASIO_DISABLE_CONCEPTS']
The location of `rippled` binary in your build directory depends on your
CMake generator. Pass `--help` to see the rest of the command line options.
#### Conan lockfile
To achieve reproducible dependencies, we use [Conan lockfile](https://docs.conan.io/2/tutorial/versioning/lockfiles.html).
The `conan.lock` file in the repository contains a "snapshot" of the current dependencies.
It is implicitly used when running `conan` commands, you don't need to specify it.
You have to update this file every time you add a new dependency or change a revision or version of an existing dependency.
> [!NOTE]
> Conan uses local cache by default when creating a lockfile.
>
> To ensure, that lockfile creation works the same way on all developer machines, you should clear the local cache before creating a new lockfile.
To create a new lockfile, run the following commands in the repository root:
```bash
conan remove '*' --confirm
rm conan.lock
# This ensure that xrplf remote is the first to be consulted
conan remote add --force --index 0 xrplf https://conan.ripplex.io
conan lock create . -o '&:jemalloc=True' -o '&:rocksdb=True'
```
> [!NOTE]
> If some dependencies are exclusive for some OS, you may need to run the last command for them adding `--profile:all <PROFILE>`.
## Coverage report
The coverage report is intended for developers using compilers GCC
@@ -564,7 +595,13 @@ After any updates or changes to dependencies, you may need to do the following:
```
3. Re-run [conan export](#patched-recipes) if needed.
4. Re-run [conan install](#build-and-test).
4. [Regenerate lockfile](#conan-lockfile).
5. Re-run [conan install](#build-and-test).
#### ERROR: Package not resolved
If you're seeing an error like `ERROR: Package 'snappy/1.1.10' not resolved: Unable to find 'snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246' in remotes.`,
please add `xrplf` remote or re-run `conan export` for [patched recipes](#patched-recipes).
### `protobuf/port_def.inc` file not found

View File

@@ -6,7 +6,7 @@ The [XRP Ledger](https://xrpl.org/) is a decentralized cryptographic ledger powe
## XRP
[XRP](https://xrpl.org/xrp.html) is a public, counterparty-free asset native to the XRP Ledger, and is designed to bridge the many different currencies in use worldwide. XRP is traded on the open-market and is available for anyone to access. The XRP Ledger was created in 2012 with a finite supply of 100 billion units of XRP.
[XRP](https://xrpl.org/xrp.html) is a public, counterparty-free crypto-asset native to the XRP Ledger, and is designed as a gas token for network services and to bridge different currencies. XRP is traded on the open-market and is available for anyone to access. The XRP Ledger was created in 2012 with a finite supply of 100 billion units of XRP.
## rippled
@@ -23,19 +23,19 @@ If you are interested in running an **API Server** (including a **Full History S
- **[Censorship-Resistant Transaction Processing][]:** No single party decides which transactions succeed or fail, and no one can "roll back" a transaction after it completes. As long as those who choose to participate in the network keep it healthy, they can settle transactions in seconds.
- **[Fast, Efficient Consensus Algorithm][]:** The XRP Ledger's consensus algorithm settles transactions in 4 to 5 seconds, processing at a throughput of up to 1500 transactions per second. These properties put XRP at least an order of magnitude ahead of other top digital assets.
- **[Finite XRP Supply][]:** When the XRP Ledger began, 100 billion XRP were created, and no more XRP will ever be created. The available supply of XRP decreases slowly over time as small amounts are destroyed to pay transaction costs.
- **[Responsible Software Governance][]:** A team of full-time, world-class developers at Ripple maintain and continually improve the XRP Ledger's underlying software with contributions from the open-source community. Ripple acts as a steward for the technology and an advocate for its interests, and builds constructive relationships with governments and financial institutions worldwide.
- **[Finite XRP Supply][]:** When the XRP Ledger began, 100 billion XRP were created, and no more XRP will ever be created. The available supply of XRP decreases slowly over time as small amounts are destroyed to pay transaction fees.
- **[Responsible Software Governance][]:** A team of full-time developers at Ripple & other organizations maintain and continually improve the XRP Ledger's underlying software with contributions from the open-source community. Ripple acts as a steward for the technology and an advocate for its interests.
- **[Secure, Adaptable Cryptography][]:** The XRP Ledger relies on industry standard digital signature systems like ECDSA (the same scheme used by Bitcoin) but also supports modern, efficient algorithms like Ed25519. The extensible nature of the XRP Ledger's software makes it possible to add and disable algorithms as the state of the art in cryptography advances.
- **[Modern Features for Smart Contracts][]:** Features like Escrow, Checks, and Payment Channels support cutting-edge financial applications including the [Interledger Protocol](https://interledger.org/). This toolbox of advanced features comes with safety features like a process for amending the network and separate checks against invariant constraints.
- **[Modern Features][]:** Features like Escrow, Checks, and Payment Channels support financial applications atop of the XRP Ledger. This toolbox of advanced features comes with safety features like a process for amending the network and separate checks against invariant constraints.
- **[On-Ledger Decentralized Exchange][]:** In addition to all the features that make XRP useful on its own, the XRP Ledger also has a fully-functional accounting system for tracking and trading obligations denominated in any way users want, and an exchange built into the protocol. The XRP Ledger can settle long, cross-currency payment paths and exchanges of multiple currencies in atomic transactions, bridging gaps of trust with XRP.
[Censorship-Resistant Transaction Processing]: https://xrpl.org/xrp-ledger-overview.html#censorship-resistant-transaction-processing
[Fast, Efficient Consensus Algorithm]: https://xrpl.org/xrp-ledger-overview.html#fast-efficient-consensus-algorithm
[Finite XRP Supply]: https://xrpl.org/xrp-ledger-overview.html#finite-xrp-supply
[Responsible Software Governance]: https://xrpl.org/xrp-ledger-overview.html#responsible-software-governance
[Secure, Adaptable Cryptography]: https://xrpl.org/xrp-ledger-overview.html#secure-adaptable-cryptography
[Modern Features for Smart Contracts]: https://xrpl.org/xrp-ledger-overview.html#modern-features-for-smart-contracts
[On-Ledger Decentralized Exchange]: https://xrpl.org/xrp-ledger-overview.html#on-ledger-decentralized-exchange
[Censorship-Resistant Transaction Processing]: https://xrpl.org/transaction-censorship-detection.html#transaction-censorship-detection
[Fast, Efficient Consensus Algorithm]: https://xrpl.org/consensus-research.html#consensus-research
[Finite XRP Supply]: https://xrpl.org/what-is-xrp.html
[Responsible Software Governance]: https://xrpl.org/contribute-code.html#contribute-code-to-the-xrp-ledger
[Secure, Adaptable Cryptography]: https://xrpl.org/cryptographic-keys.html#cryptographic-keys
[Modern Features]: https://xrpl.org/use-specialized-payment-types.html
[On-Ledger Decentralized Exchange]: https://xrpl.org/decentralized-exchange.html#decentralized-exchange
## Source Code

View File

@@ -104,6 +104,11 @@
# 2025-08-28, Bronek Kozicki
# - fix "At least one COMMAND must be given" CMake warning from policy CMP0175
#
# 2025-09-03, Jingchen Wu
# - remove the unused function append_coverage_compiler_flags and append_coverage_compiler_flags_to_target
# - add a new function add_code_coverage_to_target
# - remove some unused code
#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
@@ -112,10 +117,8 @@
# using a CMake option() to enable it just optionally):
# include(CodeCoverage)
#
# 3. Append necessary compiler flags for all supported source files:
# append_coverage_compiler_flags()
# Or for specific target:
# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME)
# 3. Append necessary compiler flags and linker flags for all supported source files:
# add_code_coverage_to_target(<target> <PRIVATE|PUBLIC|INTERFACE>)
#
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
#
@@ -204,67 +207,69 @@ endforeach()
set(COVERAGE_COMPILER_FLAGS "-g --coverage"
CACHE INTERNAL "")
set(COVERAGE_CXX_COMPILER_FLAGS "")
set(COVERAGE_C_COMPILER_FLAGS "")
set(COVERAGE_CXX_LINKER_FLAGS "")
set(COVERAGE_C_LINKER_FLAGS "")
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
include(CheckCXXCompilerFlag)
include(CheckCCompilerFlag)
include(CheckLinkerFlag)
set(COVERAGE_CXX_COMPILER_FLAGS ${COVERAGE_COMPILER_FLAGS})
set(COVERAGE_C_COMPILER_FLAGS ${COVERAGE_COMPILER_FLAGS})
set(COVERAGE_CXX_LINKER_FLAGS ${COVERAGE_COMPILER_FLAGS})
set(COVERAGE_C_LINKER_FLAGS ${COVERAGE_COMPILER_FLAGS})
check_cxx_compiler_flag(-fprofile-abs-path HAVE_cxx_fprofile_abs_path)
if(HAVE_cxx_fprofile_abs_path)
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_CXX_COMPILER_FLAGS} -fprofile-abs-path")
endif()
check_c_compiler_flag(-fprofile-abs-path HAVE_c_fprofile_abs_path)
if(HAVE_c_fprofile_abs_path)
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_C_COMPILER_FLAGS} -fprofile-abs-path")
endif()
check_cxx_compiler_flag(-fprofile-update HAVE_cxx_fprofile_update)
check_linker_flag(CXX -fprofile-abs-path HAVE_cxx_linker_fprofile_abs_path)
if(HAVE_cxx_linker_fprofile_abs_path)
set(COVERAGE_CXX_LINKER_FLAGS "${COVERAGE_CXX_LINKER_FLAGS} -fprofile-abs-path")
endif()
check_linker_flag(C -fprofile-abs-path HAVE_c_linker_fprofile_abs_path)
if(HAVE_c_linker_fprofile_abs_path)
set(COVERAGE_C_LINKER_FLAGS "${COVERAGE_C_LINKER_FLAGS} -fprofile-abs-path")
endif()
check_cxx_compiler_flag(-fprofile-update=atomic HAVE_cxx_fprofile_update)
if(HAVE_cxx_fprofile_update)
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-update=atomic")
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_CXX_COMPILER_FLAGS} -fprofile-update=atomic")
endif()
check_c_compiler_flag(-fprofile-update HAVE_c_fprofile_update)
check_c_compiler_flag(-fprofile-update=atomic HAVE_c_fprofile_update)
if(HAVE_c_fprofile_update)
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-update=atomic")
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_C_COMPILER_FLAGS} -fprofile-update=atomic")
endif()
endif()
set(CMAKE_Fortran_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
FORCE )
set(CMAKE_CXX_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
set(CMAKE_C_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
mark_as_advanced(
CMAKE_Fortran_FLAGS_COVERAGE
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
check_linker_flag(CXX -fprofile-update=atomic HAVE_cxx_linker_fprofile_update)
if(HAVE_cxx_linker_fprofile_update)
set(COVERAGE_CXX_LINKER_FLAGS "${COVERAGE_CXX_LINKER_FLAGS} -fprofile-update=atomic")
endif()
check_linker_flag(C -fprofile-update=atomic HAVE_c_linker_fprofile_update)
if(HAVE_c_linker_fprofile_update)
set(COVERAGE_C_LINKER_FLAGS "${COVERAGE_C_LINKER_FLAGS} -fprofile-update=atomic")
endif()
endif()
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
link_libraries(gcov)
endif()
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
@@ -454,18 +459,19 @@ function(setup_target_for_coverage_gcovr)
)
endfunction() # setup_target_for_coverage_gcovr
function(append_coverage_compiler_flags)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
endfunction() # append_coverage_compiler_flags
function(add_code_coverage_to_target name scope)
separate_arguments(COVERAGE_CXX_COMPILER_FLAGS NATIVE_COMMAND "${COVERAGE_CXX_COMPILER_FLAGS}")
separate_arguments(COVERAGE_C_COMPILER_FLAGS NATIVE_COMMAND "${COVERAGE_C_COMPILER_FLAGS}")
separate_arguments(COVERAGE_CXX_LINKER_FLAGS NATIVE_COMMAND "${COVERAGE_CXX_LINKER_FLAGS}")
separate_arguments(COVERAGE_C_LINKER_FLAGS NATIVE_COMMAND "${COVERAGE_C_LINKER_FLAGS}")
# Setup coverage for specific library
function(append_coverage_compiler_flags_to_target name)
separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}")
target_compile_options(${name} PRIVATE ${_flag_list})
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
target_link_libraries(${name} PRIVATE gcov)
endif()
endfunction()
# Add compiler options to the target
target_compile_options(${name} ${scope}
$<$<COMPILE_LANGUAGE:CXX>:${COVERAGE_CXX_COMPILER_FLAGS}>
$<$<COMPILE_LANGUAGE:C>:${COVERAGE_C_COMPILER_FLAGS}>)
target_link_libraries (${name} ${scope}
$<$<LINK_LANGUAGE:CXX>:${COVERAGE_CXX_LINKER_FLAGS} gcov>
$<$<LINK_LANGUAGE:C>:${COVERAGE_C_LINKER_FLAGS} gcov>
)
endfunction() # add_code_coverage_to_target

View File

@@ -16,16 +16,13 @@ set(CMAKE_CXX_EXTENSIONS OFF)
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>)
# ^^^^ NOTE: CMAKE release builds already have NDEBUG
# defined, so no need to add it explicitly except for
# this special case of (profile ON) and (assert OFF)
# -- presumably this is because we don't want profile
# builds asserting unless asserts were specifically
# requested
if (MSVC)
# remove existing exception flag since we set it to -EHa

View File

@@ -111,6 +111,12 @@ target_link_libraries(xrpl.libxrpl.net PUBLIC
add_module(xrpl server)
target_link_libraries(xrpl.libxrpl.server PUBLIC xrpl.libxrpl.protocol)
add_module(xrpl ledger)
target_link_libraries(xrpl.libxrpl.ledger PUBLIC
xrpl.libxrpl.basics
xrpl.libxrpl.json
xrpl.libxrpl.protocol
)
add_library(xrpl.libxrpl)
set_target_properties(xrpl.libxrpl PROPERTIES OUTPUT_NAME xrpl)
@@ -131,6 +137,7 @@ target_link_modules(xrpl PUBLIC
resource
server
net
ledger
)
# All headers in libxrpl are in modules.

View File

@@ -33,6 +33,8 @@ setup_target_for_coverage_gcovr(
FORMAT ${coverage_format}
EXECUTABLE rippled
EXECUTABLE_ARGS --unittest$<$<BOOL:${coverage_test}>:=${coverage_test}> --unittest-jobs ${coverage_test_parallelism} --quiet --unittest-log
EXCLUDE "src/test" "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
EXCLUDE "src/test" "src/tests" "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
DEPENDENCIES rippled
)
add_code_coverage_to_target(opts INTERFACE)

View File

@@ -18,6 +18,7 @@ install (
xrpl.libxrpl.json
xrpl.libxrpl.protocol
xrpl.libxrpl.resource
xrpl.libxrpl.ledger
xrpl.libxrpl.server
xrpl.libxrpl.net
xrpl.libxrpl

View File

@@ -28,15 +28,11 @@ target_compile_options (opts
$<$<AND:$<BOOL:${is_gcc}>,$<COMPILE_LANGUAGE:CXX>>:-Wsuggest-override>
$<$<BOOL:${is_gcc}>:-Wno-maybe-uninitialized>
$<$<BOOL:${perf}>:-fno-omit-frame-pointer>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${coverage}>>:-g --coverage -fprofile-abs-path>
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>>:-g --coverage>
$<$<BOOL:${profile}>:-pg>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
target_link_libraries (opts
INTERFACE
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${coverage}>>:-g --coverage -fprofile-abs-path>
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>>:-g --coverage>
$<$<BOOL:${profile}>:-pg>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)

56
conan.lock Normal file
View File

@@ -0,0 +1,56 @@
{
"version": "0.5",
"requires": [
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1756234289.683",
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1756234266.869",
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1756234262.318",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246",
"rocksdb/10.0.1#85537f46e538974d67da0c3977de48ac%1756234304.347",
"re2/20230301#dfd6e2bf050eb90ddd8729cfb4c844a4%1756234257.976",
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
"openssl/1.1.1w#a8f0792d7c5121b954578a7149d23e03%1756223730.729",
"nudb/2.0.9#c62cfd501e57055a7e0d8ee3d5e5427d%1756234237.107",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1756234228.999",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1756223727.64",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1756230911.03",
"libarchive/3.8.1#5cf685686322e906cb42706ab7e099a8%1756234256.696",
"jemalloc/5.3.0#e951da9cf599e956cebc117880d2d9f8%1729241615.244",
"grpc/1.50.1#02291451d1e17200293a409410d1c4e1%1756234248.958",
"doctest/2.4.11#a4211dfc329a16ba9f280f9574025659%1756234220.819",
"date/3.0.4#f74bbba5a08fa388256688743136cb6f%1756234217.493",
"c-ares/1.34.5#b78b91e7cfb1f11ce777a285bbf169c6%1756234217.915",
"bzip2/1.0.8#00b4a4658791c1f06914e087f0e792f5%1756234261.716",
"boost/1.88.0#8852c0b72ce8271fb8ff7c53456d4983%1756223752.326",
"abseil/20230802.1#f0f91485b111dc9837a68972cb19ca7b%1756234220.907"
],
"build_requires": [
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497",
"strawberryperl/5.32.1.1#707032463aa0620fa17ec0d887f5fe41%1756234281.733",
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1756234232.901",
"msys2/cci.latest#5b73b10144f73cc5bfe0572ed9be39e1%1751977009.857",
"m4/1.4.19#b38ced39a01e31fef5435bc634461fd2%1700758725.451",
"cmake/3.31.8#dde3bde00bb843687e55aea5afa0e220%1756234232.89",
"b2/5.3.3#107c15377719889654eb9a162a673975%1756234226.28",
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86"
],
"python_requires": [],
"overrides": {
"protobuf/3.21.12": [
null,
"protobuf/3.21.12"
],
"lz4/1.9.4": [
"lz4/1.10.0"
],
"boost/1.83.0": [
"boost/1.88.0"
],
"sqlite3/3.44.2": [
"sqlite3/3.49.1"
]
},
"config_requires": []
}

View File

@@ -27,7 +27,7 @@ class Xrpl(ConanFile):
'grpc/1.50.1',
'libarchive/3.8.1',
'nudb/2.0.9',
'openssl/3.5.2',
'openssl/1.1.1w',
'soci/4.0.3',
'zlib/1.3.1',
]

View File

@@ -30,6 +30,7 @@
#include <map>
#include <memory>
#include <mutex>
#include <string_view>
#include <utility>
namespace ripple {
@@ -130,27 +131,14 @@ private:
Does nothing if there is no associated system file.
*/
void
write(char const* text);
write(std::string_view text);
/** write to the log file and append an end of line marker.
Does nothing if there is no associated system file.
*/
void
writeln(char const* text);
writeln(std::string_view text);
/** Write to the log file using std::string. */
/** @{ */
void
write(std::string const& str)
{
write(str.c_str());
}
void
writeln(std::string const& str)
{
writeln(str.c_str());
}
/** @} */
private:
@@ -186,6 +174,14 @@ public:
beast::Journal::Sink&
operator[](std::string const& name);
template <typename AttributesFactory>
beast::Journal
journal(std::string const& name, AttributesFactory&& factory)
{
return beast::Journal{
get(name), name, std::forward<AttributesFactory>(factory)};
}
beast::Journal
journal(std::string const& name);
@@ -237,30 +233,34 @@ public:
static LogSeverity
fromString(std::string const& s);
private:
enum {
// Maximum line length for log messages.
// If the message exceeds this length it will be truncated with elipses.
maximumMessageCharacters = 12 * 1024
};
static void
format(
std::string& output,
std::string const& message,
beast::severities::Severity severity,
std::string const& partition);
private:
enum {
// Maximum line length for log messages.
// If the message exceeds this length it will be truncated with elipses.
maximumMessageCharacters = 12 * 1024
};
};
// Wraps a Journal::Stream to skip evaluation of
// expensive argument lists if the stream is not active.
#ifndef JLOG
#define JLOG(x) \
if (!x) \
{ \
} \
else \
x
#define JLOG_JOIN_(a, b) a##b
#define JLOG_JOIN(a, b) JLOG_JOIN_(a, b)
#define JLOG_UNIQUE(base) JLOG_JOIN(base, __LINE__) // line-based unique name
#define JLOG(x) \
if (auto JLOG_UNIQUE(stream) = (x); !JLOG_UNIQUE(stream)) \
{ \
} \
else \
std::move(JLOG_UNIQUE(stream))
#endif
#ifndef CLOG

View File

@@ -150,6 +150,24 @@ public:
return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
}
Number
truncate() const noexcept
{
if (exponent_ >= 0 || mantissa_ == 0)
return *this;
Number ret = *this;
while (ret.exponent_ < 0 && ret.mantissa_ != 0)
{
ret.exponent_ += 1;
ret.mantissa_ /= rep(10);
}
// We are guaranteed that normalize() will never throw an exception
// because exponent is either negative or zero at this point.
ret.normalize();
return ret;
}
friend constexpr bool
operator>(Number const& x, Number const& y) noexcept
{

View File

@@ -632,6 +632,16 @@ to_string(base_uint<Bits, Tag> const& a)
return strHex(a.cbegin(), a.cend());
}
template <std::size_t Bits, class Tag>
inline std::string
to_short_string(base_uint<Bits, Tag> const& a)
{
static_assert(
base_uint<Bits, Tag>::bytes > 4,
"For 4 bytes or less, use a native type");
return strHex(a.cbegin(), a.cbegin() + 4) + "...";
}
template <std::size_t Bits, class Tag>
inline std::ostream&
operator<<(std::ostream& out, base_uint<Bits, Tag> const& u)

View File

@@ -28,9 +28,8 @@ namespace ripple {
// the destination can hold all values of the source. This is particularly
// handy when the source or destination is an enumeration type.
template <class Dest, class Src>
static constexpr bool is_safetocasttovalue_v =
(std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
template <class Src, class Dest>
concept SafeToCast = (std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
(std::is_signed<Src>::value || std::is_unsigned<Dest>::value) &&
(std::is_signed<Src>::value != std::is_signed<Dest>::value
? sizeof(Dest) > sizeof(Src)
@@ -78,7 +77,7 @@ inline constexpr std::
unsafe_cast(Src s) noexcept
{
static_assert(
!is_safetocasttovalue_v<Dest, Src>,
!SafeToCast<Src, Dest>,
"Only unsafe if casting signed to unsigned or "
"destination is too small");
return static_cast<Dest>(s);

View File

@@ -22,10 +22,266 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <atomic>
#include <charconv>
#include <cstring>
#include <deque>
#include <mutex>
#include <shared_mutex>
#include <source_location>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
namespace ripple::log {
template <typename T>
class LogParameter
{
public:
template <typename TArg>
LogParameter(char const* name, TArg&& value)
: name_(name), value_(std::forward<TArg>(value))
{
}
private:
char const* name_;
T value_;
template <typename U>
friend std::ostream&
operator<<(std::ostream& os, LogParameter<U> const&);
};
template <typename T>
class LogField
{
public:
template <typename TArg>
LogField(char const* name, TArg&& value)
: name_(name), value_(std::forward<TArg>(value))
{
}
private:
char const* name_;
T value_;
template <typename U>
friend std::ostream&
operator<<(std::ostream& os, LogField<U> const&);
};
template <typename T>
std::ostream&
operator<<(std::ostream& os, LogField<T> const& param);
template <typename T>
std::ostream&
operator<<(std::ostream& os, LogParameter<T> const& param);
} // namespace ripple::log
namespace beast {
namespace detail {
class SimpleJsonWriter
{
public:
explicit SimpleJsonWriter(std::string* buffer) : buffer_(buffer)
{
}
SimpleJsonWriter() = default;
SimpleJsonWriter(SimpleJsonWriter const& other) = default;
SimpleJsonWriter&
operator=(SimpleJsonWriter const& other) = default;
std::string&
buffer()
{
return *buffer_;
}
void
startObject() const
{
buffer_->push_back('{');
}
void
endObject() const
{
using namespace std::string_view_literals;
if (buffer_->back() == ',')
buffer_->pop_back();
buffer_->append("},"sv);
}
void
writeKey(std::string_view key) const
{
writeString(key);
buffer_->back() = ':';
}
void
startArray() const
{
buffer_->push_back('[');
}
void
endArray() const
{
using namespace std::string_view_literals;
if (buffer_->back() == ',')
buffer_->pop_back();
buffer_->append("],"sv);
}
void
writeString(std::string_view str) const
{
using namespace std::string_view_literals;
buffer_->push_back('"');
escape(str, *buffer_);
buffer_->append("\","sv);
}
std::string_view
writeInt(std::int32_t val) const
{
return pushNumber(val, *buffer_);
}
std::string_view
writeInt(std::int64_t val) const
{
return pushNumber(val, *buffer_);
}
std::string_view
writeUInt(std::uint32_t val) const
{
return pushNumber(val, *buffer_);
}
std::string_view
writeUInt(std::uint64_t val) const
{
return pushNumber(val, *buffer_);
}
std::string_view
writeDouble(double val) const
{
return pushNumber(val, *buffer_);
}
std::string_view
writeBool(bool val) const
{
using namespace std::string_view_literals;
auto str = val ? "true,"sv : "false,"sv;
buffer_->append(str);
return str;
}
void
writeNull() const
{
using namespace std::string_view_literals;
buffer_->append("null,"sv);
}
void
writeRaw(std::string_view str) const
{
buffer_->append(str);
}
void
finish()
{
buffer_->pop_back();
}
private:
template <typename T>
static std::string_view
pushNumber(T val, std::string& str)
{
thread_local char buffer[128];
auto result = std::to_chars(std::begin(buffer), std::end(buffer), val);
auto ptr = result.ptr;
*ptr = ',';
auto len = ptr - std::begin(buffer);
str.append(buffer, len + 1);
return {buffer, static_cast<size_t>(len)};
}
static void
escape(std::string_view str, std::string& buffer)
{
static constexpr char HEX[] = "0123456789ABCDEF";
char const* p = str.data();
char const* end = p + str.size();
char const* chunk = p;
while (p < end)
{
auto c = static_cast<unsigned char>(*p);
// JSON requires escaping for <0x20 and the two specials below.
bool needsEscape = (c < 0x20) || (c == '"') || (c == '\\');
if (!needsEscape)
{
++p;
continue;
}
// Flush the preceding safe run in one go.
if (chunk != p)
buffer.append(chunk, p - chunk);
switch (c)
{
case '"':
buffer.append("\\\"", 2);
break;
case '\\':
buffer.append("\\\\", 2);
break;
case '\b':
buffer.append("\\b", 2);
break;
case '\f':
buffer.append("\\f", 2);
break;
case '\n':
buffer.append("\\n", 2);
break;
case '\r':
buffer.append("\\r", 2);
break;
case '\t':
buffer.append("\\t", 2);
break;
default: {
// Other C0 controls -> \u00XX (JSON compliant)
char buf[6]{
'\\', 'u', '0', '0', HEX[(c >> 4) & 0xF], HEX[c & 0xF]};
buffer.append(buf, 6);
break;
}
}
++p;
chunk = p;
}
// Flush trailing safe run
if (chunk != p)
buffer.append(chunk, p - chunk);
}
std::string* buffer_ = nullptr;
};
} // namespace detail
/** A namespace for easy access to logging severity values. */
namespace severities {
/** Severity level / threshold of a Journal message. */
@@ -42,6 +298,9 @@ enum Severity {
kDisabled,
kNone = kDisabled
};
std::string_view
to_string(Severity severity);
} // namespace severities
/** A generic endpoint for log messages.
@@ -59,18 +318,114 @@ enum Severity {
class Journal
{
public:
template <typename T>
friend std::ostream&
ripple::log::operator<<(
std::ostream& os,
ripple::log::LogField<T> const& param);
template <typename T>
friend std::ostream&
ripple::log::operator<<(
std::ostream& os,
ripple::log::LogParameter<T> const& param);
class Sink;
class JsonLogContext
{
std::string messageBuffer_;
detail::SimpleJsonWriter jsonWriter_;
bool hasMessageParams_ = false;
std::size_t messageOffset_ = 0;
public:
JsonLogContext() : jsonWriter_(&messageBuffer_)
{
messageBuffer_.reserve(4 * 1024);
}
std::string&
messageBuffer()
{
return messageBuffer_;
}
void
startMessageParams()
{
if (!hasMessageParams_)
{
writer().writeKey("Dt");
writer().startObject();
hasMessageParams_ = true;
}
}
void
endMessageParams()
{
if (hasMessageParams_)
{
writer().endObject();
}
}
detail::SimpleJsonWriter&
writer()
{
return jsonWriter_;
}
void
reuseJson();
void
finish();
void
start(
std::source_location location,
severities::Severity severity,
std::string_view moduleName,
std::string_view journalAttributes) noexcept;
};
private:
// Severity level / threshold of a Journal message.
using Severity = severities::Severity;
std::string name_;
std::string attributes_;
static std::string globalLogAttributes_;
static std::shared_mutex globalLogAttributesMutex_;
static bool jsonLogsEnabled_;
static thread_local JsonLogContext currentJsonLogContext_;
// Invariant: m_sink always points to a valid Sink
Sink* m_sink;
Sink* m_sink = nullptr;
void
initMessageContext(
std::source_location location,
severities::Severity severity) const;
static std::string&
formatLog(std::string const& message);
public:
//--------------------------------------------------------------------------
static void
enableStructuredJournal();
static void
disableStructuredJournal();
static bool
isStructuredJournalEnabled();
/** Abstraction for the underlying message destination. */
class Sink
{
@@ -261,11 +616,32 @@ public:
/** Output stream support. */
/** @{ */
ScopedStream
operator<<(std::ostream& manip(std::ostream&)) const;
operator<<(std::ostream& manip(std::ostream&)) const&&
{
return {*this, manip};
}
template <typename T>
ScopedStream
operator<<(T const& t) const;
operator<<(T const& t) const&&
{
return {*this, t};
}
ScopedStream
operator<<(std::ostream& manip(std::ostream&)) const&
{
currentJsonLogContext_.reuseJson();
return {*this, manip};
}
template <typename T>
ScopedStream
operator<<(T const& t) const&
{
currentJsonLogContext_.reuseJson();
return {*this, t};
}
/** @} */
private:
@@ -287,11 +663,73 @@ public:
/** Journal has no default constructor. */
Journal() = delete;
/** Create a journal that writes to the specified sink. */
explicit Journal(Sink& sink) : m_sink(&sink)
Journal(Journal const& other)
: name_(other.name_)
, attributes_(other.attributes_)
, m_sink(other.m_sink)
{
}
template <typename TAttributesFactory>
Journal(Journal const& other, TAttributesFactory&& attributesFactory)
: name_(other.name_), m_sink(other.m_sink)
{
std::string buffer{other.attributes_};
detail::SimpleJsonWriter writer{&buffer};
if (other.attributes_.empty() && jsonLogsEnabled_)
{
writer.startObject();
}
attributesFactory(writer);
attributes_ = std::move(buffer);
}
/** Create a journal that writes to the specified sink. */
explicit Journal(Sink& sink, std::string const& name = {})
: name_(name), m_sink(&sink)
{
}
/** Create a journal that writes to the specified sink. */
template <typename TAttributesFactory>
explicit Journal(
Sink& sink,
std::string const& name,
TAttributesFactory&& attributesFactory)
: name_(name), m_sink(&sink)
{
std::string buffer;
buffer.reserve(128);
detail::SimpleJsonWriter writer{&buffer};
if (jsonLogsEnabled_)
{
writer.startObject();
}
attributesFactory(writer);
attributes_ = std::move(buffer);
}
Journal&
operator=(Journal const& other)
{
if (&other == this)
return *this; // LCOV_EXCL_LINE
m_sink = other.m_sink;
name_ = other.name_;
attributes_ = other.attributes_;
return *this;
}
Journal&
operator=(Journal&& other) noexcept
{
m_sink = other.m_sink;
name_ = std::move(other.name_);
attributes_ = std::move(other.attributes_);
return *this;
}
/** Returns the Sink associated with this Journal. */
Sink&
sink() const
@@ -301,8 +739,11 @@ public:
/** Returns a stream for this sink, with the specified severity level. */
Stream
stream(Severity level) const
stream(
Severity level,
std::source_location location = std::source_location::current()) const
{
initMessageContext(location, level);
return Stream(*m_sink, level);
}
@@ -319,41 +760,69 @@ public:
/** Severity stream access functions. */
/** @{ */
Stream
trace() const
trace(std::source_location location = std::source_location::current()) const
{
initMessageContext(location, severities::kTrace);
return {*m_sink, severities::kTrace};
}
Stream
debug() const
debug(std::source_location location = std::source_location::current()) const
{
initMessageContext(location, severities::kDebug);
return {*m_sink, severities::kDebug};
}
Stream
info() const
info(std::source_location location = std::source_location::current()) const
{
initMessageContext(location, severities::kInfo);
return {*m_sink, severities::kInfo};
}
Stream
warn() const
warn(std::source_location location = std::source_location::current()) const
{
initMessageContext(location, severities::kWarning);
return {*m_sink, severities::kWarning};
}
Stream
error() const
error(std::source_location location = std::source_location::current()) const
{
initMessageContext(location, severities::kError);
return {*m_sink, severities::kError};
}
Stream
fatal() const
fatal(std::source_location location = std::source_location::current()) const
{
initMessageContext(location, severities::kFatal);
return {*m_sink, severities::kFatal};
}
/** @} */
static void
resetGlobalAttributes()
{
std::unique_lock lock(globalLogAttributesMutex_);
globalLogAttributes_.clear();
}
template <typename TAttributesFactory>
static void
addGlobalAttributes(TAttributesFactory&& factory)
{
std::unique_lock lock(globalLogAttributesMutex_);
globalLogAttributes_.reserve(1024);
auto isEmpty = globalLogAttributes_.empty();
detail::SimpleJsonWriter writer{&globalLogAttributes_};
if (isEmpty && jsonLogsEnabled_)
{
writer.startObject();
}
factory(writer);
}
};
#ifndef __INTELLISENSE__
@@ -368,7 +837,7 @@ static_assert(std::is_nothrow_destructible<Journal>::value == true, "");
//------------------------------------------------------------------------------
template <typename T>
Journal::ScopedStream::ScopedStream(Journal::Stream const& stream, T const& t)
Journal::ScopedStream::ScopedStream(Stream const& stream, T const& t)
: ScopedStream(stream.sink(), stream.level())
{
m_ostream << t;
@@ -384,13 +853,6 @@ Journal::ScopedStream::operator<<(T const& t) const
//------------------------------------------------------------------------------
template <typename T>
Journal::ScopedStream
Journal::Stream::operator<<(T const& t) const
{
return ScopedStream(*this, t);
}
namespace detail {
template <class CharT, class Traits = std::char_traits<CharT>>
@@ -460,4 +922,244 @@ using logwstream = basic_logstream<wchar_t>;
} // namespace beast
namespace ripple::log {
namespace detail {
template <typename T>
concept ToCharsFormattable = requires(T val) {
{
to_chars(std::declval<char*>(), std::declval<char*>(), val)
} -> std::convertible_to<std::to_chars_result>;
};
template <typename T>
concept StreamFormattable = requires(T val) {
{
std::declval<std::ostream&>() << val
} -> std::convertible_to<std::ostream&>;
};
template <typename T>
void
setTextValue(
beast::detail::SimpleJsonWriter& writer,
char const* name,
T&& value)
{
using ValueType = std::decay_t<T>;
writer.buffer() += name;
writer.buffer() += ": ";
if constexpr (
std::is_same_v<ValueType, std::string> ||
std::is_same_v<ValueType, std::string_view> ||
std::is_same_v<ValueType, char const*> ||
std::is_same_v<ValueType, char*>)
{
writer.buffer() += value;
}
else
{
std::ostringstream oss;
oss << value;
writer.buffer() += value;
;
}
writer.buffer() += " ";
}
template <typename T>
void
setJsonValue(
beast::detail::SimpleJsonWriter& writer,
char const* name,
T&& value,
std::ostream* outStream)
{
using ValueType = std::decay_t<T>;
writer.writeKey(name);
if constexpr (std::is_same_v<ValueType, bool>)
{
auto sv = writer.writeBool(value);
if (outStream)
{
outStream->write(sv.data(), sv.size());
}
}
else if constexpr (std::is_integral_v<ValueType>)
{
std::string_view sv;
if constexpr (std::is_signed_v<ValueType>)
{
if constexpr (sizeof(ValueType) > 4)
{
sv = writer.writeInt(static_cast<std::int64_t>(value));
}
else
{
sv = writer.writeInt(static_cast<std::int32_t>(value));
}
}
else
{
if constexpr (sizeof(ValueType) > 4)
{
sv = writer.writeUInt(static_cast<std::uint64_t>(value));
}
else
{
sv = writer.writeUInt(static_cast<std::uint32_t>(value));
}
}
if (outStream)
{
outStream->write(sv.data(), sv.size());
}
}
else if constexpr (std::is_floating_point_v<ValueType>)
{
auto sv = writer.writeDouble(value);
if (outStream)
{
outStream->write(sv.data(), sv.size());
}
}
else if constexpr (
std::is_same_v<ValueType, char const*> ||
std::is_same_v<ValueType, char*>)
{
writer.writeString(value);
if (outStream)
{
outStream->write(value, std::strlen(value));
}
}
else if constexpr (
std::is_same_v<ValueType, std::string> ||
std::is_same_v<ValueType, std::string_view>)
{
writer.writeString(value);
if (outStream)
{
outStream->write(value.data(), value.size());
}
}
else
{
if constexpr (ToCharsFormattable<ValueType>)
{
char buffer[1024];
std::to_chars_result result =
to_chars(std::begin(buffer), std::end(buffer), value);
if (result.ec == std::errc{})
{
std::string_view sv{std::begin(buffer), result.ptr};
writer.writeString(sv);
if (outStream)
{
outStream->write(sv.data(), sv.size());
}
return;
}
}
if constexpr (StreamFormattable<ValueType>)
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << value;
auto str = oss.str();
writer.writeString(str);
if (outStream)
{
outStream->write(
str.c_str(), static_cast<std::streamsize>(str.size()));
}
return;
}
static_assert(
ToCharsFormattable<ValueType> || StreamFormattable<ValueType>);
}
}
} // namespace detail
template <typename T>
std::ostream&
operator<<(std::ostream& os, LogParameter<T> const& param)
{
if (!beast::Journal::jsonLogsEnabled_)
{
os << param.value_;
return os;
}
beast::Journal::currentJsonLogContext_.startMessageParams();
detail::setJsonValue(
beast::Journal::currentJsonLogContext_.writer(),
param.name_,
param.value_,
&os);
return os;
}
template <typename T>
std::ostream&
operator<<(std::ostream& os, LogField<T> const& param)
{
if (!beast::Journal::jsonLogsEnabled_)
return os;
beast::Journal::currentJsonLogContext_.startMessageParams();
detail::setJsonValue(
beast::Journal::currentJsonLogContext_.writer(),
param.name_,
param.value_,
nullptr);
return os;
}
template <typename T>
LogParameter<T>
param(char const* name, T&& value)
{
return LogParameter<T>{name, std::forward<T>(value)};
}
template <typename T>
LogField<T>
field(char const* name, T&& value)
{
return LogField<T>{name, std::forward<T>(value)};
}
template <typename... Pair>
[[nodiscard]] auto
attributes(Pair&&... pairs)
{
return [&](beast::detail::SimpleJsonWriter& writer) {
if (beast::Journal::isStructuredJournalEnabled())
{
(detail::setJsonValue(writer, pairs.first, pairs.second, nullptr),
...);
}
else
{
(detail::setTextValue(writer, pairs.first, pairs.second), ...);
}
};
}
template <typename T>
[[nodiscard]] std::pair<char const*, std::decay_t<T>>
attr(char const* name, T&& value)
{
return std::make_pair(name, std::forward<T>(value));
}
} // namespace ripple::log
#endif

View File

@@ -39,11 +39,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#endif
#define XRPL_ASSERT ALWAYS_OR_UNREACHABLE
#define XRPL_ASSERT_PARTS(cond, function, description, ...) \
XRPL_ASSERT(cond, function " : " description)
// How to use the instrumentation macros:
//
// * XRPL_ASSERT if cond must be true but the line might not be reached during
// fuzzing. Same like `assert` in normal use.
// * XRPL_ASSERT_PARTS is for convenience, and works like XRPL_ASSERT, but
// splits the message param into "function" and "description", then joins
// them with " : " before passing to XRPL_ASSERT.
// * ALWAYS if cond must be true _and_ the line must be reached during fuzzing.
// Same like `assert` in normal use.
// * REACHABLE if the line must be reached during fuzzing

View File

@@ -20,11 +20,10 @@
#ifndef RIPPLE_LEDGER_APPLYVIEW_H_INCLUDED
#define RIPPLE_LEDGER_APPLYVIEW_H_INCLUDED
#include <xrpld/ledger/RawView.h>
#include <xrpld/ledger/ReadView.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/ReadView.h>
namespace ripple {

View File

@@ -20,9 +20,8 @@
#ifndef RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
#define RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
#include <xrpld/ledger/OpenView.h>
#include <xrpld/ledger/detail/ApplyViewBase.h>
#include <xrpl/ledger/OpenView.h>
#include <xrpl/ledger/detail/ApplyViewBase.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/TER.h>

View File

@@ -20,9 +20,8 @@
#ifndef RIPPLE_LEDGER_BOOK_DIRS_H_INCLUDED
#define RIPPLE_LEDGER_BOOK_DIRS_H_INCLUDED
#include <xrpld/ledger/ReadView.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
namespace ripple {

View File

@@ -20,10 +20,9 @@
#ifndef RIPPLE_LEDGER_CACHEDVIEW_H_INCLUDED
#define RIPPLE_LEDGER_CACHEDVIEW_H_INCLUDED
#include <xrpld/ledger/CachedSLEs.h>
#include <xrpld/ledger/ReadView.h>
#include <xrpl/basics/hardened_hash.h>
#include <xrpl/ledger/CachedSLEs.h>
#include <xrpl/ledger/ReadView.h>
#include <mutex>
#include <type_traits>

View File

@@ -20,12 +20,11 @@
#ifndef RIPPLE_APP_MISC_CREDENTIALHELPERS_H_INCLUDED
#define RIPPLE_APP_MISC_CREDENTIALHELPERS_H_INCLUDED
#include <xrpld/ledger/ApplyView.h>
#include <xrpld/ledger/ReadView.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/STTx.h>

View File

@@ -20,8 +20,7 @@
#ifndef RIPPLE_LEDGER_DIR_H_INCLUDED
#define RIPPLE_LEDGER_DIR_H_INCLUDED
#include <xrpld/ledger/ReadView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Indexes.h>
namespace ripple {

View File

@@ -20,10 +20,9 @@
#ifndef RIPPLE_LEDGER_OPENVIEW_H_INCLUDED
#define RIPPLE_LEDGER_OPENVIEW_H_INCLUDED
#include <xrpld/ledger/RawView.h>
#include <xrpld/ledger/ReadView.h>
#include <xrpld/ledger/detail/RawStateTable.h>
#include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/detail/RawStateTable.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/XRPAmount.h>

View File

@@ -20,10 +20,9 @@
#ifndef RIPPLE_LEDGER_PAYMENTSANDBOX_H_INCLUDED
#define RIPPLE_LEDGER_PAYMENTSANDBOX_H_INCLUDED
#include <xrpld/ledger/RawView.h>
#include <xrpld/ledger/Sandbox.h>
#include <xrpld/ledger/detail/ApplyViewBase.h>
#include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/detail/ApplyViewBase.h>
#include <xrpl/protocol/AccountID.h>
#include <map>

View File

@@ -20,8 +20,7 @@
#ifndef RIPPLE_LEDGER_RAWVIEW_H_INCLUDED
#define RIPPLE_LEDGER_RAWVIEW_H_INCLUDED
#include <xrpld/ledger/ReadView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/Serializer.h>

View File

@@ -20,10 +20,9 @@
#ifndef RIPPLE_LEDGER_READVIEW_H_INCLUDED
#define RIPPLE_LEDGER_READVIEW_H_INCLUDED
#include <xrpld/ledger/detail/ReadViewFwdRange.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/hash/uhash.h>
#include <xrpl/ledger/detail/ReadViewFwdRange.h>
#include <xrpl/protocol/Fees.h>
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/Indexes.h>
@@ -280,6 +279,6 @@ makeRulesGivenLedger(
} // namespace ripple
#include <xrpld/ledger/detail/ReadViewFwdRange.ipp>
#include <xrpl/ledger/detail/ReadViewFwdRange.ipp>
#endif

View File

@@ -20,8 +20,8 @@
#ifndef RIPPLE_LEDGER_SANDBOX_H_INCLUDED
#define RIPPLE_LEDGER_SANDBOX_H_INCLUDED
#include <xrpld/ledger/RawView.h>
#include <xrpld/ledger/detail/ApplyViewBase.h>
#include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/detail/ApplyViewBase.h>
namespace ripple {

View File

@@ -20,11 +20,10 @@
#ifndef RIPPLE_LEDGER_VIEW_H_INCLUDED
#define RIPPLE_LEDGER_VIEW_H_INCLUDED
#include <xrpld/ledger/ApplyView.h>
#include <xrpld/ledger/OpenView.h>
#include <xrpld/ledger/ReadView.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/OpenView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
@@ -562,12 +561,28 @@ createPseudoAccount(
[[nodiscard]] bool
isPseudoAccount(std::shared_ptr<SLE const> sleAcct);
// Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account if
// set
// Pseudo-account designator fields MUST be maintained by including the
// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to
// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated,
// since a non-active amendment will not set any field, by definition.
// Specific properties of a pseudo-account are NOT checked here, that's what
// InvariantCheck is for.
[[nodiscard]] std::vector<SField const*> const&
getPseudoAccountFields();
[[nodiscard]] inline bool
isPseudoAccount(ReadView const& view, AccountID accountId)
{
return isPseudoAccount(view.read(keylet::account(accountId)));
}
[[nodiscard]] TER
canAddHolding(ReadView const& view, Asset const& asset);
/// Any transactors that call addEmptyHolding() in doApply must call
/// canAddHolding() in preflight with the same View and Asset
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,
@@ -719,7 +734,8 @@ rippleUnlockEscrowMPT(
ApplyView& view,
AccountID const& uGrantorID,
AccountID const& uGranteeID,
STAmount const& saAmount,
STAmount const& netAmount,
STAmount const& grossAmount,
beast::Journal j);
/** Calls static accountSendIOU if saAmount represents Issue.
@@ -912,28 +928,41 @@ deleteAMMTrustLine(
std::optional<AccountID> const& ammAccountID,
beast::Journal j);
// From the perspective of a vault,
// return the number of shares to give the depositor
// when they deposit a fixed amount of assets.
[[nodiscard]] STAmount
// From the perspective of a vault, return the number of shares to give the
// depositor when they deposit a fixed amount of assets. Since shares are MPT
// this number is integral and always truncated in this calculation.
[[nodiscard]] std::optional<STAmount>
assetsToSharesDeposit(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& assets);
// From the perspective of a vault,
// return the number of shares to demand from the depositor
// when they ask to withdraw a fixed amount of assets.
[[nodiscard]] STAmount
// From the perspective of a vault, return the number of assets to take from
// depositor when they receive a fixed amount of shares. Note, since shares are
// MPT, they are always an integral number.
[[nodiscard]] std::optional<STAmount>
sharesToAssetsDeposit(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& shares);
enum class TruncateShares : bool { no = false, yes = true };
// From the perspective of a vault, return the number of shares to demand from
// the depositor when they ask to withdraw a fixed amount of assets. Since
// shares are MPT this number is integral, and it will be rounded to nearest
// unless explicitly requested to be truncated instead.
[[nodiscard]] std::optional<STAmount>
assetsToSharesWithdraw(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& assets);
STAmount const& assets,
TruncateShares truncate = TruncateShares::no);
// From the perspective of a vault,
// return the number of assets to give the depositor
// when they redeem a fixed amount of shares.
[[nodiscard]] STAmount
// From the perspective of a vault, return the number of assets to give the
// depositor when they redeem a fixed amount of shares. Note, since shares are
// MPT, they are always an integral number.
[[nodiscard]] std::optional<STAmount>
sharesToAssetsWithdraw(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,

View File

@@ -20,11 +20,10 @@
#ifndef RIPPLE_LEDGER_APPLYSTATETABLE_H_INCLUDED
#define RIPPLE_LEDGER_APPLYSTATETABLE_H_INCLUDED
#include <xrpld/ledger/OpenView.h>
#include <xrpld/ledger/RawView.h>
#include <xrpld/ledger/ReadView.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/OpenView.h>
#include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxMeta.h>
#include <xrpl/protocol/XRPAmount.h>

View File

@@ -20,10 +20,9 @@
#ifndef RIPPLE_LEDGER_APPLYVIEWBASE_H_INCLUDED
#define RIPPLE_LEDGER_APPLYVIEWBASE_H_INCLUDED
#include <xrpld/ledger/ApplyView.h>
#include <xrpld/ledger/ReadView.h>
#include <xrpld/ledger/detail/ApplyStateTable.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/detail/ApplyStateTable.h>
#include <xrpl/protocol/XRPAmount.h>
namespace ripple {

View File

@@ -20,8 +20,8 @@
#ifndef RIPPLE_LEDGER_RAWSTATETABLE_H_INCLUDED
#define RIPPLE_LEDGER_RAWSTATETABLE_H_INCLUDED
#include <xrpld/ledger/RawView.h>
#include <xrpld/ledger/ReadView.h>
#include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/ReadView.h>
#include <boost/container/pmr/monotonic_buffer_resource.hpp>
#include <boost/container/pmr/polymorphic_allocator.hpp>

View File

@@ -1,565 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2019 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef BASICS_FEES_H_INCLUDED
#define BASICS_FEES_H_INCLUDED
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/operators.hpp>
#include <iosfwd>
#include <limits>
#include <optional>
namespace ripple {
namespace feeunit {
/** "drops" are the smallest divisible amount of XRP. This is what most
of the code uses. */
struct dropTag;
/** "fee units" calculations are a not-really-unitless value that is used
to express the cost of a given transaction vs. a reference transaction.
They are primarily used by the Transactor classes. */
struct feeunitTag;
/** "fee levels" are used by the transaction queue to compare the relative
cost of transactions that require different levels of effort to process.
See also: src/ripple/app/misc/FeeEscalation.md#fee-level */
struct feelevelTag;
/** unitless values are plain scalars wrapped in a TaggedFee. They are
used for calculations in this header. */
struct unitlessTag;
template <class T>
using enable_if_unit_t = typename std::enable_if_t<
std::is_class_v<T> && std::is_object_v<typename T::unit_type> &&
std::is_object_v<typename T::value_type>>;
/** `is_usable_unit_v` is checked to ensure that only values with
known valid type tags can be used (sometimes transparently) in
non-fee contexts. At the time of implementation, this includes
all known tags, but more may be added in the future, and they
should not be added automatically unless determined to be
appropriate.
*/
template <class T, class = enable_if_unit_t<T>>
constexpr bool is_usable_unit_v =
std::is_same_v<typename T::unit_type, feeunitTag> ||
std::is_same_v<typename T::unit_type, feelevelTag> ||
std::is_same_v<typename T::unit_type, unitlessTag> ||
std::is_same_v<typename T::unit_type, dropTag>;
template <class UnitTag, class T>
class TaggedFee : private boost::totally_ordered<TaggedFee<UnitTag, T>>,
private boost::additive<TaggedFee<UnitTag, T>>,
private boost::equality_comparable<TaggedFee<UnitTag, T>, T>,
private boost::dividable<TaggedFee<UnitTag, T>, T>,
private boost::modable<TaggedFee<UnitTag, T>, T>,
private boost::unit_steppable<TaggedFee<UnitTag, T>>
{
public:
using unit_type = UnitTag;
using value_type = T;
private:
value_type fee_;
protected:
template <class Other>
static constexpr bool is_compatible_v =
std::is_arithmetic_v<Other> && std::is_arithmetic_v<value_type> &&
std::is_convertible_v<Other, value_type>;
template <class OtherFee, class = enable_if_unit_t<OtherFee>>
static constexpr bool is_compatiblefee_v =
is_compatible_v<typename OtherFee::value_type> &&
std::is_same_v<UnitTag, typename OtherFee::unit_type>;
template <class Other>
using enable_if_compatible_t =
typename std::enable_if_t<is_compatible_v<Other>>;
template <class OtherFee>
using enable_if_compatiblefee_t =
typename std::enable_if_t<is_compatiblefee_v<OtherFee>>;
public:
TaggedFee() = default;
constexpr TaggedFee(TaggedFee const& other) = default;
constexpr TaggedFee&
operator=(TaggedFee const& other) = default;
constexpr explicit TaggedFee(beast::Zero) : fee_(0)
{
}
constexpr TaggedFee&
operator=(beast::Zero)
{
fee_ = 0;
return *this;
}
constexpr explicit TaggedFee(value_type fee) : fee_(fee)
{
}
TaggedFee&
operator=(value_type fee)
{
fee_ = fee;
return *this;
}
/** Instances with the same unit, and a type that is
"safe" to convert to this one can be converted
implicitly */
template <
class Other,
class = std::enable_if_t<
is_compatible_v<Other> &&
is_safetocasttovalue_v<value_type, Other>>>
constexpr TaggedFee(TaggedFee<unit_type, Other> const& fee)
: TaggedFee(safe_cast<value_type>(fee.fee()))
{
}
constexpr TaggedFee
operator*(value_type const& rhs) const
{
return TaggedFee{fee_ * rhs};
}
friend constexpr TaggedFee
operator*(value_type lhs, TaggedFee const& rhs)
{
// multiplication is commutative
return rhs * lhs;
}
constexpr value_type
operator/(TaggedFee const& rhs) const
{
return fee_ / rhs.fee_;
}
TaggedFee&
operator+=(TaggedFee const& other)
{
fee_ += other.fee();
return *this;
}
TaggedFee&
operator-=(TaggedFee const& other)
{
fee_ -= other.fee();
return *this;
}
TaggedFee&
operator++()
{
++fee_;
return *this;
}
TaggedFee&
operator--()
{
--fee_;
return *this;
}
TaggedFee&
operator*=(value_type const& rhs)
{
fee_ *= rhs;
return *this;
}
TaggedFee&
operator/=(value_type const& rhs)
{
fee_ /= rhs;
return *this;
}
template <class transparent = value_type>
std::enable_if_t<std::is_integral_v<transparent>, TaggedFee&>
operator%=(value_type const& rhs)
{
fee_ %= rhs;
return *this;
}
TaggedFee
operator-() const
{
static_assert(
std::is_signed_v<T>, "- operator illegal on unsigned fee types");
return TaggedFee{-fee_};
}
bool
operator==(TaggedFee const& other) const
{
return fee_ == other.fee_;
}
template <class Other, class = enable_if_compatible_t<Other>>
bool
operator==(TaggedFee<unit_type, Other> const& other) const
{
return fee_ == other.fee();
}
bool
operator==(value_type other) const
{
return fee_ == other;
}
template <class Other, class = enable_if_compatible_t<Other>>
bool
operator!=(TaggedFee<unit_type, Other> const& other) const
{
return !operator==(other);
}
bool
operator<(TaggedFee const& other) const
{
return fee_ < other.fee_;
}
/** Returns true if the amount is not zero */
explicit constexpr
operator bool() const noexcept
{
return fee_ != 0;
}
/** Return the sign of the amount */
constexpr int
signum() const noexcept
{
return (fee_ < 0) ? -1 : (fee_ ? 1 : 0);
}
/** Returns the number of drops */
constexpr value_type
fee() const
{
return fee_;
}
template <class Other>
constexpr double
decimalFromReference(TaggedFee<unit_type, Other> reference) const
{
return static_cast<double>(fee_) / reference.fee();
}
// `is_usable_unit_v` is checked to ensure that only values with
// known valid type tags can be converted to JSON. At the time
// of implementation, that includes all known tags, but more may
// be added in the future.
std::enable_if_t<is_usable_unit_v<TaggedFee>, Json::Value>
jsonClipped() const
{
if constexpr (std::is_integral_v<value_type>)
{
using jsontype = std::conditional_t<
std::is_signed_v<value_type>,
Json::Int,
Json::UInt>;
constexpr auto min = std::numeric_limits<jsontype>::min();
constexpr auto max = std::numeric_limits<jsontype>::max();
if (fee_ < min)
return min;
if (fee_ > max)
return max;
return static_cast<jsontype>(fee_);
}
else
{
return fee_;
}
}
/** Returns the underlying value. Code SHOULD NOT call this
function unless the type has been abstracted away,
e.g. in a templated function.
*/
constexpr value_type
value() const
{
return fee_;
}
friend std::istream&
operator>>(std::istream& s, TaggedFee& val)
{
s >> val.fee_;
return s;
}
};
// Output Fees as just their numeric value.
template <class Char, class Traits, class UnitTag, class T>
std::basic_ostream<Char, Traits>&
operator<<(std::basic_ostream<Char, Traits>& os, TaggedFee<UnitTag, T> const& q)
{
return os << q.value();
}
template <class UnitTag, class T>
std::string
to_string(TaggedFee<UnitTag, T> const& amount)
{
return std::to_string(amount.fee());
}
template <class Source, class = enable_if_unit_t<Source>>
constexpr bool can_muldiv_source_v =
std::is_convertible_v<typename Source::value_type, std::uint64_t>;
template <class Dest, class = enable_if_unit_t<Dest>>
constexpr bool can_muldiv_dest_v =
can_muldiv_source_v<Dest> && // Dest is also a source
std::is_convertible_v<std::uint64_t, typename Dest::value_type> &&
sizeof(typename Dest::value_type) >= sizeof(std::uint64_t);
template <
class Source1,
class Source2,
class = enable_if_unit_t<Source1>,
class = enable_if_unit_t<Source2>>
constexpr bool can_muldiv_sources_v =
can_muldiv_source_v<Source1> && can_muldiv_source_v<Source2> &&
std::is_same_v<typename Source1::unit_type, typename Source2::unit_type>;
template <
class Source1,
class Source2,
class Dest,
class = enable_if_unit_t<Source1>,
class = enable_if_unit_t<Source2>,
class = enable_if_unit_t<Dest>>
constexpr bool can_muldiv_v =
can_muldiv_sources_v<Source1, Source2> && can_muldiv_dest_v<Dest>;
// Source and Dest can be the same by default
template <
class Source1,
class Source2,
class Dest,
class = enable_if_unit_t<Source1>,
class = enable_if_unit_t<Source2>,
class = enable_if_unit_t<Dest>>
constexpr bool can_muldiv_commute_v = can_muldiv_v<Source1, Source2, Dest> &&
!std::is_same_v<typename Source1::unit_type, typename Dest::unit_type>;
template <class T>
using enable_muldiv_source_t =
typename std::enable_if_t<can_muldiv_source_v<T>>;
template <class T>
using enable_muldiv_dest_t = typename std::enable_if_t<can_muldiv_dest_v<T>>;
template <class Source1, class Source2>
using enable_muldiv_sources_t =
typename std::enable_if_t<can_muldiv_sources_v<Source1, Source2>>;
template <class Source1, class Source2, class Dest>
using enable_muldiv_t =
typename std::enable_if_t<can_muldiv_v<Source1, Source2, Dest>>;
template <class Source1, class Source2, class Dest>
using enable_muldiv_commute_t =
typename std::enable_if_t<can_muldiv_commute_v<Source1, Source2, Dest>>;
template <class T>
TaggedFee<unitlessTag, T>
scalar(T value)
{
return TaggedFee<unitlessTag, T>{value};
}
template <
class Source1,
class Source2,
class Dest,
class = enable_muldiv_t<Source1, Source2, Dest>>
std::optional<Dest>
mulDivU(Source1 value, Dest mul, Source2 div)
{
// Fees can never be negative in any context.
if (value.value() < 0 || mul.value() < 0 || div.value() < 0)
{
// split the asserts so if one hits, the user can tell which
// without a debugger.
XRPL_ASSERT(
value.value() >= 0,
"ripple::feeunit::mulDivU : minimum value input");
XRPL_ASSERT(
mul.value() >= 0, "ripple::feeunit::mulDivU : minimum mul input");
XRPL_ASSERT(
div.value() >= 0, "ripple::feeunit::mulDivU : minimum div input");
return std::nullopt;
}
using desttype = typename Dest::value_type;
constexpr auto max = std::numeric_limits<desttype>::max();
// Shortcuts, since these happen a lot in the real world
if (value == div)
return mul;
if (mul.value() == div.value())
{
if (value.value() > max)
return std::nullopt;
return Dest{static_cast<desttype>(value.value())};
}
using namespace boost::multiprecision;
uint128_t product;
product = multiply(
product,
static_cast<std::uint64_t>(value.value()),
static_cast<std::uint64_t>(mul.value()));
auto quotient = product / div.value();
if (quotient > max)
return std::nullopt;
return Dest{static_cast<desttype>(quotient)};
}
} // namespace feeunit
template <class T>
using FeeLevel = feeunit::TaggedFee<feeunit::feelevelTag, T>;
using FeeLevel64 = FeeLevel<std::uint64_t>;
using FeeLevelDouble = FeeLevel<double>;
template <
class Source1,
class Source2,
class Dest,
class = feeunit::enable_muldiv_t<Source1, Source2, Dest>>
std::optional<Dest>
mulDiv(Source1 value, Dest mul, Source2 div)
{
return feeunit::mulDivU(value, mul, div);
}
template <
class Source1,
class Source2,
class Dest,
class = feeunit::enable_muldiv_commute_t<Source1, Source2, Dest>>
std::optional<Dest>
mulDiv(Dest value, Source1 mul, Source2 div)
{
// Multiplication is commutative
return feeunit::mulDivU(mul, value, div);
}
template <class Dest, class = feeunit::enable_muldiv_dest_t<Dest>>
std::optional<Dest>
mulDiv(std::uint64_t value, Dest mul, std::uint64_t div)
{
// Give the scalars a non-tag so the
// unit-handling version gets called.
return feeunit::mulDivU(feeunit::scalar(value), mul, feeunit::scalar(div));
}
template <class Dest, class = feeunit::enable_muldiv_dest_t<Dest>>
std::optional<Dest>
mulDiv(Dest value, std::uint64_t mul, std::uint64_t div)
{
// Multiplication is commutative
return mulDiv(mul, value, div);
}
template <
class Source1,
class Source2,
class = feeunit::enable_muldiv_sources_t<Source1, Source2>>
std::optional<std::uint64_t>
mulDiv(Source1 value, std::uint64_t mul, Source2 div)
{
// Give the scalars a dimensionless unit so the
// unit-handling version gets called.
auto unitresult = feeunit::mulDivU(value, feeunit::scalar(mul), div);
if (!unitresult)
return std::nullopt;
return unitresult->value();
}
template <
class Source1,
class Source2,
class = feeunit::enable_muldiv_sources_t<Source1, Source2>>
std::optional<std::uint64_t>
mulDiv(std::uint64_t value, Source1 mul, Source2 div)
{
// Multiplication is commutative
return mulDiv(mul, value, div);
}
template <class Dest, class Src>
constexpr std::enable_if_t<
std::is_same_v<typename Dest::unit_type, typename Src::unit_type> &&
std::is_integral_v<typename Dest::value_type> &&
std::is_integral_v<typename Src::value_type>,
Dest>
safe_cast(Src s) noexcept
{
// Dest may not have an explicit value constructor
return Dest{safe_cast<typename Dest::value_type>(s.value())};
}
template <class Dest, class Src>
constexpr std::enable_if_t<
std::is_same_v<typename Dest::unit_type, typename Src::unit_type> &&
std::is_integral_v<typename Dest::value_type> &&
std::is_integral_v<typename Src::value_type>,
Dest>
unsafe_cast(Src s) noexcept
{
// Dest may not have an explicit value constructor
return Dest{unsafe_cast<typename Dest::value_type>(s.value())};
}
} // namespace ripple
#endif // BASICS_FEES_H_INCLUDED

View File

@@ -287,9 +287,11 @@ delegate(AccountID const& account, AccountID const& authorizedAccount) noexcept;
Keylet
bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType);
// `seq` is stored as `sfXChainClaimID` in the object
Keylet
xChainClaimID(STXChainBridge const& bridge, std::uint64_t seq);
// `seq` is stored as `sfXChainAccountCreateCount` in the object
Keylet
xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t seq);

View File

@@ -56,7 +56,7 @@ enum LedgerEntryType : std::uint16_t
#pragma push_macro("LEDGER_ENTRY")
#undef LEDGER_ENTRY
#define LEDGER_ENTRY(tag, value, name, rpcName, fields) tag = value,
#define LEDGER_ENTRY(tag, value, ...) tag = value,
#include <xrpl/protocol/detail/ledger_entries.macro>
@@ -188,6 +188,15 @@ enum LedgerSpecificFlags {
lsfMPTCanTransfer = 0x00000020,
lsfMPTCanClawback = 0x00000040,
lmfMPTCanMutateCanLock = 0x00000002,
lmfMPTCanMutateRequireAuth = 0x00000004,
lmfMPTCanMutateCanEscrow = 0x00000008,
lmfMPTCanMutateCanTrade = 0x00000010,
lmfMPTCanMutateCanTransfer = 0x00000020,
lmfMPTCanMutateCanClawback = 0x00000040,
lmfMPTCanMutateMetadata = 0x00010000,
lmfMPTCanMutateTransferFee = 0x00020000,
// ltMPTOKEN
lsfMPTAuthorized = 0x00000002,

View File

@@ -20,6 +20,8 @@
#ifndef RIPPLE_PROTOCOL_PERMISSION_H_INCLUDED
#define RIPPLE_PROTOCOL_PERMISSION_H_INCLUDED
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <optional>
@@ -53,6 +55,8 @@ class Permission
private:
Permission();
std::unordered_map<std::uint16_t, uint256> txFeatureMap_;
std::unordered_map<std::uint16_t, Delegation> delegatableTx_;
std::unordered_map<std::string, GranularPermissionType>
@@ -70,6 +74,9 @@ public:
Permission&
operator=(Permission const&) = delete;
std::optional<std::string>
getPermissionName(std::uint32_t const value) const;
std::optional<std::uint32_t>
getGranularValue(std::string const& name) const;
@@ -80,7 +87,8 @@ public:
getGranularTxType(GranularPermissionType const& gpType) const;
bool
isDelegatable(std::uint32_t const& permissionValue) const;
isDelegatable(std::uint32_t const& permissionValue, Rules const& rules)
const;
// for tx level permission, permission value is equal to tx type plus one
uint32_t

View File

@@ -22,7 +22,6 @@
#include <xrpl/basics/ByteUtilities.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/partitioned_unordered_map.h>
#include <cstdint>
@@ -122,6 +121,13 @@ std::size_t constexpr maxDataPayloadLength = 256;
/** Vault withdrawal policies */
std::uint8_t constexpr vaultStrategyFirstComeFirstServe = 1;
/** Default IOU scale factor for a Vault */
std::uint8_t constexpr vaultDefaultIOUScale = 6;
/** Maximum scale factor for a Vault. The number is chosen to ensure that
1 IOU can be always converted to shares.
10^19 > maxMPTokenAmount (2^64-1) > 10^18 */
std::uint8_t constexpr vaultMaximumIOUScale = 18;
/** Maximum recursion depth for vault shares being put as an asset inside
* another vault; counted from 0 */
std::uint8_t constexpr maxAssetCheckDepth = 5;

View File

@@ -22,6 +22,7 @@
#include <xrpl/basics/safe_cast.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/Units.h>
#include <cstdint>
#include <map>
@@ -148,8 +149,10 @@ public:
sMD_ChangeNew = 0x02, // new value when it changes
sMD_DeleteFinal = 0x04, // final value when it is deleted
sMD_Create = 0x08, // value when it's created
sMD_Always = 0x10, // value when node containing it is affected at all
sMD_BaseTen = 0x20,
sMD_Always = 0x10, // value when node containing it is affected at all
sMD_BaseTen = 0x20, // value is treated as base 10, overriding behavior
sMD_PseudoAccount = 0x40, // if this field is set in an ACCOUNT_ROOT
// _only_, then it is a pseudo-account
sMD_Default =
sMD_ChangeOrig | sMD_ChangeNew | sMD_DeleteFinal | sMD_Create
};
@@ -184,7 +187,7 @@ public:
char const* fn,
int meta = sMD_Default,
IsSigning signing = IsSigning::yes);
explicit SField(private_access_tag_t, int fc);
explicit SField(private_access_tag_t, int fc, char const* fn);
static SField const&
getField(int fieldCode);
@@ -297,7 +300,7 @@ public:
static int
compare(SField const& f1, SField const& f2);
static std::map<int, SField const*> const&
static std::unordered_map<int, SField const*> const&
getKnownCodeToField()
{
return knownCodeToField;
@@ -305,7 +308,8 @@ public:
private:
static int num;
static std::map<int, SField const*> knownCodeToField;
static std::unordered_map<int, SField const*> knownCodeToField;
static std::unordered_map<std::string, SField const*> knownNameToField;
};
/** A field with a type known at compile time. */

View File

@@ -26,7 +26,9 @@
namespace ripple {
class Rules;
namespace test {
class Invariants_test;
}
class STLedgerEntry final : public STObject, public CountedObject<STLedgerEntry>
{
@@ -36,6 +38,8 @@ class STLedgerEntry final : public STObject, public CountedObject<STLedgerEntry>
public:
using pointer = std::shared_ptr<STLedgerEntry>;
using ref = std::shared_ptr<STLedgerEntry> const&;
using const_pointer = std::shared_ptr<STLedgerEntry const>;
using const_ref = std::shared_ptr<STLedgerEntry const> const&;
/** Create an empty object with the given key and type. */
explicit STLedgerEntry(Keylet const& k);
@@ -54,7 +58,7 @@ public:
getText() const override;
Json::Value
getJson(JsonOptions options) const override;
getJson(JsonOptions options = JsonOptions::none) const override;
/** Returns the 'key' (or 'index') of this item.
The key identifies this entry's position in
@@ -84,7 +88,8 @@ private:
void
setSLEType();
friend Invariants_test; // this test wants access to the private type_
friend test::Invariants_test; // this test wants access to the private
// type_
STBase*
copy(std::size_t n, void* buf) const override;

View File

@@ -25,7 +25,6 @@
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/FeeUnits.h>
#include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/SOTemplate.h>
#include <xrpl/protocol/STAmount.h>
@@ -34,6 +33,7 @@
#include <xrpl/protocol/STIssue.h>
#include <xrpl/protocol/STPathSet.h>
#include <xrpl/protocol/STVector256.h>
#include <xrpl/protocol/Units.h>
#include <xrpl/protocol/detail/STVar.h>
#include <boost/iterator/transform_iterator.hpp>

View File

@@ -54,34 +54,6 @@ public:
Json::Value error;
};
/** Holds the serialized result of parsing an input JSON array.
This does validation and checking on the provided JSON.
*/
class STParsedJSONArray
{
public:
/** Parses and creates an STParsedJSON array.
The result of the parsing is stored in array and error.
Exceptions:
Does not throw.
@param name The name of the JSON field, used in diagnostics.
@param json The JSON-RPC to parse.
*/
STParsedJSONArray(std::string const& name, Json::Value const& json);
STParsedJSONArray() = delete;
STParsedJSONArray(STParsedJSONArray const&) = delete;
STParsedJSONArray&
operator=(STParsedJSONArray const&) = delete;
~STParsedJSONArray() = default;
/** The STArray if the parse was successful. */
std::optional<STArray> array;
/** On failure, an appropriate set of error values. */
Json::Value error;
};
} // namespace ripple
#endif

View File

@@ -22,10 +22,10 @@
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/FeeUnits.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/SecretKey.h>
#include <xrpl/protocol/Units.h>
#include <cstdint>
#include <optional>

View File

@@ -127,6 +127,8 @@ constexpr std::uint32_t tfTrustSetPermissionMask = ~(tfUniversal | tfSetfAuth |
// EnableAmendment flags:
constexpr std::uint32_t tfGotMajority = 0x00010000;
constexpr std::uint32_t tfLostMajority = 0x00020000;
constexpr std::uint32_t tfChangeMask =
~( tfUniversal | tfGotMajority | tfLostMajority);
// PaymentChannelClaim flags:
constexpr std::uint32_t tfRenew = 0x00010000;
@@ -141,7 +143,8 @@ constexpr std::uint32_t const tfTransferable = 0x00000008;
constexpr std::uint32_t const tfMutable = 0x00000010;
// MPTokenIssuanceCreate flags:
// NOTE - there is intentionally no flag here for lsfMPTLocked, which this transaction cannot mutate.
// Note: tf/lsfMPTLocked is intentionally omitted, since this transaction
// is not allowed to modify it.
constexpr std::uint32_t const tfMPTCanLock = lsfMPTCanLock;
constexpr std::uint32_t const tfMPTRequireAuth = lsfMPTRequireAuth;
constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow;
@@ -151,6 +154,20 @@ constexpr std::uint32_t const tfMPTCanClawback = lsfMPTCanClawback;
constexpr std::uint32_t const tfMPTokenIssuanceCreateMask =
~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback);
// MPTokenIssuanceCreate MutableFlags:
// Indicating specific fields or flags may be changed after issuance.
constexpr std::uint32_t const tmfMPTCanMutateCanLock = lmfMPTCanMutateCanLock;
constexpr std::uint32_t const tmfMPTCanMutateRequireAuth = lmfMPTCanMutateRequireAuth;
constexpr std::uint32_t const tmfMPTCanMutateCanEscrow = lmfMPTCanMutateCanEscrow;
constexpr std::uint32_t const tmfMPTCanMutateCanTrade = lmfMPTCanMutateCanTrade;
constexpr std::uint32_t const tmfMPTCanMutateCanTransfer = lmfMPTCanMutateCanTransfer;
constexpr std::uint32_t const tmfMPTCanMutateCanClawback = lmfMPTCanMutateCanClawback;
constexpr std::uint32_t const tmfMPTCanMutateMetadata = lmfMPTCanMutateMetadata;
constexpr std::uint32_t const tmfMPTCanMutateTransferFee = lmfMPTCanMutateTransferFee;
constexpr std::uint32_t const tmfMPTokenIssuanceCreateMutableMask =
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow | tmfMPTCanMutateCanTrade
| tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
// MPTokenAuthorize flags:
constexpr std::uint32_t const tfMPTUnauthorize = 0x00000001;
constexpr std::uint32_t const tfMPTokenAuthorizeMask = ~(tfUniversal | tfMPTUnauthorize);
@@ -161,6 +178,25 @@ constexpr std::uint32_t const tfMPTUnlock = 0x00000002;
constexpr std::uint32_t const tfMPTokenIssuanceSetMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock);
constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock);
// MPTokenIssuanceSet MutableFlags:
// Set or Clear flags.
constexpr std::uint32_t const tmfMPTSetCanLock = 0x00000001;
constexpr std::uint32_t const tmfMPTClearCanLock = 0x00000002;
constexpr std::uint32_t const tmfMPTSetRequireAuth = 0x00000004;
constexpr std::uint32_t const tmfMPTClearRequireAuth = 0x00000008;
constexpr std::uint32_t const tmfMPTSetCanEscrow = 0x00000010;
constexpr std::uint32_t const tmfMPTClearCanEscrow = 0x00000020;
constexpr std::uint32_t const tmfMPTSetCanTrade = 0x00000040;
constexpr std::uint32_t const tmfMPTClearCanTrade = 0x00000080;
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 tmfMPTokenIssuanceSetMutableMask = ~(tmfMPTSetCanLock | tmfMPTClearCanLock |
tmfMPTSetRequireAuth | tmfMPTClearRequireAuth | tmfMPTSetCanEscrow | tmfMPTClearCanEscrow |
tmfMPTSetCanTrade | tmfMPTClearCanTrade | tmfMPTSetCanTransfer | tmfMPTClearCanTransfer |
tmfMPTSetCanClawback | tmfMPTClearCanClawback);
// MPTokenIssuanceDestroy flags:
constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal;

View File

@@ -59,7 +59,7 @@ enum TxType : std::uint16_t
#pragma push_macro("TRANSACTION")
#undef TRANSACTION
#define TRANSACTION(tag, value, name, delegatable, fields) tag = value,
#define TRANSACTION(tag, value, ...) tag = value,
#include <xrpl/protocol/detail/transactions.macro>

View File

@@ -0,0 +1,555 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2019 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef PROTOCOL_UNITS_H_INCLUDED
#define PROTOCOL_UNITS_H_INCLUDED
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/operators.hpp>
#include <iosfwd>
#include <limits>
#include <optional>
namespace ripple {
namespace unit {
/** "drops" are the smallest divisible amount of XRP. This is what most
of the code uses. */
struct dropTag;
/** "fee levels" are used by the transaction queue to compare the relative
cost of transactions that require different levels of effort to process.
See also: src/ripple/app/misc/FeeEscalation.md#fee-level */
struct feelevelTag;
/** unitless values are plain scalars wrapped in a ValueUnit. They are
used for calculations in this header. */
struct unitlessTag;
/** Units to represent basis points (bips) and 1/10 basis points */
class BipsTag;
class TenthBipsTag;
// These names don't have to be too descriptive, because we're in the "unit"
// namespace.
template <class T>
concept Valid = std::is_class_v<T> && std::is_object_v<typename T::unit_type> &&
std::is_object_v<typename T::value_type>;
/** `Usable` is checked to ensure that only values with
known valid type tags can be used (sometimes transparently) in
non-unit contexts. At the time of implementation, this includes
all known tags, but more may be added in the future, and they
should not be added automatically unless determined to be
appropriate.
*/
template <class T>
concept Usable = Valid<T> &&
(std::is_same_v<typename T::unit_type, feelevelTag> ||
std::is_same_v<typename T::unit_type, unitlessTag> ||
std::is_same_v<typename T::unit_type, dropTag> ||
std::is_same_v<typename T::unit_type, BipsTag> ||
std::is_same_v<typename T::unit_type, TenthBipsTag>);
template <class Other, class VU>
concept Compatible = Valid<VU> && std::is_arithmetic_v<Other> &&
std::is_arithmetic_v<typename VU::value_type> &&
std::is_convertible_v<Other, typename VU::value_type>;
template <class T>
concept Integral = std::is_integral_v<T>;
template <class VU>
concept IntegralValue = Integral<typename VU::value_type>;
template <class VU1, class VU2>
concept CastableValue = IntegralValue<VU1> && IntegralValue<VU2> &&
std::is_same_v<typename VU1::unit_type, typename VU2::unit_type>;
template <class UnitTag, class T>
class ValueUnit : private boost::totally_ordered<ValueUnit<UnitTag, T>>,
private boost::additive<ValueUnit<UnitTag, T>>,
private boost::equality_comparable<ValueUnit<UnitTag, T>, T>,
private boost::dividable<ValueUnit<UnitTag, T>, T>,
private boost::modable<ValueUnit<UnitTag, T>, T>,
private boost::unit_steppable<ValueUnit<UnitTag, T>>
{
public:
using unit_type = UnitTag;
using value_type = T;
private:
value_type value_;
public:
ValueUnit() = default;
constexpr ValueUnit(ValueUnit const& other) = default;
constexpr ValueUnit&
operator=(ValueUnit const& other) = default;
constexpr explicit ValueUnit(beast::Zero) : value_(0)
{
}
constexpr ValueUnit&
operator=(beast::Zero)
{
value_ = 0;
return *this;
}
constexpr explicit ValueUnit(value_type value) : value_(value)
{
}
constexpr ValueUnit&
operator=(value_type value)
{
value_ = value;
return *this;
}
/** Instances with the same unit, and a type that is
"safe" to convert to this one can be converted
implicitly */
template <Compatible<ValueUnit> Other>
constexpr ValueUnit(ValueUnit<unit_type, Other> const& value)
requires SafeToCast<Other, value_type>
: ValueUnit(safe_cast<value_type>(value.value()))
{
}
constexpr ValueUnit
operator+(value_type const& rhs) const
{
return ValueUnit{value_ + rhs};
}
friend constexpr ValueUnit
operator+(value_type lhs, ValueUnit const& rhs)
{
// addition is commutative
return rhs + lhs;
}
constexpr ValueUnit
operator-(value_type const& rhs) const
{
return ValueUnit{value_ - rhs};
}
friend constexpr ValueUnit
operator-(value_type lhs, ValueUnit const& rhs)
{
// subtraction is NOT commutative, but (lhs + (-rhs)) is addition, which
// is
return -rhs + lhs;
}
constexpr ValueUnit
operator*(value_type const& rhs) const
{
return ValueUnit{value_ * rhs};
}
friend constexpr ValueUnit
operator*(value_type lhs, ValueUnit const& rhs)
{
// multiplication is commutative
return rhs * lhs;
}
constexpr value_type
operator/(ValueUnit const& rhs) const
{
return value_ / rhs.value_;
}
ValueUnit&
operator+=(ValueUnit const& other)
{
value_ += other.value();
return *this;
}
ValueUnit&
operator-=(ValueUnit const& other)
{
value_ -= other.value();
return *this;
}
ValueUnit&
operator++()
{
++value_;
return *this;
}
ValueUnit&
operator--()
{
--value_;
return *this;
}
ValueUnit&
operator*=(value_type const& rhs)
{
value_ *= rhs;
return *this;
}
ValueUnit&
operator/=(value_type const& rhs)
{
value_ /= rhs;
return *this;
}
template <Integral transparent = value_type>
ValueUnit&
operator%=(value_type const& rhs)
{
value_ %= rhs;
return *this;
}
ValueUnit
operator-() const
{
static_assert(
std::is_signed_v<T>, "- operator illegal on unsigned value types");
return ValueUnit{-value_};
}
constexpr bool
operator==(ValueUnit const& other) const
{
return value_ == other.value_;
}
template <Compatible<ValueUnit> Other>
constexpr bool
operator==(ValueUnit<unit_type, Other> const& other) const
{
return value_ == other.value();
}
constexpr bool
operator==(value_type other) const
{
return value_ == other;
}
template <Compatible<ValueUnit> Other>
constexpr bool
operator!=(ValueUnit<unit_type, Other> const& other) const
{
return !operator==(other);
}
constexpr bool
operator<(ValueUnit const& other) const
{
return value_ < other.value_;
}
/** Returns true if the amount is not zero */
explicit constexpr
operator bool() const noexcept
{
return value_ != 0;
}
/** Return the sign of the amount */
constexpr int
signum() const noexcept
{
return (value_ < 0) ? -1 : (value_ ? 1 : 0);
}
/** Returns the number of drops */
// TODO: Move this to a new class, maybe with the old "TaggedFee" name
constexpr value_type
fee() const
{
return value_;
}
template <class Other>
constexpr double
decimalFromReference(ValueUnit<unit_type, Other> reference) const
{
return static_cast<double>(value_) / reference.value();
}
// `Usable` is checked to ensure that only values with
// known valid type tags can be converted to JSON. At the time
// of implementation, that includes all known tags, but more may
// be added in the future.
Json::Value
jsonClipped() const
requires Usable<ValueUnit>
{
if constexpr (std::is_integral_v<value_type>)
{
using jsontype = std::conditional_t<
std::is_signed_v<value_type>,
Json::Int,
Json::UInt>;
constexpr auto min = std::numeric_limits<jsontype>::min();
constexpr auto max = std::numeric_limits<jsontype>::max();
if (value_ < min)
return min;
if (value_ > max)
return max;
return static_cast<jsontype>(value_);
}
else
{
return value_;
}
}
/** Returns the underlying value. Code SHOULD NOT call this
function unless the type has been abstracted away,
e.g. in a templated function.
*/
constexpr value_type
value() const
{
return value_;
}
friend std::istream&
operator>>(std::istream& s, ValueUnit& val)
{
s >> val.value_;
return s;
}
};
// Output Values as just their numeric value.
template <class Char, class Traits, class UnitTag, class T>
std::basic_ostream<Char, Traits>&
operator<<(std::basic_ostream<Char, Traits>& os, ValueUnit<UnitTag, T> const& q)
{
return os << q.value();
}
template <class UnitTag, class T>
std::string
to_string(ValueUnit<UnitTag, T> const& amount)
{
return std::to_string(amount.value());
}
template <class Source>
concept muldivSource = Valid<Source> &&
std::is_convertible_v<typename Source::value_type, std::uint64_t>;
template <class Dest>
concept muldivDest = muldivSource<Dest> && // Dest is also a source
std::is_convertible_v<std::uint64_t, typename Dest::value_type> &&
sizeof(typename Dest::value_type) >= sizeof(std::uint64_t);
template <class Source2, class Source1>
concept muldivSources = muldivSource<Source1> && muldivSource<Source2> &&
std::is_same_v<typename Source1::unit_type, typename Source2::unit_type>;
template <class Dest, class Source1, class Source2>
concept muldivable = muldivSources<Source1, Source2> && muldivDest<Dest>;
// Source and Dest can be the same by default
template <class Dest, class Source1, class Source2>
concept muldivCommutable = muldivable<Dest, Source1, Source2> &&
!std::is_same_v<typename Source1::unit_type, typename Dest::unit_type>;
template <class T>
ValueUnit<unitlessTag, T>
scalar(T value)
{
return ValueUnit<unitlessTag, T>{value};
}
template <class Source1, class Source2, unit::muldivable<Source1, Source2> Dest>
std::optional<Dest>
mulDivU(Source1 value, Dest mul, Source2 div)
{
// values can never be negative in any context.
if (value.value() < 0 || mul.value() < 0 || div.value() < 0)
{
// split the asserts so if one hits, the user can tell which
// without a debugger.
XRPL_ASSERT(
value.value() >= 0, "ripple::unit::mulDivU : minimum value input");
XRPL_ASSERT(
mul.value() >= 0, "ripple::unit::mulDivU : minimum mul input");
XRPL_ASSERT(
div.value() > 0, "ripple::unit::mulDivU : minimum div input");
return std::nullopt;
}
using desttype = typename Dest::value_type;
constexpr auto max = std::numeric_limits<desttype>::max();
// Shortcuts, since these happen a lot in the real world
if (value == div)
return mul;
if (mul.value() == div.value())
{
if (value.value() > max)
return std::nullopt;
return Dest{static_cast<desttype>(value.value())};
}
using namespace boost::multiprecision;
uint128_t product;
product = multiply(
product,
static_cast<std::uint64_t>(value.value()),
static_cast<std::uint64_t>(mul.value()));
auto quotient = product / div.value();
if (quotient > max)
return std::nullopt;
return Dest{static_cast<desttype>(quotient)};
}
} // namespace unit
// Fee Levels
template <class T>
using FeeLevel = unit::ValueUnit<unit::feelevelTag, T>;
using FeeLevel64 = FeeLevel<std::uint64_t>;
using FeeLevelDouble = FeeLevel<double>;
// Basis points (Bips)
template <class T>
using Bips = unit::ValueUnit<unit::BipsTag, T>;
using Bips16 = Bips<std::uint16_t>;
using Bips32 = Bips<std::uint32_t>;
template <class T>
using TenthBips = unit::ValueUnit<unit::TenthBipsTag, T>;
using TenthBips16 = TenthBips<std::uint16_t>;
using TenthBips32 = TenthBips<std::uint32_t>;
template <class Source1, class Source2, unit::muldivable<Source1, Source2> Dest>
std::optional<Dest>
mulDiv(Source1 value, Dest mul, Source2 div)
{
return unit::mulDivU(value, mul, div);
}
template <
class Source1,
class Source2,
unit::muldivCommutable<Source1, Source2> Dest>
std::optional<Dest>
mulDiv(Dest value, Source1 mul, Source2 div)
{
// Multiplication is commutative
return unit::mulDivU(mul, value, div);
}
template <unit::muldivDest Dest>
std::optional<Dest>
mulDiv(std::uint64_t value, Dest mul, std::uint64_t div)
{
// Give the scalars a non-tag so the
// unit-handling version gets called.
return unit::mulDivU(unit::scalar(value), mul, unit::scalar(div));
}
template <unit::muldivDest Dest>
std::optional<Dest>
mulDiv(Dest value, std::uint64_t mul, std::uint64_t div)
{
// Multiplication is commutative
return mulDiv(mul, value, div);
}
template <unit::muldivSource Source1, unit::muldivSources<Source1> Source2>
std::optional<std::uint64_t>
mulDiv(Source1 value, std::uint64_t mul, Source2 div)
{
// Give the scalars a dimensionless unit so the
// unit-handling version gets called.
auto unitresult = unit::mulDivU(value, unit::scalar(mul), div);
if (!unitresult)
return std::nullopt;
return unitresult->value();
}
template <unit::muldivSource Source1, unit::muldivSources<Source1> Source2>
std::optional<std::uint64_t>
mulDiv(std::uint64_t value, Source1 mul, Source2 div)
{
// Multiplication is commutative
return mulDiv(mul, value, div);
}
template <unit::IntegralValue Dest, unit::CastableValue<Dest> Src>
constexpr Dest
safe_cast(Src s) noexcept
{
// Dest may not have an explicit value constructor
return Dest{safe_cast<typename Dest::value_type>(s.value())};
}
template <unit::IntegralValue Dest, unit::Integral Src>
constexpr Dest
safe_cast(Src s) noexcept
{
// Dest may not have an explicit value constructor
return Dest{safe_cast<typename Dest::value_type>(s)};
}
template <unit::IntegralValue Dest, unit::CastableValue<Dest> Src>
constexpr Dest
unsafe_cast(Src s) noexcept
{
// Dest may not have an explicit value constructor
return Dest{unsafe_cast<typename Dest::value_type>(s.value())};
}
template <unit::IntegralValue Dest, unit::Integral Src>
constexpr Dest
unsafe_cast(Src s) noexcept
{
// Dest may not have an explicit value constructor
return Dest{unsafe_cast<typename Dest::value_type>(s)};
}
} // namespace ripple
#endif // PROTOCOL_UNITS_H_INCLUDED

View File

@@ -24,7 +24,7 @@
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/FeeUnits.h>
#include <xrpl/protocol/Units.h>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/operators.hpp>
@@ -42,7 +42,7 @@ class XRPAmount : private boost::totally_ordered<XRPAmount>,
private boost::additive<XRPAmount, std::int64_t>
{
public:
using unit_type = feeunit::dropTag;
using unit_type = unit::dropTag;
using value_type = std::int64_t;
private:

View File

@@ -32,9 +32,13 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FIX (PriceOracleOrder, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (IncludeKeyletFields, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(DynamicMPT, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (DelegateV1_1, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (PriceOracleOrder, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (AMMClawbackRounding, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (AMMClawbackRounding, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo)

View File

@@ -120,6 +120,7 @@ LEDGER_ENTRY(ltNFTOKEN_PAGE, 0x0050, NFTokenPage, nft_page, ({
// All fields are soeREQUIRED because there is always a SignerEntries.
// If there are no SignerEntries the node is deleted.
LEDGER_ENTRY(ltSIGNER_LIST, 0x0053, SignerList, signer_list, ({
{sfOwner, soeOPTIONAL},
{sfOwnerNode, soeREQUIRED},
{sfSignerQuorum, soeREQUIRED},
{sfSignerEntries, soeREQUIRED},
@@ -188,7 +189,7 @@ LEDGER_ENTRY(ltDIR_NODE, 0x0064, DirectoryNode, directory, ({
{sfNFTokenID, soeOPTIONAL},
{sfPreviousTxnID, soeOPTIONAL},
{sfPreviousTxnLgrSeq, soeOPTIONAL},
{sfDomainID, soeOPTIONAL}
{sfDomainID, soeOPTIONAL} // order book directories
}))
/** The ledger object which lists details about amendments on the network.
@@ -343,6 +344,7 @@ LEDGER_ENTRY(ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID, 0x0074, XChainOwnedCreateAc
*/
LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
{sfAccount, soeREQUIRED},
{sfSequence, soeOPTIONAL},
{sfDestination, soeREQUIRED},
{sfAmount, soeREQUIRED},
{sfCondition, soeOPTIONAL},
@@ -365,6 +367,7 @@ LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
LEDGER_ENTRY(ltPAYCHAN, 0x0078, PayChannel, payment_channel, ({
{sfAccount, soeREQUIRED},
{sfDestination, soeREQUIRED},
{sfSequence, soeOPTIONAL},
{sfAmount, soeREQUIRED},
{sfBalance, soeREQUIRED},
{sfPublicKey, soeREQUIRED},
@@ -412,6 +415,7 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfPreviousTxnID, soeREQUIRED},
{sfPreviousTxnLgrSeq, soeREQUIRED},
{sfDomainID, soeOPTIONAL},
{sfMutableFlags, soeDEFAULT},
}))
/** A ledger object which tracks MPToken
@@ -432,6 +436,7 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
*/
LEDGER_ENTRY(ltORACLE, 0x0080, Oracle, oracle, ({
{sfOwner, soeREQUIRED},
{sfOracleDocumentID, soeOPTIONAL},
{sfProvider, soeREQUIRED},
{sfPriceDataSeries, soeREQUIRED},
{sfAssetClass, soeREQUIRED},
@@ -499,6 +504,7 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({
{sfLossUnrealized, soeREQUIRED},
{sfShareMPTID, soeREQUIRED},
{sfWithdrawalPolicy, soeREQUIRED},
{sfScale, soeDEFAULT},
// no SharesTotal ever (use MPTIssuance.sfOutstandingAmount)
// no PermissionedDomainID ever (use MPTIssuance.sfDomainID)
}))

View File

@@ -114,6 +114,7 @@ TYPED_SFIELD(sfVoteWeight, UINT32, 48)
TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50)
TYPED_SFIELD(sfOracleDocumentID, UINT32, 51)
TYPED_SFIELD(sfPermissionValue, UINT32, 52)
TYPED_SFIELD(sfMutableFlags, UINT32, 53)
// 64-bit integers (common)
TYPED_SFIELD(sfIndexNext, UINT64, 1)
@@ -173,7 +174,8 @@ TYPED_SFIELD(sfNFTokenID, UINT256, 10)
TYPED_SFIELD(sfEmitParentTxnID, UINT256, 11)
TYPED_SFIELD(sfEmitNonce, UINT256, 12)
TYPED_SFIELD(sfEmitHookHash, UINT256, 13)
TYPED_SFIELD(sfAMMID, UINT256, 14)
TYPED_SFIELD(sfAMMID, UINT256, 14,
SField::sMD_PseudoAccount | SField::sMD_Default)
// 256-bit (uncommon)
TYPED_SFIELD(sfBookDirectory, UINT256, 16)
@@ -195,7 +197,8 @@ TYPED_SFIELD(sfHookHash, UINT256, 31)
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
TYPED_SFIELD(sfDomainID, UINT256, 34)
TYPED_SFIELD(sfVaultID, UINT256, 35)
TYPED_SFIELD(sfVaultID, UINT256, 35,
SField::sMD_PseudoAccount | SField::sMD_Default)
TYPED_SFIELD(sfParentBatchID, UINT256, 36)
// number (common)

View File

@@ -22,14 +22,32 @@
#endif
/**
* TRANSACTION(tag, value, name, delegatable, fields)
* TRANSACTION(tag, value, name, delegatable, amendments, privileges, fields)
*
* To ease maintenance, you may replace any unneeded values with "..."
* e.g. #define TRANSACTION(tag, value, name, ...)
*
* You must define a transactor class in the `ripple` namespace named `name`,
* and include its header in `src/xrpld/app/tx/detail/applySteps.cpp`.
* and include its header alongside the TRANSACTOR definition using this
* format:
* #if TRANSACTION_INCLUDE
* # include <xrpld/app/tx/detail/HEADER.h>
* #endif
*
* The `privileges` parameter of the TRANSACTION macro is a bitfield
* defining which operations the transaction can perform.
* The values are defined and used in InvariantCheck.cpp
*/
/** This transaction type executes a payment. */
TRANSACTION(ttPAYMENT, 0, Payment, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/Payment.h>
#endif
TRANSACTION(ttPAYMENT, 0, Payment,
Delegation::delegatable,
uint256{},
createAcct,
({
{sfDestination, soeREQUIRED},
{sfAmount, soeREQUIRED, soeMPTSupported},
{sfSendMax, soeOPTIONAL, soeMPTSupported},
@@ -42,7 +60,14 @@ TRANSACTION(ttPAYMENT, 0, Payment, Delegation::delegatable, ({
}))
/** This transaction type creates an escrow object. */
TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/Escrow.h>
#endif
TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate,
Delegation::delegatable,
uint256{},
noPriv,
({
{sfDestination, soeREQUIRED},
{sfAmount, soeREQUIRED, soeMPTSupported},
{sfCondition, soeOPTIONAL},
@@ -52,7 +77,11 @@ TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegatable, ({
}))
/** This transaction type completes an existing escrow. */
TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegatable, ({
TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish,
Delegation::delegatable,
uint256{},
noPriv,
({
{sfOwner, soeREQUIRED},
{sfOfferSequence, soeREQUIRED},
{sfFulfillment, soeOPTIONAL},
@@ -62,7 +91,14 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegatable, ({
/** This transaction type adjusts various account settings. */
TRANSACTION(ttACCOUNT_SET, 3, AccountSet, Delegation::notDelegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/SetAccount.h>
#endif
TRANSACTION(ttACCOUNT_SET, 3, AccountSet,
Delegation::notDelegatable,
uint256{},
noPriv,
({
{sfEmailHash, soeOPTIONAL},
{sfWalletLocator, soeOPTIONAL},
{sfWalletSize, soeOPTIONAL},
@@ -76,20 +112,41 @@ TRANSACTION(ttACCOUNT_SET, 3, AccountSet, Delegation::notDelegatable, ({
}))
/** This transaction type cancels an existing escrow. */
TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/Escrow.h>
#endif
TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel,
Delegation::delegatable,
uint256{},
noPriv,
({
{sfOwner, soeREQUIRED},
{sfOfferSequence, soeREQUIRED},
}))
/** This transaction type sets or clears an account's "regular key". */
TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, Delegation::notDelegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/SetRegularKey.h>
#endif
TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey,
Delegation::notDelegatable,
uint256{},
noPriv,
({
{sfRegularKey, soeOPTIONAL},
}))
// 6 deprecated
/** This transaction type creates an offer to trade one asset for another. */
TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/CreateOffer.h>
#endif
TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
Delegation::delegatable,
uint256{},
noPriv,
({
{sfTakerPays, soeREQUIRED},
{sfTakerGets, soeREQUIRED},
{sfExpiration, soeOPTIONAL},
@@ -98,14 +155,28 @@ TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, Delegation::delegatable, ({
}))
/** This transaction type cancels existing offers to trade one asset for another. */
TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/CancelOffer.h>
#endif
TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel,
Delegation::delegatable,
uint256{},
noPriv,
({
{sfOfferSequence, soeREQUIRED},
}))
// 9 deprecated
/** This transaction type creates a new set of tickets. */
TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/CreateTicket.h>
#endif
TRANSACTION(ttTICKET_CREATE, 10, TicketCreate,
Delegation::delegatable,
featureTicketBatch,
noPriv,
({
{sfTicketCount, soeREQUIRED},
}))
@@ -114,13 +185,27 @@ TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, Delegation::delegatable, ({
/** This transaction type modifies the signer list associated with an account. */
// The SignerEntries are optional because a SignerList is deleted by
// setting the SignerQuorum to zero and omitting SignerEntries.
TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, Delegation::notDelegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/SetSignerList.h>
#endif
TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet,
Delegation::notDelegatable,
uint256{},
noPriv,
({
{sfSignerQuorum, soeREQUIRED},
{sfSignerEntries, soeOPTIONAL},
}))
/** This transaction type creates a new unidirectional XRP payment channel. */
TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/PayChan.h>
#endif
TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate,
Delegation::delegatable,
uint256{},
noPriv,
({
{sfDestination, soeREQUIRED},
{sfAmount, soeREQUIRED},
{sfSettleDelay, soeREQUIRED},
@@ -130,14 +215,22 @@ TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, Delegation::delegatable,
}))
/** This transaction type funds an existing unidirectional XRP payment channel. */
TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, Delegation::delegatable, ({
TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund,
Delegation::delegatable,
uint256{},
noPriv,
({
{sfChannel, soeREQUIRED},
{sfAmount, soeREQUIRED},
{sfExpiration, soeOPTIONAL},
}))
/** This transaction type submits a claim against an existing unidirectional payment channel. */
TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, Delegation::delegatable, ({
TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim,
Delegation::delegatable,
uint256{},
noPriv,
({
{sfChannel, soeREQUIRED},
{sfAmount, soeOPTIONAL},
{sfBalance, soeOPTIONAL},
@@ -147,7 +240,14 @@ TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, Delegation::delegatable, (
}))
/** This transaction type creates a new check. */
TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/CreateCheck.h>
#endif
TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
Delegation::delegatable,
featureChecks,
noPriv,
({
{sfDestination, soeREQUIRED},
{sfSendMax, soeREQUIRED},
{sfExpiration, soeOPTIONAL},
@@ -156,19 +256,40 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, Delegation::delegatable, ({
}))
/** This transaction type cashes an existing check. */
TRANSACTION(ttCHECK_CASH, 17, CheckCash, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/CashCheck.h>
#endif
TRANSACTION(ttCHECK_CASH, 17, CheckCash,
Delegation::delegatable,
featureChecks,
noPriv,
({
{sfCheckID, soeREQUIRED},
{sfAmount, soeOPTIONAL},
{sfDeliverMin, soeOPTIONAL},
}))
/** This transaction type cancels an existing check. */
TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/CancelCheck.h>
#endif
TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel,
Delegation::delegatable,
featureChecks,
noPriv,
({
{sfCheckID, soeREQUIRED},
}))
/** This transaction type grants or revokes authorization to transfer funds. */
TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/DepositPreauth.h>
#endif
TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth,
Delegation::delegatable,
featureDepositPreauth,
noPriv,
({
{sfAuthorize, soeOPTIONAL},
{sfUnauthorize, soeOPTIONAL},
{sfAuthorizeCredentials, soeOPTIONAL},
@@ -176,14 +297,28 @@ TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, Delegation::delegatable, ({
}))
/** This transaction type modifies a trustline between two accounts. */
TRANSACTION(ttTRUST_SET, 20, TrustSet, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/SetTrust.h>
#endif
TRANSACTION(ttTRUST_SET, 20, TrustSet,
Delegation::delegatable,
uint256{},
noPriv,
({
{sfLimitAmount, soeOPTIONAL},
{sfQualityIn, soeOPTIONAL},
{sfQualityOut, soeOPTIONAL},
}))
/** This transaction type deletes an existing account. */
TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, Delegation::notDelegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/DeleteAccount.h>
#endif
TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
Delegation::notDelegatable,
uint256{},
mustDeleteAcct,
({
{sfDestination, soeREQUIRED},
{sfDestinationTag, soeOPTIONAL},
{sfCredentialIDs, soeOPTIONAL},
@@ -192,7 +327,14 @@ TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, Delegation::notDelegatable, ({
// 22 reserved
/** This transaction mints a new NFT. */
TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/NFTokenMint.h>
#endif
TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
Delegation::delegatable,
featureNonFungibleTokensV1,
changeNFTCounts,
({
{sfNFTokenTaxon, soeREQUIRED},
{sfTransferFee, soeOPTIONAL},
{sfIssuer, soeOPTIONAL},
@@ -203,13 +345,27 @@ TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, Delegation::delegatable, ({
}))
/** This transaction burns (i.e. destroys) an existing NFT. */
TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/NFTokenBurn.h>
#endif
TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn,
Delegation::delegatable,
featureNonFungibleTokensV1,
changeNFTCounts,
({
{sfNFTokenID, soeREQUIRED},
{sfOwner, soeOPTIONAL},
}))
/** This transaction creates a new offer to buy or sell an NFT. */
TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/NFTokenCreateOffer.h>
#endif
TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
Delegation::delegatable,
featureNonFungibleTokensV1,
noPriv,
({
{sfNFTokenID, soeREQUIRED},
{sfAmount, soeREQUIRED},
{sfDestination, soeOPTIONAL},
@@ -218,25 +374,53 @@ TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, Delegation::delegata
}))
/** This transaction cancels an existing offer to buy or sell an existing NFT. */
TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/NFTokenCancelOffer.h>
#endif
TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer,
Delegation::delegatable,
featureNonFungibleTokensV1,
noPriv,
({
{sfNFTokenOffers, soeREQUIRED},
}))
/** This transaction accepts an existing offer to buy or sell an existing NFT. */
TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/NFTokenAcceptOffer.h>
#endif
TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
Delegation::delegatable,
featureNonFungibleTokensV1,
noPriv,
({
{sfNFTokenBuyOffer, soeOPTIONAL},
{sfNFTokenSellOffer, soeOPTIONAL},
{sfNFTokenBrokerFee, soeOPTIONAL},
}))
/** This transaction claws back issued tokens. */
TRANSACTION(ttCLAWBACK, 30, Clawback, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/Clawback.h>
#endif
TRANSACTION(ttCLAWBACK, 30, Clawback,
Delegation::delegatable,
featureClawback,
noPriv,
({
{sfAmount, soeREQUIRED, soeMPTSupported},
{sfHolder, soeOPTIONAL},
}))
/** This transaction claws back tokens from an AMM pool. */
TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/AMMClawback.h>
#endif
TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
Delegation::delegatable,
featureAMMClawback,
mayDeleteAcct | overrideFreeze,
({
{sfHolder, soeREQUIRED},
{sfAsset, soeREQUIRED},
{sfAsset2, soeREQUIRED},
@@ -244,14 +428,28 @@ TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, Delegation::delegatable, ({
}))
/** This transaction type creates an AMM instance */
TRANSACTION(ttAMM_CREATE, 35, AMMCreate, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/AMMCreate.h>
#endif
TRANSACTION(ttAMM_CREATE, 35, AMMCreate,
Delegation::delegatable,
featureAMM,
createPseudoAcct,
({
{sfAmount, soeREQUIRED},
{sfAmount2, soeREQUIRED},
{sfTradingFee, soeREQUIRED},
}))
/** This transaction type deposits into an AMM instance */
TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/AMMDeposit.h>
#endif
TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
Delegation::delegatable,
featureAMM,
noPriv,
({
{sfAsset, soeREQUIRED},
{sfAsset2, soeREQUIRED},
{sfAmount, soeOPTIONAL},
@@ -262,7 +460,14 @@ TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, Delegation::delegatable, ({
}))
/** This transaction type withdraws from an AMM instance */
TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/AMMWithdraw.h>
#endif
TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw,
Delegation::delegatable,
featureAMM,
mayDeleteAcct,
({
{sfAsset, soeREQUIRED},
{sfAsset2, soeREQUIRED},
{sfAmount, soeOPTIONAL},
@@ -272,14 +477,28 @@ TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, Delegation::delegatable, ({
}))
/** This transaction type votes for the trading fee */
TRANSACTION(ttAMM_VOTE, 38, AMMVote, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/AMMVote.h>
#endif
TRANSACTION(ttAMM_VOTE, 38, AMMVote,
Delegation::delegatable,
featureAMM,
noPriv,
({
{sfAsset, soeREQUIRED},
{sfAsset2, soeREQUIRED},
{sfTradingFee, soeREQUIRED},
}))
/** This transaction type bids for the auction slot */
TRANSACTION(ttAMM_BID, 39, AMMBid, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/AMMBid.h>
#endif
TRANSACTION(ttAMM_BID, 39, AMMBid,
Delegation::delegatable,
featureAMM,
noPriv,
({
{sfAsset, soeREQUIRED},
{sfAsset2, soeREQUIRED},
{sfBidMin, soeOPTIONAL},
@@ -288,20 +507,38 @@ TRANSACTION(ttAMM_BID, 39, AMMBid, Delegation::delegatable, ({
}))
/** This transaction type deletes AMM in the empty state */
TRANSACTION(ttAMM_DELETE, 40, AMMDelete, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/AMMDelete.h>
#endif
TRANSACTION(ttAMM_DELETE, 40, AMMDelete,
Delegation::delegatable,
featureAMM,
mustDeleteAcct,
({
{sfAsset, soeREQUIRED},
{sfAsset2, soeREQUIRED},
}))
/** This transactions creates a crosschain sequence number */
TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/XChainBridge.h>
#endif
TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID,
Delegation::delegatable,
featureXChainBridge,
noPriv,
({
{sfXChainBridge, soeREQUIRED},
{sfSignatureReward, soeREQUIRED},
{sfOtherChainSource, soeREQUIRED},
}))
/** This transactions initiates a crosschain transaction */
TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, Delegation::delegatable, ({
TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit,
Delegation::delegatable,
featureXChainBridge,
noPriv,
({
{sfXChainBridge, soeREQUIRED},
{sfXChainClaimID, soeREQUIRED},
{sfAmount, soeREQUIRED},
@@ -309,7 +546,11 @@ TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, Delegation::delegatable, ({
}))
/** This transaction completes a crosschain transaction */
TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, Delegation::delegatable, ({
TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim,
Delegation::delegatable,
featureXChainBridge,
noPriv,
({
{sfXChainBridge, soeREQUIRED},
{sfXChainClaimID, soeREQUIRED},
{sfDestination, soeREQUIRED},
@@ -318,7 +559,11 @@ TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, Delegation::delegatable, ({
}))
/** This transaction initiates a crosschain account create transaction */
TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, Delegation::delegatable, ({
TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit,
Delegation::delegatable,
featureXChainBridge,
noPriv,
({
{sfXChainBridge, soeREQUIRED},
{sfDestination, soeREQUIRED},
{sfAmount, soeREQUIRED},
@@ -326,7 +571,11 @@ TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, Deleg
}))
/** This transaction adds an attestation to a claim */
TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, Delegation::delegatable, ({
TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation,
Delegation::delegatable,
featureXChainBridge,
createAcct,
({
{sfXChainBridge, soeREQUIRED},
{sfAttestationSignerAccount, soeREQUIRED},
@@ -342,7 +591,12 @@ TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, Deleg
}))
/** This transaction adds an attestation to an account */
TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateAttestation, Delegation::delegatable, ({
TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46,
XChainAddAccountCreateAttestation,
Delegation::delegatable,
featureXChainBridge,
createAcct,
({
{sfXChainBridge, soeREQUIRED},
{sfAttestationSignerAccount, soeREQUIRED},
@@ -359,31 +613,57 @@ TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateA
}))
/** This transaction modifies a sidechain */
TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge, Delegation::delegatable, ({
TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge,
Delegation::delegatable,
featureXChainBridge,
noPriv,
({
{sfXChainBridge, soeREQUIRED},
{sfSignatureReward, soeOPTIONAL},
{sfMinAccountCreateAmount, soeOPTIONAL},
}))
/** This transactions creates a sidechain */
TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge, Delegation::delegatable, ({
TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge,
Delegation::delegatable,
featureXChainBridge,
noPriv,
({
{sfXChainBridge, soeREQUIRED},
{sfSignatureReward, soeREQUIRED},
{sfMinAccountCreateAmount, soeOPTIONAL},
}))
/** This transaction type creates or updates a DID */
TRANSACTION(ttDID_SET, 49, DIDSet, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/DID.h>
#endif
TRANSACTION(ttDID_SET, 49, DIDSet,
Delegation::delegatable,
featureDID,
noPriv,
({
{sfDIDDocument, soeOPTIONAL},
{sfURI, soeOPTIONAL},
{sfData, soeOPTIONAL},
}))
/** This transaction type deletes a DID */
TRANSACTION(ttDID_DELETE, 50, DIDDelete, Delegation::delegatable, ({}))
TRANSACTION(ttDID_DELETE, 50, DIDDelete,
Delegation::delegatable,
featureDID,
noPriv,
({}))
/** This transaction type creates an Oracle instance */
TRANSACTION(ttORACLE_SET, 51, OracleSet, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/SetOracle.h>
#endif
TRANSACTION(ttORACLE_SET, 51, OracleSet,
Delegation::delegatable,
featurePriceOracle,
noPriv,
({
{sfOracleDocumentID, soeREQUIRED},
{sfProvider, soeOPTIONAL},
{sfURI, soeOPTIONAL},
@@ -393,45 +673,98 @@ TRANSACTION(ttORACLE_SET, 51, OracleSet, Delegation::delegatable, ({
}))
/** This transaction type deletes an Oracle instance */
TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/DeleteOracle.h>
#endif
TRANSACTION(ttORACLE_DELETE, 52, OracleDelete,
Delegation::delegatable,
featurePriceOracle,
noPriv,
({
{sfOracleDocumentID, soeREQUIRED},
}))
/** This transaction type fixes a problem in the ledger state */
TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/LedgerStateFix.h>
#endif
TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix,
Delegation::delegatable,
fixNFTokenPageLinks,
noPriv,
({
{sfLedgerFixType, soeREQUIRED},
{sfOwner, soeOPTIONAL},
}))
/** This transaction type creates a MPTokensIssuance instance */
TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/MPTokenIssuanceCreate.h>
#endif
TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate,
Delegation::delegatable,
featureMPTokensV1,
createMPTIssuance,
({
{sfAssetScale, soeOPTIONAL},
{sfTransferFee, soeOPTIONAL},
{sfMaximumAmount, soeOPTIONAL},
{sfMPTokenMetadata, soeOPTIONAL},
{sfDomainID, soeOPTIONAL},
{sfMutableFlags, soeOPTIONAL},
}))
/** This transaction type destroys a MPTokensIssuance instance */
TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/MPTokenIssuanceDestroy.h>
#endif
TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy,
Delegation::delegatable,
featureMPTokensV1,
destroyMPTIssuance,
({
{sfMPTokenIssuanceID, soeREQUIRED},
}))
/** This transaction type sets flags on a MPTokensIssuance or MPToken instance */
TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/MPTokenIssuanceSet.h>
#endif
TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
Delegation::delegatable,
featureMPTokensV1,
noPriv,
({
{sfMPTokenIssuanceID, soeREQUIRED},
{sfHolder, soeOPTIONAL},
{sfDomainID, soeOPTIONAL},
{sfMPTokenMetadata, soeOPTIONAL},
{sfTransferFee, soeOPTIONAL},
{sfMutableFlags, soeOPTIONAL},
}))
/** This transaction type authorizes a MPToken instance */
TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/MPTokenAuthorize.h>
#endif
TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize,
Delegation::delegatable,
featureMPTokensV1,
mustAuthorizeMPT,
({
{sfMPTokenIssuanceID, soeREQUIRED},
{sfHolder, soeOPTIONAL},
}))
/** This transaction type create an Credential instance */
TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/Credentials.h>
#endif
TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate,
Delegation::delegatable,
featureCredentials,
noPriv,
({
{sfSubject, soeREQUIRED},
{sfCredentialType, soeREQUIRED},
{sfExpiration, soeOPTIONAL},
@@ -439,54 +772,105 @@ TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, Delegation::delegatable,
}))
/** This transaction type accept an Credential object */
TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, Delegation::delegatable, ({
TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept,
Delegation::delegatable,
featureCredentials,
noPriv,
({
{sfIssuer, soeREQUIRED},
{sfCredentialType, soeREQUIRED},
}))
/** This transaction type delete an Credential object */
TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, Delegation::delegatable, ({
TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete,
Delegation::delegatable,
featureCredentials,
noPriv,
({
{sfSubject, soeOPTIONAL},
{sfIssuer, soeOPTIONAL},
{sfCredentialType, soeREQUIRED},
}))
/** This transaction type modify a NFToken */
TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/NFTokenModify.h>
#endif
TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify,
Delegation::delegatable,
featureDynamicNFT,
noPriv,
({
{sfNFTokenID, soeREQUIRED},
{sfOwner, soeOPTIONAL},
{sfURI, soeOPTIONAL},
}))
/** This transaction type creates or modifies a Permissioned Domain */
TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/PermissionedDomainSet.h>
#endif
TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet,
Delegation::delegatable,
featurePermissionedDomains,
noPriv,
({
{sfDomainID, soeOPTIONAL},
{sfAcceptedCredentials, soeREQUIRED},
}))
/** This transaction type deletes a Permissioned Domain */
TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/PermissionedDomainDelete.h>
#endif
TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete,
Delegation::delegatable,
featurePermissionedDomains,
noPriv,
({
{sfDomainID, soeREQUIRED},
}))
/** This transaction type delegates authorized account specified permissions */
TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, Delegation::notDelegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/DelegateSet.h>
#endif
TRANSACTION(ttDELEGATE_SET, 64, DelegateSet,
Delegation::notDelegatable,
featurePermissionDelegation,
noPriv,
({
{sfAuthorize, soeREQUIRED},
{sfPermissions, soeREQUIRED},
}))
/** This transaction creates a single asset vault. */
TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/VaultCreate.h>
#endif
TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
Delegation::delegatable,
featureSingleAssetVault,
createPseudoAcct | createMPTIssuance,
({
{sfAsset, soeREQUIRED, soeMPTSupported},
{sfAssetsMaximum, soeOPTIONAL},
{sfMPTokenMetadata, soeOPTIONAL},
{sfDomainID, soeOPTIONAL},
{sfWithdrawalPolicy, soeOPTIONAL},
{sfData, soeOPTIONAL},
{sfScale, soeOPTIONAL},
}))
/** This transaction updates a single asset vault. */
TRANSACTION(ttVAULT_SET, 66, VaultSet, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/VaultSet.h>
#endif
TRANSACTION(ttVAULT_SET, 66, VaultSet,
Delegation::delegatable,
featureSingleAssetVault,
noPriv,
({
{sfVaultID, soeREQUIRED},
{sfAssetsMaximum, soeOPTIONAL},
{sfDomainID, soeOPTIONAL},
@@ -494,18 +878,39 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet, Delegation::delegatable, ({
}))
/** This transaction deletes a single asset vault. */
TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/VaultDelete.h>
#endif
TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
Delegation::delegatable,
featureSingleAssetVault,
mustDeleteAcct | destroyMPTIssuance,
({
{sfVaultID, soeREQUIRED},
}))
/** This transaction trades assets for shares with a vault. */
TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/VaultDeposit.h>
#endif
TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit,
Delegation::delegatable,
featureSingleAssetVault,
mayAuthorizeMPT,
({
{sfVaultID, soeREQUIRED},
{sfAmount, soeREQUIRED, soeMPTSupported},
}))
/** This transaction trades shares for assets with a vault. */
TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/VaultWithdraw.h>
#endif
TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
Delegation::delegatable,
featureSingleAssetVault,
mayDeleteMPT,
({
{sfVaultID, soeREQUIRED},
{sfAmount, soeREQUIRED, soeMPTSupported},
{sfDestination, soeOPTIONAL},
@@ -513,14 +918,28 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, Delegation::delegatable, ({
}))
/** This transaction claws back tokens from a vault. */
TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, Delegation::delegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/VaultClawback.h>
#endif
TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
Delegation::delegatable,
featureSingleAssetVault,
mayDeleteMPT,
({
{sfVaultID, soeREQUIRED},
{sfHolder, soeREQUIRED},
{sfAmount, soeOPTIONAL, soeMPTSupported},
}))
/** This transaction type batches together transactions. */
TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/Batch.h>
#endif
TRANSACTION(ttBATCH, 71, Batch,
Delegation::notDelegatable,
featureBatch,
noPriv,
({
{sfRawTransactions, soeREQUIRED},
{sfBatchSigners, soeOPTIONAL},
}))
@@ -529,7 +948,14 @@ TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegatable, ({
For details, see: https://xrpl.org/amendments.html
*/
TRANSACTION(ttAMENDMENT, 100, EnableAmendment, Delegation::notDelegatable, ({
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/Change.h>
#endif
TRANSACTION(ttAMENDMENT, 100, EnableAmendment,
Delegation::notDelegatable,
uint256{},
noPriv,
({
{sfLedgerSequence, soeREQUIRED},
{sfAmendment, soeREQUIRED},
}))
@@ -537,7 +963,11 @@ TRANSACTION(ttAMENDMENT, 100, EnableAmendment, Delegation::notDelegatable, ({
/** This system-generated transaction type is used to update the network's fee settings.
For details, see: https://xrpl.org/fee-voting.html
*/
TRANSACTION(ttFEE, 101, SetFee, Delegation::notDelegatable, ({
TRANSACTION(ttFEE, 101, SetFee,
Delegation::notDelegatable,
uint256{},
noPriv,
({
{sfLedgerSequence, soeOPTIONAL},
// Old version uses raw numbers
{sfBaseFee, soeOPTIONAL},
@@ -554,7 +984,11 @@ TRANSACTION(ttFEE, 101, SetFee, Delegation::notDelegatable, ({
For details, see: https://xrpl.org/negative-unl.html
*/
TRANSACTION(ttUNL_MODIFY, 102, UNLModify, Delegation::notDelegatable, ({
TRANSACTION(ttUNL_MODIFY, 102, UNLModify,
Delegation::notDelegatable,
uint256{},
noPriv,
({
{sfUNLModifyDisabling, soeREQUIRED},
{sfLedgerSequence, soeREQUIRED},
{sfUNLModifyValidator, soeREQUIRED},

View File

@@ -710,7 +710,7 @@ JSS(write_load); // out: GetCounts
#pragma push_macro("TRANSACTION")
#undef TRANSACTION
#define TRANSACTION(tag, value, name, delegatable, fields) JSS(name);
#define TRANSACTION(tag, value, name, ...) JSS(name);
#include <xrpl/protocol/detail/transactions.macro>
@@ -722,11 +722,11 @@ JSS(write_load); // out: GetCounts
#pragma push_macro("LEDGER_ENTRY_DUPLICATE")
#undef LEDGER_ENTRY_DUPLICATE
#define LEDGER_ENTRY(tag, value, name, rpcName, fields) \
JSS(name); \
#define LEDGER_ENTRY(tag, value, name, rpcName, ...) \
JSS(name); \
JSS(rpcName);
#define LEDGER_ENTRY_DUPLICATE(tag, value, name, rpcName, fields) JSS(rpcName);
#define LEDGER_ENTRY_DUPLICATE(tag, value, name, rpcName, ...) JSS(rpcName);
#include <xrpl/protocol/detail/ledger_entries.macro>

View File

@@ -47,7 +47,6 @@ protected:
Port const& port_;
Handler& handler_;
endpoint_type remote_address_;
beast::WrappedSink sink_;
beast::Journal const j_;
boost::asio::executor_work_guard<boost::asio::executor> work_;
@@ -84,13 +83,13 @@ BasePeer<Handler, Impl>::BasePeer(
: port_(port)
, handler_(handler)
, remote_address_(remote_address)
, sink_(
journal.sink(),
[] {
static std::atomic<unsigned> id{0};
return "##" + std::to_string(++id) + " ";
}())
, j_(sink_)
, j_(journal,
log::attributes(log::attr(
"PeerID",
[] {
static std::atomic<unsigned> id{0};
return "##" + std::to_string(++id) + " ";
}())))
, work_(boost::asio::make_work_guard(executor))
, strand_(boost::asio::make_strand(executor))
{

View File

@@ -113,14 +113,14 @@ Logs::File::close()
}
void
Logs::File::write(char const* text)
Logs::File::write(std::string_view text)
{
if (m_stream != nullptr)
(*m_stream) << text;
}
void
Logs::File::writeln(char const* text)
Logs::File::writeln(std::string_view text)
{
if (m_stream != nullptr)
{
@@ -196,11 +196,15 @@ Logs::write(
bool console)
{
std::string s;
format(s, text, level, partition);
std::string_view result = text;
if (!beast::Journal::isStructuredJournalEnabled())
{
format(s, text, level, partition);
result = text;
}
std::lock_guard lock(mutex_);
file_.writeln(s);
if (!silent_)
std::cerr << s << '\n';
file_.writeln(result);
// VFALCO TODO Fix console output
// if (console)
// out_.write_console(s);

View File

@@ -19,12 +19,102 @@
#include <xrpl/beast/utility/Journal.h>
#include <chrono>
#include <ios>
#include <ostream>
#include <ranges>
#include <string>
#include <thread>
namespace beast {
namespace {
// Fast timestamp to ISO string conversion
// Returns string like "2024-01-15T10:30:45.123Z"
std::string_view
fastTimestampToString(std::int64_t milliseconds_since_epoch)
{
thread_local char buffer[64]; // "2024-01-15T10:30:45.123Z"
// Precomputed lookup table for 2-digit numbers 00-99
static constexpr char digits[200] = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6',
'0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3',
'1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0',
'2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7',
'2', '8', '2', '9', '3', '0', '3', '1', '3', '2', '3', '3', '3', '4',
'3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1',
'4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8',
'4', '9', '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5',
'5', '6', '5', '7', '5', '8', '5', '9', '6', '0', '6', '1', '6', '2',
'6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6',
'7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3',
'8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '9', '0',
'9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7',
'9', '8', '9', '9'};
constexpr std::int64_t UNIX_EPOCH_DAYS =
719468; // Days from year 0 to 1970-01-01
std::int64_t seconds = milliseconds_since_epoch / 1000;
int ms = milliseconds_since_epoch % 1000;
std::int64_t days = seconds / 86400 + UNIX_EPOCH_DAYS;
int sec_of_day = seconds % 86400;
// Calculate year, month, day from days using Gregorian calendar algorithm
int era = (days >= 0 ? days : days - 146096) / 146097;
int doe = days - era * 146097;
int yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
int year = yoe + era * 400;
int doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
int mp = (5 * doy + 2) / 153;
int day = doy - (153 * mp + 2) / 5 + 1;
int month = mp + (mp < 10 ? 3 : -9);
year += (month <= 2);
// Calculate hour, minute, second
int hour = sec_of_day / 3600;
int min = (sec_of_day % 3600) / 60;
int sec = sec_of_day % 60;
// Format: "2024-01-15T10:30:45.123Z"
buffer[0] = '0' + year / 1000;
buffer[1] = '0' + (year / 100) % 10;
buffer[2] = '0' + (year / 10) % 10;
buffer[3] = '0' + year % 10;
buffer[4] = '-';
buffer[5] = digits[month * 2];
buffer[6] = digits[month * 2 + 1];
buffer[7] = '-';
buffer[8] = digits[day * 2];
buffer[9] = digits[day * 2 + 1];
buffer[10] = 'T';
buffer[11] = digits[hour * 2];
buffer[12] = digits[hour * 2 + 1];
buffer[13] = ':';
buffer[14] = digits[min * 2];
buffer[15] = digits[min * 2 + 1];
buffer[16] = ':';
buffer[17] = digits[sec * 2];
buffer[18] = digits[sec * 2 + 1];
buffer[19] = '.';
buffer[20] = '0' + ms / 100;
buffer[21] = '0' + (ms / 10) % 10;
buffer[22] = '0' + ms % 10;
buffer[23] = 'Z';
return {buffer, 24};
}
} // anonymous namespace
std::string Journal::globalLogAttributes_;
std::shared_mutex Journal::globalLogAttributesMutex_;
bool Journal::jsonLogsEnabled_ = false;
thread_local Journal::JsonLogContext Journal::currentJsonLogContext_{};
//------------------------------------------------------------------------------
// A Sink that does nothing.
@@ -87,6 +177,186 @@ Journal::getNullSink()
//------------------------------------------------------------------------------
std::string_view
severities::to_string(Severity severity)
{
using namespace std::string_view_literals;
switch (severity)
{
case kDisabled:
return "disabled"sv;
case kTrace:
return "trace"sv;
case kDebug:
return "debug"sv;
case kInfo:
return "info"sv;
case kWarning:
return "warning"sv;
case kError:
return "error"sv;
case kFatal:
return "fatal"sv;
default:
UNREACHABLE("Unexpected severity value!");
}
return ""sv;
}
void
Journal::JsonLogContext::start(
std::source_location location,
severities::Severity severity,
std::string_view moduleName,
std::string_view journalAttributes) noexcept
{
struct ThreadIdStringInitializer
{
std::string value;
ThreadIdStringInitializer()
{
std::stringstream threadIdStream;
threadIdStream << std::this_thread::get_id();
value = threadIdStream.str();
}
};
thread_local ThreadIdStringInitializer const threadId;
messageOffset_ = 0;
messageBuffer_.clear();
jsonWriter_ = detail::SimpleJsonWriter{&messageBuffer_};
if (!jsonLogsEnabled_)
{
messageBuffer_ = journalAttributes;
return;
}
writer().startObject();
if (!journalAttributes.empty())
{
writer().writeKey("Jnl");
writer().writeRaw(journalAttributes);
writer().endObject();
}
{
std::shared_lock lock(globalLogAttributesMutex_);
if (!globalLogAttributes_.empty())
{
writer().writeKey("Glb");
writer().writeRaw(globalLogAttributes_);
writer().endObject();
}
}
writer().writeKey("Mtd");
writer().startObject();
writer().writeKey("Mdl");
writer().writeString(moduleName);
writer().writeKey("Fl");
constexpr size_t FILE_NAME_KEEP_CHARS = 20;
std::string_view fileName = location.file_name();
std::string_view trimmedFileName = (fileName.size() > FILE_NAME_KEEP_CHARS)
? fileName.substr(fileName.size() - FILE_NAME_KEEP_CHARS)
: fileName;
writer().writeString(trimmedFileName);
writer().writeKey("Ln");
writer().writeUInt(location.line());
writer().writeKey("ThId");
writer().writeString(threadId.value);
auto severityStr = to_string(severity);
writer().writeKey("Lv");
writer().writeString(severityStr);
auto nowMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
writer().writeKey("Tm");
writer().writeString(fastTimestampToString(nowMs));
writer().endObject();
hasMessageParams_ = false;
}
void
Journal::JsonLogContext::reuseJson()
{
messageOffset_ = messageBuffer_.size();
}
void
Journal::JsonLogContext::finish()
{
if (messageOffset_ != 0)
{
messageBuffer_.erase(messageOffset_);
}
else
{
messageBuffer_.clear();
}
jsonWriter_ = detail::SimpleJsonWriter{&messageBuffer_};
}
void
Journal::initMessageContext(
std::source_location location,
severities::Severity severity) const
{
currentJsonLogContext_.start(location, severity, name_, attributes_);
}
std::string&
Journal::formatLog(std::string const& message)
{
if (!jsonLogsEnabled_)
{
currentJsonLogContext_.writer().buffer() += message;
return currentJsonLogContext_.messageBuffer();
}
auto& writer = currentJsonLogContext_.writer();
currentJsonLogContext_.endMessageParams();
writer.writeKey("Msg");
writer.writeString(message);
writer.endObject();
writer.finish();
return currentJsonLogContext_.messageBuffer();
}
void
Journal::enableStructuredJournal()
{
jsonLogsEnabled_ = true;
}
void
Journal::disableStructuredJournal()
{
jsonLogsEnabled_ = false;
resetGlobalAttributes();
}
bool
Journal::isStructuredJournalEnabled()
{
return jsonLogsEnabled_;
}
Journal::Sink::Sink(Severity thresh, bool console)
: thresh_(thresh), m_console(console)
{
@@ -143,13 +413,14 @@ Journal::ScopedStream::ScopedStream(
Journal::ScopedStream::~ScopedStream()
{
std::string const& s(m_ostream.str());
std::string s = m_ostream.str();
if (!s.empty())
{
if (s == "\n")
m_sink.write(m_level, "");
else
m_sink.write(m_level, s);
s = "";
m_sink.write(m_level, formatLog(s));
currentJsonLogContext_.finish();
}
}
@@ -159,12 +430,4 @@ Journal::ScopedStream::operator<<(std::ostream& manip(std::ostream&)) const
return m_ostream << manip;
}
//------------------------------------------------------------------------------
Journal::ScopedStream
Journal::Stream::operator<<(std::ostream& manip(std::ostream&)) const
{
return ScopedStream(*this, manip);
}
} // namespace beast

View File

@@ -17,11 +17,10 @@
*/
//==============================================================================
#include <xrpld/ledger/detail/ApplyStateTable.h>
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/to_string.h>
#include <xrpl/ledger/detail/ApplyStateTable.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/st.h>

View File

@@ -17,10 +17,9 @@
*/
//==============================================================================
#include <xrpld/ledger/ApplyView.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/protocol/Protocol.h>
namespace ripple {

View File

@@ -17,7 +17,7 @@
*/
//==============================================================================
#include <xrpld/ledger/detail/ApplyViewBase.h>
#include <xrpl/ledger/detail/ApplyViewBase.h>
namespace ripple {
namespace detail {

View File

@@ -17,7 +17,7 @@
*/
//==============================================================================
#include <xrpld/ledger/ApplyViewImpl.h>
#include <xrpl/ledger/ApplyViewImpl.h>
namespace ripple {

View File

@@ -18,9 +18,8 @@
*/
//==============================================================================
#include <xrpld/ledger/BookDirs.h>
#include <xrpld/ledger/View.h>
#include <xrpl/ledger/BookDirs.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/Indexes.h>
namespace ripple {

View File

@@ -17,9 +17,8 @@
*/
//==============================================================================
#include <xrpld/ledger/CachedView.h>
#include <xrpl/basics/TaggedCache.ipp>
#include <xrpl/ledger/CachedView.h>
namespace ripple {
namespace detail {

View File

@@ -17,9 +17,8 @@
*/
//==============================================================================
#include <xrpld/app/misc/CredentialHelpers.h>
#include <xrpld/ledger/View.h>
#include <xrpl/ledger/CredentialHelpers.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/digest.h>

View File

@@ -17,7 +17,7 @@
*/
//==============================================================================
#include <xrpld/ledger/Dir.h>
#include <xrpl/ledger/Dir.h>
namespace ripple {

View File

@@ -17,9 +17,8 @@
*/
//==============================================================================
#include <xrpld/ledger/OpenView.h>
#include <xrpl/basics/contract.h>
#include <xrpl/ledger/OpenView.h>
namespace ripple {

View File

@@ -17,11 +17,9 @@
*/
//==============================================================================
#include <xrpld/app/paths/detail/AmountSpec.h>
#include <xrpld/ledger/PaymentSandbox.h>
#include <xrpld/ledger/View.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/SField.h>
namespace ripple {

View File

@@ -17,9 +17,8 @@
*/
//==============================================================================
#include <xrpld/ledger/detail/RawStateTable.h>
#include <xrpl/basics/contract.h>
#include <xrpl/ledger/detail/RawStateTable.h>
namespace ripple {
namespace detail {

View File

@@ -17,7 +17,7 @@
*/
//==============================================================================
#include <xrpld/ledger/ReadView.h>
#include <xrpl/ledger/ReadView.h>
namespace ripple {

View File

@@ -17,14 +17,13 @@
*/
//==============================================================================
#include <xrpld/app/misc/CredentialHelpers.h>
#include <xrpld/ledger/ReadView.h>
#include <xrpld/ledger/View.h>
#include <xrpl/basics/Expected.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/CredentialHelpers.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
@@ -627,8 +626,8 @@ xrpLiquid(
std::uint32_t const ownerCount = confineOwnerCount(
view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj);
// AMMs have no reserve requirement
auto const reserve = sle->isFieldPresent(sfAMMID)
// Pseudo-accounts have no reserve requirement
auto const reserve = isPseudoAccount(sle)
? XRPAmount{0}
: view.fees().accountReserve(ownerCount);
@@ -1040,7 +1039,7 @@ adjustOwnerCount(
AccountID const id = (*sle)[sfAccount];
std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j);
view.adjustOwnerCountHook(id, current, adjusted);
sle->setFieldU32(sfOwnerCount, adjusted);
sle->at(sfOwnerCount) = adjusted;
view.update(sle);
}
@@ -1080,15 +1079,51 @@ pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey)
return beast::zero;
}
// Note, the list of the pseudo-account designator fields below MUST be
// maintained but it does NOT need to be amendment-gated, since a
// non-active amendment will not set any field, by definition. Specific
// properties of a pseudo-account are NOT checked here, that's what
// Pseudo-account designator fields MUST be maintained by including the
// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to
// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated,
// since a non-active amendment will not set any field, by definition.
// Specific properties of a pseudo-account are NOT checked here, that's what
// InvariantCheck is for.
static std::array<SField const*, 2> const pseudoAccountOwnerFields = {
&sfAMMID, //
&sfVaultID, //
};
[[nodiscard]] std::vector<SField const*> const&
getPseudoAccountFields()
{
static std::vector<SField const*> const pseudoFields = []() {
auto const ar = LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT);
if (!ar)
{
// LCOV_EXCL_START
LogicError(
"ripple::isPseudoAccount : unable to find account root ledger "
"format");
// LCOV_EXCL_STOP
}
auto const& soTemplate = ar->getSOTemplate();
std::vector<SField const*> pseudoFields;
for (auto const& field : soTemplate)
{
if (field.sField().shouldMeta(SField::sMD_PseudoAccount))
pseudoFields.emplace_back(&field.sField());
}
return pseudoFields;
}();
return pseudoFields;
}
[[nodiscard]] bool
isPseudoAccount(std::shared_ptr<SLE const> sleAcct)
{
auto const& fields = getPseudoAccountFields();
// Intentionally use defensive coding here because it's cheap and makes the
// semantics of true return value clean.
return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
std::count_if(
fields.begin(), fields.end(), [&sleAcct](SField const* sf) -> bool {
return sleAcct->isFieldPresent(*sf);
}) > 0;
}
Expected<std::shared_ptr<SLE>, TER>
createPseudoAccount(
@@ -1096,10 +1131,11 @@ createPseudoAccount(
uint256 const& pseudoOwnerKey,
SField const& ownerField)
{
auto const& fields = getPseudoAccountFields();
XRPL_ASSERT(
std::count_if(
pseudoAccountOwnerFields.begin(),
pseudoAccountOwnerFields.end(),
fields.begin(),
fields.end(),
[&ownerField](SField const* sf) -> bool {
return *sf == ownerField;
}) == 1,
@@ -1135,18 +1171,42 @@ createPseudoAccount(
return account;
}
[[nodiscard]] bool
isPseudoAccount(std::shared_ptr<SLE const> sleAcct)
[[nodiscard]] TER
canAddHolding(ReadView const& view, Issue const& issue)
{
// Intentionally use defensive coding here because it's cheap and makes the
// semantics of true return value clean.
return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
std::count_if(
pseudoAccountOwnerFields.begin(),
pseudoAccountOwnerFields.end(),
[&sleAcct](SField const* sf) -> bool {
return sleAcct->isFieldPresent(*sf);
}) > 0;
if (issue.native())
return tesSUCCESS; // No special checks for XRP
auto const issuer = view.read(keylet::account(issue.getIssuer()));
if (!issuer)
return terNO_ACCOUNT;
else if (!issuer->isFlag(lsfDefaultRipple))
return terNO_RIPPLE;
return tesSUCCESS;
}
[[nodiscard]] TER
canAddHolding(ReadView const& view, MPTIssue const& mptIssue)
{
auto mptID = mptIssue.getMptID();
auto issuance = view.read(keylet::mptIssuance(mptID));
if (!issuance)
return tecOBJECT_NOT_FOUND;
if (!issuance->isFlag(lsfMPTCanTransfer))
return tecNO_AUTH;
return tesSUCCESS;
}
[[nodiscard]] TER
canAddHolding(ReadView const& view, Asset const& asset)
{
return std::visit(
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
return canAddHolding(view, issue);
},
asset.value());
}
[[nodiscard]] TER
@@ -2793,58 +2853,113 @@ rippleCredit(
saAmount.asset().value());
}
[[nodiscard]] STAmount
[[nodiscard]] std::optional<STAmount>
assetsToSharesDeposit(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& assets)
{
XRPL_ASSERT(
!assets.negative(),
"ripple::assetsToSharesDeposit : non-negative assets");
XRPL_ASSERT(
assets.asset() == vault->at(sfAsset),
"ripple::assetsToSharesDeposit : assets and vault match");
Number assetTotal = vault->at(sfAssetsTotal);
STAmount shares{vault->at(sfShareMPTID), static_cast<Number>(assets)};
if (assets.negative() || assets.asset() != vault->at(sfAsset))
return std::nullopt; // LCOV_EXCL_LINE
Number const assetTotal = vault->at(sfAssetsTotal);
STAmount shares{vault->at(sfShareMPTID)};
if (assetTotal == 0)
return shares;
Number shareTotal = issuance->at(sfOutstandingAmount);
shares = shareTotal * (assets / assetTotal);
return STAmount{
shares.asset(),
Number(assets.mantissa(), assets.exponent() + vault->at(sfScale))
.truncate()};
Number const shareTotal = issuance->at(sfOutstandingAmount);
shares = (shareTotal * (assets / assetTotal)).truncate();
return shares;
}
[[nodiscard]] STAmount
[[nodiscard]] std::optional<STAmount>
sharesToAssetsDeposit(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& shares)
{
XRPL_ASSERT(
!shares.negative(),
"ripple::sharesToAssetsDeposit : non-negative shares");
XRPL_ASSERT(
shares.asset() == vault->at(sfShareMPTID),
"ripple::sharesToAssetsDeposit : shares and vault match");
if (shares.negative() || shares.asset() != vault->at(sfShareMPTID))
return std::nullopt; // LCOV_EXCL_LINE
Number const assetTotal = vault->at(sfAssetsTotal);
STAmount assets{vault->at(sfAsset)};
if (assetTotal == 0)
return STAmount{
assets.asset(),
shares.mantissa(),
shares.exponent() - vault->at(sfScale),
false};
Number const shareTotal = issuance->at(sfOutstandingAmount);
assets = assetTotal * (shares / shareTotal);
return assets;
}
[[nodiscard]] std::optional<STAmount>
assetsToSharesWithdraw(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& assets)
STAmount const& assets,
TruncateShares truncate)
{
XRPL_ASSERT(
!assets.negative(),
"ripple::assetsToSharesDeposit : non-negative assets");
XRPL_ASSERT(
assets.asset() == vault->at(sfAsset),
"ripple::assetsToSharesWithdraw : assets and vault match");
if (assets.negative() || assets.asset() != vault->at(sfAsset))
return std::nullopt; // LCOV_EXCL_LINE
Number assetTotal = vault->at(sfAssetsTotal);
assetTotal -= vault->at(sfLossUnrealized);
STAmount shares{vault->at(sfShareMPTID)};
if (assetTotal == 0)
return shares;
Number shareTotal = issuance->at(sfOutstandingAmount);
shares = shareTotal * (assets / assetTotal);
Number const shareTotal = issuance->at(sfOutstandingAmount);
Number result = shareTotal * (assets / assetTotal);
if (truncate == TruncateShares::yes)
result = result.truncate();
shares = result;
return shares;
}
[[nodiscard]] STAmount
[[nodiscard]] std::optional<STAmount>
sharesToAssetsWithdraw(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& shares)
{
XRPL_ASSERT(
!shares.negative(),
"ripple::sharesToAssetsDeposit : non-negative shares");
XRPL_ASSERT(
shares.asset() == vault->at(sfShareMPTID),
"ripple::sharesToAssetsWithdraw : shares and vault match");
if (shares.negative() || shares.asset() != vault->at(sfShareMPTID))
return std::nullopt; // LCOV_EXCL_LINE
Number assetTotal = vault->at(sfAssetsTotal);
assetTotal -= vault->at(sfLossUnrealized);
STAmount assets{vault->at(sfAsset)};
if (assetTotal == 0)
return assets;
Number shareTotal = issuance->at(sfOutstandingAmount);
Number const shareTotal = issuance->at(sfOutstandingAmount);
assets = assetTotal * (shares / shareTotal);
return assets;
}
@@ -2951,11 +3066,17 @@ rippleUnlockEscrowMPT(
ApplyView& view,
AccountID const& sender,
AccountID const& receiver,
STAmount const& amount,
STAmount const& netAmount,
STAmount const& grossAmount,
beast::Journal j)
{
auto const issuer = amount.getIssuer();
auto const mptIssue = amount.get<MPTIssue>();
if (!view.rules().enabled(fixTokenEscrowV1))
XRPL_ASSERT(
netAmount == grossAmount,
"ripple::rippleUnlockEscrowMPT : netAmount == grossAmount");
auto const& issuer = netAmount.getIssuer();
auto const& mptIssue = netAmount.get<MPTIssue>();
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
auto sleIssuance = view.peek(mptID);
if (!sleIssuance)
@@ -2976,7 +3097,7 @@ rippleUnlockEscrowMPT(
} // LCOV_EXCL_STOP
auto const locked = sleIssuance->getFieldU64(sfLockedAmount);
auto const redeem = amount.mpt().value();
auto const redeem = grossAmount.mpt().value();
// Underflow check for subtraction
if (!canSubtract(
@@ -3009,7 +3130,7 @@ rippleUnlockEscrowMPT(
} // LCOV_EXCL_STOP
auto current = sle->getFieldU64(sfMPTAmount);
auto delta = amount.mpt().value();
auto delta = netAmount.mpt().value();
// Overflow check for addition
if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta)))
@@ -3027,7 +3148,7 @@ rippleUnlockEscrowMPT(
{
// Decrease the Issuance OutstandingAmount
auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
auto const redeem = amount.mpt().value();
auto const redeem = netAmount.mpt().value();
// Underflow check for subtraction
if (!canSubtract(
@@ -3071,7 +3192,7 @@ rippleUnlockEscrowMPT(
} // LCOV_EXCL_STOP
auto const locked = sle->getFieldU64(sfLockedAmount);
auto const delta = amount.mpt().value();
auto const delta = grossAmount.mpt().value();
// Underflow check for subtraction
if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta)))
@@ -3089,6 +3210,28 @@ rippleUnlockEscrowMPT(
sle->setFieldU64(sfLockedAmount, newLocked);
view.update(sle);
}
// Note: The gross amount is the amount that was locked, the net
// amount is the amount that is being unlocked. The difference is the fee
// that was charged for the transfer. If this difference is greater than
// zero, we need to update the outstanding amount.
auto const diff = grossAmount.mpt().value() - netAmount.mpt().value();
if (diff != 0)
{
auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
// Underflow check for subtraction
if (!canSubtract(
STAmount(mptIssue, outstanding), STAmount(mptIssue, diff)))
{ // LCOV_EXCL_START
JLOG(j.error())
<< "rippleUnlockEscrowMPT: insufficient outstanding amount for "
<< mptIssue.getMptID() << ": " << outstanding << " < " << diff;
return tecINTERNAL;
} // LCOV_EXCL_STOP
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff);
view.update(sleIssuance);
}
return tesSUCCESS;
}

View File

@@ -383,7 +383,7 @@ public:
static boost::regex reStatus{
"\\`HTTP/1\\S+ (\\d{3}) .*\\'"}; // HTTP/1.1 200 OK
static boost::regex reSize{
"\\`.*\\r\\nContent-Length:\\s+([0-9]+).*\\'"};
"\\`.*\\r\\nContent-Length:\\s+([0-9]+).*\\'", boost::regex::icase};
static boost::regex reBody{"\\`.*\\r\\n\\r\\n(.*)\\'"};
boost::smatch smMatch;

View File

@@ -36,7 +36,7 @@ namespace BuildInfo {
// and follow the format described at http://semver.org/
//------------------------------------------------------------------------------
// clang-format off
char const* const versionString = "2.6.0"
char const* const versionString = "2.6.1-rc1"
// clang-format on
#if defined(DEBUG) || defined(SANITIZER)

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Permissions.h>
#include <xrpl/protocol/jss.h>
@@ -25,11 +26,24 @@ namespace ripple {
Permission::Permission()
{
txFeatureMap_ = {
#pragma push_macro("TRANSACTION")
#undef TRANSACTION
#define TRANSACTION(tag, value, name, delegatable, amendment, ...) \
{value, amendment},
#include <xrpl/protocol/detail/transactions.macro>
#undef TRANSACTION
#pragma pop_macro("TRANSACTION")
};
delegatableTx_ = {
#pragma push_macro("TRANSACTION")
#undef TRANSACTION
#define TRANSACTION(tag, value, name, delegatable, fields) {value, delegatable},
#define TRANSACTION(tag, value, name, delegatable, ...) {value, delegatable},
#include <xrpl/protocol/detail/transactions.macro>
@@ -87,6 +101,22 @@ Permission::getInstance()
return instance;
}
std::optional<std::string>
Permission::getPermissionName(std::uint32_t const value) const
{
auto const permissionValue = static_cast<GranularPermissionType>(value);
if (auto const granular = getGranularName(permissionValue))
return *granular;
// not a granular permission, check if it maps to a transaction type
auto const txType = permissionToTxType(value);
if (auto const* item = TxFormats::getInstance().findByType(txType);
item != nullptr)
return item->getName();
return std::nullopt;
}
std::optional<std::uint32_t>
Permission::getGranularValue(std::string const& name) const
{
@@ -118,7 +148,9 @@ Permission::getGranularTxType(GranularPermissionType const& gpType) const
}
bool
Permission::isDelegatable(std::uint32_t const& permissionValue) const
Permission::isDelegatable(
std::uint32_t const& permissionValue,
Rules const& rules) const
{
auto const granularPermission =
getGranularName(static_cast<GranularPermissionType>(permissionValue));
@@ -126,7 +158,27 @@ Permission::isDelegatable(std::uint32_t const& permissionValue) const
// granular permissions are always allowed to be delegated
return true;
auto const it = delegatableTx_.find(permissionValue - 1);
auto const txType = permissionToTxType(permissionValue);
auto const it = delegatableTx_.find(txType);
if (rules.enabled(fixDelegateV1_1))
{
if (it == delegatableTx_.end())
return false;
auto const txFeaturesIt = txFeatureMap_.find(txType);
XRPL_ASSERT(
txFeaturesIt != txFeatureMap_.end(),
"ripple::Permissions::isDelegatable : tx exists in txFeatureMap_");
// fixDelegateV1_1: Delegation is only allowed if the required amendment
// for the transaction is enabled. For transactions that do not require
// an amendment, delegation is always allowed.
if (txFeaturesIt->second != uint256{} &&
!rules.enabled(txFeaturesIt->second))
return false;
}
if (it != delegatableTx_.end() && it->second == Delegation::notDelegatable)
return false;

View File

@@ -17,6 +17,7 @@
*/
//==============================================================================
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/SField.h>
#include <map>
@@ -27,7 +28,8 @@ namespace ripple {
// Storage for static const members.
SField::IsSigning const SField::notSigning;
int SField::num = 0;
std::map<int, SField const*> SField::knownCodeToField;
std::unordered_map<int, SField const*> SField::knownCodeToField;
std::unordered_map<std::string, SField const*> SField::knownNameToField;
// Give only this translation unit permission to construct SFields
struct SField::private_access_tag_t
@@ -45,7 +47,7 @@ TypedField<T>::TypedField(private_access_tag_t pat, Args&&... args)
}
// Construct all compile-time SFields, and register them in the knownCodeToField
// database:
// and knownNameToField databases:
// Use macros for most SField construction to enforce naming conventions.
#pragma push_macro("UNTYPED_SFIELD")
@@ -69,8 +71,8 @@ TypedField<T>::TypedField(private_access_tag_t pat, Args&&... args)
##__VA_ARGS__);
// SFields which, for historical reasons, do not follow naming conventions.
SField const sfInvalid(access, -1);
SField const sfGeneric(access, 0);
SField const sfInvalid(access, -1, "");
SField const sfGeneric(access, 0, "Generic");
// The following two fields aren't used anywhere, but they break tests/have
// downstream effects.
SField const sfHash(access, STI_UINT256, 257, "hash");
@@ -99,19 +101,34 @@ SField::SField(
, signingField(signing)
, jsonName(fieldName.c_str())
{
XRPL_ASSERT(
!knownCodeToField.contains(fieldCode),
"ripple::SField::SField(tid,fv,fn,meta,signing) : fieldCode is unique");
XRPL_ASSERT(
!knownNameToField.contains(fieldName),
"ripple::SField::SField(tid,fv,fn,meta,signing) : fieldName is unique");
knownCodeToField[fieldCode] = this;
knownNameToField[fieldName] = this;
}
SField::SField(private_access_tag_t, int fc)
SField::SField(private_access_tag_t, int fc, char const* fn)
: fieldCode(fc)
, fieldType(STI_UNKNOWN)
, fieldValue(0)
, fieldName(fn)
, fieldMeta(sMD_Never)
, fieldNum(++num)
, signingField(IsSigning::yes)
, jsonName(fieldName.c_str())
{
XRPL_ASSERT(
!knownCodeToField.contains(fieldCode),
"ripple::SField::SField(fc,fn) : fieldCode is unique");
XRPL_ASSERT(
!knownNameToField.contains(fieldName),
"ripple::SField::SField(fc,fn) : fieldName is unique");
knownCodeToField[fieldCode] = this;
knownNameToField[fieldName] = this;
}
SField const&
@@ -145,11 +162,11 @@ SField::compare(SField const& f1, SField const& f2)
SField const&
SField::getField(std::string const& fieldName)
{
for (auto const& [_, f] : knownCodeToField)
auto it = knownNameToField.find(fieldName);
if (it != knownNameToField.end())
{
(void)_;
if (f->fieldName == fieldName)
return *f;
return *(it->second);
}
return sfInvalid;
}

View File

@@ -62,8 +62,10 @@ STUInt8::getText() const
if (transResultInfo(TER::fromInt(value_), token, human))
return human;
// LCOV_EXCL_START
JLOG(debugLog().error())
<< "Unknown result code in metadata: " << value_;
// LCOV_EXCL_STOP
}
return std::to_string(value_);
@@ -80,8 +82,10 @@ STUInt8::getJson(JsonOptions) const
if (transResultInfo(TER::fromInt(value_), token, human))
return token;
// LCOV_EXCL_START
JLOG(debugLog().error())
<< "Unknown result code in metadata: " << value_;
// LCOV_EXCL_STOP
}
return value_;
@@ -171,6 +175,13 @@ template <>
std::string
STUInt32::getText() const
{
if (getFName() == sfPermissionValue)
{
auto const permissionName =
Permission::getInstance().getPermissionName(value_);
if (permissionName)
return *permissionName;
}
return std::to_string(value_);
}
@@ -180,23 +191,10 @@ STUInt32::getJson(JsonOptions) const
{
if (getFName() == sfPermissionValue)
{
auto const permissionValue =
static_cast<GranularPermissionType>(value_);
auto const granular =
Permission::getInstance().getGranularName(permissionValue);
if (granular)
{
return *granular;
}
else
{
auto const txType =
Permission::getInstance().permissionToTxType(value_);
auto item = TxFormats::getInstance().findByType(txType);
if (item != nullptr)
return item->getName();
}
auto const permissionName =
Permission::getInstance().getPermissionName(value_);
if (permissionName)
return *permissionName;
}
return value_;

View File

@@ -83,7 +83,8 @@ constexpr std::
return static_cast<U1>(value);
}
static std::string
// LCOV_EXCL_START
static inline std::string
make_name(std::string const& object, std::string const& field)
{
if (field.empty())
@@ -92,7 +93,7 @@ make_name(std::string const& object, std::string const& field)
return object + "." + field;
}
static Json::Value
static inline Json::Value
not_an_object(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -100,20 +101,20 @@ not_an_object(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' is not a JSON object.");
}
static Json::Value
static inline Json::Value
not_an_object(std::string const& object)
{
return not_an_object(object, "");
}
static Json::Value
static inline Json::Value
not_an_array(std::string const& object)
{
return RPC::make_error(
rpcINVALID_PARAMS, "Field '" + object + "' is not a JSON array.");
}
static Json::Value
static inline Json::Value
unknown_field(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -121,7 +122,7 @@ unknown_field(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' is unknown.");
}
static Json::Value
static inline Json::Value
out_of_range(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -129,7 +130,7 @@ out_of_range(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' is out of range.");
}
static Json::Value
static inline Json::Value
bad_type(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -137,7 +138,7 @@ bad_type(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' has bad type.");
}
static Json::Value
static inline Json::Value
invalid_data(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -145,13 +146,13 @@ invalid_data(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' has invalid data.");
}
static Json::Value
static inline Json::Value
invalid_data(std::string const& object)
{
return invalid_data(object, "");
}
static Json::Value
static inline Json::Value
array_expected(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -159,7 +160,7 @@ array_expected(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' must be a JSON array.");
}
static Json::Value
static inline Json::Value
string_expected(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -167,7 +168,7 @@ string_expected(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' must be a string.");
}
static Json::Value
static inline Json::Value
too_deep(std::string const& object)
{
return RPC::make_error(
@@ -175,7 +176,7 @@ too_deep(std::string const& object)
"Field '" + object + "' exceeds nesting depth limit.");
}
static Json::Value
static inline Json::Value
singleton_expected(std::string const& object, unsigned int index)
{
return RPC::make_error(
@@ -184,7 +185,7 @@ singleton_expected(std::string const& object, unsigned int index)
"]' must be an object with a single key/object value.");
}
static Json::Value
static inline Json::Value
template_mismatch(SField const& sField)
{
return RPC::make_error(
@@ -193,7 +194,7 @@ template_mismatch(SField const& sField)
"' contents did not meet requirements for that type.");
}
static Json::Value
static inline Json::Value
non_object_in_array(std::string const& item, Json::UInt index)
{
return RPC::make_error(
@@ -201,6 +202,176 @@ non_object_in_array(std::string const& item, Json::UInt index)
"Item '" + item + "' at index " + std::to_string(index) +
" is not an object. Arrays may only contain objects.");
}
// LCOV_EXCL_STOP
template <class STResult, class Integer>
static std::optional<detail::STVar>
parseUnsigned(
SField const& field,
std::string const& json_name,
std::string const& fieldName,
SField const* name,
Json::Value const& value,
Json::Value& error)
{
std::optional<detail::STVar> ret;
try
{
if (value.isString())
{
ret = detail::make_stvar<STResult>(
field,
safe_cast<typename STResult::value_type>(
beast::lexicalCastThrow<Integer>(value.asString())));
}
else if (value.isInt())
{
ret = detail::make_stvar<STResult>(
field,
to_unsigned<typename STResult::value_type>(value.asInt()));
}
else if (value.isUInt())
{
ret = detail::make_stvar<STResult>(
field,
to_unsigned<typename STResult::value_type>(value.asUInt()));
}
else
{
error = bad_type(json_name, fieldName);
return ret;
}
}
catch (std::exception const&)
{
error = invalid_data(json_name, fieldName);
return ret;
}
return ret;
}
template <class STResult, class Integer = std::uint16_t>
static std::optional<detail::STVar>
parseUint16(
SField const& field,
std::string const& json_name,
std::string const& fieldName,
SField const* name,
Json::Value const& value,
Json::Value& error)
{
std::optional<detail::STVar> ret;
try
{
if (value.isString())
{
std::string const strValue = value.asString();
if (!strValue.empty() &&
((strValue[0] < '0') || (strValue[0] > '9')))
{
if (field == sfTransactionType)
{
ret = detail::make_stvar<STResult>(
field,
safe_cast<typename STResult::value_type>(
static_cast<Integer>(
TxFormats::getInstance().findTypeByName(
strValue))));
if (*name == sfGeneric)
name = &sfTransaction;
}
else if (field == sfLedgerEntryType)
{
ret = detail::make_stvar<STResult>(
field,
safe_cast<typename STResult::value_type>(
static_cast<Integer>(
LedgerFormats::getInstance().findTypeByName(
strValue))));
if (*name == sfGeneric)
name = &sfLedgerEntry;
}
else
{
error = invalid_data(json_name, fieldName);
return ret;
}
}
}
if (!ret)
return parseUnsigned<STResult, Integer>(
field, json_name, fieldName, name, value, error);
}
catch (std::exception const&)
{
error = invalid_data(json_name, fieldName);
return ret;
}
return ret;
}
template <class STResult, class Integer = std::uint32_t>
static std::optional<detail::STVar>
parseUint32(
SField const& field,
std::string const& json_name,
std::string const& fieldName,
SField const* name,
Json::Value const& value,
Json::Value& error)
{
std::optional<detail::STVar> ret;
try
{
if (value.isString())
{
if (field == sfPermissionValue)
{
std::string const strValue = value.asString();
auto const granularPermission =
Permission::getInstance().getGranularValue(strValue);
if (granularPermission)
{
ret = detail::make_stvar<STResult>(
field, *granularPermission);
}
else
{
auto const& txType =
TxFormats::getInstance().findTypeByName(strValue);
ret = detail::make_stvar<STResult>(
field,
Permission::getInstance().txToPermissionType(txType));
}
}
else
{
ret = detail::make_stvar<STResult>(
field,
safe_cast<typename STResult::value_type>(
beast::lexicalCastThrow<Integer>(value.asString())));
}
}
if (!ret)
return parseUnsigned<STResult, Integer>(
field, json_name, fieldName, name, value, error);
}
catch (std::exception const&)
{
error = invalid_data(json_name, fieldName);
return ret;
}
return ret;
}
// This function is used by parseObject to parse any JSON type that doesn't
// recurse. Everything represented here is a leaf-type.
@@ -216,10 +387,13 @@ parseLeaf(
auto const& field = SField::getField(fieldName);
// checked in parseObject
if (field == sfInvalid)
{
// LCOV_EXCL_START
error = unknown_field(json_name, fieldName);
return ret;
// LCOV_EXCL_STOP
}
switch (field.fieldType)
@@ -302,130 +476,18 @@ parseLeaf(
break;
case STI_UINT16:
try
{
if (value.isString())
{
std::string const strValue = value.asString();
if (!strValue.empty() &&
((strValue[0] < '0') || (strValue[0] > '9')))
{
if (field == sfTransactionType)
{
ret = detail::make_stvar<STUInt16>(
field,
static_cast<std::uint16_t>(
TxFormats::getInstance().findTypeByName(
strValue)));
if (*name == sfGeneric)
name = &sfTransaction;
}
else if (field == sfLedgerEntryType)
{
ret = detail::make_stvar<STUInt16>(
field,
static_cast<std::uint16_t>(
LedgerFormats::getInstance().findTypeByName(
strValue)));
if (*name == sfGeneric)
name = &sfLedgerEntry;
}
else
{
error = invalid_data(json_name, fieldName);
return ret;
}
}
else
{
ret = detail::make_stvar<STUInt16>(
field,
beast::lexicalCastThrow<std::uint16_t>(strValue));
}
}
else if (value.isInt())
{
ret = detail::make_stvar<STUInt16>(
field, to_unsigned<std::uint16_t>(value.asInt()));
}
else if (value.isUInt())
{
ret = detail::make_stvar<STUInt16>(
field, to_unsigned<std::uint16_t>(value.asUInt()));
}
else
{
error = bad_type(json_name, fieldName);
return ret;
}
}
catch (std::exception const&)
{
error = invalid_data(json_name, fieldName);
ret = parseUint16<STUInt16>(
field, json_name, fieldName, name, value, error);
if (!ret)
return ret;
}
break;
case STI_UINT32:
try
{
if (value.isString())
{
if (field == sfPermissionValue)
{
std::string const strValue = value.asString();
auto const granularPermission =
Permission::getInstance().getGranularValue(
strValue);
if (granularPermission)
{
ret = detail::make_stvar<STUInt32>(
field, *granularPermission);
}
else
{
auto const& txType =
TxFormats::getInstance().findTypeByName(
strValue);
ret = detail::make_stvar<STUInt32>(
field,
Permission::getInstance().txToPermissionType(
txType));
}
}
else
{
ret = detail::make_stvar<STUInt32>(
field,
beast::lexicalCastThrow<std::uint32_t>(
value.asString()));
}
}
else if (value.isInt())
{
ret = detail::make_stvar<STUInt32>(
field, to_unsigned<std::uint32_t>(value.asInt()));
}
else if (value.isUInt())
{
ret = detail::make_stvar<STUInt32>(
field, safe_cast<std::uint32_t>(value.asUInt()));
}
else
{
error = bad_type(json_name, fieldName);
return ret;
}
}
catch (std::exception const&)
{
error = invalid_data(json_name, fieldName);
ret = parseUint32<STUInt32>(
field, json_name, fieldName, name, value, error);
if (!ret)
return ret;
}
break;
@@ -703,6 +765,12 @@ parseLeaf(
AccountID uAccount, uIssuer;
Currency uCurrency;
if (!account && !currency && !issuer)
{
error = invalid_data(element_name);
return ret;
}
if (account)
{
// human account id
@@ -1096,24 +1164,4 @@ STParsedJSONObject::STParsedJSONObject(
object = parseObject(name, json, sfGeneric, 0, error);
}
//------------------------------------------------------------------------------
STParsedJSONArray::STParsedJSONArray(
std::string const& name,
Json::Value const& json)
{
using namespace STParsedJSONDetail;
auto arr = parseArray(name, json, sfGeneric, 0, error);
if (!arr)
array.reset();
else
{
auto p = dynamic_cast<STArray*>(&arr->get());
if (p == nullptr)
array.reset();
else
array = std::move(*p);
}
}
} // namespace ripple

View File

@@ -55,7 +55,8 @@ TxFormats::TxFormats()
#undef TRANSACTION
#define UNWRAP(...) __VA_ARGS__
#define TRANSACTION(tag, value, name, delegatable, fields) \
#define TRANSACTION( \
tag, value, name, delegatable, amendment, privileges, fields) \
add(jss::name, tag, UNWRAP fields, commonFields);
#include <xrpl/protocol/detail/transactions.macro>

View File

@@ -67,7 +67,7 @@ class AMMCalc_test : public beast::unit_test::suite
// drops
else if (match[1] == "XRPA")
return XRPAmount{std::stoll(match[2])};
return amountFromString(gw[match[1]], match[2]);
return amountFromString(gw[match[1]].asset(), match[2]);
}
return std::nullopt;
}

View File

@@ -2442,8 +2442,7 @@ class AMMClawback_test : public beast::unit_test::suite
void
run() override
{
FeatureBitset const all{
jtx::testable_amendments() | fixAMMClawbackRounding};
FeatureBitset const all = jtx::testable_amendments();
testInvalidRequest();
testFeatureDisabled(all - featureAMMClawback);

View File

@@ -29,8 +29,8 @@
#include <xrpld/app/paths/AMMOffer.h>
#include <xrpld/app/paths/Flow.h>
#include <xrpld/app/paths/detail/StrandFlow.h>
#include <xrpld/ledger/PaymentSandbox.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/STParsedJSON.h>
@@ -181,9 +181,9 @@ private:
BEAST_EXPECT(expectLedgerEntryRoot(
env, alice, XRP(20'000) - XRP(50) - txfee(env, 1)));
BEAST_EXPECT(expectLine(env, bob, USD1(100)));
BEAST_EXPECT(expectLine(env, bob, USD2(0)));
BEAST_EXPECT(expectLine(env, carol, USD2(50)));
BEAST_EXPECT(expectHolding(env, bob, USD1(100)));
BEAST_EXPECT(expectHolding(env, bob, USD2(0)));
BEAST_EXPECT(expectHolding(env, carol, USD2(50)));
}
}
@@ -220,7 +220,7 @@ private:
BEAST_EXPECT(expectLedgerEntryRoot(
env, carol, XRP(30'000) - (txfee(env, 1))));
BEAST_EXPECT(expectOffers(env, carol, 0));
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
// Order that can be filled
env(offer(carol, XRP(100), USD(100)),
@@ -230,7 +230,7 @@ private:
XRP(10'000), USD(10'100), ammAlice.tokens()));
BEAST_EXPECT(expectLedgerEntryRoot(
env, carol, XRP(30'000) + XRP(100) - txfee(env, 2)));
BEAST_EXPECT(expectLine(env, carol, USD(29'900)));
BEAST_EXPECT(expectHolding(env, carol, USD(29'900)));
BEAST_EXPECT(expectOffers(env, carol, 0));
},
{{XRP(10'100), USD(10'000)}},
@@ -254,7 +254,7 @@ private:
BEAST_EXPECT(expectLedgerEntryRoot(
env, carol, XRP(30'000) + XRP(100) - txfee(env, 1)));
// AMM
BEAST_EXPECT(expectLine(env, carol, USD(29'900)));
BEAST_EXPECT(expectHolding(env, carol, USD(29'900)));
BEAST_EXPECT(expectOffers(env, carol, 0));
},
{{XRP(10'100), USD(10'000)}},
@@ -327,7 +327,7 @@ private:
USD(49),
IOUAmount{273'861'278752583, -8}));
BEAST_EXPECT(expectLine(env, bob, STAmount{USD, 101}));
BEAST_EXPECT(expectHolding(env, bob, STAmount{USD, 101}));
BEAST_EXPECT(expectLedgerEntryRoot(
env, bob, XRP(300'000) - xrpTransferred - txfee(env, 1)));
BEAST_EXPECT(expectOffers(env, bob, 0));
@@ -390,7 +390,7 @@ private:
BEAST_EXPECT(
ammBob.expectBalances(USD(300), XRP(1'000), ammBob.tokens()));
BEAST_EXPECT(expectLine(env, alice, USD(0)));
BEAST_EXPECT(expectHolding(env, alice, USD(0)));
auto jrr = ledgerEntryRoot(env, alice);
BEAST_EXPECT(
@@ -423,7 +423,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRPAmount{9'900'990'100}, USD(10'100), ammAlice.tokens()));
// initial 30,000 - 10,000AMM - 100pay
BEAST_EXPECT(expectLine(env, alice, USD(19'900)));
BEAST_EXPECT(expectHolding(env, alice, USD(19'900)));
// initial 30,000 - 10,0000AMM + 99.009900pay - fee*3
BEAST_EXPECT(expectLedgerEntryRoot(
env,
@@ -453,7 +453,7 @@ private:
env(pay(alice, bob, USD(100)), sendmax(XRP(100)));
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
BEAST_EXPECT(expectLine(env, bob, USD(100)));
BEAST_EXPECT(expectHolding(env, bob, USD(100)));
},
{{XRP(10'000), USD(10'100)}},
0,
@@ -533,7 +533,7 @@ private:
STAmount{USD1, UINT64_C(5'030'181086519115), -12},
ammCarol.tokens()));
BEAST_EXPECT(expectOffers(env, dan, 1, {{Amounts{XRP(200), EUR(20)}}}));
BEAST_EXPECT(expectLine(env, bob, STAmount{EUR1, 30}));
BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR1, 30}));
}
void
@@ -642,7 +642,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000), USD(9'999), ammAlice.tokens()));
BEAST_EXPECT(expectOffers(env, carol, 0));
BEAST_EXPECT(expectLine(env, carol, USD(30'101)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'101)));
BEAST_EXPECT(expectLedgerEntryRoot(
env, carol, XRP(30'000) - XRP(100) - txfee(env, 1)));
},
@@ -682,7 +682,7 @@ private:
env(offer(alice, USD(100), XRP(200)), json(jss::Flags, tfSell));
BEAST_EXPECT(
ammBob.expectBalances(XRP(1'100), USD(2'000), ammBob.tokens()));
BEAST_EXPECT(expectLine(env, alice, USD(200)));
BEAST_EXPECT(expectHolding(env, alice, USD(200)));
BEAST_EXPECT(expectLedgerEntryRoot(env, alice, XRP(250)));
BEAST_EXPECT(expectOffers(env, alice, 0));
}
@@ -733,7 +733,7 @@ private:
STAmount(XTS, UINT64_C(101'010101010101), -12),
XXX(99),
ammAlice.tokens()));
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, bob, STAmount{XTS, UINT64_C(98'989898989899), -12}));
}
else
@@ -742,10 +742,10 @@ private:
STAmount(XTS, UINT64_C(101'0101010101011), -13),
XXX(99),
ammAlice.tokens()));
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, bob, STAmount{XTS, UINT64_C(98'9898989898989), -13}));
}
BEAST_EXPECT(expectLine(env, bob, XXX(101)));
BEAST_EXPECT(expectHolding(env, bob, XXX(101)));
}
void
@@ -783,8 +783,8 @@ private:
XRP(10'100), USD(10'000), ammAlice.tokens()));
BEAST_EXPECT(ammBob.expectBalances(
XRP(10'000), EUR(10'100), ammBob.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(15'100)));
BEAST_EXPECT(expectLine(env, carol, EUR(14'900)));
BEAST_EXPECT(expectHolding(env, carol, USD(15'100)));
BEAST_EXPECT(expectHolding(env, carol, EUR(14'900)));
BEAST_EXPECT(expectOffers(env, carol, 0));
}
@@ -816,8 +816,8 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(15'100)));
BEAST_EXPECT(expectLine(env, carol, EUR(14'900)));
BEAST_EXPECT(expectHolding(env, carol, USD(15'100)));
BEAST_EXPECT(expectHolding(env, carol, EUR(14'900)));
BEAST_EXPECT(expectOffers(env, carol, 0));
BEAST_EXPECT(expectOffers(env, bob, 0));
}
@@ -850,8 +850,8 @@ private:
BEAST_EXPECT(ammBob.expectBalances(
XRP(10'000), EUR(10'100), ammBob.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(15'100)));
BEAST_EXPECT(expectLine(env, carol, EUR(14'900)));
BEAST_EXPECT(expectHolding(env, carol, USD(15'100)));
BEAST_EXPECT(expectHolding(env, carol, EUR(14'900)));
BEAST_EXPECT(expectOffers(env, carol, 0));
BEAST_EXPECT(expectOffers(env, alice, 0));
}
@@ -894,7 +894,7 @@ private:
XRP(20'220),
STAmount{USD, UINT64_C(197'8239366963403), -13},
ammBob.tokens()));
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, alice, STAmount{USD, UINT64_C(1'002'17606330366), -11}));
BEAST_EXPECT(expectOffers(env, alice, 0));
}
@@ -912,7 +912,7 @@ private:
XRP(21'500),
STAmount{USD, UINT64_C(186'046511627907), -12},
ammBob.tokens()));
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, alice, STAmount{USD, UINT64_C(1'013'953488372093), -12}));
BEAST_EXPECT(expectOffers(env, alice, 0));
}
@@ -953,7 +953,7 @@ private:
// AMM doesn't pay the transfer fee
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
BEAST_EXPECT(expectOffers(env, carol, 0));
},
{{XRP(10'000), USD(10'100)}},
@@ -974,7 +974,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000), USD(10'100), ammAlice.tokens()));
// Carol pays 25% transfer fee
BEAST_EXPECT(expectLine(env, carol, USD(29'875)));
BEAST_EXPECT(expectHolding(env, carol, USD(29'875)));
BEAST_EXPECT(expectOffers(env, carol, 0));
},
{{XRP(10'100), USD(10'000)}},
@@ -1011,9 +1011,9 @@ private:
// AMM doesn't pay the transfer fee
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(15'100)));
BEAST_EXPECT(expectHolding(env, carol, USD(15'100)));
// Carol pays 25% transfer fee.
BEAST_EXPECT(expectLine(env, carol, EUR(14'875)));
BEAST_EXPECT(expectHolding(env, carol, EUR(14'875)));
BEAST_EXPECT(expectOffers(env, carol, 0));
BEAST_EXPECT(expectOffers(env, bob, 0));
}
@@ -1051,9 +1051,9 @@ private:
// AMM doesn't pay the transfer fee
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'050), USD(10'000), ammAlice.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(15'050)));
BEAST_EXPECT(expectHolding(env, carol, USD(15'050)));
// Carol pays 25% transfer fee.
BEAST_EXPECT(expectLine(env, carol, EUR(14'937.5)));
BEAST_EXPECT(expectHolding(env, carol, EUR(14'937.5)));
BEAST_EXPECT(expectOffers(env, carol, 0));
BEAST_EXPECT(
expectOffers(env, bob, 1, {{Amounts{EUR(50), XRP(50)}}}));
@@ -1077,7 +1077,7 @@ private:
env(pay(gw, carol, EUR(1'000)), sendmax(EUR(10'000)));
env.close();
// 1000 / 0.8
BEAST_EXPECT(expectLine(env, carol, EUR(1'250)));
BEAST_EXPECT(expectHolding(env, carol, EUR(1'250)));
// The scenario:
// o USD/XRP AMM is created.
// o EUR/XRP Offer is created.
@@ -1096,9 +1096,9 @@ private:
// AMM doesn't pay the transfer fee
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(100)));
BEAST_EXPECT(expectHolding(env, carol, USD(100)));
// Carol pays 25% transfer fee: 1250 - 100(offer) - 25(transfer fee)
BEAST_EXPECT(expectLine(env, carol, EUR(1'125)));
BEAST_EXPECT(expectHolding(env, carol, EUR(1'125)));
BEAST_EXPECT(expectOffers(env, carol, 0));
BEAST_EXPECT(expectOffers(env, bob, 0));
}
@@ -1120,7 +1120,7 @@ private:
env(pay(gw, alice, USD(11'000)));
env(pay(gw, carol, EUR(1'000)), sendmax(EUR(10'000)));
env.close();
BEAST_EXPECT(expectLine(env, carol, EUR(1'000)));
BEAST_EXPECT(expectHolding(env, carol, EUR(1'000)));
// The scenario:
// o USD/XRP AMM is created.
// o EUR/XRP Offer is created.
@@ -1139,9 +1139,9 @@ private:
// AMM pay doesn't transfer fee
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(100)));
BEAST_EXPECT(expectHolding(env, carol, USD(100)));
// Carol pays 25% transfer fee: 1000 - 100(offer) - 25(transfer fee)
BEAST_EXPECT(expectLine(env, carol, EUR(875)));
BEAST_EXPECT(expectHolding(env, carol, EUR(875)));
BEAST_EXPECT(expectOffers(env, carol, 0));
BEAST_EXPECT(expectOffers(env, bob, 0));
}
@@ -1170,7 +1170,7 @@ private:
BEAST_EXPECT(ammBob.expectBalances(
XRP(10'100), USD_bob(10'000), ammBob.tokens()));
BEAST_EXPECT(expectOffers(env, alice, 0));
BEAST_EXPECT(expectLine(env, alice, USD_bob(100)));
BEAST_EXPECT(expectHolding(env, alice, USD_bob(100)));
}
void
@@ -1206,19 +1206,19 @@ private:
env.close();
env(pay(dan, bob, D_BUX(100)));
env.close();
BEAST_EXPECT(expectLine(env, bob, D_BUX(100)));
BEAST_EXPECT(expectHolding(env, bob, D_BUX(100)));
env(pay(ann, cam, D_BUX(60)), path(bob, dan), sendmax(A_BUX(200)));
env.close();
BEAST_EXPECT(expectLine(env, ann, A_BUX(none)));
BEAST_EXPECT(expectLine(env, ann, D_BUX(none)));
BEAST_EXPECT(expectLine(env, bob, A_BUX(72)));
BEAST_EXPECT(expectLine(env, bob, D_BUX(40)));
BEAST_EXPECT(expectLine(env, cam, A_BUX(none)));
BEAST_EXPECT(expectLine(env, cam, D_BUX(60)));
BEAST_EXPECT(expectLine(env, dan, A_BUX(none)));
BEAST_EXPECT(expectLine(env, dan, D_BUX(none)));
BEAST_EXPECT(expectHolding(env, ann, A_BUX(none)));
BEAST_EXPECT(expectHolding(env, ann, D_BUX(none)));
BEAST_EXPECT(expectHolding(env, bob, A_BUX(72)));
BEAST_EXPECT(expectHolding(env, bob, D_BUX(40)));
BEAST_EXPECT(expectHolding(env, cam, A_BUX(none)));
BEAST_EXPECT(expectHolding(env, cam, D_BUX(60)));
BEAST_EXPECT(expectHolding(env, dan, A_BUX(none)));
BEAST_EXPECT(expectHolding(env, dan, D_BUX(none)));
AMM ammBob(env, bob, A_BUX(30), D_BUX(30));
@@ -1234,12 +1234,12 @@ private:
BEAST_EXPECT(
ammBob.expectBalances(A_BUX(30), D_BUX(30), ammBob.tokens()));
BEAST_EXPECT(expectLine(env, ann, A_BUX(none)));
BEAST_EXPECT(expectLine(env, ann, D_BUX(0)));
BEAST_EXPECT(expectLine(env, cam, A_BUX(none)));
BEAST_EXPECT(expectLine(env, cam, D_BUX(60)));
BEAST_EXPECT(expectLine(env, dan, A_BUX(0)));
BEAST_EXPECT(expectLine(env, dan, D_BUX(none)));
BEAST_EXPECT(expectHolding(env, ann, A_BUX(none)));
BEAST_EXPECT(expectHolding(env, ann, D_BUX(0)));
BEAST_EXPECT(expectHolding(env, cam, A_BUX(none)));
BEAST_EXPECT(expectHolding(env, cam, D_BUX(60)));
BEAST_EXPECT(expectHolding(env, dan, A_BUX(0)));
BEAST_EXPECT(expectHolding(env, dan, D_BUX(none)));
}
}
@@ -1363,7 +1363,7 @@ private:
env(pay(gw, bob, USD(50)));
env.close();
BEAST_EXPECT(expectLine(env, bob, USD(50)));
BEAST_EXPECT(expectHolding(env, bob, USD(50)));
// Bob's offer should cross Alice's AMM
env(offer(bob, XRP(50), USD(50)));
@@ -1372,7 +1372,7 @@ private:
BEAST_EXPECT(
ammAlice.expectBalances(USD(1'050), XRP(1'000), ammAlice.tokens()));
BEAST_EXPECT(expectOffers(env, bob, 0));
BEAST_EXPECT(expectLine(env, bob, USD(0)));
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
}
void
@@ -1403,7 +1403,7 @@ private:
env(pay(gw, bob, USD(50)));
env.close();
BEAST_EXPECT(expectLine(env, bob, USD(50)));
BEAST_EXPECT(expectHolding(env, bob, USD(50)));
// Alice should not be able to create AMM without authorization.
{
@@ -1440,7 +1440,7 @@ private:
BEAST_EXPECT(
ammAlice.expectBalances(USD(1'050), XRP(1'000), ammAlice.tokens()));
BEAST_EXPECT(expectOffers(env, bob, 0));
BEAST_EXPECT(expectLine(env, bob, USD(0)));
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
}
void
@@ -1535,7 +1535,7 @@ private:
// AMM offer is 51.282052XRP/11AUD, 11AUD/1.1 = 10AUD to bob
BEAST_EXPECT(
ammCarol.expectBalances(XRP(51), AUD(40), ammCarol.tokens()));
BEAST_EXPECT(expectLine(env, bob, AUD(10)));
BEAST_EXPECT(expectHolding(env, bob, AUD(10)));
auto const result =
find_paths(env, alice, bob, Account(bob)["USD"](25));
@@ -1950,10 +1950,10 @@ private:
env(pay(alice, carol, USD(50)), path(~USD), sendmax(BTC(50)));
BEAST_EXPECT(expectLine(env, alice, BTC(50)));
BEAST_EXPECT(expectLine(env, bob, BTC(0)));
BEAST_EXPECT(expectLine(env, bob, USD(0)));
BEAST_EXPECT(expectLine(env, carol, USD(200)));
BEAST_EXPECT(expectHolding(env, alice, BTC(50)));
BEAST_EXPECT(expectHolding(env, bob, BTC(0)));
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
BEAST_EXPECT(expectHolding(env, carol, USD(200)));
BEAST_EXPECT(
ammBob.expectBalances(BTC(150), USD(100), ammBob.tokens()));
}
@@ -1974,10 +1974,10 @@ private:
env(pay(alice, carol, USD(50)), path(~XRP, ~USD), sendmax(BTC(50)));
BEAST_EXPECT(expectLine(env, alice, BTC(50)));
BEAST_EXPECT(expectLine(env, bob, BTC(0)));
BEAST_EXPECT(expectLine(env, bob, USD(0)));
BEAST_EXPECT(expectLine(env, carol, USD(200)));
BEAST_EXPECT(expectHolding(env, alice, BTC(50)));
BEAST_EXPECT(expectHolding(env, bob, BTC(0)));
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
BEAST_EXPECT(expectHolding(env, carol, USD(200)));
BEAST_EXPECT(ammBobBTC_XRP.expectBalances(
BTC(150), XRP(100), ammBobBTC_XRP.tokens()));
BEAST_EXPECT(ammBobXRP_USD.expectBalances(
@@ -2003,8 +2003,8 @@ private:
env, alice, xrpMinusFee(env, 10'000 - 50)));
BEAST_EXPECT(expectLedgerEntryRoot(
env, bob, XRP(10'000) - XRP(100) - ammCrtFee(env)));
BEAST_EXPECT(expectLine(env, bob, USD(0)));
BEAST_EXPECT(expectLine(env, carol, USD(200)));
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
BEAST_EXPECT(expectHolding(env, carol, USD(200)));
BEAST_EXPECT(
ammBob.expectBalances(XRP(150), USD(100), ammBob.tokens()));
}
@@ -2024,10 +2024,10 @@ private:
env(pay(alice, carol, XRP(50)), path(~XRP), sendmax(USD(50)));
BEAST_EXPECT(expectLine(env, alice, USD(50)));
BEAST_EXPECT(expectHolding(env, alice, USD(50)));
BEAST_EXPECT(expectLedgerEntryRoot(
env, bob, XRP(10'000) - XRP(150) - ammCrtFee(env)));
BEAST_EXPECT(expectLine(env, bob, USD(0)));
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRP(10'000 + 50)));
BEAST_EXPECT(
ammBob.expectBalances(USD(150), XRP(100), ammBob.tokens()));
@@ -2209,7 +2209,7 @@ private:
sendmax(USD(0.4)),
txflags(tfNoRippleDirect | tfPartialPayment));
BEAST_EXPECT(expectLine(env, carol, EUR(1)));
BEAST_EXPECT(expectHolding(env, carol, EUR(1)));
BEAST_EXPECT(ammBob.expectBalances(
USD(8.4), XRPAmount{20}, ammBob.tokens()));
}
@@ -2244,7 +2244,7 @@ private:
// alice buys 107.1428USD with 120GBP and pays 25% tr fee on 120GBP
// 1,000 - 120*1.25 = 850GBP
BEAST_EXPECT(expectLine(env, alice, GBP(850)));
BEAST_EXPECT(expectHolding(env, alice, GBP(850)));
if (!features[fixAMMv1_1])
{
// 120GBP is swapped in for 107.1428USD
@@ -2262,7 +2262,7 @@ private:
}
// 25% of 85.7142USD is paid in tr fee
// 85.7142*1.25 = 107.1428USD
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, carol, STAmount(USD, UINT64_C(1'085'714285714286), -12)));
}
@@ -2294,10 +2294,10 @@ private:
// alice buys 120EUR with 120GBP via the offer
// and pays 25% tr fee on 120GBP
// 1,000 - 120*1.25 = 850GBP
BEAST_EXPECT(expectLine(env, alice, GBP(850)));
BEAST_EXPECT(expectHolding(env, alice, GBP(850)));
// consumed offer is 120GBP/120EUR
// ed doesn't pay tr fee
BEAST_EXPECT(expectLine(env, ed, EUR(880), GBP(1'120)));
BEAST_EXPECT(expectHolding(env, ed, EUR(880), GBP(1'120)));
BEAST_EXPECT(
expectOffers(env, ed, 1, {Amounts{GBP(880), EUR(880)}}));
// 25% on 96EUR is paid in tr fee 96*1.25 = 120EUR
@@ -2307,7 +2307,7 @@ private:
STAmount{USD, UINT64_C(912'4087591240876), -13},
amm.tokens()));
// 25% on 70.0729USD is paid in tr fee 70.0729*1.25 = 87.5912USD
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, carol, STAmount(USD, UINT64_C(1'070'07299270073), -11)));
}
{
@@ -2333,7 +2333,7 @@ private:
txflags(tfNoRippleDirect | tfPartialPayment));
env.close();
BEAST_EXPECT(expectLine(env, alice, GBP(850)));
BEAST_EXPECT(expectHolding(env, alice, GBP(850)));
if (!features[fixAMMv1_1])
{
// alice buys 107.1428EUR with 120GBP and pays 25% tr fee on
@@ -2367,7 +2367,7 @@ private:
amm2.tokens()));
}
// 25% on 63.1578USD is paid in tr fee 63.1578*1.25 = 78.9473USD
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, carol, STAmount(USD, UINT64_C(1'063'157894736842), -12)));
}
{
@@ -2386,7 +2386,7 @@ private:
BEAST_EXPECT(
amm.expectBalances(USD(1'100), EUR(1'000), amm.tokens()));
// alice pays 25% tr fee on 100USD 1100-100*1.25 = 975USD
BEAST_EXPECT(expectLine(env, alice, USD(975), EUR(1'200)));
BEAST_EXPECT(expectHolding(env, alice, USD(975), EUR(1'200)));
BEAST_EXPECT(expectOffers(env, alice, 0));
}
@@ -2416,7 +2416,7 @@ private:
// alice buys 125USD with 142.8571GBP and pays 25% tr fee
// on 142.8571GBP
// 1,000 - 142.8571*1.25 = 821.4285GBP
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, alice, STAmount(GBP, UINT64_C(821'4285714285712), -13)));
// 142.8571GBP is swapped in for 125USD
BEAST_EXPECT(amm.expectBalances(
@@ -2425,7 +2425,7 @@ private:
amm.tokens()));
// 25% on 100USD is paid in tr fee
// 100*1.25 = 125USD
BEAST_EXPECT(expectLine(env, carol, USD(1'100)));
BEAST_EXPECT(expectHolding(env, carol, USD(1'100)));
}
{
// Payment via AMM with limit quality, deliver less
@@ -2456,7 +2456,7 @@ private:
// alice buys 28.125USD with 24GBP and pays 25% tr fee
// on 24GBP
// 1,200 - 24*1.25 = 1,170GBP
BEAST_EXPECT(expectLine(env, alice, GBP(1'170)));
BEAST_EXPECT(expectHolding(env, alice, GBP(1'170)));
// 24GBP is swapped in for 28.125USD
BEAST_EXPECT(amm.expectBalances(
GBP(1'024), USD(1'171.875), amm.tokens()));
@@ -2466,7 +2466,7 @@ private:
// alice buys 28.125USD with 24GBP and pays 25% tr fee
// on 24GBP
// 1,200 - 24*1.25 =~ 1,170GBP
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
alice,
STAmount{GBP, UINT64_C(1'169'999999999999), -12}));
@@ -2478,7 +2478,7 @@ private:
}
// 25% on 22.5USD is paid in tr fee
// 22.5*1.25 = 28.125USD
BEAST_EXPECT(expectLine(env, carol, USD(1'222.5)));
BEAST_EXPECT(expectHolding(env, carol, USD(1'222.5)));
}
{
// Payment via offer and AMM with limit quality, deliver less
@@ -2513,13 +2513,13 @@ private:
// alice buys 70.4210EUR with 70.4210GBP via the offer
// and pays 25% tr fee on 70.4210GBP
// 1,400 - 70.4210*1.25 = 1400 - 88.0262 = 1311.9736GBP
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
alice,
STAmount{GBP, UINT64_C(1'311'973684210527), -12}));
// ed doesn't pay tr fee, the balances reflect consumed offer
// 70.4210GBP/70.4210EUR
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
ed,
STAmount{EUR, UINT64_C(1'329'578947368421), -12},
@@ -2543,13 +2543,13 @@ private:
// alice buys 70.4210EUR with 70.4210GBP via the offer
// and pays 25% tr fee on 70.4210GBP
// 1,400 - 70.4210*1.25 = 1400 - 88.0262 = 1311.9736GBP
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
alice,
STAmount{GBP, UINT64_C(1'311'973684210525), -12}));
// ed doesn't pay tr fee, the balances reflect consumed offer
// 70.4210GBP/70.4210EUR
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
ed,
STAmount{EUR, UINT64_C(1'329'57894736842), -11},
@@ -2569,7 +2569,7 @@ private:
amm.tokens()));
}
// 25% on 59.7321USD is paid in tr fee 59.7321*1.25 = 74.6651USD
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, carol, STAmount(USD, UINT64_C(1'459'732142857143), -12)));
}
{
@@ -2605,7 +2605,7 @@ private:
// alice buys 53.3322EUR with 56.3368GBP via the amm
// and pays 25% tr fee on 56.3368GBP
// 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
alice,
STAmount{GBP, UINT64_C(1'329'578947368421), -12}));
@@ -2622,7 +2622,7 @@ private:
// alice buys 53.3322EUR with 56.3368GBP via the amm
// and pays 25% tr fee on 56.3368GBP
// 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
alice,
STAmount{GBP, UINT64_C(1'329'57894736842), -11}));
@@ -2636,7 +2636,7 @@ private:
}
// 25% on 42.6658EUR is paid in tr fee 42.6658*1.25 = 53.3322EUR
// 42.6658EUR/59.7321USD
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
ed,
STAmount{USD, UINT64_C(1'340'267857142857), -12},
@@ -2649,7 +2649,7 @@ private:
STAmount{EUR, UINT64_C(957'3341836734693), -13},
STAmount{USD, UINT64_C(1'340'267857142857), -12}}}));
// 25% on 47.7857USD is paid in tr fee 47.7857*1.25 = 59.7321USD
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, carol, STAmount(USD, UINT64_C(1'447'785714285714), -12)));
}
{
@@ -2683,7 +2683,7 @@ private:
// alice buys 53.3322EUR with 107.5308GBP
// 25% on 86.0246GBP is paid in tr fee
// 1,400 - 86.0246*1.25 = 1400 - 107.5308 = 1229.4691GBP
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
alice,
STAmount{GBP, UINT64_C(1'292'469135802469), -12}));
@@ -2704,7 +2704,7 @@ private:
// alice buys 53.3322EUR with 107.5308GBP
// 25% on 86.0246GBP is paid in tr fee
// 1,400 - 86.0246*1.25 = 1400 - 107.5308 = 1229.4691GBP
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
alice,
STAmount{GBP, UINT64_C(1'292'469135802466), -12}));
@@ -2721,7 +2721,7 @@ private:
amm2.tokens()));
}
// 25% on 66.7432USD is paid in tr fee 66.7432*1.25 = 83.4291USD
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, carol, STAmount(USD, UINT64_C(1'466'743295019157), -12)));
}
{
@@ -2778,7 +2778,7 @@ private:
amm2.tokens()));
}
// 25% on 81.1111USD is paid in tr fee 81.1111*1.25 = 101.3888USD
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, carol, STAmount{USD, UINT64_C(1'481'111111111111), -12}));
}
}
@@ -2808,7 +2808,7 @@ private:
BEAST_EXPECT(
ammBob.expectBalances(XRP(1'050), USD(1'000), ammBob.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(2'050)));
BEAST_EXPECT(expectHolding(env, carol, USD(2'050)));
BEAST_EXPECT(expectOffers(env, bob, 1, {{{XRP(100), USD(50)}}}));
}
}

View File

@@ -97,8 +97,8 @@ private:
AMM ammAlice(env, alice, USD(20'000), BTC(0.5));
BEAST_EXPECT(ammAlice.expectBalances(
USD(20'000), BTC(0.5), IOUAmount{100, 0}));
BEAST_EXPECT(expectLine(env, alice, USD(0)));
BEAST_EXPECT(expectLine(env, alice, BTC(0)));
BEAST_EXPECT(expectHolding(env, alice, USD(0)));
BEAST_EXPECT(expectHolding(env, alice, BTC(0)));
}
// Require authorization is set, account is authorized
@@ -1394,7 +1394,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
// 30,000 less deposited 1,000
BEAST_EXPECT(expectLine(env, carol, USD(29'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(29'000)));
// 30,000 less deposited 1,000 and 10 drops tx fee
BEAST_EXPECT(expectLedgerEntryRoot(
env, carol, XRPAmount{29'000'000'000 - baseFee}));
@@ -1449,7 +1449,8 @@ private:
IOUAmount{1, 7} + newLPTokens));
// 30,000 less deposited depositUSD
BEAST_EXPECT(expectLine(env, carol, USD(30'000) - depositUSD));
BEAST_EXPECT(
expectHolding(env, carol, USD(30'000) - depositUSD));
// 30,000 less deposited depositXRP and 10 drops tx fee
BEAST_EXPECT(expectLedgerEntryRoot(
env, carol, XRP(30'000) - depositXRP - txfee(env, 1)));
@@ -1553,15 +1554,15 @@ private:
AMM ammAlice(env, alice, USD(20'000), BTC(0.5));
BEAST_EXPECT(ammAlice.expectBalances(
USD(20'000), BTC(0.5), IOUAmount{100, 0}));
BEAST_EXPECT(expectLine(env, alice, USD(0)));
BEAST_EXPECT(expectLine(env, alice, BTC(0)));
BEAST_EXPECT(expectHolding(env, alice, USD(0)));
BEAST_EXPECT(expectHolding(env, alice, BTC(0)));
fund(env, gw, {carol}, {USD(2'000), BTC(0.05)}, Fund::Acct);
// no transfer fee on deposit
ammAlice.deposit(carol, 10);
BEAST_EXPECT(ammAlice.expectBalances(
USD(22'000), BTC(0.55), IOUAmount{110, 0}));
BEAST_EXPECT(expectLine(env, carol, USD(0)));
BEAST_EXPECT(expectLine(env, carol, BTC(0)));
BEAST_EXPECT(expectHolding(env, carol, USD(0)));
BEAST_EXPECT(expectHolding(env, carol, BTC(0)));
}
// Tiny deposits
@@ -2281,7 +2282,7 @@ private:
BEAST_EXPECT(
ammAlice.expectLPTokens(carol, IOUAmount{1'000'000, 0}));
// 30,000 less deposited 1,000
BEAST_EXPECT(expectLine(env, carol, USD(29'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(29'000)));
// 30,000 less deposited 1,000 and 10 drops tx fee
BEAST_EXPECT(expectLedgerEntryRoot(
env, carol, XRPAmount{29'000'000'000 - baseFee}));
@@ -2290,7 +2291,7 @@ private:
ammAlice.withdraw(carol, 1'000'000);
BEAST_EXPECT(
ammAlice.expectLPTokens(carol, IOUAmount(beast::Zero())));
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
BEAST_EXPECT(expectLedgerEntryRoot(
env, carol, XRPAmount{30'000'000'000 - 2 * baseFee}));
});
@@ -2525,22 +2526,22 @@ private:
AMM ammAlice(env, alice, USD(20'000), BTC(0.5));
BEAST_EXPECT(ammAlice.expectBalances(
USD(20'000), BTC(0.5), IOUAmount{100, 0}));
BEAST_EXPECT(expectLine(env, alice, USD(0)));
BEAST_EXPECT(expectLine(env, alice, BTC(0)));
BEAST_EXPECT(expectHolding(env, alice, USD(0)));
BEAST_EXPECT(expectHolding(env, alice, BTC(0)));
fund(env, gw, {carol}, {USD(2'000), BTC(0.05)}, Fund::Acct);
// no transfer fee on deposit
ammAlice.deposit(carol, 10);
BEAST_EXPECT(ammAlice.expectBalances(
USD(22'000), BTC(0.55), IOUAmount{110, 0}));
BEAST_EXPECT(expectLine(env, carol, USD(0)));
BEAST_EXPECT(expectLine(env, carol, BTC(0)));
BEAST_EXPECT(expectHolding(env, carol, USD(0)));
BEAST_EXPECT(expectHolding(env, carol, BTC(0)));
// no transfer fee on withdraw
ammAlice.withdraw(carol, 10);
BEAST_EXPECT(ammAlice.expectBalances(
USD(20'000), BTC(0.5), IOUAmount{100, 0}));
BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0, 0}));
BEAST_EXPECT(expectLine(env, carol, USD(2'000)));
BEAST_EXPECT(expectLine(env, carol, BTC(0.05)));
BEAST_EXPECT(expectHolding(env, carol, USD(2'000)));
BEAST_EXPECT(expectHolding(env, carol, BTC(0.05)));
}
// Tiny withdraw
@@ -3527,7 +3528,7 @@ private:
// Alice doesn't have anymore lp tokens
env(amm.bid({.account = alice, .bidMin = 500}));
BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{500}));
BEAST_EXPECT(expectLine(env, alice, STAmount{lpIssue, 0}));
BEAST_EXPECT(expectHolding(env, alice, STAmount{lpIssue, 0}));
// But trades with the discounted fee since she still owns the slot.
// Alice pays 10011 drops in fees
env(pay(alice, bob, USD(10)), path(~USD), sendmax(XRP(11)));
@@ -3790,7 +3791,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
// Initial balance 30,000 + 100
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
// Initial balance 30,000 - 100(sendmax) - 10(tx fee)
BEAST_EXPECT(expectLedgerEntryRoot(
env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
@@ -3810,7 +3811,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
// Initial balance 30,000 + 100
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
// Initial balance 30,000 - 100(sendmax) - 10(tx fee)
BEAST_EXPECT(expectLedgerEntryRoot(
env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
@@ -3831,7 +3832,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
// Initial balance 30,000 + 100
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
// Initial balance 30,000 - 100(sendmax) - 10(tx fee)
BEAST_EXPECT(expectLedgerEntryRoot(
env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
@@ -3857,7 +3858,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'010), USD(10'000), ammAlice.tokens()));
// Initial balance 30,000 + 10(limited by limitQuality)
BEAST_EXPECT(expectLine(env, carol, USD(30'010)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'010)));
// Initial balance 30,000 - 10(limited by limitQuality) - 10(tx
// fee)
BEAST_EXPECT(expectLedgerEntryRoot(
@@ -3897,7 +3898,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'010), USD(10'000), ammAlice.tokens()));
// 10USD - 10% transfer fee
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{USD, UINT64_C(30'009'09090909091), -11}));
@@ -3984,7 +3985,7 @@ private:
BEAST_EXPECT(expectOffers(env, alice, 1, {{expectedAmounts}}));
}
// Initial 30,000 + 100
BEAST_EXPECT(expectLine(env, carol, STAmount{USD, 30'100}));
BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, 30'100}));
// Initial 1,000 - 30082730(AMM pool) - 70798251(offer) - 10(tx fee)
BEAST_EXPECT(expectLedgerEntryRoot(
env,
@@ -4027,7 +4028,7 @@ private:
STAmount(EUR, UINT64_C(49'98750312422), -11),
STAmount(USD, UINT64_C(49'98750312422), -11)}}}));
// Initial 30,000 + 99.99999999999
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{USD, UINT64_C(30'099'99999999999), -11}));
@@ -4061,7 +4062,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
// Initial 30,000 + 200
BEAST_EXPECT(expectLine(env, carol, USD(30'200)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'200)));
}
else
{
@@ -4069,7 +4070,7 @@ private:
XRP(10'100),
STAmount(USD, UINT64_C(10'000'00000000001), -11),
ammAlice.tokens()));
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount(USD, UINT64_C(30'199'99999999999), -11)));
@@ -4104,7 +4105,7 @@ private:
env.close();
BEAST_EXPECT(ammAlice.expectBalances(
XRP(1'050), USD(1'000), ammAlice.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(2'200)));
BEAST_EXPECT(expectHolding(env, carol, USD(2'200)));
BEAST_EXPECT(expectOffers(env, bob, 0));
}
@@ -4118,7 +4119,7 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100), USD(10'000), ammAlice.tokens()));
// Initial 1,000 + 100
BEAST_EXPECT(expectLine(env, bob, USD(1'100)));
BEAST_EXPECT(expectHolding(env, bob, USD(1'100)));
// Initial 30,000 - 100(offer) - 10(tx fee)
BEAST_EXPECT(expectLedgerEntryRoot(
env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
@@ -4145,9 +4146,9 @@ private:
BEAST_EXPECT(ammAlice.expectBalances(
GBP(1'100), EUR(1'000), ammAlice.tokens()));
// Initial 30,000 - 100(offer) - 25% transfer fee
BEAST_EXPECT(expectLine(env, carol, GBP(29'875)));
BEAST_EXPECT(expectHolding(env, carol, GBP(29'875)));
// Initial 30,000 + 100(offer)
BEAST_EXPECT(expectLine(env, carol, EUR(30'100)));
BEAST_EXPECT(expectHolding(env, carol, EUR(30'100)));
BEAST_EXPECT(expectOffers(env, bob, 0));
},
{{GBP(1'000), EUR(1'100)}},
@@ -4285,12 +4286,12 @@ private:
// = 58.825 = ~29941.17
// carol bought ~72.93EUR at the cost of ~70.68GBP
// the offer is partially consumed
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{GBP, UINT64_C(29'941'16770347333), -11}));
// Initial 30,000 + ~49.3(offers = 39.3(AMM) + 10(LOB))
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{EUR, UINT64_C(30'049'31517120716), -11}));
@@ -4324,20 +4325,20 @@ private:
// = 88.35 = ~29911.64
// carol bought ~72.93EUR at the cost of ~70.68GBP
// the offer is partially consumed
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{GBP, UINT64_C(29'911'64396400896), -11}));
// Initial 30,000 + ~72.93(offers = 62.93(AMM) + 10(LOB))
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{EUR, UINT64_C(30'072'93416277865), -11}));
}
// Initial 2000 + 10 = 2010
BEAST_EXPECT(expectLine(env, bob, GBP(2'010)));
BEAST_EXPECT(expectHolding(env, bob, GBP(2'010)));
// Initial 2000 - 10 * 1.25 = 1987.5
BEAST_EXPECT(expectLine(env, ed, EUR(1'987.5)));
BEAST_EXPECT(expectHolding(env, ed, EUR(1'987.5)));
},
{{GBP(1'000), EUR(1'100)}},
0,
@@ -4363,8 +4364,8 @@ private:
env.close();
BEAST_EXPECT(ammAlice.expectBalances(
GBP(1'100), EUR(1'000), ammAlice.tokens()));
BEAST_EXPECT(expectLine(env, bob, GBP(75)));
BEAST_EXPECT(expectLine(env, carol, EUR(30'080)));
BEAST_EXPECT(expectHolding(env, bob, GBP(75)));
BEAST_EXPECT(expectHolding(env, carol, EUR(30'080)));
},
{{GBP(1'000), EUR(1'100)}},
0,
@@ -4401,12 +4402,12 @@ private:
sendmax(CAN(195.3125)),
txflags(tfPartialPayment));
env.close();
BEAST_EXPECT(expectLine(env, bob, CAN(0)));
BEAST_EXPECT(expectLine(env, dan, CAN(356.25), GBP(43.75)));
BEAST_EXPECT(expectHolding(env, bob, CAN(0)));
BEAST_EXPECT(expectHolding(env, dan, CAN(356.25), GBP(43.75)));
BEAST_EXPECT(ammAlice.expectBalances(
GBP(10'125), EUR(10'000), ammAlice.tokens()));
BEAST_EXPECT(expectLine(env, ed, EUR(300), USD(100)));
BEAST_EXPECT(expectLine(env, carol, USD(80)));
BEAST_EXPECT(expectHolding(env, ed, EUR(300), USD(100)));
BEAST_EXPECT(expectHolding(env, carol, USD(80)));
},
{{GBP(10'000), EUR(10'125)}},
0,
@@ -4523,7 +4524,7 @@ private:
BEAST_EXPECT(btc_usd.expectBalances(
BTC(10'100), USD(10'000), btc_usd.tokens()));
BEAST_EXPECT(expectLine(env, carol, USD(300)));
BEAST_EXPECT(expectHolding(env, carol, USD(300)));
}
// Dependent AMM
@@ -4594,7 +4595,7 @@ private:
STAmount{EUR, UINT64_C(10'917'2945958102), -10},
eth_eur.tokens()));
}
BEAST_EXPECT(expectLine(env, carol, USD(300)));
BEAST_EXPECT(expectHolding(env, carol, USD(300)));
}
// AMM offers limit
@@ -4620,7 +4621,7 @@ private:
XRP(10'030),
STAmount{USD, UINT64_C(9'970'089730807577), -12},
ammAlice.tokens()));
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{USD, UINT64_C(30'029'91026919241), -11}));
@@ -4631,7 +4632,7 @@ private:
XRP(10'030),
STAmount{USD, UINT64_C(9'970'089730807827), -12},
ammAlice.tokens()));
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{USD, UINT64_C(30'029'91026919217), -11}));
@@ -4663,14 +4664,14 @@ private:
if (!features[fixAMMv1_1])
{
// Carol gets ~100USD
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{USD, UINT64_C(30'099'99999999999), -11}));
}
else
{
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
}
BEAST_EXPECT(expectOffers(
env,
@@ -4717,7 +4718,7 @@ private:
1,
{{{XRPAmount{50'074'628},
STAmount{USD, UINT64_C(50'07512950697), -11}}}}));
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
}
}
@@ -4809,11 +4810,11 @@ private:
env(offer(carol, STAmount{token2, 100}, STAmount{token1, 100}));
env.close();
BEAST_EXPECT(
expectLine(env, alice, STAmount{token1, 10'000'100}) &&
expectLine(env, alice, STAmount{token2, 9'999'900}));
expectHolding(env, alice, STAmount{token1, 10'000'100}) &&
expectHolding(env, alice, STAmount{token2, 9'999'900}));
BEAST_EXPECT(
expectLine(env, carol, STAmount{token2, 1'000'100}) &&
expectLine(env, carol, STAmount{token1, 999'900}));
expectHolding(env, carol, STAmount{token2, 1'000'100}) &&
expectHolding(env, carol, STAmount{token1, 999'900}));
BEAST_EXPECT(
expectOffers(env, alice, 0) && expectOffers(env, carol, 0));
});
@@ -5034,7 +5035,7 @@ private:
BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000}));
ammAlice.withdrawAll(carol, USD(3'000));
BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
// Set fee to 1%
ammAlice.vote(alice, 1'000);
BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
@@ -5043,12 +5044,12 @@ private:
ammAlice.deposit(carol, USD(3'000));
BEAST_EXPECT(ammAlice.expectLPTokens(
carol, IOUAmount{994'981155689671, -12}));
BEAST_EXPECT(expectLine(env, carol, USD(27'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(27'000)));
// Set fee to 0
ammAlice.vote(alice, 0);
ammAlice.withdrawAll(carol, USD(0));
// Carol gets back less than the original deposit
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{USD, UINT64_C(29'994'96220068281), -11}));
@@ -5109,13 +5110,13 @@ private:
ammAlice.deposit(carol, USD(3'000));
BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000}));
BEAST_EXPECT(expectLine(env, carol, USD(27'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(27'000)));
// Set fee to 1%
ammAlice.vote(alice, 1'000);
BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
// Single withdrawal. Carol gets ~5USD less than deposited.
ammAlice.withdrawAll(carol, USD(0));
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{USD, UINT64_C(29'994'97487437186), -11}));
@@ -5189,9 +5190,9 @@ private:
{USD(1'000), EUR(1'000)},
Fund::Acct);
// Alice contributed 1010EUR and 1000USD to the pool
BEAST_EXPECT(expectLine(env, alice, EUR(28'990)));
BEAST_EXPECT(expectLine(env, alice, USD(29'000)));
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
BEAST_EXPECT(expectHolding(env, alice, EUR(28'990)));
BEAST_EXPECT(expectHolding(env, alice, USD(29'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
// Carol pays to Alice with no fee
env(pay(carol, alice, EUR(10)),
path(~EUR),
@@ -5199,9 +5200,9 @@ private:
txflags(tfNoRippleDirect));
env.close();
// Alice has 10EUR more and Carol has 10USD less
BEAST_EXPECT(expectLine(env, alice, EUR(29'000)));
BEAST_EXPECT(expectLine(env, alice, USD(29'000)));
BEAST_EXPECT(expectLine(env, carol, USD(29'990)));
BEAST_EXPECT(expectHolding(env, alice, EUR(29'000)));
BEAST_EXPECT(expectHolding(env, alice, USD(29'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(29'990)));
// Set fee to 1%
ammAlice.vote(alice, 1'000);
@@ -5213,10 +5214,10 @@ private:
txflags(tfNoRippleDirect));
env.close();
// Bob sends 10.1~EUR to pay 10USD
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, bob, STAmount{EUR, UINT64_C(989'8989898989899), -13}));
// Carol got 10USD
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
BEAST_EXPECT(ammAlice.expectBalances(
USD(1'000),
STAmount{EUR, UINT64_C(1'010'10101010101), -11},
@@ -5233,8 +5234,8 @@ private:
// No fee
env(offer(carol, EUR(10), USD(10)));
env.close();
BEAST_EXPECT(expectLine(env, carol, USD(29'990)));
BEAST_EXPECT(expectLine(env, carol, EUR(30'010)));
BEAST_EXPECT(expectHolding(env, carol, USD(29'990)));
BEAST_EXPECT(expectHolding(env, carol, EUR(30'010)));
// Change pool composition back
env(offer(carol, USD(10), EUR(10)));
env.close();
@@ -5245,11 +5246,11 @@ private:
env.close();
// Alice gets fewer ~4.97EUR for ~5.02USD, the difference goes
// to the pool
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{USD, UINT64_C(29'995'02512562814), -11}));
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{EUR, UINT64_C(30'004'97487437186), -11}));
@@ -5299,16 +5300,16 @@ private:
path(~USD),
sendmax(EUR(15)),
txflags(tfNoRippleDirect));
BEAST_EXPECT(expectLine(env, ed, USD(2'010)));
BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
if (!features[fixAMMv1_1])
{
BEAST_EXPECT(expectLine(env, bob, EUR(1'990)));
BEAST_EXPECT(expectHolding(env, bob, EUR(1'990)));
BEAST_EXPECT(ammAlice.expectBalances(
USD(1'000), EUR(1'005), ammAlice.tokens()));
}
else
{
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env, bob, STAmount(EUR, UINT64_C(1989'999999999999), -12)));
BEAST_EXPECT(ammAlice.expectBalances(
USD(1'000),
@@ -5336,10 +5337,10 @@ private:
path(~USD),
sendmax(EUR(15)),
txflags(tfNoRippleDirect));
BEAST_EXPECT(expectLine(env, ed, USD(2'010)));
BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
if (!features[fixAMMv1_1])
{
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
bob,
STAmount{EUR, UINT64_C(1'989'987453007618), -12}));
@@ -5350,7 +5351,7 @@ private:
}
else
{
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
bob,
STAmount{EUR, UINT64_C(1'989'987453007628), -12}));
@@ -5381,8 +5382,8 @@ private:
path(~USD),
sendmax(EUR(15)),
txflags(tfNoRippleDirect));
BEAST_EXPECT(expectLine(env, ed, USD(2'010)));
BEAST_EXPECT(expectLine(env, bob, EUR(1'990)));
BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
BEAST_EXPECT(expectHolding(env, bob, EUR(1'990)));
BEAST_EXPECT(ammAlice.expectBalances(
USD(1'005), EUR(1'000), ammAlice.tokens()));
BEAST_EXPECT(expectOffers(env, carol, 0));
@@ -5408,8 +5409,8 @@ private:
path(~USD),
sendmax(EUR(15)),
txflags(tfNoRippleDirect));
BEAST_EXPECT(expectLine(env, ed, USD(2'010)));
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
BEAST_EXPECT(expectHolding(
env, bob, STAmount{EUR, UINT64_C(1'989'993923296712), -12}));
BEAST_EXPECT(ammAlice.expectBalances(
USD(1'004),
@@ -5480,47 +5481,47 @@ private:
else
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000), USD(10'000), IOUAmount{10'000'000}));
BEAST_EXPECT(expectLine(env, ben, USD(1'500'000)));
BEAST_EXPECT(expectLine(env, simon, USD(1'500'000)));
BEAST_EXPECT(expectLine(env, chris, USD(1'500'000)));
BEAST_EXPECT(expectLine(env, dan, USD(1'500'000)));
BEAST_EXPECT(expectHolding(env, ben, USD(1'500'000)));
BEAST_EXPECT(expectHolding(env, simon, USD(1'500'000)));
BEAST_EXPECT(expectHolding(env, chris, USD(1'500'000)));
BEAST_EXPECT(expectHolding(env, dan, USD(1'500'000)));
if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
carol,
STAmount{USD, UINT64_C(30'000'00000000001), -11}));
else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
else
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
BEAST_EXPECT(expectLine(env, ed, USD(1'500'000)));
BEAST_EXPECT(expectLine(env, paul, USD(1'500'000)));
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
BEAST_EXPECT(expectHolding(env, ed, USD(1'500'000)));
BEAST_EXPECT(expectHolding(env, paul, USD(1'500'000)));
if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
nataly,
STAmount{USD, UINT64_C(1'500'000'000000002), -9}));
else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
nataly,
STAmount{USD, UINT64_C(1'500'000'000000005), -9}));
else
BEAST_EXPECT(expectLine(env, nataly, USD(1'500'000)));
BEAST_EXPECT(expectHolding(env, nataly, USD(1'500'000)));
ammAlice.withdrawAll(alice);
BEAST_EXPECT(!ammAlice.ammExists());
if (!features[fixAMMv1_1])
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
alice,
STAmount{USD, UINT64_C(30'000'0000000013), -10}));
else if (features[fixAMMv1_3])
BEAST_EXPECT(expectLine(
BEAST_EXPECT(expectHolding(
env,
alice,
STAmount{USD, UINT64_C(30'000'0000000003), -10}));
else
BEAST_EXPECT(expectLine(env, alice, USD(30'000)));
BEAST_EXPECT(expectHolding(env, alice, USD(30'000)));
// alice XRP balance is 30,000initial - 50 ammcreate fee -
// 10drops fee
BEAST_EXPECT(
@@ -5883,7 +5884,7 @@ private:
BEAST_EXPECT(amm->expectBalances(
USD(1'000), ETH(1'000), amm->tokens()));
}
BEAST_EXPECT(expectLine(env, bob, USD(2'100)));
BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
q[i] = Quality(Amounts{
ETH(2'000) - env.balance(carol, ETH),
env.balance(bob, USD) - USD(2'000)});
@@ -6056,7 +6057,7 @@ private:
-13}}}}));
}
}
BEAST_EXPECT(expectLine(env, bob, USD(2'100)));
BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
q[i] = Quality(Amounts{
ETH(2'000) - env.balance(carol, ETH),
env.balance(bob, USD) - USD(2'000)});
@@ -6203,7 +6204,7 @@ private:
sendmax(ETH(600)));
env.close();
BEAST_EXPECT(expectLine(env, bob, USD(2'100)));
BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
if (i == 2 && !features[fixAMMv1_1])
{
@@ -7484,7 +7485,7 @@ private:
using namespace test::jtx;
auto const testCase = [&](std::string suffix, FeatureBitset features) {
testcase("Failed pseudo-account allocation " + suffix);
testcase("Fail pseudo-account allocation " + suffix);
std::string logs;
Env env{*this, features, std::make_unique<CaptureLogs>(&logs)};
env.fund(XRP(30'000), gw, alice);

View File

@@ -19,10 +19,9 @@
#include <test/jtx.h>
#include <xrpld/app/misc/CredentialHelpers.h>
#include <xrpld/ledger/ApplyViewImpl.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/ledger/ApplyViewImpl.h>
#include <xrpl/ledger/CredentialHelpers.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h>
@@ -34,15 +33,6 @@
namespace ripple {
namespace test {
static inline bool
checkVL(
std::shared_ptr<SLE const> const& sle,
SField const& field,
std::string const& expected)
{
return strHex(expected) == strHex(sle->getFieldVL(field));
}
struct Credentials_test : public beast::unit_test::suite
{
void

View File

@@ -27,14 +27,6 @@
namespace ripple {
namespace test {
bool
checkVL(Slice const& result, std::string expected)
{
Serializer s;
s.addRaw(result);
return s.getString() == expected;
}
struct DID_test : public beast::unit_test::suite
{
void

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