|
24 | 24 | #include <xrpld/app/misc/HashRouter.h>
|
25 | 25 | #include <xrpld/app/misc/Transaction.h>
|
26 | 26 | #include <xrpld/app/tx/apply.h>
|
| 27 | +#include <xrpld/app/tx/detail/Batch.h> |
27 | 28 |
|
28 | 29 | #include <xrpl/protocol/Batch.h>
|
29 | 30 | #include <xrpl/protocol/Feature.h>
|
@@ -317,15 +318,16 @@ class Batch_test : public beast::unit_test::suite
|
317 | 318 | env.close();
|
318 | 319 | }
|
319 | 320 |
|
320 |
| - // temINVALID: Batch: batch cannot have inner batch txn. |
| 321 | + // DEFENSIVE: temINVALID: Batch: batch cannot have inner batch txn. |
| 322 | + // ACTUAL: telENV_RPC_FAILED: isRawTransactionOkay() |
321 | 323 | {
|
322 | 324 | auto const seq = env.seq(alice);
|
323 | 325 | auto const batchFee = batch::calcBatchFee(env, 0, 2);
|
324 | 326 | env(batch::outer(alice, seq, batchFee, tfAllOrNothing),
|
325 | 327 | batch::inner(
|
326 | 328 | batch::outer(alice, seq, batchFee, tfAllOrNothing), seq),
|
327 | 329 | batch::inner(pay(alice, bob, XRP(1)), seq + 2),
|
328 |
| - ter(temINVALID)); |
| 330 | + ter(telENV_RPC_FAILED)); |
329 | 331 | env.close();
|
330 | 332 | }
|
331 | 333 |
|
@@ -3953,6 +3955,176 @@ class Batch_test : public beast::unit_test::suite
|
3953 | 3955 | }
|
3954 | 3956 | }
|
3955 | 3957 |
|
| 3958 | + void |
| 3959 | + testValidateRPCResponse(FeatureBitset features) |
| 3960 | + { |
| 3961 | + // Verifying that the RPC response from submit includes |
| 3962 | + // the account_sequence_available, account_sequence_next, |
| 3963 | + // open_ledger_cost and validated_ledger_index fields. |
| 3964 | + testcase("Validate RPC response"); |
| 3965 | + |
| 3966 | + using namespace jtx; |
| 3967 | + Env env(*this); |
| 3968 | + Account const alice("alice"); |
| 3969 | + Account const bob("bob"); |
| 3970 | + env.fund(XRP(10000), alice, bob); |
| 3971 | + env.close(); |
| 3972 | + |
| 3973 | + // tes |
| 3974 | + { |
| 3975 | + auto const baseFee = env.current()->fees().base; |
| 3976 | + auto const aliceSeq = env.seq(alice); |
| 3977 | + auto jtx = env.jt(pay(alice, bob, XRP(1))); |
| 3978 | + |
| 3979 | + Serializer s; |
| 3980 | + jtx.stx->add(s); |
| 3981 | + auto const jr = env.rpc("submit", strHex(s.slice()))[jss::result]; |
| 3982 | + env.close(); |
| 3983 | + |
| 3984 | + BEAST_EXPECT(jr.isMember(jss::account_sequence_available)); |
| 3985 | + BEAST_EXPECT( |
| 3986 | + jr[jss::account_sequence_available].asUInt() == aliceSeq + 1); |
| 3987 | + BEAST_EXPECT(jr.isMember(jss::account_sequence_next)); |
| 3988 | + BEAST_EXPECT( |
| 3989 | + jr[jss::account_sequence_next].asUInt() == aliceSeq + 1); |
| 3990 | + BEAST_EXPECT(jr.isMember(jss::open_ledger_cost)); |
| 3991 | + BEAST_EXPECT(jr[jss::open_ledger_cost] == to_string(baseFee)); |
| 3992 | + BEAST_EXPECT(jr.isMember(jss::validated_ledger_index)); |
| 3993 | + } |
| 3994 | + |
| 3995 | + // tec failure |
| 3996 | + { |
| 3997 | + auto const baseFee = env.current()->fees().base; |
| 3998 | + auto const aliceSeq = env.seq(alice); |
| 3999 | + env(fset(bob, asfRequireDest)); |
| 4000 | + auto jtx = env.jt(pay(alice, bob, XRP(1)), seq(aliceSeq)); |
| 4001 | + |
| 4002 | + Serializer s; |
| 4003 | + jtx.stx->add(s); |
| 4004 | + auto const jr = env.rpc("submit", strHex(s.slice()))[jss::result]; |
| 4005 | + env.close(); |
| 4006 | + |
| 4007 | + BEAST_EXPECT(jr.isMember(jss::account_sequence_available)); |
| 4008 | + BEAST_EXPECT( |
| 4009 | + jr[jss::account_sequence_available].asUInt() == aliceSeq + 1); |
| 4010 | + BEAST_EXPECT(jr.isMember(jss::account_sequence_next)); |
| 4011 | + BEAST_EXPECT( |
| 4012 | + jr[jss::account_sequence_next].asUInt() == aliceSeq + 1); |
| 4013 | + BEAST_EXPECT(jr.isMember(jss::open_ledger_cost)); |
| 4014 | + BEAST_EXPECT(jr[jss::open_ledger_cost] == to_string(baseFee)); |
| 4015 | + BEAST_EXPECT(jr.isMember(jss::validated_ledger_index)); |
| 4016 | + } |
| 4017 | + |
| 4018 | + // tem failure |
| 4019 | + { |
| 4020 | + auto const baseFee = env.current()->fees().base; |
| 4021 | + auto const aliceSeq = env.seq(alice); |
| 4022 | + auto jtx = env.jt(pay(alice, bob, XRP(1)), seq(aliceSeq + 1)); |
| 4023 | + |
| 4024 | + Serializer s; |
| 4025 | + jtx.stx->add(s); |
| 4026 | + auto const jr = env.rpc("submit", strHex(s.slice()))[jss::result]; |
| 4027 | + env.close(); |
| 4028 | + |
| 4029 | + BEAST_EXPECT(jr.isMember(jss::account_sequence_available)); |
| 4030 | + BEAST_EXPECT( |
| 4031 | + jr[jss::account_sequence_available].asUInt() == aliceSeq); |
| 4032 | + BEAST_EXPECT(jr.isMember(jss::account_sequence_next)); |
| 4033 | + BEAST_EXPECT(jr[jss::account_sequence_next].asUInt() == aliceSeq); |
| 4034 | + BEAST_EXPECT(jr.isMember(jss::open_ledger_cost)); |
| 4035 | + BEAST_EXPECT(jr[jss::open_ledger_cost] == to_string(baseFee)); |
| 4036 | + BEAST_EXPECT(jr.isMember(jss::validated_ledger_index)); |
| 4037 | + } |
| 4038 | + } |
| 4039 | + |
| 4040 | + void |
| 4041 | + testBatchCalculateBaseFee(FeatureBitset features) |
| 4042 | + { |
| 4043 | + using namespace jtx; |
| 4044 | + Env env(*this); |
| 4045 | + Account const alice("alice"); |
| 4046 | + Account const bob("bob"); |
| 4047 | + Account const carol("carol"); |
| 4048 | + env.fund(XRP(10000), alice, bob, carol); |
| 4049 | + env.close(); |
| 4050 | + |
| 4051 | + auto getBaseFee = [&](JTx const& jtx) -> XRPAmount { |
| 4052 | + Serializer s; |
| 4053 | + jtx.stx->add(s); |
| 4054 | + return Batch::calculateBaseFee(*env.current(), *jtx.stx); |
| 4055 | + }; |
| 4056 | + |
| 4057 | + // bad: Inner Batch transaction found |
| 4058 | + { |
| 4059 | + auto const seq = env.seq(alice); |
| 4060 | + XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2); |
| 4061 | + auto jtx = env.jt( |
| 4062 | + batch::outer(alice, seq, batchFee, tfAllOrNothing), |
| 4063 | + batch::inner( |
| 4064 | + batch::outer(alice, seq, batchFee, tfAllOrNothing), seq), |
| 4065 | + batch::inner(pay(alice, bob, XRP(1)), seq + 2)); |
| 4066 | + XRPAmount const txBaseFee = getBaseFee(jtx); |
| 4067 | + BEAST_EXPECT(txBaseFee == XRPAmount(INITIAL_XRP)); |
| 4068 | + } |
| 4069 | + |
| 4070 | + // bad: Raw Transactions array exceeds max entries. |
| 4071 | + { |
| 4072 | + auto const seq = env.seq(alice); |
| 4073 | + XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2); |
| 4074 | + |
| 4075 | + auto jtx = env.jt( |
| 4076 | + batch::outer(alice, seq, batchFee, tfAllOrNothing), |
| 4077 | + batch::inner(pay(alice, bob, XRP(1)), seq + 1), |
| 4078 | + batch::inner(pay(alice, bob, XRP(1)), seq + 2), |
| 4079 | + batch::inner(pay(alice, bob, XRP(1)), seq + 3), |
| 4080 | + batch::inner(pay(alice, bob, XRP(1)), seq + 4), |
| 4081 | + batch::inner(pay(alice, bob, XRP(1)), seq + 5), |
| 4082 | + batch::inner(pay(alice, bob, XRP(1)), seq + 6), |
| 4083 | + batch::inner(pay(alice, bob, XRP(1)), seq + 7), |
| 4084 | + batch::inner(pay(alice, bob, XRP(1)), seq + 8), |
| 4085 | + batch::inner(pay(alice, bob, XRP(1)), seq + 9)); |
| 4086 | + |
| 4087 | + XRPAmount const txBaseFee = getBaseFee(jtx); |
| 4088 | + BEAST_EXPECT(txBaseFee == XRPAmount(INITIAL_XRP)); |
| 4089 | + } |
| 4090 | + |
| 4091 | + // bad: Signers array exceeds max entries. |
| 4092 | + { |
| 4093 | + auto const seq = env.seq(alice); |
| 4094 | + XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2); |
| 4095 | + |
| 4096 | + auto jtx = env.jt( |
| 4097 | + batch::outer(alice, seq, batchFee, tfAllOrNothing), |
| 4098 | + batch::inner(pay(alice, bob, XRP(10)), seq + 1), |
| 4099 | + batch::inner(pay(alice, bob, XRP(5)), seq + 2), |
| 4100 | + batch::sig( |
| 4101 | + bob, |
| 4102 | + carol, |
| 4103 | + alice, |
| 4104 | + bob, |
| 4105 | + carol, |
| 4106 | + alice, |
| 4107 | + bob, |
| 4108 | + carol, |
| 4109 | + alice, |
| 4110 | + alice)); |
| 4111 | + XRPAmount const txBaseFee = getBaseFee(jtx); |
| 4112 | + BEAST_EXPECT(txBaseFee == XRPAmount(INITIAL_XRP)); |
| 4113 | + } |
| 4114 | + |
| 4115 | + // good: |
| 4116 | + { |
| 4117 | + auto const seq = env.seq(alice); |
| 4118 | + XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2); |
| 4119 | + auto jtx = env.jt( |
| 4120 | + batch::outer(alice, seq, batchFee, tfAllOrNothing), |
| 4121 | + batch::inner(pay(alice, bob, XRP(1)), seq + 1), |
| 4122 | + batch::inner(pay(bob, alice, XRP(2)), seq + 2)); |
| 4123 | + XRPAmount const txBaseFee = getBaseFee(jtx); |
| 4124 | + BEAST_EXPECT(txBaseFee == batchFee); |
| 4125 | + } |
| 4126 | + } |
| 4127 | + |
3956 | 4128 | void
|
3957 | 4129 | testWithFeats(FeatureBitset features)
|
3958 | 4130 | {
|
@@ -3983,6 +4155,8 @@ class Batch_test : public beast::unit_test::suite
|
3983 | 4155 | testBatchTxQueue(features);
|
3984 | 4156 | testBatchNetworkOps(features);
|
3985 | 4157 | testBatchDelegate(features);
|
| 4158 | + testValidateRPCResponse(features); |
| 4159 | + testBatchCalculateBaseFee(features); |
3986 | 4160 | }
|
3987 | 4161 |
|
3988 | 4162 | public:
|
|
0 commit comments