mirror of
https://github.com/Xahau/xahaud.git
synced 2026-10-27 22:27:09 +00:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c939b656f3 | ||
|
|
fa71bda29c | ||
|
|
412593d7bc | ||
|
|
12d8342c34 | ||
|
|
d17f7151ab | ||
|
|
4466175231 | ||
|
|
621ca9c865 | ||
|
|
85a752235a | ||
|
|
d878fd4a6e | ||
|
|
532a471a35 | ||
|
|
e9468d8b4a | ||
|
|
9d54da3880 | ||
|
|
542172f0a1 | ||
|
|
e086724772 | ||
|
|
21863b05f3 | ||
|
+3 |
61ac04aacc |
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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 ../ &&
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {};
|
||||
{
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -25,7 +25,9 @@
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/utility/string_view.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
#ifndef RIPPLE_BASICS_BASE64_H_INCLUDED
|
||||
#define RIPPLE_BASICS_BASE64_H_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -184,6 +184,7 @@ enum TEFcodes : TERUnderlyingType {
|
||||
tefPAST_IMPORT_SEQ,
|
||||
tefPAST_IMPORT_VL_SEQ,
|
||||
tefNONDIR_EMIT,
|
||||
tefIMPORT_BLACKHOLED,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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."),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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&,
|
||||
|
||||
@@ -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
|
||||
@@ -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&
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
Reference in new issue
Block a user