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
|
||||
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
|
||||
{
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
FeeUnit64
|
||||
@@ -255,13 +244,14 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
hookExecutionFee +=
|
||||
calculateHookChainFee(view, tx, keylet::hook(tx.getAccountID(sfAccount)));
|
||||
|
||||
std::optional<AccountID>
|
||||
destAccountID = getDestinationAccount(tx);
|
||||
|
||||
// if there is a receiving account then we also compute the fee for its hook chain
|
||||
if (destAccountID)
|
||||
hookExecutionFee +=
|
||||
calculateHookChainFee(view, tx, keylet::hook(*destAccountID));
|
||||
// find any additional stakeholders whose hooks will be executed and charged to this transaction
|
||||
std::vector<std::pair<AccountID, bool>> tsh =
|
||||
hook::getTransactionalStakeHolders(tx, view);
|
||||
|
||||
for (auto& [tshAcc, canRollback] : tsh)
|
||||
if (canRollback)
|
||||
hookExecutionFee +=
|
||||
calculateHookChainFee(view, tx, keylet::hook(tshAcc));
|
||||
}
|
||||
|
||||
return baseFee + (signerCount * baseFee) + hookExecutionFee; // RH NOTE: hookExecutionFee = 0
|
||||
@@ -1118,17 +1108,29 @@ Transactor::operator()()
|
||||
rollback = executeHookChain(hooksSending, stateMap,
|
||||
sendResults, executedHookCount, accountID, ctx_, j_, result);
|
||||
|
||||
// Next check if the Receiving account has as a hook that can be fired...
|
||||
std::optional<AccountID>
|
||||
destAccountID = getDestinationAccount(ctx_.tx);
|
||||
|
||||
if (!rollback && destAccountID)
|
||||
// Next check if there are any transactional stake holders whose hooks need to be executed
|
||||
if (!rollback)
|
||||
{
|
||||
auto const& hooksReceiving = ledger.read(keylet::hook(*destAccountID));
|
||||
if (hooksReceiving && hooksReceiving->isFieldPresent(sfHooks))
|
||||
rollback =
|
||||
executeHookChain(hooksReceiving, stateMap,
|
||||
recvResults, executedHookCount, *destAccountID, ctx_, j_, result);
|
||||
std::vector<std::pair<AccountID, bool>> tsh =
|
||||
hook::getTransactionalStakeHolders(ctx_.tx, ledger);
|
||||
|
||||
for (auto& [tshAccountID, canRollback] : tsh)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -154,6 +154,17 @@ public:
|
||||
static FeeUnit64
|
||||
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
|
||||
preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,174 @@
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
@@ -544,6 +544,25 @@ private:
|
||||
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>]
|
||||
Json::Value
|
||||
parseGetCounts(Json::Value const& jvParams)
|
||||
@@ -1313,6 +1332,7 @@ public:
|
||||
{"deposit_authorized", &RPCParser::parseDepositAuthorized, 2, 3},
|
||||
{"download_shard", &RPCParser::parseDownloadShard, 2, -1},
|
||||
{"feature", &RPCParser::parseFeature, 0, 2},
|
||||
{"fee", &RPCParser::parseFee, 0, 1},
|
||||
{"fetch_info", &RPCParser::parseFetchInfo, 0, 1},
|
||||
{"gateway_balances", &RPCParser::parseGatewayBalances, 1, -1},
|
||||
{"get_counts", &RPCParser::parseGetCounts, 0, 1},
|
||||
|
||||
@@ -253,6 +253,7 @@ JSS(features); // out: Feature
|
||||
JSS(fee); // out: NetworkOPs, Peers
|
||||
JSS(fee_base); // out: NetworkOPs
|
||||
JSS(fee_div_max); // in: TransactionSign
|
||||
JSS(fee_hooks_feeunits); // out: Fee rpc call
|
||||
JSS(fee_level); // out: AccountInfo
|
||||
JSS(fee_mult_max); // in: TransactionSign
|
||||
JSS(fee_ref); // out: NetworkOPs
|
||||
|
||||
@@ -25,14 +25,64 @@
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/GRPCHandlers.h>
|
||||
#include <ripple/app/tx/applyHook.h>
|
||||
#include <ripple/app/tx/impl/Transactor.h>
|
||||
|
||||
namespace ripple {
|
||||
Json::Value
|
||||
doFee(RPC::JsonContext& context)
|
||||
{
|
||||
auto result = context.app.getTxQ().doRPC(context.app);
|
||||
if (result.type() == Json::objectValue)
|
||||
return result;
|
||||
Json::Value jvResult = context.app.getTxQ().doRPC(context.app);
|
||||
if (jvResult.type() == Json::objectValue)
|
||||
{
|
||||
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);
|
||||
RPC::inject_error(rpcINTERNAL, context.params);
|
||||
return context.params;
|
||||
|
||||
Reference in New Issue
Block a user