Skip to content

Commit d86374b

Browse files
authored
Add random memo to apply-load transactions (#5322)
In order to preserve the uniqueness of transaction hashes across multiple `apply-load` runs, this PR introduces a random memo to every legacy payment transaction.
2 parents f292a5f + 68267a5 commit d86374b

6 files changed

Lines changed: 52 additions & 19 deletions

File tree

src/simulation/ApplyLoad.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "simulation/ApplyLoad.h"
22

33
#include <algorithm>
4+
#include <chrono>
45
#include <cmath>
56
#include <memory>
67
#include <numeric>
@@ -738,6 +739,18 @@ ApplyLoad::ApplyLoad(Application& app)
738739
throw std::runtime_error(
739740
"APPLY_LOAD_LEDGER_MAX_DEPENDENT_TX_CLUSTERS cannot be zero");
740741
}
742+
743+
// Seed the classic payment memo id from the current wall-clock time (in
744+
// nanoseconds) so that classic payment tx hashes differ across runs. The id
745+
// is then incremented per generated payment to stay unique within a run.
746+
// Nanosecond resolution makes cross-run collisions practically impossible:
747+
// two runs would have to start within (number of payments) ns of each other
748+
// to overlap.
749+
mNextClassicPaymentMemoId = static_cast<uint64_t>(
750+
std::chrono::duration_cast<std::chrono::nanoseconds>(
751+
mApp.getClock().system_now().time_since_epoch())
752+
.count());
753+
741754
setup();
742755
}
743756

@@ -1872,9 +1885,14 @@ ApplyLoad::generateClassicPayments(std::vector<TransactionFrameBasePtr>& txs,
18721885
auto it = accounts.find(accountIdx);
18731886
releaseAssert(it != accounts.end());
18741887
it->second->loadSequenceNumber();
1888+
// Attach a unique memo id so that the generated classic payment tx
1889+
// hashes are unique within a run and across runs (the id is seeded from
1890+
// wall-clock time in the constructor and incremented per payment).
1891+
Memo memo(MEMO_ID);
1892+
memo.id() = mNextClassicPaymentMemoId++;
18751893
auto [_, tx] = mTxGenerator.paymentTransaction(
18761894
mNumAccounts, 0, lm.getLastClosedLedgerNum() + 1, it->first, 1,
1877-
std::nullopt);
1895+
std::nullopt, memo);
18781896
auto res =
18791897
tx->checkValid(appConnector, ledgerView, 0, 0, 0, diagnostics);
18801898
releaseAssert(res && res->isSuccess());

src/simulation/ApplyLoad.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,12 @@ class ApplyLoad
178178

179179
// Counter for generating unique destination addresses for SAC payments
180180
uint32_t mDestCounter = 0;
181+
182+
// Monotonic memo id assigned to generated classic payments to keep their
183+
// tx hashes unique within a run and across runs. Seeded from the wall-clock
184+
// time at construction (see constructor) so that separate runs start from
185+
// different values.
186+
uint64_t mNextClassicPaymentMemoId = 0;
181187
};
182188

183189
#ifdef BUILD_TESTS

src/simulation/TxGenerator.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ TxGenerator::createAccounts(uint64_t start, uint64_t count, uint32_t ledgerNum,
234234
return ops;
235235
}
236236

237-
TransactionFrameBasePtr
237+
TransactionFrameBaseConstPtr
238238
TxGenerator::createTransactionFramePtr(
239239
TxGenerator::TestAccountPtr from, std::vector<Operation> ops,
240240
std::optional<uint32_t> maxGeneratedFeeRate)
@@ -246,31 +246,32 @@ TxGenerator::createTransactionFramePtr(
246246
return txf;
247247
}
248248

249-
TransactionFrameBasePtr
249+
TransactionFrameBaseConstPtr
250250
TxGenerator::createTransactionFramePtr(
251251
TxGenerator::TestAccountPtr from, std::vector<Operation> ops,
252252
std::optional<uint32_t> maxGeneratedFeeRate,
253-
std::optional<uint32_t> byteCount)
253+
std::optional<uint32_t> byteCount, std::optional<Memo> memo)
254254
{
255255
if (byteCount.has_value())
256256
{
257257
return paddedTransactionFromOperations(
258258
mApp, from->getSecretKey(), from->nextSequenceNumber(), ops,
259-
generateFee(maxGeneratedFeeRate, ops.size()), *byteCount);
259+
generateFee(maxGeneratedFeeRate, ops.size()), *byteCount, memo);
260260
}
261261
else
262262
{
263263
return transactionFromOperations(
264264
mApp, from->getSecretKey(), from->nextSequenceNumber(), ops,
265-
generateFee(maxGeneratedFeeRate, ops.size()));
265+
generateFee(maxGeneratedFeeRate, ops.size()), memo);
266266
}
267267
}
268268

269269
std::pair<TxGenerator::TestAccountPtr, TransactionFrameBasePtr>
270270
TxGenerator::paymentTransaction(uint32_t numAccounts, uint32_t offset,
271271
uint32_t ledgerNum, uint64_t sourceAccount,
272272
std::optional<uint32_t> byteCount,
273-
std::optional<uint32_t> maxGeneratedFeeRate)
273+
std::optional<uint32_t> maxGeneratedFeeRate,
274+
std::optional<Memo> memo)
274275
{
275276
TxGenerator::TestAccountPtr to, from;
276277
uint64_t amount = 1;
@@ -281,7 +282,7 @@ TxGenerator::paymentTransaction(uint32_t numAccounts, uint32_t offset,
281282

282283
return std::make_pair(from, createTransactionFramePtr(from, paymentOps,
283284
maxGeneratedFeeRate,
284-
byteCount));
285+
byteCount, memo));
285286
}
286287

287288
std::pair<TxGenerator::TestAccountPtr, TransactionFrameBaseConstPtr>

src/simulation/TxGenerator.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,15 @@ class TxGenerator
189189
TransactionFrameBaseConstPtr
190190
createTransactionFramePtr(TestAccountPtr from, std::vector<Operation> ops,
191191
std::optional<uint32_t> maxGeneratedFeeRate,
192-
std::optional<uint32_t> byteCount);
192+
std::optional<uint32_t> byteCount,
193+
std::optional<Memo> memo = std::nullopt);
193194

194195
std::pair<TestAccountPtr, TransactionFrameBaseConstPtr>
195196
paymentTransaction(uint32_t numAccounts, uint32_t offset,
196197
uint32_t ledgerNum, uint64_t sourceAccount,
197198
std::optional<uint32_t> byteCount,
198-
std::optional<uint32_t> maxGeneratedFeeRate);
199+
std::optional<uint32_t> maxGeneratedFeeRate,
200+
std::optional<Memo> memo = std::nullopt);
199201

200202
std::pair<TestAccountPtr, TransactionFrameBaseConstPtr>
201203
createUploadWasmTransaction(

src/test/TxTests.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -775,11 +775,17 @@ TransactionTestFramePtr
775775
paddedTransactionFromOperationsV1(Application& app, SecretKey const& from,
776776
SequenceNumber seq,
777777
std::vector<Operation> const& ops,
778-
uint32_t fee, uint32_t desiredSize)
778+
uint32_t fee, uint32_t desiredSize,
779+
std::optional<Memo> memo)
779780
{
780781
TransactionEnvelope e =
781782
makeEnvelopeV1(app, from, seq, ops, fee, std::nullopt);
782783

784+
if (memo)
785+
{
786+
e.v1().tx.memo = *memo;
787+
}
788+
783789
uint32_t baseSize = xdr::xdr_argpack_size(e);
784790

785791
StellarMessage baseMessage;
@@ -855,7 +861,7 @@ TransactionTestFramePtr
855861
paddedTransactionFromOperations(Application& app, SecretKey const& from,
856862
SequenceNumber seq,
857863
std::vector<Operation> const& ops, uint32_t fee,
858-
uint32_t desiredSize)
864+
uint32_t desiredSize, std::optional<Memo> memo)
859865
{
860866
auto ledgerVersion =
861867
app.getLedgerManager().getLastClosedLedgerHeader().header.ledgerVersion;
@@ -865,7 +871,7 @@ paddedTransactionFromOperations(Application& app, SecretKey const& from,
865871
"paddedTransactionFromOperations() called from pre-V23 protocol");
866872
}
867873
return paddedTransactionFromOperationsV1(app, from, seq, ops, fee,
868-
desiredSize);
874+
desiredSize, memo);
869875
}
870876

871877
TransactionTestFramePtr

src/test/TxTests.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ transactionFromOperationsV0(Application& app, SecretKey const& from,
144144
// the cost to enable the sorobanData extension (~36 bytes).
145145
TransactionTestFramePtr paddedTransactionFromOperationsV1(
146146
Application& app, SecretKey const& from, SequenceNumber seq,
147-
std::vector<Operation> const& ops, uint32_t fee, uint32_t desiredSize);
147+
std::vector<Operation> const& ops, uint32_t fee, uint32_t desiredSize,
148+
std::optional<Memo> memo = std::nullopt);
148149

149150
TransactionTestFramePtr
150151
transactionFromOperationsV1(Application& app, SecretKey const& from,
@@ -157,11 +158,10 @@ transactionFromOperationsV1(Application& app, SecretKey const& from,
157158
// If `app` protocol version is >=23, attempts to pad to around `desiredSize`
158159
// (see comment on `paddedTransactionFromOperationsV1`). Otherwise, throw an
159160
// error.
160-
TransactionTestFramePtr
161-
paddedTransactionFromOperations(Application& app, SecretKey const& from,
162-
SequenceNumber seq,
163-
std::vector<Operation> const& ops,
164-
uint32_t fee = 0, uint32_t desiredSize = 0);
161+
TransactionTestFramePtr paddedTransactionFromOperations(
162+
Application& app, SecretKey const& from, SequenceNumber seq,
163+
std::vector<Operation> const& ops, uint32_t fee = 0,
164+
uint32_t desiredSize = 0, std::optional<Memo> memo = std::nullopt);
165165

166166
TransactionTestFramePtr
167167
transactionFromOperations(Application& app, SecretKey const& from,

0 commit comments

Comments
 (0)