rippled
Loading...
Searching...
No Matches
PaymentTests.cpp
1// Auto-generated unit tests for transaction Payment
2
3
4#include <gtest/gtest.h>
5
6#include <protocol_autogen/TestHelpers.h>
7
8#include <xrpl/protocol/SecretKey.h>
9#include <xrpl/protocol/Seed.h>
10#include <xrpl/protocol/STTx.h>
11#include <xrpl/protocol_autogen/transactions/Payment.h>
12#include <xrpl/protocol_autogen/transactions/AccountSet.h>
13
14#include <string>
15
16namespace xrpl::transactions {
17
18// 1 & 4) Set fields via builder setters, build, then read them back via
19// wrapper getters. After build(), validate() should succeed.
20TEST(TransactionsPaymentTests, BuilderSettersRoundTrip)
21{
22 // Generate a deterministic keypair for signing
23 auto const [publicKey, secretKey] =
25
26 // Common transaction fields
27 auto const accountValue = calcAccountID(publicKey);
28 std::uint32_t const sequenceValue = 1;
29 auto const feeValue = canonical_AMOUNT();
30
31 // Transaction-specific field values
32 auto const destinationValue = canonical_ACCOUNT();
33 auto const amountValue = canonical_AMOUNT();
34 auto const sendMaxValue = canonical_AMOUNT();
35 auto const pathsValue = canonical_PATHSET();
36 auto const invoiceIDValue = canonical_UINT256();
37 auto const destinationTagValue = canonical_UINT32();
38 auto const deliverMinValue = canonical_AMOUNT();
39 auto const credentialIDsValue = canonical_VECTOR256();
40 auto const domainIDValue = canonical_UINT256();
41
42 PaymentBuilder builder{
43 accountValue,
44 destinationValue,
45 amountValue,
46 sequenceValue,
47 feeValue
48 };
49
50 // Set optional fields
51 builder.setSendMax(sendMaxValue);
52 builder.setPaths(pathsValue);
53 builder.setInvoiceID(invoiceIDValue);
54 builder.setDestinationTag(destinationTagValue);
55 builder.setDeliverMin(deliverMinValue);
56 builder.setCredentialIDs(credentialIDsValue);
57 builder.setDomainID(domainIDValue);
58
59 auto tx = builder.build(publicKey, secretKey);
60
61 std::string reason;
62 EXPECT_TRUE(tx.validate(reason)) << reason;
63
64 // Verify signing was applied
65 EXPECT_FALSE(tx.getSigningPubKey().empty());
66 EXPECT_TRUE(tx.hasTxnSignature());
67
68 // Verify common fields
69 EXPECT_EQ(tx.getAccount(), accountValue);
70 EXPECT_EQ(tx.getSequence(), sequenceValue);
71 EXPECT_EQ(tx.getFee(), feeValue);
72
73 // Verify required fields
74 {
75 auto const& expected = destinationValue;
76 auto const actual = tx.getDestination();
77 expectEqualField(expected, actual, "sfDestination");
78 }
79
80 {
81 auto const& expected = amountValue;
82 auto const actual = tx.getAmount();
83 expectEqualField(expected, actual, "sfAmount");
84 }
85
86 // Verify optional fields
87 {
88 auto const& expected = sendMaxValue;
89 auto const actualOpt = tx.getSendMax();
90 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSendMax should be present";
91 expectEqualField(expected, *actualOpt, "sfSendMax");
92 EXPECT_TRUE(tx.hasSendMax());
93 }
94
95 {
96 auto const& expected = pathsValue;
97 auto const actualOpt = tx.getPaths();
98 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaths should be present";
99 expectEqualField(expected, *actualOpt, "sfPaths");
100 EXPECT_TRUE(tx.hasPaths());
101 }
102
103 {
104 auto const& expected = invoiceIDValue;
105 auto const actualOpt = tx.getInvoiceID();
106 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInvoiceID should be present";
107 expectEqualField(expected, *actualOpt, "sfInvoiceID");
108 EXPECT_TRUE(tx.hasInvoiceID());
109 }
110
111 {
112 auto const& expected = destinationTagValue;
113 auto const actualOpt = tx.getDestinationTag();
114 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present";
115 expectEqualField(expected, *actualOpt, "sfDestinationTag");
116 EXPECT_TRUE(tx.hasDestinationTag());
117 }
118
119 {
120 auto const& expected = deliverMinValue;
121 auto const actualOpt = tx.getDeliverMin();
122 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDeliverMin should be present";
123 expectEqualField(expected, *actualOpt, "sfDeliverMin");
124 EXPECT_TRUE(tx.hasDeliverMin());
125 }
126
127 {
128 auto const& expected = credentialIDsValue;
129 auto const actualOpt = tx.getCredentialIDs();
130 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present";
131 expectEqualField(expected, *actualOpt, "sfCredentialIDs");
132 EXPECT_TRUE(tx.hasCredentialIDs());
133 }
134
135 {
136 auto const& expected = domainIDValue;
137 auto const actualOpt = tx.getDomainID();
138 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present";
139 expectEqualField(expected, *actualOpt, "sfDomainID");
140 EXPECT_TRUE(tx.hasDomainID());
141 }
142
143}
144
145// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper,
146// and verify all fields match.
147TEST(TransactionsPaymentTests, BuilderFromStTxRoundTrip)
148{
149 // Generate a deterministic keypair for signing
150 auto const [publicKey, secretKey] =
151 generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentFromTx"));
152
153 // Common transaction fields
154 auto const accountValue = calcAccountID(publicKey);
155 std::uint32_t const sequenceValue = 2;
156 auto const feeValue = canonical_AMOUNT();
157
158 // Transaction-specific field values
159 auto const destinationValue = canonical_ACCOUNT();
160 auto const amountValue = canonical_AMOUNT();
161 auto const sendMaxValue = canonical_AMOUNT();
162 auto const pathsValue = canonical_PATHSET();
163 auto const invoiceIDValue = canonical_UINT256();
164 auto const destinationTagValue = canonical_UINT32();
165 auto const deliverMinValue = canonical_AMOUNT();
166 auto const credentialIDsValue = canonical_VECTOR256();
167 auto const domainIDValue = canonical_UINT256();
168
169 // Build an initial transaction
170 PaymentBuilder initialBuilder{
171 accountValue,
172 destinationValue,
173 amountValue,
174 sequenceValue,
175 feeValue
176 };
177
178 initialBuilder.setSendMax(sendMaxValue);
179 initialBuilder.setPaths(pathsValue);
180 initialBuilder.setInvoiceID(invoiceIDValue);
181 initialBuilder.setDestinationTag(destinationTagValue);
182 initialBuilder.setDeliverMin(deliverMinValue);
183 initialBuilder.setCredentialIDs(credentialIDsValue);
184 initialBuilder.setDomainID(domainIDValue);
185
186 auto initialTx = initialBuilder.build(publicKey, secretKey);
187
188 // Create builder from existing STTx
189 PaymentBuilder builderFromTx{initialTx.getSTTx()};
190
191 auto rebuiltTx = builderFromTx.build(publicKey, secretKey);
192
193 std::string reason;
194 EXPECT_TRUE(rebuiltTx.validate(reason)) << reason;
195
196 // Verify common fields
197 EXPECT_EQ(rebuiltTx.getAccount(), accountValue);
198 EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue);
199 EXPECT_EQ(rebuiltTx.getFee(), feeValue);
200
201 // Verify required fields
202 {
203 auto const& expected = destinationValue;
204 auto const actual = rebuiltTx.getDestination();
205 expectEqualField(expected, actual, "sfDestination");
206 }
207
208 {
209 auto const& expected = amountValue;
210 auto const actual = rebuiltTx.getAmount();
211 expectEqualField(expected, actual, "sfAmount");
212 }
213
214 // Verify optional fields
215 {
216 auto const& expected = sendMaxValue;
217 auto const actualOpt = rebuiltTx.getSendMax();
218 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSendMax should be present";
219 expectEqualField(expected, *actualOpt, "sfSendMax");
220 }
221
222 {
223 auto const& expected = pathsValue;
224 auto const actualOpt = rebuiltTx.getPaths();
225 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaths should be present";
226 expectEqualField(expected, *actualOpt, "sfPaths");
227 }
228
229 {
230 auto const& expected = invoiceIDValue;
231 auto const actualOpt = rebuiltTx.getInvoiceID();
232 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInvoiceID should be present";
233 expectEqualField(expected, *actualOpt, "sfInvoiceID");
234 }
235
236 {
237 auto const& expected = destinationTagValue;
238 auto const actualOpt = rebuiltTx.getDestinationTag();
239 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present";
240 expectEqualField(expected, *actualOpt, "sfDestinationTag");
241 }
242
243 {
244 auto const& expected = deliverMinValue;
245 auto const actualOpt = rebuiltTx.getDeliverMin();
246 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDeliverMin should be present";
247 expectEqualField(expected, *actualOpt, "sfDeliverMin");
248 }
249
250 {
251 auto const& expected = credentialIDsValue;
252 auto const actualOpt = rebuiltTx.getCredentialIDs();
253 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present";
254 expectEqualField(expected, *actualOpt, "sfCredentialIDs");
255 }
256
257 {
258 auto const& expected = domainIDValue;
259 auto const actualOpt = rebuiltTx.getDomainID();
260 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present";
261 expectEqualField(expected, *actualOpt, "sfDomainID");
262 }
263
264}
265
266// 3) Verify wrapper throws when constructed from wrong transaction type.
267TEST(TransactionsPaymentTests, WrapperThrowsOnWrongTxType)
268{
269 // Build a valid transaction of a different type
270 auto const [pk, sk] =
272 auto const account = calcAccountID(pk);
273
274 AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()};
275 auto wrongTx = wrongBuilder.build(pk, sk);
276
277 EXPECT_THROW(Payment{wrongTx.getSTTx()}, std::runtime_error);
278}
279
280// 4) Verify builder throws when constructed from wrong transaction type.
281TEST(TransactionsPaymentTests, BuilderThrowsOnWrongTxType)
282{
283 // Build a valid transaction of a different type
284 auto const [pk, sk] =
285 generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder"));
286 auto const account = calcAccountID(pk);
287
288 AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()};
289 auto wrongTx = wrongBuilder.build(pk, sk);
290
291 EXPECT_THROW(PaymentBuilder{wrongTx.getSTTx()}, std::runtime_error);
292}
293
294// 5) Build with only required fields and verify optional fields return nullopt.
295TEST(TransactionsPaymentTests, OptionalFieldsReturnNullopt)
296{
297 // Generate a deterministic keypair for signing
298 auto const [publicKey, secretKey] =
299 generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentNullopt"));
300
301 // Common transaction fields
302 auto const accountValue = calcAccountID(publicKey);
303 std::uint32_t const sequenceValue = 3;
304 auto const feeValue = canonical_AMOUNT();
305
306 // Transaction-specific required field values
307 auto const destinationValue = canonical_ACCOUNT();
308 auto const amountValue = canonical_AMOUNT();
309
310 PaymentBuilder builder{
311 accountValue,
312 destinationValue,
313 amountValue,
314 sequenceValue,
315 feeValue
316 };
317
318 // Do NOT set optional fields
319
320 auto tx = builder.build(publicKey, secretKey);
321
322 // Verify optional fields are not present
323 EXPECT_FALSE(tx.hasSendMax());
324 EXPECT_FALSE(tx.getSendMax().has_value());
325 EXPECT_FALSE(tx.hasPaths());
326 EXPECT_FALSE(tx.getPaths().has_value());
327 EXPECT_FALSE(tx.hasInvoiceID());
328 EXPECT_FALSE(tx.getInvoiceID().has_value());
329 EXPECT_FALSE(tx.hasDestinationTag());
330 EXPECT_FALSE(tx.getDestinationTag().has_value());
331 EXPECT_FALSE(tx.hasDeliverMin());
332 EXPECT_FALSE(tx.getDeliverMin().has_value());
333 EXPECT_FALSE(tx.hasCredentialIDs());
334 EXPECT_FALSE(tx.getCredentialIDs().has_value());
335 EXPECT_FALSE(tx.hasDomainID());
336 EXPECT_FALSE(tx.getDomainID().has_value());
337}
338
339}
Payment build(PublicKey const &publicKey, SecretKey const &secretKey)
Build and return the Payment wrapper.
std::shared_ptr< STTx const > getSTTx() const
Get the underlying STTx object.
TEST(TransactionsAccountDeleteTests, BuilderSettersRoundTrip)
Vector256Value canonical_VECTOR256()
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Definition Seed.cpp:57
std::pair< PublicKey, SecretKey > generateKeyPair(KeyType type, Seed const &seed)
Generate a key pair deterministically.
AccountID calcAccountID(PublicKey const &pk)
void expectEqualField(T const &expected, T const &actual, char const *fieldName)