Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor TxSet validation logic. #4627

Merged
merged 2 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
719 changes: 452 additions & 267 deletions src/herder/TxSetFrame.cpp

Large diffs are not rendered by default.

44 changes: 28 additions & 16 deletions src/herder/TxSetFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,18 +196,18 @@ class TxSetXDRFrame : public NonMovableOrCopyable
// - The whole phase (`TxStageFrameList`) consists of several sequential
// 'stages' (`TxStageFrame`). A stage has to be executed after every
// transaction in the previous stage has been applied.
// - A 'stage' (`TxStageFrame`) consists of several parallel 'threads'
// (`TxThreadFrame`). Transactions in different 'threads' are independent of
// - A 'stage' (`TxStageFrame`) consists of several independent 'clusters'
// (`TxClusterFrame`). Transactions in different 'clusters' are independent of
// each other and can be applied in parallel.
// - A 'thread' (`TxThreadFrame`) consists of transactions that should
// - A 'cluster' (`TxClusterFrame`) consists of transactions that should
// generally be applied sequentially. However, not all the transactions in
// the thread are necessarily conflicting with each other; it is possible
// that some, or even all transactions in the thread structure can be applied
// the cluster are necessarily conflicting with each other; it is possible
// that some, or even all transactions in the cluster structure can be applied
// in parallel with each other (depending on their footprints).
//
// This structure mimics the XDR structure of the `ParallelTxsComponent`.
using TxThreadFrame = TxFrameList;
using TxStageFrame = std::vector<TxThreadFrame>;
using TxClusterFrame = TxFrameList;
using TxStageFrame = std::vector<TxClusterFrame>;
using TxStageFrameList = std::vector<TxStageFrame>;

// Alias for the map from transaction to its inclusion fee as defined by the
Expand Down Expand Up @@ -276,19 +276,23 @@ class TxSetPhaseFrame
Iterator(TxStageFrameList const& txs, size_t stageIndex);
TxStageFrameList const& mStages;
size_t mStageIndex = 0;
size_t mThreadIndex = 0;
size_t mClusterIndex = 0;
size_t mTxIndex = 0;
};
Iterator begin() const;
Iterator end() const;
size_t size() const;
size_t sizeTx() const;
size_t sizeOp() const;
size_t size(LedgerHeader const& lclHeader) const;
bool empty() const;

// Get _inclusion_ fee map for this phase. The map contains lowest base
// fee for each transaction (lowest base fee is identical for all
// transactions in the same lane)
InclusionFeeMap const& getInclusionFeeMap() const;

std::optional<Resource> getTotalResources() const;

private:
friend class TxSetXDRFrame;
friend class ApplicableTxSetFrame;
Expand All @@ -312,16 +316,16 @@ class TxSetPhaseFrame
TxFrameList& invalidTxs,
bool enforceTxsApplyOrder);
#endif

TxSetPhaseFrame(TxFrameList const& txs,
TxSetPhaseFrame(TxSetPhase phase, TxFrameList const& txs,
std::shared_ptr<InclusionFeeMap> inclusionFeeMap);
TxSetPhaseFrame(TxStageFrameList&& txs,
TxSetPhaseFrame(TxSetPhase phase, TxStageFrameList&& txs,
std::shared_ptr<InclusionFeeMap> inclusionFeeMap);

// Creates a new phase from `TransactionPhase` XDR coming from a
// `GeneralizedTransactionSet`.
static std::optional<TxSetPhaseFrame>
makeFromWire(Hash const& networkID, TransactionPhase const& xdrPhase);
makeFromWire(TxSetPhase phase, Hash const& networkID,
TransactionPhase const& xdrPhase);

// Creates a new phase from all the transactions in the legacy
// `TransactionSet` XDR.
Expand All @@ -330,10 +334,20 @@ class TxSetPhaseFrame
xdr::xvector<TransactionEnvelope> const& xdrTxs);

// Creates a valid empty phase with given `isParallel` flag.
static TxSetPhaseFrame makeEmpty(bool isParallel);
static TxSetPhaseFrame makeEmpty(TxSetPhase phase, bool isParallel);

// Returns a copy of this phase with transactions sorted for apply.
TxSetPhaseFrame sortedForApply(Hash const& txSetHash) const;
bool checkValid(Application& app, uint64_t lowerBoundCloseTimeOffset,
uint64_t upperBoundCloseTimeOffset) const;
bool checkValidClassic(LedgerHeader const& lclHeader) const;
bool checkValidSoroban(LedgerHeader const& lclHeader,
SorobanNetworkConfig const& sorobanConfig) const;

bool txsAreValid(Application& app, uint64_t lowerBoundCloseTimeOffset,
uint64_t upperBoundCloseTimeOffset) const;

TxSetPhase mPhase;

TxStageFrameList mStages;
std::shared_ptr<InclusionFeeMap> mInclusionFeeMap;
Expand Down Expand Up @@ -471,8 +485,6 @@ class ApplicableTxSetFrame
ApplicableTxSetFrame(ApplicableTxSetFrame const&) = default;
ApplicableTxSetFrame(ApplicableTxSetFrame&&) = default;

std::optional<Resource> getTxSetSorobanResource() const;

void toXDR(TransactionSet& set) const;
void toXDR(GeneralizedTransactionSet& generalizedTxSet) const;

Expand Down
69 changes: 65 additions & 4 deletions src/herder/test/TestTxSetUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ makeTxSetXDR(std::vector<TransactionFrameBasePtr> const& txs,
}

GeneralizedTransactionSet
makeGeneralizedTxSetXDR(std::vector<ComponentPhases> const& phases,
makeGeneralizedTxSetXDR(std::vector<PhaseComponents> const& phases,
Hash const& previousLedgerHash,
bool useParallelSorobanPhase)
{
Expand Down Expand Up @@ -76,11 +76,11 @@ makeGeneralizedTxSetXDR(std::vector<ComponentPhases> const& phases,
}
if (!txs.empty())
{
auto& thread =
auto& cluster =
component.executionStages.emplace_back().emplace_back();
for (auto const& tx : txs)
{
thread.emplace_back(tx->getEnvelope());
cluster.emplace_back(tx->getEnvelope());
}
}
#else
Expand Down Expand Up @@ -120,7 +120,7 @@ makeNonValidatedTxSet(std::vector<TransactionFrameBasePtr> const& txs,

std::pair<TxSetXDRFrameConstPtr, ApplicableTxSetFrameConstPtr>
makeNonValidatedGeneralizedTxSet(
std::vector<ComponentPhases> const& txsPerBaseFee, Application& app,
std::vector<PhaseComponents> const& txsPerBaseFee, Application& app,
Hash const& previousLedgerHash, std::optional<bool> useParallelSorobanPhase)
{
if (!useParallelSorobanPhase.has_value())
Expand Down Expand Up @@ -157,5 +157,66 @@ makeNonValidatedTxSetBasedOnLedgerVersion(
}
}

#ifdef ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION
void
normalizeParallelPhaseXDR(TransactionPhase& phase)
{
auto compareTxHash = [](TransactionEnvelope const& tx1,
TransactionEnvelope const& tx2) -> bool {
return xdrSha256(tx1) < xdrSha256(tx2);
};
for (auto& stage : phase.parallelTxsComponent().executionStages)
{
for (auto& cluster : stage)
{
std::sort(cluster.begin(), cluster.end(), compareTxHash);
}
std::sort(stage.begin(), stage.end(),
[&](auto const& c1, auto const& c2) {
return compareTxHash(c1.front(), c2.front());
});
}
std::sort(phase.parallelTxsComponent().executionStages.begin(),
phase.parallelTxsComponent().executionStages.end(),
[&](auto const& s1, auto const& s2) {
return compareTxHash(s1.front().front(), s2.front().front());
});
}

std::pair<TxSetXDRFrameConstPtr, ApplicableTxSetFrameConstPtr>
makeNonValidatedGeneralizedTxSet(PhaseComponents const& classicTxsPerBaseFee,
std::optional<int64_t> sorobanBaseFee,
TxStageFrameList const& sorobanTxsPerStage,
Application& app,
Hash const& previousLedgerHash)
{
auto xdrTxSet = makeGeneralizedTxSetXDR({classicTxsPerBaseFee},
previousLedgerHash, false);
xdrTxSet.v1TxSet().phases.emplace_back(1);
auto& phase = xdrTxSet.v1TxSet().phases.back();
if (sorobanBaseFee)
{
phase.parallelTxsComponent().baseFee.activate() = *sorobanBaseFee;
}

auto& stages = phase.parallelTxsComponent().executionStages;
for (auto const& stage : sorobanTxsPerStage)
{
auto& xdrStage = stages.emplace_back();
for (auto const& cluster : stage)
{
auto& xdrCluster = xdrStage.emplace_back();
for (auto const& tx : cluster)
{
xdrCluster.emplace_back(tx->getEnvelope());
}
}
}
normalizeParallelPhaseXDR(phase);
auto txSet = TxSetXDRFrame::makeFromWire(xdrTxSet);
return std::make_pair(txSet, txSet->prepareForApply(app));
}
#endif

} // namespace testtxset
} // namespace stellar
14 changes: 12 additions & 2 deletions src/herder/test/TestTxSetUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,27 @@ namespace stellar
namespace testtxset
{

using ComponentPhases = std::vector<
using PhaseComponents = std::vector<
std::pair<std::optional<int64_t>, std::vector<TransactionFrameBasePtr>>>;
std::pair<TxSetXDRFrameConstPtr, ApplicableTxSetFrameConstPtr>
makeNonValidatedGeneralizedTxSet(
std::vector<ComponentPhases> const& txsPerBaseFee, Application& app,
std::vector<PhaseComponents> const& txsPerBaseFee, Application& app,
Hash const& previousLedgerHash,
std::optional<bool> useParallelSorobanPhase = std::nullopt);

std::pair<TxSetXDRFrameConstPtr, ApplicableTxSetFrameConstPtr>
makeNonValidatedTxSetBasedOnLedgerVersion(
std::vector<TransactionFrameBasePtr> const& txs, Application& app,
Hash const& previousLedgerHash);
#ifdef ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION
void normalizeParallelPhaseXDR(TransactionPhase& phase);

std::pair<TxSetXDRFrameConstPtr, ApplicableTxSetFrameConstPtr>
makeNonValidatedGeneralizedTxSet(PhaseComponents const& classicTxsPerBaseFee,
std::optional<int64_t> sorobanBaseFee,
TxStageFrameList const& sorobanTxsPerStage,
Application& app,
Hash const& previousLedgerHash);
#endif
} // namespace testtxset
} // namespace stellar
Loading
Loading