mirror of
https://github.com/Xahau/xahaud.git
synced 2026-01-17 21:25:16 +00:00
Compare commits
6 Commits
feature-em
...
fixPageCap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff82ea1305 | ||
|
|
f2f9983590 | ||
|
|
faebfce3ed | ||
|
|
833df20fce | ||
|
|
5737c2b6e8 | ||
|
|
a15d0b2ecc |
@@ -9,6 +9,15 @@ echo "-- GITHUB_RUN_NUMBER: $4"
|
||||
|
||||
umask 0000;
|
||||
|
||||
echo "Fixing CentOS 7 EOL"
|
||||
|
||||
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
|
||||
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
|
||||
yum clean all
|
||||
yum-config-manager --disable centos-sclo-sclo
|
||||
|
||||
####
|
||||
|
||||
cd /io;
|
||||
mkdir src/certs;
|
||||
curl --silent -k https://raw.githubusercontent.com/RichardAH/rippled-release-builder/main/ca-bundle/certbundle.h -o src/certs/certbundle.h;
|
||||
|
||||
@@ -1142,8 +1142,12 @@ NetworkOPsImp::submitTransaction(std::shared_ptr<STTx const> const& iTrans)
|
||||
// Enforce Network bar for emitted txn
|
||||
if (view->rules().enabled(featureHooks) && hook::isEmittedTxn(*iTrans))
|
||||
{
|
||||
JLOG(m_journal.warn())
|
||||
<< "Submitted transaction invalid: EmitDetails present.";
|
||||
// RH NOTE: Warning removed here due to ConsesusSet using this function
|
||||
// which continually triggers this bar. Doesn't seem dangerous, just
|
||||
// annoying.
|
||||
|
||||
// JLOG(m_journal.warn())
|
||||
// << "Submitted transaction invalid: EmitDetails present.";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1155,7 +1159,11 @@ NetworkOPsImp::submitTransaction(std::shared_ptr<STTx const> const& iTrans)
|
||||
|
||||
if ((flags & SF_BAD) != 0)
|
||||
{
|
||||
JLOG(m_journal.warn()) << "Submitted transaction cached bad";
|
||||
// RH NOTE: Warning removed here due to ConsesusSet using this function
|
||||
// which continually triggers this bar. Doesn't seem dangerous, just
|
||||
// annoying.
|
||||
|
||||
// JLOG(m_journal.warn()) << "Submitted transaction cached bad";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -173,6 +173,11 @@ updateLedgerDBs(
|
||||
|
||||
auto const sParentHash{to_string(ledger->info().parentHash)};
|
||||
auto const sDrops{to_string(ledger->info().drops)};
|
||||
auto const closingTime{
|
||||
ledger->info().closeTime.time_since_epoch().count()};
|
||||
auto const prevClosingTime{
|
||||
ledger->info().parentCloseTime.time_since_epoch().count()};
|
||||
auto const closeTimeRes{ledger->info().closeTimeResolution.count()};
|
||||
auto const sAccountHash{to_string(ledger->info().accountHash)};
|
||||
auto const sTxHash{to_string(ledger->info().txHash)};
|
||||
|
||||
@@ -188,11 +193,8 @@ updateLedgerDBs(
|
||||
":closingTime, :prevClosingTime, :closeTimeRes,"
|
||||
":closeFlags, :accountSetHash, :transSetHash);",
|
||||
soci::use(sHash), soci::use(ledgerSeq), soci::use(sParentHash),
|
||||
soci::use(sDrops),
|
||||
soci::use(ledger->info().closeTime.time_since_epoch().count()),
|
||||
soci::use(
|
||||
ledger->info().parentCloseTime.time_since_epoch().count()),
|
||||
soci::use(ledger->info().closeTimeResolution.count()),
|
||||
soci::use(sDrops), soci::use(closingTime),
|
||||
soci::use(prevClosingTime), soci::use(closeTimeRes),
|
||||
soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
|
||||
soci::use(sTxHash);
|
||||
|
||||
|
||||
@@ -205,19 +205,20 @@ insertPeerReservation(
|
||||
PublicKey const& nodeId,
|
||||
std::string const& description)
|
||||
{
|
||||
auto const sNodeId = toBase58(TokenType::NodePublic, nodeId);
|
||||
session << "INSERT INTO PeerReservations (PublicKey, Description) "
|
||||
"VALUES (:nodeId, :desc) "
|
||||
"ON CONFLICT (PublicKey) DO UPDATE SET "
|
||||
"Description=excluded.Description",
|
||||
soci::use(toBase58(TokenType::NodePublic, nodeId)),
|
||||
soci::use(description);
|
||||
soci::use(sNodeId), soci::use(description);
|
||||
}
|
||||
|
||||
void
|
||||
deletePeerReservation(soci::session& session, PublicKey const& nodeId)
|
||||
{
|
||||
auto const sNodeId = toBase58(TokenType::NodePublic, nodeId);
|
||||
session << "DELETE FROM PeerReservations WHERE PublicKey = :nodeId",
|
||||
soci::use(toBase58(TokenType::NodePublic, nodeId));
|
||||
soci::use(sNodeId);
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -1921,6 +1921,12 @@ Transactor::operator()()
|
||||
STObject const meta = metaRaw.getAsObject();
|
||||
|
||||
uint32_t lgrCur = view().seq();
|
||||
|
||||
bool const has240819 = view().rules().enabled(fix240819);
|
||||
|
||||
auto const& sfRewardFields =
|
||||
*(ripple::SField::knownCodeToField.at(917511 - has240819));
|
||||
|
||||
// iterate all affected balances
|
||||
for (auto const& node : meta.getFieldArray(sfAffectedNodes))
|
||||
{
|
||||
@@ -1932,7 +1938,7 @@ Transactor::operator()()
|
||||
if (nodeType != ltACCOUNT_ROOT || metaType == sfDeletedNode)
|
||||
continue;
|
||||
|
||||
if (!node.isFieldPresent(sfFinalFields) ||
|
||||
if (!node.isFieldPresent(sfRewardFields) ||
|
||||
!node.isFieldPresent(sfLedgerIndex))
|
||||
continue;
|
||||
|
||||
@@ -1948,7 +1954,7 @@ Transactor::operator()()
|
||||
continue;
|
||||
|
||||
STObject& finalFields = (const_cast<STObject&>(node))
|
||||
.getField(sfFinalFields)
|
||||
.getField(sfRewardFields)
|
||||
.downcast<STObject>();
|
||||
|
||||
if (!finalFields.isFieldPresent(sfBalance))
|
||||
|
||||
@@ -127,7 +127,7 @@ sizedItems
|
||||
{SizedItem::txnDBCache, {{ 4, 12, 24, 64, 128 }}},
|
||||
{SizedItem::lgrDBCache, {{ 4, 8, 16, 32, 128 }}},
|
||||
{SizedItem::openFinalLimit, {{ 8, 16, 32, 64, 128 }}},
|
||||
{SizedItem::burstSize, {{ 4, 8, 16, 32, 48 }}},
|
||||
{SizedItem::burstSize, {{ 4, 8, 16, 32, 64*1024*1024 }}},
|
||||
{SizedItem::ramSizeGB, {{ 8, 12, 16, 24, 32 }}},
|
||||
{SizedItem::accountIdCacheSize, {{ 20047, 50053, 77081, 150061, 300007 }}}
|
||||
}};
|
||||
|
||||
@@ -91,8 +91,10 @@ ApplyView::dirAdd(
|
||||
return page;
|
||||
}
|
||||
|
||||
bool const capped = !rules().enabled(fixPageCap);
|
||||
|
||||
// Check whether we're out of pages.
|
||||
if (++page >= dirNodeMaxPages)
|
||||
if (++page >= dirNodeMaxPages && capped)
|
||||
return std::nullopt;
|
||||
|
||||
// We are about to create a new node; we'll link it to
|
||||
|
||||
@@ -1,493 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
// make this typedef to keep dkim happy
|
||||
typedef int _Bool;
|
||||
#include <opendkim/dkim.h>
|
||||
|
||||
using namespace ripple;
|
||||
namespace Email
|
||||
{
|
||||
|
||||
enum EmailType : uint8_t
|
||||
{
|
||||
INVALID = 0,
|
||||
REMIT = 1,
|
||||
REKEY = 2
|
||||
};
|
||||
|
||||
struct EmailDetails
|
||||
{
|
||||
std::string domain; // from address domain
|
||||
std::string dkimDomain; // dkim signature domain
|
||||
|
||||
AccountID from;
|
||||
std::string fromEmail;
|
||||
|
||||
std::optional<std::string> toEmail;
|
||||
std::optional<AccountID> to;
|
||||
|
||||
EmailType emailType { EmailType::INVALID };
|
||||
std::optional<STAmount> amount; // only valid if REMIT type
|
||||
std::optional<AccountID> rekey; // only valid if REKEY type
|
||||
};
|
||||
|
||||
class OpenDKIM
|
||||
{
|
||||
private:
|
||||
DKIM_STAT status;
|
||||
public:
|
||||
DKIM_LIB* dkim_lib;
|
||||
DKIM* dkim;
|
||||
|
||||
bool sane()
|
||||
{
|
||||
return !!dkim_lib && !!dkim;
|
||||
}
|
||||
|
||||
OpenDKIM()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// setup is in its own function not the constructor to make failure graceful
|
||||
bool setup(beast::Journal& j)
|
||||
{
|
||||
dkim_lib = dkim_init(nullptr, nullptr);
|
||||
if (!dkim_lib)
|
||||
{
|
||||
JLOG(j.warn()) << "EmailAmendment: Failed to init dkim_lib.";
|
||||
return false;
|
||||
}
|
||||
|
||||
DKIM_STAT status;
|
||||
DKIM* dkim = dkim_verify(dkim_lib, (uint8_t const*)"id", nullptr, &status);
|
||||
if (!dkim_lib)
|
||||
{
|
||||
JLOG(j.warn()) << "EmailAmendment: Failed to init dkim_verify.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
~OpenDKIM()
|
||||
{
|
||||
if (dkim)
|
||||
{
|
||||
dkim_free(dkim);
|
||||
dkim = nullptr;
|
||||
}
|
||||
|
||||
if (dkim_lib)
|
||||
{
|
||||
dkim_close(dkim_lib);
|
||||
dkim_lib = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
std::optional<std::pair<std::string /* canonical email addr */, std::string /* canonical domain */>>
|
||||
canonicalizeEmailAddress(const std::string& rawEmailAddr)
|
||||
{
|
||||
if (rawEmailAddr.empty())
|
||||
return {};
|
||||
|
||||
// trim
|
||||
auto start = std::find_if_not(str.begin(), str.end(), ::isspace);
|
||||
auto end = std::find_if_not(str.rbegin(), str.rend(), ::isspace).base();
|
||||
|
||||
if (end >= start)
|
||||
return {};
|
||||
|
||||
std::email = std::string(start, end);
|
||||
|
||||
if (email.empty())
|
||||
return {};
|
||||
|
||||
// to lower
|
||||
std::transform(email.begin(), email.end(), email.begin(), ::tolower);
|
||||
|
||||
// find the @
|
||||
size_t atPos = email.find('@');
|
||||
if (atPos == std::string::npos || atPos == email.size() - 1)
|
||||
return {};
|
||||
|
||||
std::string localPart = email.substr(0, atPos);
|
||||
std::string domain = email.substr(atPos + 1);
|
||||
|
||||
if (domain.empty() || localPart.empty())
|
||||
return {};
|
||||
|
||||
// ensure there's only one @
|
||||
if (domain.find('@') != std::string::npos)
|
||||
return {};
|
||||
|
||||
// canonicalize domain part
|
||||
{
|
||||
std::string result = domain;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||
while (!result.empty() && result.back() == '.')
|
||||
result.pop_back();
|
||||
|
||||
doamin = result;
|
||||
}
|
||||
|
||||
if (domain.empty())
|
||||
return {};
|
||||
|
||||
// canonicalize local part
|
||||
{
|
||||
std::string part = localPart;
|
||||
part.erase(std::remove_if(
|
||||
part.begin(), part.end(),
|
||||
[](char c) { return c == '(' || c == ')' || std::isspace(c); }), part.end());
|
||||
|
||||
size_t plusPos = part.find('+');
|
||||
if (plusPos != std::string::npos)
|
||||
part = part.substr(0, plusPos);
|
||||
|
||||
while (!part.empty() && part.back() == '.')
|
||||
part.pop_back();
|
||||
|
||||
// gmail ignores dots
|
||||
if (domain == "gmail.com")
|
||||
part.erase(std::remove(part.begin(), part.end(), '.'), part.end());
|
||||
|
||||
localPart = part;
|
||||
}
|
||||
|
||||
if (localPart.empty())
|
||||
return {};
|
||||
|
||||
return {{localPart + "@" + domain, domain}};
|
||||
};
|
||||
|
||||
// Warning: must supply already canonicalzied email
|
||||
inline
|
||||
std::optional<AccountID>
|
||||
emailToAccountID(const std::string& canonicalEmail)
|
||||
{
|
||||
uint8_t innerHash[SHA512_DIGEST_LENGTH + 4];
|
||||
SHA512_CTX sha512;
|
||||
SHA512_Init(&sha512);
|
||||
SHA512_Update(&sha512, canonicalEmail.c_str(), canonicalEmail.size());
|
||||
SHA512_Final(innerHash + 4, &sha512);
|
||||
innerHash[0] = 0xEEU;
|
||||
innerHash[1] = 0xEEU;
|
||||
innerHash[2] = 0xFFU;
|
||||
innerHash[3] = 0xFFU;
|
||||
{
|
||||
uint8_t hash[SHA512_DIGEST_LENGTH];
|
||||
SHA512_CTX sha512;
|
||||
SHA512_Init(&sha512);
|
||||
SHA512_Update(&sha512, innerHash, sizeof(innerHash));
|
||||
SHA512_Final(hash, &sha512);
|
||||
|
||||
return AccountID::fromVoid((void*)hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
std::optional<EmailDetails>
|
||||
parseEmail(std::string const& rawEmail, beast::Journal& j)
|
||||
{
|
||||
EmailDetails out;
|
||||
|
||||
// parse email into headers and body
|
||||
std::vector<std::string> headers;
|
||||
std::string body;
|
||||
{
|
||||
std::istringstream stream(rawEmail);
|
||||
std::string line;
|
||||
|
||||
while (std::getline(stream, line))
|
||||
{
|
||||
if (line.empty() || line == "\r")
|
||||
break;
|
||||
|
||||
// Handle header line continuations
|
||||
while (stream.peek() == ' ' || stream.peek() == '\t') {
|
||||
std::string continuation;
|
||||
std::getline(stream, continuation);
|
||||
line += '\n' + continuation;
|
||||
}
|
||||
if (!line.empty()) {
|
||||
headers.push_back(line.substr(0, line.size() - (line.back() == '\r' ? 1 : 0)));
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream body_stream;
|
||||
while (std::getline(stream, line))
|
||||
body_stream << line << "\n";
|
||||
body = body_stream.str();
|
||||
}
|
||||
|
||||
|
||||
// find the from address, canonicalize it and extract the domain
|
||||
bool foundFrom = false;
|
||||
bool foundTo = false;
|
||||
{
|
||||
static const std::regex
|
||||
from_regex(R"(^From:\s*(?:.*<)?([^<>\s]+@[^<>\s]+)(?:>)?)", std::regex::icase);
|
||||
|
||||
static const std::regex
|
||||
to_regex(R"(^To:\s*(?:.*<)?([^<>\s]+@[^<>\s]+)(?:>)?)", std::regex::icase);
|
||||
|
||||
for (const auto& header : headers)
|
||||
{
|
||||
if (foundFrom && foundTo)
|
||||
break;
|
||||
|
||||
std::smatch match;
|
||||
if (!foundFrom && std::regex_search(header, match, from_regex) && match.size() > 1)
|
||||
{
|
||||
auto canon = canonicalizeEmailAddress(match[1].str());
|
||||
if (!canon)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: Cannot parse From address: `"
|
||||
<< match[1].str() << "`";
|
||||
return {};
|
||||
}
|
||||
|
||||
out.fromEmail = canon->first;
|
||||
out.domain = canon->second;
|
||||
out.from = emailToAccountID(out.fromEmail);
|
||||
foundFrom = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::regex_search(header, match, to_regex) && match.size() > 1)
|
||||
{
|
||||
auto canon = canonicalizeEmailAddress(match[1].str());
|
||||
if (!canon)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: Cannot parse To address: `"
|
||||
<< match[1].str() << "`";
|
||||
return {};
|
||||
}
|
||||
|
||||
out.toEmail = canon->first;
|
||||
out.to = emailToAccountID(out.toEmail);
|
||||
|
||||
foundTo = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundFrom)
|
||||
{
|
||||
JLOG(j.warn()) << "EmailAmendment: No From address present in email.";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// execution to here means we have:
|
||||
// 1. Parsed headers and body
|
||||
// 2. Found a from address and canonicalzied it
|
||||
// 3. Potentially found a to address and canonicalized it.
|
||||
|
||||
// Find instructions
|
||||
{
|
||||
static const std::regex
|
||||
remitPattern(R"(^REMIT (\d+(?:\.\d+)?) ([A-Z]{3})(?:/([r][a-zA-Z0-9]{24,34}))?)");
|
||||
|
||||
static const std::regex
|
||||
rekeyPattern(R"(^REKEY ([r][a-zA-Z0-9]{24,34}))");
|
||||
|
||||
std::istringstream stream(body);
|
||||
std::string line;
|
||||
|
||||
out.emailType = EmailType::INVALID;
|
||||
|
||||
while (std::getline(stream, line, '\n'))
|
||||
{
|
||||
if (!line.empty() && line.back() == '\r')
|
||||
line.pop_back(); // Remove '\r' if present
|
||||
|
||||
std::smatch match;
|
||||
if (std::regex_match(line, match, remitPattern))
|
||||
{
|
||||
try
|
||||
{
|
||||
Currency cur;
|
||||
if (!to_currency(cur, match[2]))
|
||||
{
|
||||
JLOG(j.warn()) << "EmailAmendment: Could not parse currency code.";
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
AccountID issuer = noAccount();
|
||||
if (match[3].matched)
|
||||
{
|
||||
if (isXRP(cur))
|
||||
{
|
||||
JLOG(j.warn()) << "EmailAmendment: Native currency cannot specify issuer.";
|
||||
return {};
|
||||
}
|
||||
|
||||
issuer = decodeBase58Token(match[3], TokenType::AccountID);
|
||||
if (issuer.empty())
|
||||
{
|
||||
JLOG(j.warn()) << "EmailAmendment: Could not parse issuer address.";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
out.amount = amountFromString({cur, issuer}, match[1]);
|
||||
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
JLOG(j.warn()) << "EmailAmendment: Exception while parsing REMIT. " << e.what();
|
||||
return {};
|
||||
}
|
||||
|
||||
out.emailType = EmailType::REMIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (std::regex_match(line, match, rekeyPattern))
|
||||
{
|
||||
AccountID rekey = decodeBase58Token(match[1], TokenType::AccountID);
|
||||
if (rekey.empty())
|
||||
{
|
||||
JLOG(j.warn()) << "EmailAmendment: Could not parse rekey address.";
|
||||
return {};
|
||||
}
|
||||
|
||||
out.rekey = rekey;
|
||||
out.emailType = EmailType::REKEY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (out.emailType == EmailType::INVALID)
|
||||
{
|
||||
JLOG(j.warn()) << "EmailAmendment: Invalid email type, could not find REMIT or REKEY.";
|
||||
return{};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// perform DKIM checks...
|
||||
// to do this we will use OpenDKIM, and manage it with a smart pointer to prevent
|
||||
// any leaks from uncommon exit pathways
|
||||
|
||||
std::unique<OpenDKIM> odkim;
|
||||
|
||||
// perform setup
|
||||
if (!odkim->setup(j) || !odkim->sane())
|
||||
return {};
|
||||
|
||||
// when odkim goes out of scope it will call the C-apis to destroy the dkim instances
|
||||
|
||||
DKIM_STAT status;
|
||||
DKIM_LIB* dkim_lib = odkim->dkim_lib;
|
||||
DKIM* dkim = odkim->dkim;
|
||||
|
||||
// feed opendkim all headers
|
||||
{
|
||||
for (const auto& header : headers)
|
||||
{
|
||||
status = dkim_header(dkim, (uint8_t*)header.c_str(), header.length());
|
||||
if (status != DKIM_STAT_OK)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: OpenDKIM Failed to process header: "
|
||||
<< dkim_geterror(dkim);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
status = dkim_eoh(dkim);
|
||||
if (status != DKIM_STAT_OK)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: OpenDKIM Failed to send end-of-headers"l
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// feed opendkim email body
|
||||
{
|
||||
status = dkim_body(dkim, (uint8_t*)body.c_str(), body.size());
|
||||
if (status != DKIM_STAT_OK)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: OpenDKIM Failed to process body: "
|
||||
<< dkim_geterror(dkim);
|
||||
return {};
|
||||
}
|
||||
|
||||
_Bool testkey;
|
||||
status = dkim_eom(dkim, &testkey);
|
||||
if (status != DKIM_STAT_OK)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: OpenDKIM end-of-message error: "
|
||||
<< dkim_geterror(dkim);
|
||||
return {};
|
||||
}
|
||||
|
||||
DKIM_SIGINFO* sig = dkim_getsignature(dkim);
|
||||
if (!sig)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: No DKIM signature found";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (dkim_sig_getbh(sig) != DKIM_SIGBH_MATCH)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: DKIM body hash mismatch";
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
DKIM_SIGINFO* sig = dkim_getsignature(dkim);
|
||||
if (!sig)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: DKIM signature not found.";
|
||||
return {};
|
||||
}
|
||||
|
||||
out.dkimDomain =
|
||||
std::string(reinterpret_cast<char const*>(
|
||||
reinterpret_cast<void const*>(dkim_sig_getdomain(sig))));
|
||||
|
||||
if (out.dkimDomain.empty())
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: DKIM signature domain empty.";
|
||||
return {};
|
||||
}
|
||||
|
||||
// RH TODO: decide whether to relax this or not
|
||||
// strict domain check
|
||||
if (out.dkimDomain != out.domain)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "EmailAmendment: DKIM domain does not match From address domain.";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// execution to here means all checks passed and the instruction was correctly parsed
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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 = 71;
|
||||
static constexpr std::size_t numFeatures = 72;
|
||||
|
||||
/** Amendments that this server supports and the default voting behavior.
|
||||
Whether they are enabled depends on the Rules defined in the validated
|
||||
@@ -358,7 +358,8 @@ extern uint256 const fixXahauV2;
|
||||
extern uint256 const featureRemit;
|
||||
extern uint256 const featureZeroB2M;
|
||||
extern uint256 const fixNSDelete;
|
||||
extern uint256 const featureEmail;
|
||||
extern uint256 const fix240819;
|
||||
extern uint256 const fixPageCap;
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <ripple/protocol/TxFormats.h>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <functional>
|
||||
#include <ripple/protocol/Email.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
|
||||
@@ -57,9 +57,8 @@ namespace ripple {
|
||||
// Universal Transaction flags:
|
||||
enum UniversalFlags : uint32_t {
|
||||
tfFullyCanonicalSig = 0x80000000,
|
||||
tfEmailSig = 0x40000000,
|
||||
};
|
||||
constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfEmailSig;
|
||||
constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig;
|
||||
constexpr std::uint32_t tfUniversalMask = ~tfUniversal;
|
||||
|
||||
// AccountSet flags:
|
||||
|
||||
@@ -464,7 +464,8 @@ REGISTER_FIX (fixXahauV2, Supported::yes, VoteBehavior::De
|
||||
REGISTER_FEATURE(Remit, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(ZeroB2M, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FIX (fixNSDelete, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(Email, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FIX (fix240819, Supported::yes, VoteBehavior::DefaultYes);
|
||||
REGISTER_FIX (fixPageCap, Supported::yes, VoteBehavior::DefaultYes);
|
||||
|
||||
// The following amendments are obsolete, but must remain supported
|
||||
// because they could potentially get enabled.
|
||||
|
||||
@@ -304,60 +304,9 @@ STTx::checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
||||
bool const isWildcardNetwork =
|
||||
isFieldPresent(sfNetworkID) && getFieldU32(sfNetworkID) == 65535;
|
||||
|
||||
// email signature flag signals that the txn is authorized
|
||||
// only by the presence of a DKIM signed email in memos[0]
|
||||
|
||||
bool const isEmailSig =
|
||||
getFlags() & tfEmailSig;
|
||||
|
||||
|
||||
bool validSig = false;
|
||||
do
|
||||
try
|
||||
{
|
||||
|
||||
if (isEmailSig)
|
||||
{
|
||||
|
||||
if (!isFieldPresent(sfMemos))
|
||||
break;
|
||||
|
||||
auto const& memos = st.getFieldArray(sfMemos);
|
||||
auto const& memo = memos[0];
|
||||
auto memoObj = dynamic_cast<STObject const*>(&memo);
|
||||
if (!memoObj || (memoObj->getFName() != sfMemo))
|
||||
break;
|
||||
|
||||
bool emailValid = false;
|
||||
|
||||
for (auto const& memoElement : *memoObj)
|
||||
{
|
||||
auto const& name = memoElement.getFName();
|
||||
|
||||
if (name != sfMemoType && name != sfMemoData &&
|
||||
name != sfMemoFormat)
|
||||
break;
|
||||
|
||||
// The raw data is stored as hex-octets, which we want to decode.
|
||||
std::optional<Blob> optData = strUnHex(memoElement.getText());
|
||||
|
||||
if (!optData)
|
||||
break;
|
||||
|
||||
if (name != sfMemoData)
|
||||
continue;
|
||||
|
||||
std::string const emailContent((char const*)(optData->data()), optData->size());
|
||||
|
||||
|
||||
// RH UPTO
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig) ||
|
||||
(requireCanonicalSig == RequireFullyCanonicalSig::yes);
|
||||
|
||||
@@ -379,8 +328,7 @@ STTx::checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
||||
{
|
||||
// Assume it was a signature failure.
|
||||
validSig = false;
|
||||
} while (0);
|
||||
|
||||
}
|
||||
if (validSig == false)
|
||||
return Unexpected("Invalid signature.");
|
||||
// Signature was verified.
|
||||
|
||||
@@ -256,6 +256,8 @@ BaseWSPeer<Handler, Impl>::close(
|
||||
return post(strand_, [self = impl().shared_from_this(), reason] {
|
||||
self->close(reason);
|
||||
});
|
||||
if (do_close_)
|
||||
return;
|
||||
do_close_ = true;
|
||||
if (wq_.empty())
|
||||
{
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <ripple/app/hook/Enum.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/tx/impl/SetHook.h>
|
||||
#include <ripple/json/json_reader.h>
|
||||
#include <ripple/json/json_writer.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/app/SetHook_wasm.h>
|
||||
@@ -87,6 +89,147 @@ public:
|
||||
// fee unit tests, the rest of the time we want to ignore it.
|
||||
#define HSFEE fee(100'000'000)
|
||||
#define M(m) memo(m, "", "")
|
||||
|
||||
std::unique_ptr<Config>
|
||||
makePageCapConfig(
|
||||
FeatureBitset features,
|
||||
uint32_t networkID,
|
||||
std::string fee,
|
||||
std::string a_res,
|
||||
std::string o_res,
|
||||
uint32_t ledgerID)
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
Json::Value jsonValue;
|
||||
Json::Reader reader;
|
||||
std::string base_genesis = R"json({
|
||||
"ledger": {
|
||||
"accepted": true,
|
||||
"accountState": [
|
||||
{
|
||||
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
||||
"Balance": "100000000000000000",
|
||||
"Flags": 0,
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"OwnerCount": 0,
|
||||
"PreviousTxnID": "A92EF82C3C68F771927E3892A2F708F12CBD492EF68A860F042E4053C8EC6C8D",
|
||||
"PreviousTxnLgrSeq": 0,
|
||||
"Sequence": 1,
|
||||
"index": "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8"
|
||||
},
|
||||
{
|
||||
"Amendments": [],
|
||||
"Flags": 0,
|
||||
"LedgerEntryType": "Amendments",
|
||||
"index": "7DB0788C020F02780A673DC74757F23823FA3014C1866E72CC4CD8B226CD6EF4"
|
||||
},
|
||||
{
|
||||
"BaseFee": "A",
|
||||
"Flags": 0,
|
||||
"LedgerEntryType": "FeeSettings",
|
||||
"ReferenceFeeUnits": 10,
|
||||
"ReserveBase": 1000000,
|
||||
"ReserveIncrement": 200000,
|
||||
"XahauActivationLgrSeq": 0,
|
||||
"index": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A651"
|
||||
},
|
||||
{
|
||||
"Flags": 0,
|
||||
"IndexNext": "40000",
|
||||
"IndexPrevious": "3fffe",
|
||||
"Indexes": [],
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"Owner": "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
|
||||
"RootIndex": "EC1A88838E9ADA27E8ED583917C1530D2F48C3A3C93F50EDAD662D662E9BCC76",
|
||||
"index": "EC1A88838E9ADA27E8ED583917C1530D2F48C3A3C93F50EDAD662D662E9BCC76"
|
||||
},
|
||||
{
|
||||
"Flags": 0,
|
||||
"IndexNext": "3fffe",
|
||||
"IndexPrevious": "3fffd",
|
||||
"Indexes": [],
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"Owner": "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
|
||||
"RootIndex": "EC1A88838E9ADA27E8ED583917C1530D2F48C3A3C93F50EDAD662D662E9BCC76",
|
||||
"index": "4A5F3F9E6762A4F89FFCD385FF2309E1F7D1309321BFEEA61D5C9ACB768DB61B"
|
||||
},
|
||||
{
|
||||
"Flags": 0,
|
||||
"Indexes": [],
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"Owner": "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
|
||||
"RootIndex": "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D",
|
||||
"index": "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D"
|
||||
},
|
||||
{
|
||||
"Account": "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
|
||||
"Balance": "99999899000000",
|
||||
"Flags": 8388608,
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"HookNamespaces": [ "0000000000000000000000000000000000000000000000000000000000000000" ],
|
||||
"HookStateCount": 8388576,
|
||||
"OwnerCount": 8388577,
|
||||
"PreviousTxnID": "A92EF82C3C68F771927E3892A2F708F12CBD492EF68A860F042E4053C8EC6C8D",
|
||||
"PreviousTxnLgrSeq": 0,
|
||||
"Sequence": 3,
|
||||
"index": "92FA6A9FC8EA6018D5D16532D7795C91BFB0831355BDFDA177E86C8BF997985F"
|
||||
}
|
||||
],
|
||||
"account_hash": "5DF3A98772FB73E782B8740E87885C6BAD9BA486422E3626DEF968AD2CB2C514",
|
||||
"close_flags": 0,
|
||||
"close_time": 0,
|
||||
"close_time_human": "2000-Jan-01 00:00:00.000000",
|
||||
"close_time_resolution": 10,
|
||||
"closed": true,
|
||||
"hash": "56DA0940767AC2F17F0E384F04816002403D0756432B9D503DDA20128A2AAF11",
|
||||
"ledger_hash": "56DA0940767AC2F17F0E384F04816002403D0756432B9D503DDA20128A2AAF11",
|
||||
"ledger_index": "0",
|
||||
"parent_close_time": 0,
|
||||
"parent_hash": "56DA0940767AC2F17F0E384F04816002403D0756432B9D503DDA20128A2AAF11",
|
||||
"seqNum": "5",
|
||||
"totalCoins": "100000000000000000",
|
||||
"total_coins": "100000000000000000",
|
||||
"transaction_hash": "9A77D1D1A4B36DA77B9C4DC63FDEB8F821741D157802F9C42A6ED86003D8B4A0",
|
||||
"transactions": []
|
||||
},
|
||||
"ledger_current_index": 0,
|
||||
"status": "success",
|
||||
"validated": true
|
||||
})json";
|
||||
reader.parse(base_genesis, jsonValue);
|
||||
|
||||
foreachFeature(features, [&](uint256 const& feature) {
|
||||
std::string featureName = featureToName(feature);
|
||||
std::optional<uint256> featureHash =
|
||||
getRegisteredFeature(featureName);
|
||||
if (featureHash.has_value())
|
||||
{
|
||||
std::string hashString = to_string(featureHash.value());
|
||||
jsonValue["ledger"]["accountState"][1]["Amendments"].append(
|
||||
hashString);
|
||||
}
|
||||
});
|
||||
|
||||
jsonValue["ledger_current_index"] = ledgerID;
|
||||
jsonValue["ledger"]["ledger_index"] = to_string(ledgerID);
|
||||
jsonValue["ledger"]["seqNum"] = to_string(ledgerID);
|
||||
|
||||
return envconfig([&](std::unique_ptr<Config> cfg) {
|
||||
cfg->NETWORK_ID = networkID;
|
||||
cfg->START_LEDGER = jsonValue.toStyledString();
|
||||
cfg->START_UP = Config::LOAD_JSON;
|
||||
Section config;
|
||||
config.append(
|
||||
{"reference_fee = " + fee,
|
||||
"account_reserve = " + a_res,
|
||||
"owner_reserve = " + o_res});
|
||||
auto setup = setup_FeeVote(config);
|
||||
cfg->FEES = setup;
|
||||
return cfg;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
testHooksOwnerDir(FeatureBitset features)
|
||||
{
|
||||
@@ -928,6 +1071,73 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testPageCap(FeatureBitset features)
|
||||
{
|
||||
testcase("Test page cap");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
test::jtx::Env env{
|
||||
*this,
|
||||
makePageCapConfig(features, 21337, "10", "1000000", "200000", 0),
|
||||
features};
|
||||
|
||||
bool const hasFix = env.current()->rules().enabled(fixPageCap);
|
||||
|
||||
auto const alice = Account{"alice"};
|
||||
env.memoize(alice);
|
||||
|
||||
auto const bob = Account{"bob"};
|
||||
env.fund(XRP(10000000), bob);
|
||||
|
||||
auto const preHookCount = (*env.le(alice))[sfHookStateCount];
|
||||
auto const preOwnerCount = (*env.le(alice))[sfOwnerCount];
|
||||
|
||||
std::string hook =
|
||||
"0061736D01000000012A0660057F7F7F7F7F017E60027F7F017E60027F7F017F60"
|
||||
"047F7F7F7F017E60037F7F7E017E60017F017E02520603656E7605747261636500"
|
||||
"0003656E760C686F6F6B5F6163636F756E74000103656E76025F67000203656E76"
|
||||
"057374617465000303656E760973746174655F736574000303656E760661636365"
|
||||
"70740004030201050503010002062B077F0141B088040B7F004180080B7F0041A2"
|
||||
"080B7F004180080B7F0041B088040B7F0041000B7F0041010B07080104686F6F6B"
|
||||
"00060ABF830001BB830002017F017E230041D0006B220124002001200036024C41"
|
||||
"900841114180084110410010001A200141306A2200411410011A4101410110021A"
|
||||
"200141286A41082000411410031A2001200131002F200131002842388620013100"
|
||||
"294230867C200131002A4228867C200131002B4220867C200131002C4218867C20"
|
||||
"0131002D4210867C200131002E4208867C7C3703202001410036021C0340419280"
|
||||
"80807841C90110021A200128021C41C8014E4504402001200134021C2001290320"
|
||||
"42C8017E7C370310200141106A220041082000410810041A2001200128021C4101"
|
||||
"6A36021C0C010B0B2001200129032042017C3703202001200141286A220036020C"
|
||||
"200128020C200129032042388842FF01833C0000200128020C2001290320423088"
|
||||
"42FF01833C0001200128020C200129032042288842FF01833C0002200128020C20"
|
||||
"0129032042208842FF01833C0003200128020C200129032042188842FF01833C00"
|
||||
"04200128020C200129032042108842FF01833C0005200128020C20012903204208"
|
||||
"8842FF01833C0006200128020C200129032042FF01833C00072000410820014130"
|
||||
"6A411410041A4180084110421C1005200141D0006A24000B0B2801004180080B21"
|
||||
"426173652E633A2043616C6C65642E0022426173652E633A2043616C6C65642E2"
|
||||
"2";
|
||||
|
||||
// install the hook on alice
|
||||
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
||||
M("set fix_page_cap"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
env(invoke::invoke(alice),
|
||||
M("test simple"),
|
||||
fee(XRP(1)),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(
|
||||
(*env.le(alice))[sfHookStateCount] == hasFix ? preHookCount + 200
|
||||
: preHookCount + 64);
|
||||
BEAST_EXPECT(
|
||||
(*env.le(alice))[sfOwnerCount] == hasFix ? preHookCount + 202
|
||||
: preHookCount + 66);
|
||||
}
|
||||
|
||||
void
|
||||
testCreate(FeatureBitset features)
|
||||
{
|
||||
@@ -11761,6 +11971,7 @@ public:
|
||||
|
||||
testNSDelete(features);
|
||||
testNSDeletePartial(features);
|
||||
testPageCap(features);
|
||||
|
||||
testWasm(features);
|
||||
test_accept(features);
|
||||
@@ -11862,6 +12073,8 @@ public:
|
||||
testWithFeatures(sa - fixXahauV2);
|
||||
testWithFeatures(sa - fixXahauV1 - fixXahauV2);
|
||||
testWithFeatures(sa - fixXahauV1 - fixXahauV2 - fixNSDelete);
|
||||
testWithFeatures(
|
||||
sa - fixXahauV1 - fixXahauV2 - fixNSDelete - fixPageCap);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -3968,8 +3968,8 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
using namespace std::chrono_literals;
|
||||
testcase("test claim reward valid without unl report");
|
||||
|
||||
Env env{
|
||||
*this, envconfig(), supported_amendments() - featureXahauGenesis};
|
||||
Env env{*this, envconfig(), features - featureXahauGenesis};
|
||||
bool const has240819 = env.current()->rules().enabled(fix240819);
|
||||
|
||||
double const rateDrops = 0.00333333333 * 1'000'000;
|
||||
STAmount const feesXRP = XRP(1);
|
||||
@@ -4050,7 +4050,12 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
// validate account fields
|
||||
STAmount const postUser = preUser + netReward;
|
||||
BEAST_EXPECT(expectAccountFields(
|
||||
env, user, preLedger, preLedger + 1, postUser, preTime));
|
||||
env,
|
||||
user,
|
||||
preLedger,
|
||||
preLedger + 1,
|
||||
has240819 ? (preUser - feesXRP) : postUser,
|
||||
preTime));
|
||||
|
||||
env(claimReward(user, env.master), fee(feesXRP), ter(tecHOOK_REJECTED));
|
||||
env.close();
|
||||
@@ -4095,7 +4100,12 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
// validate account fields
|
||||
STAmount const postUser1 = preUser1 + netReward1;
|
||||
BEAST_EXPECT(expectAccountFields(
|
||||
env, user, preLedger1, preLedger1 + 1, postUser1, preTime1));
|
||||
env,
|
||||
user,
|
||||
preLedger1,
|
||||
preLedger1 + 1,
|
||||
has240819 ? (preUser1 - feesXRP) : postUser1,
|
||||
preTime1));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -4219,8 +4229,14 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
|
||||
// validate account fields
|
||||
STAmount const postUser = preUser + netReward;
|
||||
bool const has240819 = env.current()->rules().enabled(fix240819);
|
||||
BEAST_EXPECT(expectAccountFields(
|
||||
env, user, preLedger, preLedger + 1, postUser, preTime));
|
||||
env,
|
||||
user,
|
||||
preLedger,
|
||||
preLedger + 1,
|
||||
has240819 ? (preUser - feesXRP) : postUser,
|
||||
preTime));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -4352,10 +4368,15 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
// validate account fields
|
||||
STAmount const postAlice = preAlice + netReward + l1Reward;
|
||||
bool const boolResult = withXahauV1 ? true : false;
|
||||
bool const has240819 = env.current()->rules().enabled(fix240819);
|
||||
BEAST_EXPECT(
|
||||
expectAccountFields(
|
||||
env, alice, preLedger, preLedger + 1, postAlice, preTime) ==
|
||||
boolResult);
|
||||
env,
|
||||
alice,
|
||||
preLedger,
|
||||
preLedger + 1,
|
||||
has240819 ? (preAlice - feesXRP) : postAlice,
|
||||
preTime) == boolResult);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4367,6 +4388,7 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
testcase("test claim reward optin optout");
|
||||
|
||||
Env env{*this, envconfig(), features - featureXahauGenesis};
|
||||
bool const has240819 = env.current()->rules().enabled(fix240819);
|
||||
|
||||
double const rateDrops = 0.00333333333 * 1'000'000;
|
||||
STAmount const feesXRP = XRP(1);
|
||||
@@ -4436,7 +4458,12 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
// validate account fields
|
||||
STAmount const postUser = preUser + netReward;
|
||||
BEAST_EXPECT(expectAccountFields(
|
||||
env, user, preLedger, preLedger + 1, postUser, preTime));
|
||||
env,
|
||||
user,
|
||||
preLedger,
|
||||
preLedger + 1,
|
||||
has240819 ? (preUser - feesXRP) : postUser,
|
||||
preTime));
|
||||
|
||||
// opt out of claim rewards
|
||||
env(claimReward(user, std::nullopt, 1), fee(feesXRP), ter(tesSUCCESS));
|
||||
@@ -4461,7 +4488,7 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
user,
|
||||
preLedger1,
|
||||
preLedger1 + 1,
|
||||
env.balance(user),
|
||||
has240819 ? (env.balance(user) + feesXRP) : env.balance(user),
|
||||
preTime1));
|
||||
}
|
||||
|
||||
@@ -4543,8 +4570,14 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
|
||||
// validate account fields
|
||||
STAmount const postUser = preUser + netReward;
|
||||
bool const has240819 = env.current()->rules().enabled(fix240819);
|
||||
BEAST_EXPECT(expectAccountFields(
|
||||
env, user, preLedger, preLedger + 1, postUser, preTime));
|
||||
env,
|
||||
user,
|
||||
preLedger,
|
||||
has240819 ? preLedger : preLedger + 1,
|
||||
has240819 ? (preUser - feesXRP) : postUser,
|
||||
preTime));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -4618,8 +4651,14 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
|
||||
// validate account fields
|
||||
STAmount const postUser = preUser + netReward;
|
||||
bool const has240819 = env.current()->rules().enabled(fix240819);
|
||||
BEAST_EXPECT(expectAccountFields(
|
||||
env, user, preLedger, preLedger + 1, postUser, preTime));
|
||||
env,
|
||||
user,
|
||||
preLedger,
|
||||
preLedger + 1,
|
||||
has240819 ? (preUser - feesXRP) : postUser,
|
||||
preTime));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -4824,13 +4863,13 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
Env env{
|
||||
*this,
|
||||
makeGenesisConfig(
|
||||
supported_amendments() - featureXahauGenesis,
|
||||
features - featureXahauGenesis,
|
||||
21337,
|
||||
"10",
|
||||
"1000000",
|
||||
"200000",
|
||||
0),
|
||||
supported_amendments() - featureXahauGenesis};
|
||||
features - featureXahauGenesis};
|
||||
|
||||
STAmount const feesXRP = XRP(1);
|
||||
|
||||
@@ -4890,8 +4929,7 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
using namespace std::chrono_literals;
|
||||
testcase("test compound interest over 12 claims");
|
||||
|
||||
Env env{
|
||||
*this, envconfig(), supported_amendments() - featureXahauGenesis};
|
||||
Env env{*this, envconfig(), features - featureXahauGenesis};
|
||||
|
||||
double const rateDrops = 0.00333333333 * 1'000'000;
|
||||
STAmount const feesXRP = XRP(1);
|
||||
@@ -4965,8 +5003,14 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
|
||||
// validate account fields
|
||||
STAmount const postUser = preUser + netReward;
|
||||
bool const has240819 = env.current()->rules().enabled(fix240819);
|
||||
BEAST_EXPECT(expectAccountFields(
|
||||
env, user, preLedger, preLedger + 1, postUser, preTime));
|
||||
env,
|
||||
user,
|
||||
preLedger,
|
||||
preLedger + 1,
|
||||
has240819 ? (preUser - feesXRP) : postUser,
|
||||
preTime));
|
||||
}
|
||||
|
||||
STAmount const endBal = env.balance(user);
|
||||
@@ -5012,6 +5056,7 @@ struct XahauGenesis_test : public beast::unit_test::suite
|
||||
using namespace test::jtx;
|
||||
auto const sa = supported_amendments();
|
||||
testGovernHookWithFeats(sa);
|
||||
testRewardHookWithFeats(sa - fix240819);
|
||||
testRewardHookWithFeats(sa);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user