Compare commits

..

11 Commits

Author SHA1 Message Date
Denis Angell
9df80a90d0 fix weak tsh 2024-01-19 15:42:35 +01:00
RichardAH
f21d3e1e97 Merge pull request #260 from Xahau/emit_guard
Fix: EmittedTxn Reliability
2024-01-19 14:05:06 +01:00
Denis Angell
858055c811 clang-format 2024-01-19 11:07:22 +01:00
Denis Angell
5d66f17574 add test 2024-01-19 11:05:45 +01:00
Denis Angell
00328cb782 Merge branch 'dev' into emit_guard 2024-01-19 10:24:34 +01:00
Richard Holland
fdf7ea4174 dbg inject clang + permission check 2024-01-17 18:15:17 +00:00
Richard Holland
7877ed9704 debug txn injector 2024-01-17 18:07:05 +00:00
Richard Holland
17ccec9ac5 Add additional checks for emitted txns 2024-01-17 15:39:02 +00:00
RichardAH
de522ac4ae Merge pull request #255 from Xahau/candidate
Candidate/release/sync
2023-12-29 22:18:04 +01:00
Wietse Wind
66ee96d456 Build on release after all 2023-12-29 15:43:33 +01:00
Wietse Wind
b476aea55b Do not auto build on release 2023-12-29 15:39:48 +01:00
13 changed files with 373 additions and 14 deletions

View File

@@ -30,6 +30,7 @@
#include <boost/circular_buffer.hpp> #include <boost/circular_buffer.hpp>
#include <boost/intrusive/set.hpp> #include <boost/intrusive/set.hpp>
#include <optional> #include <optional>
#include <vector>
namespace ripple { namespace ripple {
@@ -56,7 +57,14 @@ class Config;
*/ */
class TxQ class TxQ
{ {
private:
std::mutex debugTxInjectMutex;
std::vector<STTx> debugTxInjectQueue;
public: public:
void
debugTxInject(STTx const& txn);
/// Fee level for single-signed reference transaction. /// Fee level for single-signed reference transaction.
static constexpr FeeLevel64 baseLevel{256}; static constexpr FeeLevel64 baseLevel{256};

View File

@@ -93,6 +93,13 @@ increase(FeeLevel64 level, std::uint32_t increasePercent)
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void
TxQ::debugTxInject(STTx const& txn)
{
const std::lock_guard<std::mutex> _(debugTxInjectMutex);
debugTxInjectQueue.push_back(txn);
}
std::size_t std::size_t
TxQ::FeeMetrics::update( TxQ::FeeMetrics::update(
Application& app, Application& app,
@@ -1444,6 +1451,32 @@ TxQ::accept(Application& app, OpenView& view)
auto const metricsSnapshot = feeMetrics_.getSnapshot(); auto const metricsSnapshot = feeMetrics_.getSnapshot();
// try to inject any debug txns waiting in the debug queue
{
std::unique_lock<std::mutex> trylock(
TxQ::debugTxInjectMutex, std::try_to_lock);
if (trylock.owns_lock() && !debugTxInjectQueue.empty())
{
// pop everything
for (STTx const& txn : debugTxInjectQueue)
{
auto txnHash = txn.getTransactionID();
app.getHashRouter().setFlags(txnHash, SF_EMITTED | SF_PRIVATE2);
auto const& emitted =
const_cast<ripple::STTx&>(txn).downcast<STObject>();
auto s = std::make_shared<ripple::Serializer>();
emitted.add(*s);
view.rawTxInsert(txnHash, std::move(s), nullptr);
ledgerChanged = true;
}
debugTxInjectQueue.clear();
}
}
// Inject emitted transactions if any // Inject emitted transactions if any
if (view.rules().enabled(featureHooks)) if (view.rules().enabled(featureHooks))
do do

View File

@@ -648,6 +648,43 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx)
if (ctx.view.txExists(ctx.tx.getTransactionID())) if (ctx.view.txExists(ctx.tx.getTransactionID()))
return tefALREADY; return tefALREADY;
if (hook::isEmittedTxn(ctx.tx) && ctx.view.rules().enabled(featureHooks) &&
ctx.view.rules().enabled(fixXahauV2))
{
// check if the emitted txn exists on ledger and is in the emission
// directory if not that's a re-apply so discard
auto const kl = keylet::emittedTxn(ctx.tx.getTransactionID());
auto const sleE = ctx.view.read(kl);
if (!sleE)
return tefNONDIR_EMIT;
// lookup the page
uint64_t const page = sleE->getFieldU64(sfOwnerNode);
auto node = ctx.view.read(keylet::page(keylet::emittedDir(), page));
if (!node)
{
JLOG(ctx.j.warn())
<< "applyTransaction: orphaned emitted txn detected. keylet="
<< to_string(kl.key);
// RH TODO: work out how to safely delete the object
return tefNONDIR_EMIT;
}
auto entries = node->getFieldV256(sfIndexes);
auto it = std::find(entries.begin(), entries.end(), kl.key);
if (entries.end() == it)
{
JLOG(ctx.j.warn()) << "applyTransaction: orphaned emitted txn "
"detected (2). keylet="
<< to_string(kl.key);
// RH TODO: work out how to safely delete the object
return tefNONDIR_EMIT;
}
}
return tesSUCCESS; return tesSUCCESS;
} }
@@ -1437,14 +1474,15 @@ Transactor::addWeakTSHFromSandbox(detail::ApplyViewBase const& pv)
TER TER
Transactor::doTSH( Transactor::doTSH(
bool strong, // only strong iff true, only weak iff false bool strong, // only strong iff true, only weak iff false
std::vector<std::pair<AccountID, bool>> tsh,
hook::HookStateMap& stateMap, hook::HookStateMap& stateMap,
std::vector<hook::HookResult>& results, std::vector<hook::HookResult>& results,
std::shared_ptr<STObject const> const& provisionalMeta) std::shared_ptr<STObject const> const& provisionalMeta)
{ {
auto& view = ctx_.view(); auto& view = ctx_.view();
std::vector<std::pair<AccountID, bool>> tsh = // std::vector<std::pair<AccountID, bool>> tsh =
hook::getTransactionalStakeHolders(ctx_.tx, view); // hook::getTransactionalStakeHolders(ctx_.tx, view);
// add the extra TSH marked out by the specific transactor (if applicable) // add the extra TSH marked out by the specific transactor (if applicable)
if (!strong) if (!strong)
@@ -1698,6 +1736,8 @@ Transactor::operator()()
// application to the ledger // application to the ledger
std::map<AccountID, std::set<uint256>> aawMap; std::map<AccountID, std::set<uint256>> aawMap;
std::vector<std::pair<AccountID, bool>> tsh = hook::getTransactionalStakeHolders(ctx_.tx, ctx_.view());
// Pre-application (Strong TSH) Hooks are executed here // Pre-application (Strong TSH) Hooks are executed here
// These TSH have the right to rollback. // These TSH have the right to rollback.
// Weak TSH and callback are executed post-application. // Weak TSH and callback are executed post-application.
@@ -1726,7 +1766,7 @@ Transactor::operator()()
// (who have the right to rollback the txn), any weak TSH will be // (who have the right to rollback the txn), any weak TSH will be
// executed after doApply has been successful (callback as well) // executed after doApply has been successful (callback as well)
result = doTSH(true, stateMap, hookResults, {}); result = doTSH(true, tsh, stateMap, hookResults, {});
} }
// write state if all chains executed successfully // write state if all chains executed successfully
@@ -1969,7 +2009,7 @@ Transactor::operator()()
hook::HookStateMap stateMap; hook::HookStateMap stateMap;
std::vector<hook::HookResult> weakResults; std::vector<hook::HookResult> weakResults;
doTSH(false, stateMap, weakResults, proMeta); doTSH(false, tsh, stateMap, weakResults, proMeta);
// execute any hooks that nominated for 'again as weak' // execute any hooks that nominated for 'again as weak'
for (auto const& [accID, hookHashes] : aawMap) for (auto const& [accID, hookHashes] : aawMap)

View File

@@ -188,6 +188,7 @@ protected:
TER TER
doTSH( doTSH(
bool strong, // only do strong TSH iff true, otheriwse only weak bool strong, // only do strong TSH iff true, otheriwse only weak
std::vector<std::pair<AccountID, bool>> tsh,
hook::HookStateMap& stateMap, hook::HookStateMap& stateMap,
std::vector<hook::HookResult>& result, std::vector<hook::HookResult>& result,
std::shared_ptr<STObject const> const& provisionalMeta); std::shared_ptr<STObject const> const& provisionalMeta);

View File

@@ -74,7 +74,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how // 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 // 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. // the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 66; static constexpr std::size_t numFeatures = 67;
/** Amendments that this server supports and the default voting behavior. /** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated Whether they are enabled depends on the Rules defined in the validated
@@ -354,6 +354,7 @@ extern uint256 const featureImport;
extern uint256 const featureXahauGenesis; extern uint256 const featureXahauGenesis;
extern uint256 const featureHooksUpdate1; extern uint256 const featureHooksUpdate1;
extern uint256 const fixXahauV1; extern uint256 const fixXahauV1;
extern uint256 const fixXahauV2;
} // namespace ripple } // namespace ripple

View File

@@ -183,6 +183,7 @@ enum TEFcodes : TERUnderlyingType {
tefNFTOKEN_IS_NOT_TRANSFERABLE, tefNFTOKEN_IS_NOT_TRANSFERABLE,
tefPAST_IMPORT_SEQ, tefPAST_IMPORT_SEQ,
tefPAST_IMPORT_VL_SEQ, tefPAST_IMPORT_VL_SEQ,
tefNONDIR_EMIT,
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -460,7 +460,7 @@ REGISTER_FEATURE(Import, Supported::yes, VoteBehavior::De
REGISTER_FEATURE(XahauGenesis, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FEATURE(XahauGenesis, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(HooksUpdate1, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FEATURE(HooksUpdate1, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixXahauV1, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FIX (fixXahauV1, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixXahauV2, Supported::yes, VoteBehavior::DefaultNo);
// The following amendments are obsolete, but must remain supported // The following amendments are obsolete, but must remain supported
// because they could potentially get enabled. // because they could potentially get enabled.

View File

@@ -115,6 +115,7 @@ transResults()
MAKE_ERROR(tefTOO_BIG, "Transaction affects too many items."), MAKE_ERROR(tefTOO_BIG, "Transaction affects too many items."),
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."), MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."), MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."),
MAKE_ERROR(tefNONDIR_EMIT, "An emitted txn was injected into the ledger without a corresponding directory entry."),
MAKE_ERROR(telLOCAL_ERROR, "Local failure."), MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."), MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),

View File

@@ -353,11 +353,12 @@ JSS(ident); // in: AccountCurrencies, AccountInfo,
// OwnerInfo // OwnerInfo
JSS(ignore_default); // in: AccountLines JSS(ignore_default); // in: AccountLines
JSS(inLedger); // out: tx/Transaction JSS(inLedger); // out: tx/Transaction
JSS(inbound); // out: PeerImp JSS(in_queue);
JSS(index); // in: LedgerEntry, DownloadShard JSS(inbound); // out: PeerImp
// out: STLedgerEntry, JSS(index); // in: LedgerEntry, DownloadShard
// LedgerEntry, TxHistory, LedgerData // out: STLedgerEntry,
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo // LedgerEntry, TxHistory, LedgerData
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
JSS(initial_sync_duration_us); JSS(initial_sync_duration_us);
JSS(internal_command); // in: Internal JSS(internal_command); // in: Internal
JSS(invalid_API_version); // out: Many, when a request has an invalid JSS(invalid_API_version); // out: Many, when a request has an invalid

View File

@@ -141,6 +141,8 @@ doCrawlShards(RPC::JsonContext&);
Json::Value Json::Value
doStop(RPC::JsonContext&); doStop(RPC::JsonContext&);
Json::Value Json::Value
doInject(RPC::JsonContext&);
Json::Value
doSubmit(RPC::JsonContext&); doSubmit(RPC::JsonContext&);
Json::Value Json::Value
doSubmitMultiSigned(RPC::JsonContext&); doSubmitMultiSigned(RPC::JsonContext&);

View File

@@ -20,6 +20,7 @@
#include <ripple/app/ledger/LedgerMaster.h> #include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/misc/HashRouter.h> #include <ripple/app/misc/HashRouter.h>
#include <ripple/app/misc/Transaction.h> #include <ripple/app/misc/Transaction.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/tx/apply.h> #include <ripple/app/tx/apply.h>
#include <ripple/net/RPCErr.h> #include <ripple/net/RPCErr.h>
#include <ripple/protocol/ErrorCodes.h> #include <ripple/protocol/ErrorCodes.h>
@@ -39,6 +40,51 @@ getFailHard(RPC::JsonContext const& context)
context.params["fail_hard"].asBool()); context.params["fail_hard"].asBool());
} }
// {
// tx_blob: serialized tx
// }
// Only for debug use!!!!
Json::Value
doInject(RPC::JsonContext& context)
{
if (context.role != Role::ADMIN)
return RPC::make_error(
rpcNOT_SUPPORTED, "Signing is not supported by this server.");
if (context.role != Role::ADMIN)
return rpcError(rpcNO_PERMISSION);
Json::Value jvResult;
auto ret = strUnHex(context.params[jss::tx_blob].asString());
if (!ret || !ret->size())
return rpcError(rpcINVALID_PARAMS);
SerialIter sitTrans(makeSlice(*ret));
std::shared_ptr<STTx const> stpTrans;
try
{
stpTrans = std::make_shared<STTx const>(std::ref(sitTrans));
}
catch (std::exception& e)
{
jvResult[jss::error] = "invalidTransaction";
jvResult[jss::error_exception] = e.what();
jvResult[jss::in_queue] = false;
return jvResult;
}
context.app.getTxQ().debugTxInject(*stpTrans);
jvResult[jss::tx_json] = stpTrans->getJson(JsonOptions::none);
jvResult[jss::in_queue] = true;
return jvResult;
}
// { // {
// tx_json: <object>, // tx_json: <object>,
// secret: <secret> // secret: <secret>

View File

@@ -141,6 +141,7 @@ Handler const handlerArray[]{
{"ripple_path_find", byRef(&doRipplePathFind), Role::USER, NO_CONDITION}, {"ripple_path_find", byRef(&doRipplePathFind), Role::USER, NO_CONDITION},
{"sign", byRef(&doSign), Role::USER, NO_CONDITION}, {"sign", byRef(&doSign), Role::USER, NO_CONDITION},
{"sign_for", byRef(&doSignFor), Role::USER, NO_CONDITION}, {"sign_for", byRef(&doSignFor), Role::USER, NO_CONDITION},
{"inject", byRef(&doInject), Role::USER, NEEDS_CURRENT_LEDGER},
{"submit", byRef(&doSubmit), Role::USER, NEEDS_CURRENT_LEDGER}, {"submit", byRef(&doSubmit), Role::USER, NEEDS_CURRENT_LEDGER},
{"submit_multisigned", {"submit_multisigned",
byRef(&doSubmitMultiSigned), byRef(&doSubmitMultiSigned),

View File

@@ -18,6 +18,9 @@
*/ */
//============================================================================== //==============================================================================
#include <ripple/app/misc/HashRouter.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/tx/apply.h>
#include <ripple/protocol/Feature.h> #include <ripple/protocol/Feature.h>
#include <ripple/protocol/PayChan.h> #include <ripple/protocol/PayChan.h>
#include <ripple/protocol/jss.h> #include <ripple/protocol/jss.h>
@@ -4331,7 +4334,220 @@ private:
} }
void void
testWithFeats(FeatureBitset features) testEmittedTxnReliability(FeatureBitset features)
{
testcase("emitted txn reliability");
using namespace test::jtx;
using namespace std::literals;
test::jtx::Env env{
*this,
network::makeNetworkConfig(21337, "10", "1000000", "200000"),
features};
auto const account = Account("alice");
auto const dest = Account("bob");
env.fund(XRP(1000), account, dest);
env.close();
auto setHook = [](test::jtx::Account const& account) {
std::string const createCodeHex =
"0061736D0100000001350860057F7F7F7F7F017E60017F017E60047F7F7F7F"
"017E60037F7F7E017E60027F7F017E60037F7F7F017E60027F7F017F600001"
"7E02CD010D03656E76057472616365000003656E760C6574786E5F72657365"
"727665000103656E760A7574696C5F6163636964000203656E760974726163"
"655F6E756D000303656E760C686F6F6B5F6163636F756E74000403656E760A"
"6F74786E5F6669656C64000503656E7608726F6C6C6261636B000303656E76"
"025F67000603656E7606616363657074000303656E760A6C65646765725F73"
"6571000703656E760C6574786E5F64657461696C73000403656E760D657478"
"6E5F6665655F62617365000403656E7604656D697400020303020101050301"
"0002062B077F0141C08C040B7F004180080B7F0041B40C0B7F004180080B7F"
"0041C08C040B7F0041000B7F0041010B070F02046362616B000D04686F6F6B"
"000E0AE1930002AC800001017F230041106B220124002001200036020C41F6"
"0B411A41B70A4119410010001A200141106A240042000BAE930002017F017C"
"230041B0056B22012400200120003602AC0541E40B411141840A4110410010"
"001A410110011A200120014190056A411441940A4123100237038805418C08"
"410320012903880510031A200141F0046A411410041A2001200141D0046A41"
"144181802010053E02CC0441E409411120013402CC0410031A20012802CC04"
"411448044041910C4123420110061A0B200141003602C804200141013602C8"
"04200141003602C4040340418180B48178411510071A4100210020012802C8"
"04047F20012802C4044114480520000B4101710440200120012802C4042001"
"41F0046A6A2D000020012802C404200141D0046A6A2D0000463602C8042001"
"20012802C40441016A3602C4040C010B0B20012802C804450440419B08411D"
"420210081A0B200120014190046A413041818018100537038804200142C084"
"3D370380040240200129038804420852044041D00A41CE0041D40841CD0041"
"0010001A0C010B419F0B41C40041A10941C300410010001A200120012D0090"
"04410776047E427E052001310097042001310090044291A2C480B001834238"
"862001310091044230867C2001310092044228867C2001310093044220867C"
"2001310094044218867C2001310095044210867C2001310096044208867C7C"
"0B3703F803419008410A20012903F80310031A20012903F80342A08D065504"
"402001027E20012903F803B94400000040E17A843FA2220299440000000000"
"00E0436304402002B00C010B428080808080808080807F0B370380040B0B41"
"F609410D20012903800410031A2001200141E0016A3602DC01200120012903"
"80043703B801200141003602B401200141003602B001200110093E02AC0120"
"0141C0016A411410041A200141003A00AB0120012802DC0141123A00002001"
"2802DC0120012D00AB014108763A000120012802DC0120012D00AB013A0002"
"200120012802DC0141036A3602DC0120014180808080783602A40120014102"
"3A00A30120012802DC0120012D00A301410F7141206A3A000020012802DC01"
"20012802A4014118763A000120012802DC0120012802A4014110763A000220"
"012802DC0120012802A4014108763A000320012802DC0120012802A4013A00"
"04200120012802DC0141056A3602DC01200120012802B00136029C01200141"
"033A009B0120012802DC0120012D009B01410F7141206A3A000020012802DC"
"01200128029C014118763A000120012802DC01200128029C014110763A0002"
"20012802DC01200128029C014108763A000320012802DC01200128029C013A"
"0004200120012802DC0141056A3602DC012001410036029401200141043A00"
"930120012802DC0120012D009301410F7141206A3A000020012802DC012001"
"280294014118763A000120012802DC012001280294014110763A0002200128"
"02DC012001280294014108763A000320012802DC012001280294013A000420"
"0120012802DC0141056A3602DC01200120012802B40136028C012001410E3A"
"008B0120012802DC0120012D008B01410F7141206A3A000020012802DC0120"
"0128028C014118763A000120012802DC01200128028C014110763A00022001"
"2802DC01200128028C014108763A000320012802DC01200128028C013A0004"
"200120012802DC0141056A3602DC01200120012802AC0141016A3602840120"
"01411A3A00830120012802DC0141203A000020012802DC0120012D0083013A"
"000120012802DC012001280284014118763A000220012802DC012001280284"
"014110763A000320012802DC012001280284014108763A000420012802DC01"
"2001280284013A0005200120012802DC0141066A3602DC01200120012802AC"
"0141056A36027C2001411B3A007B20012802DC0141203A000020012802DC01"
"20012D007B3A000120012802DC01200128027C4118763A000220012802DC01"
"200128027C4110763A000320012802DC01200128027C4108763A0004200128"
"02DC01200128027C3A0005200120012802DC0141066A3602DC01200141013A"
"007A200120012903B80137037020012802DC0120012D007A410F7141E0006A"
"3A000020012802DC012001290370423888423F8342407D3C000120012802DC"
"01200129037042308842FF01833C000220012802DC01200129037042288842"
"FF01833C000320012802DC01200129037042208842FF01833C000420012802"
"DC01200129037042188842FF01833C000520012802DC012001290370421088"
"42FF01833C000620012802DC01200129037042088842FF01833C0007200128"
"02DC01200129037042FF01833C0008200120012802DC0141096A3602DC0120"
"0120012802DC0136026C200141083A006B2001420037036020012802DC0120"
"012D006B410F7141E0006A3A000020012802DC012001290360423888423F83"
"42407D3C000120012802DC01200129036042308842FF01833C000220012802"
"DC01200129036042288842FF01833C000320012802DC012001290360422088"
"42FF01833C000420012802DC01200129036042188842FF01833C0005200128"
"02DC01200129036042108842FF01833C000620012802DC0120012903604208"
"8842FF01833C000720012802DC01200129036042FF01833C00082001200128"
"02DC0141096A3602DC0120012802DC0141F3003A000020012802DC0141213A"
"000120012802DC01420037030220012802DC01420037030A20012802DC0142"
"0037031220012802DC014200370319200120012802DC0141236A3602DC0120"
"0141013A005F20012802DC0120012D005F4180016A3A000020012802DC0141"
"143A000120012802DC0120012903C00137030220012802DC0120012903C801"
"37030A20012802DC0120012802D001360212200120012802DC0141166A3602"
"DC01200141033A005E20012802DC0120012D005E4180016A3A000020012802"
"DC0141143A000120012802DC0120012903900537030220012802DC01200129"
"03980537030A20012802DC0120012802A005360212200120012802DC014116"
"6A3602DC01200120012802DC01418E02100A3703502001200141E0016A418E"
"02100B370348200141083A004720012001290348370338200128026C20012D"
"0047410F7141E0006A3A0000200128026C2001290338423888423F8342407D"
"3C0001200128026C200129033842308842FF01833C0002200128026C200129"
"033842288842FF01833C0003200128026C200129033842208842FF01833C00"
"04200128026C200129033842188842FF01833C0005200128026C2001290338"
"42108842FF01833C0006200128026C200129033842088842FF01833C000720"
"0128026C200129033842FF01833C00082001200128026C41096A36026C2001"
"200141106A4120200141E0016A418E02100C370308418008410B2001290308"
"10031A41B808411C420010081A200141B0056A240042000B0BBB0401004180"
"080BB304656D69745F726573756C7400726574006F74786E5F64726F707300"
"436172626F6E3A20496E636F6D696E67207472616E73616374696F6E004361"
"72626F6E3A20456D6974746564207472616E73616374696F6E00436172626F"
"6E3A204E6F6E2D787270207472616E73616374696F6E206465746563746564"
"2C2073656E64696E672064656661756C7420313030302064726F707320746F"
"207266436172626F6E00436172626F6E3A20585250207472616E7361637469"
"6F6E2064657465637465642C20636F6D707574696E6720312520746F207365"
"6E6420746F207266436172626F6E006163636F756E745F6669656C645F6C65"
"6E0064726F70735F746F5F73656E6400436172626F6E3A2073746172746564"
"0072504D68375069396374363939695A5554576179744A556F48634A376367"
"797A694B00436172626F6E3A2063616C6C6261636B2063616C6C65642E0022"
"436172626F6E3A204E6F6E2D787270207472616E73616374696F6E20646574"
"65637465642C2073656E64696E672064656661756C7420313030302064726F"
"707320746F207266436172626F6E220022436172626F6E3A20585250207472"
"616E73616374696F6E2064657465637465642C20636F6D707574696E672031"
"2520746F2073656E6420746F207266436172626F6E220022436172626F6E3A"
"2073746172746564220022436172626F6E3A2063616C6C6261636B2063616C"
"6C65642E2200436172626F6E3A2073664163636F756E74206669656C64206D"
"697373696E67212121";
Json::Value jhv = hso(createCodeHex);
jhv[jss::HookOn] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffbfff"
"ff";
Json::Value jv = ripple::test::jtx::hook(account, {{jhv}}, 0);
return jv;
};
env(setHook(account), HSFEE);
env.close();
// ttINVOKE
env(invoke::invoke(account), fee(XRP(1)), ter(tesSUCCESS));
env.close();
Blob txBlob;
auto meta = env.meta();
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
for (auto const& node : meta->getFieldArray(sfAffectedNodes))
{
SField const& metaType = node.getFName();
uint16_t nodeType = node.getFieldU16(sfLedgerEntryType);
if (metaType == sfCreatedNode && nodeType == ltEMITTED_TXN)
{
auto const& nf = const_cast<ripple::STObject&>(node)
.getField(sfNewFields)
.downcast<STObject>();
auto const& et = const_cast<ripple::STObject&>(nf)
.getField(sfEmittedTxn)
.downcast<STObject>();
txBlob = et.getSerializer().getData();
break;
}
}
env.close();
auto const preDest = env.balance(dest);
bool const withFix = env.current()->rules().enabled(fixXahauV2);
bool didApply;
TER terRes;
env.app().openLedger().modify([&](OpenView& view, beast::Journal j) {
auto const tx =
std::make_unique<STTx>(Slice{txBlob.data(), txBlob.size()});
std::tie(terRes, didApply) =
ripple::apply(env.app(), view, *tx, tapNONE, env.journal);
bool const applyResult = withFix ? false : true;
if (withFix)
{
BEAST_EXPECT(terRes == tefNONDIR_EMIT);
}
else
{
BEAST_EXPECT(terRes == tesSUCCESS);
}
BEAST_EXPECT(didApply == applyResult);
return didApply;
});
env.close();
auto const postDest = env.balance(dest);
auto const postValue = withFix ? XRP(0) : XRP(1);
BEAST_EXPECT(postDest == preDest + postValue);
for (size_t i = 0; i < 4; i++)
{
Json::Value params1;
params1[jss::tx_blob] = strHex(Slice{txBlob.data(), txBlob.size()});
auto const jrr1 = env.rpc("json", "inject", to_string(params1));
env.close();
}
auto const postDest1 = env.balance(dest);
auto const postValue1 = withFix ? XRP(0) : XRP(2);
BEAST_EXPECT(postDest1 == postDest + postValue1);
}
void
testTSH(FeatureBitset features)
{ {
testAccountSetTSH(features); testAccountSetTSH(features);
testAccountDeleteTSH(features); testAccountDeleteTSH(features);
@@ -4366,14 +4582,22 @@ private:
testURITokenCreateSellOfferTSH(features); testURITokenCreateSellOfferTSH(features);
} }
void
testEmittedTxn(FeatureBitset features)
{
testEmittedTxnReliability(features);
}
public: public:
void void
run() override run() override
{ {
using namespace test::jtx; using namespace test::jtx;
auto const sa = supported_amendments(); auto const sa = supported_amendments();
testWithFeats(sa - fixXahauV1); testTSH(sa - fixXahauV1);
testWithFeats(sa); testTSH(sa);
testEmittedTxn(sa - fixXahauV2);
testEmittedTxn(sa);
} }
}; };