Compare commits

...
113 Commits
Author SHA1 Message Date
Richard Holland 6b49032436 feature count 2025-04-15 20:19:44 +10:00
RichardAH 7a62559da9 Merge branch 'dev' into remarks 2025-04-15 20:09:33 +10:00
tequ 59e334c099 fixRewardClaimFlags (#487) 2025-04-15 20:08:19 +10:00
Denis Angell d7dd6196e8 fix test 2025-04-15 11:06:56 +02:00
Richard Holland d3cfd46af3 add 1 to feature count 2025-04-15 17:15:40 +10:00
Richard Holland 94fab7d58b tx flags 2025-04-15 14:36:07 +10:00
Richard Holland 53b3b543a7 cleanup 2025-04-15 14:14:07 +10:00
Richard Holland 69e72ecb91 ensure numerically 0 value blob isnt a deletion 2025-04-15 13:59:42 +10:00
Richard Holland 98a33d11e0 change tem code 2025-04-15 13:54:59 +10:00
Richard Holland c908018647 re-order check 2025-04-15 13:51:45 +10:00
RichardAH c6ddd6d2c4 Merge branch 'dev' into remarks 2025-04-15 13:42:28 +10:00
tequ 9018596532 HookCanEmit (#392) 2025-04-15 13:32:35 +10:00
Niq Dudfield b827f0170d feat(catalogue): add cli commands and fix file_size (#486)
* feat(catalogue): add cli commands and fix file_size

* feat(catalogue): add cli commands and fix file_size

* feat(catalogue): fix tests

* feat(catalogue): fix tests

* feat(catalogue): use formatBytesIEC

* feat: add file_size_estimated

* feat: add file_size_estimated

* feat: add file_size_estimated
2025-04-15 08:50:15 +10:00
tequ e4b7e8f0f2 Update sfcodes script (#479) 2025-04-10 09:44:31 +10:00
tequ 1485078d91 Update CHooks build script (#465) 2025-04-09 20:22:34 +10:00
tequ 6625d2be92 Add xpop_slot test (#470) 2025-04-09 20:20:23 +10:00
RichardAH 78906ee086 Merge branch 'dev' into remarks 2025-04-09 17:14:04 +10:00
tequ 2fb5c92140 feat: Run unittests in parallel with Github Actions (#483)
Implement parallel execution for unit tests using Github Actions to improve CI pipeline efficiency and reduce build times.
2025-04-04 19:32:47 +02:00
Niq Dudfield c4b5ae3787 Fix missing includes in Catalogue.cpp for non-unity builds (#485) 2025-04-04 12:53:45 +10:00
Niq Dudfield d546d761ce Fix using using Status with rpcError (#484) 2025-04-01 21:00:13 +10:00
RichardAH e84a36867b Catalogue (#443) 2025-04-01 16:47:48 +10:00
Niq Dudfield 0b675465b4 Fix ServerDefinitions_test regression intro in #475 (#477) 2025-03-19 12:32:27 +10:00
Niq Dudfield d088ad61a9 Prevent dangling reference in getHash() (#475)
Replace temporary uint256 with static variable when returning fallback hash
to avoid returning a const reference to a local temporary object.
2025-03-18 18:37:18 +10:00
Niq Dudfield ef77b02d7f CI Release Builder (#455) 2025-03-11 13:19:28 +01:00
RichardAH 7385828983 Touch Amendment (#294) 2025-03-06 08:25:42 +01:00
Niq Dudfield 88b01514c1 fix: remove negative rate test failing on MacOS (#452) 2025-03-03 13:12:13 +01:00
Denis AngellandNiq Dudfield aeece15096 [fix] github runner (#451)
Co-authored-by: Niq Dudfield <[email protected]>
2025-03-03 09:55:51 +01:00
tequ 89cacb1258 Enhance shell script error handling and debugging on GHA (#447) 2025-02-24 10:33:21 +01:00
tequ 8ccff44e8c Fix Error handling on build action (#412) 2025-02-24 18:16:21 +10:00
tequ 420240a2ab Fixed not to use a large fixed range in the magic_enum. (#436) 2025-02-24 17:46:42 +10:00
Richard Holland 230873f196 debug gh builds 2025-02-06 15:21:37 +11:00
Wietse Wind 1fb1a99ea2 Update build-in-docker.yml 2025-02-05 08:23:49 +01:00
Richard Holland e0b63ac70e Revert "debug account tx tests under release builder"
This reverts commit da8df63be3.

Revert "add strict filtering to account_tx api (#429)"

This reverts commit 317bd4bc6e.
2025-02-05 14:59:33 +11:00
Richard Holland da8df63be3 debug account tx tests under release builder 2025-02-04 17:02:17 +11:00
RichardAH 317bd4bc6e add strict filtering to account_tx api (#429) 2025-02-03 17:56:08 +10:00
RichardAHandDenis Angell 2fd465bb3f fix20250131 (#428)
Co-authored-by: Denis Angell <[email protected]>
2025-02-03 10:33:19 +10:00
Wietse Wind fa71bda29c Artifact v4 continue on error 2025-02-01 08:58:13 +01:00
Wietse Wind 412593d7bc Update artifact 2025-02-01 08:57:48 +01:00
Wietse Wind 12d8342c34 Update artifact 2025-02-01 08:57:25 +01:00
tequ d17f7151ab Fix HookResult(ExitType) when accept() is not called (#415) 2025-01-22 13:33:59 +10:00
tequ 4466175231 Update boost link for build-full.sh (#421) 2025-01-22 08:38:12 +10:00
tequ 621ca9c865 Add space to trace_float log (#424) 2025-01-22 08:34:33 +10:00
tequ 85a752235a add URITokenIssuer to account_flags for account_info (#404) 2024-12-16 16:10:01 +10:00
RichardAH d878fd4a6e allow multiple datagram monitor endpoints (#408) 2024-12-14 08:44:40 +10:00
Richard HollandandDenis Angell 532a471a35 fixReduceImport (#398)
Co-authored-by: Denis Angell <[email protected]>
2024-12-11 13:29:37 +11:00
RichardAHandDenis Angell e9468d8b4a Datagram monitor (#400)
Co-authored-by: Denis Angell <[email protected]>
2024-12-11 13:29:30 +11:00
Denis Angell 9d54da3880 Fix: failing assert (#397) 2024-12-11 13:08:50 +11:00
Ekiserrepé 542172f0a1 Update README.md (#396)
Updated Xaman link.
2024-12-11 13:08:50 +11:00
Richard Holland e086724772 UDP RPC (admin) support (#390) 2024-12-11 13:08:44 +11:00
RichardAH 21863b05f3 Limit xahau genesis to networks starting with 2133X (#395) 2024-11-23 21:19:09 +10:00
RichardAH 987247ddc1 Merge branch 'dev' into remarks 2024-11-20 12:15:32 +10:00
+3 61ac04aacc Sync: Ripple(d) 1.11.0 (#299)
* Add jss fields used by Clio `nft_info`: (#4320)

Add Clio-specific JSS constants to ensure a common vocabulary of
keywords in Clio and this project. By providing visibility of the full
API keyword namespace, it reduces the likelihood of developers
introducing minor variations on names used by Clio, or unknowingly
claiming a keyword that Clio has already claimed. This change moves this
project slightly away from having only the code necessary for running
the core server, but it is a step toward the goal of keeping this
server's and Clio's APIs similar. The added JSS constants are annotated
to indicate their relevance to Clio.

Clio can be found here: https://github.com/XRPLF/clio

Signed-off-by: ledhed2222 <[email protected]>

* Introduce support for a slabbed allocator: (#4218)

When instantiating a large amount of fixed-sized objects on the heap
the overhead that dynamic memory allocation APIs impose will quickly
become significant.

In some cases, allocating a large amount of memory at once and using
a slabbing allocator to carve the large block into fixed-sized units
that are used to service requests for memory out will help to reduce
memory fragmentation significantly and, potentially, improve overall
performance.

This commit introduces a new `SlabAllocator<>` class that exposes an
API that is _similar_ to the C++ concept of an `Allocator` but it is
not meant to be a general-purpose allocator.

It should not be used unless profiling and analysis of specific memory
allocation patterns indicates that the additional complexity introduced
will improve the performance of the system overall, and subsequent
profiling proves it.

A helper class, `SlabAllocatorSet<>` simplifies handling of variably
sized objects that benefit from slab allocations.

This commit incorporates improvements suggested by Greg Popovitch
(@greg7mdp).

Commit 1 of 3 in #4218.

* Optimize `SHAMapItem` and leverage new slab allocator: (#4218)

The `SHAMapItem` class contains a variable-sized buffer that
holds the serialized data associated with a particular item
inside a `SHAMap`.

Prior to this commit, the buffer for the serialized data was
allocated separately. Coupled with the fact that most instances
of `SHAMapItem` were wrapped around a `std::shared_ptr` meant
that an instantiation might result in up to three separate
memory allocations.

This commit switches away from `std::shared_ptr` for `SHAMapItem`
and uses `boost::intrusive_ptr` instead, allowing the reference
count for an instance to live inside the instance itself. Coupled
with using a slab-based allocator to optimize memory allocation
for the most commonly sized buffers, the net result is significant
memory savings. In testing, the reduction in memory usage hovers
between 400MB and 650MB. Other scenarios might result in larger
savings.

In performance testing with NFTs, this commit reduces memory size by
about 15% sustained over long duration.

Commit 2 of 3 in #4218.

* Avoid using std::shared_ptr when not necessary: (#4218)

The `Ledger` class contains two `SHAMap` instances: the state and
transaction maps. Previously, the maps were dynamically allocated using
`std::make_shared` despite the fact that they did not require lifetime
management separate from the lifetime of the `Ledger` instance to which
they belong.

The two `SHAMap` instances are now regular member variables. Some smart
pointers and dynamic memory allocation was avoided by using stack-based
alternatives.

Commit 3 of 3 in #4218.

* Prevent replay attacks with NetworkID field: (#4370)

Add a `NetworkID` field to help prevent replay attacks on and from
side-chains.

The new field must be used when the server is using a network id > 1024.

To preserve legacy behavior, all chains with a network ID less than 1025
retain the existing behavior. This includes Mainnet, Testnet, Devnet,
and hooks-testnet. If `sfNetworkID` is present in any transaction
submitted to any of the nodes on one of these chains, then
`telNETWORK_ID_MAKES_TX_NON_CANONICAL` is returned.

Since chains with a network ID less than 1025, including Mainnet, retain
the existing behavior, there is no need for an amendment.

The `NetworkID` helps to prevent replay attacks because users specify a
`NetworkID` field in every transaction for that chain.

This change introduces a new UINT32 field, `sfNetworkID` ("NetworkID").
There are also three new local error codes for transaction results:

- `telNETWORK_ID_MAKES_TX_NON_CANONICAL`
- `telREQUIRES_NETWORK_ID`
- `telWRONG_NETWORK`

To learn about the other transaction result codes, see:
https://xrpl.org/transaction-results.html

Local error codes were chosen because a transaction is not necessarily
malformed if it is submitted to a node running on the incorrect chain.
This is a local error specific to that node and could be corrected by
switching to a different node or by changing the `network_id` on that
node. See:
https://xrpl.org/connect-your-rippled-to-the-xrp-test-net.html

In addition to using `NetworkID`, it is still generally recommended to
use different accounts and keys on side-chains. However, people will
undoubtedly use the same keys on multiple chains; for example, this is
common practice on other blockchain networks. There are also some
legitimate use cases for this.

A `app.NetworkID` test suite has been added, and `core.Config` was
updated to include some network_id tests.

* Fix the fix for std::result_of (#4496)

Newer compilers, such as Apple Clang 15.0, have removed `std::result_of`
as part of C++20. The build instructions provided a fix for this (by
adding a preprocessor definition), but the fix was broken.

This fixes the fix by:
* Adding the `conf` prefix for tool configurations (which had been
  forgotten).
* Passing `extra_b2_flags` to `boost` package to fix its build.
  * Define `BOOST_ASIO_HAS_STD_INVOKE_RESULT` in order to build boost
    1.77 with a newer compiler.

* Use quorum specified via command line: (#4489)

If `--quorum` setting is present on the command line, use the specified
value as the minimum quorum. This allows for the use of a potentially
fork-unsafe quorum, but it is sometimes necessary for small and test
networks.

Fix #4488.

---------

Co-authored-by: RichardAH <[email protected]>

* Fix errors for Clang 16: (#4501)

Address issues related to the removal of `std::{u,bi}nary_function` in
C++17 and some warnings with Clang 16. Some warnings appeared with the
upgrade to Apple clang version 14.0.3 (clang-1403.0.22.14.1).

- `std::{u,bi}nary_function` were removed in C++17. They were empty
  classes with a few associated types. We already have conditional code
  to define the types. Just make it unconditional.
- libc++ checks a cast in an unevaluated context to see if a type
  inherits from a binary function class in the standard library, e.g.
  `std::equal_to`, and this causes an error when the type privately
  inherits from such a class. Change these instances to public
  inheritance.
- We don't need a middle-man for the empty base optimization. Prefer to
  inherit directly from an empty class than from
  `beast::detail::empty_base_optimization`.
- Clang warns when all the uses of a variable are removed by conditional
  compilation of assertions. Add a `[[maybe_unused]]` annotation to
  suppress it.
- As a drive-by clean-up, remove commented code.

See related work in #4486.

* Fix typo (#4508)

* fix!: Prevent API from accepting seed or public key for account (#4404)

The API would allow seeds (and public keys) to be used in place of
accounts at several locations in the API. For example, when calling
account_info, you could pass `"account": "foo"`. The string "foo" is
treated like a seed, so the method returns `actNotFound` (instead of
`actMalformed`, as most developers would expect). In the early days,
this was a convenience to make testing easier. However, it allows for
poor security practices, so it is no longer a good idea. Allowing a
secret or passphrase is now considered a bug. Previously, it was
controlled by the `strict` option on some methods. With this commit,
since the API does not interpret `account` as `seed`, the option
`strict` is no longer needed and is removed.

Removing this behavior from the API is a [breaking
change](https://xrpl.org/request-formatting.html#breaking-changes). One
could argue that it shouldn't be done without bumping the API version;
however, in this instance, there is no evidence that anyone is using the
API in the "legacy" way. Furthermore, it is a potential security hole,
as it allows users to send secrets to places where they are not needed,
where they could end up in logs, error messages, etc. There's no reason
to take such a risk with a seed/secret, since only the public address is
needed.

Resolves: #3329, #3330, #4337

BREAKING CHANGE: Remove non-strict account parsing (#3330)

* Add nftoken_id, nftoken_ids, offer_id fields for NFTokens (#4447)

Three new fields are added to the `Tx` responses for NFTs:

1. `nftoken_id`: This field is included in the `Tx` responses for
   `NFTokenMint` and `NFTokenAcceptOffer`. This field indicates the
   `NFTokenID` for the `NFToken` that was modified on the ledger by the
   transaction.
2. `nftoken_ids`: This array is included in the `Tx` response for
   `NFTokenCancelOffer`. This field provides a list of all the
   `NFTokenID`s for the `NFToken`s that were modified on the ledger by
   the transaction.
3. `offer_id`: This field is included in the `Tx` response for
   `NFTokenCreateOffer` transactions and shows the OfferID of the
   `NFTokenOffer` created.

The fields make it easier to track specific tokens and offers. The
implementation includes code (by @ledhed2222) from the Clio project to
extract NFTokenIDs from mint transactions.

* Ensure that switchover vars are initialized before use: (#4527)

Global variables in different TUs are initialized in an undefined order.
At least one global variable was accessing a global switchover variable.
This caused the switchover variable to be accessed in an uninitialized
state.

Since the switchover is always explicitly set before transaction
processing, this bug can not effect transaction processing, but could
effect unit tests (and potentially the value of some global variables).
Note: at the time of this patch the offending bug is not yet in
production.

* Move faulty assert (#4533)

This assert was put in the wrong place, but it only triggers if shards
are configured. This change moves the assert to the right place and
updates it to ensure correctness.

The assert could be hit after the server downloads some shards. It may
be necessary to restart after the shards are downloaded.

Note that asserts are normally checked only in debug builds, so release
packages should not be affected.

Introduced in: #4319 (66627b26cf)

* Fix unaligned load and stores: (#4528) (#4531)

Misaligned load and store operations are supported by both Intel and ARM
CPUs. However, in C++, these operations are undefined behavior (UB).
Substituting these operations with a `memcpy` fixes this UB. The
compiled assembly code is equivalent to the original, so there is no
performance penalty to using memcpy.

For context: The unaligned load and store operations fixed here were
originally introduced in the slab allocator (#4218).

* Add missing includes for gcc 13.1: (#4555)

gcc 13.1 failed to compile due to missing headers. This patch adds the
needed headers.

* Trivial: add comments for NFToken-related invariants (#4558)

* fix node size estimation (#4536)

Fix a bug in the `NODE_SIZE` auto-detection feature in `Config.cpp`.
Specifically, this patch corrects the calculation for the total amount
of RAM available, which was previously returned in bytes, but is now
being returned in units of the system's memory unit. Additionally, the
patch adjusts the node size based on the number of available hardware
threads of execution.

* fix: remove redundant moves (#4565)

- Resolve gcc compiler warning:
      AccountObjects.cpp:182:47: warning: redundant move in initialization [-Wredundant-move]
  - The std::move() operation on trivially copyable types may generate a
    compile warning in newer versions of gcc.
- Remove extraneous header (unused imports) from a unit test file.

* Revert "Fix the fix for std::result_of (#4496)"

This reverts commit cee8409d60.

* Revert "Fix typo (#4508)"

This reverts commit 2956f14de8.

* clang

* [fold] bad merge

* [fold] fix bad merge

- add back filter for ripple state on account_channels
- add back network id test (env auto adds network id in xahau)

* [fold] fix build error

---------

Signed-off-by: ledhed2222 <[email protected]>
Co-authored-by: ledhed2222 <[email protected]>
Co-authored-by: Nik Bougalis <[email protected]>
Co-authored-by: RichardAH <[email protected]>
Co-authored-by: John Freeman <[email protected]>
Co-authored-by: Mark Travis <[email protected]>
Co-authored-by: solmsted <[email protected]>
Co-authored-by: drlongle <[email protected]>
Co-authored-by: Shawn Xie <[email protected]>
Co-authored-by: Scott Determan <[email protected]>
Co-authored-by: Ed Hennis <[email protected]>
Co-authored-by: Scott Schurr <[email protected]>
Co-authored-by: Chenna Keshava B S <[email protected]>
2024-11-20 10:54:03 +10:00
tequandDenis Angell 57a1329bff Fix lexicographical_compare_three_way build error at macos (#391)
Co-authored-by: Denis Angell <[email protected]>
2024-11-15 08:33:55 +10:00
Denis Angell daf22b3b85 Fix: RWDB (#389) 2024-11-15 07:31:55 +10:00
RichardAHandDenis Angell 2b225977e2 Feature: RWDB (#378)
Co-authored-by: Denis Angell <[email protected]>
2024-11-12 08:55:56 +10:00
RichardAH a5e2fd0699 Merge branch 'dev' into remarks 2024-11-09 15:27:55 +10:00
Denis AngellandRichardAH 58b22901cb Fix: float_divide rounding error (#351)
Co-authored-by: RichardAH <[email protected]>
2024-11-09 15:17:00 +10:00
8ba37a3138 Add Script for SfCode generation (#358)
Co-authored-by: RichardAH <[email protected]>
Co-authored-by: tequ <[email protected]>
2024-11-09 14:17:49 +10:00
tequ 8cffd3054d add trace message to exception on etxn_fee_base (#387) 2024-11-09 14:00:59 +10:00
RichardAH d92403ce35 Merge branch 'dev' into remarks 2024-11-09 13:45:31 +10:00
Denis Angell 6b26045cbc Update settings.json (#342) 2024-10-25 11:56:16 +10:00
Wietse Wind 08f13b7cfe Fix account_tx sluggishness as per https://github.com/XRPLF/rippled/commit/2e9261cb (#308) 2024-10-25 11:13:42 +10:00
tequ 766f5d7ee1 Update macro.h (#366) 2024-10-25 10:10:43 +10:00
Wietse Wind 287c01ad04 Improve Admin command RPC Post (#384)
* Improve ADMIN HTTP POST RPC notifications: no queue limit, shorter HTTP call TTL
2024-10-25 10:10:14 +10:00
tequ 4239124750 Update amendments for rippled-standalone.cfg (#385) 2024-10-25 09:10:45 +10:00
RichardAHandDenis Angell 1e45d4120c Update to boost186 (#377)
Co-Authored-By: Denis Angell <[email protected]>
2024-10-17 01:29:17 +02:00
Denis Angell 9e446bcc85 Fix: Missing Headers - Linker Errors (#300) 2024-10-16 18:19:21 +10:00
RichardAH 376727d20c use std::lexicographical_compare_three_way for uint spaceship operator (#374)
* use std::lexicographical_compare_three_way for uint spaceship operator

* clang
2024-10-16 11:37:26 +10:00
Richard Holland d921c87c88 also update max transactions for tx queue 2024-10-11 09:59:04 +11:00
RichardAH 7b94d3d99d increase txn in ledger target to 1000 (#372) 2024-10-11 08:21:23 +10:00
Denis Angell 6fb8fef883 clang-format 2024-09-19 16:29:18 +02:00
Denis Angell a8a4774232 add tests 2024-09-19 16:27:33 +02:00
Denis Angell eaec08471b Merge branch 'dev' into remarks 2024-09-19 14:41:53 +02:00
Denis Angell 79d83bd424 fix240911 (#363) 2024-09-11 13:43:03 +10:00
Richard Holland 1a4d54f9d9 force build 2024-09-07 15:41:00 +10:00
Wietse WindandWietse Wind 26cd629d28 Merge/2.2.2 jobqueue (#360)
* Merge https://github.com/XRPLF/rippled/commit/fbbea9e6e25795a8a6bd1bf64b780771933a9579

* Merge https://github.com/XRPLF/rippled/commit/774148389467781aca7c01bac90af2fba870570c

* clang-format

* Oops

---------

Co-authored-by: Wietse Wind <[email protected]>
2024-09-07 15:22:15 +10:00
RichardAHandDenis Angell 2fb93f874b fixPageCap (#359)
* page cap fix

---------

Co-authored-by: Denis Angell <[email protected]>
2024-09-07 11:39:24 +10:00
RichardAHandDenis Angell 833df20fce Fix240819 (#350)
fix240918
---------

Co-authored-by: Denis Angell <[email protected]>
2024-08-20 09:40:31 +10:00
Wietse Wind 5737c2b6e8 Workaround CentOS7 EOL 2024-08-18 01:50:44 +02:00
Richard Holland a15d0b2ecc set huge mode nudb cache to 64mib 2024-08-14 16:40:07 +10:00
Denis Angell 18d76d3082 fix delivered amount (#310)
* fix delivered amount
2024-07-16 08:56:30 +10:00
Denis Angell caffeea6fc Merge branch 'dev' into remarks 2024-07-08 09:59:26 +02:00
Wietse WindandDenis Angell 849a4435e0 CI Split jobs with prev job dependency & CI on jshooks (#320)
* CI on `jshooks` branch

* CI Split jobs with prev job dependency

* No multi branch worker in parallel

---------

Co-authored-by: Denis Angell <[email protected]>
2024-05-29 13:45:59 +02:00
Wietse Wind 247e9d98bf CI on jshooks branch (#317) 2024-05-24 10:10:40 +10:00
Denis Angell 23d49d0548 Merge branch 'dev' into remarks 2024-05-23 08:23:54 +02:00
Denis Angell acd455f5df Fix: Server Definitions Typo (#306) 2024-04-19 07:39:21 +10:00
Denis AngellandRichardAH 6636e3b6fd Add RPC Tests (#295)
Co-authored-by: RichardAH <[email protected]>
2024-04-18 18:22:46 +10:00
Denis Angell 3c5f118b59 Fix: Add Tx Flags To Server Definitions (#304) 2024-04-18 15:43:48 +10:00
Denis Angell 519ab34e4f add more tests 2024-04-03 15:28:23 +02:00
Denis Angell bdc59ac4ec apply sandbox and fixup 2024-04-03 15:28:07 +02:00
Denis Angell 96bb67bfe5 clang-format 2024-04-02 17:12:59 +02:00
Denis Angell 798212f87c add tests 2024-04-02 17:08:46 +02:00
Denis Angell a3d61c0fbf make sfRemarkValue Optional 2024-04-02 16:55:31 +02:00
Denis Angell 3e926c9946 add remark fee 2024-04-02 16:55:15 +02:00
Denis Angell 4392342c99 update error warning 2024-04-02 16:54:47 +02:00
Denis Angell f4fe7b7d9a add jtx helper 2024-04-02 16:53:33 +02:00
Richard Holland d268638a39 whoops 2024-03-27 03:36:01 +00:00
Richard Holland b1447afcc0 refactor, feature enable check 2024-03-27 02:22:02 +00:00
Denis Angell f40621c662 Update mulDiv.cpp 2024-03-25 22:18:01 +01:00
Denis Angell 36ff48474a Revert "fix muldiv"
This reverts commit 63b0245d06.
2024-03-25 22:05:09 +01:00
Denis Angell 2adc234bf1 Update SetRemarks_test.cpp 2024-03-25 17:39:43 +01:00
Denis Angell 89bcacca5b use sandbox and peak 2024-03-25 17:30:26 +01:00
Denis Angell 6d496cc16f create set remarks test 2024-03-25 17:06:51 +01:00
Denis Angell 63b0245d06 fix muldiv 2024-03-25 17:06:39 +01:00
Denis Angell fdf02a3853 Update SetRemarks.cpp 2024-03-25 17:06:32 +01:00
Denis Angell 9edf7ae67a create SetRemarks header 2024-03-25 17:06:29 +01:00
Denis Angell 533ba7ab75 nit: remit headerfile 2024-03-25 17:05:37 +01:00
Denis Angell 4e10d7d61f fix applySteps headers 2024-03-25 17:05:25 +01:00
Denis Angell 01e7caa0d6 fix applyHook headers 2024-03-25 17:05:15 +01:00
Denis Angell 349f4d2d68 reorder cmake 2024-03-25 17:05:04 +01:00
Richard Holland 24ac5d5f51 bug fixes, but levelisation issues 2024-03-25 06:58:31 +00:00
Richard Holland 8522c6684b transactor 2024-03-25 00:55:42 +00:00
Richard Holland 7efc26a8b1 initial version of remarks 2024-03-25 00:54:08 +00:00
260 changed files with 17607 additions and 1912 deletions

No files matched your search

+94 -15
View File
@@ -2,25 +2,104 @@ name: Build using Docker
on:
push:
branches: [ "dev", "candidate", "release" ]
branches: ["dev", "candidate", "release", "jshooks"]
pull_request:
branches: [ "dev", "candidate", "release" ]
branches: ["dev", "candidate", "release", "jshooks"]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
builder:
runs-on: [self-hosted, vanity]
steps:
- uses: actions/checkout@v3
with:
clean: false
- name: Check for suspicious patterns
run: /bin/bash suspicious_patterns.sh
- name: Build using Docker
run: /bin/bash release-builder.sh
- name: Unit tests
run: /bin/bash docker-unit-tests.sh
env:
DEBUG_BUILD_CONTAINERS_AFTER_CLEANUP: 1
jobs:
checkout:
runs-on: [self-hosted, vanity]
outputs:
checkout_path: ${{ steps.vars.outputs.checkout_path }}
steps:
- name: Prepare checkout path
id: vars
run: |
SAFE_BRANCH=$(echo "${{ github.ref_name }}" | sed -e 's/[^a-zA-Z0-9._-]/-/g')
CHECKOUT_PATH="${SAFE_BRANCH}-${{ github.sha }}"
echo "checkout_path=${CHECKOUT_PATH}" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v4
with:
path: ${{ steps.vars.outputs.checkout_path }}
clean: true
fetch-depth: 2 # Only get the last 2 commits, to avoid fetching all history
checkpatterns:
runs-on: [self-hosted, vanity]
needs: checkout
defaults:
run:
working-directory: ${{ needs.checkout.outputs.checkout_path }}
steps:
- name: Check for suspicious patterns
run: /bin/bash suspicious_patterns.sh
build:
runs-on: [self-hosted, vanity]
needs: [checkpatterns, checkout]
defaults:
run:
working-directory: ${{ needs.checkout.outputs.checkout_path }}
steps:
- name: Set Cleanup Script Path
run: |
echo "JOB_CLEANUP_SCRIPT=$(mktemp)" >> $GITHUB_ENV
- name: Build using Docker
run: /bin/bash release-builder.sh
- name: Stop Container (Cleanup)
if: always()
run: |
echo "Running cleanup script: $JOB_CLEANUP_SCRIPT"
/bin/bash -e -x "$JOB_CLEANUP_SCRIPT"
CLEANUP_EXIT_CODE=$?
if [[ "$CLEANUP_EXIT_CODE" -eq 0 ]]; then
echo "Cleanup script succeeded."
rm -f "$JOB_CLEANUP_SCRIPT"
echo "Cleanup script removed."
else
echo "⚠️ Cleanup script failed! Keeping for debugging: $JOB_CLEANUP_SCRIPT"
fi
if [[ "${DEBUG_BUILD_CONTAINERS_AFTER_CLEANUP}" == "1" ]]; then
echo "🔍 Checking for leftover containers..."
BUILD_CONTAINERS=$(docker ps --format '{{.Names}}' | grep '^xahaud_cached_builder' || echo "")
if [[ -n "$BUILD_CONTAINERS" ]]; then
echo "⚠️ WARNING: Some build containers are still running"
echo "$BUILD_CONTAINERS"
else
echo "✅ No build containers found"
fi
fi
tests:
runs-on: [self-hosted, vanity]
needs: [build, checkout]
defaults:
run:
working-directory: ${{ needs.checkout.outputs.checkout_path }}
steps:
- name: Unit tests
run: /bin/bash docker-unit-tests.sh
cleanup:
runs-on: [self-hosted, vanity]
needs: [tests, checkout]
if: always()
steps:
- name: Cleanup workspace
run: |
CHECKOUT_PATH="${{ needs.checkout.outputs.checkout_path }}"
echo "Cleaning workspace for ${CHECKOUT_PATH}"
rm -rf "${{ github.workspace }}/${CHECKOUT_PATH}"
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
git diff --exit-code | tee "clang-format.patch"
- name: Upload patch
if: failure() && steps.assert.outcome == 'failure'
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
continue-on-error: true
with:
name: clang-format.patch
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
git diff --exit-code | tee "levelization.patch"
- name: Upload patch
if: failure() && steps.assert.outcome == 'failure'
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
continue-on-error: true
with:
name: levelization.patch
+2
View File
@@ -114,3 +114,5 @@ pkg_out
pkg
CMakeUserPresets.json
bld.rippled/
generated
+1 -1
View File
@@ -3,7 +3,7 @@
"C_Cpp.clang_format_path": ".clang-format",
"C_Cpp.clang_format_fallbackStyle": "{ ColumnLimit: 0 }",
"[cpp]":{
"editor.wordBasedSuggestions": false,
"editor.wordBasedSuggestions": "off",
"editor.suggest.insertMode": "replace",
"editor.semanticHighlighting.enabled": true,
"editor.tabSize": 4,
+23 -9
View File
@@ -392,6 +392,7 @@ target_sources (rippled PRIVATE
src/ripple/app/misc/NegativeUNLVote.cpp
src/ripple/app/misc/NetworkOPs.cpp
src/ripple/app/misc/SHAMapStoreImp.cpp
src/ripple/app/misc/StateAccounting.cpp
src/ripple/app/misc/detail/impl/WorkSSL.cpp
src/ripple/app/misc/impl/AccountTxPaging.cpp
src/ripple/app/misc/impl/AmendmentTable.cpp
@@ -433,13 +434,17 @@ target_sources (rippled PRIVATE
src/ripple/app/tx/impl/CancelOffer.cpp
src/ripple/app/tx/impl/CashCheck.cpp
src/ripple/app/tx/impl/Change.cpp
src/ripple/app/tx/impl/ClaimReward.cpp
src/ripple/app/tx/impl/CreateCheck.cpp
src/ripple/app/tx/impl/CreateOffer.cpp
src/ripple/app/tx/impl/CreateTicket.cpp
src/ripple/app/tx/impl/DeleteAccount.cpp
src/ripple/app/tx/impl/DepositPreauth.cpp
src/ripple/app/tx/impl/Escrow.cpp
src/ripple/app/tx/impl/GenesisMint.cpp
src/ripple/app/tx/impl/Import.cpp
src/ripple/app/tx/impl/InvariantCheck.cpp
src/ripple/app/tx/impl/Invoke.cpp
src/ripple/app/tx/impl/NFTokenAcceptOffer.cpp
src/ripple/app/tx/impl/NFTokenBurn.cpp
src/ripple/app/tx/impl/NFTokenCancelOffer.cpp
@@ -448,14 +453,11 @@ target_sources (rippled PRIVATE
src/ripple/app/tx/impl/OfferStream.cpp
src/ripple/app/tx/impl/PayChan.cpp
src/ripple/app/tx/impl/Payment.cpp
src/ripple/app/tx/impl/SetAccount.cpp
src/ripple/app/tx/impl/SetRegularKey.cpp
src/ripple/app/tx/impl/SetHook.cpp
src/ripple/app/tx/impl/ClaimReward.cpp
src/ripple/app/tx/impl/GenesisMint.cpp
src/ripple/app/tx/impl/Import.cpp
src/ripple/app/tx/impl/Invoke.cpp
src/ripple/app/tx/impl/Remit.cpp
src/ripple/app/tx/impl/SetAccount.cpp
src/ripple/app/tx/impl/SetHook.cpp
src/ripple/app/tx/impl/SetRemarks.cpp
src/ripple/app/tx/impl/SetRegularKey.cpp
src/ripple/app/tx/impl/SetSignerList.cpp
src/ripple/app/tx/impl/SetTrust.cpp
src/ripple/app/tx/impl/SignerEntries.cpp
@@ -538,7 +540,9 @@ target_sources (rippled PRIVATE
subdir: nodestore
#]===============================]
src/ripple/nodestore/backend/CassandraFactory.cpp
src/ripple/nodestore/backend/RWDBFactory.cpp
src/ripple/nodestore/backend/MemoryFactory.cpp
src/ripple/nodestore/backend/FlatmapFactory.cpp
src/ripple/nodestore/backend/NuDBFactory.cpp
src/ripple/nodestore/backend/NullFactory.cpp
src/ripple/nodestore/backend/RocksDBFactory.cpp
@@ -603,6 +607,7 @@ target_sources (rippled PRIVATE
src/ripple/rpc/handlers/BlackList.cpp
src/ripple/rpc/handlers/BookOffers.cpp
src/ripple/rpc/handlers/CanDelete.cpp
src/ripple/rpc/handlers/Catalogue.cpp
src/ripple/rpc/handlers/Connect.cpp
src/ripple/rpc/handlers/ConsensusInfo.cpp
src/ripple/rpc/handlers/CrawlShards.cpp
@@ -658,6 +663,7 @@ target_sources (rippled PRIVATE
src/ripple/rpc/handlers/ValidatorListSites.cpp
src/ripple/rpc/handlers/Validators.cpp
src/ripple/rpc/handlers/WalletPropose.cpp
src/ripple/rpc/handlers/Catalogue.cpp
src/ripple/rpc/impl/DeliveredAmount.cpp
src/ripple/rpc/impl/Handler.cpp
src/ripple/rpc/impl/LegacyPathFind.cpp
@@ -669,6 +675,9 @@ target_sources (rippled PRIVATE
src/ripple/rpc/impl/ShardVerificationScheduler.cpp
src/ripple/rpc/impl/Status.cpp
src/ripple/rpc/impl/TransactionSign.cpp
src/ripple/rpc/impl/NFTokenID.cpp
src/ripple/rpc/impl/NFTokenOfferID.cpp
src/ripple/rpc/impl/NFTSyntheticSerializer.cpp
#[===============================[
main sources:
subdir: perflog
@@ -744,11 +753,15 @@ if (tests)
src/test/app/Remit_test.cpp
src/test/app/SHAMapStore_test.cpp
src/test/app/SetAuth_test.cpp
src/test/app/SetHook_test.cpp
src/test/app/SetHookTSH_test.cpp
src/test/app/SetRegularKey_test.cpp
src/test/app/SetRemarks_test.cpp
src/test/app/SetTrust_test.cpp
src/test/app/Taker_test.cpp
src/test/app/TheoreticalQuality_test.cpp
src/test/app/Ticket_test.cpp
src/test/app/Touch_test.cpp
src/test/app/Transaction_ordering_test.cpp
src/test/app/TrustAndBalance_test.cpp
src/test/app/TxQ_test.cpp
@@ -756,8 +769,6 @@ if (tests)
src/test/app/ValidatorKeys_test.cpp
src/test/app/ValidatorList_test.cpp
src/test/app/ValidatorSite_test.cpp
src/test/app/SetHook_test.cpp
src/test/app/SetHookTSH_test.cpp
src/test/app/Wildcard_test.cpp
src/test/app/XahauGenesis_test.cpp
src/test/app/tx/apply_test.cpp
@@ -891,6 +902,7 @@ if (tests)
src/test/jtx/impl/rate.cpp
src/test/jtx/impl/regkey.cpp
src/test/jtx/impl/reward.cpp
src/test/jtx/impl/remarks.cpp
src/test/jtx/impl/remit.cpp
src/test/jtx/impl/sendmax.cpp
src/test/jtx/impl/seq.cpp
@@ -983,10 +995,12 @@ if (tests)
src/test/rpc/AccountLinesRPC_test.cpp
src/test/rpc/AccountObjects_test.cpp
src/test/rpc/AccountOffers_test.cpp
src/test/rpc/AccountNamespace_test.cpp
src/test/rpc/AccountSet_test.cpp
src/test/rpc/AccountTx_test.cpp
src/test/rpc/AmendmentBlocked_test.cpp
src/test/rpc/Book_test.cpp
src/test/rpc/Catalogue_test.cpp
src/test/rpc/DepositAuthorized_test.cpp
src/test/rpc/DeliveredAmount_test.cpp
src/test/rpc/Feature_test.cpp
+7 -3
View File
@@ -1,14 +1,16 @@
#[===================================================================[
NIH dep: boost
#]===================================================================]
if((NOT DEFINED BOOST_ROOT) AND(DEFINED ENV{BOOST_ROOT}))
set(BOOST_ROOT $ENV{BOOST_ROOT})
endif()
if((NOT DEFINED BOOST_LIBRARYDIR) AND(DEFINED ENV{BOOST_LIBRARYDIR}))
set(BOOST_LIBRARYDIR $ENV{BOOST_LIBRARYDIR})
endif()
file(TO_CMAKE_PATH "${BOOST_ROOT}" BOOST_ROOT)
if(WIN32 OR CYGWIN)
# Workaround for MSVC having two boost versions - x86 and x64 on same PC in stage folders
if(DEFINED BOOST_ROOT)
if((NOT DEFINED BOOST_LIBRARYDIR) AND (DEFINED BOOST_ROOT))
if(IS_DIRECTORY ${BOOST_ROOT}/stage64/lib)
set(BOOST_LIBRARYDIR ${BOOST_ROOT}/stage64/lib)
elseif(IS_DIRECTORY ${BOOST_ROOT}/stage/lib)
@@ -44,7 +46,7 @@ else()
endif()
# TBD:
# Boost_USE_DEBUG_RUNTIME: When ON, uses Boost libraries linked against the
find_package(Boost 1.70 REQUIRED
find_package(Boost 1.86 REQUIRED
COMPONENTS
chrono
container
@@ -55,6 +57,7 @@ find_package(Boost 1.70 REQUIRED
program_options
regex
system
iostreams
thread)
add_library(ripple_boost INTERFACE)
@@ -74,6 +77,7 @@ target_link_libraries(ripple_boost
Boost::coroutine
Boost::date_time
Boost::filesystem
Boost::iostreams
Boost::program_options
Boost::regex
Boost::system
+31 -1
View File
@@ -248,6 +248,7 @@ include(FindPackageHandleStandardArgs)
# Save project's policies
cmake_policy(PUSH)
cmake_policy(SET CMP0057 NEW) # if IN_LIST
#cmake_policy(SET CMP0144 NEW)
#-------------------------------------------------------------------------------
# Before we go searching, check whether a boost cmake package is available, unless
@@ -969,7 +970,24 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret)
set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic)
set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
endif()
if(NOT Boost_VERSION_STRING VERSION_LESS 1.77.0)
# Special handling for Boost 1.86.0 and higher
if(NOT Boost_VERSION_STRING VERSION_LESS 1.86.0)
# Explicitly set these for Boost 1.86
set(_Boost_IOSTREAMS_DEPENDENCIES "") # No dependencies for iostreams in 1.86
# Debug output to help diagnose the issue
if(Boost_DEBUG)
message(STATUS "Using special dependency settings for Boost 1.86.0+")
message(STATUS "Component: ${component}, uppercomponent: ${uppercomponent}")
message(STATUS "Boost_VERSION_STRING: ${Boost_VERSION_STRING}")
message(STATUS "BOOST_ROOT: $ENV{BOOST_ROOT}")
message(STATUS "BOOST_LIBRARYDIR: $ENV{BOOST_LIBRARYDIR}")
endif()
endif()
# Only show warning for versions beyond what we've defined
if(NOT Boost_VERSION_STRING VERSION_LESS 1.87.0)
message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets")
endif()
endif()
@@ -1879,6 +1897,18 @@ foreach(COMPONENT ${Boost_FIND_COMPONENTS})
list(INSERT _boost_LIBRARY_SEARCH_DIRS_RELEASE 0 ${Boost_LIBRARY_DIR_DEBUG})
endif()
if(NOT Boost_VERSION_STRING VERSION_LESS 1.86.0)
if(BOOST_LIBRARYDIR AND EXISTS "${BOOST_LIBRARYDIR}")
# Clear existing search paths and use only BOOST_LIBRARYDIR
set(_boost_LIBRARY_SEARCH_DIRS_RELEASE "${BOOST_LIBRARYDIR}" NO_DEFAULT_PATH)
set(_boost_LIBRARY_SEARCH_DIRS_DEBUG "${BOOST_LIBRARYDIR}" NO_DEFAULT_PATH)
if(Boost_DEBUG)
message(STATUS "Boost 1.86: Setting library search dirs to BOOST_LIBRARYDIR: ${BOOST_LIBRARYDIR}")
endif()
endif()
endif()
# Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing.
string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp "${_boost_LIBRARY_SEARCH_DIRS_RELEASE}")
+6 -1
View File
@@ -74,7 +74,11 @@ else ()
if (NOT _location)
message (FATAL_ERROR "using pkg-config for grpc, can't find c-ares")
endif ()
add_library (c-ares::cares ${_static} IMPORTED GLOBAL)
if(${_location} MATCHES "\\.a$")
add_library(c-ares::cares STATIC IMPORTED GLOBAL)
else()
add_library(c-ares::cares SHARED IMPORTED GLOBAL)
endif()
set_target_properties (c-ares::cares PROPERTIES
IMPORTED_LOCATION ${_location}
INTERFACE_INCLUDE_DIRECTORIES "${${_prefix}_INCLUDE_DIRS}"
@@ -204,6 +208,7 @@ else ()
CMAKE_ARGS
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_STANDARD=17
$<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:-DCMAKE_VERBOSE_MAKEFILE=ON>
$<$<BOOL:${CMAKE_TOOLCHAIN_FILE}>:-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}>
$<$<BOOL:${VCPKG_TARGET_TRIPLET}>:-DVCPKG_TARGET_TRIPLET=${VCPKG_TARGET_TRIPLET}>
+3
View File
@@ -52,6 +52,9 @@ Loop: ripple.overlay ripple.rpc
Loop: test.app test.jtx
test.app > test.jtx
Loop: test.app test.rpc
test.rpc ~= test.app
Loop: test.jtx test.toplevel
test.toplevel > test.jtx
-1
View File
@@ -90,7 +90,6 @@ test.app > ripple.overlay
test.app > ripple.protocol
test.app > ripple.resource
test.app > ripple.rpc
test.app > test.rpc
test.app > test.toplevel
test.app > test.unit_test
test.basics > ripple.basics
+8 -4
View File
@@ -1,14 +1,18 @@
cmake_minimum_required (VERSION 3.16)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (POLICY CMP0074)
cmake_policy(SET CMP0074 NEW)
endif ()
project (rippled)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(POLICY CMP0144)
cmake_policy(SET CMP0144 NEW)
endif()
project (rippled)
set(Boost_NO_BOOST_CMAKE ON)
# make GIT_COMMIT_HASH define available to all sources
+3 -3
View File
@@ -1,4 +1,4 @@
# Xahau
# Xahau
**Note:** Throughout this README, references to "we" or "our" pertain to the community and contributors involved in the Xahau network. It does not imply a legal entity or a specific collection of individuals.
@@ -67,5 +67,5 @@ git-subtree. See those directories' README files for more details.
- [explorer.xahau.network](https://explorer.xahau.network)
- **Testnet & Faucet**: Test applications and obtain test XAH at [xahau-test.net](https://xahau-test.net) and use the testnet explorer at [explorer.xahau.network](https://explorer.xahau.network).
- **Supporting Wallets**: A list of wallets that support XAH and Xahau-based assets.
- [Xumm](https://xumm.app)
- [Crossmark](https://crossmark.io)
- [Xaman](https://xaman.app)
- [Crossmark](https://crossmark.io)
+9 -2
View File
@@ -1,4 +1,11 @@
#!/bin/bash
#!/bin/bash -u
# We use set -e and bash with -u to bail on first non zero exit code of any
# processes launched or upon any unbound variable.
# We use set -x to print commands before running them to help with
# debugging.
set -ex
set -e
echo "START INSIDE CONTAINER - CORE"
@@ -23,7 +30,7 @@ fi
perl -i -pe "s/^(\\s*)-DBUILD_SHARED_LIBS=OFF/\\1-DBUILD_SHARED_LIBS=OFF\\n\\1-DROCKSDB_BUILD_SHARED=OFF/g" Builds/CMake/deps/Rocksdb.cmake &&
mv Builds/CMake/deps/WasmEdge.cmake Builds/CMake/deps/WasmEdge.old &&
echo "find_package(LLVM REQUIRED CONFIG)
message(STATUS \"Found LLVM ${LLVM_PACKAGE_VERSION}\")
message(STATUS \"Found LLVM \${LLVM_PACKAGE_VERSION}\")
message(STATUS \"Using LLVMConfig.cmake in: \${LLVM_DIR}\")
add_library (wasmedge STATIC IMPORTED GLOBAL)
set_target_properties(wasmedge PROPERTIES IMPORTED_LOCATION \${WasmEdge_LIB})
+28 -12
View File
@@ -1,4 +1,11 @@
#!/bin/bash
#!/bin/bash -u
# We use set -e and bash with -u to bail on first non zero exit code of any
# processes launched or upon any unbound variable.
# We use set -x to print commands before running them to help with
# debugging.
set -ex
set -e
echo "START INSIDE CONTAINER - FULL"
@@ -9,8 +16,17 @@ echo "-- GITHUB_RUN_NUMBER: $4"
umask 0000;
echo "Fixing CentOS 7 EOL"
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
yum clean all
yum-config-manager --disable centos-sclo-sclo
####
cd /io;
mkdir src/certs;
mkdir -p src/certs;
curl --silent -k https://raw.githubusercontent.com/RichardAH/rippled-release-builder/main/ca-bundle/certbundle.h -o src/certs/certbundle.h;
if [ "`grep certbundle.h src/ripple/net/impl/RegisterSSLCerts.cpp | wc -l`" -eq "0" ]
then
@@ -57,8 +73,8 @@ then
#endif/g" src/ripple/net/impl/RegisterSSLCerts.cpp &&
sed -i "s/#include <ripple\/net\/RegisterSSLCerts.h>/\0\n#include <certs\/certbundle.h>/g" src/ripple/net/impl/RegisterSSLCerts.cpp
fi
mkdir .nih_c;
mkdir .nih_toolchain;
mkdir -p .nih_c;
mkdir -p .nih_toolchain;
cd .nih_toolchain &&
yum install -y wget lz4 lz4-devel git llvm13-static.x86_64 llvm13-devel.x86_64 devtoolset-10-binutils zlib-static ncurses-static -y \
devtoolset-7-gcc-c++ \
@@ -81,11 +97,11 @@ echo "-- Install Cmake 3.23.1 --" &&
pwd &&
( wget -nc -q https://github.com/Kitware/CMake/releases/download/v3.23.1/cmake-3.23.1-linux-x86_64.tar.gz; echo "" ) &&
tar -xzf cmake-3.23.1-linux-x86_64.tar.gz -C /hbb/ &&
echo "-- Install Boost 1.75.0 --" &&
echo "-- Install Boost 1.86.0 --" &&
pwd &&
( wget -nc -q https://boostorg.jfrog.io/artifactory/main/release/1.75.0/source/boost_1_75_0.tar.gz; echo "" ) &&
tar -xzf boost_1_75_0.tar.gz &&
cd boost_1_75_0 && ./bootstrap.sh && ./b2 link=static -j$3 && ./b2 install &&
( wget -nc -q https://archives.boost.io/release/1.86.0/source/boost_1_86_0.tar.gz; echo "" ) &&
tar -xzf boost_1_86_0.tar.gz &&
cd boost_1_86_0 && ./bootstrap.sh && ./b2 link=static -j$3 && ./b2 install &&
cd ../ &&
echo "-- Install Protobuf 3.20.0 --" &&
pwd &&
@@ -106,7 +122,7 @@ tar -xf libunwind-13.0.1.src.tar.xz &&
cp -r libunwind-13.0.1.src/include libunwind-13.0.1.src/src lld-13.0.1.src/ &&
cd lld-13.0.1.src &&
rm -rf build CMakeCache.txt &&
mkdir build &&
mkdir -p build &&
cd build &&
cmake .. -DLLVM_LIBRARY_DIR=/usr/lib64/llvm13/lib/ -DCMAKE_INSTALL_PREFIX=/usr/lib64/llvm13/ -DCMAKE_BUILD_TYPE=Release &&
make -j$3 install &&
@@ -116,11 +132,11 @@ cd ../../ &&
echo "-- Build WasmEdge --" &&
( wget -nc -q https://github.com/WasmEdge/WasmEdge/archive/refs/tags/0.11.2.zip; unzip -o 0.11.2.zip; ) &&
cd WasmEdge-0.11.2 &&
( mkdir build; echo "" ) &&
( mkdir -p build; echo "" ) &&
cd build &&
export BOOST_ROOT="/usr/local/src/boost_1_75_0" &&
export BOOST_ROOT="/usr/local/src/boost_1_86_0" &&
export Boost_LIBRARY_DIRS="/usr/local/lib" &&
export BOOST_INCLUDEDIR="/usr/local/src/boost_1_75_0" &&
export BOOST_INCLUDEDIR="/usr/local/src/boost_1_86_0" &&
export PATH=`echo $PATH | sed -E "s/devtoolset-7/devtoolset-9/g"` &&
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
+14 -2
View File
@@ -1056,7 +1056,18 @@
# Cassandra is an alternative backend to be used only with Reporting Mode.
# See the Reporting Mode section for more details about Reporting Mode.
#
# Required keys for NuDB and RocksDB:
# type = RWDB
#
# RWDB is a high-performance memory store written by XRPL-Labs and optimized
# for xahaud. RWDB is NOT persistent and the data will be lost on restart.
# RWDB is recommended for Validator and Peer nodes that are not required to
# store history.
#
# RWDB maintains its high speed regardless of the amount of history
# stored. Online delete should NOT be used instead RWDB will use the
# ledger_history config value to determine how many ledgers to keep in memory.
#
# Required keys for NuDB, RWDB and RocksDB:
#
# path Location to store the database
#
@@ -1112,7 +1123,8 @@
# online_delete Minimum value of 256. Enable automatic purging
# of older ledger information. Maintain at least this
# number of ledger records online. Must be greater
# than or equal to ledger_history.
# than or equal to ledger_history. If using RWDB
# this value is ignored.
#
# These keys modify the behavior of online_delete, and thus are only
# relevant if online_delete is defined and non-zero:
+9 -1
View File
@@ -144,4 +144,12 @@ D686F2538F410C9D0D856788E98E3579595DAF7B38D38887F81ECAC934B06040 HooksUpdate1
86E83A7D2ECE3AD5FA87AB2195AE015C950469ABF0B72EAACED318F74886AE90 CryptoConditionsSuite
3C43D9A973AA4443EF3FC38E42DD306160FBFFDAB901CD8BAA15D09F2597EB87 NonFungibleTokensV1
0285B7E5E08E1A8E4C15636F0591D87F73CB6A7B6452A932AD72BBC8E5D1CBE3 fixNFTokenDirV1
36799EA497B1369B170805C078AEFE6188345F9B3E324C21E9CA3FF574E3C3D6 fixNFTokenNegOffer
36799EA497B1369B170805C078AEFE6188345F9B3E324C21E9CA3FF574E3C3D6 fixNFTokenNegOffer
4C499D17719BB365B69010A436B64FD1A82AAB199FC1CEB06962EBD01059FB09 fixXahauV1
215181D23BF5C173314B5FDB9C872C92DE6CC918483727DE037C0C13E7E6EE9D fixXahauV2
0D8BF22FF7570D58598D1EF19EBB6E142AD46E59A223FD3816262FBB69345BEA Remit
7CA0426E7F411D39BB014E57CD9E08F61DE1750F0D41FCD428D9FB80BB7596B0 ZeroB2M
4B8466415FAB32FFA89D9DCBE166A42340115771DF611A7160F8D7439C87ECD8 fixNSDelete
EDB4EE4C524E16BDD91D9A529332DED08DCAAA51CC6DC897ACFA1A0ED131C5B6 fix240819
8063140E9260799D6716756B891CEC3E7006C4E4F277AB84670663A88F94B9C4 fixPageCap
88693F108C3CD8A967F3F4253A32DEF5E35F9406ACD2A11B88B11D90865763A9 fix240911
Regular → Executable
+9 -2
View File
@@ -1,4 +1,11 @@
#!/bin/bash
#!/bin/bash -x
docker run --rm -i -v $(pwd):/io ubuntu sh -c '/io/release-build/xahaud -u'
BUILD_CORES=$(echo "scale=0 ; `nproc` / 1.337" | bc)
if [[ "$GITHUB_REPOSITORY" == "" ]]; then
#Default
BUILD_CORES=8
fi
echo "Mounting $(pwd)/io in ubuntu and running unit tests"
docker run --rm -i -v $(pwd):/io -e BUILD_CORES=$BUILD_CORES ubuntu sh -c '/io/release-build/xahaud --unittest-jobs $BUILD_CORES -u'
+29
View File
@@ -0,0 +1,29 @@
#/bin/bash
RIPPLED_ROOT="../src/ripple"
echo '// For documentation please see: https://xrpl-hooks.readme.io/reference/'
echo '// Generated using generate_sfcodes.sh'
cat $RIPPLED_ROOT/protocol/impl/SField.cpp | grep -E '^CONSTRUCT_' |
sed 's/UINT16,/1,/g' |
sed 's/UINT32,/2,/g' |
sed 's/UINT64,/3,/g' |
sed 's/HASH128,/4,/g' |
sed 's/HASH256,/5,/g' |
sed 's/UINT128,/4,/g' |
sed 's/UINT256,/5,/g' |
sed 's/AMOUNT,/6,/g' |
sed 's/VL,/7,/g' |
sed 's/ACCOUNT,/8,/g' |
sed 's/OBJECT,/14,/g' |
sed 's/ARRAY,/15,/g' |
sed 's/UINT8,/16,/g' |
sed 's/HASH160,/17,/g' |
sed 's/UINT160,/17,/g' |
sed 's/PATHSET,/18,/g' |
sed 's/VECTOR256,/19,/g' |
sed 's/UINT96,/20,/g' |
sed 's/UINT192,/21,/g' |
sed 's/UINT384,/22,/g' |
sed 's/UINT512,/23,/g' |
grep -Eo '"([^"]+)", *([0-9]+), *([0-9]+)' |
sed 's/"//g' | sed 's/ *//g' | sed 's/,/ /g' |
awk '{print ("#define sf"$1" (("$2"U << 16U) + "$3"U)")}'
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* These are helper macros for writing hooks, all of them are optional as is including hookmacro.h at all
* These are helper macros for writing hooks, all of them are optional as is including macro.h at all
*/
#include <stdint.h>
+29 -5
View File
@@ -60,7 +60,10 @@
#define sfBurnedNFTokens ((2U << 16U) + 44U)
#define sfHookStateCount ((2U << 16U) + 45U)
#define sfEmitGeneration ((2U << 16U) + 46U)
#define sfLockCount ((2U << 16U) + 47U)
#define sfLockCount ((2U << 16U) + 49U)
#define sfFirstNFTokenSequence ((2U << 16U) + 50U)
#define sfXahauActivationLgrSeq ((2U << 16U) + 96U)
#define sfImportSequence ((2U << 16U) + 97U)
#define sfRewardTime ((2U << 16U) + 98U)
#define sfRewardLgrFirst ((2U << 16U) + 99U)
#define sfRewardLgrLast ((2U << 16U) + 100U)
@@ -80,12 +83,15 @@
#define sfHookInstructionCount ((3U << 16U) + 17U)
#define sfHookReturnCode ((3U << 16U) + 18U)
#define sfReferenceCount ((3U << 16U) + 19U)
#define sfTouchCount ((3U << 16U) + 97U)
#define sfAccountIndex ((3U << 16U) + 98U)
#define sfAccountCount ((3U << 16U) + 99U)
#define sfRewardAccumulator ((3U << 16U) + 100U)
#define sfEmailHash ((4U << 16U) + 1U)
#define sfTakerPaysCurrency ((10U << 16U) + 1U)
#define sfTakerPaysIssuer ((10U << 16U) + 2U)
#define sfTakerGetsCurrency ((10U << 16U) + 3U)
#define sfTakerGetsIssuer ((10U << 16U) + 4U)
#define sfTakerPaysCurrency ((17U << 16U) + 1U)
#define sfTakerPaysIssuer ((17U << 16U) + 2U)
#define sfTakerGetsCurrency ((17U << 16U) + 3U)
#define sfTakerGetsIssuer ((17U << 16U) + 4U)
#define sfLedgerHash ((5U << 16U) + 1U)
#define sfParentHash ((5U << 16U) + 2U)
#define sfTransactionHash ((5U << 16U) + 3U)
@@ -120,6 +126,9 @@
#define sfOfferID ((5U << 16U) + 34U)
#define sfEscrowID ((5U << 16U) + 35U)
#define sfURITokenID ((5U << 16U) + 36U)
#define sfGovernanceFlags ((5U << 16U) + 99U)
#define sfGovernanceMarks ((5U << 16U) + 98U)
#define sfEmittedTxnID ((5U << 16U) + 97U)
#define sfAmount ((6U << 16U) + 1U)
#define sfBalance ((6U << 16U) + 2U)
#define sfLimitAmount ((6U << 16U) + 3U)
@@ -136,6 +145,9 @@
#define sfNFTokenBrokerFee ((6U << 16U) + 19U)
#define sfHookCallbackFee ((6U << 16U) + 20U)
#define sfLockedBalance ((6U << 16U) + 21U)
#define sfBaseFeeDrops ((6U << 16U) + 22U)
#define sfReserveBaseDrops ((6U << 16U) + 23U)
#define sfReserveIncrementDrops ((6U << 16U) + 24U)
#define sfPublicKey ((7U << 16U) + 1U)
#define sfMessageKey ((7U << 16U) + 2U)
#define sfSigningPubKey ((7U << 16U) + 3U)
@@ -171,11 +183,13 @@
#define sfNFTokenMinter ((8U << 16U) + 9U)
#define sfEmitCallback ((8U << 16U) + 10U)
#define sfHookAccount ((8U << 16U) + 16U)
#define sfInform ((8U << 16U) + 99U)
#define sfIndexes ((19U << 16U) + 1U)
#define sfHashes ((19U << 16U) + 2U)
#define sfAmendments ((19U << 16U) + 3U)
#define sfNFTokenOffers ((19U << 16U) + 4U)
#define sfHookNamespaces ((19U << 16U) + 5U)
#define sfURITokenIDs ((19U << 16U) + 99U)
#define sfPaths ((18U << 16U) + 1U)
#define sfTransactionMetaData ((14U << 16U) + 2U)
#define sfCreatedNode ((14U << 16U) + 3U)
@@ -198,6 +212,12 @@
#define sfHookDefinition ((14U << 16U) + 22U)
#define sfHookParameter ((14U << 16U) + 23U)
#define sfHookGrant ((14U << 16U) + 24U)
#define sfGenesisMint ((14U << 16U) + 96U)
#define sfActiveValidator ((14U << 16U) + 95U)
#define sfImportVLKey ((14U << 16U) + 94U)
#define sfHookEmission ((14U << 16U) + 93U)
#define sfMintURIToken ((14U << 16U) + 92U)
#define sfAmountEntry ((14U << 16U) + 91U)
#define sfSigners ((15U << 16U) + 3U)
#define sfSignerEntries ((15U << 16U) + 4U)
#define sfTemplate ((15U << 16U) + 5U)
@@ -212,4 +232,8 @@
#define sfHookExecutions ((15U << 16U) + 18U)
#define sfHookParameters ((15U << 16U) + 19U)
#define sfHookGrants ((15U << 16U) + 20U)
#define sfGenesisMints ((15U << 16U) + 96U)
#define sfActiveValidators ((15U << 16U) + 95U)
#define sfImportVLKeys ((15U << 16U) + 94U)
#define sfHookEmissions ((15U << 16U) + 93U)
#define sfAmounts ((15U << 16U) + 92U)
+30 -3
View File
@@ -1,4 +1,9 @@
#!/bin/bash
#!/bin/bash -u
# We use set -e and bash with -u to bail on first non zero exit code of any
# processes launched or upon any unbound variable.
# We use set -x to print commands before running them to help with
# debugging.
set -ex
echo "START BUILDING (HOST)"
@@ -12,7 +17,26 @@ if [[ "$GITHUB_REPOSITORY" == "" ]]; then
BUILD_CORES=8
fi
CONTAINER_NAME=xahaud_cached_builder_$(echo "$GITHUB_ACTOR" | awk '{print tolower($0)}')
EXIT_IF_CONTAINER_RUNNING=${EXIT_IF_CONTAINER_RUNNING:-1}
# Ensure still works outside of GH Actions by setting these to /dev/null
# GA will run this script and then delete it at the end of the job
JOB_CLEANUP_SCRIPT=${JOB_CLEANUP_SCRIPT:-/dev/null}
NORMALIZED_WORKFLOW=$(echo "$GITHUB_WORKFLOW" | tr -c 'a-zA-Z0-9' '-')
NORMALIZED_REF=$(echo "$GITHUB_REF" | tr -c 'a-zA-Z0-9' '-')
CONTAINER_NAME="xahaud_cached_builder_${NORMALIZED_WORKFLOW}-${NORMALIZED_REF}"
# Check if the container is already running
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
echo "⚠️ A running container (${CONTAINER_NAME}) was detected."
if [[ "$EXIT_IF_CONTAINER_RUNNING" -eq 1 ]]; then
echo "❌ EXIT_IF_CONTAINER_RUNNING is set. Exiting."
exit 1
else
echo "🛑 Stopping the running container: ${CONTAINER_NAME}"
docker stop "${CONTAINER_NAME}"
fi
fi
echo "-- BUILD CORES: $BUILD_CORES"
echo "-- GITHUB_REPOSITORY: $GITHUB_REPOSITORY"
@@ -36,7 +60,8 @@ fi
STATIC_CONTAINER=$(docker ps -a | grep $CONTAINER_NAME |wc -l)
if [[ "$STATIC_CONTAINER" -gt "0" && "$GITHUB_REPOSITORY" != "" ]]; then
#if [[ "$STATIC_CONTAINER" -gt "0" && "$GITHUB_REPOSITORY" != "" ]]; then
if false; then
echo "Static container, execute in static container to have max. cache"
docker start $CONTAINER_NAME
docker exec -i $CONTAINER_NAME /hbb_exe/activate-exec bash -x /io/build-core.sh "$GITHUB_REPOSITORY" "$GITHUB_SHA" "$BUILD_CORES" "$GITHUB_RUN_NUMBER"
@@ -54,6 +79,8 @@ else
# GH Action, runner
echo "GH Action, runner, clean & re-create create persistent container"
docker rm -f $CONTAINER_NAME
echo "echo 'Stopping container: $CONTAINER_NAME'" >> "$JOB_CLEANUP_SCRIPT"
echo "docker stop --time=15 \"$CONTAINER_NAME\" || echo 'Failed to stop container or container not running'" >> "$JOB_CLEANUP_SCRIPT"
docker run -di --user 0:$(id -g) --name $CONTAINER_NAME -v /data/builds:/data/builds -v `pwd`:/io --network host ghcr.io/foobarwidget/holy-build-box-x64 /hbb_exe/activate-exec bash
docker exec -i $CONTAINER_NAME /hbb_exe/activate-exec bash -x /io/build-full.sh "$GITHUB_REPOSITORY" "$GITHUB_SHA" "$BUILD_CORES" "$GITHUB_RUN_NUMBER"
docker stop $CONTAINER_NAME
+98 -72
View File
@@ -5,11 +5,11 @@
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.9.3
// |___/ version 0.9.5
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2023 Daniil Goncharov <[email protected]>.
// Copyright (c) 2019 - 2024 Daniil Goncharov <[email protected]>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -34,7 +34,7 @@
#define MAGIC_ENUM_VERSION_MAJOR 0
#define MAGIC_ENUM_VERSION_MINOR 9
#define MAGIC_ENUM_VERSION_PATCH 3
#define MAGIC_ENUM_VERSION_PATCH 5
#include <array>
#include <cstddef>
@@ -60,7 +60,7 @@
#if defined(MAGIC_ENUM_NO_ASSERT)
# define MAGIC_ENUM_ASSERT(...) static_cast<void>(0)
#else
#elif !defined(MAGIC_ENUM_ASSERT)
# include <cassert>
# define MAGIC_ENUM_ASSERT(...) assert((__VA_ARGS__))
#endif
@@ -69,9 +69,11 @@
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunknown-warning-option"
# pragma clang diagnostic ignored "-Wenum-constexpr-conversion"
# pragma clang diagnostic ignored "-Wuseless-cast" // suppresses 'static_cast<char_type>('\0')' for char_type = char (common on Linux).
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'.
# pragma GCC diagnostic ignored "-Wuseless-cast" // suppresses 'static_cast<char_type>('\0')' for char_type = char (common on Linux).
#elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 26495) // Variable 'static_str<N>::chars_' is uninitialized.
@@ -164,14 +166,11 @@ namespace customize {
// If need another range for specific enum type, add specialization enum_range for necessary enum type.
template <typename E>
struct enum_range {
static_assert(std::is_enum_v<E>, "magic_enum::customize::enum_range requires enum type.");
static constexpr int min = MAGIC_ENUM_RANGE_MIN;
static constexpr int max = MAGIC_ENUM_RANGE_MAX;
static_assert(max > min, "magic_enum::customize::enum_range requires max > min.");
};
static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN.");
static_assert((MAGIC_ENUM_RANGE_MAX - MAGIC_ENUM_RANGE_MIN) < (std::numeric_limits<std::uint16_t>::max)(), "MAGIC_ENUM_RANGE must be less than UINT16_MAX.");
namespace detail {
@@ -216,9 +215,9 @@ namespace detail {
template <typename T>
struct supported
#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT)
: std::true_type {};
: std::true_type {};
#else
: std::false_type {};
: std::false_type {};
#endif
template <auto V, typename E = std::decay_t<decltype(V)>, std::enable_if_t<std::is_enum_v<E>, int> = 0>
@@ -423,10 +422,20 @@ constexpr auto n() noexcept {
constexpr auto name_ptr = MAGIC_ENUM_GET_TYPE_NAME_BUILTIN(E);
constexpr auto name = name_ptr ? str_view{name_ptr, std::char_traits<char>::length(name_ptr)} : str_view{};
#elif defined(__clang__)
auto name = str_view{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36};
str_view name;
if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) {
static_assert(always_false_v<E>, "magic_enum::detail::n requires __PRETTY_FUNCTION__.");
return str_view{};
} else {
name.size_ = sizeof(__PRETTY_FUNCTION__) - 36;
name.str_ = __PRETTY_FUNCTION__ + 34;
}
#elif defined(__GNUC__)
auto name = str_view{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1};
if (name.str_[name.size_ - 1] == ']') {
if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) {
static_assert(always_false_v<E>, "magic_enum::detail::n requires __PRETTY_FUNCTION__.");
return str_view{};
} else if (name.str_[name.size_ - 1] == ']') {
name.size_ -= 50;
name.str_ += 49;
} else {
@@ -489,7 +498,14 @@ constexpr auto n() noexcept {
constexpr auto name_ptr = MAGIC_ENUM_GET_ENUM_NAME_BUILTIN(V);
auto name = name_ptr ? str_view{name_ptr, std::char_traits<char>::length(name_ptr)} : str_view{};
#elif defined(__clang__)
auto name = str_view{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36};
str_view name;
if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) {
static_assert(always_false_v<decltype(V)>, "magic_enum::detail::n requires __PRETTY_FUNCTION__.");
return str_view{};
} else {
name.size_ = sizeof(__PRETTY_FUNCTION__) - 36;
name.str_ = __PRETTY_FUNCTION__ + 34;
}
if (name.size_ > 22 && name.str_[0] == '(' && name.str_[1] == 'a' && name.str_[10] == ' ' && name.str_[22] == ':') {
name.size_ -= 23;
name.str_ += 23;
@@ -499,7 +515,10 @@ constexpr auto n() noexcept {
}
#elif defined(__GNUC__)
auto name = str_view{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1};
if (name.str_[name.size_ - 1] == ']') {
if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) {
static_assert(always_false_v<decltype(V)>, "magic_enum::detail::n requires __PRETTY_FUNCTION__.");
return str_view{};
} else if (name.str_[name.size_ - 1] == ']') {
name.size_ -= 55;
name.str_ += 54;
} else {
@@ -698,7 +717,7 @@ constexpr void valid_count(bool* valid, std::size_t& count) noexcept {
} \
}
MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_V);
MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_V)
if constexpr ((I + 256) < Size) {
valid_count<E, S, Size, Min, I + 256>(valid, count);
@@ -750,7 +769,6 @@ constexpr auto values() noexcept {
constexpr auto max = reflected_max<E, S>();
constexpr auto range_size = max - min + 1;
static_assert(range_size > 0, "magic_enum::enum_range requires valid size.");
static_assert(range_size < (std::numeric_limits<std::uint16_t>::max)(), "magic_enum::enum_range requires valid size.");
return values<E, S, range_size, min>();
}
@@ -807,7 +825,8 @@ inline constexpr auto max_v = (count_v<E, S> > 0) ? static_cast<U>(values_v<E, S
template <typename E, enum_subtype S, std::size_t... I>
constexpr auto names(std::index_sequence<I...>) noexcept {
return std::array<string_view, sizeof...(I)>{{enum_name_v<E, values_v<E, S>[I]>...}};
constexpr auto names = std::array<string_view, sizeof...(I)>{{enum_name_v<E, values_v<E, S>[I]>...}};
return names;
}
template <typename E, enum_subtype S>
@@ -818,7 +837,8 @@ using names_t = decltype((names_v<D, S>));
template <typename E, enum_subtype S, std::size_t... I>
constexpr auto entries(std::index_sequence<I...>) noexcept {
return std::array<std::pair<E, string_view>, sizeof...(I)>{{{values_v<E, S>[I], enum_name_v<E, values_v<E, S>[I]>}...}};
constexpr auto entries = std::array<std::pair<E, string_view>, sizeof...(I)>{{{values_v<E, S>[I], enum_name_v<E, values_v<E, S>[I]>}...}};
return entries;
}
template <typename E, enum_subtype S>
@@ -845,17 +865,16 @@ constexpr bool is_sparse() noexcept {
template <typename E, enum_subtype S = subtype_v<E>>
inline constexpr bool is_sparse_v = is_sparse<E, S>();
template <typename E, enum_subtype S, typename U = std::underlying_type_t<E>>
constexpr U values_ors() noexcept {
static_assert(S == enum_subtype::flags, "magic_enum::detail::values_ors requires valid subtype.");
template <typename E, enum_subtype S>
struct is_reflected
#if defined(MAGIC_ENUM_NO_CHECK_REFLECTED_ENUM)
: std::true_type {};
#else
: std::bool_constant<std::is_enum_v<E> && (count_v<E, S> != 0)> {};
#endif
auto ors = U{0};
for (std::size_t i = 0; i < count_v<E, S>; ++i) {
ors |= static_cast<U>(values_v<E, S>[i]);
}
return ors;
}
template <typename E, enum_subtype S>
inline constexpr bool is_reflected_v = is_reflected<std::decay_t<E>, S>{};
template <bool, typename R>
struct enable_if_enum {};
@@ -1156,6 +1175,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if constexpr (detail::is_sparse_v<D, S>) {
return MAGIC_ENUM_ASSERT(index < detail::count_v<D, S>), detail::values_v<D, S>[index];
@@ -1170,6 +1190,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <typename E, std::size_t I, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
static_assert(I < detail::count_v<D, S>, "magic_enum::enum_value out of range.");
return enum_value<D, S>(I);
@@ -1178,7 +1199,10 @@ template <typename E, std::size_t I, detail::enum_subtype S = detail::subtype_v<
// Returns std::array with enum values, sorted by enum value.
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_t<E, detail::values_t<E, S>> {
return detail::values_v<std::decay_t<E>, S>;
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
return detail::values_v<D, S>;
}
// Returns integer value from enum value.
@@ -1199,11 +1223,9 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t<E, optional<std::size_t>> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
return {}; // Empty enum.
} else if constexpr (detail::is_sparse_v<D, S> || (S == detail::enum_subtype::flags)) {
if constexpr (detail::is_sparse_v<D, S> || (S == detail::enum_subtype::flags)) {
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::index>(
[](std::size_t i) { return optional<std::size_t>{i}; },
@@ -1231,14 +1253,17 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <detail::enum_subtype S, typename E>
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t<E, optional<std::size_t>> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
return enum_index<D, S>(value);
}
// Obtains index in enum values from static storage enum variable.
template <auto V, detail::enum_subtype S = detail::subtype_v<std::decay_t<decltype(V)>>>
[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t<decltype(V), std::size_t> {
constexpr auto index = enum_index<std::decay_t<decltype(V)>, S>(V);
[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t<decltype(V), std::size_t> {\
using D = std::decay_t<decltype(V)>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
constexpr auto index = enum_index<D, S>(V);
static_assert(index, "magic_enum::enum_index enum value does not have a index.");
return *index;
@@ -1259,6 +1284,7 @@ template <auto V>
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t<E, string_view> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if (const auto i = enum_index<D, S>(value)) {
return detail::names_v<D, S>[*i];
@@ -1271,6 +1297,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <detail::enum_subtype S, typename E>
[[nodiscard]] constexpr auto enum_name(E value) -> detail::enable_if_t<E, string_view> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
return enum_name<D, S>(value);
}
@@ -1278,13 +1305,19 @@ template <detail::enum_subtype S, typename E>
// Returns std::array with names, sorted by enum value.
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_t<E, detail::names_t<E, S>> {
return detail::names_v<std::decay_t<E>, S>;
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
return detail::names_v<D, S>;
}
// Returns std::array with pairs (value, name), sorted by enum value.
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_t<E, detail::entries_t<E, S>> {
return detail::entries_v<std::decay_t<E>, S>;
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
return detail::entries_v<D, S>;
}
// Allows you to write magic_enum::enum_cast<foo>("bar", magic_enum::case_insensitive);
@@ -1295,31 +1328,27 @@ inline constexpr auto case_insensitive = detail::case_insensitive<>{};
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
return {}; // Empty enum.
} else {
if constexpr (detail::is_sparse_v<D, S> || (S == detail::enum_subtype::flags)) {
if constexpr (detail::is_sparse_v<D, S> || (S == detail::enum_subtype::flags)) {
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
[](D v) { return optional<D>{v}; },
static_cast<D>(value),
detail::default_result_type_lambda<optional<D>>);
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
[](D v) { return optional<D>{v}; },
static_cast<D>(value),
detail::default_result_type_lambda<optional<D>>);
#else
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (value == static_cast<underlying_type_t<D>>(enum_value<D, S>(i))) {
return static_cast<D>(value);
}
}
return {}; // Invalid value or out of range.
#endif
} else {
if (value >= detail::min_v<D, S> && value <= detail::max_v<D, S>) {
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (value == static_cast<underlying_type_t<D>>(enum_value<D, S>(i))) {
return static_cast<D>(value);
}
return {}; // Invalid value or out of range.
}
return {}; // Invalid value or out of range.
#endif
} else {
if (value >= detail::min_v<D, S> && value <= detail::max_v<D, S>) {
return static_cast<D>(value);
}
return {}; // Invalid value or out of range.
}
}
@@ -1328,26 +1357,23 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <typename E, detail::enum_subtype S = detail::subtype_v<E>, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
return {}; // Empty enum.
#if defined(MAGIC_ENUM_ENABLE_HASH)
} else if constexpr (detail::is_default_predicate<BinaryPredicate>()) {
return detail::constexpr_switch<&detail::names_v<D, S>, detail::case_call_t::index>(
[](std::size_t i) { return optional<D>{detail::values_v<D, S>[i]}; },
value,
detail::default_result_type_lambda<optional<D>>,
[&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); });
#endif
} else {
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (detail::cmp_equal(value, detail::names_v<D, S>[i], p)) {
return enum_value<D, S>(i);
}
}
return {}; // Invalid value or out of range.
if constexpr (detail::is_default_predicate<BinaryPredicate>()) {
return detail::constexpr_switch<&detail::names_v<D, S>, detail::case_call_t::index>(
[](std::size_t i) { return optional<D>{detail::values_v<D, S>[i]}; },
value,
detail::default_result_type_lambda<optional<D>>,
[&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); });
}
#endif
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (detail::cmp_equal(value, detail::names_v<D, S>[i], p)) {
return enum_value<D, S>(i);
}
}
return {}; // Invalid value or out of range.
}
// Checks whether enum contains value with such value.
+12 -7
View File
@@ -134,8 +134,12 @@ RCLConsensus::Adaptor::acquireLedger(LedgerHash const& hash)
acquiringLedger_ = hash;
app_.getJobQueue().addJob(
jtADVANCE, "getConsensusLedger", [id = hash, &app = app_]() {
app.getInboundLedgers().acquire(
jtADVANCE,
"getConsensusLedger1",
[id = hash, &app = app_, this]() {
JLOG(j_.debug())
<< "JOB advanceLedger getConsensusLedger1 started";
app.getInboundLedgers().acquireAsync(
id, 0, InboundLedger::Reason::CONSENSUS);
});
}
@@ -182,7 +186,7 @@ RCLConsensus::Adaptor::share(RCLCxTx const& tx)
if (app_.getHashRouter().shouldRelay(tx.id()))
{
JLOG(j_.debug()) << "Relaying disputed tx " << tx.id();
auto const slice = tx.tx_.slice();
auto const slice = tx.tx_->slice();
protocol::TMTransaction msg;
msg.set_rawtransaction(slice.data(), slice.size());
msg.set_status(protocol::tsNEW);
@@ -326,7 +330,7 @@ RCLConsensus::Adaptor::onClose(
tx.first->add(s);
initialSet->addItem(
SHAMapNodeType::tnTRANSACTION_NM,
SHAMapItem(tx.first->getTransactionID(), s.slice()));
make_shamapitem(tx.first->getTransactionID(), s.slice()));
}
// Add pseudo-transactions to the set
@@ -370,7 +374,8 @@ RCLConsensus::Adaptor::onClose(
RCLCensorshipDetector<TxID, LedgerIndex>::TxIDSeqVec proposed;
initialSet->visitLeaves(
[&proposed, seq](std::shared_ptr<SHAMapItem const> const& item) {
[&proposed,
seq](boost::intrusive_ptr<SHAMapItem const> const& item) {
proposed.emplace_back(item->key(), seq);
});
@@ -535,7 +540,7 @@ RCLConsensus::Adaptor::doAccept(
std::vector<TxID> accepted;
result.txns.map_->visitLeaves(
[&accepted](std::shared_ptr<SHAMapItem const> const& item) {
[&accepted](boost::intrusive_ptr<SHAMapItem const> const& item) {
accepted.push_back(item->key());
});
@@ -610,7 +615,7 @@ RCLConsensus::Adaptor::doAccept(
<< "Test applying disputed transaction that did"
<< " not get in " << dispute.tx().id();
SerialIter sit(dispute.tx().tx_.slice());
SerialIter sit(dispute.tx().tx_->slice());
auto txn = std::make_shared<STTx const>(sit);
// Disputed pseudo-transactions that were not accepted
+5 -6
View File
@@ -42,7 +42,7 @@ public:
@param txn The transaction to wrap
*/
RCLCxTx(SHAMapItem const& txn) : tx_{txn}
RCLCxTx(boost::intrusive_ptr<SHAMapItem const> txn) : tx_(std::move(txn))
{
}
@@ -50,11 +50,11 @@ public:
ID const&
id() const
{
return tx_.key();
return tx_->key();
}
//! The SHAMapItem that represents the transaction.
SHAMapItem const tx_;
boost::intrusive_ptr<SHAMapItem const> tx_;
};
/** Represents a set of transactions in RCLConsensus.
@@ -90,8 +90,7 @@ public:
bool
insert(Tx const& t)
{
return map_->addItem(
SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{t.tx_});
return map_->addItem(SHAMapNodeType::tnTRANSACTION_NM, t.tx_);
}
/** Remove a transaction from the set.
@@ -145,7 +144,7 @@ public:
code uses the shared_ptr semantics to know whether the find
was successful and properly creates a Tx as needed.
*/
std::shared_ptr<const SHAMapItem> const&
boost::intrusive_ptr<SHAMapItem const> const&
find(Tx::ID const& entry) const
{
return map_->peekItem(entry);
+24 -4
View File
@@ -135,8 +135,10 @@ RCLValidationsAdaptor::acquire(LedgerHash const& hash)
Application* pApp = &app_;
app_.getJobQueue().addJob(
jtADVANCE, "getConsensusLedger", [pApp, hash]() {
pApp->getInboundLedgers().acquire(
jtADVANCE, "getConsensusLedger2", [pApp, hash, this]() {
JLOG(j_.debug())
<< "JOB advanceLedger getConsensusLedger2 started";
pApp->getInboundLedgers().acquireAsync(
hash, 0, InboundLedger::Reason::CONSENSUS);
});
return std::nullopt;
@@ -152,7 +154,9 @@ void
handleNewValidation(
Application& app,
std::shared_ptr<STValidation> const& val,
std::string const& source)
std::string const& source,
BypassAccept const bypassAccept,
std::optional<beast::Journal> j)
{
auto const& signingKey = val->getSignerPublic();
auto const& hash = val->getLedgerHash();
@@ -177,7 +181,23 @@ handleNewValidation(
if (outcome == ValStatus::current)
{
if (val->isTrusted())
app.getLedgerMaster().checkAccept(hash, seq);
{
// Was: app.getLedgerMaster().checkAccept(hash, seq);
// https://github.com/XRPLF/rippled/commit/fbbea9e6e25795a8a6bd1bf64b780771933a9579
if (bypassAccept == BypassAccept::yes)
{
assert(j.has_value());
if (j.has_value())
{
JLOG(j->trace()) << "Bypassing checkAccept for validation "
<< val->getLedgerHash();
}
}
else
{
app.getLedgerMaster().checkAccept(hash, seq);
}
}
return;
}
+7 -1
View File
@@ -25,12 +25,16 @@
#include <ripple/protocol/Protocol.h>
#include <ripple/protocol/RippleLedgerHash.h>
#include <ripple/protocol/STValidation.h>
#include <optional>
#include <set>
#include <vector>
namespace ripple {
class Application;
enum class BypassAccept : bool { no = false, yes };
/** Wrapper over STValidation for generic Validation code
Wraps an STValidation for compatibility with the generic validation code.
@@ -248,7 +252,9 @@ void
handleNewValidation(
Application& app,
std::shared_ptr<STValidation> const& val,
std::string const& source);
std::string const& source,
BypassAccept const bypassAccept = BypassAccept::no,
std::optional<beast::Journal> j = std::nullopt);
} // namespace ripple
+31 -2
View File
@@ -3,6 +3,7 @@
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <ostream>
#include <stack>
#include <string>
@@ -271,7 +272,19 @@ check_guard(
int guard_func_idx,
int last_import_idx,
GuardLog guardLog,
std::string guardLogAccStr)
std::string guardLogAccStr,
/* RH NOTE:
* rules version is a bit field, so rule update 1 is 0x01, update 2 is 0x02
* and update 3 is 0x04 ideally at rule version 3 all bits so far are set
* (0b111) so the ruleVersion = 7, however if a specific rule update must be
* rolled back due to unforeseen behaviour then this may no longer be the
* case. using a bit field here leaves us flexible to rollback changes that
* might have unforeseen consequences, without also rolling back further
* changes that are fine.
*/
uint64_t rulesVersion = 0
)
{
#define MAX_GUARD_CALLS 1024
uint32_t guard_count = 0;
@@ -621,11 +634,17 @@ check_guard(
}
else if (fc_type == 10) // memory.copy
{
if (rulesVersion & 0x02U)
GUARD_ERROR("Memory.copy instruction is not allowed.");
REQUIRE(2);
ADVANCE(2);
}
else if (fc_type == 11) // memory.fill
{
if (rulesVersion & 0x02U)
GUARD_ERROR("Memory.fill instruction is not allowed.");
ADVANCE(1);
}
else if (fc_type <= 7) // numeric instructions
@@ -807,6 +826,15 @@ validateGuards(
std::vector<uint8_t> const& wasm,
GuardLog guardLog,
std::string guardLogAccStr,
/* RH NOTE:
* rules version is a bit field, so rule update 1 is 0x01, update 2 is 0x02
* and update 3 is 0x04 ideally at rule version 3 all bits so far are set
* (0b111) so the ruleVersion = 7, however if a specific rule update must be
* rolled back due to unforeseen behaviour then this may no longer be the
* case. using a bit field here leaves us flexible to rollback changes that
* might have unforeseen consequences, without also rolling back further
* changes that are fine.
*/
uint64_t rulesVersion = 0)
{
uint64_t byteCount = wasm.size();
@@ -1477,7 +1505,8 @@ validateGuards(
guard_import_number,
last_import_number,
guardLog,
guardLogAccStr);
guardLogAccStr,
rulesVersion);
if (!valid)
return {};
+8
View File
@@ -428,6 +428,12 @@ namespace hook {
bool
canHook(ripple::TxType txType, ripple::uint256 hookOn);
bool
canEmit(ripple::TxType txType, ripple::uint256 hookCanEmit);
ripple::uint256
getHookCanEmit(ripple::STObject const& hookObj, SLE::pointer const& hookDef);
struct HookResult;
HookResult
@@ -436,6 +442,7 @@ apply(
used for caching (one day) */
ripple::uint256 const&
hookHash, /* hash of the actual hook byte code, used for metadata */
ripple::uint256 const& hookCanEmit,
ripple::uint256 const& hookNamespace,
ripple::Blob const& wasm,
std::map<
@@ -472,6 +479,7 @@ struct HookResult
{
ripple::uint256 const hookSetTxnID;
ripple::uint256 const hookHash;
ripple::uint256 const hookCanEmit;
ripple::Keylet const accountKeylet;
ripple::Keylet const ownerDirKeylet;
ripple::Keylet const hookKeylet;
+1 -1
View File
@@ -79,7 +79,7 @@ main(int argc, char** argv)
close(fd);
auto result = validateGuards(hook, std::cout, "", 1);
auto result = validateGuards(hook, std::cout, "", 3);
if (!result)
{
+63 -9
View File
@@ -1,12 +1,17 @@
#include <ripple/app/hook/applyHook.h>
#include <ripple/app/ledger/OpenLedger.h>
#include <ripple/app/ledger/TransactionMaster.h>
#include <ripple/app/misc/HashRouter.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/app/misc/Transaction.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/tx/impl/Import.h>
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/Slice.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/st.h>
#include <ripple/protocol/tokens.h>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <any>
@@ -1023,6 +1028,29 @@ hook::canHook(ripple::TxType txType, ripple::uint256 hookOn)
return (hookOn & UINT256_BIT[txType]) != beast::zero;
}
bool
hook::canEmit(ripple::TxType txType, ripple::uint256 hookCanEmit)
{
return hook::canHook(txType, hookCanEmit);
}
ripple::uint256
hook::getHookCanEmit(
ripple::STObject const& hookObj,
SLE::pointer const& hookDef)
{
// default allows all transaction types
uint256 defaultHookCanEmit = UINT256_BIT[ttHOOK_SET];
uint256 hookCanEmit =
(hookObj.isFieldPresent(sfHookCanEmit)
? hookObj.getFieldH256(sfHookCanEmit)
: hookDef->isFieldPresent(sfHookCanEmit)
? hookDef->getFieldH256(sfHookCanEmit)
: defaultHookCanEmit);
return hookCanEmit;
}
// Update HookState ledger objects for the hook... only called after accept()
// assumes the specified acc has already been checked for authoriation (hook
// grants)
@@ -1174,6 +1202,7 @@ hook::apply(
used for caching (one day) */
ripple::uint256 const&
hookHash, /* hash of the actual hook byte code, used for metadata */
ripple::uint256 const& hookCanEmit,
ripple::uint256 const& hookNamespace,
ripple::Blob const& wasm,
std::map<
@@ -1201,6 +1230,7 @@ hook::apply(
.result =
{.hookSetTxnID = hookSetTxnID,
.hookHash = hookHash,
.hookCanEmit = hookCanEmit,
.accountKeylet = keylet::account(account),
.ownerDirKeylet = keylet::ownerDir(account),
.hookKeylet = keylet::hook(account),
@@ -1212,9 +1242,10 @@ hook::apply(
.hookParamOverrides = hookParamOverrides,
.hookParams = hookParams,
.hookSkips = {},
.exitType =
hook_api::ExitType::ROLLBACK, // default is to rollback unless
// hook calls accept()
.exitType = applyCtx.view().rules().enabled(fixXahauV3)
? hook_api::ExitType::UNSET
: hook_api::ExitType::ROLLBACK, // default is to rollback
// unless hook calls accept()
.exitReason = std::string(""),
.exitCode = -1,
.hasCallback = hasCallback,
@@ -3264,6 +3295,16 @@ DEFINE_HOOK_FUNCTION(
return EMISSION_FAILURE;
}
ripple::TxType txType = stpTrans->getTxnType();
ripple::uint256 const& hookCanEmit = hookCtx.result.hookCanEmit;
if (!hook::canEmit(txType, hookCanEmit))
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: Hook cannot emit this txn.";
return EMISSION_FAILURE;
}
// check the emitted txn is valid
/* Emitted TXN rules
* 0. Account must match the hook account
@@ -4612,6 +4653,8 @@ DEFINE_HOOK_FUNCTION(
}
catch (std::exception& e)
{
JLOG(j.trace()) << "HookInfo[" << HC_ACC()
<< "]: etxn_fee_base exception: " << e.what();
return INVALID_TXN;
}
@@ -4783,7 +4826,7 @@ DEFINE_HOOK_FUNCTION(
if (float1 == 0)
{
j.trace() << "HookTrace[" << HC_ACC() << "]:"
j.trace() << "HookTrace[" << HC_ACC() << "]: "
<< (read_len == 0
? ""
: std::string_view(
@@ -5397,7 +5440,7 @@ DEFINE_HOOK_FUNCTION(
const int64_t float_one_internal = make_float(1000000000000000ull, -15, false);
inline int64_t
float_divide_internal(int64_t float1, int64_t float2)
float_divide_internal(int64_t float1, int64_t float2, bool hasFix)
{
RETURN_IF_INVALID_FLOAT(float1);
RETURN_IF_INVALID_FLOAT(float2);
@@ -5450,8 +5493,16 @@ float_divide_internal(int64_t float1, int64_t float2)
while (man2 > 0)
{
int i = 0;
for (; man1 > man2; man1 -= man2, ++i)
;
if (hasFix)
{
for (; man1 >= man2; man1 -= man2, ++i)
;
}
else
{
for (; man1 > man2; man1 -= man2, ++i)
;
}
man3 *= 10;
man3 += i;
@@ -5471,7 +5522,8 @@ DEFINE_HOOK_FUNCTION(int64_t, float_divide, int64_t float1, int64_t float2)
HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx,
// hookCtx on current stack
return float_divide_internal(float1, float2);
bool const hasFix = view.rules().enabled(fixFloatDivide);
return float_divide_internal(float1, float2, hasFix);
HOOK_TEARDOWN();
}
@@ -5490,7 +5542,9 @@ DEFINE_HOOK_FUNCTION(int64_t, float_invert, int64_t float1)
return DIVISION_BY_ZERO;
if (float1 == float_one_internal)
return float_one_internal;
return float_divide_internal(float_one_internal, float1);
bool const fixV3 = view.rules().enabled(fixFloatDivide);
return float_divide_internal(float_one_internal, float1, fixV3);
HOOK_TEARDOWN();
}
+11
View File
@@ -38,10 +38,21 @@ public:
virtual ~InboundLedgers() = default;
// VFALCO TODO Should this be called findOrAdd ?
// Callers should use this if they possibly need an authoritative
// response immediately.
//
virtual std::shared_ptr<Ledger const>
acquire(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason) = 0;
// Callers should use this if they are known to be executing on the Job
// Queue. TODO review whether all callers of acquire() can use this
// instead. Inbound ledger acquisition is asynchronous anyway.
virtual void
acquireAsync(
uint256 const& hash,
std::uint32_t seq,
InboundLedger::Reason reason) = 0;
virtual std::shared_ptr<InboundLedger>
find(LedgerHash const& hash) = 0;
+95 -82
View File
@@ -119,9 +119,8 @@ public:
sles_type::value_type
dereference() const override
{
auto const item = *iter_;
SerialIter sit(item.slice());
return std::make_shared<SLE const>(sit, item.key());
SerialIter sit(iter_->slice());
return std::make_shared<SLE const>(sit, iter_->key());
}
};
@@ -168,7 +167,7 @@ public:
txs_type::value_type
dereference() const override
{
auto const item = *iter_;
auto const& item = *iter_;
if (metadata_)
return deserializeTxPlusMeta(item);
return {deserializeTx(item), nullptr};
@@ -183,8 +182,8 @@ Ledger::Ledger(
std::vector<uint256> const& amendments,
Family& family)
: mImmutable(false)
, txMap_(std::make_shared<SHAMap>(SHAMapType::TRANSACTION, family))
, stateMap_(std::make_shared<SHAMap>(SHAMapType::STATE, family))
, txMap_(SHAMapType::TRANSACTION, family)
, stateMap_(SHAMapType::STATE, family)
, rules_{config.features}
, j_(beast::Journal(beast::Journal::getNullSink()))
{
@@ -247,7 +246,7 @@ Ledger::Ledger(
rawInsert(sle);
}
stateMap_->flushDirty(hotACCOUNT_NODE);
stateMap_.flushDirty(hotACCOUNT_NODE);
setImmutable();
}
@@ -259,12 +258,8 @@ Ledger::Ledger(
Family& family,
beast::Journal j)
: mImmutable(true)
, txMap_(std::make_shared<SHAMap>(
SHAMapType::TRANSACTION,
info.txHash,
family))
, stateMap_(
std::make_shared<SHAMap>(SHAMapType::STATE, info.accountHash, family))
, txMap_(SHAMapType::TRANSACTION, info.txHash, family)
, stateMap_(SHAMapType::STATE, info.accountHash, family)
, rules_(config.features)
, info_(info)
, j_(j)
@@ -272,7 +267,7 @@ Ledger::Ledger(
loaded = true;
if (info_.txHash.isNonZero() &&
!txMap_->fetchRoot(SHAMapHash{info_.txHash}, nullptr))
!txMap_.fetchRoot(SHAMapHash{info_.txHash}, nullptr))
{
if (config.reporting())
{
@@ -284,7 +279,7 @@ Ledger::Ledger(
}
if (info_.accountHash.isNonZero() &&
!stateMap_->fetchRoot(SHAMapHash{info_.accountHash}, nullptr))
!stateMap_.fetchRoot(SHAMapHash{info_.accountHash}, nullptr))
{
if (config.reporting())
{
@@ -295,8 +290,8 @@ Ledger::Ledger(
JLOG(j.warn()) << "Don't have state data root for ledger" << info_.seq;
}
txMap_->setImmutable();
stateMap_->setImmutable();
txMap_.setImmutable();
stateMap_.setImmutable();
defaultFees(config);
if (!setup())
@@ -310,13 +305,25 @@ Ledger::Ledger(
}
}
Ledger::Ledger(
LedgerInfo& info,
Config const& config,
Family& family,
SHAMap const& baseState)
: mImmutable(false)
, info_(info)
, txMap_(SHAMapType::TRANSACTION, family)
, stateMap_(baseState, true)
, rules_{config.features}
, j_(beast::Journal(beast::Journal::getNullSink()))
{
}
// Create a new ledger that follows this one
Ledger::Ledger(Ledger const& prevLedger, NetClock::time_point closeTime)
: mImmutable(false)
, txMap_(std::make_shared<SHAMap>(
SHAMapType::TRANSACTION,
prevLedger.stateMap_->family()))
, stateMap_(prevLedger.stateMap_->snapShot(true))
, txMap_(SHAMapType::TRANSACTION, prevLedger.txMap_.family())
, stateMap_(prevLedger.stateMap_, true)
, fees_(prevLedger.fees_)
, rules_(prevLedger.rules_)
, j_(beast::Journal(beast::Journal::getNullSink()))
@@ -345,12 +352,8 @@ Ledger::Ledger(Ledger const& prevLedger, NetClock::time_point closeTime)
Ledger::Ledger(LedgerInfo const& info, Config const& config, Family& family)
: mImmutable(true)
, txMap_(std::make_shared<SHAMap>(
SHAMapType::TRANSACTION,
info.txHash,
family))
, stateMap_(
std::make_shared<SHAMap>(SHAMapType::STATE, info.accountHash, family))
, txMap_(SHAMapType::TRANSACTION, info.txHash, family)
, stateMap_(SHAMapType::STATE, info.accountHash, family)
, rules_{config.features}
, info_(info)
, j_(beast::Journal(beast::Journal::getNullSink()))
@@ -364,8 +367,8 @@ Ledger::Ledger(
Config const& config,
Family& family)
: mImmutable(false)
, txMap_(std::make_shared<SHAMap>(SHAMapType::TRANSACTION, family))
, stateMap_(std::make_shared<SHAMap>(SHAMapType::STATE, family))
, txMap_(SHAMapType::TRANSACTION, family)
, stateMap_(SHAMapType::STATE, family)
, rules_{config.features}
, j_(beast::Journal(beast::Journal::getNullSink()))
{
@@ -383,19 +386,32 @@ Ledger::setImmutable(bool rehash)
// place the hash transitions to valid
if (!mImmutable && rehash)
{
info_.txHash = txMap_->getHash().as_uint256();
info_.accountHash = stateMap_->getHash().as_uint256();
info_.txHash = txMap_.getHash().as_uint256();
info_.accountHash = stateMap_.getHash().as_uint256();
}
if (rehash)
info_.hash = calculateLedgerHash(info_);
mImmutable = true;
txMap_->setImmutable();
stateMap_->setImmutable();
txMap_.setImmutable();
stateMap_.setImmutable();
setup();
}
// raw setters for catalogue
void
Ledger::setCloseFlags(int closeFlags)
{
info_.closeFlags = closeFlags;
}
void
Ledger::setDrops(uint64_t drops)
{
info_.drops = drops;
}
void
Ledger::setAccepted(
NetClock::time_point closeTime,
@@ -415,8 +431,8 @@ bool
Ledger::addSLE(SLE const& sle)
{
auto const s = sle.getSerializer();
SHAMapItem item(sle.key(), s.slice());
return stateMap_->addItem(SHAMapNodeType::tnACCOUNT_STATE, std::move(item));
return stateMap_.addItem(
SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle.key(), s.slice()));
}
//------------------------------------------------------------------------------
@@ -451,20 +467,20 @@ bool
Ledger::exists(Keylet const& k) const
{
// VFALCO NOTE Perhaps check the type for debug builds?
return stateMap_->hasItem(k.key);
return stateMap_.hasItem(k.key);
}
bool
Ledger::exists(uint256 const& key) const
{
return stateMap_->hasItem(key);
return stateMap_.hasItem(key);
}
std::optional<uint256>
Ledger::succ(uint256 const& key, std::optional<uint256> const& last) const
{
auto item = stateMap_->upper_bound(key);
if (item == stateMap_->end())
auto item = stateMap_.upper_bound(key);
if (item == stateMap_.end())
return std::nullopt;
if (last && item->key() >= last)
return std::nullopt;
@@ -479,7 +495,7 @@ Ledger::read(Keylet const& k) const
assert(false);
return nullptr;
}
auto const& item = stateMap_->peekItem(k.key);
auto const& item = stateMap_.peekItem(k.key);
if (!item)
return nullptr;
auto sle = std::make_shared<SLE>(SerialIter{item->slice()}, item->key());
@@ -493,45 +509,44 @@ Ledger::read(Keylet const& k) const
auto
Ledger::slesBegin() const -> std::unique_ptr<sles_type::iter_base>
{
return std::make_unique<sles_iter_impl>(stateMap_->begin());
return std::make_unique<sles_iter_impl>(stateMap_.begin());
}
auto
Ledger::slesEnd() const -> std::unique_ptr<sles_type::iter_base>
{
return std::make_unique<sles_iter_impl>(stateMap_->end());
return std::make_unique<sles_iter_impl>(stateMap_.end());
}
auto
Ledger::slesUpperBound(uint256 const& key) const
-> std::unique_ptr<sles_type::iter_base>
{
return std::make_unique<sles_iter_impl>(stateMap_->upper_bound(key));
return std::make_unique<sles_iter_impl>(stateMap_.upper_bound(key));
}
auto
Ledger::txsBegin() const -> std::unique_ptr<txs_type::iter_base>
{
return std::make_unique<txs_iter_impl>(!open(), txMap_->begin());
return std::make_unique<txs_iter_impl>(!open(), txMap_.begin());
}
auto
Ledger::txsEnd() const -> std::unique_ptr<txs_type::iter_base>
{
return std::make_unique<txs_iter_impl>(!open(), txMap_->end());
return std::make_unique<txs_iter_impl>(!open(), txMap_.end());
}
bool
Ledger::txExists(uint256 const& key) const
{
return txMap_->hasItem(key);
return txMap_.hasItem(key);
}
auto
Ledger::txRead(key_type const& key) const -> tx_type
{
assert(txMap_);
auto const& item = txMap_->peekItem(key);
auto const& item = txMap_.peekItem(key);
if (!item)
return {};
if (!open())
@@ -548,7 +563,7 @@ Ledger::digest(key_type const& key) const -> std::optional<digest_type>
SHAMapHash digest;
// VFALCO Unfortunately this loads the item
// from the NodeStore needlessly.
if (!stateMap_->peekItem(key, digest))
if (!stateMap_.peekItem(key, digest))
return std::nullopt;
return digest.as_uint256();
}
@@ -558,14 +573,14 @@ Ledger::digest(key_type const& key) const -> std::optional<digest_type>
void
Ledger::rawErase(std::shared_ptr<SLE> const& sle)
{
if (!stateMap_->delItem(sle->key()))
if (!stateMap_.delItem(sle->key()))
LogicError("Ledger::rawErase: key not found");
}
void
Ledger::rawErase(uint256 const& key)
{
if (!stateMap_->delItem(key))
if (!stateMap_.delItem(key))
LogicError("Ledger::rawErase: key not found");
}
@@ -574,9 +589,9 @@ Ledger::rawInsert(std::shared_ptr<SLE> const& sle)
{
Serializer ss;
sle->add(ss);
if (!stateMap_->addGiveItem(
if (!stateMap_.addGiveItem(
SHAMapNodeType::tnACCOUNT_STATE,
std::make_shared<SHAMapItem const>(sle->key(), ss.slice())))
make_shamapitem(sle->key(), ss.slice())))
LogicError("Ledger::rawInsert: key already exists");
}
@@ -585,9 +600,9 @@ Ledger::rawReplace(std::shared_ptr<SLE> const& sle)
{
Serializer ss;
sle->add(ss);
if (!stateMap_->updateGiveItem(
if (!stateMap_.updateGiveItem(
SHAMapNodeType::tnACCOUNT_STATE,
std::make_shared<SHAMapItem const>(sle->key(), ss.slice())))
make_shamapitem(sle->key(), ss.slice())))
LogicError("Ledger::rawReplace: key not found");
}
@@ -603,9 +618,8 @@ Ledger::rawTxInsert(
Serializer s(txn->getDataLength() + metaData->getDataLength() + 16);
s.addVL(txn->peekData());
s.addVL(metaData->peekData());
if (!txMap().addGiveItem(
SHAMapNodeType::tnTRANSACTION_MD,
std::make_shared<SHAMapItem const>(key, s.slice())))
if (!txMap_.addGiveItem(
SHAMapNodeType::tnTRANSACTION_MD, make_shamapitem(key, s.slice())))
LogicError("duplicate_tx: " + to_string(key));
}
@@ -621,9 +635,9 @@ Ledger::rawTxInsertWithHash(
Serializer s(txn->getDataLength() + metaData->getDataLength() + 16);
s.addVL(txn->peekData());
s.addVL(metaData->peekData());
auto item = std::make_shared<SHAMapItem const>(key, s.slice());
auto item = make_shamapitem(key, s.slice());
auto hash = sha512Half(HashPrefix::txNode, item->slice(), item->key());
if (!txMap().addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, std::move(item)))
if (!txMap_.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, std::move(item)))
LogicError("duplicate_tx: " + to_string(key));
return hash;
@@ -723,7 +737,7 @@ Ledger::defaultFees(Config const& config)
std::shared_ptr<SLE>
Ledger::peek(Keylet const& k) const
{
auto const& value = stateMap_->peekItem(k.key);
auto const& value = stateMap_.peekItem(k.key);
if (!value)
return nullptr;
auto sle = std::make_shared<SLE>(SerialIter{value->slice()}, value->key());
@@ -845,8 +859,8 @@ Ledger::walkLedger(beast::Journal j, bool parallel) const
std::vector<SHAMapMissingNode> missingNodes1;
std::vector<SHAMapMissingNode> missingNodes2;
if (stateMap_->getHash().isZero() && !info_.accountHash.isZero() &&
!stateMap_->fetchRoot(SHAMapHash{info_.accountHash}, nullptr))
if (stateMap_.getHash().isZero() && !info_.accountHash.isZero() &&
!stateMap_.fetchRoot(SHAMapHash{info_.accountHash}, nullptr))
{
missingNodes1.emplace_back(
SHAMapType::STATE, SHAMapHash{info_.accountHash});
@@ -854,9 +868,9 @@ Ledger::walkLedger(beast::Journal j, bool parallel) const
else
{
if (parallel)
return stateMap_->walkMapParallel(missingNodes1, 32);
return stateMap_.walkMapParallel(missingNodes1, 32);
else
stateMap_->walkMap(missingNodes1, 32);
stateMap_.walkMap(missingNodes1, 32);
}
if (!missingNodes1.empty())
@@ -868,15 +882,15 @@ Ledger::walkLedger(beast::Journal j, bool parallel) const
}
}
if (txMap_->getHash().isZero() && info_.txHash.isNonZero() &&
!txMap_->fetchRoot(SHAMapHash{info_.txHash}, nullptr))
if (txMap_.getHash().isZero() && info_.txHash.isNonZero() &&
!txMap_.fetchRoot(SHAMapHash{info_.txHash}, nullptr))
{
missingNodes2.emplace_back(
SHAMapType::TRANSACTION, SHAMapHash{info_.txHash});
}
else
{
txMap_->walkMap(missingNodes2, 32);
txMap_.walkMap(missingNodes2, 32);
}
if (!missingNodes2.empty())
@@ -893,9 +907,9 @@ Ledger::walkLedger(beast::Journal j, bool parallel) const
bool
Ledger::assertSensible(beast::Journal ledgerJ) const
{
if (info_.hash.isNonZero() && info_.accountHash.isNonZero() && stateMap_ &&
txMap_ && (info_.accountHash == stateMap_->getHash().as_uint256()) &&
(info_.txHash == txMap_->getHash().as_uint256()))
if (info_.hash.isNonZero() && info_.accountHash.isNonZero() &&
(info_.accountHash == stateMap_.getHash().as_uint256()) &&
(info_.txHash == txMap_.getHash().as_uint256()))
{
return true;
}
@@ -1057,15 +1071,14 @@ pendSaveValidated(
return true;
}
JobType const jobType{isCurrent ? jtPUBLEDGER : jtPUBOLDLEDGER};
char const* const jobName{
isCurrent ? "Ledger::pendSave" : "Ledger::pendOldSave"};
// See if we can use the JobQueue.
if (!isSynchronous &&
app.getJobQueue().addJob(jobType, jobName, [&app, ledger, isCurrent]() {
saveValidatedLedger(app, ledger, isCurrent);
}))
app.getJobQueue().addJob(
isCurrent ? jtPUBLEDGER : jtPUBOLDLEDGER,
std::to_string(ledger->seq()),
[&app, ledger, isCurrent]() {
saveValidatedLedger(app, ledger, isCurrent);
}))
{
return true;
}
@@ -1077,15 +1090,15 @@ pendSaveValidated(
void
Ledger::unshare() const
{
stateMap_->unshare();
txMap_->unshare();
stateMap_.unshare();
txMap_.unshare();
}
void
Ledger::invariants() const
{
stateMap_->invariants();
txMap_->invariants();
stateMap_.invariants();
txMap_.invariants();
}
//------------------------------------------------------------------------------
+30 -10
View File
@@ -83,6 +83,10 @@ public:
Ledger&
operator=(Ledger const&) = delete;
Ledger(Ledger&&) = delete;
Ledger&
operator=(Ledger&&) = delete;
/** Create the Genesis ledger.
The Genesis ledger contains a single account whose
@@ -117,6 +121,13 @@ public:
Family& family,
beast::Journal j);
// used when loading ledgers from catalogue files
Ledger(
LedgerInfo& info,
Config const& config,
Family& family,
SHAMap const& baseState);
/** Create a new ledger following a previous ledger
The ledger will have the sequence number that
@@ -271,6 +282,12 @@ public:
void
setImmutable(bool rehash = true);
void
setCloseFlags(int closeFlags);
void
setDrops(uint64_t drops);
bool
isImmutable() const
{
@@ -290,10 +307,10 @@ public:
void
setFull() const
{
txMap_->setFull();
stateMap_->setFull();
txMap_->setLedgerSeq(info_.seq);
stateMap_->setLedgerSeq(info_.seq);
txMap_.setFull();
txMap_.setLedgerSeq(info_.seq);
stateMap_.setFull();
stateMap_.setLedgerSeq(info_.seq);
}
void
@@ -305,25 +322,25 @@ public:
SHAMap const&
stateMap() const
{
return *stateMap_;
return stateMap_;
}
SHAMap&
stateMap()
{
return *stateMap_;
return stateMap_;
}
SHAMap const&
txMap() const
{
return *txMap_;
return txMap_;
}
SHAMap&
txMap()
{
return *txMap_;
return txMap_;
}
// returns false on error
@@ -401,8 +418,11 @@ private:
bool mImmutable;
std::shared_ptr<SHAMap> txMap_;
std::shared_ptr<SHAMap> stateMap_;
// A SHAMap containing the transactions associated with this ledger.
SHAMap mutable txMap_;
// A SHAMap containing the state objects for this ledger.
SHAMap mutable stateMap_;
// Protects fee variables
std::mutex mutable mutex_;
+21 -18
View File
@@ -51,7 +51,9 @@ LedgerHistory::LedgerHistory(
}
bool
LedgerHistory::insert(std::shared_ptr<Ledger const> ledger, bool validated)
LedgerHistory::insert(
std::shared_ptr<Ledger const> const& ledger,
bool validated)
{
if (!ledger->isImmutable())
LogicError("mutable Ledger in insert");
@@ -72,12 +74,9 @@ LedgerHash
LedgerHistory::getLedgerHash(LedgerIndex index)
{
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
auto it = mLedgersByIndex.find(index);
if (it != mLedgersByIndex.end())
if (auto it = mLedgersByIndex.find(index); it != mLedgersByIndex.end())
return it->second;
return uint256();
return {};
}
std::shared_ptr<Ledger const>
@@ -167,19 +166,19 @@ log_metadata_difference(
uint256 const& tx,
beast::Journal j)
{
auto getMeta = [](ReadView const& ledger,
uint256 const& txID) -> std::shared_ptr<TxMeta> {
auto meta = ledger.txRead(txID).second;
if (!meta)
return {};
return std::make_shared<TxMeta>(txID, ledger.seq(), *meta);
auto getMeta = [](ReadView const& ledger, uint256 const& txID) {
std::optional<TxMeta> ret;
if (auto meta = ledger.txRead(txID).second)
ret.emplace(txID, ledger.seq(), *meta);
return ret;
};
auto validMetaData = getMeta(validLedger, tx);
auto builtMetaData = getMeta(builtLedger, tx);
assert(validMetaData != nullptr || builtMetaData != nullptr);
if (validMetaData != nullptr && builtMetaData != nullptr)
assert(validMetaData || builtMetaData);
if (validMetaData && builtMetaData)
{
auto const& validNodes = validMetaData->getNodes();
auto const& builtNodes = builtMetaData->getNodes();
@@ -280,17 +279,21 @@ log_metadata_difference(
<< validNodes.getJson(JsonOptions::none);
}
}
return;
}
else if (validMetaData != nullptr)
if (validMetaData)
{
JLOG(j.error()) << "MISMATCH on TX " << tx
<< ": Metadata Difference (built has none)\n"
<< ": Metadata Difference. Valid=\n"
<< validMetaData->getJson(JsonOptions::none);
}
else // builtMetaData != nullptr
if (builtMetaData)
{
JLOG(j.error()) << "MISMATCH on TX " << tx
<< ": Metadata Difference (valid has none)\n"
<< ": Metadata Difference. Built=\n"
<< builtMetaData->getJson(JsonOptions::none);
}
}
+1 -3
View File
@@ -44,7 +44,7 @@ public:
@return `true` if the ledger was already tracked
*/
bool
insert(std::shared_ptr<Ledger const> ledger, bool validated);
insert(std::shared_ptr<Ledger const> const& ledger, bool validated);
/** Get the ledgers_by_hash cache hit rate
@return the hit rate
@@ -70,8 +70,6 @@ public:
LedgerHash
getLedgerHash(LedgerIndex ledgerIndex);
/** Remove stale cache entries
*/
void
sweep()
{
+15 -2
View File
@@ -128,7 +128,7 @@ public:
getEarliestFetch();
bool
storeLedger(std::shared_ptr<Ledger const> ledger);
storeLedger(std::shared_ptr<Ledger const> ledger, bool pin = false);
void
setFullLedger(
@@ -152,6 +152,15 @@ public:
std::string
getCompleteLedgers();
std::string
getPinnedLedgers();
RangeSet<std::uint32_t>
getCompleteLedgersRangeSet();
RangeSet<std::uint32_t>
getPinnedLedgersRangeSet();
/** Apply held transactions to the open ledger
This is normally called as we close the ledger.
The open ledger remains open to handle new transactions
@@ -197,7 +206,10 @@ public:
getLedgerByHash(uint256 const& hash);
void
setLedgerRangePresent(std::uint32_t minV, std::uint32_t maxV);
setLedgerRangePresent(
std::uint32_t minV,
std::uint32_t maxV,
bool pin = false /* if true, do not let these leaders be removed */);
std::optional<NetClock::time_point>
getCloseTimeBySeq(LedgerIndex ledgerIndex);
@@ -370,6 +382,7 @@ private:
std::recursive_mutex mCompleteLock;
RangeSet<std::uint32_t> mCompleteLedgers;
RangeSet<std::uint32_t> mPinnedLedgers; // Track pinned ledger ranges
// Publish thread is running.
bool mAdvanceThread{false};
+1 -1
View File
@@ -105,7 +105,7 @@ public:
void
gotSkipList(
LedgerInfo const& info,
std::shared_ptr<SHAMapItem const> const& data);
boost::intrusive_ptr<SHAMapItem const> const& data);
/**
* Process a ledger delta (extracted from a TMReplayDeltaResponse message)
+1 -1
View File
@@ -68,7 +68,7 @@ public:
std::shared_ptr<STTx const>
fetch(
std::shared_ptr<SHAMapItem> const& item,
boost::intrusive_ptr<SHAMapItem> const& item,
SHAMapNodeType type,
std::uint32_t uCommitLedger);
+1 -1
View File
@@ -560,7 +560,7 @@ InboundLedger::trigger(std::shared_ptr<Peer> const& peer, TriggerReason reason)
return;
}
if (auto stream = journal_.trace())
if (auto stream = journal_.debug())
{
if (peer)
stream << "Trigger acquiring ledger " << hash_ << " from " << peer;
@@ -28,6 +28,7 @@
#include <ripple/core/JobQueue.h>
#include <ripple/nodestore/DatabaseShard.h>
#include <ripple/protocol/jss.h>
#include <exception>
#include <memory>
#include <mutex>
#include <vector>
@@ -141,6 +142,37 @@ public:
return inbound->getLedger();
}
void
acquireAsync(
uint256 const& hash,
std::uint32_t seq,
InboundLedger::Reason reason) override
{
std::unique_lock lock(acquiresMutex_);
try
{
if (pendingAcquires_.contains(hash))
return;
pendingAcquires_.insert(hash);
lock.unlock();
acquire(hash, seq, reason);
}
catch (std::exception const& e)
{
JLOG(j_.warn())
<< "Exception thrown for acquiring new inbound ledger " << hash
<< ": " << e.what();
}
catch (...)
{
JLOG(j_.warn())
<< "Unknown exception thrown for acquiring new inbound ledger "
<< hash;
}
lock.lock();
pendingAcquires_.erase(hash);
}
std::shared_ptr<InboundLedger>
find(uint256 const& hash) override
{
@@ -426,6 +458,9 @@ private:
beast::insight::Counter mCounter;
std::unique_ptr<PeerSetBuilder> mPeerSetBuilder;
std::set<uint256> pendingAcquires_;
std::mutex acquiresMutex_;
};
//------------------------------------------------------------------------------
+8 -5
View File
@@ -219,7 +219,7 @@ private:
run()
{
beast::setCurrentThreadName("LedgerCleaner");
JLOG(j_.debug()) << "Started";
JLOG(j_.debug()) << "Started ledger cleaner";
while (true)
{
@@ -392,7 +392,8 @@ private:
if (app_.getFeeTrack().isLoadedLocal())
{
JLOG(j_.debug()) << "Waiting for load to subside";
JLOG(j_.debug())
<< "Ledger Cleaner: Waiting for load to subside";
std::this_thread::sleep_for(std::chrono::seconds(5));
continue;
}
@@ -415,13 +416,15 @@ private:
bool fail = false;
if (ledgerHash.isZero())
{
JLOG(j_.info())
<< "Unable to get hash for ledger " << ledgerIndex;
JLOG(j_.warn())
<< "Ledger Cleaner: Unable to get hash for ledger "
<< ledgerIndex;
fail = true;
}
else if (!doLedger(ledgerIndex, ledgerHash, doNodes, doTxns))
{
JLOG(j_.info()) << "Failed to process ledger " << ledgerIndex;
JLOG(j_.warn()) << "Ledger Cleaner: Failed to process ledger "
<< ledgerIndex;
fail = true;
}
+71 -5
View File
@@ -533,11 +533,20 @@ LedgerMaster::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash)
}
bool
LedgerMaster::storeLedger(std::shared_ptr<Ledger const> ledger)
LedgerMaster::storeLedger(std::shared_ptr<Ledger const> ledger, bool pin)
{
bool validated = ledger->info().validated;
// Returns true if we already had the ledger
return mLedgerHistory.insert(std::move(ledger), validated);
if (!mLedgerHistory.insert(std::move(ledger), validated))
return false;
if (pin)
{
uint32_t seq = ledger->info().seq;
mPinnedLedgers.insert(range(seq, seq));
JLOG(m_journal.info()) << "Pinned ledger : " << seq;
}
return true;
}
/** Apply held transactions to the open ledger
@@ -595,6 +604,15 @@ void
LedgerMaster::clearLedger(std::uint32_t seq)
{
std::lock_guard sl(mCompleteLock);
// Don't clear pinned ledgers
if (boost::icl::contains(mPinnedLedgers, seq))
{
JLOG(m_journal.trace())
<< "Ledger " << seq << " is pinned, not clearing";
return;
}
mCompleteLedgers.erase(seq);
}
@@ -1714,6 +1732,27 @@ LedgerMaster::getCompleteLedgers()
return to_string(mCompleteLedgers);
}
std::string
LedgerMaster::getPinnedLedgers()
{
std::lock_guard sl(mCompleteLock);
return to_string(mPinnedLedgers);
}
RangeSet<std::uint32_t>
LedgerMaster::getCompleteLedgersRangeSet()
{
std::lock_guard sl(mCompleteLock);
return mCompleteLedgers;
}
RangeSet<std::uint32_t>
LedgerMaster::getPinnedLedgersRangeSet()
{
std::lock_guard sl(mCompleteLock);
return mPinnedLedgers;
}
std::optional<NetClock::time_point>
LedgerMaster::getCloseTimeBySeq(LedgerIndex ledgerIndex)
{
@@ -1869,15 +1908,26 @@ LedgerMaster::getLedgerByHash(uint256 const& hash)
}
void
LedgerMaster::setLedgerRangePresent(std::uint32_t minV, std::uint32_t maxV)
LedgerMaster::setLedgerRangePresent(
std::uint32_t minV,
std::uint32_t maxV,
bool pin)
{
std::lock_guard sl(mCompleteLock);
mCompleteLedgers.insert(range(minV, maxV));
if (pin)
{
mPinnedLedgers.insert(range(minV, maxV));
JLOG(m_journal.info())
<< "Pinned ledger range: " << minV << " - " << maxV;
}
}
void
LedgerMaster::sweep()
{
std::lock_guard sl(mCompleteLock);
mLedgerHistory.sweep();
fetch_packs_.sweep();
}
@@ -1892,8 +1942,24 @@ void
LedgerMaster::clearPriorLedgers(LedgerIndex seq)
{
std::lock_guard sl(mCompleteLock);
if (seq > 0)
mCompleteLedgers.erase(range(0u, seq - 1));
if (seq <= 0)
return;
// First, save a copy of the pinned ledgers
auto pinnedCopy = mPinnedLedgers;
// Clear everything before seq
RangeSet<std::uint32_t> toClear;
toClear.insert(range(0u, seq - 1));
for (auto const& interval : toClear)
mCompleteLedgers.erase(interval);
// Re-add the pinned ledgers to ensure they're preserved
for (auto const& interval : pinnedCopy)
mCompleteLedgers.insert(interval);
JLOG(m_journal.debug()) << "clearPriorLedgers: after restoration, pinned="
<< to_string(mPinnedLedgers);
}
void
@@ -163,15 +163,15 @@ LedgerReplayMsgHandler::processProofPathResponse(
JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
return false;
}
auto item = static_cast<SHAMapLeafNode*>(node.get())->peekItem();
if (!item)
if (auto item = static_cast<SHAMapLeafNode*>(node.get())->peekItem())
{
JLOG(journal_.debug()) << "Bad message: Cannot get ShaMapItem";
return false;
replayer_.gotSkipList(info, item);
return true;
}
replayer_.gotSkipList(info, item);
return true;
JLOG(journal_.debug()) << "Bad message: Cannot get ShaMapItem";
return false;
}
protocol::TMReplayDeltaResponse
@@ -206,9 +206,10 @@ LedgerReplayMsgHandler::processReplayDeltaRequest(
reply.set_ledgerheader(nData.getDataPtr(), nData.getLength());
// pack transactions
auto const& txMap = ledger->txMap();
txMap.visitLeaves([&](std::shared_ptr<SHAMapItem const> const& txNode) {
reply.add_transaction(txNode->data(), txNode->size());
});
txMap.visitLeaves(
[&](boost::intrusive_ptr<SHAMapItem const> const& txNode) {
reply.add_transaction(txNode->data(), txNode->size());
});
JLOG(journal_.debug()) << "getReplayDelta for ledger " << ledgerHash
<< " txMap hash " << txMap.getHash().as_uint256();
@@ -264,10 +265,9 @@ LedgerReplayMsgHandler::processReplayDeltaResponse(
STObject meta(metaSit, sfMetadata);
orderedTxns.emplace(meta[sfTransactionIndex], std::move(tx));
auto item =
std::make_shared<SHAMapItem const>(tid, shaMapItemData.slice());
if (!item ||
!txMap.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, item))
if (!txMap.addGiveItem(
SHAMapNodeType::tnTRANSACTION_MD,
make_shamapitem(tid, shaMapItemData.slice())))
{
JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
return false;
@@ -172,7 +172,7 @@ LedgerReplayer::createDeltas(std::shared_ptr<LedgerReplayTask> task)
void
LedgerReplayer::gotSkipList(
LedgerInfo const& info,
std::shared_ptr<SHAMapItem const> const& item)
boost::intrusive_ptr<SHAMapItem const> const& item)
{
std::shared_ptr<SkipListAcquire> skipList = {};
{
+6 -6
View File
@@ -131,14 +131,14 @@ fillJsonTx(
if (stMeta)
{
txJson[jss::metaData] = stMeta->getJson(JsonOptions::none);
// If applicable, insert delivered amount
if (txnType == ttPAYMENT || txnType == ttCHECK_CASH)
{
// Insert delivered amount
auto txMeta = std::make_shared<TxMeta>(
txn->getTransactionID(), fill.ledger.seq(), *stMeta);
RPC::insertDeliveredAmount(
txJson[jss::metaData], fill.ledger, txn, *txMeta);
}
txJson[jss::metaData],
fill.ledger,
txn,
{txn->getTransactionID(), fill.ledger.seq(), *stMeta});
}
}
@@ -137,7 +137,7 @@ SkipListAcquire::pmDowncast()
void
SkipListAcquire::processData(
std::uint32_t ledgerSeq,
std::shared_ptr<SHAMapItem const> const& item)
boost::intrusive_ptr<SHAMapItem const> const& item)
{
assert(ledgerSeq != 0 && item);
ScopedLockType sl(mtx_);
+1 -1
View File
@@ -96,7 +96,7 @@ public:
void
processData(
std::uint32_t ledgerSeq,
std::shared_ptr<SHAMapItem const> const& item);
boost::intrusive_ptr<SHAMapItem const> const& item);
/**
* Add a callback that will be called when the skipList is ready or failed.
@@ -111,7 +111,7 @@ TransactionMaster::fetch(
std::shared_ptr<STTx const>
TransactionMaster::fetch(
std::shared_ptr<SHAMapItem> const& item,
boost::intrusive_ptr<SHAMapItem> const& item,
SHAMapNodeType type,
std::uint32_t uCommitLedger)
{
+11
View File
@@ -37,6 +37,7 @@
#include <ripple/app/main/NodeStoreScheduler.h>
#include <ripple/app/main/Tuning.h>
#include <ripple/app/misc/AmendmentTable.h>
#include <ripple/app/misc/DatagramMonitor.h>
#include <ripple/app/misc/HashRouter.h>
#include <ripple/app/misc/LoadFeeTrack.h>
#include <ripple/app/misc/NetworkOPs.h>
@@ -167,6 +168,8 @@ public:
std::unique_ptr<Logs> logs_;
std::unique_ptr<TimeKeeper> timeKeeper_;
std::unique_ptr<DatagramMonitor> datagram_monitor_;
std::uint64_t const instanceCookie_;
beast::Journal m_journal;
@@ -1523,6 +1526,14 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
if (reportingETL_)
reportingETL_->start();
// Datagram monitor if applicable
if (!config_->standalone() && !config_->DATAGRAM_MONITOR.empty())
{
datagram_monitor_ = std::make_unique<DatagramMonitor>(*this);
if (datagram_monitor_)
datagram_monitor_->start();
}
return true;
}
-1
View File
@@ -19,7 +19,6 @@
#ifndef RIPPLE_APP_MAIN_APPLICATION_H_INCLUDED
#define RIPPLE_APP_MAIN_APPLICATION_H_INCLUDED
#include <ripple/basics/TaggedCache.h>
#include <ripple/beast/utility/PropertyStream.h>
#include <ripple/core/Config.h>
+4 -6
View File
@@ -124,14 +124,12 @@ printHelp(const po::options_description& desc)
<< systemName() << "d [options] <command> <params>\n"
<< desc << std::endl
<< "Commands: \n"
" account_currencies <account> [<ledger>] [strict]\n"
" account_info <account>|<seed>|<pass_phrase>|<key> [<ledger>] "
"[strict]\n"
" account_currencies <account> [<ledger>]\n"
" account_info <account>|<key> [<ledger>]\n"
" account_lines <account> <account>|\"\" [<ledger>]\n"
" account_channels <account> <account>|\"\" [<ledger>]\n"
" account_objects <account> [<ledger>] [strict]\n"
" account_offers <account>|<account_public_key> [<ledger>] "
"[strict]\n"
" account_objects <account> [<ledger>]\n"
" account_offers <account>|<account_public_key> [<ledger>]\n"
" account_tx accountID [ledger_index_min [ledger_index_max "
"[limit "
"]]] [binary]\n"
+1 -2
View File
@@ -172,8 +172,7 @@ public:
initialPosition->addGiveItem(
SHAMapNodeType::tnTRANSACTION_NM,
std::make_shared<SHAMapItem>(
amendTx.getTransactionID(), s.slice()));
make_shamapitem(amendTx.getTransactionID(), s.slice()));
}
}
};
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -326,7 +326,7 @@ FeeVoteImpl::doVoting(
if (!initialPosition->addGiveItem(
SHAMapNodeType::tnTRANSACTION_NM,
std::make_shared<SHAMapItem>(txID, s.slice())))
make_shamapitem(txID, s.slice())))
{
JLOG(journal_.warn()) << "Ledger already had fee change";
}
+8 -6
View File
@@ -19,7 +19,10 @@
#include <ripple/app/consensus/RCLValidations.h>
#include <ripple/app/ledger/Ledger.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/NegativeUNLVote.h>
#include <ripple/json/to_string.h>
#include <ripple/shamap/SHAMapItem.h>
namespace ripple {
@@ -145,7 +148,7 @@ NegativeUNLVote::addReportingTx(
repUnlTx.add(s);
if (!initalSet->addGiveItem(
SHAMapNodeType::tnTRANSACTION_NM,
std::make_shared<SHAMapItem>(txID, s.slice())))
make_shamapitem(txID, s.slice())))
{
JLOG(j_.warn()) << "R-UNL: ledger seq=" << seq
<< ", add ttUNL_REPORT tx failed";
@@ -200,7 +203,7 @@ NegativeUNLVote::addImportVLTx(
repUnlTx.add(s);
if (!initalSet->addGiveItem(
SHAMapNodeType::tnTRANSACTION_NM,
std::make_shared<SHAMapItem>(txID, s.slice())))
make_shamapitem(txID, s.slice())))
{
JLOG(j_.warn()) << "R-UNL: ledger seq=" << seq
<< ", add ttUNL_REPORT tx failed (import_vl_key)";
@@ -229,12 +232,11 @@ NegativeUNLVote::addTx(
obj.setFieldVL(sfUNLModifyValidator, vp.slice());
});
uint256 txID = negUnlTx.getTransactionID();
Serializer s;
negUnlTx.add(s);
if (!initialSet->addGiveItem(
SHAMapNodeType::tnTRANSACTION_NM,
std::make_shared<SHAMapItem>(txID, s.slice())))
make_shamapitem(negUnlTx.getTransactionID(), s.slice())))
{
JLOG(j_.warn()) << "N-UNL: ledger seq=" << seq
<< ", add ttUNL_MODIFY tx failed";
@@ -242,8 +244,8 @@ NegativeUNLVote::addTx(
else
{
JLOG(j_.debug()) << "N-UNL: ledger seq=" << seq
<< ", add a ttUNL_MODIFY Tx with txID: " << txID
<< ", the validator to "
<< ", add a ttUNL_MODIFY Tx with txID: "
<< negUnlTx.getTransactionID() << ", the validator to "
<< (modify == ToDisable ? "disable: " : "re-enable: ")
<< vp;
}
+83 -140
View File
@@ -33,6 +33,7 @@
#include <ripple/app/misc/HashRouter.h>
#include <ripple/app/misc/LoadFeeTrack.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/app/misc/StateAccounting.h>
#include <ripple/app/misc/Transaction.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/misc/ValidatorKeys.h>
@@ -67,10 +68,12 @@
#include <ripple/rpc/CTID.h>
#include <ripple/rpc/DeliveredAmount.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/impl/UDPInfoSub.h>
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/steady_timer.hpp>
#include <exception>
#include <mutex>
#include <set>
#include <string>
#include <tuple>
#include <unordered_map>
@@ -114,81 +117,6 @@ class NetworkOPsImp final : public NetworkOPs
running,
};
static std::array<char const*, 5> const states_;
/**
* State accounting records two attributes for each possible server state:
* 1) Amount of time spent in each state (in microseconds). This value is
* updated upon each state transition.
* 2) Number of transitions to each state.
*
* This data can be polled through server_info and represented by
* monitoring systems similarly to how bandwidth, CPU, and other
* counter-based metrics are managed.
*
* State accounting is more accurate than periodic sampling of server
* state. With periodic sampling, it is very likely that state transitions
* are missed, and accuracy of time spent in each state is very rough.
*/
class StateAccounting
{
struct Counters
{
explicit Counters() = default;
std::uint64_t transitions = 0;
std::chrono::microseconds dur = std::chrono::microseconds(0);
};
OperatingMode mode_ = OperatingMode::DISCONNECTED;
std::array<Counters, 5> counters_;
mutable std::mutex mutex_;
std::chrono::steady_clock::time_point start_ =
std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point const processStart_ = start_;
std::uint64_t initialSyncUs_{0};
static std::array<Json::StaticString const, 5> const states_;
public:
explicit StateAccounting()
{
counters_[static_cast<std::size_t>(OperatingMode::DISCONNECTED)]
.transitions = 1;
}
/**
* Record state transition. Update duration spent in previous
* state.
*
* @param om New state.
*/
void
mode(OperatingMode om);
/**
* Output state counters in JSON format.
*
* @obj Json object to which to add state accounting data.
*/
void
json(Json::Value& obj) const;
struct CounterData
{
decltype(counters_) counters;
decltype(mode_) mode;
decltype(start_) start;
decltype(initialSyncUs_) initialSyncUs;
};
CounterData
getCounterData() const
{
std::lock_guard lock(mutex_);
return {counters_, mode_, start_, initialSyncUs_};
}
};
//! Server fees published on `server` subscription
struct ServerFeeSummary
{
@@ -270,6 +198,9 @@ public:
std::string
strOperatingMode(bool const admin = false) const override;
StateAccounting::CounterData
getStateAccountingData();
//
// Transaction operations.
//
@@ -774,7 +705,16 @@ private:
DispatchState mDispatchState = DispatchState::none;
std::vector<TransactionStatus> mTransactions;
StateAccounting accounting_{};
StateAccounting accounting_;
std::set<uint256> pendingValidations_;
std::mutex validationsMutex_;
RCLConsensus&
getConsensus();
LedgerMaster&
getLedgerMaster();
private:
struct Stats
@@ -838,19 +778,6 @@ private:
//------------------------------------------------------------------------------
static std::array<char const*, 5> const stateNames{
{"disconnected", "connected", "syncing", "tracking", "full"}};
std::array<char const*, 5> const NetworkOPsImp::states_ = stateNames;
std::array<Json::StaticString const, 5> const
NetworkOPsImp::StateAccounting::states_ = {
{Json::StaticString(stateNames[0]),
Json::StaticString(stateNames[1]),
Json::StaticString(stateNames[2]),
Json::StaticString(stateNames[3]),
Json::StaticString(stateNames[4])}};
static auto const genesisAccountId = calcAccountID(
generateKeyPair(KeyType::secp256k1, generateSeed("masterpassphrase"))
.first);
@@ -1125,7 +1052,7 @@ NetworkOPsImp::strOperatingMode(OperatingMode const mode, bool const admin)
}
}
return states_[static_cast<std::size_t>(mode)];
return {StateAccounting::states_[static_cast<std::size_t>(mode)].c_str()};
}
void
@@ -1142,8 +1069,12 @@ NetworkOPsImp::submitTransaction(std::shared_ptr<STTx const> const& iTrans)
// Enforce Network bar for emitted txn
if (view->rules().enabled(featureHooks) && hook::isEmittedTxn(*iTrans))
{
JLOG(m_journal.warn())
<< "Submitted transaction invalid: EmitDetails present.";
// RH NOTE: Warning removed here due to ConsesusSet using this function
// which continually triggers this bar. Doesn't seem dangerous, just
// annoying.
// JLOG(m_journal.warn())
// << "Submitted transaction invalid: EmitDetails present.";
return;
}
@@ -1155,7 +1086,11 @@ NetworkOPsImp::submitTransaction(std::shared_ptr<STTx const> const& iTrans)
if ((flags & SF_BAD) != 0)
{
JLOG(m_journal.warn()) << "Submitted transaction cached bad";
// RH NOTE: Warning removed here due to ConsesusSet using this function
// which continually triggers this bar. Doesn't seem dangerous, just
// annoying.
// JLOG(m_journal.warn()) << "Submitted transaction cached bad";
return;
}
@@ -1783,7 +1718,8 @@ NetworkOPsImp::checkLastClosedLedger(
}
JLOG(m_journal.warn()) << "We are not running on the consensus ledger";
JLOG(m_journal.info()) << "Our LCL: " << getJson({*ourClosed, {}});
JLOG(m_journal.info()) << "Our LCL: " << ourClosed->info().hash
<< getJson({*ourClosed, {}});
JLOG(m_journal.info()) << "Net LCL " << closedLedger;
if ((mMode == OperatingMode::TRACKING) || (mMode == OperatingMode::FULL))
@@ -2337,7 +2273,37 @@ NetworkOPsImp::recvValidation(
JLOG(m_journal.trace())
<< "recvValidation " << val->getLedgerHash() << " from " << source;
handleNewValidation(app_, val, source);
// handleNewValidation(app_, val, source);
// https://github.com/XRPLF/rippled/commit/fbbea9e6e25795a8a6bd1bf64b780771933a9579
std::unique_lock lock(validationsMutex_);
BypassAccept bypassAccept = BypassAccept::no;
try
{
if (pendingValidations_.contains(val->getLedgerHash()))
bypassAccept = BypassAccept::yes;
else
pendingValidations_.insert(val->getLedgerHash());
lock.unlock();
handleNewValidation(app_, val, source, bypassAccept, m_journal);
}
catch (std::exception const& e)
{
JLOG(m_journal.warn())
<< "Exception thrown for handling new validation "
<< val->getLedgerHash() << ": " << e.what();
}
catch (...)
{
JLOG(m_journal.warn())
<< "Unknown exception thrown for handling new validation "
<< val->getLedgerHash();
}
if (bypassAccept == BypassAccept::no)
{
lock.lock();
pendingValidations_.erase(val->getLedgerHash());
lock.unlock();
}
pubValidation(val);
@@ -2352,6 +2318,19 @@ NetworkOPsImp::getConsensusInfo()
return mConsensus.getJson(true);
}
// RHTODO: not threadsafe?
RCLConsensus&
NetworkOPsImp::getConsensus()
{
return mConsensus;
}
LedgerMaster&
NetworkOPsImp::getLedgerMaster()
{
return m_ledgerMaster;
}
Json::Value
NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters)
{
@@ -2514,6 +2493,8 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters)
toBase58(TokenType::NodePublic, app_.nodeIdentity().first);
info[jss::complete_ledgers] = app_.getLedgerMaster().getCompleteLedgers();
info[jss::complete_ledgers_pinned] =
app_.getLedgerMaster().getPinnedLedgers();
if (amendmentBlocked_)
info[jss::amendment_blocked] = true;
@@ -4149,6 +4130,12 @@ NetworkOPsImp::stateAccounting(Json::Value& obj)
accounting_.json(obj);
}
StateAccounting::CounterData
NetworkOPsImp::getStateAccountingData()
{
return accounting_.getCounterData();
}
// <-- bool: true=erased, false=was not there
bool
NetworkOPsImp::unsubValidations(std::uint64_t uSeq)
@@ -4619,50 +4606,6 @@ NetworkOPsImp::collect_metrics()
counters[static_cast<std::size_t>(OperatingMode::FULL)].transitions);
}
void
NetworkOPsImp::StateAccounting::mode(OperatingMode om)
{
auto now = std::chrono::steady_clock::now();
std::lock_guard lock(mutex_);
++counters_[static_cast<std::size_t>(om)].transitions;
if (om == OperatingMode::FULL &&
counters_[static_cast<std::size_t>(om)].transitions == 1)
{
initialSyncUs_ = std::chrono::duration_cast<std::chrono::microseconds>(
now - processStart_)
.count();
}
counters_[static_cast<std::size_t>(mode_)].dur +=
std::chrono::duration_cast<std::chrono::microseconds>(now - start_);
mode_ = om;
start_ = now;
}
void
NetworkOPsImp::StateAccounting::json(Json::Value& obj) const
{
auto [counters, mode, start, initialSync] = getCounterData();
auto const current = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - start);
counters[static_cast<std::size_t>(mode)].dur += current;
obj[jss::state_accounting] = Json::objectValue;
for (std::size_t i = static_cast<std::size_t>(OperatingMode::DISCONNECTED);
i <= static_cast<std::size_t>(OperatingMode::FULL);
++i)
{
obj[jss::state_accounting][states_[i]] = Json::objectValue;
auto& state = obj[jss::state_accounting][states_[i]];
state[jss::transitions] = std::to_string(counters[i].transitions);
state[jss::duration_us] = std::to_string(counters[i].dur.count());
}
obj[jss::server_state_duration_us] = std::to_string(current.count());
if (initialSync)
obj[jss::initial_sync_duration_us] = std::to_string(initialSync);
}
//------------------------------------------------------------------------------
std::unique_ptr<NetworkOPs>
+12 -29
View File
@@ -20,8 +20,10 @@
#ifndef RIPPLE_APP_MISC_NETWORKOPS_H_INCLUDED
#define RIPPLE_APP_MISC_NETWORKOPS_H_INCLUDED
#include <ripple/app/consensus/RCLConsensus.h>
#include <ripple/app/consensus/RCLCxPeerPos.h>
#include <ripple/app/ledger/Ledger.h>
#include <ripple/app/misc/StateAccounting.h>
#include <ripple/core/JobQueue.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/net/InfoSub.h>
@@ -42,35 +44,6 @@ class LedgerMaster;
class Transaction;
class ValidatorKeys;
// This is the primary interface into the "client" portion of the program.
// Code that wants to do normal operations on the network such as
// creating and monitoring accounts, creating transactions, and so on
// should use this interface. The RPC code will primarily be a light wrapper
// over this code.
//
// Eventually, it will check the node's operating mode (synched, unsynched,
// etectera) and defer to the correct means of processing. The current
// code assumes this node is synched (and will continue to do so until
// there's a functional network.
//
/** Specifies the mode under which the server believes it's operating.
This has implications about how the server processes transactions and
how it responds to requests (e.g. account balance request).
@note Other code relies on the numerical values of these constants; do
not change them without verifying each use and ensuring that it is
not a breaking change.
*/
enum class OperatingMode {
DISCONNECTED = 0, //!< not ready to process requests
CONNECTED = 1, //!< convinced we are talking to the network
SYNCING = 2, //!< fallen slightly behind
TRACKING = 3, //!< convinced we agree with the network
FULL = 4 //!< we have the ledger and can even validate
};
/** Provides server functionality for clients.
Clients include backend applications, local commands, and connected
@@ -221,6 +194,13 @@ public:
virtual Json::Value
getConsensusInfo() = 0;
virtual RCLConsensus&
getConsensus() = 0;
virtual LedgerMaster&
getLedgerMaster() = 0;
virtual Json::Value
getServerInfo(bool human, bool admin, bool counters) = 0;
virtual void
@@ -228,6 +208,9 @@ public:
virtual Json::Value
getLedgerFetchInfo() = 0;
virtual StateAccounting::CounterData
getStateAccountingData() = 0;
/** Accepts the current transaction tree, return the new ledger's sequence
This API is only used via RPC with the server in STANDALONE mode and
+35 -30
View File
@@ -162,7 +162,8 @@ SHAMapStoreImp::SHAMapStoreImp(
}
state_db_.init(config, dbName_);
dbPaths();
if (!config.mem_backend())
dbPaths();
}
}
@@ -195,6 +196,7 @@ SHAMapStoreImp::makeNodeStore(int readThreads)
"online_delete info from config");
}
SavedState state = state_db_.getState();
auto writableBackend = makeBackendRotating(state.writableDb);
auto archiveBackend = makeBackendRotating(state.archiveDb);
if (!state.writableDb.size())
@@ -207,6 +209,7 @@ SHAMapStoreImp::makeNodeStore(int readThreads)
// Create NodeStore with two backends to allow online deletion of
// data
auto dbr = std::make_unique<NodeStore::DatabaseRotatingImp>(
app_,
scheduler_,
readThreads,
std::move(writableBackend),
@@ -293,6 +296,8 @@ SHAMapStoreImp::run()
fullBelowCache_ = &(*app_.getNodeFamily().getFullBelowCache(0));
treeNodeCache_ = &(*app_.getNodeFamily().getTreeNodeCache(0));
bool const isMem = app_.config().mem_backend();
if (advisoryDelete_)
canDelete_ = state_db_.getCanDelete();
@@ -351,7 +356,7 @@ SHAMapStoreImp::run()
// will delete up to (not including) lastRotated
if (readyToRotate && !waitForImport)
{
JLOG(journal_.warn())
JLOG(journal_.debug())
<< "rotating validatedSeq " << validatedSeq << " lastRotated "
<< lastRotated << " deleteInterval " << deleteInterval_
<< " canDelete_ " << canDelete_ << " state "
@@ -395,7 +400,7 @@ SHAMapStoreImp::run()
// Only log if we completed without a "health" abort
JLOG(journal_.debug()) << validatedSeq << " freshened caches";
JLOG(journal_.trace()) << "Making a new backend";
JLOG(journal_.debug()) << "Making a new backend";
auto newBackend = makeBackendRotating();
JLOG(journal_.debug())
<< validatedSeq << " new backend " << newBackend->getName();
@@ -640,6 +645,33 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated)
if (!db)
Throw<std::runtime_error>("Failed to get relational database");
if (app_.config().useTxTables())
{
clearSql(
lastRotated,
"Transactions",
[&db]() -> std::optional<LedgerIndex> {
return db->getTransactionsMinLedgerSeq();
},
[&db](LedgerIndex min) -> void {
db->deleteTransactionsBeforeLedgerSeq(min);
});
if (healthWait() == stopping)
return;
clearSql(
lastRotated,
"AccountTransactions",
[&db]() -> std::optional<LedgerIndex> {
return db->getAccountTransactionsMinLedgerSeq();
},
[&db](LedgerIndex min) -> void {
db->deleteAccountTransactionsBeforeLedgerSeq(min);
});
if (healthWait() == stopping)
return;
}
clearSql(
lastRotated,
"Ledgers",
@@ -647,33 +679,6 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated)
[db](LedgerIndex min) -> void { db->deleteBeforeLedgerSeq(min); });
if (healthWait() == stopping)
return;
if (!app_.config().useTxTables())
return;
clearSql(
lastRotated,
"Transactions",
[&db]() -> std::optional<LedgerIndex> {
return db->getTransactionsMinLedgerSeq();
},
[&db](LedgerIndex min) -> void {
db->deleteTransactionsBeforeLedgerSeq(min);
});
if (healthWait() == stopping)
return;
clearSql(
lastRotated,
"AccountTransactions",
[&db]() -> std::optional<LedgerIndex> {
return db->getAccountTransactionsMinLedgerSeq();
},
[&db](LedgerIndex min) -> void {
db->deleteAccountTransactionsBeforeLedgerSeq(min);
});
if (healthWait() == stopping)
return;
}
SHAMapStoreImp::HealthResult
+49
View File
@@ -0,0 +1,49 @@
#include <ripple/app/misc/StateAccounting.h>
namespace ripple {
void
StateAccounting::mode(OperatingMode om)
{
std::lock_guard lock(mutex_);
auto now = std::chrono::steady_clock::now();
++counters_[static_cast<std::size_t>(om)].transitions;
if (om == OperatingMode::FULL &&
counters_[static_cast<std::size_t>(om)].transitions == 1)
{
initialSyncUs_ = std::chrono::duration_cast<std::chrono::microseconds>(
now - processStart_)
.count();
}
counters_[static_cast<std::size_t>(mode_)].dur +=
std::chrono::duration_cast<std::chrono::microseconds>(now - start_);
mode_ = om;
start_ = now;
}
void
StateAccounting::json(Json::Value& obj)
{
auto [counters, mode, start, initialSync] = getCounterData();
auto const current = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - start);
counters[static_cast<std::size_t>(mode)].dur += current;
obj[jss::state_accounting] = Json::objectValue;
for (std::size_t i = static_cast<std::size_t>(OperatingMode::DISCONNECTED);
i <= static_cast<std::size_t>(OperatingMode::FULL);
++i)
{
obj[jss::state_accounting][states_[i]] = Json::objectValue;
auto& state = obj[jss::state_accounting][states_[i]];
state[jss::transitions] = std::to_string(counters[i].transitions);
state[jss::duration_us] = std::to_string(counters[i].dur.count());
}
obj[jss::server_state_duration_us] = std::to_string(current.count());
if (initialSync)
obj[jss::initial_sync_duration_us] = std::to_string(initialSync);
}
} // namespace ripple
+99
View File
@@ -0,0 +1,99 @@
#ifndef RIPPLE_APP_MAIN_STATEACCOUNTING_H_INCLUDED
#define RIPPLE_APP_MAIN_STATEACCOUNTING_H_INCLUDED
#include <ripple/basics/chrono.h>
#include <ripple/beast/utility/Journal.h>
#include <ripple/json/json_value.h>
#include <ripple/protocol/jss.h>
#include <array>
#include <mutex>
namespace ripple {
// This is the primary interface into the "client" portion of the program.
// Code that wants to do normal operations on the network such as
// creating and monitoring accounts, creating transactions, and so on
// should use this interface. The RPC code will primarily be a light wrapper
// over this code.
//
// Eventually, it will check the node's operating mode (synched, unsynched,
// etectera) and defer to the correct means of processing. The current
// code assumes this node is synched (and will continue to do so until
// there's a functional network.
//
/** Specifies the mode under which the server believes it's operating.
This has implications about how the server processes transactions and
how it responds to requests (e.g. account balance request).
@note Other code relies on the numerical values of these constants; do
not change them without verifying each use and ensuring that it is
not a breaking change.
*/
enum class OperatingMode {
DISCONNECTED = 0, //!< not ready to process requests
CONNECTED = 1, //!< convinced we are talking to the network
SYNCING = 2, //!< fallen slightly behind
TRACKING = 3, //!< convinced we agree with the network
FULL = 4 //!< we have the ledger and can even validate
};
class StateAccounting
{
public:
constexpr static std::array<Json::StaticString const, 5> const states_ = {
{Json::StaticString("disconnected"),
Json::StaticString("connected"),
Json::StaticString("syncing"),
Json::StaticString("tracking"),
Json::StaticString("full")}};
struct Counters
{
explicit Counters() = default;
std::uint64_t transitions = 0;
std::chrono::microseconds dur = std::chrono::microseconds(0);
};
private:
OperatingMode mode_ = OperatingMode::DISCONNECTED;
std::array<Counters, 5> counters_;
mutable std::mutex mutex_;
std::chrono::steady_clock::time_point start_ =
std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point const processStart_ = start_;
std::uint64_t initialSyncUs_{0};
public:
explicit StateAccounting()
{
counters_[static_cast<std::size_t>(OperatingMode::DISCONNECTED)]
.transitions = 1;
}
//! Record state transition. Update duration spent in previous state.
void
mode(OperatingMode om);
//! Output state counters in JSON format.
void
json(Json::Value& obj);
using CounterData = std::tuple<
decltype(counters_),
decltype(mode_),
decltype(start_),
decltype(initialSyncUs_)>;
CounterData
getCounterData()
{
return {counters_, mode_, start_, initialSyncUs_};
}
};
} // namespace ripple
#endif
+1 -1
View File
@@ -111,7 +111,7 @@ public:
std::uint32_t minimumTxnInLedgerSA = 1000;
/// Number of transactions per ledger that fee escalation "works
/// towards".
std::uint32_t targetTxnInLedger = 256;
std::uint32_t targetTxnInLedger = 1000;
/** Optional maximum allowed value of transactions per ledger before
fee escalation kicks in. By default, the maximum is an emergent
property of network, validator, and consensus performance. This
+10 -14
View File
@@ -1710,6 +1710,15 @@ ValidatorList::calculateQuorum(
std::size_t effectiveUnlSize,
std::size_t seenSize)
{
// Use quorum if specified via command line.
if (minimumQuorum_ > 0)
{
JLOG(j_.warn()) << "Using potentially unsafe quorum of "
<< *minimumQuorum_
<< " as specified on the command line";
return *minimumQuorum_;
}
// Do not use achievable quorum until lists from all configured
// publishers are available
for (auto const& list : publisherLists_)
@@ -1752,21 +1761,8 @@ ValidatorList::calculateQuorum(
// Note that the negative UNL protocol introduced the
// AbsoluteMinimumQuorum which is 60% of the original UNL size. The
// effective quorum should not be lower than it.
auto quorum = static_cast<std::size_t>(std::max(
return static_cast<std::size_t>(std::max(
std::ceil(effectiveUnlSize * 0.8f), std::ceil(unlSize * 0.6f)));
// Use lower quorum specified via command line if the normal quorum
// appears unreachable based on the number of recently received
// validations.
if (minimumQuorum_) // < quorum && seenSize < quorum)
{
quorum = *minimumQuorum_;
JLOG(j_.warn()) << "Using unsafe quorum of " << quorum
<< " as specified in the command line";
}
return quorum;
}
TrustChanges
@@ -0,0 +1,853 @@
#ifndef RIPPLE_APP_RDB_BACKEND_FLATMAPDATABASE_H_INCLUDED
#define RIPPLE_APP_RDB_BACKEND_FLATMAPDATABASE_H_INCLUDED
#include <ripple/app/ledger/AcceptedLedger.h>
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/ledger/TransactionMaster.h>
#include <ripple/app/rdb/backend/SQLiteDatabase.h>
#include <algorithm>
#include <map>
#include <mutex>
#include <optional>
#include <shared_mutex>
#include <vector>
#include <boost/unordered/concurrent_flat_map.hpp>
namespace ripple {
struct base_uint_hasher
{
using result_type = std::size_t;
result_type
operator()(base_uint<256> const& value) const
{
return hardened_hash<>{}(value);
}
result_type
operator()(AccountID const& value) const
{
return hardened_hash<>{}(value);
}
};
class FlatmapDatabase : public SQLiteDatabase
{
private:
struct LedgerData
{
LedgerInfo info;
boost::unordered::
concurrent_flat_map<uint256, AccountTx, base_uint_hasher>
transactions;
};
struct AccountTxData
{
boost::unordered::
concurrent_flat_map<std::pair<uint32_t, uint32_t>, AccountTx>
transactions;
};
Application& app_;
Config const& config_;
JobQueue& jobQueue_;
boost::unordered::concurrent_flat_map<LedgerIndex, LedgerData> ledgers_;
boost::unordered::
concurrent_flat_map<uint256, LedgerIndex, base_uint_hasher>
ledgerHashToSeq_;
boost::unordered::concurrent_flat_map<uint256, AccountTx, base_uint_hasher>
transactionMap_;
boost::unordered::
concurrent_flat_map<AccountID, AccountTxData, base_uint_hasher>
accountTxMap_;
public:
FlatmapDatabase(Application& app, Config const& config, JobQueue& jobQueue)
: app_(app), config_(config), jobQueue_(jobQueue)
{
}
std::optional<LedgerIndex>
getMinLedgerSeq() override
{
std::optional<LedgerIndex> minSeq;
ledgers_.visit_all([&minSeq](auto const& pair) {
if (!minSeq || pair.first < *minSeq)
{
minSeq = pair.first;
}
});
return minSeq;
}
std::optional<LedgerIndex>
getTransactionsMinLedgerSeq() override
{
std::optional<LedgerIndex> minSeq;
transactionMap_.visit_all([&minSeq](auto const& pair) {
LedgerIndex seq = pair.second.second->getLgrSeq();
if (!minSeq || seq < *minSeq)
{
minSeq = seq;
}
});
return minSeq;
}
std::optional<LedgerIndex>
getAccountTransactionsMinLedgerSeq() override
{
std::optional<LedgerIndex> minSeq;
accountTxMap_.visit_all([&minSeq](auto const& pair) {
pair.second.transactions.visit_all([&minSeq](auto const& tx) {
if (!minSeq || tx.first.first < *minSeq)
{
minSeq = tx.first.first;
}
});
});
return minSeq;
}
std::optional<LedgerIndex>
getMaxLedgerSeq() override
{
std::optional<LedgerIndex> maxSeq;
ledgers_.visit_all([&maxSeq](auto const& pair) {
if (!maxSeq || pair.first > *maxSeq)
{
maxSeq = pair.first;
}
});
return maxSeq;
}
void
deleteTransactionByLedgerSeq(LedgerIndex ledgerSeq) override
{
ledgers_.visit(ledgerSeq, [this](auto& item) {
item.second.transactions.visit_all([this](auto const& txPair) {
transactionMap_.erase(txPair.first);
});
item.second.transactions.clear();
});
accountTxMap_.visit_all([ledgerSeq](auto& item) {
item.second.transactions.erase_if([ledgerSeq](auto const& tx) {
return tx.first.first == ledgerSeq;
});
});
}
void
deleteBeforeLedgerSeq(LedgerIndex ledgerSeq) override
{
ledgers_.erase_if([this, ledgerSeq](auto const& item) {
if (item.first < ledgerSeq)
{
item.second.transactions.visit_all([this](auto const& txPair) {
transactionMap_.erase(txPair.first);
});
ledgerHashToSeq_.erase(item.second.info.hash);
return true;
}
return false;
});
accountTxMap_.visit_all([ledgerSeq](auto& item) {
item.second.transactions.erase_if([ledgerSeq](auto const& tx) {
return tx.first.first < ledgerSeq;
});
});
}
void
deleteTransactionsBeforeLedgerSeq(LedgerIndex ledgerSeq) override
{
ledgers_.visit_all([this, ledgerSeq](auto& item) {
if (item.first < ledgerSeq)
{
item.second.transactions.visit_all([this](auto const& txPair) {
transactionMap_.erase(txPair.first);
});
item.second.transactions.clear();
}
});
accountTxMap_.visit_all([ledgerSeq](auto& item) {
item.second.transactions.erase_if([ledgerSeq](auto const& tx) {
return tx.first.first < ledgerSeq;
});
});
}
void
deleteAccountTransactionsBeforeLedgerSeq(LedgerIndex ledgerSeq) override
{
accountTxMap_.visit_all([ledgerSeq](auto& item) {
item.second.transactions.erase_if([ledgerSeq](auto const& tx) {
return tx.first.first < ledgerSeq;
});
});
}
std::size_t
getTransactionCount() override
{
return transactionMap_.size();
}
std::size_t
getAccountTransactionCount() override
{
std::size_t count = 0;
accountTxMap_.visit_all([&count](auto const& item) {
count += item.second.transactions.size();
});
return count;
}
CountMinMax
getLedgerCountMinMax() override
{
CountMinMax result{0, 0, 0};
ledgers_.visit_all([&result](auto const& item) {
result.numberOfRows++;
if (result.minLedgerSequence == 0 ||
item.first < result.minLedgerSequence)
{
result.minLedgerSequence = item.first;
}
if (item.first > result.maxLedgerSequence)
{
result.maxLedgerSequence = item.first;
}
});
return result;
}
bool
saveValidatedLedger(
std::shared_ptr<Ledger const> const& ledger,
bool current) override
{
try
{
LedgerData ledgerData;
ledgerData.info = ledger->info();
auto aLedger = std::make_shared<AcceptedLedger>(ledger, app_);
for (auto const& acceptedLedgerTx : *aLedger)
{
auto const& txn = acceptedLedgerTx->getTxn();
auto const& meta = acceptedLedgerTx->getMeta();
auto const& id = txn->getTransactionID();
std::string reason;
auto accTx = std::make_pair(
std::make_shared<ripple::Transaction>(txn, reason, app_),
std::make_shared<ripple::TxMeta>(meta));
ledgerData.transactions.emplace(id, accTx);
transactionMap_.emplace(id, accTx);
for (auto const& account : meta.getAffectedAccounts())
{
accountTxMap_.visit(account, [&](auto& data) {
data.second.transactions.emplace(
std::make_pair(
ledger->info().seq,
acceptedLedgerTx->getTxnSeq()),
accTx);
});
}
}
ledgers_.emplace(ledger->info().seq, std::move(ledgerData));
ledgerHashToSeq_.emplace(ledger->info().hash, ledger->info().seq);
if (current)
{
auto const cutoffSeq =
ledger->info().seq > app_.config().LEDGER_HISTORY
? ledger->info().seq - app_.config().LEDGER_HISTORY
: 0;
if (cutoffSeq > 0)
{
const std::size_t BATCH_SIZE = 128;
std::size_t deleted = 0;
ledgers_.erase_if([&](auto const& item) {
if (deleted >= BATCH_SIZE)
return false;
if (item.first < cutoffSeq)
{
item.second.transactions.visit_all(
[this](auto const& txPair) {
transactionMap_.erase(txPair.first);
});
ledgerHashToSeq_.erase(item.second.info.hash);
deleted++;
return true;
}
return false;
});
if (deleted > 0)
{
accountTxMap_.visit_all([cutoffSeq](auto& item) {
item.second.transactions.erase_if(
[cutoffSeq](auto const& tx) {
return tx.first.first < cutoffSeq;
});
});
}
app_.getLedgerMaster().clearPriorLedgers(cutoffSeq);
}
}
return true;
}
catch (std::exception const&)
{
deleteTransactionByLedgerSeq(ledger->info().seq);
return false;
}
}
std::optional<LedgerInfo>
getLedgerInfoByIndex(LedgerIndex ledgerSeq) override
{
std::optional<LedgerInfo> result;
ledgers_.visit(ledgerSeq, [&result](auto const& item) {
result = item.second.info;
});
return result;
}
std::optional<LedgerInfo>
getNewestLedgerInfo() override
{
std::optional<LedgerInfo> result;
ledgers_.visit_all([&result](auto const& item) {
if (!result || item.second.info.seq > result->seq)
{
result = item.second.info;
}
});
return result;
}
std::optional<LedgerInfo>
getLimitedOldestLedgerInfo(LedgerIndex ledgerFirstIndex) override
{
std::optional<LedgerInfo> result;
ledgers_.visit_all([&](auto const& item) {
if (item.first >= ledgerFirstIndex &&
(!result || item.first < result->seq))
{
result = item.second.info;
}
});
return result;
}
std::optional<LedgerInfo>
getLimitedNewestLedgerInfo(LedgerIndex ledgerFirstIndex) override
{
std::optional<LedgerInfo> result;
ledgers_.visit_all([&](auto const& item) {
if (item.first >= ledgerFirstIndex &&
(!result || item.first > result->seq))
{
result = item.second.info;
}
});
return result;
}
std::optional<LedgerInfo>
getLedgerInfoByHash(uint256 const& ledgerHash) override
{
std::optional<LedgerInfo> result;
ledgerHashToSeq_.visit(ledgerHash, [this, &result](auto const& item) {
ledgers_.visit(item.second, [&result](auto const& item) {
result = item.second.info;
});
});
return result;
}
uint256
getHashByIndex(LedgerIndex ledgerIndex) override
{
uint256 result;
ledgers_.visit(ledgerIndex, [&result](auto const& item) {
result = item.second.info.hash;
});
return result;
}
std::optional<LedgerHashPair>
getHashesByIndex(LedgerIndex ledgerIndex) override
{
std::optional<LedgerHashPair> result;
ledgers_.visit(ledgerIndex, [&result](auto const& item) {
result = LedgerHashPair{
item.second.info.hash, item.second.info.parentHash};
});
return result;
}
std::map<LedgerIndex, LedgerHashPair>
getHashesByIndex(LedgerIndex minSeq, LedgerIndex maxSeq) override
{
std::map<LedgerIndex, LedgerHashPair> result;
ledgers_.visit_all([&](auto const& item) {
if (item.first >= minSeq && item.first <= maxSeq)
{
result[item.first] = LedgerHashPair{
item.second.info.hash, item.second.info.parentHash};
}
});
return result;
}
std::variant<AccountTx, TxSearched>
getTransaction(
uint256 const& id,
std::optional<ClosedInterval<std::uint32_t>> const& range,
error_code_i& ec) override
{
std::variant<AccountTx, TxSearched> result = TxSearched::unknown;
transactionMap_.visit(id, [&](auto const& item) {
auto const& tx = item.second;
if (!range ||
(range->lower() <= tx.second->getLgrSeq() &&
tx.second->getLgrSeq() <= range->upper()))
{
result = tx;
}
else
{
result = TxSearched::all;
}
});
return result;
}
bool
ledgerDbHasSpace(Config const& config) override
{
return true; // In-memory database always has space
}
bool
transactionDbHasSpace(Config const& config) override
{
return true; // In-memory database always has space
}
std::uint32_t
getKBUsedAll() override
{
std::uint32_t size = sizeof(*this);
size += ledgers_.size() * (sizeof(LedgerIndex) + sizeof(LedgerData));
size +=
ledgerHashToSeq_.size() * (sizeof(uint256) + sizeof(LedgerIndex));
size += transactionMap_.size() * (sizeof(uint256) + sizeof(AccountTx));
accountTxMap_.visit_all([&size](auto const& item) {
size += sizeof(AccountID) + sizeof(AccountTxData);
size += item.second.transactions.size() * sizeof(AccountTx);
});
return size / 1024; // Convert to KB
}
std::uint32_t
getKBUsedLedger() override
{
std::uint32_t size =
ledgers_.size() * (sizeof(LedgerIndex) + sizeof(LedgerData));
size +=
ledgerHashToSeq_.size() * (sizeof(uint256) + sizeof(LedgerIndex));
return size / 1024;
}
std::uint32_t
getKBUsedTransaction() override
{
std::uint32_t size =
transactionMap_.size() * (sizeof(uint256) + sizeof(AccountTx));
accountTxMap_.visit_all([&size](auto const& item) {
size += sizeof(AccountID) + sizeof(AccountTxData);
size += item.second.transactions.size() * sizeof(AccountTx);
});
return size / 1024;
}
void
closeLedgerDB() override
{
// No-op for in-memory database
}
void
closeTransactionDB() override
{
// No-op for in-memory database
}
~FlatmapDatabase()
{
// Concurrent maps need visit_all
accountTxMap_.visit_all(
[](auto& pair) { pair.second.transactions.clear(); });
accountTxMap_.clear();
transactionMap_.clear();
ledgers_.visit_all(
[](auto& pair) { pair.second.transactions.clear(); });
ledgers_.clear();
ledgerHashToSeq_.clear();
}
std::vector<std::shared_ptr<Transaction>>
getTxHistory(LedgerIndex startIndex) override
{
std::vector<std::shared_ptr<Transaction>> result;
transactionMap_.visit_all([&](auto const& item) {
if (item.second.second->getLgrSeq() >= startIndex)
{
result.push_back(item.second.first);
}
});
std::sort(
result.begin(), result.end(), [](auto const& a, auto const& b) {
return a->getLedger() > b->getLedger();
});
if (result.size() > 20)
{
result.resize(20);
}
return result;
}
// Helper function to handle limits
template <typename Container>
void
applyLimit(Container& container, std::size_t limit, bool bUnlimited)
{
if (!bUnlimited && limit > 0 && container.size() > limit)
{
container.resize(limit);
}
}
AccountTxs
getOldestAccountTxs(AccountTxOptions const& options) override
{
AccountTxs result;
accountTxMap_.visit(options.account, [&](auto const& item) {
item.second.transactions.visit_all([&](auto const& tx) {
if (tx.first.first >= options.minLedger &&
tx.first.first <= options.maxLedger)
{
result.push_back(tx.second);
}
});
});
std::sort(
result.begin(), result.end(), [](auto const& a, auto const& b) {
return a.second->getLgrSeq() < b.second->getLgrSeq();
});
applyLimit(result, options.limit, options.bUnlimited);
return result;
}
AccountTxs
getNewestAccountTxs(AccountTxOptions const& options) override
{
AccountTxs result;
accountTxMap_.visit(options.account, [&](auto const& item) {
item.second.transactions.visit_all([&](auto const& tx) {
if (tx.first.first >= options.minLedger &&
tx.first.first <= options.maxLedger)
{
result.push_back(tx.second);
}
});
});
std::sort(
result.begin(), result.end(), [](auto const& a, auto const& b) {
return a.second->getLgrSeq() > b.second->getLgrSeq();
});
applyLimit(result, options.limit, options.bUnlimited);
return result;
}
MetaTxsList
getOldestAccountTxsB(AccountTxOptions const& options) override
{
MetaTxsList result;
accountTxMap_.visit(options.account, [&](auto const& item) {
item.second.transactions.visit_all([&](auto const& tx) {
if (tx.first.first >= options.minLedger &&
tx.first.first <= options.maxLedger)
{
result.emplace_back(
tx.second.first->getSTransaction()
->getSerializer()
.peekData(),
tx.second.second->getAsObject()
.getSerializer()
.peekData(),
tx.first.first);
}
});
});
std::sort(
result.begin(), result.end(), [](auto const& a, auto const& b) {
return std::get<2>(a) < std::get<2>(b);
});
applyLimit(result, options.limit, options.bUnlimited);
return result;
}
MetaTxsList
getNewestAccountTxsB(AccountTxOptions const& options) override
{
MetaTxsList result;
accountTxMap_.visit(options.account, [&](auto const& item) {
item.second.transactions.visit_all([&](auto const& tx) {
if (tx.first.first >= options.minLedger &&
tx.first.first <= options.maxLedger)
{
result.emplace_back(
tx.second.first->getSTransaction()
->getSerializer()
.peekData(),
tx.second.second->getAsObject()
.getSerializer()
.peekData(),
tx.first.first);
}
});
});
std::sort(
result.begin(), result.end(), [](auto const& a, auto const& b) {
return std::get<2>(a) > std::get<2>(b);
});
applyLimit(result, options.limit, options.bUnlimited);
return result;
}
std::pair<AccountTxs, std::optional<AccountTxMarker>>
oldestAccountTxPage(AccountTxPageOptions const& options) override
{
AccountTxs result;
std::optional<AccountTxMarker> marker;
accountTxMap_.visit(options.account, [&](auto const& item) {
std::vector<std::pair<std::pair<uint32_t, uint32_t>, AccountTx>>
txs;
item.second.transactions.visit_all([&](auto const& tx) {
if (tx.first.first >= options.minLedger &&
tx.first.first <= options.maxLedger)
{
txs.emplace_back(tx);
}
});
std::sort(txs.begin(), txs.end(), [](auto const& a, auto const& b) {
return a.first < b.first;
});
auto it = txs.begin();
if (options.marker)
{
it = std::find_if(txs.begin(), txs.end(), [&](auto const& tx) {
return tx.first.first == options.marker->ledgerSeq &&
tx.first.second == options.marker->txnSeq;
});
if (it != txs.end())
++it;
}
for (; it != txs.end() &&
(options.limit == 0 || result.size() < options.limit);
++it)
{
result.push_back(it->second);
}
if (it != txs.end())
{
marker = AccountTxMarker{it->first.first, it->first.second};
}
});
return {result, marker};
}
std::pair<AccountTxs, std::optional<AccountTxMarker>>
newestAccountTxPage(AccountTxPageOptions const& options) override
{
AccountTxs result;
std::optional<AccountTxMarker> marker;
accountTxMap_.visit(options.account, [&](auto const& item) {
std::vector<std::pair<std::pair<uint32_t, uint32_t>, AccountTx>>
txs;
item.second.transactions.visit_all([&](auto const& tx) {
if (tx.first.first >= options.minLedger &&
tx.first.first <= options.maxLedger)
{
txs.emplace_back(tx);
}
});
std::sort(txs.begin(), txs.end(), [](auto const& a, auto const& b) {
return a.first > b.first;
});
auto it = txs.begin();
if (options.marker)
{
it = std::find_if(txs.begin(), txs.end(), [&](auto const& tx) {
return tx.first.first == options.marker->ledgerSeq &&
tx.first.second == options.marker->txnSeq;
});
if (it != txs.end())
++it;
}
for (; it != txs.end() &&
(options.limit == 0 || result.size() < options.limit);
++it)
{
result.push_back(it->second);
}
if (it != txs.end())
{
marker = AccountTxMarker{it->first.first, it->first.second};
}
});
return {result, marker};
}
std::pair<MetaTxsList, std::optional<AccountTxMarker>>
oldestAccountTxPageB(AccountTxPageOptions const& options) override
{
MetaTxsList result;
std::optional<AccountTxMarker> marker;
accountTxMap_.visit(options.account, [&](auto const& item) {
std::vector<std::tuple<uint32_t, uint32_t, AccountTx>> txs;
item.second.transactions.visit_all([&](auto const& tx) {
if (tx.first.first >= options.minLedger &&
tx.first.first <= options.maxLedger)
{
txs.emplace_back(
tx.first.first, tx.first.second, tx.second);
}
});
std::sort(txs.begin(), txs.end());
auto it = txs.begin();
if (options.marker)
{
it = std::find_if(txs.begin(), txs.end(), [&](auto const& tx) {
return std::get<0>(tx) == options.marker->ledgerSeq &&
std::get<1>(tx) == options.marker->txnSeq;
});
if (it != txs.end())
++it;
}
for (; it != txs.end() &&
(options.limit == 0 || result.size() < options.limit);
++it)
{
const auto& [_, __, tx] = *it;
result.emplace_back(
tx.first->getSTransaction()->getSerializer().peekData(),
tx.second->getAsObject().getSerializer().peekData(),
std::get<0>(*it));
}
if (it != txs.end())
{
marker = AccountTxMarker{std::get<0>(*it), std::get<1>(*it)};
}
});
return {result, marker};
}
std::pair<MetaTxsList, std::optional<AccountTxMarker>>
newestAccountTxPageB(AccountTxPageOptions const& options) override
{
MetaTxsList result;
std::optional<AccountTxMarker> marker;
accountTxMap_.visit(options.account, [&](auto const& item) {
std::vector<std::tuple<uint32_t, uint32_t, AccountTx>> txs;
item.second.transactions.visit_all([&](auto const& tx) {
if (tx.first.first >= options.minLedger &&
tx.first.first <= options.maxLedger)
{
txs.emplace_back(
tx.first.first, tx.first.second, tx.second);
}
});
std::sort(txs.begin(), txs.end(), std::greater<>());
auto it = txs.begin();
if (options.marker)
{
it = std::find_if(txs.begin(), txs.end(), [&](auto const& tx) {
return std::get<0>(tx) == options.marker->ledgerSeq &&
std::get<1>(tx) == options.marker->txnSeq;
});
if (it != txs.end())
++it;
}
for (; it != txs.end() &&
(options.limit == 0 || result.size() < options.limit);
++it)
{
const auto& [_, __, tx] = *it;
result.emplace_back(
tx.first->getSTransaction()->getSerializer().peekData(),
tx.second->getAsObject().getSerializer().peekData(),
std::get<0>(*it));
}
if (it != txs.end())
{
marker = AccountTxMarker{std::get<0>(*it), std::get<1>(*it)};
}
});
return {result, marker};
}
};
// Factory function
std::unique_ptr<SQLiteDatabase>
getFlatmapDatabase(Application& app, Config const& config, JobQueue& jobQueue)
{
return std::make_unique<FlatmapDatabase>(app, config, jobQueue);
}
} // namespace ripple
#endif // RIPPLE_APP_RDB_BACKEND_FLATMAPDATABASE_H_INCLUDED
File diff suppressed because it is too large. Load diff
@@ -1132,7 +1132,7 @@ accountTxPage(
{
sql = boost::str(
boost::format(
prefix + (R"(AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u'
prefix + (R"(AccountTransactions.LedgerSeq BETWEEN %u AND %u
ORDER BY AccountTransactions.LedgerSeq %s,
AccountTransactions.TxnSeq %s
LIMIT %u;)")) %
@@ -1155,12 +1155,14 @@ accountTxPage(
FROM AccountTransactions, Transactions WHERE
(AccountTransactions.TransID = Transactions.TransID AND
AccountTransactions.Account = '%s' AND
AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u')
OR
AccountTransactions.LedgerSeq BETWEEN %u AND %u)
UNION
SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,Status,RawTxn,TxnMeta
FROM AccountTransactions, Transactions WHERE
(AccountTransactions.TransID = Transactions.TransID AND
AccountTransactions.Account = '%s' AND
AccountTransactions.LedgerSeq = '%u' AND
AccountTransactions.TxnSeq %s '%u')
AccountTransactions.LedgerSeq = %u AND
AccountTransactions.TxnSeq %s %u)
ORDER BY AccountTransactions.LedgerSeq %s,
AccountTransactions.TxnSeq %s
LIMIT %u;
@@ -601,11 +601,44 @@ SQLiteDatabaseImp::deleteTransactionByLedgerSeq(LedgerIndex ledgerSeq)
void
SQLiteDatabaseImp::deleteBeforeLedgerSeq(LedgerIndex ledgerSeq)
{
// Get a reference to the pinned ledgers set for quick lookups
RangeSet<std::uint32_t> pinnedLedgers;
{
auto& ledgerMaster = app_.getLedgerMaster();
// Use public API to get the pinned ledgers
pinnedLedgers = ledgerMaster.getPinnedLedgersRangeSet();
}
if (existsLedger())
{
auto db = checkoutLedger();
detail::deleteBeforeLedgerSeq(
*db, detail::TableType::Ledgers, ledgerSeq);
// Check if any ledgers in the range to be deleted are pinned
bool hasPinnedLedgers = false;
for (LedgerIndex seq = 1; seq < ledgerSeq && !hasPinnedLedgers; ++seq)
{
if (boost::icl::contains(pinnedLedgers, seq))
hasPinnedLedgers = true;
}
if (!hasPinnedLedgers)
{
// No pinned ledgers in the range, proceed with normal delete
detail::deleteBeforeLedgerSeq(
*db, detail::TableType::Ledgers, ledgerSeq);
}
else
{
// Delete ledgers individually, skipping pinned ones
for (LedgerIndex seq = 1; seq < ledgerSeq; ++seq)
{
if (!boost::icl::contains(pinnedLedgers, seq))
{
detail::deleteByLedgerSeq(
*db, detail::TableType::Ledgers, seq);
}
}
}
return;
}
@@ -614,8 +647,39 @@ SQLiteDatabaseImp::deleteBeforeLedgerSeq(LedgerIndex ledgerSeq)
iterateLedgerBack(
seqToShardIndex(ledgerSeq),
[&](soci::session& session, std::uint32_t shardIndex) {
detail::deleteBeforeLedgerSeq(
session, detail::TableType::Ledgers, ledgerSeq);
LedgerIndex firstSeq = firstLedgerSeq(shardIndex);
LedgerIndex lastSeq =
std::min(lastLedgerSeq(shardIndex), ledgerSeq - 1);
// Check if any ledgers in this shard's range are pinned
bool hasPinnedLedgers = false;
for (LedgerIndex seq = firstSeq;
seq <= lastSeq && !hasPinnedLedgers;
++seq)
{
if (boost::icl::contains(pinnedLedgers, seq))
hasPinnedLedgers = true;
}
if (!hasPinnedLedgers)
{
// No pinned ledgers in this shard range, proceed with
// normal delete
detail::deleteBeforeLedgerSeq(
session, detail::TableType::Ledgers, ledgerSeq);
}
else
{
// Delete ledgers individually, skipping pinned ones
for (LedgerIndex seq = firstSeq; seq <= lastSeq; ++seq)
{
if (!boost::icl::contains(pinnedLedgers, seq))
{
detail::deleteByLedgerSeq(
session, detail::TableType::Ledgers, seq);
}
}
}
return true;
});
}
@@ -627,11 +691,43 @@ SQLiteDatabaseImp::deleteTransactionsBeforeLedgerSeq(LedgerIndex ledgerSeq)
if (!useTxTables_)
return;
// Get a reference to the pinned ledgers set for quick lookups
RangeSet<std::uint32_t> pinnedLedgers;
{
auto& ledgerMaster = app_.getLedgerMaster();
pinnedLedgers = ledgerMaster.getPinnedLedgersRangeSet();
}
if (existsTransaction())
{
auto db = checkoutTransaction();
detail::deleteBeforeLedgerSeq(
*db, detail::TableType::Transactions, ledgerSeq);
// Check if any ledgers in the range to be deleted are pinned
bool hasPinnedLedgers = false;
for (LedgerIndex seq = 1; seq < ledgerSeq && !hasPinnedLedgers; ++seq)
{
if (boost::icl::contains(pinnedLedgers, seq))
hasPinnedLedgers = true;
}
if (!hasPinnedLedgers)
{
// No pinned ledgers in the range, proceed with normal delete
detail::deleteBeforeLedgerSeq(
*db, detail::TableType::Transactions, ledgerSeq);
}
else
{
// Delete transaction data individually, skipping pinned ledgers
for (LedgerIndex seq = 1; seq < ledgerSeq; ++seq)
{
if (!boost::icl::contains(pinnedLedgers, seq))
{
detail::deleteByLedgerSeq(
*db, detail::TableType::Transactions, seq);
}
}
}
return;
}
@@ -640,8 +736,40 @@ SQLiteDatabaseImp::deleteTransactionsBeforeLedgerSeq(LedgerIndex ledgerSeq)
iterateTransactionBack(
seqToShardIndex(ledgerSeq),
[&](soci::session& session, std::uint32_t shardIndex) {
detail::deleteBeforeLedgerSeq(
session, detail::TableType::Transactions, ledgerSeq);
LedgerIndex firstSeq = firstLedgerSeq(shardIndex);
LedgerIndex lastSeq =
std::min(lastLedgerSeq(shardIndex), ledgerSeq - 1);
// Check if any ledgers in this shard's range are pinned
bool hasPinnedLedgers = false;
for (LedgerIndex seq = firstSeq;
seq <= lastSeq && !hasPinnedLedgers;
++seq)
{
if (boost::icl::contains(pinnedLedgers, seq))
hasPinnedLedgers = true;
}
if (!hasPinnedLedgers)
{
// No pinned ledgers in this shard range, proceed with
// normal delete
detail::deleteBeforeLedgerSeq(
session, detail::TableType::Transactions, ledgerSeq);
}
else
{
// Delete transaction data individually, skipping pinned
// ledgers
for (LedgerIndex seq = firstSeq; seq <= lastSeq; ++seq)
{
if (!boost::icl::contains(pinnedLedgers, seq))
{
detail::deleteByLedgerSeq(
session, detail::TableType::Transactions, seq);
}
}
}
return true;
});
}
@@ -654,11 +782,44 @@ SQLiteDatabaseImp::deleteAccountTransactionsBeforeLedgerSeq(
if (!useTxTables_)
return;
// Get a reference to the pinned ledgers set for quick lookups
RangeSet<std::uint32_t> pinnedLedgers;
{
auto& ledgerMaster = app_.getLedgerMaster();
pinnedLedgers = ledgerMaster.getPinnedLedgersRangeSet();
}
if (existsTransaction())
{
auto db = checkoutTransaction();
detail::deleteBeforeLedgerSeq(
*db, detail::TableType::AccountTransactions, ledgerSeq);
// Check if any ledgers in the range to be deleted are pinned
bool hasPinnedLedgers = false;
for (LedgerIndex seq = 1; seq < ledgerSeq && !hasPinnedLedgers; ++seq)
{
if (boost::icl::contains(pinnedLedgers, seq))
hasPinnedLedgers = true;
}
if (!hasPinnedLedgers)
{
// No pinned ledgers in the range, proceed with normal delete
detail::deleteBeforeLedgerSeq(
*db, detail::TableType::AccountTransactions, ledgerSeq);
}
else
{
// Delete account transaction data individually, skipping pinned
// ledgers
for (LedgerIndex seq = 1; seq < ledgerSeq; ++seq)
{
if (!boost::icl::contains(pinnedLedgers, seq))
{
detail::deleteByLedgerSeq(
*db, detail::TableType::AccountTransactions, seq);
}
}
}
return;
}
@@ -667,8 +828,44 @@ SQLiteDatabaseImp::deleteAccountTransactionsBeforeLedgerSeq(
iterateTransactionBack(
seqToShardIndex(ledgerSeq),
[&](soci::session& session, std::uint32_t shardIndex) {
detail::deleteBeforeLedgerSeq(
session, detail::TableType::AccountTransactions, ledgerSeq);
LedgerIndex firstSeq = firstLedgerSeq(shardIndex);
LedgerIndex lastSeq =
std::min(lastLedgerSeq(shardIndex), ledgerSeq - 1);
// Check if any ledgers in this shard's range are pinned
bool hasPinnedLedgers = false;
for (LedgerIndex seq = firstSeq;
seq <= lastSeq && !hasPinnedLedgers;
++seq)
{
if (boost::icl::contains(pinnedLedgers, seq))
hasPinnedLedgers = true;
}
if (!hasPinnedLedgers)
{
// No pinned ledgers in this shard range, proceed with
// normal delete
detail::deleteBeforeLedgerSeq(
session,
detail::TableType::AccountTransactions,
ledgerSeq);
}
else
{
// Delete account transaction data individually, skipping
// pinned ledgers
for (LedgerIndex seq = firstSeq; seq <= lastSeq; ++seq)
{
if (!boost::icl::contains(pinnedLedgers, seq))
{
detail::deleteByLedgerSeq(
session,
detail::TableType::AccountTransactions,
seq);
}
}
}
return true;
});
}
@@ -19,6 +19,8 @@
#include <ripple/app/main/Application.h>
#include <ripple/app/rdb/RelationalDatabase.h>
#include <ripple/app/rdb/backend/FlatmapDatabase.h>
#include <ripple/app/rdb/backend/RWDBDatabase.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/nodestore/DatabaseShard.h>
@@ -38,6 +40,8 @@ RelationalDatabase::init(
{
bool use_sqlite = false;
bool use_postgres = false;
bool use_rwdb = false;
bool use_flatmap = false;
if (config.reporting())
{
@@ -52,6 +56,14 @@ RelationalDatabase::init(
{
use_sqlite = true;
}
else if (boost::iequals(get(rdb_section, "backend"), "rwdb"))
{
use_rwdb = true;
}
else if (boost::iequals(get(rdb_section, "backend"), "flatmap"))
{
use_flatmap = true;
}
else
{
Throw<std::runtime_error>(
@@ -73,6 +85,14 @@ RelationalDatabase::init(
{
return getPostgresDatabase(app, config, jobQueue);
}
else if (use_rwdb)
{
return getRWDBDatabase(app, config, jobQueue);
}
else if (use_flatmap)
{
return getFlatmapDatabase(app, config, jobQueue);
}
return std::unique_ptr<RelationalDatabase>();
}
+16 -12
View File
@@ -103,22 +103,24 @@ updateLedgerDBs(
for (auto const& item : ledger->txs)
{
if (stop)
if (stop.load(std::memory_order_relaxed))
return false;
auto const txID{item.first->getTransactionID()};
auto const sTxID{to_string(txID)};
auto const txMeta{std::make_shared<TxMeta>(
txID, ledger->seq(), *item.second)};
TxMeta const txMeta{
item.first->getTransactionID(),
ledger->seq(),
*item.second};
auto const sTxID = to_string(txMeta.getTxID());
session << "DELETE FROM AccountTransactions "
"WHERE TransID = :txID;",
soci::use(sTxID);
auto const& accounts = txMeta->getAffectedAccounts();
auto const& accounts = txMeta.getAffectedAccounts();
if (!accounts.empty())
{
auto const sTxnSeq{std::to_string(txMeta->getIndex())};
auto const sTxnSeq{std::to_string(txMeta.getIndex())};
auto const s{boost::str(
boost::format("('%s','%s',%s,%s)") % sTxID % "%s" %
sSeq % sTxnSeq)};
@@ -173,6 +175,11 @@ updateLedgerDBs(
auto const sParentHash{to_string(ledger->info().parentHash)};
auto const sDrops{to_string(ledger->info().drops)};
auto const closingTime{
ledger->info().closeTime.time_since_epoch().count()};
auto const prevClosingTime{
ledger->info().parentCloseTime.time_since_epoch().count()};
auto const closeTimeRes{ledger->info().closeTimeResolution.count()};
auto const sAccountHash{to_string(ledger->info().accountHash)};
auto const sTxHash{to_string(ledger->info().txHash)};
@@ -188,11 +195,8 @@ updateLedgerDBs(
":closingTime, :prevClosingTime, :closeTimeRes,"
":closeFlags, :accountSetHash, :transSetHash);",
soci::use(sHash), soci::use(ledgerSeq), soci::use(sParentHash),
soci::use(sDrops),
soci::use(ledger->info().closeTime.time_since_epoch().count()),
soci::use(
ledger->info().parentCloseTime.time_since_epoch().count()),
soci::use(ledger->info().closeTimeResolution.count()),
soci::use(sDrops), soci::use(closingTime),
soci::use(prevClosingTime), soci::use(closeTimeRes),
soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
soci::use(sTxHash);
+4 -3
View File
@@ -205,19 +205,20 @@ insertPeerReservation(
PublicKey const& nodeId,
std::string const& description)
{
auto const sNodeId = toBase58(TokenType::NodePublic, nodeId);
session << "INSERT INTO PeerReservations (PublicKey, Description) "
"VALUES (:nodeId, :desc) "
"ON CONFLICT (PublicKey) DO UPDATE SET "
"Description=excluded.Description",
soci::use(toBase58(TokenType::NodePublic, nodeId)),
soci::use(description);
soci::use(sNodeId), soci::use(description);
}
void
deletePeerReservation(soci::session& session, PublicKey const& nodeId)
{
auto const sNodeId = toBase58(TokenType::NodePublic, nodeId);
session << "DELETE FROM PeerReservations WHERE PublicKey = :nodeId",
soci::use(toBase58(TokenType::NodePublic, nodeId));
soci::use(sNodeId);
}
bool
+7 -1
View File
@@ -194,8 +194,14 @@ ETLSource::onResolve(
{
boost::beast::get_lowest_layer(*ws_).expires_after(
std::chrono::seconds(30));
// Use async_connect with the entire results
boost::beast::get_lowest_layer(*ws_).async_connect(
results, [this](auto ec, auto ep) { onConnect(ec, ep); });
results,
[this](
boost::beast::error_code ec,
boost::asio::ip::tcp::resolver::results_type::endpoint_type
ep) { onConnect(ec, ep); });
}
}
+9 -1
View File
@@ -458,6 +458,13 @@ Change::activateXahauGenesis()
bool const isTest =
(ctx_.tx.getFlags() & tfTestSuite) && ctx_.app.config().standalone();
// RH NOTE: we'll only configure xahau governance structure on networks that
// begin with 2133... so production xahau: 21337 and its testnet 21338
// with 21330-21336 and 21339 also valid and reserved for dev nets etc.
// all other Network IDs will be conventionally configured.
if ((ctx_.app.config().NETWORK_ID / 10) != 2133 && !isTest)
return;
auto [ng_entries, l1_entries, l2_entries, gov_params] =
normalizeXahauGenesis(
isTest ? TestNonGovernanceDistribution : NonGovernanceDistribution,
@@ -580,7 +587,8 @@ Change::activateXahauGenesis()
wasmBytes, // wasm to verify
loggerStream,
"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
ctx_.view().rules().enabled(featureHooksUpdate1) ? 1 : 0);
(ctx_.view().rules().enabled(featureHooksUpdate1) ? 1 : 0) +
(ctx_.view().rules().enabled(fix20250131) ? 2 : 0));
if (!result)
{
+5 -2
View File
@@ -45,8 +45,11 @@ ClaimReward::preflight(PreflightContext const& ctx)
return ret;
// can have flag 1 set to opt-out of rewards
if (ctx.tx.isFieldPresent(sfFlags) &&
ctx.tx.getFieldU32(sfFlags) > tfOptOut)
auto const invalidFlags = ctx.rules.enabled(fixRewardClaimFlags)
? (ctx.tx.getFlags() & tfClaimRewardMask)
: (ctx.tx.isFieldPresent(sfFlags) &&
ctx.tx.getFieldU32(sfFlags) > tfOptOut);
if (invalidFlags)
return temINVALID_FLAG;
if (ctx.tx.isFieldPresent(sfIssuer) &&
+1
View File
@@ -23,6 +23,7 @@
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/st.h>
namespace ripple {
+42
View File
@@ -24,12 +24,15 @@
#include <ripple/basics/base64.h>
#include <ripple/json/json_reader.h>
#include <ripple/json/json_value.h>
#include <ripple/json/to_string.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Import.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/STTx.h>
#include <ripple/protocol/STValidation.h>
#include <ripple/protocol/st.h>
#include <algorithm>
#include <charconv>
#include <iostream>
@@ -886,6 +889,45 @@ Import::preclaim(PreclaimContext const& ctx)
}
auto const& sle = ctx.view.read(keylet::account(ctx.tx[sfAccount]));
auto const tt = stpTrans->getTxnType();
if ((tt == ttSIGNER_LIST_SET || tt == ttREGULAR_KEY_SET) &&
ctx.view.rules().enabled(fixReduceImport) && sle)
{
// blackhole check
do
{
// if master key is not set then it is not blackholed
if (!(sle->getFlags() & lsfDisableMaster))
break;
// if a regular key is set then it must be acc 0, 1, or 2 otherwise
// not blackholed
if (sle->isFieldPresent(sfRegularKey))
{
AccountID rk = sle->getAccountID(sfRegularKey);
static const AccountID ACCOUNT_ZERO(0);
static const AccountID ACCOUNT_ONE(1);
static const AccountID ACCOUNT_TWO(2);
if (rk != ACCOUNT_ZERO && rk != ACCOUNT_ONE &&
rk != ACCOUNT_TWO)
break;
}
// if a signer list is set then it's not blackholed
auto const signerListKeylet = keylet::signers(ctx.tx[sfAccount]);
if (ctx.view.exists(signerListKeylet))
break;
// execution to here means it's blackholed
JLOG(ctx.j.warn())
<< "Import: during preclaim target account is blackholed "
<< ctx.tx[sfAccount] << ", bailing.";
return tefIMPORT_BLACKHOLED;
} while (0);
}
if (sle && sle->isFieldPresent(sfImportSequence))
{
uint32_t sleImportSequence = sle->getFieldU32(sfImportSequence);
+24
View File
@@ -319,6 +319,17 @@ public:
beast::Journal const&);
};
/**
* @brief Invariant: Validates several invariants for NFToken pages.
*
* The following checks are made:
* - The page is correctly associated with the owner.
* - The page is correctly ordered between the next and previous links.
* - The page contains at least one and no more than 32 NFTokens.
* - The NFTokens on this page do not belong on a lower or higher page.
* - The NFTokens are correctly sorted on the page.
* - Each URI, if present, is not empty.
*/
class ValidNFTokenPage
{
bool badEntry_ = false;
@@ -343,6 +354,19 @@ public:
beast::Journal const&);
};
/**
* @brief Invariant: Validates counts of NFTokens after all transaction types.
*
* The following checks are made:
* - The number of minted or burned NFTokens can only be changed by
* NFTokenMint or NFTokenBurn transactions.
* - A successful NFTokenMint must increase the number of NFTokens.
* - A failed NFTokenMint must not change the number of minted NFTokens.
* - An NFTokenMint transaction cannot change the number of burned NFTokens.
* - A successful NFTokenBurn must increase the number of burned NFTokens.
* - A failed NFTokenBurn must not change the number of burned NFTokens.
* - An NFTokenBurn transaction cannot change the number of minted NFTokens.
*/
class NFTokenCountTracking
{
std::uint32_t beforeMintedTotal = 0;
+1
View File
@@ -22,6 +22,7 @@
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/st.h>
namespace ripple {
+13
View File
@@ -23,6 +23,9 @@
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/st.h>
namespace ripple {
TxConsequences
@@ -69,6 +72,16 @@ Remit::preflight(PreflightContext const& ctx)
return temREDUNDANT;
}
if (ctx.rules.enabled(fix20250131))
{
if (!dstID || dstID == noAccount())
{
JLOG(ctx.j.warn())
<< "Malformed transaction: Remit to invalid account.";
return temMALFORMED;
}
}
if (ctx.tx.isFieldPresent(sfInform))
{
AccountID const infID = ctx.tx.getAccountID(sfInform);
+2 -2
View File
@@ -17,8 +17,8 @@
*/
//==============================================================================
#ifndef RIPPLE_TX_SIMPLE_PAYMENT_H_INCLUDED
#define RIPPLE_TX_SIMPLE_PAYMENT_H_INCLUDED
#ifndef RIPPLE_TX_REMIT_H_INCLUDED
#define RIPPLE_TX_REMIT_H_INCLUDED
#include <ripple/app/tx/impl/Transactor.h>
#include <ripple/basics/Log.h>
+67 -7
View File
@@ -225,6 +225,7 @@ SetHook::inferOperation(STObject const& hookSetObj)
!hookSetObj.isFieldPresent(sfHookNamespace) &&
!hookSetObj.isFieldPresent(sfHookParameters) &&
!hookSetObj.isFieldPresent(sfHookOn) &&
!hookSetObj.isFieldPresent(sfHookCanEmit) &&
!hookSetObj.isFieldPresent(sfHookApiVersion) &&
!hookSetObj.isFieldPresent(sfFlags))
return hsoNOOP;
@@ -259,6 +260,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
if (hookSetObj.isFieldPresent(sfHookGrants) ||
hookSetObj.isFieldPresent(sfHookParameters) ||
hookSetObj.isFieldPresent(sfHookOn) ||
hookSetObj.isFieldPresent(sfHookCanEmit) ||
hookSetObj.isFieldPresent(sfHookApiVersion) ||
!hookSetObj.isFieldPresent(sfFlags) ||
!hookSetObj.isFieldPresent(sfHookNamespace))
@@ -288,6 +290,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
if (hookSetObj.isFieldPresent(sfHookGrants) ||
hookSetObj.isFieldPresent(sfHookParameters) ||
hookSetObj.isFieldPresent(sfHookOn) ||
hookSetObj.isFieldPresent(sfHookCanEmit) ||
hookSetObj.isFieldPresent(sfHookApiVersion) ||
hookSetObj.isFieldPresent(sfHookNamespace) ||
!hookSetObj.isFieldPresent(sfFlags))
@@ -452,6 +455,13 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
return false;
}
// validate sfHookCanEmit
// HookCanEmit field is an optional field for backward compatibility
if (!hookSetObj.isFieldPresent(sfHookCanEmit))
{
// pass
}
// finally validate web assembly byte code
{
if (!hookSetObj.isFieldPresent(sfCreateCode))
@@ -479,7 +489,8 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
hook, // wasm to verify
logger,
hsacc,
ctx.rules.enabled(featureHooksUpdate1) ? 1 : 0);
(ctx.rules.enabled(featureHooksUpdate1) ? 1 : 0) +
(ctx.rules.enabled(fix20250131) ? 2 : 0));
if (ctx.j.trace())
{
@@ -713,6 +724,10 @@ SetHook::preflight(PreflightContext const& ctx)
allBlank = false;
if (!ctx.rules.enabled(featureHookCanEmit) &&
hookSetObj.isFieldPresent(sfHookCanEmit))
return temDISABLED;
for (auto const& hookSetElement : hookSetObj)
{
auto const& name = hookSetElement.getFName();
@@ -720,7 +735,8 @@ SetHook::preflight(PreflightContext const& ctx)
if (name != sfCreateCode && name != sfHookHash &&
name != sfHookNamespace && name != sfHookParameters &&
name != sfHookOn && name != sfHookGrants &&
name != sfHookApiVersion && name != sfFlags)
name != sfHookApiVersion && name != sfFlags &&
name != sfHookCanEmit)
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOK_INVALID_FIELD << ")["
@@ -1221,6 +1237,10 @@ SetHook::setHook()
std::optional<uint256> newHookOn;
std::optional<uint256> defHookOn;
std::optional<uint256> oldHookCanEmit;
std::optional<uint256> newHookCanEmit;
std::optional<uint256> defHookCanEmit;
// when hsoCREATE is invoked it populates this variable in case the hook
// definition already exists and the operation falls through into a
// hsoINSTALL operation instead
@@ -1281,6 +1301,14 @@ SetHook::setHook()
oldHookOn = oldHook->get().getFieldH256(sfHookOn);
else if (defHookOn)
oldHookOn = *defHookOn;
if (oldDefSLE && oldDefSLE->isFieldPresent(sfHookCanEmit))
defHookCanEmit = oldDefSLE->getFieldH256(sfHookCanEmit);
if (oldHook && oldHook->get().isFieldPresent(sfHookCanEmit))
oldHookCanEmit = oldHook->get().getFieldH256(sfHookCanEmit);
else if (defHookCanEmit)
oldHookCanEmit = *defHookCanEmit;
}
// in preparation for three way merge populate fields if they are
@@ -1297,6 +1325,9 @@ SetHook::setHook()
if (hookSetObj->get().isFieldPresent(sfHookOn))
newHookOn = hookSetObj->get().getFieldH256(sfHookOn);
if (hookSetObj->get().isFieldPresent(sfHookCanEmit))
newHookCanEmit = hookSetObj->get().getFieldH256(sfHookCanEmit);
if (hookSetObj->get().isFieldPresent(sfHookNamespace))
{
newNamespace = hookSetObj->get().getFieldH256(sfHookNamespace);
@@ -1314,13 +1345,14 @@ SetHook::setHook()
}
else if (op == hsoNSDELETE && newDirKeylet)
{
printf("Marking a namespace for destruction.... NSDELETE\n");
JLOG(ctx.j.trace())
<< "Marking a namespace for destruction.... NSDELETE";
namespacesToDestroy.emplace(*newNamespace);
}
else if (oldDirKeylet)
{
printf(
"Marking a namespace for destruction.... non-NSDELETE\n");
JLOG(ctx.j.trace())
<< "Marking a namespace for destruction.... non-NSDELETE";
namespacesToDestroy.emplace(*oldNamespace);
}
else
@@ -1406,6 +1438,10 @@ SetHook::setHook()
if (oldHook->get().isFieldPresent(sfHookOn))
newHook.setFieldH256(
sfHookOn, oldHook->get().getFieldH256(sfHookOn));
if (oldHook->get().isFieldPresent(sfHookCanEmit))
newHook.setFieldH256(
sfHookCanEmit,
oldHook->get().getFieldH256(sfHookCanEmit));
if (oldHook->get().isFieldPresent(sfHookNamespace))
newHook.setFieldH256(
sfHookNamespace,
@@ -1435,6 +1471,19 @@ SetHook::setHook()
newHook.setFieldH256(sfHookOn, *newHookOn);
}
// set the hookcanemit field if it differs from definition
if (newHookCanEmit)
{
if (defHookCanEmit.has_value() &&
*defHookCanEmit == *newHookCanEmit)
{
if (newHook.isFieldPresent(sfHookCanEmit))
newHook.makeFieldAbsent(sfHookCanEmit);
}
else
newHook.setFieldH256(sfHookCanEmit, *newHookCanEmit);
}
// parameters
if (hookSetObj->get().isFieldPresent(sfHookParameters) &&
hookSetObj->get().getFieldArray(sfHookParameters).empty())
@@ -1584,6 +1633,9 @@ SetHook::setHook()
auto newHookDef = std::make_shared<SLE>(keylet);
newHookDef->setFieldH256(sfHookHash, *createHookHash);
newHookDef->setFieldH256(sfHookOn, *newHookOn);
if (newHookCanEmit)
newHookDef->setFieldH256(
sfHookCanEmit, *newHookCanEmit);
newHookDef->setFieldH256(sfHookNamespace, *newNamespace);
newHookDef->setFieldArray(
sfHookParameters,
@@ -1677,6 +1729,8 @@ SetHook::setHook()
// change which definition we're using to the new target
defNamespace = newDefSLE->getFieldH256(sfHookNamespace);
defHookOn = newDefSLE->getFieldH256(sfHookOn);
if (newDefSLE->isFieldPresent(sfHookCanEmit))
defHookCanEmit = newDefSLE->getFieldH256(sfHookCanEmit);
// set the namespace if it differs from the definition namespace
if (newNamespace && *defNamespace != *newNamespace)
@@ -1686,6 +1740,12 @@ SetHook::setHook()
if (newHookOn && *defHookOn != *newHookOn)
newHook.setFieldH256(sfHookOn, *newHookOn);
// set the hookcanemit field if it differs from definition
if (newHookCanEmit &&
!(defHookCanEmit.has_value() &&
*defHookCanEmit == *newHookCanEmit))
newHook.setFieldH256(sfHookCanEmit, *newHookCanEmit);
// parameters
TER result = updateHookParameters(
ctx,
@@ -1734,8 +1794,8 @@ SetHook::setHook()
// sfHook: 1 reserve PER non-blank entry
// sfParameters: 1 reserve PER entry
// sfGrants are: 1 reserve PER entry
// sfHookHash, sfHookNamespace, sfHookOn, sfHookApiVersion, sfFlags:
// free
// sfHookHash, sfHookNamespace, sfHookOn, sfHookCanEmit,
// sfHookApiVersion, sfFlags: free
// sfHookDefinition is not reserved because it is an unowned object,
// rather the uploader is billed via fee according to the following:
+450
View File
@@ -0,0 +1,450 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2024 XRPL-Labs
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.
*/
//==============================================================================
#include <ripple/app/tx/impl/SetRemarks.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/Quality.h>
#include <ripple/protocol/st.h>
namespace ripple {
TxConsequences
SetRemarks::makeTxConsequences(PreflightContext const& ctx)
{
return TxConsequences{ctx.tx, TxConsequences::normal};
}
NotTEC
SetRemarks::validateRemarks(STArray const& remarks, beast::Journal const& j)
{
std::set<Blob> already_seen;
if (remarks.empty() || remarks.size() > 32)
{
JLOG(j.warn()) << "SetRemarks: Cannot set more than 32 remarks (or "
"fewer than 1) in a txn.";
return temMALFORMED;
}
for (auto const& remark : remarks)
{
if (remark.getFName() != sfRemark)
{
JLOG(j.warn()) << "SetRemarks: contained non-sfRemark field.";
return temMALFORMED;
}
// will be checked by template system, extra check for security
if (!remark.isFieldPresent(sfRemarkName))
return temMALFORMED;
Blob const& name = remark.getFieldVL(sfRemarkName);
if (name.size() == 0 || name.size() > 256)
{
JLOG(j.warn()) << "SetRemarks: RemarkName cannot be empty or "
"larger than 256 chars.";
return temMALFORMED;
}
if (already_seen.find(name) != already_seen.end())
{
JLOG(j.warn()) << "SetRemarks: duplicate RemarkName entry.";
return temMALFORMED;
}
already_seen.emplace(name);
uint32_t flags =
remark.isFieldPresent(sfFlags) ? remark.getFieldU32(sfFlags) : 0;
if (flags != 0 && flags != tfImmutable)
{
JLOG(j.warn())
<< "SetRemarks: Flags must be either tfImmutable or 0";
return temMALFORMED;
}
if (!remark.isFieldPresent(sfRemarkValue))
{
if (flags & tfImmutable)
{
JLOG(j.warn()) << "SetRemarks: A remark deletion cannot be "
"marked immutable.";
return temMALFORMED;
}
continue;
}
Blob const& val = remark.getFieldVL(sfRemarkValue);
if (val.size() == 0 || val.size() > 256)
{
JLOG(j.warn()) << "SetRemarks: RemarkValue cannot be empty or "
"larger than 256 chars.";
return temMALFORMED;
}
}
return tesSUCCESS;
}
NotTEC
SetRemarks::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureRemarks))
return temDISABLED;
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;
auto& tx = ctx.tx;
auto& j = ctx.j;
if (tx.getFlags() & tfUniversalMask)
{
JLOG(j.warn()) << "SetRemarks: Invalid flags set.";
return temINVALID_FLAG;
}
auto const& remarks = tx.getFieldArray(sfRemarks);
if (NotTEC result = validateRemarks(remarks, j); !isTesSuccess(result))
return result;
return preflight2(ctx);
}
template <typename T>
inline std::optional<AccountID>
getRemarksIssuer(T const& sleO)
{
std::optional<AccountID> issuer;
// check if it's an allowable object type
uint16_t lt = sleO->getFieldU16(sfLedgerEntryType);
switch (lt)
{
case ltACCOUNT_ROOT:
case ltOFFER:
case ltESCROW:
case ltTICKET:
case ltPAYCHAN:
case ltCHECK:
case ltDEPOSIT_PREAUTH: {
issuer = sleO->getAccountID(sfAccount);
break;
}
case ltNFTOKEN_OFFER: {
issuer = sleO->getAccountID(sfOwner);
break;
}
case ltURI_TOKEN: {
issuer = sleO->getAccountID(sfIssuer);
break;
}
case ltRIPPLE_STATE: {
// remarks can only be attached to a trustline by the issuer
AccountID lowAcc = sleO->getFieldAmount(sfLowLimit).getIssuer();
AccountID highAcc = sleO->getFieldAmount(sfHighLimit).getIssuer();
STAmount bal = sleO->getFieldAmount(sfBalance);
if (bal < beast::zero)
{
// low account is issuer
issuer = lowAcc;
break;
}
if (bal > beast::zero)
{
// high acccount is issuer
issuer = highAcc;
break;
}
// if the balance is zero we'll look for the side in default state
// and assume this is the issuer
uint32_t flags = sleO->getFieldU32(sfFlags);
bool const highReserve = (flags & lsfHighReserve);
bool const lowReserve = (flags & lsfLowReserve);
if (!highReserve && !lowReserve)
{
// error state
// do nothing, fallthru.
}
else if (highReserve && lowReserve)
{
// in this edge case we don't know who is the issuer, because
// there isn't a clear issuer. do nothing, fallthru.
}
else
{
issuer = (highReserve ? lowAcc : highAcc);
break;
}
}
}
return issuer;
}
TER
SetRemarks::preclaim(PreclaimContext const& ctx)
{
if (!ctx.view.rules().enabled(featureRemarks))
return temDISABLED;
auto const id = ctx.tx[sfAccount];
auto const sle = ctx.view.read(keylet::account(id));
if (!sle)
return terNO_ACCOUNT;
auto const objID = ctx.tx[sfObjectID];
auto const sleO = ctx.view.read(keylet::unchecked(objID));
if (!sleO)
return tecNO_TARGET;
std::optional<AccountID> issuer = getRemarksIssuer(sleO);
if (!issuer || *issuer != id)
return tecNO_PERMISSION;
// sanity check the remarks merge between txn and obj
auto const& remarksTxn = ctx.tx.getFieldArray(sfRemarks);
std::map<Blob, std::pair<Blob, bool>> keys;
if (sleO->isFieldPresent(sfRemarks))
{
auto const& remarksObj = sleO->getFieldArray(sfRemarks);
// map the remark name to its value and whether it's immutable
for (auto const& remark : remarksObj)
keys.emplace(std::make_pair(
remark.getFieldVL(sfRemarkName),
std::make_pair(
remark.getFieldVL(sfRemarkValue),
remark.isFieldPresent(sfFlags) &&
remark.getFieldU32(sfFlags) & tfImmutable)));
}
int64_t count = keys.size();
for (auto const& remark : remarksTxn)
{
std::optional<Blob> valTxn;
if (remark.isFieldPresent(sfRemarkValue))
valTxn = remark.getFieldVL(sfRemarkValue);
bool const isDeletion = !valTxn.has_value();
Blob name = remark.getFieldVL(sfRemarkName);
if (keys.find(name) == keys.end())
{
// new remark
if (isDeletion)
{
// this could have been an error but deleting something
// that doesn't exist is traditionally not an error in xrpl
continue;
}
++count;
continue;
}
auto const& [valObj, immutable] = keys[name];
// even if it's mutable, if we don't mutate it that's a noop so just
// pass it
if (valTxn.has_value() && *valTxn == valObj)
continue;
if (immutable)
{
JLOG(ctx.j.warn())
<< "SetRemarks: attempt to mutate an immutable remark.";
return tecIMMUTABLE;
}
if (isDeletion)
{
if (--count < 0)
{
JLOG(ctx.j.warn()) << "SetRemarks: insane remarks accounting.";
return tecCLAIM;
}
}
}
if (count > 32)
{
JLOG(ctx.j.warn())
<< "SetRemarks: an object may have at most 32 remarks.";
return tecTOO_MANY_REMARKS;
}
return tesSUCCESS;
}
TER
SetRemarks::doApply()
{
auto j = ctx_.journal;
Sandbox sb(&ctx_.view());
auto const sle = sb.read(keylet::account(account_));
if (!sle)
return terNO_ACCOUNT;
auto const objID = ctx_.tx[sfObjectID];
auto sleO = sb.peek(keylet::unchecked(objID));
if (!sleO)
return terNO_ACCOUNT;
std::optional<AccountID> issuer = getRemarksIssuer(sleO);
if (!issuer || *issuer != account_)
return tecNO_PERMISSION;
auto const& remarksTxn = ctx_.tx.getFieldArray(sfRemarks);
std::map<Blob, std::pair<Blob, bool>> remarksMap;
if (sleO->isFieldPresent(sfRemarks))
{
auto const& remarksObj = sleO->getFieldArray(sfRemarks);
for (auto const& remark : remarksObj)
{
uint32_t flags = remark.isFieldPresent(sfFlags)
? remark.getFieldU32(sfFlags)
: 0;
bool const immutable = (flags & tfImmutable) != 0;
remarksMap[remark.getFieldVL(sfRemarkName)] = {
remark.getFieldVL(sfRemarkValue),
remark.isFieldPresent(sfFlags) && immutable};
}
}
for (auto const& remark : remarksTxn)
{
std::optional<Blob> val;
if (remark.isFieldPresent(sfRemarkValue))
val = remark.getFieldVL(sfRemarkValue);
Blob name = remark.getFieldVL(sfRemarkName);
bool const isDeletion = !val.has_value();
uint32_t flags =
remark.isFieldPresent(sfFlags) ? remark.getFieldU32(sfFlags) : 0;
bool const setImmutable = (flags & tfImmutable) != 0;
if (isDeletion)
{
if (remarksMap.find(name) != remarksMap.end())
remarksMap.erase(name);
continue;
}
if (remarksMap.find(name) == remarksMap.end())
{
remarksMap[name] = std::make_pair(*val, setImmutable);
continue;
}
remarksMap[name].first = *val;
if (!remarksMap[name].second)
remarksMap[name].second = setImmutable;
}
// canonically order
std::vector<Blob> keys;
for (auto const& [k, _] : remarksMap)
keys.push_back(k);
std::sort(keys.begin(), keys.end());
STArray newRemarks{sfRemarks, static_cast<int>(keys.size())};
for (auto const& k : keys)
{
STObject remark{sfRemark};
remark.setFieldVL(sfRemarkName, k);
remark.setFieldVL(sfRemarkValue, remarksMap[k].first);
if (remarksMap[k].second)
remark.setFieldU32(sfFlags, lsfImmutable);
newRemarks.push_back(std::move(remark));
}
if (newRemarks.size() > 32)
return tecTOO_MANY_REMARKS;
if (newRemarks.empty() && sleO->isFieldPresent(sfRemarks))
sleO->makeFieldAbsent(sfRemarks);
else
sleO->setFieldArray(sfRemarks, std::move(newRemarks));
sb.update(sleO);
sb.apply(ctx_.rawView());
return tesSUCCESS;
}
XRPAmount
SetRemarks::calculateBaseFee(ReadView const& view, STTx const& tx)
{
XRPAmount remarkFee{0};
if (tx.isFieldPresent(sfRemarks))
{
int64_t remarkBytes = 0;
auto const& remarks = tx.getFieldArray(sfRemarks);
for (auto const& remark : remarks)
{
int64_t entryBytes = 0;
if (remark.isFieldPresent(sfRemarkName))
{
entryBytes += remark.getFieldVL(sfRemarkName).size();
}
if (remark.isFieldPresent(sfRemarkValue))
{
entryBytes += remark.getFieldVL(sfRemarkValue).size();
}
// overflow
if (remarkBytes + entryBytes < remarkBytes)
return INITIAL_XRP;
remarkBytes += entryBytes;
}
// one drop per byte
remarkFee = XRPAmount{remarkBytes};
}
auto fee = Transactor::calculateBaseFee(view, tx);
return fee + remarkFee;
}
} // namespace ripple
+60
View File
@@ -0,0 +1,60 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2024 XRPL-Labs
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 RIPPLE_TX_SETREMARKS_H_INCLUDED
#define RIPPLE_TX_SETREMARKS_H_INCLUDED
#include <ripple/app/tx/impl/Transactor.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/protocol/Indexes.h>
namespace ripple {
class SetRemarks : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
explicit SetRemarks(ApplyContext& ctx) : Transactor(ctx)
{
}
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const&);
TER
doApply() override;
static NotTEC
validateRemarks(STArray const& remarks, beast::Journal const& j);
};
} // namespace ripple
#endif
+6 -4
View File
@@ -153,8 +153,10 @@ SetSignerList::preCompute()
// NOTE: This way of computing the OwnerCount associated with a SignerList
// is valid until the featureMultiSignReserve amendment passes. Once it
// passes then just 1 OwnerCount is associated with a SignerList.
static int
signerCountBasedOwnerCountDelta(std::size_t entryCount, Rules const& rules)
int
SetSignerList::signerCountBasedOwnerCountDelta(
std::size_t entryCount,
Rules const& rules)
{
// We always compute the full change in OwnerCount, taking into account:
// o The fact that we're adding/removing a SignerList and
@@ -176,8 +178,8 @@ signerCountBasedOwnerCountDelta(std::size_t entryCount, Rules const& rules)
return 2 + static_cast<int>(entryCount);
}
static TER
removeSignersFromLedger(
TER
SetSignerList::removeSignersFromLedger(
Application& app,
ApplyView& view,
Keylet const& accountKeylet,
+12
View File
@@ -71,6 +71,18 @@ public:
AccountID const& account,
beast::Journal j);
static TER
removeSignersFromLedger(
Application& app,
ApplyView& view,
Keylet const& accountKeylet,
Keylet const& ownerDirKeylet,
Keylet const& signerListKeylet,
beast::Journal j);
static int
signerCountBasedOwnerCountDelta(std::size_t entryCount, Rules const& rules);
private:
static void
writeSignersToSLE(
+59 -11
View File
@@ -1079,6 +1079,24 @@ Transactor::checkMultiSign(PreclaimContext const& ctx)
//------------------------------------------------------------------------------
// increment the touch counter on an account
static void
touchAccount(ApplyView& view, AccountID const& id)
{
if (!view.rules().enabled(featureTouch))
return;
std::shared_ptr<SLE> sle = view.peek(keylet::account(id));
if (!sle)
return;
uint64_t tc =
sle->isFieldPresent(sfTouchCount) ? sle->getFieldU64(sfTouchCount) : 0;
sle->setFieldU64(sfTouchCount, tc + 1);
view.update(sle);
}
static void
removeUnfundedOffers(
ApplyView& view,
@@ -1212,6 +1230,8 @@ Transactor::executeHookChain(
if (!hook::canHook(ctx_.tx.getTxnType(), hookOn))
continue; // skip if it can't
uint256 hookCanEmit = hook::getHookCanEmit(hookObj, hookDef);
uint32_t flags =
(hookObj.isFieldPresent(sfFlags) ? hookObj.getFieldU32(sfFlags)
: hookDef->getFieldU32(sfFlags));
@@ -1247,6 +1267,7 @@ Transactor::executeHookChain(
results.push_back(hook::apply(
hookDef->getFieldH256(sfHookSetTxnID),
hookHash,
hookCanEmit,
ns,
hookDef->getFieldVL(sfCreateCode),
parameters,
@@ -1270,10 +1291,18 @@ Transactor::executeHookChain(
if (results.back().exitType == hook_api::ExitType::WASM_ERROR)
{
JLOG(j_.warn()) << "HookError[" << account << "-"
<< ctx_.tx.getAccountID(sfAccount) << "]: "
<< ctx_.tx.getAccountID(sfAccount)
<< "]: Execution failure (graceful) "
<< "HookHash: " << hookHash;
}
if (results.back().exitType == hook_api::ExitType::UNSET)
{
JLOG(j_.warn())
<< "HookError[" << account << "-"
<< ctx_.tx.getAccountID(sfAccount)
<< "]: Execution failure (no exit type specified) "
<< "HookHash: " << hookHash;
}
return tecHOOK_REJECTED;
}
@@ -1298,7 +1327,7 @@ Transactor::executeHookChain(
{
JLOG(j_.warn())
<< "HookError[" << account << "-"
<< ctx_.tx.getAccountID(sfAccount) << "]: "
<< ctx_.tx.getAccountID(sfAccount)
<< "]: Execution failure (exceptional) "
<< "Exception: " << e.what() << " HookHash: " << hookHash;
@@ -1369,6 +1398,8 @@ Transactor::doHookCallback(
if (hookObj.getFieldH256(sfHookHash) != callbackHookHash)
continue;
uint256 hookCanEmit = hook::getHookCanEmit(hookObj, hookDef);
// fetch the namespace either from the hook object of, if absent, the
// hook def
uint256 const& ns =
@@ -1394,6 +1425,7 @@ Transactor::doHookCallback(
hook::HookResult callbackResult = hook::apply(
hookDef->getFieldH256(sfHookSetTxnID),
callbackHookHash,
hookCanEmit,
ns,
hookDef->getFieldVL(sfCreateCode),
parameters,
@@ -1426,13 +1458,13 @@ Transactor::doHookCallback(
finalizeHookResult(callbackResult, ctx_, success);
JLOG(j_.trace()) << "HookInfo[" << callbackAccountID << "-"
<< ctx_.tx.getAccountID(sfAccount) << "]: "
<< "Callback finalizeHookResult = " << result;
<< ctx_.tx.getAccountID(sfAccount)
<< "]: Callback finalizeHookResult = " << result;
}
catch (std::exception& e)
{
JLOG(j_.fatal()) << "HookError[" << callbackAccountID << "-"
<< ctx_.tx.getAccountID(sfAccount) << "]: "
<< ctx_.tx.getAccountID(sfAccount)
<< "]: Callback failure " << e.what();
}
}
@@ -1511,6 +1543,8 @@ Transactor::doTSH(
if ((!canRollback && strong) || (canRollback && !strong))
continue;
touchAccount(view, tshAccountID);
auto klTshHook = keylet::hook(tshAccountID);
auto tshHook = view.read(klTshHook);
@@ -1639,6 +1673,8 @@ Transactor::doAgainAsWeak(
continue;
}
uint256 hookCanEmit = hook::getHookCanEmit(hookObj, hookDef);
// fetch the namespace either from the hook object of, if absent, the
// hook def
uint256 const& ns =
@@ -1659,6 +1695,7 @@ Transactor::doAgainAsWeak(
hook::HookResult aawResult = hook::apply(
hookDef->getFieldH256(sfHookSetTxnID),
hookHash,
hookCanEmit,
ns,
hookDef->getFieldVL(sfCreateCode),
parameters,
@@ -1678,13 +1715,13 @@ Transactor::doAgainAsWeak(
results.push_back(aawResult);
JLOG(j_.trace()) << "HookInfo[" << hookAccountID << "-"
<< ctx_.tx.getAccountID(sfAccount) << "]: "
<< " aaw Hook ExitCode = " << aawResult.exitCode;
<< ctx_.tx.getAccountID(sfAccount)
<< "]: aaw Hook ExitCode = " << aawResult.exitCode;
}
catch (std::exception& e)
{
JLOG(j_.fatal()) << "HookError[" << hookAccountID << "-"
<< ctx_.tx.getAccountID(sfAccount) << "]: "
<< ctx_.tx.getAccountID(sfAccount)
<< "]: aaw failure " << e.what();
}
}
@@ -1921,6 +1958,13 @@ Transactor::operator()()
STObject const meta = metaRaw.getAsObject();
uint32_t lgrCur = view().seq();
bool const has240819 = view().rules().enabled(fix240819);
bool const has240911 = view().rules().enabled(fix240911);
auto const& sfRewardFields =
*(ripple::SField::knownCodeToField.at(917511 - has240819));
// iterate all affected balances
for (auto const& node : meta.getFieldArray(sfAffectedNodes))
{
@@ -1932,7 +1976,7 @@ Transactor::operator()()
if (nodeType != ltACCOUNT_ROOT || metaType == sfDeletedNode)
continue;
if (!node.isFieldPresent(sfFinalFields) ||
if (!node.isFieldPresent(sfRewardFields) ||
!node.isFieldPresent(sfLedgerIndex))
continue;
@@ -1948,7 +1992,7 @@ Transactor::operator()()
continue;
STObject& finalFields = (const_cast<STObject&>(node))
.getField(sfFinalFields)
.getField(sfRewardFields)
.downcast<STObject>();
if (!finalFields.isFieldPresent(sfBalance))
@@ -1965,7 +2009,11 @@ Transactor::operator()()
uint32_t lgrElapsed = lgrCur - lgrLast;
// overflow safety
if (lgrElapsed > lgrCur || lgrElapsed > lgrLast || lgrElapsed == 0)
if (!has240911 &&
(lgrElapsed > lgrCur || lgrElapsed > lgrLast ||
lgrElapsed == 0))
continue;
if (has240911 && (lgrElapsed > lgrCur || lgrElapsed == 0))
continue;
uint64_t accum = sle->getFieldU64(sfRewardAccumulator);
+14
View File
@@ -23,6 +23,7 @@
#include <ripple/app/tx/impl/CancelOffer.h>
#include <ripple/app/tx/impl/CashCheck.h>
#include <ripple/app/tx/impl/Change.h>
#include <ripple/app/tx/impl/ClaimReward.h>
#include <ripple/app/tx/impl/CreateCheck.h>
#include <ripple/app/tx/impl/CreateOffer.h>
#include <ripple/app/tx/impl/CreateTicket.h>
@@ -39,12 +40,15 @@
#include <ripple/app/tx/impl/NFTokenMint.h>
#include <ripple/app/tx/impl/PayChan.h>
#include <ripple/app/tx/impl/Payment.h>
#include <ripple/app/tx/impl/Remit.h>
#include <ripple/app/tx/impl/SetAccount.h>
#include <ripple/app/tx/impl/SetHook.h>
#include <ripple/app/tx/impl/SetRegularKey.h>
#include <ripple/app/tx/impl/SetRemarks.h>
#include <ripple/app/tx/impl/SetSignerList.h>
#include <ripple/app/tx/impl/SetTrust.h>
#include <ripple/app/tx/impl/URIToken.h>
#include <ripple/app/tx/impl/XahauGenesis.h>
namespace ripple {
@@ -166,6 +170,8 @@ invoke_preflight(PreflightContext const& ctx)
return invoke_preflight_helper<Invoke>(ctx);
case ttREMIT:
return invoke_preflight_helper<Remit>(ctx);
case ttREMARKS_SET:
return invoke_preflight_helper<SetRemarks>(ctx);
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:
@@ -287,6 +293,8 @@ invoke_preclaim(PreclaimContext const& ctx)
return invoke_preclaim<Invoke>(ctx);
case ttREMIT:
return invoke_preclaim<Remit>(ctx);
case ttREMARKS_SET:
return invoke_preclaim<SetRemarks>(ctx);
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:
@@ -370,6 +378,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
return Invoke::calculateBaseFee(view, tx);
case ttREMIT:
return Remit::calculateBaseFee(view, tx);
case ttREMARKS_SET:
return SetRemarks::calculateBaseFee(view, tx);
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:
@@ -553,6 +563,10 @@ invoke_apply(ApplyContext& ctx)
Remit p(ctx);
return p();
}
case ttREMARKS_SET: {
SetRemarks p(ctx);
return p();
}
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:
+11 -4
View File
@@ -186,7 +186,14 @@ mulRatio(
std::uint32_t den,
bool roundUp);
extern LocalValue<bool> stNumberSwitchover;
// Since many uses of the number class do not have access to a ledger,
// getSTNumberSwitchover needs to be globally accessible.
bool
getSTNumberSwitchover();
void
setSTNumberSwitchover(bool v);
/** RAII class to set and restore the Number switchover.
*/
@@ -198,16 +205,16 @@ class NumberSO
public:
~NumberSO()
{
*stNumberSwitchover = saved_;
setSTNumberSwitchover(saved_);
}
NumberSO(NumberSO const&) = delete;
NumberSO&
operator=(NumberSO const&) = delete;
explicit NumberSO(bool v) : saved_(*stNumberSwitchover)
explicit NumberSO(bool v) : saved_(getSTNumberSwitchover())
{
*stNumberSwitchover = v;
setSTNumberSwitchover(v);
}
};
+1
View File
@@ -24,6 +24,7 @@
#include <ripple/beast/utility/Journal.h>
#include <boost/beast/core/string.hpp>
#include <boost/filesystem.hpp>
#include <fstream>
#include <map>
#include <memory>
#include <mutex>
+432
View File
@@ -0,0 +1,432 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright 2022, Nikolaos D. Bougalis <nikb@bougalis.net>
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 RIPPLE_BASICS_SLABALLOCATOR_H_INCLUDED
#define RIPPLE_BASICS_SLABALLOCATOR_H_INCLUDED
#include <ripple/beast/type_name.h>
#include <boost/align.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/predef.h>
#include <algorithm>
#include <atomic>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <mutex>
#if BOOST_OS_LINUX
#include <sys/mman.h>
#endif
namespace ripple {
template <typename Type>
class SlabAllocator
{
static_assert(
sizeof(Type) >= sizeof(std::uint8_t*),
"SlabAllocator: the requested object must be larger than a pointer.");
static_assert(alignof(Type) == 8 || alignof(Type) == 4);
/** A block of memory that is owned by a slab allocator */
struct SlabBlock
{
// A mutex to protect the freelist for this block:
std::mutex m_;
// A linked list of appropriately sized free buffers:
std::uint8_t* l_ = nullptr;
// The next memory block
SlabBlock* next_;
// The underlying memory block:
std::uint8_t const* const p_ = nullptr;
// The extent of the underlying memory block:
std::size_t const size_;
SlabBlock(
SlabBlock* next,
std::uint8_t* data,
std::size_t size,
std::size_t item)
: next_(next), p_(data), size_(size)
{
// We don't need to grab the mutex here, since we're the only
// ones with access at this moment.
while (data + item <= p_ + size_)
{
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(data, &l_, sizeof(std::uint8_t*));
l_ = data;
data += item;
}
}
~SlabBlock()
{
// Calling this destructor will release the allocated memory but
// will not properly destroy any objects that are constructed in
// the block itself.
}
SlabBlock(SlabBlock const& other) = delete;
SlabBlock&
operator=(SlabBlock const& other) = delete;
SlabBlock(SlabBlock&& other) = delete;
SlabBlock&
operator=(SlabBlock&& other) = delete;
/** Determines whether the given pointer belongs to this allocator */
bool
own(std::uint8_t const* p) const noexcept
{
return (p >= p_) && (p < p_ + size_);
}
std::uint8_t*
allocate() noexcept
{
std::uint8_t* ret;
{
std::lock_guard l(m_);
ret = l_;
if (ret)
{
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(&l_, ret, sizeof(std::uint8_t*));
}
}
return ret;
}
/** Return an item to this allocator's freelist.
@param ptr The pointer to the chunk of memory being deallocated.
@note This is a dangerous, private interface; the item being
returned should belong to this allocator. Debug builds
will check and assert if this is not the case. Release
builds will not.
*/
void
deallocate(std::uint8_t* ptr) noexcept
{
assert(own(ptr));
std::lock_guard l(m_);
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(ptr, &l_, sizeof(std::uint8_t*));
l_ = ptr;
}
};
private:
// A linked list of slabs
std::atomic<SlabBlock*> slabs_ = nullptr;
// The alignment requirements of the item we're allocating:
std::size_t const itemAlignment_;
// The size of an item, including the extra bytes requested and
// any padding needed for alignment purposes:
std::size_t const itemSize_;
// The size of each individual slab:
std::size_t const slabSize_;
public:
/** Constructs a slab allocator able to allocate objects of a fixed size
@param count the number of items the slab allocator can allocate; note
that a count of 0 is valid and means that the allocator
is, effectively, disabled. This can be very useful in some
contexts (e.g. when mimimal memory usage is needed) and
allows for graceful failure.
*/
constexpr explicit SlabAllocator(
std::size_t extra,
std::size_t alloc = 0,
std::size_t align = 0)
: itemAlignment_(align ? align : alignof(Type))
, itemSize_(
boost::alignment::align_up(sizeof(Type) + extra, itemAlignment_))
, slabSize_(alloc)
{
assert((itemAlignment_ & (itemAlignment_ - 1)) == 0);
}
SlabAllocator(SlabAllocator const& other) = delete;
SlabAllocator&
operator=(SlabAllocator const& other) = delete;
SlabAllocator(SlabAllocator&& other) = delete;
SlabAllocator&
operator=(SlabAllocator&& other) = delete;
~SlabAllocator()
{
// FIXME: We can't destroy the memory blocks we've allocated, because
// we can't be sure that they are not being used. Cleaning the
// shutdown process up could make this possible.
}
/** Returns the size of the memory block this allocator returns. */
constexpr std::size_t
size() const noexcept
{
return itemSize_;
}
/** Returns a suitably aligned pointer, if one is available.
@return a pointer to a block of memory from the allocator, or
nullptr if the allocator can't satisfy this request.
*/
std::uint8_t*
allocate() noexcept
{
auto slab = slabs_.load();
while (slab != nullptr)
{
if (auto ret = slab->allocate())
return ret;
slab = slab->next_;
}
// No slab can satisfy our request, so we attempt to allocate a new
// one here:
std::size_t size = slabSize_;
// We want to allocate the memory at a 2 MiB boundary, to make it
// possible to use hugepage mappings on Linux:
auto buf =
boost::alignment::aligned_alloc(megabytes(std::size_t(2)), size);
// clang-format off
if (!buf) [[unlikely]]
return nullptr;
// clang-format on
#if BOOST_OS_LINUX
// When allocating large blocks, attempt to leverage Linux's
// transparent hugepage support. It is unclear and difficult
// to accurately determine if doing this impacts performance
// enough to justify using platform-specific tricks.
if (size >= megabytes(std::size_t(4)))
madvise(buf, size, MADV_HUGEPAGE);
#endif
// We need to carve out a bit of memory for the slab header
// and then align the rest appropriately:
auto slabData = reinterpret_cast<void*>(
reinterpret_cast<std::uint8_t*>(buf) + sizeof(SlabBlock));
auto slabSize = size - sizeof(SlabBlock);
// This operation is essentially guaranteed not to fail but
// let's be careful anyways.
if (!boost::alignment::align(
itemAlignment_, itemSize_, slabData, slabSize))
{
boost::alignment::aligned_free(buf);
return nullptr;
}
slab = new (buf) SlabBlock(
slabs_.load(),
reinterpret_cast<std::uint8_t*>(slabData),
slabSize,
itemSize_);
// Link the new slab
while (!slabs_.compare_exchange_weak(
slab->next_,
slab,
std::memory_order_release,
std::memory_order_relaxed))
{
; // Nothing to do
}
return slab->allocate();
}
/** Returns the memory block to the allocator.
@param ptr A pointer to a memory block.
@param size If non-zero, a hint as to the size of the block.
@return true if this memory block belonged to the allocator and has
been released; false otherwise.
*/
bool
deallocate(std::uint8_t* ptr) noexcept
{
assert(ptr);
for (auto slab = slabs_.load(); slab != nullptr; slab = slab->next_)
{
if (slab->own(ptr))
{
slab->deallocate(ptr);
return true;
}
}
return false;
}
};
/** A collection of slab allocators of various sizes for a given type. */
template <typename Type>
class SlabAllocatorSet
{
private:
// The list of allocators that belong to this set
boost::container::static_vector<SlabAllocator<Type>, 64> allocators_;
std::size_t maxSize_ = 0;
public:
class SlabConfig
{
friend class SlabAllocatorSet;
private:
std::size_t extra;
std::size_t alloc;
std::size_t align;
public:
constexpr SlabConfig(
std::size_t extra_,
std::size_t alloc_ = 0,
std::size_t align_ = alignof(Type))
: extra(extra_), alloc(alloc_), align(align_)
{
}
};
constexpr SlabAllocatorSet(std::vector<SlabConfig> cfg)
{
// Ensure that the specified allocators are sorted from smallest to
// largest by size:
std::sort(
std::begin(cfg),
std::end(cfg),
[](SlabConfig const& a, SlabConfig const& b) {
return a.extra < b.extra;
});
// We should never have two slabs of the same size
if (std::adjacent_find(
std::begin(cfg),
std::end(cfg),
[](SlabConfig const& a, SlabConfig const& b) {
return a.extra == b.extra;
}) != cfg.end())
{
throw std::runtime_error(
"SlabAllocatorSet<" + beast::type_name<Type>() +
">: duplicate slab size");
}
for (auto const& c : cfg)
{
auto& a = allocators_.emplace_back(c.extra, c.alloc, c.align);
if (a.size() > maxSize_)
maxSize_ = a.size();
}
}
SlabAllocatorSet(SlabAllocatorSet const& other) = delete;
SlabAllocatorSet&
operator=(SlabAllocatorSet const& other) = delete;
SlabAllocatorSet(SlabAllocatorSet&& other) = delete;
SlabAllocatorSet&
operator=(SlabAllocatorSet&& other) = delete;
~SlabAllocatorSet()
{
}
/** Returns a suitably aligned pointer, if one is available.
@param extra The number of extra bytes, above and beyond the size of
the object, that should be returned by the allocator.
@return a pointer to a block of memory, or nullptr if the allocator
can't satisfy this request.
*/
std::uint8_t*
allocate(std::size_t extra) noexcept
{
if (auto const size = sizeof(Type) + extra; size <= maxSize_)
{
for (auto& a : allocators_)
{
if (a.size() >= size)
return a.allocate();
}
}
return nullptr;
}
/** Returns the memory block to the allocator.
@param ptr A pointer to a memory block.
@return true if this memory block belonged to one of the allocators
in this set and has been released; false otherwise.
*/
bool
deallocate(std::uint8_t* ptr) noexcept
{
for (auto& a : allocators_)
{
if (a.deallocate(ptr))
return true;
}
return false;
}
};
} // namespace ripple
#endif // RIPPLE_BASICS_SLABALLOCATOR_H_INCLUDED
+2
View File
@@ -25,7 +25,9 @@
#include <boost/format.hpp>
#include <boost/utility/string_view.hpp>
#include <array>
#include <cstdint>
#include <optional>
#include <sstream>
#include <string>
+1
View File
@@ -57,6 +57,7 @@
#ifndef RIPPLE_BASICS_BASE64_H_INCLUDED
#define RIPPLE_BASICS_BASE64_H_INCLUDED
#include <cstdint>
#include <string>
namespace ripple {
+26 -1
View File
@@ -129,6 +129,18 @@ public:
return reinterpret_cast<const_pointer>(data_.data());
}
char const*
cdata() const
{
return reinterpret_cast<char const*>(data_.data());
}
char*
cdata()
{
return reinterpret_cast<char*>(data_.data());
}
iterator
begin()
{
@@ -549,6 +561,7 @@ using uint128 = base_uint<128>;
using uint160 = base_uint<160>;
using uint256 = base_uint<256>;
#ifdef __APPLE__
template <std::size_t Bits, class Tag>
[[nodiscard]] inline constexpr std::strong_ordering
operator<=>(base_uint<Bits, Tag> const& lhs, base_uint<Bits, Tag> const& rhs)
@@ -560,7 +573,6 @@ operator<=>(base_uint<Bits, Tag> const& lhs, base_uint<Bits, Tag> const& rhs)
//
// FIXME: use std::lexicographical_compare_three_way once support is
// added to MacOS.
auto const ret = std::mismatch(lhs.cbegin(), lhs.cend(), rhs.cbegin());
// a == b
@@ -570,6 +582,19 @@ operator<=>(base_uint<Bits, Tag> const& lhs, base_uint<Bits, Tag> const& rhs)
return (*ret.first > *ret.second) ? std::strong_ordering::greater
: std::strong_ordering::less;
}
#else
template <std::size_t Bits, class Tag>
[[nodiscard]] inline constexpr std::strong_ordering
operator<=>(base_uint<Bits, Tag> const& lhs, base_uint<Bits, Tag> const& rhs)
{
return std::lexicographical_compare_three_way(
lhs.cbegin(),
lhs.cend(),
rhs.cbegin(),
rhs.cend(),
std::compare_three_way{});
}
#endif
template <std::size_t Bits, typename Tag>
[[nodiscard]] inline constexpr bool
+4 -2
View File
@@ -18,6 +18,7 @@
//==============================================================================
#include <ripple/basics/FileUtilities.h>
#include <fstream>
namespace ripple {
@@ -41,7 +42,7 @@ getFileContents(
return {};
}
ifstream fileStream(fullPath, std::ios::in);
std::ifstream fileStream(fullPath.string(), std::ios::in);
if (!fileStream)
{
@@ -71,7 +72,8 @@ writeFileContents(
using namespace boost::filesystem;
using namespace boost::system::errc;
ofstream fileStream(destPath, std::ios::out | std::ios::trunc);
std::ofstream fileStream(
destPath.string(), std::ios::out | std::ios::trunc);
if (!fileStream)
{
+24 -3
View File
@@ -27,7 +27,28 @@
namespace ripple {
LocalValue<bool> stNumberSwitchover(true);
namespace {
// Use a static inside a function to help prevent order-of-initialzation issues
LocalValue<bool>&
getStaticSTNumberSwitchover()
{
static LocalValue<bool> r{true};
return r;
}
} // namespace
bool
getSTNumberSwitchover()
{
return *getStaticSTNumberSwitchover();
}
void
setSTNumberSwitchover(bool v)
{
*getStaticSTNumberSwitchover() = v;
}
/* The range for the mantissa when normalized */
static std::int64_t constexpr minMantissa = 1000000000000000ull;
@@ -51,7 +72,7 @@ IOUAmount::normalize()
return;
}
if (*stNumberSwitchover)
if (getSTNumberSwitchover())
{
Number const v{mantissa_, exponent_};
mantissa_ = v.mantissa();
@@ -117,7 +138,7 @@ IOUAmount::operator+=(IOUAmount const& other)
return *this;
}
if (*stNumberSwitchover)
if (getSTNumberSwitchover())
{
*this = IOUAmount{Number{*this} + Number{other}};
return *this;
+1 -1
View File
@@ -30,7 +30,7 @@ mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
{
using namespace boost::multiprecision;
uint128_t result;
boost::multiprecision::uint128_t result;
result = multiply(result, value, mul);
result /= div;
@@ -31,7 +31,11 @@ namespace ripple {
static std::size_t
extract(uint256 const& key)
{
return *reinterpret_cast<std::size_t const*>(key.data());
std::size_t result;
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(&result, key.data(), sizeof(std::size_t));
return result;
}
static std::size_t
@@ -145,111 +145,78 @@ private:
};
// VFALCO TODO This should only be enabled for maps.
class pair_value_compare
: public beast::detail::empty_base_optimization<Compare>
#ifdef _LIBCPP_VERSION
,
public std::binary_function<value_type, value_type, bool>
#endif
class pair_value_compare : public Compare
{
public:
#ifndef _LIBCPP_VERSION
using first_argument = value_type;
using second_argument = value_type;
using result_type = bool;
#endif
bool
operator()(value_type const& lhs, value_type const& rhs) const
{
return this->member()(lhs.first, rhs.first);
return Compare::operator()(lhs.first, rhs.first);
}
pair_value_compare()
{
}
pair_value_compare(pair_value_compare const& other)
: beast::detail::empty_base_optimization<Compare>(other)
pair_value_compare(pair_value_compare const& other) : Compare(other)
{
}
private:
friend aged_ordered_container;
pair_value_compare(Compare const& compare)
: beast::detail::empty_base_optimization<Compare>(compare)
pair_value_compare(Compare const& compare) : Compare(compare)
{
}
};
// Compares value_type against element, used in insert_check
// VFALCO TODO hoist to remove template argument dependencies
class KeyValueCompare
: public beast::detail::empty_base_optimization<Compare>
#ifdef _LIBCPP_VERSION
,
public std::binary_function<Key, element, bool>
#endif
class KeyValueCompare : public Compare
{
public:
#ifndef _LIBCPP_VERSION
using first_argument = Key;
using second_argument = element;
using result_type = bool;
#endif
KeyValueCompare() = default;
KeyValueCompare(Compare const& compare)
: beast::detail::empty_base_optimization<Compare>(compare)
KeyValueCompare(Compare const& compare) : Compare(compare)
{
}
// VFALCO NOTE WE might want only to enable these overloads
// if Compare has is_transparent
#if 0
template <class K>
bool operator() (K const& k, element const& e) const
{
return this->member() (k, extract (e.value));
}
template <class K>
bool operator() (element const& e, K const& k) const
{
return this->member() (extract (e.value), k);
}
#endif
bool
operator()(Key const& k, element const& e) const
{
return this->member()(k, extract(e.value));
return Compare::operator()(k, extract(e.value));
}
bool
operator()(element const& e, Key const& k) const
{
return this->member()(extract(e.value), k);
return Compare::operator()(extract(e.value), k);
}
bool
operator()(element const& x, element const& y) const
{
return this->member()(extract(x.value), extract(y.value));
return Compare::operator()(extract(x.value), extract(y.value));
}
Compare&
compare()
{
return beast::detail::empty_base_optimization<Compare>::member();
return *this;
}
Compare const&
compare() const
{
return beast::detail::empty_base_optimization<Compare>::member();
return *this;
}
};
@@ -148,115 +148,84 @@ private:
};
// VFALCO TODO hoist to remove template argument dependencies
class ValueHash : private beast::detail::empty_base_optimization<Hash>
#ifdef _LIBCPP_VERSION
,
public std::unary_function<element, std::size_t>
#endif
class ValueHash : public Hash
{
public:
#ifndef _LIBCPP_VERSION
using argument_type = element;
using result_type = size_t;
#endif
ValueHash()
{
}
ValueHash(Hash const& h)
: beast::detail::empty_base_optimization<Hash>(h)
ValueHash(Hash const& h) : Hash(h)
{
}
std::size_t
operator()(element const& e) const
{
return this->member()(extract(e.value));
return Hash::operator()(extract(e.value));
}
Hash&
hash_function()
{
return this->member();
return *this;
}
Hash const&
hash_function() const
{
return this->member();
return *this;
}
};
// Compares value_type against element, used in find/insert_check
// VFALCO TODO hoist to remove template argument dependencies
class KeyValueEqual
: private beast::detail::empty_base_optimization<KeyEqual>
#ifdef _LIBCPP_VERSION
,
public std::binary_function<Key, element, bool>
#endif
class KeyValueEqual : public KeyEqual
{
public:
#ifndef _LIBCPP_VERSION
using first_argument_type = Key;
using second_argument_type = element;
using result_type = bool;
#endif
KeyValueEqual()
{
}
KeyValueEqual(KeyEqual const& keyEqual)
: beast::detail::empty_base_optimization<KeyEqual>(keyEqual)
KeyValueEqual(KeyEqual const& keyEqual) : KeyEqual(keyEqual)
{
}
// VFALCO NOTE WE might want only to enable these overloads
// if KeyEqual has is_transparent
#if 0
template <class K>
bool operator() (K const& k, element const& e) const
{
return this->member() (k, extract (e.value));
}
template <class K>
bool operator() (element const& e, K const& k) const
{
return this->member() (extract (e.value), k);
}
#endif
bool
operator()(Key const& k, element const& e) const
{
return this->member()(k, extract(e.value));
return KeyEqual::operator()(k, extract(e.value));
}
bool
operator()(element const& e, Key const& k) const
{
return this->member()(extract(e.value), k);
return KeyEqual::operator()(extract(e.value), k);
}
bool
operator()(element const& lhs, element const& rhs) const
{
return this->member()(extract(lhs.value), extract(rhs.value));
return KeyEqual::operator()(extract(lhs.value), extract(rhs.value));
}
KeyEqual&
key_eq()
{
return this->member();
return *this;
}
KeyEqual const&
key_eq() const
{
return this->member();
return *this;
}
};
+10 -2
View File
@@ -33,6 +33,8 @@ You can contact the author at :
#include <ripple/beast/hash/impl/xxhash.h>
#include <cstring>
//**************************************
// Tuning parameters
//**************************************
@@ -87,7 +89,7 @@ You can contact the author at :
//**************************************
// Includes & Memory related functions
//**************************************
//#include "xxhash.h"
// #include "xxhash.h"
// Modify the local functions below should you wish to use some other memory
// routines for malloc(), free()
#include <stdlib.h>
@@ -260,7 +262,13 @@ FORCE_INLINE U64
XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
{
if (align == XXH_unaligned)
return endian == XXH_littleEndian ? A64(ptr) : XXH_swap64(A64(ptr));
{
// Use memcpy to avoid unaligned UB
U64 tmp_aligned;
std::memcpy(&tmp_aligned, ptr, sizeof(U64));
return endian == XXH_littleEndian ? tmp_aligned
: XXH_swap64(tmp_aligned);
}
else
return endian == XXH_littleEndian ? *(U64*)ptr : XXH_swap64(*(U64*)ptr);
}
+1 -1
View File
@@ -1644,7 +1644,7 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o)
(inThisSet && result_->txns.find(txId) && !o.find(txId)) ||
(!inThisSet && !result_->txns.find(txId) && o.find(txId)));
Tx_t tx = inThisSet ? *result_->txns.find(txId) : *o.find(txId);
Tx_t tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
auto txID = tx.id();
if (result_->disputes.find(txID) != result_->disputes.end())
Loaded 100 of 260 files, more files were not shown because too many files have changed in this diff. Show more