Compare commits

...
Author SHA1 Message Date
Richard Holland 174139dc72 Merge branch 'dev' into reduce-magic-enum-server-definitions 2026-09-25 13:13:24 +10:00
tequ c2e253b365 refactor new account seqno (#772)
When creating accounts in GenesisLedger, we previously set the account Sequence to 0. However, since this conflicts with the PseudoAccount requirements, we are changing it to 1.
There will be no impact on networks that are already running, and for future networks, there won't be any impact unless you create accounts via GenesisLedger.
This specifically addresses an issue that comes up during unittests.
2026-09-25 13:10:18 +10:00
tequandRichard Holland ba130f8363 AMMClawback supported::no (#827)
Co-authored-by: Richard Holland <[email protected]>
2026-09-25 12:36:31 +10:00
tequ e87eabd8be HookOnV2_1 Amendment (#766) 2026-09-25 11:17:12 +10:00
Niq Dudfield 2e32b5c6bb feat(server_definitions): distinguish config-forced from ledger-enabled amendments (#765) 2026-09-23 15:33:43 +10:00
tequ 902ed9b492 Use normal consequences factory for standard transactions (#774)
Replace redundant custom consequence factories that returned normal consequences with the built-in Normal factory.
2026-09-21 21:12:15 +10:00
tequ fc11c10ed1 Merge branch 'dev' into reduce-magic-enum-server-definitions 2026-09-09 01:19:41 +09:00
tequ 9e5f76dbf9 Reduce MagicEnum usage in server definitions
Use existing type, ledger, transaction, and transaction-result registries to build server definitions. This removes custom enum ranges and duplicated name translation logic.
2026-08-04 00:02:45 +09:00
34 changed files with 721 additions and 339 deletions

No files matched your search

+4 -2
View File
@@ -34,6 +34,7 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FEATURE(HookOnV2_1, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(OnChainManifests, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (HookMap, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FIX (GuardDepth32, Supported::yes, VoteBehavior::DefaultNo)
@@ -45,7 +46,7 @@ XRPL_FEATURE(HookAPISerializedType240, Supported::yes, VoteBehavior::DefaultNo
XRPL_FEATURE(PermissionedDomains, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(DynamicNFT, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(Credentials, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMMClawback, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMMClawback, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(MPTokensV1, Supported::no, VoteBehavior::DefaultNo)
// InvariantsV1_1 will be changes to Supported::yes when all the
// invariants expected to be included under it are complete.
@@ -65,7 +66,8 @@ XRPL_FEATURE(XChainBridge, Supported::no, VoteBehavior::DefaultNo
XRPL_FEATURE(AMM, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (ReducedOffersV1, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(HooksUpdate2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(HookOnV2, Supported::yes, VoteBehavior::DefaultNo)
// replaced to HookOnV2_1
XRPL_FEATURE(HookOnV2, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (HookAPI20251128, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FIX (CronStacking, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo)
+3 -1
View File
@@ -292,7 +292,9 @@ JSS(duration_us); // out: NetworkOPs
JSS(effective); // out: ValidatorList
// in: UNL
JSS(elapsed_seconds);
JSS(enabled); // out: AmendmentTable
JSS(enabled); // out: AmendmentTable (on-ledger);
// ServerDefinitions (this server)
JSS(ledger_enabled); // out: ServerDefinitions (on-ledger)
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
+3
View File
@@ -150,12 +150,15 @@ private:
env.fund(XRP(20'000), alice, bob, carol, gw1, gw2);
env.fund(XRP(20'000), dan);
env.close();
env.trust(USD1(20'000), alice, bob, carol, dan);
env.trust(USD2(1'000), alice, bob, carol, dan);
env.close();
env(pay(gw1, dan, USD1(10'050)));
env(pay(gw1, bob, USD1(50)));
env(pay(gw2, bob, USD2(50)));
env.close();
AMM ammDan(env, dan, XRP(10'000), USD1(10'050));
+4 -4
View File
@@ -104,8 +104,8 @@ class LedgerMaster_test : public beast::unit_test::suite
startLegSeq, txnIndex);
BEAST_EXPECT(
*result ==
uint256("0CC11AD1AD89661689F6B6148D82CB6A7101DA4D66EC843670262D"
"0B618F5745"));
uint256("DCAECE52F028B9D0F7CA43CBE15AB4EF7B84A8EF5D455238992E1E"
"DF2BDA1637"));
}
// success (second tx)
{
@@ -114,8 +114,8 @@ class LedgerMaster_test : public beast::unit_test::suite
startLegSeq + 1, txnIndex);
BEAST_EXPECT(
*result ==
uint256("DCAECE52F028B9D0F7CA43CBE15AB4EF7B84A8EF5D455238992E1E"
"DF2BDA1637"));
uint256("722BD5EC9E4AE36E724B6223F5A5887927AADFD0B18A3053AAE57F"
"434F8C9FE8"));
}
}
+5 -5
View File
@@ -1174,11 +1174,11 @@ class MPToken_test : public beast::unit_test::suite
JsonOptions::none)[sfAffectedNodes.fieldName];
// Issuer got 10 in the transfer fees
BEAST_EXPECT(
meta[2u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
meta[0u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
[sfOutstandingAmount.fieldName] == "9990");
// Destination account got 9'990
BEAST_EXPECT(
meta[0u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
meta[3u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
[sfMPTAmount.fieldName] == "9990");
// Source account spent 10'000
BEAST_EXPECT(
@@ -1910,8 +1910,8 @@ class MPToken_test : public beast::unit_test::suite
env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
BEAST_EXPECTS(
txHash ==
"42175D43E3D236A86B0F1B07182A1B2C78AD382372CFA09A48239C395FFFEE"
"30",
"61041108F0DAD50BAFD2F7B1102AC70B283EBDCED194CEBAF04D0184E4A70B"
"02",
txHash);
Json::Value const meta = env.rpc("tx", txHash)[jss::result][jss::meta];
auto const id = meta[jss::mpt_issuance_id].asString();
@@ -1919,7 +1919,7 @@ class MPToken_test : public beast::unit_test::suite
BEAST_EXPECT(meta.isMember(jss::mpt_issuance_id));
BEAST_EXPECT(id == to_string(mptAlice.issuanceID()));
BEAST_EXPECTS(
id == "00000001AE123A8556F3CF91154711376AFB0F894F832B3D", id);
id == "00000002AE123A8556F3CF91154711376AFB0F894F832B3D", id);
}
void
+10 -10
View File
@@ -8279,16 +8279,16 @@ private:
// validate the emitted txn ids
std::vector<std::string> const txIds = {
"9610F73CDD6590EB6B3C82E5EC55D4B4C80CD7128B98AA556F7EC9DD96AE7056",
"2F4582A29272390C0C25A80D4A3BCE5A14ACE6D86D8D0CB2C57719EB6FA881AE",
"89A301CFEF0DD781AB9032A6A2DCE0937BC0119D2CDD06033B8B2FD80968E519",
"DD8721B59024E168480B4DF8F8E93778601F0BD2E77FC991F3DA1182F5AD8B1E",
"5D735C2EE3CB8289F8E11621FDC9565F9D6D67F3AE59D65332EACE591D67945F",
"F02470E01731C968881AF4CBDEC90BB9E1F7AB0BE1CC22AF15451FB6D191096D",
"8AD65E541DECD49B1693F8C17DFD8A2B906F49C673C4FD2034FF772E2BE50C30",
"9F225229059CCC6257814D03C107884CF588C1C246A89ADFC16E50DF671B834C",
"13C2A54A14BADF3648CED05175E1CCAD713F7E5EA56D9735CF8813CD5551F281",
"87C60F41A96554587CED289F83F52DEE3CF670EEB189B067E6066B9A06056ADF",
"2FBEF981BAC322225D13C274D0118FECC1B93A89EDBC0AB3CBD8B30A1591024E",
"3C0A12F6A322486ABDA4180DC2B424BD252FD2FA6A33553B0B5D1526C020C475",
"AFFD55D757136FC0BE665F16E0D8CFBDC0D37CF85B5D3329BE1DBD3A44BB281C",
"BBE43C01068CE511DBF24F84A928E481451FF448AF2493E1A9BE099628D1F523",
"45970753F7B52448C23D66E5E19DBBC65F5BE6055C04AC3BA9896D7D67891DB1",
"97F4E09B2AEF4D798D378B1390B0624A68F304247B358334167E93217D502A16",
"770D3B786C8E3BEF560D9589B02557B0BDFC1C7FD15C6CF42E86068A6A411EFC",
"239DFA86EE8391418170D7FDF5AA8B8EEE0BBEF5E9F928BC29C23EEF6F1858AD",
"9FAA93AC6340C87B5C722FF0E8D4D3F39B9A0CA5E9799EC4CE316BFD5E379540",
"8672B2D8AA5E9EF86929B8662FE436541B2F739052732020B874BC304601C4DB",
};
Json::Value params;
params[jss::transaction] =
+342 -29
View File
@@ -1355,11 +1355,16 @@ public:
auto const baseFee = drops[jss::base_fee_no_hooks];
BEAST_EXPECT(baseFee == to_string(feeDrops));
auto const openLedgerFee = drops[jss::open_ledger_fee];
BEAST_EXPECT(openLedgerFee == expected);
BEAST_EXPECTS(
openLedgerFee == expected,
"openLedgerFee: " + openLedgerFee.asString() +
", expected: " + expected);
// verify hooks fee
auto const hooksFee = jrr[jss::result][jss::fee_hooks_feeunits];
BEAST_EXPECT(hooksFee == expected);
BEAST_EXPECTS(
hooksFee == expected,
"hooksFee: " + hooksFee.asString() + ", expected: " + expected);
}
void
@@ -1369,7 +1374,8 @@ public:
using namespace jtx;
Env env{*this, features};
bool const hookOnV2 = env.current()->rules().enabled(featureHookOnV2);
bool const hookOnV2 = env.current()->rules().enabled(featureHookOnV2) ||
env.current()->rules().enabled(featureHookOnV2_1);
auto const alice = Account{"alice"};
auto const bob = Account{"bob"};
@@ -1532,12 +1538,10 @@ public:
jv.removeMember(jss::HookOn);
jv[jss::HookOnIncoming] =
"0000000000000000000000000000000000000000000000000000000000"
"0000"
"00";
"000000";
jv[jss::HookOnOutgoing] =
"0000000000000000000000000000000000000000000000000000000000"
"0000"
"01";
"000001";
env(ripple::test::jtx::hook(alice, {{jv}}, 0),
M("Execution: Install"),
HSFEE);
@@ -1602,12 +1606,10 @@ public:
jv.removeMember(jss::HookOn);
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfff"
"ff"; // Invoke high
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bfff"
"fe"; // Payment high
"bffffe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0),
M("Execution: Install"),
HSFEE);
@@ -1618,8 +1620,7 @@ public:
jv[jss::Flags] = hsfOVERRIDE;
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"0000"
"00";
"000000";
env(ripple::test::jtx::hook(alice, {{jv}}, 0),
M("Execution: Install"),
HSFEE);
@@ -1653,18 +1654,285 @@ public:
deleteHook(alice);
}
for (auto const& withFix : {false, true})
{
// test featureHookOnV2_1 preflight
auto f = features - featureHookOnV2 - featureHookOnV2_1;
if (withFix)
f = f | featureHookOnV2_1;
else
f = f | featureHookOnV2;
Env env{*this, f};
env.fund(XRP(10000), alice, bob);
env.close();
TER const expected =
env.current()->rules().enabled(featureHookOnV2_1)
? TER(temMALFORMED)
: TER(tesSUCCESS);
// 0. only one direction HookOn is not allowed
for (auto const& key : {jss::HookOnIncoming, jss::HookOnOutgoing})
{
auto noopJv = Json::Value{};
noopJv[key] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff";
env(ripple::test::jtx::hook(alice, {{noopJv}}, 0),
M("Lone direction HookOn NOOP"),
HSFEE,
ter(expected));
env.close();
if (!withFix)
BEAST_EXPECT(!env.le(keylet::hook(alice)));
}
// 1. Create Hook with HookOnIncoming/Outgoing to alice
auto jv = hso(accept_wasm);
jv.removeMember(jss::HookOn);
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
// 2. Install Hook with HookOn, Incoming/Outgoing to bob
jv = Json::Value{};
jv[jss::HookHash] = accept_hash_str;
jv[jss::Flags] = hsfOVERRIDE;
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(bob, {{jv}}, 0), HSFEE, ter(expected));
env.close();
// 3. Update Hook with HookOn, Incoming/Outgoing to alice
jv = Json::Value{};
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0),
HSFEE,
ter(expected));
env.close();
}
for (auto const& withFix : {false, true})
{
// test featureHookOnV2_1 install/update
auto f = features - featureHookOnV2 - featureHookOnV2_1;
if (withFix)
f = f | featureHookOnV2_1;
else
f = f | featureHookOnV2;
{
// install:: HookDefinition: HookOn -> Hook:Incoming/Outgoing
// update:: Hook: HookOn -> Hook:Incoming/Outgoing
Env env{*this, f};
env.fund(XRP(10000), alice, bob);
env.close();
// Create Hook with HookOn to alice
auto jv = hso(accept_wasm);
jv.removeMember(jss::HookOn);
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
// Install Hook with Incoming/Outgoing to bob
jv = Json::Value{};
jv[jss::HookHash] = accept_hash_str;
jv[jss::Flags] = hsfOVERRIDE;
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(bob, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(bob));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
BEAST_EXPECT(!h.isFieldPresent(sfHookOn));
BEAST_EXPECT(h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(h.isFieldPresent(sfHookOnIncoming));
}
// Update Hook with HookOn, Incoming/Outgoing to alice
jv = Json::Value{};
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(alice));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
BEAST_EXPECT(!h.isFieldPresent(sfHookOn));
BEAST_EXPECT(h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(h.isFieldPresent(sfHookOnIncoming));
}
// Re-update Hook with HookOn, Incoming/Outgoing to alice
jv = Json::Value{};
jv[jss::HookOn] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff";
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(alice));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
BEAST_EXPECT(h.isFieldPresent(sfHookOn));
if (withFix)
{
BEAST_EXPECT(!h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(!h.isFieldPresent(sfHookOnIncoming));
}
else
{
BEAST_EXPECT(h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(h.isFieldPresent(sfHookOnIncoming));
}
}
}
{
// install:: HookDefinition: Incoming/Outgoing -> Hook:HookOn
// update:: Hook: Incoming/Outgoing -> Hook:HookOn
Env env{*this, f};
env.fund(XRP(10000), alice, bob);
env.close();
// Create Hook with HookOn to alice
auto jv = hso(accept_wasm);
jv.removeMember(jss::HookOn);
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
// Install Hook with Incoming/Outgoing to bob
jv = Json::Value{};
jv[jss::HookHash] = accept_hash_str;
jv[jss::Flags] = hsfOVERRIDE;
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
env(ripple::test::jtx::hook(bob, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(bob));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
BEAST_EXPECT(h.isFieldPresent(sfHookOn));
BEAST_EXPECT(!h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(!h.isFieldPresent(sfHookOnIncoming));
}
// Update Hook with HookOn, Incoming/Outgoing to alice
jv = Json::Value{};
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(alice));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
if (withFix)
BEAST_EXPECT(h.isFieldPresent(sfHookOn));
// Cause different results between gcc and clang due to
// undefined behavior so we don't test it
// else
// BEAST_EXPECT(!h.isFieldPresent(sfHookOn));
BEAST_EXPECT(!h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(!h.isFieldPresent(sfHookOnIncoming));
}
// Re-update Hook with HookOn, Incoming/Outgoing to alice
jv = Json::Value{};
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bffff0"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffff1"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(alice));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
if (withFix)
BEAST_EXPECT(!h.isFieldPresent(sfHookOn));
// Cause different results between gcc and clang due to
// undefined behavior so we don't test it
// else
// BEAST_EXPECT(h.isFieldPresent(sfHookOn));
BEAST_EXPECT(h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(h.isFieldPresent(sfHookOnIncoming));
}
}
}
// Fee RPC
{
auto jv = hso(accept_wasm);
jv.removeMember(jss::HookOn);
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfff"
"ff"; // Invoke high
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bfff"
"fe"; // Payment high
"bffffe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
@@ -3003,25 +3271,37 @@ public:
testInferHookSetOperation()
{
testcase("Test operation inference");
using namespace jtx;
auto const alice = Account{"alice"};
Env env{*this};
env.fund(XRP(10000), alice);
env(noop(alice));
SetHookCtx shCtx{
.j = env.app().journal("SetHook"),
.tx = *(env.tx()),
.app = env.app(),
.rules = env.current()->rules()};
// hsoNOOP
{
STObject hso{sfHook};
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoNOOP);
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoNOOP);
}
// hsoCREATE
{
STObject hso{sfHook};
hso.setFieldVL(sfCreateCode, {1}); // non-empty create code
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoCREATE);
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoCREATE);
}
// hsoDELETE
{
STObject hso{sfHook};
hso.setFieldVL(sfCreateCode, ripple::Blob{}); // empty create code
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoDELETE);
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoDELETE);
}
// hsoINSTALL
@@ -3029,7 +3309,7 @@ public:
STObject hso{sfHook};
hso.setFieldH256(
sfHookHash, uint256{beast::zero}); // all zeros hook hash
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoINSTALL);
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoINSTALL);
}
// hsoNSDELETE
@@ -3038,14 +3318,14 @@ public:
hso.setFieldH256(
sfHookNamespace, uint256{beast::zero}); // all zeros hook hash
hso.setFieldU32(sfFlags, hsfNSDELETE);
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoNSDELETE);
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoNSDELETE);
}
// hsoUPDATE
{
STObject hso{sfHook};
hso.setFieldH256(sfHookOn, UINT256_BIT[0]);
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoUPDATE);
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoUPDATE);
}
// hsoINVALID
@@ -3054,7 +3334,39 @@ public:
hso.setFieldVL(sfCreateCode, {1}); // non-empty create code
hso.setFieldH256(
sfHookHash, uint256{beast::zero}); // all zeros hook hash
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoINVALID);
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoINVALID);
for (auto fix : {true, false})
{
auto feature = supported_amendments() - featureHookOnV2 -
featureHookOnV2_1;
if (fix)
feature = feature | featureHookOnV2_1;
else
feature = feature | featureHookOnV2;
Env env{*this, feature};
SetHookCtx shCtx{
.j = env.app().journal("SetHook"),
.tx = *env.tx(),
.app = env.app(),
.rules = env.current()->rules()};
STObject hso2{sfHook};
hso2.setFieldH256(
sfHookOnOutgoing,
uint256{beast::zero}); // all zeros hook on outgoing
BEAST_EXPECT(
SetHook::inferOperation(shCtx, hso2) ==
(fix ? hsoINVALID : hsoNOOP));
STObject hso3{sfHook};
hso3.setFieldH256(
sfHookOnIncoming,
uint256{beast::zero}); // all zeros hook on incoming
BEAST_EXPECT(
SetHook::inferOperation(shCtx, hso3) ==
(fix ? hsoINVALID : hsoNOOP));
}
}
}
@@ -4277,10 +4589,10 @@ public:
uint8_t expected1[49] = {
0xEDU, 0x20U, 0x2EU, 0x00U, 0x00U, 0x00U, 0x01U, 0x3DU, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU, 0xB8U, 0x05U, 0xD6U,
0xC3U, 0x52U, 0xDFU, 0x7AU, 0x27U, 0x76U, 0x6DU, 0xC0U, 0x20U, 0x47U,
0xB7U, 0x64U, 0x22U, 0x5AU, 0xB7U, 0x5DU, 0xF3U, 0xFAU, 0x0DU, 0xE3U,
0xBDU, 0xC6U, 0x40U, 0xBAU, 0xD0U, 0x0AU, 0x66U, 0xEBU, 0x68U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU, 0xBCU, 0x09U, 0x95U,
0xFDU, 0x03U, 0x0FU, 0xF1U, 0x85U, 0xF4U, 0xECU, 0xACU, 0x94U, 0xDBU,
0x02U, 0x19U, 0x26U, 0xFEU, 0x58U, 0xD4U, 0x8DU, 0x48U, 0x21U, 0xCCU,
0x82U, 0x2EU, 0x4AU, 0x6AU, 0xC9U, 0xC8U, 0xC6U, 0x7CU, 0x88U
};
// 0x5CU
// EmitNonce 32bytes
@@ -15075,7 +15387,8 @@ public:
testNSDeletePartial(features);
testPageCap(features);
testHookOnV2(features);
testHookOnV2((features | featureHookOnV2) - featureHookOnV2_1);
testHookOnV2((features | featureHookOnV2_1) - featureHookOnV2);
testHookName(features);
testFillCopy(features);
+8 -8
View File
@@ -1613,10 +1613,10 @@ std::map<std::string, std::vector<uint8_t>> wasm = {
uint8_t expected1[49] = {
0xEDU, 0x20U, 0x2EU, 0x00U, 0x00U, 0x00U, 0x01U, 0x3DU, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU, 0xB8U, 0x05U, 0xD6U,
0xC3U, 0x52U, 0xDFU, 0x7AU, 0x27U, 0x76U, 0x6DU, 0xC0U, 0x20U, 0x47U,
0xB7U, 0x64U, 0x22U, 0x5AU, 0xB7U, 0x5DU, 0xF3U, 0xFAU, 0x0DU, 0xE3U,
0xBDU, 0xC6U, 0x40U, 0xBAU, 0xD0U, 0x0AU, 0x66U, 0xEBU, 0x68U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU, 0xBCU, 0x09U, 0x95U,
0xFDU, 0x03U, 0x0FU, 0xF1U, 0x85U, 0xF4U, 0xECU, 0xACU, 0x94U, 0xDBU,
0x02U, 0x19U, 0x26U, 0xFEU, 0x58U, 0xD4U, 0x8DU, 0x48U, 0x21U, 0xCCU,
0x82U, 0x2EU, 0x4AU, 0x6AU, 0xC9U, 0xC8U, 0xC6U, 0x7CU, 0x88U
};
// 0x5CU
// EmitNonce 32bytes
@@ -1763,10 +1763,10 @@ std::map<std::string, std::vector<uint8_t>> wasm = {
0x36U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0xEDU, 0x20U, 0x2EU, 0x00U, 0x00U, 0x00U, 0x01U,
0x3DU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU,
0xB8U, 0x05U, 0xD6U, 0xC3U, 0x52U, 0xDFU, 0x7AU, 0x27U, 0x76U, 0x6DU,
0xC0U, 0x20U, 0x47U, 0xB7U, 0x64U, 0x22U, 0x5AU, 0xB7U, 0x5DU, 0xF3U,
0xFAU, 0x0DU, 0xE3U, 0xBDU, 0xC6U, 0x40U, 0xBAU, 0xD0U, 0x0AU, 0x66U,
0xEBU, 0x68U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x68U, 0x61U, 0x73U,
0xBCU, 0x09U, 0x95U, 0xFDU, 0x03U, 0x0FU, 0xF1U, 0x85U, 0xF4U, 0xECU,
0xACU, 0x94U, 0xDBU, 0x02U, 0x19U, 0x26U, 0xFEU, 0x58U, 0xD4U, 0x8DU,
0x48U, 0x21U, 0xCCU, 0x82U, 0x2EU, 0x4AU, 0x6AU, 0xC9U, 0xC8U, 0xC6U,
0x7CU, 0x88U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x68U, 0x61U, 0x73U,
0x68U, 0x28U, 0x28U, 0x75U, 0x69U, 0x6EU, 0x74U, 0x33U, 0x32U, 0x5FU,
0x74U, 0x29U, 0x65U, 0x78U, 0x70U, 0x65U, 0x63U, 0x74U, 0x65U, 0x64U,
0x5FU, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x68U, 0x61U, 0x73U, 0x68U,
+1
View File
@@ -980,6 +980,7 @@ struct URIToken_test : public beast::unit_test::suite
// setup env
env.fund(XRP(1000), alice, bob, gw);
env.close();
env.trust(USD(100000), alice, bob);
env.close();
env(pay(gw, alice, USD(1000)));
+31 -31
View File
@@ -37,20 +37,36 @@ namespace test {
static char const* bobs_account_objects[] = {
R"json({
"Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory" : "B025997A323F5C3E03DDF1334471F5984ABDE31C59D463525D038D7EA4C68000",
"BookNode" : "0",
"Flags" : 65536,
"LedgerEntryType" : "Offer",
"OwnerNode" : "0",
"Sequence" : 4,
"TakerGets" : {
"currency" : "USD",
"issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
"value" : "1"
},
"TakerPays" : "100000000",
"index" : "A984D036A0E562433A8377CA57D1A1E056E58C0D04818F8DFD3A1AA3F217DD82"
"Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory" : "50AD0A9E54D2B381288D535EB724E4275FFBF41580D28A925D038D7EA4C68000",
"BookNode" : "0",
"Flags" : 65536,
"LedgerEntryType" : "Offer",
"OwnerNode" : "0",
"Sequence" : 4,
"TakerGets" : {
"currency" : "USD",
"issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"value" : "1"
},
"TakerPays" : "100000000",
"index" : "A984D036A0E562433A8377CA57D1A1E056E58C0D04818F8DFD3A1AA3F217DD82"
})json",
R"json({
"Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory" : "B025997A323F5C3E03DDF1334471F5984ABDE31C59D463525D038D7EA4C68000",
"BookNode" : "0",
"Flags" : 65536,
"LedgerEntryType" : "Offer",
"OwnerNode" : "0",
"Sequence" : 5,
"TakerGets" : {
"currency" : "USD",
"issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
"value" : "1"
},
"TakerPays" : "100000000",
"index" : "CAFE32332D752387B01083B60CC63069BA4A969C9730836929F841450F6A718E"
})json",
R"json({
"Balance" : {
@@ -95,22 +111,6 @@ static char const* bobs_account_objects[] = {
},
"LowNode" : "0",
"index" : "D89BC239086183EB9458C396E643795C1134963E6550E682A190A5F021766D43"
})json",
R"json({
"Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory" : "50AD0A9E54D2B381288D535EB724E4275FFBF41580D28A925D038D7EA4C68000",
"BookNode" : "0",
"Flags" : 65536,
"LedgerEntryType" : "Offer",
"OwnerNode" : "0",
"Sequence" : 3,
"TakerGets" : {
"currency" : "USD",
"issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"value" : "1"
},
"TakerPays" : "100000000",
"index" : "E11029302EE744401427793A4F37BCB18F698D55C96851BEC5ABBD6242CF03D7"
})json"};
class AccountObjects_test : public beast::unit_test::suite
@@ -332,7 +332,7 @@ public:
auto& aobj = resp[jss::result][jss::account_objects][i];
aobj.removeMember("PreviousTxnID");
aobj.removeMember("PreviousTxnLgrSeq");
BEAST_EXPECT(aobj == bobj[i + 1]);
BEAST_EXPECT(aobj == bobj[i + 2]);
}
}
// test stepped one-at-a-time with limit=1, resume from prev marker
+6 -6
View File
@@ -117,9 +117,9 @@ public:
BEAST_EXPECT(jroOuter[0u][jss::quality] == "100000000");
BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::currency] == "USD");
BEAST_EXPECT(
jroOuter[0u][jss::taker_gets][jss::issuer] == bob.human());
BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::value] == "1");
BEAST_EXPECT(jroOuter[0u][jss::taker_pays] == "100000000");
jroOuter[0u][jss::taker_gets][jss::issuer] == gw.human());
BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::value] == "2");
BEAST_EXPECT(jroOuter[0u][jss::taker_pays] == "200000000");
BEAST_EXPECT(jroOuter[1u][jss::quality] == "5000000");
BEAST_EXPECT(jroOuter[1u][jss::taker_gets][jss::currency] == "USD");
@@ -131,9 +131,9 @@ public:
BEAST_EXPECT(jroOuter[2u][jss::quality] == "100000000");
BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::currency] == "USD");
BEAST_EXPECT(
jroOuter[2u][jss::taker_gets][jss::issuer] == gw.human());
BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::value] == "2");
BEAST_EXPECT(jroOuter[2u][jss::taker_pays] == "200000000");
jroOuter[2u][jss::taker_gets][jss::issuer] == bob.human());
BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::value] == "1");
BEAST_EXPECT(jroOuter[2u][jss::taker_pays] == "100000000");
}
{
+128 -8
View File
@@ -163,7 +163,7 @@ public:
void
testNoParams(FeatureBitset features)
{
testcase("No Params, None Enabled");
testcase("Default Env: config-forced, none on-ledger");
using namespace test::jtx;
Env env{*this};
@@ -178,8 +178,6 @@ public:
{
if (!BEAST_EXPECT(feature.isMember(jss::name)))
return;
// default config - so all should be disabled, and
// supported. Some may be vetoed.
bool expectVeto =
(votes.at(feature[jss::name].asString()) ==
VoteBehavior::DefaultNo);
@@ -188,8 +186,12 @@ public:
VoteBehavior::Obsolete);
BEAST_EXPECTS(
feature.isMember(jss::enabled) &&
!feature[jss::enabled].asBool(),
feature[jss::enabled].asBool(),
feature[jss::name].asString() + " enabled");
BEAST_EXPECTS(
feature.isMember(jss::ledger_enabled) &&
!feature[jss::ledger_enabled].asBool(),
feature[jss::name].asString() + " ledger_enabled");
BEAST_EXPECTS(
feature.isMember(jss::vetoed) &&
feature[jss::vetoed].isBool() == !expectObsolete &&
@@ -208,7 +210,7 @@ public:
void
testSomeEnabled(FeatureBitset features)
{
testcase("No Params, Some Enabled");
testcase("Two config-forced, none on-ledger");
using namespace test::jtx;
Env env{
@@ -228,7 +230,10 @@ public:
(void)id.parseHex(it.key().asString().c_str());
if (!BEAST_EXPECT((*it).isMember(jss::name)))
return;
bool expectEnabled = env.app().getAmendmentTable().isEnabled(id);
bool const expectOnLedger =
env.app().getAmendmentTable().isEnabled(id);
bool const expectForced =
id == featureDepositAuth || id == featureDepositPreauth;
bool expectSupported =
env.app().getAmendmentTable().isSupported(id);
bool expectVeto =
@@ -239,9 +244,14 @@ public:
VoteBehavior::Obsolete);
BEAST_EXPECTS(
(*it).isMember(jss::enabled) &&
(*it)[jss::enabled].asBool() == expectEnabled,
(*it)[jss::enabled].asBool() ==
(expectOnLedger || expectForced),
(*it)[jss::name].asString() + " enabled");
if (expectEnabled)
BEAST_EXPECTS(
(*it).isMember(jss::ledger_enabled) &&
(*it)[jss::ledger_enabled].asBool() == expectOnLedger,
(*it)[jss::name].asString() + " ledger_enabled");
if (expectOnLedger)
BEAST_EXPECTS(
!(*it).isMember(jss::vetoed),
(*it)[jss::name].asString() + " vetoed");
@@ -360,12 +370,122 @@ public:
}
}
void
testConfigForced(FeatureBitset features)
{
testcase("Config-forced features ([features] stanza)");
using namespace test::jtx;
auto const forced = featurePriceOracle;
auto const forcedHex = to_string(forced);
Env env{*this, FeatureBitset(forced)};
auto jrr = env.rpc("server_definitions")[jss::result];
if (!BEAST_EXPECT(jrr.isMember(jss::features)))
return;
bool sawForced = false;
for (auto it = jrr[jss::features].begin();
it != jrr[jss::features].end();
++it)
{
auto const& f = *it;
auto const name = f[jss::name].asString();
if (!BEAST_EXPECTS(
f.isMember(jss::enabled) && f.isMember(jss::ledger_enabled),
name + " enabled/ledger_enabled"))
return;
BEAST_EXPECTS(
!f[jss::ledger_enabled].asBool(), name + " ledger_enabled");
if (it.key().asString() == forcedHex)
{
sawForced = true;
BEAST_EXPECTS(f[jss::enabled].asBool(), name + " enabled");
}
else
{
BEAST_EXPECTS(!f[jss::enabled].asBool(), name + " enabled");
}
}
BEAST_EXPECT(sawForced);
}
void
testOnLedger(FeatureBitset features)
{
testcase("On-ledger plus one config-forced");
using namespace test::jtx;
// Veto XahauGenesis: FRESH would otherwise enable it via pseudo-tx.
auto const forced = featurePriceOracle;
auto const forcedHex = to_string(forced);
Env env{
*this,
envconfig([](std::unique_ptr<Config> cfg) {
cfg->START_UP = Config::FRESH;
cfg->section(SECTION_VETO_AMENDMENTS)
.append(to_string(featureXahauGenesis) + " XahauGenesis");
return cfg;
}),
FeatureBitset(forced)};
auto jrr = env.rpc("server_definitions")[jss::result];
if (!BEAST_EXPECT(jrr.isMember(jss::features)))
return;
bool sawOnLedger = false;
bool sawForced = false;
for (auto it = jrr[jss::features].begin();
it != jrr[jss::features].end();
++it)
{
uint256 id;
(void)id.parseHex(it.key().asString().c_str());
auto const& f = *it;
auto const name = f[jss::name].asString();
if (!BEAST_EXPECTS(
f.isMember(jss::enabled) && f.isMember(jss::ledger_enabled),
name + " enabled/ledger_enabled"))
return;
bool const onLedger = env.app().getAmendmentTable().isEnabled(id);
bool const isForced = it.key().asString() == forcedHex;
BEAST_EXPECTS(
f[jss::ledger_enabled].asBool() == onLedger,
name + " ledger_enabled");
BEAST_EXPECTS(
f[jss::enabled].asBool() == (onLedger || isForced),
name + " enabled");
if (onLedger)
sawOnLedger = true;
if (isForced)
{
sawForced = true;
BEAST_EXPECTS(!onLedger, name + " forced not on-ledger");
}
}
BEAST_EXPECT(sawOnLedger);
BEAST_EXPECT(sawForced);
}
void
testServerFeatures(FeatureBitset features)
{
testNoParams(features);
testSomeEnabled(features);
testWithMajorities(features);
testConfigForced(features);
testOnLedger(features);
}
void
+1 -6
View File
@@ -252,12 +252,7 @@ applyCreate(
auto sleAMMRoot = std::make_shared<SLE>(keylet::account(*ammAccount));
sleAMMRoot->setAccountID(sfAccount, *ammAccount);
sleAMMRoot->setFieldAmount(sfBalance, STAmount{});
std::uint32_t const seqno{
ctx_.view().rules().enabled(featureXahauGenesis)
? ctx_.view().info().parentCloseTime.time_since_epoch().count()
: ctx_.view().rules().enabled(featureDeletableAccounts)
? ctx_.view().seq()
: 1};
std::uint32_t const seqno = newAccountSeqNo(ctx_.view());
sleAMMRoot->setFieldU32(sfSequence, seqno);
// Ignore reserves requirement, disable the master key, allow default
// rippling (AMM LPToken can be used in payments and offer crossing but
+1 -2
View File
@@ -554,8 +554,7 @@ Change::activateXahauGenesis()
{
sle = std::make_shared<SLE>(kl);
sle->setAccountID(sfAccount, accid);
std::uint32_t const seqno{
sb.info().parentCloseTime.time_since_epoch().count()};
std::uint32_t const seqno = newAccountSeqNo(sb);
sle->setFieldU32(sfSequence, seqno);
}
-6
View File
@@ -30,12 +30,6 @@
namespace ripple {
TxConsequences
ClaimReward::makeTxConsequences(PreflightContext const& ctx)
{
return TxConsequences{ctx.tx, TxConsequences::normal};
}
NotTEC
ClaimReward::preflight(PreflightContext const& ctx)
{
+1 -4
View File
@@ -32,15 +32,12 @@ namespace ripple {
class ClaimReward : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ClaimReward(ApplyContext& ctx) : Transactor(ctx)
{
}
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
-6
View File
@@ -30,12 +30,6 @@
namespace ripple {
TxConsequences
Cron::makeTxConsequences(PreflightContext const& ctx)
{
return TxConsequences{ctx.tx, TxConsequences::normal};
}
NotTEC
Cron::preflight(PreflightContext const& ctx)
{
+1 -4
View File
@@ -30,7 +30,7 @@ namespace ripple {
class Cron : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit Cron(ApplyContext& ctx) : Transactor(ctx)
{
@@ -39,9 +39,6 @@ public:
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
-6
View File
@@ -27,12 +27,6 @@
namespace ripple {
TxConsequences
CronSet::makeTxConsequences(PreflightContext const& ctx)
{
return TxConsequences{ctx.tx, TxConsequences::normal};
}
NotTEC
CronSet::preflight(PreflightContext const& ctx)
{
+1 -4
View File
@@ -29,7 +29,7 @@ namespace ripple {
class CronSet : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit CronSet(ApplyContext& ctx) : Transactor(ctx)
{
@@ -38,9 +38,6 @@ public:
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
+1 -2
View File
@@ -243,8 +243,7 @@ GenesisMint::doApply()
if (created)
{
// Create the account.
std::uint32_t const seqno{
view().info().parentCloseTime.time_since_epoch().count()};
std::uint32_t const seqno = newAccountSeqNo(view());
sle = std::make_shared<SLE>(k);
sle->setAccountID(sfAccount, id);
+1 -6
View File
@@ -1307,12 +1307,7 @@ Import::doApply()
if (create)
{
// Create the account.
std::uint32_t const seqno{
view().rules().enabled(featureXahauGenesis)
? view().info().parentCloseTime.time_since_epoch().count()
: view().rules().enabled(featureDeletableAccounts)
? view().seq()
: 1};
std::uint32_t const seqno = newAccountSeqNo(view());
sle = std::make_shared<SLE>(keylet::account(id));
sle->setAccountID(sfAccount, id);
+1 -5
View File
@@ -1041,11 +1041,7 @@ ValidNewAccountRoot::finalize(
tt == ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION) &&
isTesSuccess(result))
{
std::uint32_t const startingSeq{
view.rules().enabled(featureXahauGenesis)
? view.info().parentCloseTime.time_since_epoch().count()
: view.rules().enabled(featureDeletableAccounts) ? view.seq()
: 1};
std::uint32_t const startingSeq = newAccountSeqNo(view);
if (accountSeq_ != startingSeq)
{
-6
View File
@@ -26,12 +26,6 @@
namespace ripple {
TxConsequences
Invoke::makeTxConsequences(PreflightContext const& ctx)
{
return TxConsequences{ctx.tx, TxConsequences::normal};
}
NotTEC
Invoke::preflight(PreflightContext const& ctx)
{
+1 -4
View File
@@ -30,7 +30,7 @@ namespace ripple {
class Invoke : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit Invoke(ApplyContext& ctx) : Transactor(ctx)
{
@@ -39,9 +39,6 @@ public:
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
+1 -6
View File
@@ -354,12 +354,7 @@ Payment::doApply()
if (!sleDst)
{
std::uint32_t const seqno{
view().rules().enabled(featureXahauGenesis)
? view().info().parentCloseTime.time_since_epoch().count()
: view().rules().enabled(featureDeletableAccounts)
? view().seq()
: 1};
std::uint32_t const seqno = newAccountSeqNo(view());
// Create the account.
sleDst = std::make_shared<SLE>(k);
+1 -5
View File
@@ -328,11 +328,7 @@ Remit::doApply()
nativeRemit += accountReserve;
// Create the account.
std::uint32_t const seqno{
sb.rules().enabled(featureXahauGenesis)
? sb.info().parentCloseTime.time_since_epoch().count()
: sb.rules().enabled(featureDeletableAccounts) ? sb.seq()
: 1};
std::uint32_t const seqno = newAccountSeqNo(sb);
sleDstAcc = std::make_shared<SLE>(keylet::account(dstAccID));
sleDstAcc->setAccountID(sfAccount, dstAccID);
+113 -56
View File
@@ -207,7 +207,7 @@ validateHookParams(SetHookCtx& ctx, STArray const& hookParams)
// infer which operation the user is attempting to execute from the present and
// absent fields
HookSetOperation
SetHook::inferOperation(STObject const& hookSetObj)
SetHook::inferOperation(SetHookCtx& ctx, STObject const& hookSetObj)
{
uint64_t wasmByteCount = hookSetObj.isFieldPresent(sfCreateCode)
? hookSetObj.getFieldVL(sfCreateCode).size()
@@ -216,7 +216,12 @@ SetHook::inferOperation(STObject const& hookSetObj)
bool hasHash = hookSetObj.isFieldPresent(sfHookHash);
bool hasCode = hookSetObj.isFieldPresent(sfCreateCode);
if (hasHash && hasCode) // Both HookHash and CreateCode: invalid
bool invalidHookOn = ctx.rules.enabled(featureHookOnV2_1) &&
hookSetObj.isFieldPresent(sfHookOnOutgoing) !=
hookSetObj.isFieldPresent(sfHookOnIncoming);
if ((hasHash && hasCode) || invalidHookOn) // Both HookHash and CreateCode
// or invalid HookOn: invalid
return hsoINVALID;
else if (hasHash) // Hookhash only: install
return hsoINSTALL;
@@ -244,6 +249,62 @@ SetHook::inferOperation(STObject const& hookSetObj)
: hsoUPDATE;
}
bool
validateHookOn(SetHookCtx& ctx, STObject const& hookSetObj)
{
if (!hookSetObj.isFieldPresent(sfHookOn))
{
if (!ctx.rules.enabled(featureHookOnV2) &&
!ctx.rules.enabled(featureHookOnV2_1))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")[" << HS_ACC()
<< "]: Malformed transaction: SetHook must include "
"sfHookOn before featureHookOnV2_1 is enabled.";
return false;
}
if (!hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
!hookSetObj.isFieldPresent(sfHookOnIncoming))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")[" << HS_ACC()
<< "]: Malformed transaction: SetHook must include "
"sfHookOnOutgoing and sfHookOnIncoming "
"when creating a new hook without sfHookOn.";
return false;
}
auto const outgoing = hookSetObj.getFieldH256(sfHookOnOutgoing);
auto const incoming = hookSetObj.getFieldH256(sfHookOnIncoming);
if (outgoing == incoming)
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")[" << HS_ACC()
<< "]: Malformed transaction: SetHook outgoing and "
"incoming hookon must be different.";
return false;
}
return true;
}
else
{
if (hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
hookSetObj.isFieldPresent(sfHookOnIncoming))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")[" << HS_ACC()
<< "]: Malformed transaction: SetHook must no"
"include sfHookOnOutgoing and sfHookOnIncoming "
"when creating a new hook with sfHookOn.";
return false;
}
}
return true;
}
// This is a context-free validation, it does not take into account the current
// state of the ledger returns < valid, instruction count > may throw
// overflow_error
@@ -254,7 +315,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
? hookSetObj.getFieldU32(sfFlags)
: 0;
switch (inferOperation(hookSetObj))
switch (inferOperation(ctx, hookSetObj))
{
case hsoNOOP: {
return true;
@@ -363,6 +424,13 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
// hookon may be present if the user so chooses
// flags may be present if the user so chooses
if (ctx.rules.enabled(featureHookOnV2_1) &&
(hookSetObj.isFieldPresent(sfHookOn) ||
hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
hookSetObj.isFieldPresent(sfHookOnIncoming)) &&
!validateHookOn(ctx, hookSetObj))
return false;
return true;
}
@@ -407,6 +475,13 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
// hookon may be present if the user so chooses
// flags may be present if the user so chooses
if (ctx.rules.enabled(featureHookOnV2_1) &&
(hookSetObj.isFieldPresent(sfHookOn) ||
hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
hookSetObj.isFieldPresent(sfHookOnIncoming)) &&
!validateHookOn(ctx, hookSetObj))
return false;
return true;
}
@@ -456,56 +531,8 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
}
// validate sfHookOn
if (!hookSetObj.isFieldPresent(sfHookOn))
{
if (!ctx.rules.enabled(featureHookOnV2))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")["
<< HS_ACC()
<< "]: Malformed transaction: SetHook must include "
"sfHookOn before featureHookOnV2 is enabled.";
return false;
}
if (!hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
!hookSetObj.isFieldPresent(sfHookOnIncoming))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")["
<< HS_ACC()
<< "]: Malformed transaction: SetHook must include "
"sfHookOnOutgoing and sfHookOnIncoming "
"when creating a new hook without sfHookOn.";
return false;
}
auto const outgoing = hookSetObj.getFieldH256(sfHookOnOutgoing);
auto const incoming = hookSetObj.getFieldH256(sfHookOnIncoming);
if (outgoing == incoming)
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")["
<< HS_ACC()
<< "]: Malformed transaction: SetHook outgoing and "
"incoming hookon must be different.";
return false;
}
}
else
{
if (hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
hookSetObj.isFieldPresent(sfHookOnIncoming))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")["
<< HS_ACC()
<< "]: Malformed transaction: SetHook must no"
"include sfHookOnOutgoing and sfHookOnIncoming "
"when creating a new hook with sfHookOn.";
return false;
}
}
if (!validateHookOn(ctx, hookSetObj))
return false;
// validate sfHookCanEmit
// HookCanEmit field is an optional field for backward compatibility
@@ -1405,7 +1432,7 @@ SetHook::setHook()
HookSetOperation op = hsoNOOP;
if (hookSetObj)
op = inferOperation(hookSetObj->get());
op = inferOperation(ctx, hookSetObj->get());
// these flags are not able to be passed onto the ledger object
int newFlags = 0;
@@ -1627,10 +1654,23 @@ SetHook::setHook()
newHook.setFieldH256(sfHookNamespace, *newNamespace);
}
if (ctx.rules.enabled(featureHookOnV2_1))
{
// sanity check
if (newHookOn && (newHookOnOutgoing || newHookOnIncoming))
return tecINTERNAL; // LCOV_EXCL_LINE
if ((!newHookOnOutgoing && newHookOnIncoming) ||
(newHookOnOutgoing && !newHookOnIncoming))
return tecINTERNAL; // LCOV_EXCL_LINE
}
// set the hookon field if it differs from definition
if (newHookOn)
{
if (*defHookOn == *newHookOn)
if ((!view().rules().enabled(featureHookOnV2_1) ||
defHookOn.has_value()) &&
*defHookOn == *newHookOn)
{
if (newHook.isFieldPresent(sfHookOn))
newHook.makeFieldAbsent(sfHookOn);
@@ -1665,6 +1705,22 @@ SetHook::setHook()
sfHookOnIncoming, *newHookOnIncoming);
}
if (ctx.rules.enabled(featureHookOnV2_1))
{
if (newHookOn)
{
if (newHook.isFieldPresent(sfHookOnIncoming))
newHook.makeFieldAbsent(sfHookOnIncoming);
if (newHook.isFieldPresent(sfHookOnOutgoing))
newHook.makeFieldAbsent(sfHookOnOutgoing);
}
if (newHookOnOutgoing || newHookOnIncoming)
{
if (newHook.isFieldPresent(sfHookOn))
newHook.makeFieldAbsent(sfHookOn);
}
}
// set the hookcanemit field if it differs from definition
if (newHookCanEmit)
{
@@ -1843,7 +1899,8 @@ SetHook::setHook()
newHookDef->setFieldH256(sfHookHash, *createHookHash);
// only HookOn or (HookOnOutgoing and HookOnIncoming)
if (!view().rules().enabled(featureHookOnV2) ||
if ((!view().rules().enabled(featureHookOnV2) &&
!view().rules().enabled(featureHookOnV2_1)) ||
(!newHookOnOutgoing && !newHookOnIncoming))
newHookDef->setFieldH256(sfHookOn, *newHookOn);
else
+1 -1
View File
@@ -86,7 +86,7 @@ public:
calculateBaseFee(ReadView const& view, STTx const& tx);
static HookSetOperation
inferOperation(STObject const& hookSetObj);
inferOperation(SetHookCtx& ctx, STObject const& hookSetObj);
static HookSetValidation
validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj);
-6
View File
@@ -30,12 +30,6 @@
namespace ripple {
TxConsequences
SetRemarks::makeTxConsequences(PreflightContext const& ctx)
{
return TxConsequences{ctx.tx, TxConsequences::normal};
}
NotTEC
SetRemarks::validateRemarks(STArray const& remarks, beast::Journal const& j)
{
+1 -4
View File
@@ -30,7 +30,7 @@ namespace ripple {
class SetRemarks : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit SetRemarks(ApplyContext& ctx) : Transactor(ctx)
{
@@ -39,9 +39,6 @@ public:
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
+1 -5
View File
@@ -481,11 +481,7 @@ transferHelper(
}
// Create the account.
std::uint32_t const seqno{
psb.rules().enabled(featureXahauGenesis)
? psb.info().parentCloseTime.time_since_epoch().count()
: psb.rules().enabled(featureDeletableAccounts) ? psb.seq()
: 1};
std::uint32_t const seqno = newAccountSeqNo(psb);
sleDst = std::make_shared<SLE>(dstK);
sleDst->setAccountID(sfAccount, dst);
+18
View File
@@ -1266,6 +1266,24 @@ deleteAMMTrustLine(
std::optional<AccountID> const& ammAccountID,
beast::Journal j);
[[nodiscard]] inline std::uint32_t
newAccountSeqNo(ReadView const& view)
{
auto const time = view.info().parentCloseTime.time_since_epoch().count();
return view.rules().enabled(featureXahauGenesis)
// TEQU:
// When creating accounts in GenesisLedger, we previously set
// the account Sequence to 0. However, since this conflicts with
// the PseudoAccount requirements, we are changing it to 1.
// There will be no impact on networks that are already running,
// and for future networks, there won't be any impact unless you
// create accounts via GenesisLedger.
// This specifically addresses an issue that comes up during
// unittests.
? (time == 0 ? 1 : time)
: view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1;
}
} // namespace ripple
#endif
+31 -91
View File
@@ -22,12 +22,10 @@
#include <xrpld/app/main/Application.h>
#include <xrpld/app/misc/AmendmentTable.h>
#include <xrpld/app/misc/NetworkOPs.h>
#include <xrpld/core/Config.h>
#include <xrpld/rpc/detail/TransactionSign.h>
#include <xrpl/json/json_value.h>
#include <xrpl/json/json_writer.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/RPCErr.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
@@ -35,14 +33,6 @@
#include <magic_enum.hpp>
#include <sstream>
#define MAGIC_ENUM(x, _min, _max) \
template <> \
struct magic_enum::customize::enum_range<x> \
{ \
static constexpr int min = _min; \
static constexpr int max = _max; \
};
#define MAGIC_ENUM_16(x) \
template <> \
struct magic_enum::customize::enum_range<x> \
@@ -58,15 +48,6 @@
static constexpr bool is_flags = true; \
};
MAGIC_ENUM(ripple::SerializedTypeID, -2, 10004);
MAGIC_ENUM(ripple::LedgerEntryType, 0, 255);
MAGIC_ENUM(ripple::TELcodes, -399, 300);
MAGIC_ENUM(ripple::TEMcodes, -299, -200);
MAGIC_ENUM(ripple::TEFcodes, -199, -100);
MAGIC_ENUM(ripple::TERcodes, -99, -1);
MAGIC_ENUM(ripple::TEScodes, 0, 1);
MAGIC_ENUM(ripple::TECcodes, 100, 255);
MAGIC_ENUM_16(ripple::TxType);
MAGIC_ENUM_FLAG(ripple::UniversalFlags);
MAGIC_ENUM_FLAG(ripple::AccountSetFlags);
MAGIC_ENUM_FLAG(ripple::OfferCreateFlags);
@@ -192,24 +173,19 @@ private:
ret[jss::TYPES]["Done"] = -1;
std::map<int32_t, std::string> type_map{{-1, "Done"}};
for (auto const& entry : magic_enum::enum_entries<SerializedTypeID>())
for (auto const& [rawName, typeValue] : sTypeMap)
{
const auto name = entry.second;
std::string type_name =
translate(name.data() + 4 /* remove STI_ */);
int32_t type_value = static_cast<int32_t>(entry.first);
ret[jss::TYPES][type_name] = type_value;
type_map[type_value] = type_name;
std::string typeName =
translate(std::string(rawName).substr(4) /* remove STI_ */);
ret[jss::TYPES][typeName] = typeValue;
type_map[typeValue] = typeName;
}
ret[jss::LEDGER_ENTRY_TYPES] = Json::objectValue;
ret[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
for (auto const& entry : magic_enum::enum_entries<LedgerEntryType>())
for (auto const& f : LedgerFormats::getInstance())
{
const auto name = entry.second;
std::string type_name = translate(name.data() + 2 /* remove lt_ */);
int32_t type_value = static_cast<int32_t>(entry.first);
ret[jss::LEDGER_ENTRY_TYPES][type_name] = type_value;
ret[jss::LEDGER_ENTRY_TYPES][f.getName()] = f.getType();
}
ret[jss::FIELDS] = Json::arrayValue;
@@ -326,71 +302,16 @@ private:
}
ret[jss::TRANSACTION_RESULTS] = Json::objectValue;
for (auto const& entry : magic_enum::enum_entries<TELcodes>())
for (auto const& [code, terInfo] : transResults())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
ret[jss::TRANSACTION_RESULTS][terInfo.first] = code;
}
for (auto const& entry : magic_enum::enum_entries<TEMcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TEFcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TERcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TEScodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TECcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
auto const translate_tt = [](std::string inp) -> std::string {
if (inp == "Amendment")
return "EnableAmendment";
if (inp == "Fee")
return "SetFee";
if (inp == "PaychanClaim")
return "PaymentChannelClaim";
if (inp == "PaychanCreate")
return "PaymentChannelCreate";
if (inp == "PaychanFund")
return "PaymentChannelFund";
if (inp == "RegularKeySet")
return "SetRegularKey";
if (inp == "HookSet")
return "SetHook";
if (inp == "RemarksSet")
return "SetRemarks";
return inp;
};
ret[jss::TRANSACTION_TYPES] = Json::objectValue;
ret[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
for (auto const& entry : magic_enum::enum_entries<TxType>())
for (auto const& f : TxFormats::getInstance())
{
const auto name = entry.second;
std::string type_name = translate_tt(translate(name.data() + 2));
int32_t type_value = static_cast<int32_t>(entry.first);
ret[jss::TRANSACTION_TYPES][type_name] = type_value;
ret[jss::TRANSACTION_TYPES][f.getName()] = f.getType();
}
// Transaction Flags:
@@ -545,6 +466,25 @@ doServerDefinitions(RPC::JsonContext& context)
features[to_string(h)][jss::majority] =
t.time_since_epoch().count();
// getJson's enabled is on-ledger; [features] also apply here.
for (auto const& name : features.getMemberNames())
{
Json::Value& entry = features[name];
entry[jss::ledger_enabled] = entry[jss::enabled].asBool();
}
for (auto const& h : context.app.config().features)
{
Json::Value& entry = features[to_string(h)];
if (!entry.isMember(jss::name))
{
if (auto const fname = featureToName(h); !fname.empty())
entry[jss::name] = fname;
}
if (!entry.isMember(jss::ledger_enabled))
entry[jss::ledger_enabled] = false;
entry[jss::enabled] = true;
}
lastFeatures = features;
{
const std::string out = Json::FastWriter().write(features);