Compare commits

...
Author SHA1 Message Date
Denis Angell c939b656f3 only build Release on release branch 2025-02-01 12:30:09 +01: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
+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
133 changed files with 4474 additions and 1104 deletions

No files matched your search

+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@v3
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@v3
uses: actions/upload-artifact@v4
continue-on-error: true
with:
name: levelization.patch
+4
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
@@ -671,6 +672,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
+1 -1
View File
@@ -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)
- [Xaman](https://xaman.app)
- [Crossmark](https://crossmark.io)
+15 -1
View File
@@ -34,7 +34,21 @@ message(\"WasmEdge DONE\")
git checkout src/ripple/protocol/impl/BuildInfo.cpp &&
sed -i s/\"0.0.0\"/\"$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4\"/g src/ripple/protocol/impl/BuildInfo.cpp &&
cd release-build &&
cmake .. -DCMAKE_BUILD_TYPE=Release -DBoost_NO_BOOST_CMAKE=ON -DLLVM_DIR=/usr/lib64/llvm13/lib/cmake/llvm/ -DLLVM_LIBRARY_DIR=/usr/lib64/llvm13/lib/ -DWasmEdge_LIB=/usr/local/lib64/libwasmedge.a &&
if [[ "$(git rev-parse --abbrev-ref HEAD)" == "release" ]]; then
echo "On release branch: Building with Release configuration"
cmake .. -DCMAKE_BUILD_TYPE=Release -DBoost_NO_BOOST_CMAKE=ON \
-DLLVM_DIR=/usr/lib64/llvm13/lib/cmake/llvm/ \
-DLLVM_LIBRARY_DIR=/usr/lib64/llvm13/lib/ \
-DWasmEdge_LIB=/usr/local/lib64/libwasmedge.a
else
echo "Not on release branch ($BRANCH): Building with default configuration"
cmake .. -DCMAKE_BUILD_TYPE=Debug \
-DBoost_NO_BOOST_CMAKE=ON \
-DLLVM_DIR=/usr/lib64/llvm13/lib/cmake/llvm/ \
-DLLVM_LIBRARY_DIR=/usr/lib64/llvm13/lib/ \
-DWasmEdge_LIB=/usr/local/lib64/libwasmedge.a \
-Dassert=ON
fi
make -j$3 VERBOSE=1 &&
strip -s rippled &&
mv rippled xahaud &&
+1 -1
View File
@@ -92,7 +92,7 @@ pwd &&
tar -xzf cmake-3.23.1-linux-x86_64.tar.gz -C /hbb/ &&
echo "-- Install Boost 1.86.0 --" &&
pwd &&
( wget -nc -q https://boostorg.jfrog.io/artifactory/main/release/1.86.0/source/boost_1_86_0.tar.gz; echo "" ) &&
( 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 ../ &&
+6 -5
View File
@@ -186,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);
@@ -330,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
@@ -374,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);
});
@@ -539,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());
});
@@ -614,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);
+5 -4
View File
@@ -1217,9 +1217,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,
@@ -4790,7 +4791,7 @@ DEFINE_HOOK_FUNCTION(
if (float1 == 0)
{
j.trace() << "HookTrace[" << HC_ACC() << "]:"
j.trace() << "HookTrace[" << HC_ACC() << "]: "
<< (read_len == 0
? ""
: std::string_view(
+68 -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())
@@ -313,10 +308,8 @@ Ledger::Ledger(
// 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 +338,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 +353,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,16 +372,16 @@ 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();
}
@@ -415,8 +404,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 +440,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 +468,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 +482,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 +536,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 +546,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 +562,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 +573,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 +591,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 +608,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 +710,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 +832,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 +841,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 +855,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 +880,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 +1044,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 +1063,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();
}
//------------------------------------------------------------------------------
+17 -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
@@ -290,10 +294,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 +309,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 +405,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 -1
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
+3
View File
@@ -152,6 +152,9 @@ public:
std::string
getCompleteLedgers();
RangeSet<std::uint32_t>
getCompleteLedgersRangeSet();
/** 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
+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);
@@ -1714,6 +1714,13 @@ LedgerMaster::getCompleteLedgers()
return to_string(mCompleteLedgers);
}
RangeSet<std::uint32_t>
LedgerMaster::getCompleteLedgersRangeSet()
{
std::lock_guard sl(mCompleteLock);
return mCompleteLedgers;
}
std::optional<NetClock::time_point>
LedgerMaster::getCloseTimeBySeq(LedgerIndex ledgerIndex)
{
@@ -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;
}
+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";
}
+6 -6
View File
@@ -22,6 +22,7 @@
#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 {
@@ -147,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";
@@ -202,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)";
@@ -231,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";
@@ -244,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;
}
+32 -135
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,9 +68,9 @@
#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>
@@ -116,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
{
@@ -272,6 +198,9 @@ public:
std::string
strOperatingMode(bool const admin = false) const override;
StateAccounting::CounterData
getStateAccountingData();
//
// Transaction operations.
//
@@ -776,11 +705,17 @@ 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
{
@@ -843,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);
@@ -1130,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
@@ -2396,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)
{
@@ -4193,6 +4128,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)
@@ -4663,50 +4604,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
+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
+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
+9 -7
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)};
+7
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,
+39
View File
@@ -889,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;
+16 -8
View File
@@ -1270,10 +1270,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 +1306,7 @@ Transactor::executeHookChain(
{
JLOG(j_.warn())
<< "HookError[" << account << "-"
<< ctx_.tx.getAccountID(sfAccount) << "]: "
<< ctx_.tx.getAccountID(sfAccount)
<< "]: Execution failure (exceptional) "
<< "Exception: " << e.what() << " HookHash: " << hookHash;
@@ -1426,13 +1434,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();
}
}
@@ -1678,13 +1686,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();
}
}
+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);
}
};
+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 {
+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;
@@ -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())
+2
View File
@@ -155,6 +155,8 @@ public:
std::map<std::string, PublicKey>
IMPORT_VL_KEYS; // hex string -> class PublicKey (for caching purposes)
std::vector<std::string> DATAGRAM_MONITOR;
enum StartUpType {
FRESH,
NORMAL,
+1
View File
@@ -101,6 +101,7 @@ struct ConfigSection
#define SECTION_SWEEP_INTERVAL "sweep_interval"
#define SECTION_NETWORK_ID "network_id"
#define SECTION_IMPORT_VL_KEYS "import_vl_keys"
#define SECTION_DATAGRAM_MONITOR "datagram_monitor"
} // namespace ripple
+34 -32
View File
@@ -68,10 +68,8 @@ namespace detail {
[[nodiscard]] std::uint64_t
getMemorySize()
{
struct sysinfo si;
if (sysinfo(&si) == 0)
return static_cast<std::uint64_t>(si.totalram);
if (struct sysinfo si; sysinfo(&si) == 0)
return static_cast<std::uint64_t>(si.totalram) * si.mem_unit;
return 0;
}
@@ -265,7 +263,8 @@ getEnvVar(char const* name)
}
Config::Config()
: j_(beast::Journal::getNullSink()), ramSize_(detail::getMemorySize())
: j_(beast::Journal::getNullSink())
, ramSize_(detail::getMemorySize() / (1024 * 1024 * 1024))
{
}
@@ -282,6 +281,9 @@ Config::setupControl(bool bQuiet, bool bSilent, bool bStandalone)
// RAM and CPU resources. We default to "tiny" for standalone mode.
if (!bStandalone)
{
NODE_SIZE = 4;
return;
// First, check against 'minimum' RAM requirements per node size:
auto const& threshold =
sizedItems[std::underlying_type_t<SizedItem>(SizedItem::ramSizeGB)];
@@ -290,22 +292,18 @@ Config::setupControl(bool bQuiet, bool bSilent, bool bStandalone)
threshold.second.begin(),
threshold.second.end(),
[this](std::size_t limit) {
return (ramSize_ / (1024 * 1024 * 1024)) < limit;
return (limit == 0) || (ramSize_ < limit);
});
assert(ns != threshold.second.end());
if (ns != threshold.second.end())
NODE_SIZE = std::distance(threshold.second.begin(), ns);
// Adjust the size based on the number of hardware threads of
// execution available to us:
if (auto const hc = std::thread::hardware_concurrency())
{
if (hc == 1)
NODE_SIZE = 0;
if (hc < 4)
NODE_SIZE = std::min<std::size_t>(NODE_SIZE, 1);
}
if (auto const hc = std::thread::hardware_concurrency(); hc != 0)
NODE_SIZE = std::min<std::size_t>(hc / 2, NODE_SIZE);
}
assert(NODE_SIZE <= 4);
@@ -470,26 +468,24 @@ Config::loadFromString(std::string const& fileContents)
SNTP_SERVERS = *s;
// if the user has specified ip:port then replace : with a space.
{
auto replaceColons = [](std::vector<std::string>& strVec) {
const static std::regex e(":([0-9]+)$");
for (auto& line : strVec)
{
// skip anything that might be an ipv6 address
if (std::count(line.begin(), line.end(), ':') != 1)
continue;
auto replaceColons = [](std::vector<std::string>& strVec) {
const static std::regex e(":([0-9]+)$");
for (auto& line : strVec)
{
// skip anything that might be an ipv6 address
if (std::count(line.begin(), line.end(), ':') != 1)
continue;
std::string result = std::regex_replace(line, e, " $1");
// sanity check the result of the replace, should be same length
// as input
if (result.size() == line.size())
line = result;
}
};
std::string result = std::regex_replace(line, e, " $1");
// sanity check the result of the replace, should be same length
// as input
if (result.size() == line.size())
line = result;
}
};
replaceColons(IPS_FIXED);
replaceColons(IPS);
}
replaceColons(IPS_FIXED);
replaceColons(IPS);
{
std::string dbPath;
@@ -514,6 +510,12 @@ Config::loadFromString(std::string const& fileContents)
NETWORK_ID = beast::lexicalCastThrow<uint32_t>(strTemp);
}
if (auto s = getIniFileSection(secConfig, SECTION_DATAGRAM_MONITOR))
{
DATAGRAM_MONITOR = *s;
replaceColons(DATAGRAM_MONITOR);
}
if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_))
PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
+2
View File
@@ -19,8 +19,10 @@
#include <ripple/basics/contract.h>
#include <ripple/json/json_reader.h>
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <istream>
#include <string>
+1 -1
View File
@@ -195,7 +195,7 @@ three database entries upon completion.
Since downloads execute serially by design, the entries in this table always
correspond to the contents of a single file.
| Bytes | Size | Part |
| Bytes | size | Part |
|:------:|:----------:|:----:|
| 0x... | 2147483647 | 0 |
| 0x... | 2147483647 | 1 |
+5 -21
View File
@@ -823,11 +823,9 @@ private:
return jvRequest;
}
// owner_info <account>|<account_public_key> [strict]
// owner_info <seed>|<pass_phrase>|<key> [<ledger>] [strict]
// account_info <account>|<account_public_key> [strict]
// account_info <seed>|<pass_phrase>|<key> [<ledger>] [strict]
// account_offers <account>|<account_public_key> [<ledger>] [strict]
// owner_info <account>
// account_info <account> [<ledger>]
// account_offers <account> [<ledger>]
Json::Value
parseAccountItems(Json::Value const& jvParams)
{
@@ -1050,10 +1048,7 @@ private:
// Parameters 0 and 1 are accounts
if (i < 2)
{
if (parseBase58<PublicKey>(
TokenType::AccountPublic, strParam) ||
parseBase58<AccountID>(strParam) ||
parseGenericSeed(strParam))
if (parseBase58<AccountID>(strParam))
{
jvRequest[accFields[i]] = std::move(strParam);
}
@@ -1079,16 +1074,8 @@ private:
{
std::string strIdent = jvParams[0u].asString();
unsigned int iCursor = jvParams.size();
bool bStrict = false;
if (iCursor >= 2 && jvParams[iCursor - 1] == jss::strict)
{
bStrict = true;
--iCursor;
}
if (!parseBase58<PublicKey>(TokenType::AccountPublic, strIdent) &&
!parseBase58<AccountID>(strIdent) && !parseGenericSeed(strIdent))
if (!parseBase58<AccountID>(strIdent))
return rpcError(rpcACT_MALFORMED);
// Get info on account.
@@ -1096,9 +1083,6 @@ private:
jvRequest[jss::account] = strIdent;
if (bStrict)
jvRequest[jss::strict] = 1;
if (iCursor == 2 && !jvParseLedger(jvRequest, jvParams[1u].asString()))
return rpcError(rpcLGR_IDX_MALFORMED);
+1 -2
View File
@@ -22,7 +22,7 @@ uint64 Appnum Application defined constant
uint16 KeySize Key size in bytes
uint64 Salt A random seed
uint64 Pepper The salt hashed
uint16 BlockSize Size of a file block in bytes
uint16 BlockSize size of a file block in bytes
uint16 LoadFactor Target fraction in 65536ths
uint8[56] Reserved Zeroes
uint8[] Reserved Zero-pad to block size
@@ -160,4 +160,3 @@ Iteration 0: RIPEMD160[nudb.dat] = FAE6AE84C15968B0419FDFC014931EA12A396C71
Iteration 1: RIPEMD160[nudb.key] = F96BF2722AB2EE009FFAE4A36AAFC4F220E21951
Iteration 1: RIPEMD160[nudb.dat] = FAE6AE84C15968B0419FDFC014931EA12A396C71
```
+1 -1
View File
@@ -1,4 +1,4 @@
# Shard Size Tuning
# Shard size Tuning
The purpose of this document is to compare the sizes of shards containing
varying amounts of ledgers.
@@ -38,7 +38,6 @@ DecodedBlob::DecodedBlob(void const* key, void const* value, int valueBytes)
m_success = false;
m_key = key;
// VFALCO NOTE Ledger indexes should have started at 1
m_objectType = hotUNKNOWN;
m_objectData = nullptr;
m_dataBytes = std::max(0, valueBytes - 9);
+2 -3
View File
@@ -688,9 +688,6 @@ Shard::finalize(bool writeSQLite, std::optional<uint256> const& referenceHash)
ledger->stateMap().setLedgerSeq(ledgerSeq);
ledger->txMap().setLedgerSeq(ledgerSeq);
// RH TODO: investigate why this assertion was failing in
// ripple.rpc.NodeToShardRPC
// assert(ledger->read(keylet::fees()));
ledger->setImmutable();
if (!ledger->stateMap().fetchRoot(
SHAMapHash{ledger->info().accountHash}, nullptr))
@@ -713,6 +710,8 @@ Shard::finalize(bool writeSQLite, std::optional<uint256> const& referenceHash)
if (writeSQLite && !storeSQLite(ledger))
return fail("failed storing to SQLite databases");
assert(ledger->info().seq == ledgerSeq && ledger->read(keylet::fees()));
hash = ledger->info().parentHash;
next = std::move(ledger);
-7
View File
@@ -91,17 +91,10 @@ private:
using value_type = map_type::value_type;
struct Transform
#ifdef _LIBCPP_VERSION
: std::unary_function<
map_type::right_map::const_iterator::value_type const&,
beast::IP::Endpoint const&>
#endif
{
#ifndef _LIBCPP_VERSION
using first_argument_type =
map_type::right_map::const_iterator::value_type const&;
using result_type = beast::IP::Endpoint const&;
#endif
explicit Transform() = default;
-11
View File
@@ -69,14 +69,9 @@ public:
public:
// Iterator transformation to extract the endpoint from Element
struct Transform
#ifdef _LIBCPP_VERSION
: public std::unary_function<Element, Endpoint>
#endif
{
#ifndef _LIBCPP_VERSION
using first_argument = Element;
using result_type = Endpoint;
#endif
explicit Transform() = default;
@@ -239,15 +234,9 @@ public:
template <bool IsConst>
struct Transform
#ifdef _LIBCPP_VERSION
: public std::
unary_function<typename lists_type::value_type, Hop<IsConst>>
#endif
{
#ifndef _LIBCPP_VERSION
using first_argument = typename lists_type::value_type;
using result_type = Hop<IsConst>;
#endif
explicit Transform() = default;
+3 -1
View File
@@ -74,7 +74,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 74;
static constexpr std::size_t numFeatures = 76;
/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
@@ -362,6 +362,8 @@ extern uint256 const fix240819;
extern uint256 const fixPageCap;
extern uint256 const fix240911;
extern uint256 const fixFloatDivide;
extern uint256 const fixReduceImport;
extern uint256 const fixXahauV3;
} // namespace ripple
+9 -4
View File
@@ -587,7 +587,12 @@ isAddable(STAmount const& amt1, STAmount const& amt2)
// the low-level routine stAmountCanonicalize on an amendment switch. Only
// transactions need to use this switchover. Outside of a transaction it's safe
// to unconditionally use the new behavior.
extern LocalValue<bool> stAmountCanonicalizeSwitchover;
bool
getSTAmountCanonicalizeSwitchover();
void
setSTAmountCanonicalizeSwitchover(bool v);
/** RAII class to set and restore the STAmount canonicalize switchover.
*/
@@ -595,14 +600,14 @@ extern LocalValue<bool> stAmountCanonicalizeSwitchover;
class STAmountSO
{
public:
explicit STAmountSO(bool v) : saved_(*stAmountCanonicalizeSwitchover)
explicit STAmountSO(bool v) : saved_(getSTAmountCanonicalizeSwitchover())
{
*stAmountCanonicalizeSwitchover = v;
setSTAmountCanonicalizeSwitchover(v);
}
~STAmountSO()
{
*stAmountCanonicalizeSwitchover = saved_;
setSTAmountCanonicalizeSwitchover(saved_);
}
private:
+1
View File
@@ -184,6 +184,7 @@ enum TEFcodes : TERUnderlyingType {
tefPAST_IMPORT_SEQ,
tefPAST_IMPORT_VL_SEQ,
tefNONDIR_EMIT,
tefIMPORT_BLACKHOLED,
};
//------------------------------------------------------------------------------
+2 -9
View File
@@ -33,13 +33,8 @@ namespace BuildInfo {
// and follow the format described at http://semver.org/
//------------------------------------------------------------------------------
// clang-format off
char const* const versionString = "0.0.0"
char const* const versionString = "0.0.0";
// clang-format on
#ifdef DEBUG
"+DEBUG"
#endif
//--------------------------------------------------------------------------
;
//
// Don't touch anything below this line
@@ -51,9 +46,7 @@ getVersionString()
// if the external build engine fails to populate the version string,
// then we will do it ourselves.
static std::string generatedVersionString;
if (generatedVersionString == "" &&
(versionString == std::string("0.") + std::string("0.0") ||
versionString == std::string("0.0.0+DEBUG")))
if (generatedVersionString == "" && versionString == std::string("0.0.0"))
{
std::string y = std::string(__DATE__ + 7);
std::string d = std::string(
+2
View File
@@ -468,6 +468,8 @@ REGISTER_FIX (fix240819, Supported::yes, VoteBehavior::De
REGISTER_FIX (fixPageCap, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fix240911, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixFloatDivide, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixReduceImport, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixXahauV3, Supported::yes, VoteBehavior::DefaultNo);
// The following amendments are obsolete, but must remain supported
// because they could potentially get enabled.
+28 -7
View File
@@ -34,7 +34,28 @@
namespace ripple {
LocalValue<bool> stAmountCanonicalizeSwitchover(true);
namespace {
// Use a static inside a function to help prevent order-of-initialzation issues
LocalValue<bool>&
getStaticSTAmountCanonicalizeSwitchover()
{
static LocalValue<bool> r{true};
return r;
}
} // namespace
bool
getSTAmountCanonicalizeSwitchover()
{
return *getStaticSTAmountCanonicalizeSwitchover();
}
void
setSTAmountCanonicalizeSwitchover(bool v)
{
*getStaticSTAmountCanonicalizeSwitchover() = v;
}
static const std::uint64_t tenTo14 = 100000000000000ull;
static const std::uint64_t tenTo14m1 = tenTo14 - 1;
@@ -395,7 +416,7 @@ operator+(STAmount const& v1, STAmount const& v2)
if (v1.native())
return {v1.getFName(), getSNValue(v1) + getSNValue(v2)};
if (*stNumberSwitchover)
if (getSTNumberSwitchover())
{
auto x = v1;
x = v1.iou() + v2.iou();
@@ -717,7 +738,7 @@ STAmount::canonicalize()
return;
}
if (*stAmountCanonicalizeSwitchover)
if (getSTAmountCanonicalizeSwitchover())
{
// log(cMaxNativeN, 10) == 17
if (mOffset > 17)
@@ -725,7 +746,7 @@ STAmount::canonicalize()
"Native currency amount out of range");
}
if (*stNumberSwitchover && *stAmountCanonicalizeSwitchover)
if (getSTNumberSwitchover() && getSTAmountCanonicalizeSwitchover())
{
Number num(
mIsNegative ? -mValue : mValue, mOffset, Number::unchecked{});
@@ -744,7 +765,7 @@ STAmount::canonicalize()
while (mOffset > 0)
{
if (*stAmountCanonicalizeSwitchover)
if (getSTAmountCanonicalizeSwitchover())
{
// N.B. do not move the overflow check to after the
// multiplication
@@ -765,7 +786,7 @@ STAmount::canonicalize()
mIsNative = false;
if (*stNumberSwitchover)
if (getSTNumberSwitchover())
{
*this = iou();
return;
@@ -1208,7 +1229,7 @@ multiply(STAmount const& v1, STAmount const& v2, Issue const& issue)
return STAmount(v1.getFName(), minV * maxV);
}
if (*stNumberSwitchover)
if (getSTNumberSwitchover())
return {IOUAmount{Number{v1} * Number{v2}}, issue};
std::uint64_t value1 = v1.mantissa();
+1 -1
View File
@@ -116,6 +116,7 @@ transResults()
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."),
MAKE_ERROR(tefNONDIR_EMIT, "An emitted txn was injected into the ledger without a corresponding directory entry."),
MAKE_ERROR(tefIMPORT_BLACKHOLED, "Cannot import keying because target account is blackholed."),
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),
@@ -136,7 +137,6 @@ transResults()
MAKE_ERROR(telNON_LOCAL_EMITTED_TXN, "Emitted transaction cannot be applied because it was not generated locally."),
MAKE_ERROR(telIMPORT_VL_KEY_NOT_RECOGNISED, "Import vl key was not recognized."),
MAKE_ERROR(telCAN_NOT_QUEUE_IMPORT, "Import transaction was not able to be directly applied and cannot be queued."),
MAKE_ERROR(temMALFORMED, "Malformed transaction."),
MAKE_ERROR(temBAD_AMOUNT, "Can only send positive amounts."),
MAKE_ERROR(temBAD_CURRENCY, "Malformed: Bad currency."),
+5 -5
View File
@@ -138,8 +138,7 @@ TxMeta::getAffectedAccounts() const
if (index != -1)
{
const STObject* inner =
dynamic_cast<const STObject*>(&it.peekAtIndex(index));
auto inner = dynamic_cast<STObject const*>(&it.peekAtIndex(index));
assert(inner);
if (inner)
{
@@ -157,8 +156,7 @@ TxMeta::getAffectedAccounts() const
(field.getFName() == sfTakerPays) ||
(field.getFName() == sfTakerGets))
{
const STAmount* lim =
dynamic_cast<const STAmount*>(&field);
auto lim = dynamic_cast<STAmount const*>(&field);
assert(lim);
if (lim != nullptr)
@@ -242,7 +240,9 @@ TxMeta::addRaw(Serializer& s, TER result, std::uint32_t index)
{
mResult = TERtoInt(result);
mIndex = index;
assert((mResult == 0) || ((mResult > 100) && (mResult <= 255)));
assert(
(mResult == 0 || mResult == 1) ||
((mResult > 100) && (mResult <= 255)));
mNodes.sort([](STObject const& o1, STObject const& o2) {
return o1.getFieldH256(sfLedgerIndex) < o2.getFieldH256(sfLedgerIndex);
+6
View File
@@ -368,6 +368,7 @@ JSS(invalid_API_version); // out: Many, when a request has an invalid
// version
JSS(io_latency_ms); // out: NetworkOPs
JSS(ip); // in: Connect, out: OverlayImpl
JSS(is_burned); // out: nft_info (clio)
JSS(issuer); // in: RipplePathFind, Subscribe,
// Unsubscribe, BookOffers
// out: STPathSet, STAmount
@@ -483,6 +484,9 @@ JSS(nft_offer); // in: LedgerEntry
JSS(nft_offer_index); // out nft_buy_offers, nft_sell_offers
JSS(nft_page); // in: LedgerEntry
JSS(nft_serial); // out: account_nfts
JSS(nft_taxon); // out: nft_info (clio)
JSS(nftoken_id); // out: insertNFTokenID
JSS(nftoken_ids); // out: insertNFTokenID
JSS(no_ripple); // out: AccountLines
JSS(no_ripple_peer); // out: AccountLines
JSS(node); // out: LedgerEntry
@@ -504,6 +508,7 @@ JSS(nth); // out: RPC server_definitions
JSS(obligations); // out: GatewayBalances
JSS(offer); // in: LedgerEntry
JSS(offers); // out: NetworkOPs, AccountOffers, Subscribe
JSS(offer_id); // out: insertNFTokenOfferID
JSS(offline); // in: TransactionSign
JSS(offset); // in/out: AccountTxOld
JSS(open); // out: handlers/Ledger
@@ -660,6 +665,7 @@ JSS(transaction); // in: Tx
JSS(transaction_hash); // out: RCLCxPeerPos, LedgerToJson
JSS(transactions); // out: LedgerToJson,
// in: AccountTx*, Unsubscribe
JSS(transfer_rate); // out: nft_info (clio)
JSS(transitions); // out: NetworkOPs
JSS(treenode_cache_size); // out: GetCounts
JSS(treenode_track_size); // out: GetCounts
+58
View File
@@ -0,0 +1,58 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_RPC_NFTSYNTHETICSERIALIZER_H_INCLUDED
#define RIPPLE_RPC_NFTSYNTHETICSERIALIZER_H_INCLUDED
#include <ripple/protocol/Protocol.h>
#include <ripple/protocol/STBase.h>
#include <functional>
#include <memory>
namespace Json {
class Value;
}
namespace ripple {
class TxMeta;
class STTx;
namespace RPC {
struct JsonContext;
/**
Adds common synthetic fields to transaction-related JSON responses
@{
*/
void
insertNFTSyntheticInJson(
Json::Value&,
RPC::JsonContext const&,
std::shared_ptr<STTx const> const&,
TxMeta const&);
/** @} */
} // namespace RPC
} // namespace ripple
#endif
+68
View File
@@ -0,0 +1,68 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_RPC_NFTOKENID_H_INCLUDED
#define RIPPLE_RPC_NFTOKENID_H_INCLUDED
#include <ripple/protocol/Protocol.h>
#include <functional>
#include <memory>
namespace Json {
class Value;
}
namespace ripple {
class TxMeta;
class STTx;
namespace RPC {
/**
Add a `nftoken_ids` field to the `meta` output parameter.
The field is only added to successful NFTokenMint, NFTokenAcceptOffer,
and NFTokenCancelOffer transactions.
Helper functions are not static because they can be used by Clio.
@{
*/
bool
canHaveNFTokenID(
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta);
std::optional<uint256>
getNFTokenIDFromPage(TxMeta const& transactionMeta);
std::vector<uint256>
getNFTokenIDFromDeletedOffer(TxMeta const& transactionMeta);
void
insertNFTokenID(
Json::Value& response,
std::shared_ptr<STTx const> const& transaction,
TxMeta const& transactionMeta);
/** @} */
} // namespace RPC
} // namespace ripple
#endif
+64
View File
@@ -0,0 +1,64 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_RPC_NFTOKENOFFERID_H_INCLUDED
#define RIPPLE_RPC_NFTOKENOFFERID_H_INCLUDED
#include <ripple/protocol/Protocol.h>
#include <functional>
#include <memory>
namespace Json {
class Value;
}
namespace ripple {
class TxMeta;
class STTx;
namespace RPC {
/**
Add an `offer_id` field to the `meta` output parameter.
The field is only added to successful NFTokenCreateOffer transactions.
Helper functions are not static because they can be used by Clio.
@{
*/
bool
canHaveNFTokenOfferID(
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta);
std::optional<uint256>
getOfferIDFromCreatedOffer(TxMeta const& transactionMeta);
void
insertNFTokenOfferID(
Json::Value& response,
std::shared_ptr<STTx const> const& transaction,
TxMeta const& transactionMeta);
/** @} */
} // namespace RPC
} // namespace ripple
#endif
+21 -23
View File
@@ -58,7 +58,7 @@ addChannel(Json::Value& jsonLines, SLE const& line)
}
// {
// account: <account>|<account_public_key>
// account: <account>
// ledger_hash : <ledger>
// ledger_index : <ledger_index>
// limit: integer // optional
@@ -76,11 +76,12 @@ doAccountChannels(RPC::JsonContext& context)
if (!ledger)
return result;
std::string strIdent(params[jss::account].asString());
AccountID accountID;
if (auto const err = RPC::accountFromString(accountID, strIdent))
return err;
auto id = parseBase58<AccountID>(params[jss::account].asString());
if (!id)
{
return rpcError(rpcACT_MALFORMED);
}
AccountID const accountID{std::move(id.value())};
if (!ledger->exists(keylet::account(accountID)))
return rpcError(rpcACT_NOT_FOUND);
@@ -88,14 +89,12 @@ doAccountChannels(RPC::JsonContext& context)
std::string strDst;
if (params.isMember(jss::destination_account))
strDst = params[jss::destination_account].asString();
auto hasDst = !strDst.empty();
AccountID raDstAccount;
if (hasDst)
{
if (auto const err = RPC::accountFromString(raDstAccount, strDst))
return err;
}
auto const raDstAccount = [&]() -> std::optional<AccountID> {
return strDst.empty() ? std::nullopt : parseBase58<AccountID>(strDst);
}();
if (!strDst.empty() && !raDstAccount)
return rpcError(rpcACT_MALFORMED);
unsigned int limit;
if (auto err = readLimitField(limit, RPC::Tuning::accountChannels, context))
@@ -109,10 +108,9 @@ doAccountChannels(RPC::JsonContext& context)
{
std::vector<std::shared_ptr<SLE const>> items;
AccountID const& accountID;
bool hasDst;
AccountID const& raDstAccount;
std::optional<AccountID> const& raDstAccount;
};
VisitData visitData = {{}, accountID, hasDst, raDstAccount};
VisitData visitData = {{}, accountID, raDstAccount};
visitData.items.reserve(limit);
uint256 startAfter = beast::zero;
std::uint64_t startHint = 0;
@@ -163,7 +161,7 @@ doAccountChannels(RPC::JsonContext& context)
accountID,
startAfter,
startHint,
limit,
limit + 1,
[&visitData, &accountID, &count, &limit, &marker, &nextHint](
std::shared_ptr<SLE const> const& sleCur) {
if (!sleCur)
@@ -172,10 +170,9 @@ doAccountChannels(RPC::JsonContext& context)
return false;
}
// Filter out ripple state objects.
if (sleCur->getType() != ltPAYCHAN)
{
return false;
}
if (++count == limit)
{
@@ -183,9 +180,10 @@ doAccountChannels(RPC::JsonContext& context)
nextHint = RPC::getStartHint(sleCur, visitData.accountID);
}
if (count <= limit && (*sleCur)[sfAccount] == accountID &&
(!visitData.hasDst ||
visitData.raDstAccount == (*sleCur)[sfDestination]))
if (count <= limit && sleCur->getType() == ltPAYCHAN &&
(*sleCur)[sfAccount] == accountID &&
(!visitData.raDstAccount ||
*visitData.raDstAccount == (*sleCur)[sfDestination]))
{
visitData.items.emplace_back(sleCur);
}
@@ -199,7 +197,7 @@ doAccountChannels(RPC::JsonContext& context)
// Both conditions need to be checked because marker is set on the limit-th
// item, but if there is no item on the limit + 1 iteration, then there is
// no need to return a marker.
if (count == limit && marker)
if (count == limit + 1 && marker)
{
result[jss::limit] = limit;
result[jss::marker] =
@@ -46,13 +46,14 @@ doAccountCurrencies(RPC::JsonContext& context)
params.isMember(jss::account) ? params[jss::account].asString()
: params[jss::ident].asString());
bool const bStrict =
params.isMember(jss::strict) && params[jss::strict].asBool();
// Get info on account.
AccountID accountID; // out param
if (auto jvAccepted = RPC::accountFromString(accountID, strIdent, bStrict))
return jvAccepted;
auto id = parseBase58<AccountID>(strIdent);
if (!id)
{
RPC::inject_error(rpcACT_MALFORMED, result);
return result;
}
auto const accountID{std::move(id.value())};
if (!ledger->exists(keylet::account(accountID)))
return rpcError(rpcACT_NOT_FOUND);
+11 -12
View File
@@ -34,8 +34,6 @@ namespace ripple {
// {
// account: <ident>,
// strict: <bool> // optional (default false)
// // if true only allow public keys and addresses.
// ledger_hash : <ledger>
// ledger_index : <ledger_index>
// signer_lists : <bool> // optional (default false)
@@ -67,18 +65,17 @@ doAccountInfo(RPC::JsonContext& context)
if (!ledger)
return result;
bool bStrict = params.isMember(jss::strict) && params[jss::strict].asBool();
AccountID accountID;
// Get info on account.
auto jvAccepted = RPC::accountFromString(accountID, strIdent, bStrict);
if (jvAccepted)
return jvAccepted;
auto id = parseBase58<AccountID>(strIdent);
if (!id)
{
RPC::inject_error(rpcACT_MALFORMED, result);
return result;
}
auto const accountID{std::move(id.value())};
static constexpr std::
array<std::pair<std::string_view, LedgerSpecificFlags>, 10>
array<std::pair<std::string_view, LedgerSpecificFlags>, 11>
lsFlags{
{{"defaultRipple", lsfDefaultRipple},
{"depositAuth", lsfDepositAuth},
@@ -89,7 +86,8 @@ doAccountInfo(RPC::JsonContext& context)
{"passwordSpent", lsfPasswordSpent},
{"requireAuthorization", lsfRequireAuth},
{"tshCollect", lsfTshCollect},
{"requireDestinationTag", lsfRequireDestTag}}};
{"requireDestinationTag", lsfRequireDestTag},
{"uriTokenIssuer", lsfURITokenIssuer}}};
static constexpr std::
array<std::pair<std::string_view, LedgerSpecificFlags>, 5>
@@ -115,6 +113,7 @@ doAccountInfo(RPC::JsonContext& context)
return result;
}
Json::Value jvAccepted(Json::objectValue);
RPC::injectSLE(jvAccepted, *sleAccepted);
result[jss::account_data] = jvAccepted;
+22 -31
View File
@@ -30,17 +30,6 @@
namespace ripple {
struct VisitData
{
std::vector<RPCTrustLine> items;
AccountID const& accountID;
bool hasPeer;
AccountID const& raPeerAccount;
bool ignoreDefault;
uint32_t foundCount;
};
void
addLine(Json::Value& jsonLines, RPCTrustLine const& line)
{
@@ -88,7 +77,7 @@ addLine(Json::Value& jsonLines, RPCTrustLine const& line)
}
// {
// account: <account>|<account_public_key>
// account: <account>
// ledger_hash : <ledger>
// ledger_index : <ledger_index>
// limit: integer // optional
@@ -108,15 +97,13 @@ doAccountLines(RPC::JsonContext& context)
if (!ledger)
return result;
std::string strIdent(params[jss::account].asString());
AccountID accountID;
if (auto jv = RPC::accountFromString(accountID, strIdent))
auto id = parseBase58<AccountID>(params[jss::account].asString());
if (!id)
{
for (auto it = jv.begin(); it != jv.end(); ++it)
result[it.memberName()] = *it;
RPC::inject_error(rpcACT_MALFORMED, result);
return result;
}
auto const accountID{std::move(id.value())};
if (!ledger->exists(keylet::account(accountID)))
return rpcError(rpcACT_NOT_FOUND);
@@ -124,17 +111,14 @@ doAccountLines(RPC::JsonContext& context)
std::string strPeer;
if (params.isMember(jss::peer))
strPeer = params[jss::peer].asString();
auto hasPeer = !strPeer.empty();
AccountID raPeerAccount;
if (hasPeer)
auto const raPeerAccount = [&]() -> std::optional<AccountID> {
return strPeer.empty() ? std::nullopt : parseBase58<AccountID>(strPeer);
}();
if (!strPeer.empty() && !raPeerAccount)
{
if (auto jv = RPC::accountFromString(raPeerAccount, strPeer))
{
for (auto it = jv.begin(); it != jv.end(); ++it)
result[it.memberName()] = *it;
return result;
}
RPC::inject_error(rpcACT_MALFORMED, result);
return result;
}
unsigned int limit;
@@ -150,8 +134,15 @@ doAccountLines(RPC::JsonContext& context)
params[jss::ignore_default].asBool();
Json::Value& jsonLines(result[jss::lines] = Json::arrayValue);
VisitData visitData = {
{}, accountID, hasPeer, raPeerAccount, ignoreDefault, 0};
struct VisitData
{
std::vector<RPCTrustLine> items;
AccountID const& accountID;
std::optional<AccountID> const& raPeerAccount;
bool ignoreDefault;
uint32_t foundCount;
};
VisitData visitData = {{}, accountID, raPeerAccount, ignoreDefault, 0};
uint256 startAfter = beast::zero;
std::uint64_t startHint = 0;
@@ -239,8 +230,8 @@ doAccountLines(RPC::JsonContext& context)
RPCTrustLine::makeItem(visitData.accountID, sleCur);
if (line &&
(!visitData.hasPeer ||
visitData.raPeerAccount ==
(!visitData.raPeerAccount ||
*visitData.raPeerAccount ==
line->getAccountIDPeer()))
{
visitData.items.emplace_back(*line);
+11 -20
View File
@@ -19,7 +19,6 @@
#include <ripple/app/main/Application.h>
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
#include <ripple/json/json_writer.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/ErrorCodes.h>
@@ -39,7 +38,7 @@ namespace ripple {
/** General RPC command that can retrieve objects in the account root.
{
account: <account>|<account_public_key>
account: <account>
ledger_hash: <string> // optional
ledger_index: <string | unsigned integer> // optional
type: <string> // optional, defaults to all account objects types
@@ -60,17 +59,13 @@ doAccountNFTs(RPC::JsonContext& context)
if (ledger == nullptr)
return result;
AccountID accountID;
auto id = parseBase58<AccountID>(params[jss::account].asString());
if (!id)
{
auto const strIdent = params[jss::account].asString();
if (auto jv = RPC::accountFromString(accountID, strIdent))
{
for (auto it = jv.begin(); it != jv.end(); ++it)
result[it.memberName()] = *it;
return result;
}
RPC::inject_error(rpcACT_MALFORMED, result);
return result;
}
auto const accountID{id.value()};
if (!ledger->exists(keylet::account(accountID)))
return rpcError(rpcACT_NOT_FOUND);
@@ -177,17 +172,13 @@ doAccountObjects(RPC::JsonContext& context)
if (ledger == nullptr)
return result;
AccountID accountID;
auto const id = parseBase58<AccountID>(params[jss::account].asString());
if (!id)
{
auto const strIdent = params[jss::account].asString();
if (auto jv = RPC::accountFromString(accountID, strIdent))
{
for (auto it = jv.begin(); it != jv.end(); ++it)
result[it.memberName()] = *it;
return result;
}
RPC::inject_error(rpcACT_MALFORMED, result);
return result;
}
auto const accountID{id.value()};
if (!ledger->exists(keylet::account(accountID)))
return rpcError(rpcACT_NOT_FOUND);
+5 -8
View File
@@ -47,7 +47,7 @@ appendOfferJson(std::shared_ptr<SLE const> const& offer, Json::Value& offers)
};
// {
// account: <account>|<account_public_key>
// account: <account>
// ledger_hash : <ledger>
// ledger_index : <ledger_index>
// limit: integer // optional
@@ -65,16 +65,13 @@ doAccountOffers(RPC::JsonContext& context)
if (!ledger)
return result;
std::string strIdent(params[jss::account].asString());
AccountID accountID;
if (auto jv = RPC::accountFromString(accountID, strIdent))
auto id = parseBase58<AccountID>(params[jss::account].asString());
if (!id)
{
for (auto it = jv.begin(); it != jv.end(); ++it)
result[it.memberName()] = (*it);
RPC::inject_error(rpcACT_MALFORMED, result);
return result;
}
auto const accountID{std::move(id.value())};
// Get info on account.
result[jss::account] = toBase58(accountID);
+3
View File
@@ -34,6 +34,7 @@
#include <ripple/resource/Fees.h>
#include <ripple/rpc/Context.h>
#include <ripple/rpc/DeliveredAmount.h>
#include <ripple/rpc/NFTSyntheticSerializer.h>
#include <ripple/rpc/Role.h>
#include <ripple/rpc/impl/RPCHelpers.h>
@@ -307,6 +308,8 @@ populateJsonResponse(
jvObj[jss::validated] = true;
insertDeliveredAmount(
jvObj[jss::meta], context, txn, *txnMeta);
insertNFTSyntheticInJson(
jvObj, context, txn->getSTransaction(), *txnMeta);
}
}
}
+10 -14
View File
@@ -18,6 +18,7 @@
//==============================================================================
#include <ripple/ledger/ReadView.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/jss.h>
@@ -46,13 +47,10 @@ doDepositAuthorized(RPC::JsonContext& context)
rpcINVALID_PARAMS,
RPC::expected_field_message(jss::source_account, "a string"));
AccountID srcAcct;
{
Json::Value const jvAccepted = RPC::accountFromString(
srcAcct, params[jss::source_account].asString(), true);
if (jvAccepted)
return jvAccepted;
}
auto srcID = parseBase58<AccountID>(params[jss::source_account].asString());
if (!srcID)
return rpcError(rpcACT_MALFORMED);
auto const srcAcct{std::move(srcID.value())};
// Validate destination_account.
if (!params.isMember(jss::destination_account))
@@ -62,13 +60,11 @@ doDepositAuthorized(RPC::JsonContext& context)
rpcINVALID_PARAMS,
RPC::expected_field_message(jss::destination_account, "a string"));
AccountID dstAcct;
{
Json::Value const jvAccepted = RPC::accountFromString(
dstAcct, params[jss::destination_account].asString(), true);
if (jvAccepted)
return jvAccepted;
}
auto dstID =
parseBase58<AccountID>(params[jss::destination_account].asString());
if (!dstID)
return rpcError(rpcACT_MALFORMED);
auto const dstAcct{std::move(dstID.value())};
// Validate ledger.
std::shared_ptr<ReadView const> ledger;
+7 -21
View File
@@ -20,6 +20,7 @@
#include <ripple/app/main/Application.h>
#include <ripple/app/paths/TrustLine.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/AccountID.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/PublicKey.h>
@@ -68,16 +69,11 @@ doGatewayBalances(RPC::JsonContext& context)
params.isMember(jss::account) ? params[jss::account].asString()
: params[jss::ident].asString());
bool const bStrict =
params.isMember(jss::strict) && params[jss::strict].asBool();
// Get info on account.
AccountID accountID;
auto jvAccepted = RPC::accountFromString(accountID, strIdent, bStrict);
if (jvAccepted)
return jvAccepted;
auto id = parseBase58<AccountID>(strIdent);
if (!id)
return rpcError(rpcACT_MALFORMED);
auto const accountID{std::move(id.value())};
context.loadType = Resource::feeHighBurdenRPC;
result[jss::account] = toBase58(accountID);
@@ -90,19 +86,9 @@ doGatewayBalances(RPC::JsonContext& context)
auto addHotWallet = [&hotWallets](Json::Value const& j) {
if (j.isString())
{
auto const pk = parseBase58<PublicKey>(
TokenType::AccountPublic, j.asString());
if (pk)
if (auto id = parseBase58<AccountID>(j.asString()); id)
{
hotWallets.insert(calcAccountID(*pk));
return true;
}
auto const id = parseBase58<AccountID>(j.asString());
if (id)
{
hotWallets.insert(*id);
hotWallets.insert(std::move(id.value()));
return true;
}
}
+5 -9
View File
@@ -50,7 +50,7 @@ fillTransaction(
}
// {
// account: <account>|<account_public_key>
// account: <account>
// ledger_hash : <ledger>
// ledger_index : <ledger_index>
// limit: integer // optional, number of problems
@@ -92,17 +92,13 @@ doNoRippleCheck(RPC::JsonContext& context)
Json::Value& jvTransactions =
transactions ? (result[jss::transactions] = Json::arrayValue) : dummy;
std::string strIdent(params[jss::account].asString());
AccountID accountID;
if (auto jv = RPC::accountFromString(accountID, strIdent))
auto id = parseBase58<AccountID>(params[jss::account].asString());
if (!id)
{
for (auto it(jv.begin()); it != jv.end(); ++it)
result[it.memberName()] = *it;
RPC::inject_error(rpcACT_MALFORMED, result);
return result;
}
auto const accountID{std::move(id.value())};
auto const sle = ledger->read(keylet::account(accountID));
if (!sle)
return rpcError(rpcACT_NOT_FOUND);
+8 -12
View File
@@ -20,6 +20,7 @@
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/json/json_value.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/jss.h>
#include <ripple/rpc/Context.h>
@@ -45,21 +46,16 @@ doOwnerInfo(RPC::JsonContext& context)
Json::Value ret;
// Get info on account.
auto const& closedLedger = context.ledgerMaster.getClosedLedger();
AccountID accountID;
auto jAccepted = RPC::accountFromString(accountID, strIdent);
ret[jss::accepted] = !jAccepted
? context.netOps.getOwnerInfo(closedLedger, accountID)
: jAccepted;
std::optional<AccountID> const accountID = parseBase58<AccountID>(strIdent);
ret[jss::accepted] = accountID.has_value()
? context.netOps.getOwnerInfo(closedLedger, accountID.value())
: rpcError(rpcACT_MALFORMED);
auto const& currentLedger = context.ledgerMaster.getCurrentLedger();
auto jCurrent = RPC::accountFromString(accountID, strIdent);
ret[jss::current] = !jCurrent
? context.netOps.getOwnerInfo(currentLedger, accountID)
: jCurrent;
ret[jss::current] = accountID.has_value()
? context.netOps.getOwnerInfo(currentLedger, *accountID)
: rpcError(rpcACT_MALFORMED);
return ret;
}
+9 -1
View File
@@ -30,6 +30,7 @@
#include <ripple/rpc/Context.h>
#include <ripple/rpc/Role.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/impl/UDPInfoSub.h>
namespace ripple {
@@ -42,7 +43,7 @@ doSubscribe(RPC::JsonContext& context)
if (!context.infoSub && !context.params.isMember(jss::url))
{
// Must be a JSON-RPC call.
JLOG(context.j.info()) << "doSubscribe: RPC subscribe requires a url";
JLOG(context.j.warn()) << "doSubscribe: RPC subscribe requires a url";
return rpcError(rpcINVALID_PARAMS);
}
@@ -373,6 +374,13 @@ doSubscribe(RPC::JsonContext& context)
}
}
if (ispSub)
{
if (std::shared_ptr<UDPInfoSub> udp =
std::dynamic_pointer_cast<UDPInfoSub>(ispSub))
udp->increment();
}
return jvResult;
}
+3
View File
@@ -29,6 +29,7 @@
#include <ripple/rpc/Context.h>
#include <ripple/rpc/DeliveredAmount.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <ripple/rpc/NFTSyntheticSerializer.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <charconv>
#include <regex>
@@ -330,6 +331,8 @@ populateJsonResponse(
response[jss::meta] = meta->getJson(JsonOptions::none);
insertDeliveredAmount(
response[jss::meta], context, result.txn, *meta);
insertNFTSyntheticInJson(
response, context, result.txn->getSTransaction(), *meta);
}
}
response[jss::validated] = result.validated;
+7
View File
@@ -25,6 +25,7 @@
#include <ripple/rpc/Context.h>
#include <ripple/rpc/Role.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/impl/UDPInfoSub.h>
namespace ripple {
@@ -245,6 +246,12 @@ doUnsubscribe(RPC::JsonContext& context)
context.netOps.tryRemoveRpcSub(context.params[jss::url].asString());
}
if (ispSub)
{
if (auto udp = std::dynamic_pointer_cast<UDPInfoSub>(ispSub))
udp->destroy();
}
return jvResult;
}
@@ -0,0 +1,50 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/rpc/NFTSyntheticSerializer.h>
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/ledger/OpenLedger.h>
#include <ripple/app/misc/Transaction.h>
#include <ripple/ledger/View.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/AccountID.h>
#include <ripple/protocol/Feature.h>
#include <ripple/rpc/Context.h>
#include <ripple/rpc/NFTokenID.h>
#include <ripple/rpc/NFTokenOfferID.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <boost/algorithm/string/case_conv.hpp>
namespace ripple {
namespace RPC {
void
insertNFTSyntheticInJson(
Json::Value& response,
RPC::JsonContext const& context,
std::shared_ptr<STTx const> const& transaction,
TxMeta const& transactionMeta)
{
insertNFTokenID(response[jss::meta], transaction, transactionMeta);
insertNFTokenOfferID(response[jss::meta], transaction, transactionMeta);
}
} // namespace RPC
} // namespace ripple
+202
View File
@@ -0,0 +1,202 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/rpc/NFTokenID.h>
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/ledger/OpenLedger.h>
#include <ripple/app/misc/Transaction.h>
#include <ripple/ledger/View.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/AccountID.h>
#include <ripple/protocol/Feature.h>
#include <ripple/rpc/Context.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <boost/algorithm/string/case_conv.hpp>
namespace ripple {
namespace RPC {
bool
canHaveNFTokenID(
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta)
{
if (!serializedTx)
return false;
TxType const tt = serializedTx->getTxnType();
if (tt != ttNFTOKEN_MINT && tt != ttNFTOKEN_ACCEPT_OFFER &&
tt != ttNFTOKEN_CANCEL_OFFER)
return false;
// if the transaction failed nothing could have been delivered.
if (transactionMeta.getResultTER() != tesSUCCESS)
return false;
return true;
}
std::optional<uint256>
getNFTokenIDFromPage(TxMeta const& transactionMeta)
{
// The metadata does not make it obvious which NFT was added. To figure
// that out we gather up all of the previous NFT IDs and all of the final
// NFT IDs and compare them to find what changed.
std::vector<uint256> prevIDs;
std::vector<uint256> finalIDs;
for (STObject const& node : transactionMeta.getNodes())
{
if (node.getFieldU16(sfLedgerEntryType) != ltNFTOKEN_PAGE)
continue;
SField const& fName = node.getFName();
if (fName == sfCreatedNode)
{
STArray const& toAddPrevNFTs = node.peekAtField(sfNewFields)
.downcast<STObject>()
.getFieldArray(sfNFTokens);
std::transform(
toAddPrevNFTs.begin(),
toAddPrevNFTs.end(),
std::back_inserter(finalIDs),
[](STObject const& nft) {
return nft.getFieldH256(sfNFTokenID);
});
}
else if (fName == sfModifiedNode)
{
// When a mint results in splitting an existing page,
// it results in a created page and a modified node. Sometimes,
// the created node needs to be linked to a third page, resulting
// in modifying that third page's PreviousPageMin or NextPageMin
// field changing, but no NFTs within that page changing. In this
// case, there will be no previous NFTs and we need to skip.
// However, there will always be NFTs listed in the final fields,
// as rippled outputs all fields in final fields even if they were
// not changed.
STObject const& previousFields =
node.peekAtField(sfPreviousFields).downcast<STObject>();
if (!previousFields.isFieldPresent(sfNFTokens))
continue;
STArray const& toAddPrevNFTs =
previousFields.getFieldArray(sfNFTokens);
std::transform(
toAddPrevNFTs.begin(),
toAddPrevNFTs.end(),
std::back_inserter(prevIDs),
[](STObject const& nft) {
return nft.getFieldH256(sfNFTokenID);
});
STArray const& toAddFinalNFTs = node.peekAtField(sfFinalFields)
.downcast<STObject>()
.getFieldArray(sfNFTokens);
std::transform(
toAddFinalNFTs.begin(),
toAddFinalNFTs.end(),
std::back_inserter(finalIDs),
[](STObject const& nft) {
return nft.getFieldH256(sfNFTokenID);
});
}
}
// We expect NFTs to be added one at a time. So finalIDs should be one
// longer than prevIDs. If that's not the case something is messed up.
if (finalIDs.size() != prevIDs.size() + 1)
return std::nullopt;
// Find the first NFT ID that doesn't match. We're looking for an
// added NFT, so the one we want will be the mismatch in finalIDs.
auto const diff = std::mismatch(
finalIDs.begin(), finalIDs.end(), prevIDs.begin(), prevIDs.end());
// There should always be a difference so the returned finalIDs
// iterator should never be end(). But better safe than sorry.
if (diff.first == finalIDs.end())
return std::nullopt;
return *diff.first;
}
std::vector<uint256>
getNFTokenIDFromDeletedOffer(TxMeta const& transactionMeta)
{
std::vector<uint256> tokenIDResult;
for (STObject const& node : transactionMeta.getNodes())
{
if (node.getFieldU16(sfLedgerEntryType) != ltNFTOKEN_OFFER ||
node.getFName() != sfDeletedNode)
continue;
auto const& toAddNFT = node.peekAtField(sfFinalFields)
.downcast<STObject>()
.getFieldH256(sfNFTokenID);
tokenIDResult.push_back(toAddNFT);
}
// Deduplicate the NFT IDs because multiple offers could affect the same NFT
// and hence we would get duplicate NFT IDs
sort(tokenIDResult.begin(), tokenIDResult.end());
tokenIDResult.erase(
unique(tokenIDResult.begin(), tokenIDResult.end()),
tokenIDResult.end());
return tokenIDResult;
}
void
insertNFTokenID(
Json::Value& response,
std::shared_ptr<STTx const> const& transaction,
TxMeta const& transactionMeta)
{
if (!canHaveNFTokenID(transaction, transactionMeta))
return;
// We extract the NFTokenID from metadata by comparing affected nodes
if (auto const type = transaction->getTxnType(); type == ttNFTOKEN_MINT)
{
std::optional<uint256> result = getNFTokenIDFromPage(transactionMeta);
if (result.has_value())
response[jss::nftoken_id] = to_string(result.value());
}
else if (type == ttNFTOKEN_ACCEPT_OFFER)
{
std::vector<uint256> result =
getNFTokenIDFromDeletedOffer(transactionMeta);
if (result.size() > 0)
response[jss::nftoken_id] = to_string(result.front());
}
else if (type == ttNFTOKEN_CANCEL_OFFER)
{
std::vector<uint256> result =
getNFTokenIDFromDeletedOffer(transactionMeta);
response[jss::nftoken_ids] = Json::Value(Json::arrayValue);
for (auto const& nftID : result)
response[jss::nftoken_ids].append(to_string(nftID));
}
}
} // namespace RPC
} // namespace ripple
+85
View File
@@ -0,0 +1,85 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/rpc/NFTokenOfferID.h>
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/ledger/OpenLedger.h>
#include <ripple/app/misc/Transaction.h>
#include <ripple/ledger/View.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/AccountID.h>
#include <ripple/protocol/Feature.h>
#include <ripple/rpc/Context.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <boost/algorithm/string/case_conv.hpp>
namespace ripple {
namespace RPC {
bool
canHaveNFTokenOfferID(
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta)
{
if (!serializedTx)
return false;
TxType const tt = serializedTx->getTxnType();
if (tt != ttNFTOKEN_CREATE_OFFER)
return false;
// if the transaction failed nothing could have been delivered.
if (transactionMeta.getResultTER() != tesSUCCESS)
return false;
return true;
}
std::optional<uint256>
getOfferIDFromCreatedOffer(TxMeta const& transactionMeta)
{
for (STObject const& node : transactionMeta.getNodes())
{
if (node.getFieldU16(sfLedgerEntryType) != ltNFTOKEN_OFFER ||
node.getFName() != sfCreatedNode)
continue;
return node.getFieldH256(sfLedgerIndex);
}
return std::nullopt;
}
void
insertNFTokenOfferID(
Json::Value& response,
std::shared_ptr<STTx const> const& transaction,
TxMeta const& transactionMeta)
{
if (!canHaveNFTokenOfferID(transaction, transactionMeta))
return;
std::optional<uint256> result = getOfferIDFromCreatedOffer(transactionMeta);
if (result.has_value())
response[jss::offer_id] = to_string(result.value());
}
} // namespace RPC
} // namespace ripple
+200
View File
@@ -361,6 +361,67 @@ ServerHandlerImp::onWSMessage(
}
}
void
ServerHandlerImp::onUDPMessage(
std::string const& message,
boost::asio::ip::tcp::endpoint const& remoteEndpoint,
std::function<void(std::string const&)> sendResponse)
{
Json::Value jv;
if (message.size() > RPC::Tuning::maxRequestSize ||
!Json::Reader{}.parse(message, jv) || !jv.isObject())
{
Json::Value jvResult(Json::objectValue);
jvResult[jss::type] = jss::error;
jvResult[jss::error] = "jsonInvalid";
jvResult[jss::value] = message;
std::string const response = to_string(jvResult);
JLOG(m_journal.trace())
<< "UDP sending error response: '" << jvResult << "'";
sendResponse(response);
return;
}
JLOG(m_journal.trace())
<< "UDP received '" << jv << "' from " << remoteEndpoint;
auto const postResult = m_jobQueue.postCoro(
jtCLIENT_RPC, // Using RPC job type since this is admin RPC
"UDP-RPC",
[this,
remoteEndpoint,
jv = std::move(jv),
sendResponse = std::move(sendResponse)](
std::shared_ptr<JobQueue::Coro> const& coro) {
// Process the request similar to WebSocket but with UDP context
Role const role = Role::ADMIN; // UDP-RPC is admin-only
auto const jr =
this->processUDP(jv, role, coro, sendResponse, remoteEndpoint);
std::string const response = to_string(jr);
JLOG(m_journal.trace())
<< "UDP sending '" << jr << "' to " << remoteEndpoint;
// Send response back via UDP
sendResponse(response);
});
if (postResult == nullptr)
{
// Request rejected, probably shutting down
Json::Value jvResult(Json::objectValue);
jvResult[jss::type] = jss::error;
jvResult[jss::error] = "serverShuttingDown";
jvResult[jss::value] = "Server is shutting down";
std::string const response = to_string(jvResult);
JLOG(m_journal.trace())
<< "UDP sending shutdown response to " << remoteEndpoint;
sendResponse(response);
}
}
void
ServerHandlerImp::onClose(Session& session, boost::system::error_code const&)
{
@@ -397,6 +458,145 @@ logDuration(
<< " microseconds. request = " << request;
}
Json::Value
ServerHandlerImp::processUDP(
Json::Value const& jv,
Role const& role,
std::shared_ptr<JobQueue::Coro> const& coro,
std::optional<std::function<void(std::string const&)>>
sendResponse /* used for subscriptions */,
boost::asio::ip::tcp::endpoint const& remoteEndpoint)
{
std::shared_ptr<InfoSub> is;
// Requests without "command" are invalid.
Json::Value jr(Json::objectValue);
try
{
auto apiVersion =
RPC::getAPIVersionNumber(jv, app_.config().BETA_RPC_API);
if (apiVersion == RPC::apiInvalidVersion ||
(!jv.isMember(jss::command) && !jv.isMember(jss::method)) ||
(jv.isMember(jss::command) && !jv[jss::command].isString()) ||
(jv.isMember(jss::method) && !jv[jss::method].isString()) ||
(jv.isMember(jss::command) && jv.isMember(jss::method) &&
jv[jss::command].asString() != jv[jss::method].asString()))
{
jr[jss::type] = jss::response;
jr[jss::status] = jss::error;
jr[jss::error] = apiVersion == RPC::apiInvalidVersion
? jss::invalid_API_version
: jss::missingCommand;
jr[jss::request] = jv;
if (jv.isMember(jss::id))
jr[jss::id] = jv[jss::id];
if (jv.isMember(jss::jsonrpc))
jr[jss::jsonrpc] = jv[jss::jsonrpc];
if (jv.isMember(jss::ripplerpc))
jr[jss::ripplerpc] = jv[jss::ripplerpc];
if (jv.isMember(jss::api_version))
jr[jss::api_version] = jv[jss::api_version];
return jr;
}
auto required = RPC::roleRequired(
apiVersion,
app_.config().BETA_RPC_API,
jv.isMember(jss::command) ? jv[jss::command].asString()
: jv[jss::method].asString());
if (Role::FORBID == role)
{
jr[jss::result] = rpcError(rpcFORBIDDEN);
}
else
{
Resource::Consumer c;
Resource::Charge loadType = Resource::feeReferenceRPC;
if (sendResponse.has_value())
is = UDPInfoSub::getInfoSub(
m_networkOPs, *sendResponse, remoteEndpoint);
RPC::JsonContext context{
{app_.journal("RPCHandler"),
app_,
loadType,
app_.getOPs(),
app_.getLedgerMaster(),
c,
role,
coro,
is,
apiVersion},
jv};
auto start = std::chrono::system_clock::now();
RPC::doCommand(context, jr[jss::result]);
auto end = std::chrono::system_clock::now();
logDuration(jv, end - start, m_journal);
}
}
catch (std::exception const& ex)
{
jr[jss::result] = RPC::make_error(rpcINTERNAL);
JLOG(m_journal.error())
<< "Exception while processing WS: " << ex.what() << "\n"
<< "Input JSON: " << Json::Compact{Json::Value{jv}};
}
if (is)
{
if (auto udp = std::dynamic_pointer_cast<UDPInfoSub>(is))
udp->destroy();
}
// Currently we will simply unwrap errors returned by the RPC
// API, in the future maybe we can make the responses
// consistent.
//
// Regularize result. This is duplicate code.
if (jr[jss::result].isMember(jss::error))
{
jr = jr[jss::result];
jr[jss::status] = jss::error;
auto rq = jv;
if (rq.isObject())
{
if (rq.isMember(jss::passphrase.c_str()))
rq[jss::passphrase.c_str()] = "<masked>";
if (rq.isMember(jss::secret.c_str()))
rq[jss::secret.c_str()] = "<masked>";
if (rq.isMember(jss::seed.c_str()))
rq[jss::seed.c_str()] = "<masked>";
if (rq.isMember(jss::seed_hex.c_str()))
rq[jss::seed_hex.c_str()] = "<masked>";
}
jr[jss::request] = rq;
}
else
{
if (jr[jss::result].isMember("forwarded") &&
jr[jss::result]["forwarded"])
jr = jr[jss::result];
jr[jss::status] = jss::success;
}
if (jv.isMember(jss::id))
jr[jss::id] = jv[jss::id];
if (jv.isMember(jss::jsonrpc))
jr[jss::jsonrpc] = jv[jss::jsonrpc];
if (jv.isMember(jss::ripplerpc))
jr[jss::ripplerpc] = jv[jss::ripplerpc];
if (jv.isMember(jss::api_version))
jr[jss::api_version] = jv[jss::api_version];
jr[jss::type] = jss::response;
return jr;
}
Json::Value
ServerHandlerImp::processSession(
std::shared_ptr<WSSession> const& session,
+15
View File
@@ -24,6 +24,7 @@
#include <ripple/core/JobQueue.h>
#include <ripple/json/Output.h>
#include <ripple/rpc/RPCHandler.h>
#include <ripple/rpc/impl/UDPInfoSub.h>
#include <ripple/rpc/impl/WSInfoSub.h>
#include <ripple/server/Server.h>
#include <ripple/server/Session.h>
@@ -164,6 +165,12 @@ public:
std::shared_ptr<WSSession> session,
std::vector<boost::asio::const_buffer> const& buffers);
void
onUDPMessage(
std::string const& message,
boost::asio::ip::tcp::endpoint const& remoteEndpoint,
std::function<void(std::string const&)> sendResponse);
void
onClose(Session& session, boost::system::error_code const&);
@@ -177,6 +184,14 @@ private:
std::shared_ptr<JobQueue::Coro> const& coro,
Json::Value const& jv);
Json::Value
processUDP(
Json::Value const& jv,
Role const& role,
std::shared_ptr<JobQueue::Coro> const& coro,
std::optional<std::function<void(std::string const&)>> sendResponse,
boost::asio::ip::tcp::endpoint const& remoteEndpoint);
void
processSession(
std::shared_ptr<Session> const&,
+140
View File
@@ -0,0 +1,140 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_RPC_UDPINFOSUB_H
#define RIPPLE_RPC_UDPINFOSUB_H
#include <ripple/beast/net/IPAddressConversion.h>
#include <ripple/json/json_writer.h>
#include <ripple/json/to_string.h>
#include <ripple/net/InfoSub.h>
#include <ripple/rpc/Role.h>
#include <ripple/server/WSSession.h>
#include <boost/utility/string_view.hpp>
#include <memory>
#include <string>
namespace ripple {
class UDPInfoSub : public InfoSub
{
std::function<void(std::string const&)> send_;
boost::asio::ip::tcp::endpoint endpoint_;
UDPInfoSub(
Source& source,
std::function<void(std::string const&)>& sendResponse,
boost::asio::ip::tcp::endpoint const& remoteEndpoint)
: InfoSub(source), send_(sendResponse), endpoint_(remoteEndpoint)
{
}
struct RefCountedSub
{
std::shared_ptr<UDPInfoSub> sub;
size_t refCount;
RefCountedSub(std::shared_ptr<UDPInfoSub> s)
: sub(std::move(s)), refCount(1)
{
}
};
static inline std::mutex mtx_;
static inline std::map<boost::asio::ip::tcp::endpoint, RefCountedSub> map_;
public:
static std::shared_ptr<UDPInfoSub>
getInfoSub(
Source& source,
std::function<void(std::string const&)>& sendResponse,
boost::asio::ip::tcp::endpoint const& remoteEndpoint)
{
std::lock_guard<std::mutex> lock(mtx_);
auto it = map_.find(remoteEndpoint);
if (it != map_.end())
{
it->second.refCount++;
return it->second.sub;
}
auto sub = std::shared_ptr<UDPInfoSub>(
new UDPInfoSub(source, sendResponse, remoteEndpoint));
map_.emplace(remoteEndpoint, RefCountedSub(sub));
return sub;
}
static bool
increment(boost::asio::ip::tcp::endpoint const& remoteEndpoint)
{
std::lock_guard<std::mutex> lock(mtx_);
auto it = map_.find(remoteEndpoint);
if (it != map_.end())
{
it->second.refCount++;
return true;
}
return false;
}
bool
increment()
{
return increment(endpoint_);
}
static bool
destroy(boost::asio::ip::tcp::endpoint const& remoteEndpoint)
{
std::lock_guard<std::mutex> lock(mtx_);
auto it = map_.find(remoteEndpoint);
if (it != map_.end())
{
if (--it->second.refCount == 0)
{
map_.erase(it);
return true;
}
}
return false;
}
bool
destroy()
{
return destroy(endpoint_);
}
void
send(Json::Value const& jv, bool) override
{
std::string const str = to_string(jv);
send_(str);
}
boost::asio::ip::tcp::endpoint const&
endpoint() const
{
return endpoint_;
}
};
} // namespace ripple
#endif
+9
View File
@@ -86,6 +86,15 @@ struct Port
// Returns a string containing the list of protocols
std::string
protocols() const;
bool
has_udp() const
{
return protocol.count("udp") > 0;
}
// Maximum UDP packet size (default 64KB)
std::size_t udp_packet_size = 65536;
};
std::ostream&
+7
View File
@@ -244,6 +244,13 @@ parse_Port(ParsedPort& port, Section const& section, std::ostream& log)
optResult->begin(), optResult->end()))
port.protocol.insert(s);
}
if (port.protocol.count("udp") > 0 && port.protocol.size() > 1)
{
log << "Port " << section.name()
<< " cannot mix UDP with other protocols";
Throw<std::exception>();
}
}
{
+23 -5
View File
@@ -24,6 +24,7 @@
#include <ripple/beast/core/List.h>
#include <ripple/server/Server.h>
#include <ripple/server/impl/Door.h>
#include <ripple/server/impl/UDPDoor.h>
#include <ripple/server/impl/io_list.h>
#include <boost/asio.hpp>
#include <array>
@@ -162,18 +163,35 @@ ServerImpl<Handler>::ports(std::vector<Port> const& ports)
{
if (closed())
Throw<std::logic_error>("ports() on closed Server");
ports_.reserve(ports.size());
Endpoints eps;
eps.reserve(ports.size());
for (auto const& port : ports)
{
ports_.push_back(port);
if (auto sp = ios_.emplace<Door<Handler>>(
handler_, io_service_, ports_.back(), j_))
if (port.has_udp())
{
list_.push_back(sp);
eps.push_back(sp->get_endpoint());
sp->run();
// UDP-RPC door
if (auto sp = ios_.emplace<UDPDoor<Handler>>(
handler_, io_service_, ports_.back(), j_))
{
eps.push_back(sp->get_endpoint());
sp->run();
}
}
else
{
// Standard TCP door
if (auto sp = ios_.emplace<Door<Handler>>(
handler_, io_service_, ports_.back(), j_))
{
list_.push_back(sp);
eps.push_back(sp->get_endpoint());
sp->run();
}
}
}
return eps;
+284
View File
@@ -0,0 +1,284 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_SERVER_UDPDOOR_H_INCLUDED
#define RIPPLE_SERVER_UDPDOOR_H_INCLUDED
#include <ripple/basics/Log.h>
#include <ripple/basics/contract.h>
#include <ripple/server/impl/PlainHTTPPeer.h>
#include <ripple/server/impl/SSLHTTPPeer.h>
#include <ripple/server/impl/io_list.h>
#include <boost/asio/basic_waitable_timer.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/beast/core/detect_ssl.hpp>
#include <boost/beast/core/multi_buffer.hpp>
#include <boost/beast/core/tcp_stream.hpp>
#include <boost/container/flat_map.hpp>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
namespace ripple {
template <class Handler>
class UDPDoor : public io_list::work,
public std::enable_shared_from_this<UDPDoor<Handler>>
{
private:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using udp_socket = boost::asio::ip::udp::socket;
beast::Journal const j_;
Port const& port_;
Handler& handler_;
boost::asio::io_context& ioc_;
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
udp_socket socket_;
std::vector<char> recv_buffer_;
endpoint_type local_endpoint_; // Store TCP-style endpoint
public:
UDPDoor(
Handler& handler,
boost::asio::io_context& io_context,
Port const& port,
beast::Journal j)
: j_(j)
, port_(port)
, handler_(handler)
, ioc_(io_context)
, strand_(io_context.get_executor())
, socket_(io_context)
, recv_buffer_(port.udp_packet_size)
, local_endpoint_(port.ip, port.port) // Store as TCP endpoint
{
error_code ec;
// Create UDP endpoint from port configuration
auto const addr = port_.ip.to_v4();
boost::asio::ip::udp::endpoint udp_endpoint(addr, port_.port);
socket_.open(boost::asio::ip::udp::v4(), ec);
if (ec)
{
JLOG(j_.error()) << "UDP socket open failed: " << ec.message();
return;
}
// Set socket options
socket_.set_option(boost::asio::socket_base::reuse_address(true), ec);
if (ec)
{
JLOG(j_.error())
<< "UDP set reuse_address failed: " << ec.message();
return;
}
socket_.bind(udp_endpoint, ec);
if (ec)
{
JLOG(j_.error()) << "UDP socket bind failed: " << ec.message();
return;
}
JLOG(j_.info()) << "UDP-RPC listening on " << udp_endpoint;
}
endpoint_type
get_endpoint() const
{
return local_endpoint_;
}
void
run()
{
if (!socket_.is_open())
return;
do_receive();
}
void
close() override
{
error_code ec;
socket_.close(ec);
}
private:
void
do_receive()
{
if (!socket_.is_open())
return;
socket_.async_receive_from(
boost::asio::buffer(recv_buffer_),
sender_endpoint_,
boost::asio::bind_executor(
strand_,
std::bind(
&UDPDoor::on_receive,
this->shared_from_this(),
std::placeholders::_1,
std::placeholders::_2)));
}
void
on_receive(error_code ec, std::size_t bytes_transferred)
{
if (ec)
{
if (ec != boost::asio::error::operation_aborted)
{
JLOG(j_.error()) << "UDP receive failed: " << ec.message();
do_receive();
}
return;
}
// Convert UDP endpoint to TCP endpoint for compatibility
endpoint_type tcp_endpoint(
sender_endpoint_.address(), sender_endpoint_.port());
// Handle the received UDP message
handler_.onUDPMessage(
std::string(recv_buffer_.data(), bytes_transferred),
tcp_endpoint,
[this, tcp_endpoint](std::string const& response) {
do_send(response, tcp_endpoint);
});
do_receive();
}
void
do_send(std::string const& response, endpoint_type const& tcp_endpoint)
{
if (!socket_.is_open())
{
std::cout << "UDP SOCKET NOT OPEN WHEN SENDING\n\n";
return;
}
const size_t HEADER_SIZE = 16;
const size_t MAX_DATAGRAM_SIZE =
65487; // Allow for ipv6 header 40 bytes + 8 bytes of udp header
const size_t MAX_PAYLOAD_SIZE = MAX_DATAGRAM_SIZE - HEADER_SIZE;
// Convert TCP endpoint back to UDP for sending
boost::asio::ip::udp::endpoint udp_endpoint(
tcp_endpoint.address(), tcp_endpoint.port());
// If message fits in single datagram, send normally
if (response.length() <= MAX_DATAGRAM_SIZE)
{
socket_.async_send_to(
boost::asio::buffer(response),
udp_endpoint,
boost::asio::bind_executor(
strand_,
[this, self = this->shared_from_this()](
error_code ec, std::size_t bytes_transferred) {
if (ec && ec != boost::asio::error::operation_aborted)
{
JLOG(j_.error())
<< "UDP send failed: " << ec.message();
}
}));
return;
}
// Calculate number of packets needed
const size_t payload_size = MAX_PAYLOAD_SIZE;
const uint16_t total_packets =
(response.length() + payload_size - 1) / payload_size;
// Get current timestamp in microseconds
auto now = std::chrono::system_clock::now();
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(
now.time_since_epoch())
.count();
uint64_t timestamp = static_cast<uint64_t>(micros);
// Send fragmented packets
for (uint16_t packet_num = 0; packet_num < total_packets; packet_num++)
{
std::string fragment;
fragment.reserve(MAX_DATAGRAM_SIZE);
// Add header - 4 bytes of zeros
fragment.push_back(0);
fragment.push_back(0);
fragment.push_back(0);
fragment.push_back(0);
// Add packet number (little endian)
fragment.push_back(packet_num & 0xFF);
fragment.push_back((packet_num >> 8) & 0xFF);
// Add total packets (little endian)
fragment.push_back(total_packets & 0xFF);
fragment.push_back((total_packets >> 8) & 0xFF);
// Add timestamp (8 bytes, little endian)
fragment.push_back(timestamp & 0xFF);
fragment.push_back((timestamp >> 8) & 0xFF);
fragment.push_back((timestamp >> 16) & 0xFF);
fragment.push_back((timestamp >> 24) & 0xFF);
fragment.push_back((timestamp >> 32) & 0xFF);
fragment.push_back((timestamp >> 40) & 0xFF);
fragment.push_back((timestamp >> 48) & 0xFF);
fragment.push_back((timestamp >> 56) & 0xFF);
// Calculate payload slice
size_t start = packet_num * payload_size;
size_t length = std::min(payload_size, response.length() - start);
fragment.append(response.substr(start, length));
socket_.async_send_to(
boost::asio::buffer(fragment),
udp_endpoint,
boost::asio::bind_executor(
strand_,
[this, self = this->shared_from_this()](
error_code ec, std::size_t bytes_transferred) {
if (ec && ec != boost::asio::error::operation_aborted)
{
JLOG(j_.error())
<< "UDP send failed: " << ec.message();
}
}));
}
}
boost::asio::ip::udp::endpoint sender_endpoint_;
};
} // namespace ripple
#endif
+21 -13
View File
@@ -120,14 +120,18 @@ public:
static inline constexpr unsigned int leafDepth = 64;
using DeltaItem = std::pair<
std::shared_ptr<SHAMapItem const>,
std::shared_ptr<SHAMapItem const>>;
boost::intrusive_ptr<SHAMapItem const>,
boost::intrusive_ptr<SHAMapItem const>>;
using Delta = std::map<uint256, DeltaItem>;
SHAMap() = delete;
SHAMap(SHAMap const&) = delete;
SHAMap&
operator=(SHAMap const&) = delete;
// Take a snapshot of the given map:
SHAMap(SHAMap const& other, bool isMutable);
// build new map
SHAMap(SHAMapType t, Family& f);
@@ -190,23 +194,27 @@ public:
delItem(uint256 const& id);
bool
addItem(SHAMapNodeType type, SHAMapItem&& i);
addItem(SHAMapNodeType type, boost::intrusive_ptr<SHAMapItem const> item);
SHAMapHash
getHash() const;
// save a copy if you have a temporary anyway
bool
updateGiveItem(SHAMapNodeType type, std::shared_ptr<SHAMapItem const>);
updateGiveItem(
SHAMapNodeType type,
boost::intrusive_ptr<SHAMapItem const> item);
bool
addGiveItem(SHAMapNodeType type, std::shared_ptr<SHAMapItem const> item);
addGiveItem(
SHAMapNodeType type,
boost::intrusive_ptr<SHAMapItem const> item);
// Save a copy if you need to extend the life
// of the SHAMapItem beyond this SHAMap
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
peekItem(uint256 const& id) const;
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
peekItem(uint256 const& id, SHAMapHash& hash) const;
// traverse functions
@@ -253,8 +261,8 @@ public:
*/
void
visitLeaves(
std::function<void(std::shared_ptr<SHAMapItem const> const&)> const&)
const;
std::function<
void(boost::intrusive_ptr<SHAMapItem const> const&)> const&) const;
// comparison/sync functions
@@ -361,8 +369,8 @@ private:
using SharedPtrNodeStack =
std::stack<std::pair<std::shared_ptr<SHAMapTreeNode>, SHAMapNodeID>>;
using DeltaRef = std::pair<
std::shared_ptr<SHAMapItem const> const&,
std::shared_ptr<SHAMapItem const> const&>;
boost::intrusive_ptr<SHAMapItem const>,
boost::intrusive_ptr<SHAMapItem const>>;
// tree node cache operations
std::shared_ptr<SHAMapTreeNode>
@@ -475,7 +483,7 @@ private:
descendNoStore(std::shared_ptr<SHAMapInnerNode> const&, int branch) const;
/** If there is only one leaf below this node, get its contents */
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
onlyBelow(SHAMapTreeNode*) const;
bool
@@ -490,7 +498,7 @@ private:
bool
walkBranch(
SHAMapTreeNode* node,
std::shared_ptr<SHAMapItem const> const& otherMapItem,
boost::intrusive_ptr<SHAMapItem const> const& otherMapItem,
bool isFirstMap,
Delta& differences,
int& maxCount) const;
@@ -36,7 +36,7 @@ class SHAMapAccountStateLeafNode final
{
public:
SHAMapAccountStateLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid)
: SHAMapLeafNode(std::move(item), cowid)
{
@@ -44,7 +44,7 @@ public:
}
SHAMapAccountStateLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid,
SHAMapHash const& hash)
: SHAMapLeafNode(std::move(item), cowid, hash)
+132 -14
View File
@@ -20,26 +20,68 @@
#ifndef RIPPLE_SHAMAP_SHAMAPITEM_H_INCLUDED
#define RIPPLE_SHAMAP_SHAMAPITEM_H_INCLUDED
#include <ripple/basics/Buffer.h>
#include <ripple/basics/ByteUtilities.h>
#include <ripple/basics/CountedObject.h>
#include <ripple/basics/SlabAllocator.h>
#include <ripple/basics/Slice.h>
#include <ripple/basics/base_uint.h>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <cassert>
namespace ripple {
// an item stored in a SHAMap
class SHAMapItem : public CountedObject<SHAMapItem>
{
// These are used to support boost::intrusive_ptr reference counting
// These functions are used internally by boost::intrusive_ptr to handle
// lifetime management.
friend void
intrusive_ptr_add_ref(SHAMapItem const* x);
friend void
intrusive_ptr_release(SHAMapItem const* x);
// This is the interface for creating new instances of this class.
friend boost::intrusive_ptr<SHAMapItem>
make_shamapitem(uint256 const& tag, Slice data);
private:
uint256 tag_;
Buffer data_;
uint256 const tag_;
// We use std::uint32_t to minimize the size; there's no SHAMapItem whose
// size exceeds 4GB and there won't ever be (famous last words?), so this
// is safe.
std::uint32_t const size_;
// This is the reference count used to support boost::intrusive_ptr
mutable std::atomic<std::uint32_t> refcount_ = 1;
// Because of the unusual way in which SHAMapItem objects are constructed
// the only way to properly create one is to first allocate enough memory
// so we limit this constructor to codepaths that do this right and limit
// arbitrary construction.
SHAMapItem(uint256 const& tag, Slice data)
: tag_(tag), size_(static_cast<std::uint32_t>(data.size()))
{
std::memcpy(
reinterpret_cast<std::uint8_t*>(this) + sizeof(*this),
data.data(),
data.size());
}
public:
SHAMapItem() = delete;
SHAMapItem(uint256 const& tag, Slice data) : tag_(tag), data_(data)
{
}
SHAMapItem(SHAMapItem const& other) = delete;
SHAMapItem&
operator=(SHAMapItem const& other) = delete;
SHAMapItem(SHAMapItem&& other) = delete;
SHAMapItem&
operator=(SHAMapItem&&) = delete;
uint256 const&
key() const
@@ -47,25 +89,101 @@ public:
return tag_;
}
Slice
slice() const
{
return static_cast<Slice>(data_);
}
std::size_t
size() const
{
return data_.size();
return size_;
}
void const*
data() const
{
return data_.data();
return reinterpret_cast<std::uint8_t const*>(this) + sizeof(*this);
}
Slice
slice() const
{
return {data(), size()};
}
};
namespace detail {
// clang-format off
// The slab cutoffs and the number of megabytes per allocation are customized
// based on the number of objects of each size we expect to need at any point
// in time and with an eye to minimize the number of slack bytes in a block.
inline SlabAllocatorSet<SHAMapItem> slabber({
{ 128, megabytes(std::size_t(60)) },
{ 192, megabytes(std::size_t(46)) },
{ 272, megabytes(std::size_t(60)) },
{ 384, megabytes(std::size_t(56)) },
{ 564, megabytes(std::size_t(40)) },
{ 772, megabytes(std::size_t(46)) },
{ 1052, megabytes(std::size_t(60)) },
});
// clang-format on
} // namespace detail
inline void
intrusive_ptr_add_ref(SHAMapItem const* x)
{
// This can only happen if someone releases the last reference to the
// item while we were trying to increment the refcount.
if (x->refcount_++ == 0)
LogicError("SHAMapItem: the reference count is 0!");
}
inline void
intrusive_ptr_release(SHAMapItem const* x)
{
if (--x->refcount_ == 0)
{
auto p = reinterpret_cast<std::uint8_t const*>(x);
// The SHAMapItem constuctor isn't trivial (because the destructor
// for CountedObject isn't) so we can't avoid calling it here, but
// plan for a future where we might not need to.
if constexpr (!std::is_trivially_destructible_v<SHAMapItem>)
std::destroy_at(x);
// If the slabber doens't claim this pointer, it was allocated
// manually, so we free it manually.
if (!detail::slabber.deallocate(const_cast<std::uint8_t*>(p)))
delete[] p;
}
}
inline boost::intrusive_ptr<SHAMapItem>
make_shamapitem(uint256 const& tag, Slice data)
{
assert(data.size() <= megabytes<std::size_t>(16));
std::uint8_t* raw = detail::slabber.allocate(data.size());
// If we can't grab memory from the slab allocators, we fall back to
// the standard library and try to grab a precisely-sized memory block:
if (raw == nullptr)
raw = new std::uint8_t[sizeof(SHAMapItem) + data.size()];
// We do not increment the reference count here on purpose: the
// constructor of SHAMapItem explicitly sets it to 1. We use the fact
// that the refcount can never be zero before incrementing as an
// invariant.
return {new (raw) SHAMapItem{tag, data}, false};
}
static_assert(alignof(SHAMapItem) != 40);
static_assert(alignof(SHAMapItem) == 8 || alignof(SHAMapItem) == 4);
inline boost::intrusive_ptr<SHAMapItem>
make_shamapitem(SHAMapItem const& other)
{
return make_shamapitem(other.key(), other.slice());
}
} // namespace ripple
#endif
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff. Show more