Skip to content

Commit d63ce0d

Browse files
committed
add tests for Batch.requiredSigners
1 parent 1d10116 commit d63ce0d

2 files changed

Lines changed: 116 additions & 4 deletions

File tree

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeParams.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,8 @@
4848
*
4949
* <p>Every field other than {@link #feeResult()} and {@link #transaction()} has a default, so the common case is
5050
* {@code FeeParams.builder().feeResult(feeResult).transaction(transaction).build()}.
51-
*
52-
* <p>This class will be marked {@link Beta} until the featureBatch and featureSponsorship amendments are enabled on
53-
* mainnet. Its API is subject to change.</p>
5451
*/
5552
@Value.Immutable
56-
@Beta
5753
public interface FeeParams {
5854

5955
/**

xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/BatchTest.java

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,10 +1145,126 @@ void testBatchWithOuterSignerAsMultiSigSignerInBatchSigner() {
11451145
.hasMessageContaining("The Account submitting a Batch transaction must not sign any inner transactions.");
11461146
}
11471147

1148+
// ///////////////
1149+
// requiredSigners()
1150+
// ///////////////
1151+
1152+
@Test
1153+
void requiredSignersEmptyWhenAllInnersBelongToOuterAccount() {
1154+
Batch batch = batchWithInners(ACCOUNT,
1155+
createInnerPayment(ACCOUNT, UnsignedInteger.ONE),
1156+
createInnerPayment(ACCOUNT, UnsignedInteger.valueOf(2)));
1157+
1158+
assertThat(batch.requiredSigners()).isEmpty();
1159+
}
1160+
1161+
@Test
1162+
void requiredSignersAreTheDistinctNonOuterInnerAccounts() {
1163+
Address bob = randomAddress();
1164+
Address carol = randomAddress();
1165+
Batch batch = batchWithInners(ACCOUNT,
1166+
createInnerPayment(bob, UnsignedInteger.ONE),
1167+
createInnerPayment(carol, UnsignedInteger.valueOf(2)));
1168+
1169+
assertThat(batch.requiredSigners()).containsExactlyInAnyOrder(bob, carol);
1170+
}
1171+
1172+
@Test
1173+
void requiredSignersUsesDelegateRatherThanAccount() {
1174+
Address bob = randomAddress();
1175+
Address delegate = randomAddress();
1176+
Batch batch = batchWithInners(ACCOUNT,
1177+
createInnerPayment(bob, delegate, UnsignedInteger.ONE),
1178+
createInnerPayment(ACCOUNT, UnsignedInteger.valueOf(2)));
1179+
1180+
// A delegated inner is authorised by its delegate, so bob (the account) is not required, delegate is.
1181+
assertThat(batch.requiredSigners()).containsExactly(delegate);
1182+
}
1183+
1184+
@Test
1185+
void requiredSignersIncludesSponsorOnlyWhenSponsorSignatureIsPresent() {
1186+
Address bob = randomAddress();
1187+
Address sponsor = randomAddress();
1188+
1189+
Payment outerInner = createInnerPayment(ACCOUNT, UnsignedInteger.valueOf(2));
1190+
1191+
Batch withSponsorSignature = batchWithInners(ACCOUNT,
1192+
sponsoredInner(bob, sponsor, UnsignedInteger.ONE, true), outerInner);
1193+
assertThat(withSponsorSignature.requiredSigners()).containsExactlyInAnyOrder(bob, sponsor);
1194+
1195+
Batch withoutSponsorSignature = batchWithInners(ACCOUNT,
1196+
sponsoredInner(bob, sponsor, UnsignedInteger.ONE, false), outerInner);
1197+
assertThat(withoutSponsorSignature.requiredSigners()).containsExactly(bob);
1198+
}
1199+
1200+
@Test
1201+
void requiredSignersDeduplicatesAcrossInnersAndRoles() {
1202+
Address bob = randomAddress();
1203+
Batch batch = batchWithInners(ACCOUNT,
1204+
createInnerPayment(bob, UnsignedInteger.ONE), // bob as account
1205+
sponsoredInner(randomAddress(), bob, UnsignedInteger.valueOf(2), true)); // bob again as sponsor
1206+
1207+
// bob is required via two different inners/roles, but appears once.
1208+
assertThat(batch.requiredSigners()).hasSize(2).contains(bob);
1209+
}
1210+
1211+
@Test
1212+
void requiredSignersExcludesOuterAccountInEveryRole() {
1213+
Address other = randomAddress();
1214+
Batch batch = batchWithInners(ACCOUNT,
1215+
createInnerPayment(ACCOUNT, UnsignedInteger.ONE), // outer as account
1216+
createInnerPayment(other, ACCOUNT, UnsignedInteger.valueOf(2)), // outer as delegate
1217+
sponsoredInner(other, ACCOUNT, UnsignedInteger.valueOf(3), true)); // outer as sponsor
1218+
1219+
// The outer account authorises its own inners with its Batch signature, so it is never a required signer.
1220+
assertThat(batch.requiredSigners()).containsExactly(other);
1221+
}
1222+
1223+
@Test
1224+
void requiredSignersIsUnmodifiable() {
1225+
Batch batch = batchWithInners(ACCOUNT,
1226+
createInnerPayment(randomAddress(), UnsignedInteger.ONE),
1227+
createInnerPayment(randomAddress(), UnsignedInteger.valueOf(2)));
1228+
1229+
assertThatThrownBy(() -> batch.requiredSigners().clear())
1230+
.isInstanceOf(UnsupportedOperationException.class);
1231+
}
1232+
11481233
// ///////////////
11491234
// Private Helpers
11501235
// ///////////////
11511236

1237+
private static Address randomAddress() {
1238+
return Seed.ed25519Seed().deriveKeyPair().publicKey().deriveAddress();
1239+
}
1240+
1241+
private Batch batchWithInners(Address outerAccount, Payment... inners) {
1242+
final List<RawTransactionWrapper> wrappers = Lists.newArrayList();
1243+
for (Payment inner : inners) {
1244+
wrappers.add(RawTransactionWrapper.of(inner));
1245+
}
1246+
return Batch.builder()
1247+
.account(outerAccount)
1248+
.fee(XrpCurrencyAmount.ofDrops(0))
1249+
.sequence(UnsignedInteger.ONE)
1250+
.flags(BatchFlags.ALL_OR_NOTHING)
1251+
.rawTransactions(wrappers)
1252+
.build();
1253+
}
1254+
1255+
private Payment sponsoredInner(
1256+
Address account, Address sponsor, UnsignedInteger sequence, boolean withSponsorSignature
1257+
) {
1258+
ImmutablePayment.Builder builder = Payment.builder()
1259+
.from(createInnerPayment(account, sequence))
1260+
.sponsor(sponsor)
1261+
.sponsorFlags(SponsorFlags.SPONSOR_RESERVE);
1262+
if (withSponsorSignature) {
1263+
builder.sponsorSignature(SponsorSignature.builder().build());
1264+
}
1265+
return builder.build();
1266+
}
1267+
11521268
private Batch createValidBatch(BatchFlags batchFlags) {
11531269
return Batch.builder()
11541270
.account(ACCOUNT)

0 commit comments

Comments
 (0)