add batch

This commit is contained in:
Denis Angell
2023-12-04 10:37:47 +01:00
parent 27f43ba9ee
commit e26d4164a9
13 changed files with 383 additions and 2 deletions

View File

@@ -49,6 +49,7 @@ public:
TER const preclaimResult;
XRPAmount const baseFee;
beast::Journal const journal;
OpenView& base_;
ApplyView&
view()
@@ -139,7 +140,7 @@ private:
XRPAmount const fee,
std::index_sequence<Is...>);
OpenView& base_;
// OpenView& base_;
ApplyFlags flags_;
std::optional<ApplyViewImpl> view_;
};

View File

@@ -0,0 +1,196 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/app/tx/impl/ApplyContext.h>
#include <ripple/app/tx/applySteps.h>
#include <ripple/app/tx/impl/Batch.h>
#include <ripple/basics/Log.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/app/tx/impl/Invoke.h>
namespace ripple {
PreclaimContext
makePreclaimTx(PreclaimContext const& ctx, TxType const& tt, STObject const& txn)
{
auto const stx = STTx(tt, [&txn](STObject& obj){
obj = std::move(txn);
});
PreclaimContext const pcctx(ctx.app, ctx.view, tesSUCCESS, stx, ctx.flags, ctx.j);
return pcctx;
}
TxConsequences
Batch::makeTxConsequences(PreflightContext const& ctx)
{
return TxConsequences{ctx.tx, TxConsequences::normal};
}
NotTEC
Batch::preflight(PreflightContext const& ctx)
{
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;
auto& tx = ctx.tx;
auto const& txns = tx.getFieldArray(sfEmittedTxns);
if (txns.empty())
{
JLOG(ctx.j.warn()) << "Batch: txns array empty.";
return temMALFORMED;
}
if (txns.size() > 400)
{
JLOG(ctx.j.warn())
<< "Batch: txns array exceeds 400 entries.";
return temMALFORMED;
}
for (auto const& txn : txns)
{
if (!txn.isFieldPresent(sfTransactionType))
{
JLOG(ctx.j.warn())
<< "Batch: TransactionType missing in array entry.";
return temMALFORMED;
}
auto const tt = txn.getFieldU16(sfTransactionType);
auto const account = txn.getAccountID(sfAccount);
std::cout << "account: " << account << "\n";
auto const stx = STTx(ttINVOKE, [&txn](STObject& obj){
obj = std::move(txn);
});
auto const txBlob = strHex(stx.getSerializer().slice());
std::cout << "txBlob: " << txBlob << "\n";
PreflightContext const pfctx(ctx.app, stx, ctx.rules, ctx.flags, ctx.j);
switch (tt)
{
case ttINVOKE:
std::cout << "tt: " << "ttINVOKE" << "\n";
// DA: Create array of responses
Invoke::preflight(pfctx);
default:
std::cout << "tt: " << "temUNKNOWN" << "\n";
}
}
return preflight2(ctx);
}
TER
Batch::preclaim(PreclaimContext const& ctx)
{
if (!ctx.view.rules().enabled(featureHooks))
return temDISABLED;
auto const& txns = ctx.tx.getFieldArray(sfEmittedTxns);
for (auto const& txn : txns)
{
if (!txn.isFieldPresent(sfTransactionType))
{
JLOG(ctx.j.warn())
<< "Batch: TransactionType missing in array entry.";
return temMALFORMED;
}
auto const tt = txn.getFieldU16(sfTransactionType);
auto const stx = STTx(ttINVOKE, [&txn](STObject& obj){
obj = std::move(txn);
});
PreclaimContext const pcctx(ctx.app, ctx.view, tesSUCCESS, stx, ctx.flags, ctx.j);
switch (tt)
{
case ttINVOKE:
std::cout << "tt: " << "ttINVOKE" << "\n";
// auto const pcctx1 = makePreclaimTx(ctx, ttINVOKE, txn);
// DA: Create array of responses
Invoke::preclaim(pcctx);
break;
default:
std::cout << "tt: " << "temUNKNOWN" << "\n";
}
}
return tesSUCCESS;
}
TER
Batch::doApply()
{
auto const& txns = ctx_.tx.getFieldArray(sfEmittedTxns);
for (auto const& txn : txns)
{
if (!txn.isFieldPresent(sfTransactionType))
{
JLOG(ctx_.journal.warn())
<< "Batch: TransactionType missing in array entry.";
return temMALFORMED;
}
auto const tt = txn.getFieldU16(sfTransactionType);
auto const stx = STTx(ttINVOKE, [&txn](STObject& obj){
obj = std::move(txn);
});
ApplyContext actx(ctx_.app, ctx_.base_, stx, tesSUCCESS, XRPAmount(1), view().flags(), ctx_.journal);
Invoke p(actx);
switch (tt)
{
case ttINVOKE:
std::cout << "tt: " << "ttINVOKE" << "\n";
// DA: Create array of responses
p();
default:
std::cout << "tt: " << "temUNKNOWN" << "\n";
}
}
return tesSUCCESS;
}
XRPAmount
Batch::calculateBaseFee(ReadView const& view, STTx const& tx)
{
XRPAmount extraFee{0};
// if (tx.isFieldPresent(sfEmittedTxns))
// {
// XRPAmount txFees{0};
// auto const& txns = tx.getFieldArray(sfEmittedTxns);
// for (auto const& txn : txns)
// {
// txFees += txn.isFieldPresent(sfFee) ? txn.getFieldAmount(sfFee) : XRPAmount{0};
// }
// extraFee += txFees;
// }
return Transactor::calculateBaseFee(view, tx) + extraFee;
}
} // namespace ripple

View File

@@ -0,0 +1,57 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TX_BATCH_H_INCLUDED
#define RIPPLE_TX_BATCH_H_INCLUDED
#include <ripple/app/tx/impl/Transactor.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/protocol/Indexes.h>
namespace ripple {
class Batch : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
explicit Batch(ApplyContext& ctx) : Transactor(ctx)
{
}
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace ripple
#endif

View File

@@ -241,6 +241,9 @@ XRPNotCreated::finalize(
return drops_ == drops;
}
if (tt == ttBATCH)
return true;
// The net change should never be positive, as this would mean that the
// transaction created XRP out of thin air. That's not possible.
if (drops_ > 0)

View File

@@ -34,24 +34,27 @@ Invoke::makeTxConsequences(PreflightContext const& ctx)
NotTEC
Invoke::preflight(PreflightContext const& ctx)
{
std::cout << "Invoke::preflight" << "\n";
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;
auto& tx = ctx.tx;
if (tx.getFieldVL(sfBlob).size() > (128 * 1024))
if (tx.isFieldPresent(sfBlob) && tx.getFieldVL(sfBlob).size() > (128 * 1024))
{
JLOG(ctx.j.warn()) << "Invoke: blob was more than 128kib "
<< tx.getTransactionID();
return temMALFORMED;
}
std::cout << "Invoke::preflight" << "FINISHED" << "\n";
return preflight2(ctx);
}
TER
Invoke::preclaim(PreclaimContext const& ctx)
{
std::cout << "Invoke::preclaim" << "\n";
if (!ctx.view.rules().enabled(featureHooks))
return temDISABLED;
@@ -67,12 +70,14 @@ Invoke::preclaim(PreclaimContext const& ctx)
return tecNO_TARGET;
}
std::cout << "Invoke::preclaim" << "FINISHED" << "\n";
return tesSUCCESS;
}
TER
Invoke::doApply()
{
std::cout << "Invoke::doApply" << "\n";
// everything happens in the hooks!
return tesSUCCESS;
}

View File

@@ -19,6 +19,7 @@
#include <ripple/app/tx/applySteps.h>
#include <ripple/app/tx/impl/ApplyContext.h>
#include <ripple/app/tx/impl/Batch.h>
#include <ripple/app/tx/impl/CancelCheck.h>
#include <ripple/app/tx/impl/CancelOffer.h>
#include <ripple/app/tx/impl/CashCheck.h>
@@ -104,6 +105,8 @@ invoke_preflight(PreflightContext const& ctx)
return invoke_preflight_helper<DeleteAccount>(ctx);
case ttACCOUNT_SET:
return invoke_preflight_helper<SetAccount>(ctx);
case ttBATCH:
return invoke_preflight_helper<Batch>(ctx);
case ttCHECK_CANCEL:
return invoke_preflight_helper<CancelCheck>(ctx);
case ttCHECK_CASH:
@@ -223,6 +226,8 @@ invoke_preclaim(PreclaimContext const& ctx)
return invoke_preclaim<DeleteAccount>(ctx);
case ttACCOUNT_SET:
return invoke_preclaim<SetAccount>(ctx);
case ttBATCH:
return invoke_preclaim<Batch>(ctx);
case ttCHECK_CANCEL:
return invoke_preclaim<CancelCheck>(ctx);
case ttCHECK_CASH:
@@ -304,6 +309,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
return DeleteAccount::calculateBaseFee(view, tx);
case ttACCOUNT_SET:
return SetAccount::calculateBaseFee(view, tx);
case ttBATCH:
return Batch::calculateBaseFee(view, tx);
case ttCHECK_CANCEL:
return CancelCheck::calculateBaseFee(view, tx);
case ttCHECK_CASH:
@@ -428,6 +435,10 @@ invoke_apply(ApplyContext& ctx)
SetAccount p(ctx);
return p();
}
case ttBATCH: {
Batch p(ctx);
return p();
}
case ttCHECK_CANCEL: {
CancelCheck p(ctx);
return p();

View File

@@ -604,6 +604,7 @@ extern SField const sfMemos;
extern SField const sfNFTokens;
extern SField const sfHooks;
extern SField const sfGenesisMint;
extern SField const sfEmittedTxns;
// array of objects (uncommon)
extern SField const sfMajorities;

View File

@@ -181,6 +181,8 @@ enum TxType : std::uint16_t
ttUNL_MODIFY = 102,
ttEMIT_FAILURE = 103,
ttUNL_REPORT = 104,
ttBATCH = 105,
};
// clang-format on

View File

@@ -366,6 +366,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfDisabledValidators, "DisabledValidators", ARRAY,
CONSTRUCT_UNTYPED_SFIELD(sfHookExecutions, "HookExecutions", ARRAY, 18);
CONSTRUCT_UNTYPED_SFIELD(sfHookParameters, "HookParameters", ARRAY, 19);
CONSTRUCT_UNTYPED_SFIELD(sfHookGrants, "HookGrants", ARRAY, 20);
CONSTRUCT_UNTYPED_SFIELD(sfEmittedTxns, "EmittedTxns", ARRAY, 97);
CONSTRUCT_UNTYPED_SFIELD(sfGenesisMints, "GenesisMints", ARRAY, 96);
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidators, "ActiveValidators", ARRAY, 95);
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKeys, "ImportVLKeys", ARRAY, 94);

View File

@@ -441,6 +441,13 @@ TxFormats::TxFormats()
{sfTicketSequence, soeOPTIONAL},
},
commonFields);
add(jss::Batch,
ttBATCH,
{
{sfEmittedTxns, soeOPTIONAL},
},
commonFields);
}
TxFormats const&

View File

@@ -50,6 +50,7 @@ JSS(AccountSet); // transaction type.
JSS(Amendments); // ledger type.
JSS(Amount); // in: TransactionSign; field.
JSS(Authorize); // field
JSS(Batch); // transaction type.
JSS(Blob);
JSS(Check); // ledger type.
JSS(CheckCancel); // transaction type.