mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
add transactional stakeholder code, not compiling
This commit is contained in:
@@ -47,6 +47,46 @@ namespace hook
|
|||||||
bool, // is modified from ledger value
|
bool, // is modified from ledger value
|
||||||
ripple::Blob>>>>; // the value
|
ripple::Blob>>>>; // the value
|
||||||
|
|
||||||
|
enum TSHFlags : uint8_t
|
||||||
|
{
|
||||||
|
tshNONE = 0b000,
|
||||||
|
tshROLLBACK = 0b001,
|
||||||
|
tshCOLLECT = 0b010,
|
||||||
|
};
|
||||||
|
|
||||||
|
using namespace ripple;
|
||||||
|
static const std::map<uint16_t, uint8_t> TSHAllowances =
|
||||||
|
{
|
||||||
|
{ttPAYMENT, tshROLLBACK },
|
||||||
|
{ttESCROW_CREATE, tshROLLBACK },
|
||||||
|
{ttESCROW_FINISH, tshROLLBACK },
|
||||||
|
{ttACCOUNT_SET, tshNONE },
|
||||||
|
{ttESCROW_CANCEL, tshCOLLECT },
|
||||||
|
{ttREGULAR_KEY_SET, tshNONE },
|
||||||
|
// {ttNICKNAME_SET, tshNONE },
|
||||||
|
{ttOFFER_CREATE, tshCOLLECT },
|
||||||
|
{ttOFFER_CANCEL, tshNONE },
|
||||||
|
// {ttCONTRACT, tshNONE },
|
||||||
|
{ttTICKET_CREATE, tshNONE },
|
||||||
|
// {ttSPINAL_TAP, tshNONE },
|
||||||
|
{ttSIGNER_LIST_SET, tshROLLBACK },
|
||||||
|
{ttPAYCHAN_CREATE, tshROLLBACK },
|
||||||
|
{ttPAYCHAN_FUND, tshCOLLECT },
|
||||||
|
{ttPAYCHAN_CLAIM, tshCOLLECT },
|
||||||
|
{ttCHECK_CREATE, tshROLLBACK },
|
||||||
|
{ttCHECK_CASH, tshROLLBACK },
|
||||||
|
{ttCHECK_CANCEL, tshCOLLECT },
|
||||||
|
{ttDEPOSIT_PREAUTH, tshROLLBACK },
|
||||||
|
{ttTRUST_SET, tshCOLLECT },
|
||||||
|
{ttACCOUNT_DELETE, tshROLLBACK },
|
||||||
|
{ttHOOK_SET, tshNONE }
|
||||||
|
// RH TODO: add NFT transactions here
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::pair<AccountID, bool>>
|
||||||
|
getTransactionalStakeHolders(STTx const& tx, ReadView const& rv);
|
||||||
|
|
||||||
namespace log
|
namespace log
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -152,19 +152,9 @@ Transactor::Transactor(ApplyContext& ctx)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
|
||||||
std::optional<AccountID>
|
|
||||||
getDestinationAccount(STTx const& tx)
|
|
||||||
{
|
|
||||||
if (tx.isFieldPresent(sfOwner))
|
|
||||||
return tx.getAccountID(sfOwner);
|
|
||||||
|
|
||||||
if (tx.isFieldPresent(sfDestination))
|
|
||||||
return tx.getAccountID(sfDestination);
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// RH NOTE: this only computes one chain at a time, so if there is a receiving side to a txn
|
||||||
|
// then it must seperately be computed by a second call here
|
||||||
FeeUnit64
|
FeeUnit64
|
||||||
Transactor::calculateHookChainFee(ReadView const& view, STTx const& tx, Keylet const& hookKeylet)
|
Transactor::calculateHookChainFee(ReadView const& view, STTx const& tx, Keylet const& hookKeylet)
|
||||||
{
|
{
|
||||||
@@ -210,7 +200,6 @@ Transactor::calculateHookChainFee(ReadView const& view, STTx const& tx, Keylet c
|
|||||||
}
|
}
|
||||||
|
|
||||||
return fee;
|
return fee;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FeeUnit64
|
FeeUnit64
|
||||||
@@ -255,13 +244,14 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
hookExecutionFee +=
|
hookExecutionFee +=
|
||||||
calculateHookChainFee(view, tx, keylet::hook(tx.getAccountID(sfAccount)));
|
calculateHookChainFee(view, tx, keylet::hook(tx.getAccountID(sfAccount)));
|
||||||
|
|
||||||
std::optional<AccountID>
|
// find any additional stakeholders whose hooks will be executed and charged to this transaction
|
||||||
destAccountID = getDestinationAccount(tx);
|
std::vector<std::pair<AccountID, bool>> tsh =
|
||||||
|
hook::getTransactionalStakeHolders(tx, view);
|
||||||
// if there is a receiving account then we also compute the fee for its hook chain
|
|
||||||
if (destAccountID)
|
for (auto& [tshAcc, canRollback] : tsh)
|
||||||
hookExecutionFee +=
|
if (canRollback)
|
||||||
calculateHookChainFee(view, tx, keylet::hook(*destAccountID));
|
hookExecutionFee +=
|
||||||
|
calculateHookChainFee(view, tx, keylet::hook(tshAcc));
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseFee + (signerCount * baseFee) + hookExecutionFee; // RH NOTE: hookExecutionFee = 0
|
return baseFee + (signerCount * baseFee) + hookExecutionFee; // RH NOTE: hookExecutionFee = 0
|
||||||
@@ -1118,17 +1108,29 @@ Transactor::operator()()
|
|||||||
rollback = executeHookChain(hooksSending, stateMap,
|
rollback = executeHookChain(hooksSending, stateMap,
|
||||||
sendResults, executedHookCount, accountID, ctx_, j_, result);
|
sendResults, executedHookCount, accountID, ctx_, j_, result);
|
||||||
|
|
||||||
// Next check if the Receiving account has as a hook that can be fired...
|
// Next check if there are any transactional stake holders whose hooks need to be executed
|
||||||
std::optional<AccountID>
|
if (!rollback)
|
||||||
destAccountID = getDestinationAccount(ctx_.tx);
|
|
||||||
|
|
||||||
if (!rollback && destAccountID)
|
|
||||||
{
|
{
|
||||||
auto const& hooksReceiving = ledger.read(keylet::hook(*destAccountID));
|
std::vector<std::pair<AccountID, bool>> tsh =
|
||||||
if (hooksReceiving && hooksReceiving->isFieldPresent(sfHooks))
|
hook::getTransactionalStakeHolders(ctx_.tx, ledger);
|
||||||
rollback =
|
|
||||||
executeHookChain(hooksReceiving, stateMap,
|
for (auto& [tshAccountID, canRollback] : tsh)
|
||||||
recvResults, executedHookCount, *destAccountID, ctx_, j_, result);
|
{
|
||||||
|
auto const& hooksReceiving = ledger.read(keylet::hook(tshAccountID));
|
||||||
|
if (hooksReceiving && hooksReceiving->isFieldPresent(sfHooks))
|
||||||
|
{
|
||||||
|
bool tshRollback =
|
||||||
|
executeHookChain(hooksReceiving, stateMap,
|
||||||
|
recvResults, executedHookCount, tshAccountID, ctx_, j_, result);
|
||||||
|
if (canRollback && tshRollback)
|
||||||
|
{
|
||||||
|
rollback = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//RH TODO: charge non-rollback tsh executions a fee here
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rollback && result != temMALFORMED)
|
if (rollback && result != temMALFORMED)
|
||||||
|
|||||||
@@ -154,6 +154,17 @@ public:
|
|||||||
static FeeUnit64
|
static FeeUnit64
|
||||||
calculateHookChainFee(ReadView const& view, STTx const& tx, Keylet const& hookKeylet);
|
calculateHookChainFee(ReadView const& view, STTx const& tx, Keylet const& hookKeylet);
|
||||||
|
|
||||||
|
|
||||||
|
// Returns a list of zero or more accounts which are
|
||||||
|
// not the originator of the transaction but which are
|
||||||
|
// stakeholders in the transaction. The bool parameter
|
||||||
|
// determines whether or not the specified account has
|
||||||
|
// permission for their hook/s to cause a rollback on
|
||||||
|
// the transaction.
|
||||||
|
static std::vector<std::pair<AccountID, bool>>
|
||||||
|
getTransactionalStakeHolders(STTx const& tx);
|
||||||
|
|
||||||
|
|
||||||
static TER
|
static TER
|
||||||
preclaim(PreclaimContext const& ctx)
|
preclaim(PreclaimContext const& ctx)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,6 +19,174 @@
|
|||||||
|
|
||||||
using namespace ripple;
|
using namespace ripple;
|
||||||
|
|
||||||
|
namespace hook
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<std::pair<AccountID, bool>>
|
||||||
|
getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!rv.rules().enabled(featureHooks))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (!tx.isFieldPresent(sfAccount))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::optional<AccountID> destAcc = tx.at(sfDestination);
|
||||||
|
std::optional<AccountID> otxnAcc = tx.at(sfAccount);
|
||||||
|
|
||||||
|
if (!otxnAcc)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
uint16_t tt = tx.getFieldU16(sfTransactionType);
|
||||||
|
|
||||||
|
uint8_t tsh = tshNONE;
|
||||||
|
if (auto const& found = hook::TSHAllowances.find(tt); found != hook::TSHAllowances.end())
|
||||||
|
tsh = found->second;
|
||||||
|
else
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::map<AccountID, std::pair<int, bool>> tshEntries;
|
||||||
|
|
||||||
|
int upto = 0;
|
||||||
|
|
||||||
|
bool canRollback = tsh & tshROLLBACK;
|
||||||
|
|
||||||
|
#define ADD_TSH(acc, rb)\
|
||||||
|
{\
|
||||||
|
auto acc_r = acc;\
|
||||||
|
if (acc_r != *otxnAcc)\
|
||||||
|
{\
|
||||||
|
if (tshEntries.find(acc_r) != tshEntries.end())\
|
||||||
|
{\
|
||||||
|
if (tshEntries[acc_r].second == false && rb)\
|
||||||
|
tshEntries[acc_r].second = true;\
|
||||||
|
}\
|
||||||
|
else\
|
||||||
|
tshEntries.emplace(acc_r, std::pair<int, bool>{upto++, rb});\
|
||||||
|
}\
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (tt)
|
||||||
|
{
|
||||||
|
|
||||||
|
// self transactions
|
||||||
|
case ttACCOUNT_SET:
|
||||||
|
case ttOFFER_CANCEL:
|
||||||
|
case ttREGULAR_KEY_SET:
|
||||||
|
case ttTICKET_CREATE:
|
||||||
|
case ttHOOK_SET:
|
||||||
|
case ttOFFER_CREATE: // this is handled seperately
|
||||||
|
//case ttCONTRACT: // not used
|
||||||
|
//case ttSPINAL_TAP: // not used
|
||||||
|
//case ttNICKNAME_SET: // not used
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ttDEPOSIT_PREAUTH:
|
||||||
|
{
|
||||||
|
if (!tx.isFieldPresent(sfAuthorize))
|
||||||
|
return {};
|
||||||
|
ADD_TSH(tx.getAccountID(sfAuthorize), canRollback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// simple two party transactions
|
||||||
|
case ttPAYMENT:
|
||||||
|
case ttESCROW_CREATE:
|
||||||
|
case ttCHECK_CREATE:
|
||||||
|
case ttACCOUNT_DELETE:
|
||||||
|
case ttPAYCHAN_CREATE:
|
||||||
|
{
|
||||||
|
ADD_TSH(*destAcc, canRollback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ttTRUST_SET:
|
||||||
|
{
|
||||||
|
if (!tx.isFieldPresent(sfLimitAmount))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto const& lim = tx.getFieldAmount(sfLimitAmount);
|
||||||
|
AccountID const& issuer = lim.getIssuer();
|
||||||
|
|
||||||
|
ADD_TSH(issuer, canRollback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ttESCROW_CANCEL:
|
||||||
|
case ttESCROW_FINISH:
|
||||||
|
{
|
||||||
|
if (!tx.isFieldPresent(sfOwner) || !tx.isFieldPresent(sfOfferSequence))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto escrow = rv.read(
|
||||||
|
keylet::escrow(tx.getAccountID(sfOwner), tx.getFieldU32(sfOfferSequence)));
|
||||||
|
|
||||||
|
if (!escrow)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
ADD_TSH(escrow->getAccountID(sfAccount), true);
|
||||||
|
ADD_TSH(escrow->getAccountID(sfDestination), canRollback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ttPAYCHAN_FUND:
|
||||||
|
case ttPAYCHAN_CLAIM:
|
||||||
|
{
|
||||||
|
if (!tx.isFieldPresent(sfChannel))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto chan = rv.read(Keylet {ltPAYCHAN, tx.getFieldH256(sfChannel)});
|
||||||
|
if (!chan)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
ADD_TSH(chan->getAccountID(sfAccount), true);
|
||||||
|
ADD_TSH(chan->getAccountID(sfDestination), canRollback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ttCHECK_CASH:
|
||||||
|
case ttCHECK_CANCEL:
|
||||||
|
{
|
||||||
|
if (!tx.isFieldPresent(sfCheckID))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto check = rv.read(Keylet {ltCHECK, tx.getFieldH256(sfCheckID)});
|
||||||
|
if (!check)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
ADD_TSH(check->getAccountID(sfAccount), true);
|
||||||
|
ADD_TSH(check->getAccountID(sfDestination), canRollback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the owners of accounts whose keys appear on a signer list are entitled to prevent their inclusion
|
||||||
|
case ttSIGNER_LIST_SET:
|
||||||
|
{
|
||||||
|
STArray const& signerEntries = tx.getFieldArray(sfSignerEntries);
|
||||||
|
for (auto const& e : signerEntries)
|
||||||
|
{
|
||||||
|
auto const& entryObj = dynamic_cast<STObject const*>(&e);
|
||||||
|
if (entryObj->isFieldPresent(sfAccount))
|
||||||
|
ADD_TSH(entryObj->getAccountID(sfAccount), canRollback);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<AccountID, bool>> ret {tshEntries.size()};
|
||||||
|
for (auto& [a, e] : tshEntries)
|
||||||
|
ret[e.first] = std::pair<AccountID, bool>{a, e.second};
|
||||||
|
|
||||||
|
return std::move(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
namespace hook_float
|
namespace hook_float
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -544,6 +544,25 @@ private:
|
|||||||
return jvRequest;
|
return jvRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fee [<txblob>]
|
||||||
|
Json::Value
|
||||||
|
parseFee(Json::Value const& jvParams)
|
||||||
|
{
|
||||||
|
Json::Value jvRequest(Json::objectValue);
|
||||||
|
|
||||||
|
if (jvParams.size() == 0)
|
||||||
|
{
|
||||||
|
return jvRequest;
|
||||||
|
}
|
||||||
|
else if (jvParams.size() > 1)
|
||||||
|
{
|
||||||
|
return rpcError(rpcINVALID_PARAMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
jvRequest[jss::tx_blob] = jvParams[0u].asString();
|
||||||
|
return jvRequest;
|
||||||
|
}
|
||||||
|
|
||||||
// get_counts [<min_count>]
|
// get_counts [<min_count>]
|
||||||
Json::Value
|
Json::Value
|
||||||
parseGetCounts(Json::Value const& jvParams)
|
parseGetCounts(Json::Value const& jvParams)
|
||||||
@@ -1313,6 +1332,7 @@ public:
|
|||||||
{"deposit_authorized", &RPCParser::parseDepositAuthorized, 2, 3},
|
{"deposit_authorized", &RPCParser::parseDepositAuthorized, 2, 3},
|
||||||
{"download_shard", &RPCParser::parseDownloadShard, 2, -1},
|
{"download_shard", &RPCParser::parseDownloadShard, 2, -1},
|
||||||
{"feature", &RPCParser::parseFeature, 0, 2},
|
{"feature", &RPCParser::parseFeature, 0, 2},
|
||||||
|
{"fee", &RPCParser::parseFee, 0, 1},
|
||||||
{"fetch_info", &RPCParser::parseFetchInfo, 0, 1},
|
{"fetch_info", &RPCParser::parseFetchInfo, 0, 1},
|
||||||
{"gateway_balances", &RPCParser::parseGatewayBalances, 1, -1},
|
{"gateway_balances", &RPCParser::parseGatewayBalances, 1, -1},
|
||||||
{"get_counts", &RPCParser::parseGetCounts, 0, 1},
|
{"get_counts", &RPCParser::parseGetCounts, 0, 1},
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ JSS(features); // out: Feature
|
|||||||
JSS(fee); // out: NetworkOPs, Peers
|
JSS(fee); // out: NetworkOPs, Peers
|
||||||
JSS(fee_base); // out: NetworkOPs
|
JSS(fee_base); // out: NetworkOPs
|
||||||
JSS(fee_div_max); // in: TransactionSign
|
JSS(fee_div_max); // in: TransactionSign
|
||||||
|
JSS(fee_hooks_feeunits); // out: Fee rpc call
|
||||||
JSS(fee_level); // out: AccountInfo
|
JSS(fee_level); // out: AccountInfo
|
||||||
JSS(fee_mult_max); // in: TransactionSign
|
JSS(fee_mult_max); // in: TransactionSign
|
||||||
JSS(fee_ref); // out: NetworkOPs
|
JSS(fee_ref); // out: NetworkOPs
|
||||||
|
|||||||
@@ -25,14 +25,64 @@
|
|||||||
#include <ripple/protocol/Feature.h>
|
#include <ripple/protocol/Feature.h>
|
||||||
#include <ripple/rpc/Context.h>
|
#include <ripple/rpc/Context.h>
|
||||||
#include <ripple/rpc/GRPCHandlers.h>
|
#include <ripple/rpc/GRPCHandlers.h>
|
||||||
|
#include <ripple/app/tx/applyHook.h>
|
||||||
|
#include <ripple/app/tx/impl/Transactor.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
Json::Value
|
Json::Value
|
||||||
doFee(RPC::JsonContext& context)
|
doFee(RPC::JsonContext& context)
|
||||||
{
|
{
|
||||||
auto result = context.app.getTxQ().doRPC(context.app);
|
Json::Value jvResult = context.app.getTxQ().doRPC(context.app);
|
||||||
if (result.type() == Json::objectValue)
|
if (jvResult.type() == Json::objectValue)
|
||||||
return result;
|
{
|
||||||
|
auto const& params(context.params);
|
||||||
|
if (params.isMember(jss::tx_blob))
|
||||||
|
{
|
||||||
|
auto ret = strUnHex(context.params[jss::tx_blob].asString());
|
||||||
|
|
||||||
|
if (!ret || !ret->size())
|
||||||
|
return rpcError(rpcINVALID_PARAMS);
|
||||||
|
|
||||||
|
SerialIter sitTrans(makeSlice(*ret));
|
||||||
|
|
||||||
|
std::unique_ptr<STTx const> stpTrans;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stpTrans = std::make_unique<STTx const>(std::ref(sitTrans));
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
jvResult[jss::error] = "invalidTransaction";
|
||||||
|
jvResult[jss::error_exception] = e.what();
|
||||||
|
return jvResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stpTrans->isFieldPresent(sfAccount))
|
||||||
|
{
|
||||||
|
jvResult[jss::error] = "invalidTransaction";
|
||||||
|
jvResult[jss::error_exception] = "No sfAccount specified";
|
||||||
|
return jvResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
FeeUnit64 hookFees =
|
||||||
|
Transactor::calculateHookChainFee(*(context.app.openLedger().current()),
|
||||||
|
*stpTrans, keylet::hook(stpTrans->getAccountID(sfAccount)));
|
||||||
|
|
||||||
|
auto const view = context.app.openLedger().current();
|
||||||
|
|
||||||
|
std::vector<std::pair<AccountID, bool>> tsh =
|
||||||
|
hook::getTransactionalStakeHolders(*stpTrans, *view);
|
||||||
|
|
||||||
|
for (auto const& [tshAccount, canRollback] : tsh)
|
||||||
|
if (canRollback)
|
||||||
|
hookFees +=
|
||||||
|
Transactor::calculateHookChainFee(*view, *stpTrans, keylet::hook(tshAccount));
|
||||||
|
|
||||||
|
jvResult[jss::fee_hooks_feeunits] = to_string(hookFees.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return jvResult;
|
||||||
|
}
|
||||||
assert(false);
|
assert(false);
|
||||||
RPC::inject_error(rpcINTERNAL, context.params);
|
RPC::inject_error(rpcINTERNAL, context.params);
|
||||||
return context.params;
|
return context.params;
|
||||||
|
|||||||
Reference in New Issue
Block a user