Skip to content
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
6 changes: 5 additions & 1 deletion src/herder/HerderImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,11 @@ HerderImpl::recvTransaction(TransactionFrameBasePtr tx, bool submittedFromSelf,
bool hasClassic =
mTransactionQueue.sourceAccountPending(tx->getSourceID()) &&
tx->isSoroban();
if (hasSoroban || hasClassic)
bool enforceSourceAccountLimit = true;
#ifdef BUILD_TESTS
enforceSourceAccountLimit = !mApp.getRunInOverlayOnlyMode();
#endif
if (enforceSourceAccountLimit && (hasSoroban || hasClassic))
{
CLOG_DEBUG(Herder,
"recv transaction {} for {} rejected due to 1 tx per source "
Expand Down
8 changes: 7 additions & 1 deletion src/herder/TransactionQueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,12 @@ TransactionQueue::canAdd(
}

CheckValidLedgerViewWrapper ledgerView(mApp);
#ifdef BUILD_TESTS
// Overlay-only mode freezes on-disk seqnums at genesis but LoadGenerator
// keeps advancing local ones, so checkValid must skip the seqnum equality
// check or every tx after the first fails.
ledgerView.mSkipSeqNumCheck = mApp.getRunInOverlayOnlyMode();
#endif
// Subtle: transactions are rejected based on the source account limit
// prior to this point. This is safe because we can't evict transactions
// from the same source account, so a newer transaction won't replace an
Expand Down Expand Up @@ -496,7 +502,7 @@ TransactionQueue::canAdd(
// Loadgen transactions are given unlimited funds, and therefore do no need
// to be checked for fees
#ifdef BUILD_TESTS
if (!isLoadgenTx && !mApp.getRunInOverlayOnlyMode())
if (!isLoadgenTx)
#endif
{
auto const feeSource = ledgerView.getAccount(tx->getFeeSourceID());
Expand Down
9 changes: 8 additions & 1 deletion src/herder/TxSetFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2144,7 +2144,14 @@ ApplicableTxSetFrame::checkValidInternalWithResult(
releaseAssert(mPhases.size() == 1);
}

if (needGeneralizedTxSet)
bool enforceUniqueSourceAccount = needGeneralizedTxSet;
#ifdef BUILD_TESTS
if (app.getRunInOverlayOnlyMode())
{
enforceUniqueSourceAccount = false;
}
#endif
if (enforceUniqueSourceAccount)
{
// Ensure the tx set does not contain multiple txs per source
// account
Expand Down
4 changes: 4 additions & 0 deletions src/herder/TxSetUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ TxSetUtils::getInvalidTxListWithErrors(
ZoneScoped;
releaseAssert(threadIsMain());
CheckValidLedgerViewWrapper ledgerView(app);
#ifdef BUILD_TESTS
// See TransactionQueue::canAdd for the overlay-only-mode rationale.
ledgerView.mSkipSeqNumCheck = app.getRunInOverlayOnlyMode();
#endif
// Validate minSeqLedgerGap and LedgerBounds against the next ledgerSeq,
// which is what will be used at apply time.
std::optional<uint32_t> validationLedgerSeq;
Expand Down
7 changes: 7 additions & 0 deletions src/ledger/ImmutableLedgerView.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,13 @@ class CheckValidLedgerViewWrapper : public NonMovableOrCopyable
CheckValidLedgerViewWrapper(AbstractLedgerTxn& ltx);
CheckValidLedgerViewWrapper(Application& app);
explicit CheckValidLedgerViewWrapper(ImmutableLedgerView const& ledgerView);
#ifdef BUILD_TESTS
// Set by overlay-only mode call sites so commonValid skips the seqnum
// equality check: on-disk seqnums are frozen at genesis while
// LoadGenerator keeps advancing its local counters, so every tx after the
// first would otherwise fail isBadSeq.
bool mSkipSeqNumCheck{false};
#endif
LedgerHeaderWrapper getLedgerHeader() const;
LedgerEntryWrapper getAccount(AccountID const& account) const;
LedgerEntryWrapper
Expand Down
8 changes: 8 additions & 0 deletions src/ledger/LedgerManagerImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1620,6 +1620,9 @@ LedgerManagerImpl::applyLedger(LedgerCloseData const& ledgerData,
{
auto numTxs = txSet->sizeTxTotal();
auto numOps = txSet->sizeOpTotalForLogging();
auto numClassicTxs = applicableTxSet->sizeTx(TxSetPhase::CLASSIC);
auto numSorobanTxs = applicableTxSet->sizeTx(TxSetPhase::SOROBAN);
auto ledgerSeq = header.current().ledgerSeq;

// Force-deactivate header in overlay only mode; in normal mode, this is
// done by `processFeesSeqNums`
Expand All @@ -1631,6 +1634,11 @@ LedgerManagerImpl::applyLedger(LedgerCloseData const& ledgerData,
mApplyState.getMetrics().mOperationCount.Update(
static_cast<int64_t>(numOps));
TracyPlot("ledger.operation.count", static_cast<int64_t>(numOps));

CLOG_INFO(Ledger,
"Overlay-only mode: skipping apply for ledger {} "
"({} classic, {} soroban, {} total txs)",
ledgerSeq, numClassicTxs, numSorobanTxs, numTxs);
}
else
#endif
Expand Down
20 changes: 19 additions & 1 deletion src/main/CommandHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1446,13 +1446,31 @@ CommandHandler::generateLoad(std::string const& params, std::string& retStr)
}
}

if (cfg.mode == LoadGenMode::PAY_PREGENERATED)
if (cfg.mode == LoadGenMode::PAY_PREGENERATED || cfg.modeMixesPregen())
{
// Always use the configuration file path
cfg.preloadedTransactionsFile =
mApp.getConfig().LOADGEN_PREGENERATED_TRANSACTIONS_FILE;
}

if (cfg.modeMixesPregen())
{
auto& mix = cfg.getMutMixPregenSorobanConfig();
mix.classicTxRate =
parseOptionalParamOrDefault<uint32_t>(map, "classictxrate", 0);
mix.sorobanTxRate =
parseOptionalParamOrDefault<uint32_t>(map, "sorobantxrate", 0);
if (mix.classicTxRate == 0 && mix.sorobanTxRate == 0)
{
retStr = "At least one of classictxrate / sorobantxrate must "
"be non-zero";
return;
}
// cfg.txRate is used for progress / step scheduling logs; set the
// combined rate so users see sensible throughput output.
cfg.txRate = mix.classicTxRate + mix.sorobanTxRate;
Comment thread
marta-lokhova marked this conversation as resolved.
}

if (cfg.maxGeneratedFeeRate)
{
auto baseFee = mApp.getLedgerManager().getLastTxFee();
Expand Down
131 changes: 5 additions & 126 deletions src/simulation/ApplyLoad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,6 @@ namespace
{
constexpr double NOISY_BINARY_SEARCH_CONFIDENCE = 0.99;

LedgerKey
makeSACBalanceKey(SCAddress const& sacContract, SCVal const& holderAddrVal)
{
LedgerKey key(CONTRACT_DATA);
key.contractData().contract = sacContract;
key.contractData().key =
txtest::makeVecSCVal({makeSymbolSCVal("Balance"), holderAddrVal});
key.contractData().durability = ContractDataDurability::PERSISTENT;
return key;
}

LedgerKey
makeTrustlineKey(PublicKey const& accountID, Asset const& asset)
{
LedgerKey key(TRUSTLINE);
key.trustLine().accountID = accountID;
key.trustLine().asset = assetToTrustLineAsset(asset);
return key;
}

void
logExecutionEnvironmentSnapshot(Config const& cfg)
{
Expand Down Expand Up @@ -2819,7 +2799,7 @@ ApplyLoad::setupSoroswapContracts()
txtest::makeContractInstanceKey(pairAddress);

// Store pair info
SoroswapPairInfo pairInfo;
TxGenerator::SoroswapPairInfo pairInfo;
pairInfo.tokenAIndex = i;
pairInfo.tokenBIndex = j;
pairInfo.pairContractID = pairAddress;
Expand Down Expand Up @@ -3089,7 +3069,6 @@ ApplyLoad::generateSoroswapSwaps(std::vector<TransactionFrameBasePtr>& txs,
{
// Round-robin across pairs for parallelism
uint32_t pairIndex = i % numPairs;
auto const& pair = mSoroswapState.pairs[pairIndex];

// Unique account per tx (skip account 0 = root/issuer)
uint32_t accountIdx = i + 1;
Expand All @@ -3098,110 +3077,10 @@ ApplyLoad::generateSoroswapSwaps(std::vector<TransactionFrameBasePtr>& txs,
bool swapAForB = (mSoroswapSwapCounters[pairIndex] % 2 == 0);
mSoroswapSwapCounters[pairIndex]++;

uint32_t tokenInIdx = swapAForB ? pair.tokenAIndex : pair.tokenBIndex;
uint32_t tokenOutIdx = swapAForB ? pair.tokenBIndex : pair.tokenAIndex;

auto fromAccount =
mTxGenerator.findAccount(accountIdx, lm.getLastClosedLedgerNum());
fromAccount->loadSequenceNumber();

auto fromVal =
makeAddressSCVal(makeAccountAddress(fromAccount->getPublicKey()));

// Build path: [token_in, token_out]
auto tokenInVal = makeAddressSCVal(
mSoroswapState.sacInstances[tokenInIdx].contractID);
auto tokenOutVal = makeAddressSCVal(
mSoroswapState.sacInstances[tokenOutIdx].contractID);

SCVal pathVec(SCV_VEC);
pathVec.vec().activate();
pathVec.vec()->push_back(tokenInVal);
pathVec.vec()->push_back(tokenOutVal);

int64_t swapAmount = 100;
SCVal deadlineVal(SCV_U64);
deadlineVal.u64() = UINT64_MAX;

Operation op;
op.body.type(INVOKE_HOST_FUNCTION);
auto& ihf = op.body.invokeHostFunctionOp().hostFunction;
ihf.type(HOST_FUNCTION_TYPE_INVOKE_CONTRACT);
ihf.invokeContract().contractAddress = mSoroswapState.routerContractID;
ihf.invokeContract().functionName = "swap_exact_tokens_for_tokens";
ihf.invokeContract().args = {
txtest::makeI128(swapAmount), // amount_in
txtest::makeI128(0), // amount_out_min
pathVec, // path
fromVal, // to
deadlineVal // deadline
};

// Footprint
SorobanResources resources;
resources.instructions = TxGenerator::SOROSWAP_SWAP_TX_INSTRUCTIONS;
resources.diskReadBytes = 5000;
resources.writeBytes = 5000;

// Read-only: router instance, token_in SAC instance,
// token_out SAC instance, router code, pair code
resources.footprint.readOnly.push_back(
mSoroswapState.routerInstanceKey);
resources.footprint.readOnly.push_back(
mSoroswapState.sacInstances[tokenInIdx].readOnlyKeys.at(0));
resources.footprint.readOnly.push_back(
mSoroswapState.sacInstances[tokenOutIdx].readOnlyKeys.at(0));
resources.footprint.readOnly.push_back(mSoroswapState.routerCodeKey);
resources.footprint.readOnly.push_back(mSoroswapState.pairCodeKey);

// Read-write: user trustline(A), user trustline(B),
// Balance[pair] for token_in, Balance[pair] for
// token_out, pair instance
resources.footprint.readWrite.emplace_back(makeTrustlineKey(
fromAccount->getPublicKey(), mSoroswapState.assets[tokenInIdx]));
resources.footprint.readWrite.emplace_back(makeTrustlineKey(
fromAccount->getPublicKey(), mSoroswapState.assets[tokenOutIdx]));

auto pairAddrVal = makeAddressSCVal(pair.pairContractID);
// Balance[pair] for token_in
resources.footprint.readWrite.emplace_back(makeSACBalanceKey(
mSoroswapState.sacInstances[tokenInIdx].contractID, pairAddrVal));
// Balance[pair] for token_out
resources.footprint.readWrite.emplace_back(makeSACBalanceKey(
mSoroswapState.sacInstances[tokenOutIdx].contractID, pairAddrVal));
// Pair contract instance (RW - modified during swap)
resources.footprint.readWrite.emplace_back(
txtest::makeContractInstanceKey(pair.pairContractID));

// Auth: source_account authorizes swap_exact_tokens_for_tokens
// which sub-invokes token_in.transfer(user, pair, amount)
SorobanAuthorizedInvocation rootInvocation;
rootInvocation.function.type(
SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN);
rootInvocation.function.contractFn() = ihf.invokeContract();

SorobanAuthorizedInvocation transferInvocation;
transferInvocation.function.type(
SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN);
transferInvocation.function.contractFn().contractAddress =
mSoroswapState.sacInstances[tokenInIdx].contractID;
transferInvocation.function.contractFn().functionName = "transfer";
transferInvocation.function.contractFn().args = {
fromVal, pairAddrVal, txtest::makeI128(swapAmount)};
rootInvocation.subInvocations.push_back(transferInvocation);

SorobanCredentials credentials(SOROBAN_CREDENTIALS_SOURCE_ACCOUNT);
op.body.invokeHostFunctionOp().auth.emplace_back(credentials,
rootInvocation);

auto resourceFee =
txtest::sorobanResourceFee(mApp, resources, 1000, 200);
resourceFee += 5'000'000;

auto tx = txtest::sorobanTransactionFrameFromOps(
mApp.getNetworkID(), *fromAccount, {op}, {}, resources,
mTxGenerator.generateFee(std::nullopt, 1), resourceFee);
txs.push_back(tx);
auto tx = mTxGenerator.invokeSoroswapSwap(
lm.getLastClosedLedgerNum() + 1, accountIdx, mSoroswapState,
pairIndex, swapAForB, std::nullopt);
txs.push_back(tx.second);
}

CheckValidLedgerViewWrapper ls(mApp);
Expand Down
29 changes: 2 additions & 27 deletions src/simulation/ApplyLoad.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,33 +188,8 @@ class ApplyLoad
// Used to generate custom token transfer transactions
TxGenerator::ContractInstance mTokenInstance;

// Soroswap AMM benchmark state
struct SoroswapPairInfo
{
SCAddress pairContractID;
uint32_t tokenAIndex;
uint32_t tokenBIndex;
};

struct SoroswapState
{
SCAddress factoryContractID;
SCAddress routerContractID;

std::vector<SoroswapPairInfo> pairs;
std::vector<TxGenerator::ContractInstance> sacInstances;

LedgerKey routerCodeKey;
LedgerKey pairCodeKey;
LedgerKey factoryCodeKey;

LedgerKey routerInstanceKey;
LedgerKey factoryInstanceKey;

std::vector<Asset> assets;
uint32_t numTokens = 0;
};
SoroswapState mSoroswapState;
// Soroswap AMM benchmark state (type defined in TxGenerator.h)
TxGenerator::SoroswapState mSoroswapState;

// Counter for alternating swap direction per pair
std::vector<uint32_t> mSoroswapSwapCounters;
Expand Down
Loading
Loading