Skip to content

Commit e48a8f6

Browse files
authored
Feature/credentials (#625)
Add support for XLS-70d Credentials: * Update existing transactions * Add Credential ledger object * add Credential integration tests * Add unit and integration tests. * Update testnet faucet url
1 parent df27b56 commit e48a8f6

File tree

81 files changed

+6711
-1658
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+6711
-1658
lines changed

V5_MIGRATION.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
## Version 4 to Version 5 Migration Guide
2+
3+
This guide explains a breaking change in v5.0.0 and how to upgrade from v4.1.0 to v5.0.0.
4+
5+
With the [XLS-0070-credentials](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0070-credentials) amendment, the
6+
`DepositPreAuth` transaction can now preauthorize **credentials**, not just an account like before.
7+
8+
Because of this change, new `DepositPreAuthObject` ledger objects on the XRPL might have:
9+
10+
- **No `Authorize` field**
11+
- A new field called **`AuthorizeCredentials`**
12+
13+
So in v5.0.0, the `Authorize` field is now optional.
14+
15+
When calling `ledger_entry` or `account_objects` in rippled (starting v5.0.0), the response for a `DepositPreAuthObject`
16+
will include **either** `Authorize` **or** `AuthorizeCredentials`, **not both**.
17+
18+
Before using the response, check if the `Authorize` field is present.

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtils.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
import org.xrpl.xrpl4j.model.transactions.CheckCash;
4545
import org.xrpl.xrpl4j.model.transactions.CheckCreate;
4646
import org.xrpl.xrpl4j.model.transactions.Clawback;
47+
import org.xrpl.xrpl4j.model.transactions.CredentialAccept;
48+
import org.xrpl.xrpl4j.model.transactions.CredentialCreate;
49+
import org.xrpl.xrpl4j.model.transactions.CredentialDelete;
4750
import org.xrpl.xrpl4j.model.transactions.DepositPreAuth;
4851
import org.xrpl.xrpl4j.model.transactions.DidDelete;
4952
import org.xrpl.xrpl4j.model.transactions.DidSet;
@@ -416,6 +419,18 @@ public <T extends Transaction> SingleSignedTransaction<T> addSignatureToTransact
416419
transactionWithSignature = MpTokenIssuanceSet.builder().from((MpTokenIssuanceSet) transaction)
417420
.transactionSignature(signature)
418421
.build();
422+
} else if (CredentialCreate.class.isAssignableFrom(transaction.getClass())) {
423+
transactionWithSignature = CredentialCreate.builder().from((CredentialCreate) transaction)
424+
.transactionSignature(signature)
425+
.build();
426+
} else if (CredentialAccept.class.isAssignableFrom(transaction.getClass())) {
427+
transactionWithSignature = CredentialAccept.builder().from((CredentialAccept) transaction)
428+
.transactionSignature(signature)
429+
.build();
430+
} else if (CredentialDelete.class.isAssignableFrom(transaction.getClass())) {
431+
transactionWithSignature = CredentialDelete.builder().from((CredentialDelete) transaction)
432+
.transactionSignature(signature)
433+
.build();
419434
} else {
420435
// Should never happen, but will in a unit test if we miss one.
421436
throw new IllegalArgumentException("Signing fields could not be added to the transaction.");
@@ -647,6 +662,18 @@ public <T extends Transaction> T addMultiSignaturesToTransaction(T transaction,
647662
transactionWithSignatures = MpTokenIssuanceSet.builder().from((MpTokenIssuanceSet) transaction)
648663
.signers(signers)
649664
.build();
665+
} else if (CredentialCreate.class.isAssignableFrom(transaction.getClass())) {
666+
transactionWithSignatures = CredentialCreate.builder().from((CredentialCreate) transaction)
667+
.signers(signers)
668+
.build();
669+
} else if (CredentialAccept.class.isAssignableFrom(transaction.getClass())) {
670+
transactionWithSignatures = CredentialAccept.builder().from((CredentialAccept) transaction)
671+
.signers(signers)
672+
.build();
673+
} else if (CredentialDelete.class.isAssignableFrom(transaction.getClass())) {
674+
transactionWithSignatures = CredentialDelete.builder().from((CredentialDelete) transaction)
675+
.signers(signers)
676+
.build();
650677
} else {
651678
// Should never happen, but will in a unit test if we miss one.
652679
throw new IllegalArgumentException("Signing fields could not be added to the transaction.");

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/accounts/AccountObjectsRequestParams.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
* Licensed under the Apache License, Version 2.0 (the "License");
1010
* you may not use this file except in compliance with the License.
1111
* You may obtain a copy of the License at
12-
*
12+
*
1313
* http://www.apache.org/licenses/LICENSE-2.0
14-
*
14+
*
1515
* Unless required by applicable law or agreed to in writing, software
1616
* distributed under the License is distributed on an "AS IS" BASIS,
1717
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -93,8 +93,8 @@ default boolean deletionBlockersOnly() {
9393
}
9494

9595
/**
96-
* Specifies the ledger version to request. A ledger version can be specified by ledger hash,
97-
* numerical ledger index, or a shortcut value.
96+
* Specifies the ledger version to request. A ledger version can be specified by ledger hash, numerical ledger index,
97+
* or a shortcut value.
9898
*
9999
* @return A {@link LedgerSpecifier} specifying the ledger version to request.
100100
*/
@@ -125,6 +125,10 @@ enum AccountObjectType {
125125
* Check account object type.
126126
*/
127127
CHECK("check"),
128+
/**
129+
* Credential account object type.
130+
*/
131+
CREDENTIAL("credential"),
128132
/**
129133
* Desposit pre auth account object type.
130134
*/
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.xrpl.xrpl4j.model.client.ledger;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
5+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
6+
import org.immutables.value.Value.Immutable;
7+
import org.xrpl.xrpl4j.model.ledger.CredentialObject;
8+
import org.xrpl.xrpl4j.model.transactions.Address;
9+
import org.xrpl.xrpl4j.model.transactions.CredentialType;
10+
11+
/**
12+
* Parameters that uniquely identify an {@link CredentialObject} on ledger. Can be used in a
13+
* {@link LedgerEntryRequestParams} to request an {@link CredentialObject}.
14+
*/
15+
@Immutable
16+
@JsonSerialize(as = ImmutableCredentialLedgerEntryParams.class)
17+
@JsonDeserialize(as = ImmutableCredentialLedgerEntryParams.class)
18+
public interface CredentialLedgerEntryParams {
19+
20+
/**
21+
* Construct a {@code CredentialLedgerEntryParams} builder.
22+
*
23+
* @return An {@link ImmutableCredentialLedgerEntryParams.Builder}.
24+
*/
25+
static ImmutableCredentialLedgerEntryParams.Builder builder() {
26+
return ImmutableCredentialLedgerEntryParams.builder();
27+
}
28+
29+
/**
30+
* The subject of the credential.
31+
*
32+
* @return The unique {@link Address} of the subject of this credential.
33+
*/
34+
Address subject();
35+
36+
/**
37+
* The issuer of the credential.
38+
*
39+
* @return The unique {@link Address} of the issuer of this credential.
40+
*/
41+
Address issuer();
42+
43+
/**
44+
* A (hex-encoded) value to identify the type of credential from the issuer.
45+
*
46+
* @return A {@link CredentialType} defining the type of credential.
47+
*/
48+
@JsonProperty("credential_type")
49+
CredentialType credentialType();
50+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package org.xrpl.xrpl4j.model.client.ledger;
2+
3+
/*-
4+
* ========================LICENSE_START=================================
5+
* xrpl4j :: model
6+
* %%
7+
* Copyright (C) 2020 - 2022 XRPL Foundation and its contributors
8+
* %%
9+
* Licensed under the Apache License, Version 2.0 (the "License");
10+
* you may not use this file except in compliance with the License.
11+
* You may obtain a copy of the License at
12+
*
13+
* http://www.apache.org/licenses/LICENSE-2.0
14+
*
15+
* Unless required by applicable law or agreed to in writing, software
16+
* distributed under the License is distributed on an "AS IS" BASIS,
17+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* See the License for the specific language governing permissions and
19+
* limitations under the License.
20+
* =========================LICENSE_END==================================
21+
*/
22+
23+
import com.fasterxml.jackson.annotation.JsonProperty;
24+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
25+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
26+
import org.immutables.value.Value;
27+
import org.xrpl.xrpl4j.model.transactions.Address;
28+
import org.xrpl.xrpl4j.model.transactions.CredentialType;
29+
30+
/**
31+
* {@link DepositPreAuthCredential} inner object with Issuer and CredentialType details.
32+
*/
33+
@Value.Immutable
34+
@JsonSerialize(as = ImmutableDepositPreAuthCredential.class)
35+
@JsonDeserialize(as = ImmutableDepositPreAuthCredential.class)
36+
public interface DepositPreAuthCredential {
37+
38+
/**
39+
* Construct a builder for this class.
40+
*
41+
* @return An {@link ImmutableDepositPreAuthCredential.Builder}.
42+
*/
43+
static ImmutableDepositPreAuthCredential.Builder builder() {
44+
return ImmutableDepositPreAuthCredential.builder();
45+
}
46+
47+
/**
48+
* The issuer of the credential.
49+
*
50+
* @return The unique {@link Address} of the issuer this credential.
51+
*/
52+
@JsonProperty("issuer")
53+
Address issuer();
54+
55+
/**
56+
* A (hex-encoded) value to identify the type of credential from the issuer.
57+
*
58+
* @return A {@link CredentialType} defining the type of credential.
59+
*/
60+
@JsonProperty("credential_type")
61+
CredentialType credentialType();
62+
}

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/DepositPreAuthLedgerEntryParams.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
package org.xrpl.xrpl4j.model.client.ledger;
22

3+
import com.fasterxml.jackson.annotation.JsonProperty;
34
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
45
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
6+
import com.google.common.base.Preconditions;
7+
import org.immutables.value.Value;
58
import org.immutables.value.Value.Immutable;
69
import org.xrpl.xrpl4j.model.transactions.Address;
710

11+
import java.util.HashSet;
12+
import java.util.List;
13+
import java.util.Optional;
14+
815
/**
916
* Parameters that uniquely identify a {@link org.xrpl.xrpl4j.model.ledger.DepositPreAuthObject} on ledger that can be
1017
* used in a {@link LedgerEntryRequestParams} to request an {@link org.xrpl.xrpl4j.model.ledger.DepositPreAuthObject}.
@@ -35,6 +42,39 @@ static ImmutableDepositPreAuthLedgerEntryParams.Builder builder() {
3542
*
3643
* @return An {@link Address}.
3744
*/
38-
Address authorized();
45+
Optional<Address> authorized();
46+
47+
/**
48+
* A list of {@link DepositPreAuthCredential} that received the preauthorization.
49+
*
50+
* @return A list of type {@link DepositPreAuthCredential}.
51+
*/
52+
@JsonProperty("authorized_credentials")
53+
List<DepositPreAuthCredential> authorizedCredentials();
54+
55+
/**
56+
* Validate {@link DepositPreAuthLedgerEntryParams#authorizedCredentials} has less than or equal to 8 credentials.
57+
*/
58+
@Value.Check
59+
default void validateCredentialsLength() {
60+
if (!authorizedCredentials().isEmpty()) {
61+
Preconditions.checkArgument(
62+
authorizedCredentials().size() <= 8,
63+
"authorizedCredentials should have less than or equal to 8 items."
64+
);
65+
}
66+
}
3967

68+
/**
69+
* Validate {@link DepositPreAuthLedgerEntryParams#authorizedCredentials} are unique.
70+
*/
71+
@Value.Check
72+
default void validateUniqueCredentials() {
73+
if (!authorizedCredentials().isEmpty()) {
74+
Preconditions.checkArgument(
75+
new HashSet<>(authorizedCredentials()).size() == authorizedCredentials().size(),
76+
"authorizedCredentials should have unique values."
77+
);
78+
}
79+
}
4080
}

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParams.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.xrpl.xrpl4j.model.ledger.AmmObject;
1414
import org.xrpl.xrpl4j.model.ledger.BridgeObject;
1515
import org.xrpl.xrpl4j.model.ledger.CheckObject;
16+
import org.xrpl.xrpl4j.model.ledger.CredentialObject;
1617
import org.xrpl.xrpl4j.model.ledger.DepositPreAuthObject;
1718
import org.xrpl.xrpl4j.model.ledger.DidObject;
1819
import org.xrpl.xrpl4j.model.ledger.EscrowObject;
@@ -199,6 +200,31 @@ static LedgerEntryRequestParams<CheckObject> check(
199200
.build();
200201
}
201202

203+
/**
204+
* Construct a {@link LedgerEntryRequestParams} that requests a {@link CredentialObject} ledger entry.
205+
*
206+
* <p>Note that although the rippled API allows you to specify either the Credential's ID
207+
* or the [subject, issuer and credential_type] of the transaction that created the Credential, this class does not
208+
* allow developers to request a {@link CredentialObject} by ID via this method. Instead, developers should use
209+
* {@link LedgerEntryRequestParams#index()} and specify {@link CredentialObject} as the {@code ledgerObjectClass}
210+
* parameter.</p>
211+
*
212+
* @param params The {@link CredentialLedgerEntryParams} that uniquely identify the {@link CredentialObject}
213+
* on ledger.
214+
* @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from.
215+
*
216+
* @return A {@link LedgerEntryRequestParams} for {@link CredentialObject}.
217+
*/
218+
static LedgerEntryRequestParams<CredentialObject> credential(
219+
CredentialLedgerEntryParams params,
220+
LedgerSpecifier ledgerSpecifier
221+
) {
222+
return ImmutableLedgerEntryRequestParams.<CredentialObject>builder()
223+
.credential(params)
224+
.ledgerSpecifier(ledgerSpecifier)
225+
.build();
226+
}
227+
202228
/**
203229
* Construct a {@link LedgerEntryRequestParams} that requests a {@link EscrowObject} ledger entry.
204230
*
@@ -464,6 +490,13 @@ default boolean binary() {
464490
*/
465491
Optional<Hash256> check();
466492

493+
/**
494+
* Look up a {@link org.xrpl.xrpl4j.model.ledger.CredentialObject} by {@link CredentialLedgerEntryParams}.
495+
*
496+
* @return An optionally-present {@link CredentialLedgerEntryParams}.
497+
*/
498+
Optional<CredentialLedgerEntryParams> credential();
499+
467500
/**
468501
* Look up an {@link org.xrpl.xrpl4j.model.ledger.EscrowObject} by {@link EscrowLedgerEntryParams}.
469502
*
@@ -578,6 +611,10 @@ default Class<T> ledgerObjectClass() {
578611
return (Class<T>) CheckObject.class;
579612
}
580613

614+
if (credential().isPresent()) {
615+
return (Class<T>) CredentialObject.class;
616+
}
617+
581618
if (escrow().isPresent()) {
582619
return (Class<T>) EscrowObject.class;
583620
}

0 commit comments

Comments
 (0)