Skip to content

Commit 4c28811

Browse files
authored
Merge branch 'main' into feat/nft
2 parents 5bff1cf + 9cc99a3 commit 4c28811

File tree

48 files changed

+26388
-248
lines changed

Some content is hidden

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

48 files changed

+26388
-248
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ and address generation, transaction serialization and signing, provides useful J
1515
- Example usage can be found in the `xrpl4j-integration-tests`
1616
module [here](https://github.com/XRPLF/xrpl4j/tree/main/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests).
1717

18-
## Usage
18+
## Usage
1919

2020
### Requirements
2121

@@ -33,7 +33,7 @@ current [BOM](https://howtodoinjava.com/maven/maven-bom-bill-of-materials-depend
3333
<dependency>
3434
<groupId>org.xrpl</groupId>
3535
<artifactId>xrpl4j-bom</artifactId>
36-
<version>3.4.0</version>
36+
<version>4.0.2</version>
3737
<type>pom</type>
3838
<scope>import</scope>
3939
</dependency>
@@ -225,7 +225,7 @@ canonical JSON encoding). Read more about each here:
225225

226226
Xrpl4j is structured as a Maven multi-module project, with the following modules:
227227

228-
- **xrpl4j-core**: [![javadoc](https://javadoc.io/badge2/org.xrpl/xrpl4j-binary-codec/javadoc.svg?color=blue)](https://javadoc.io/doc/org.xrpl/xrpl4j-binary-codec)
228+
- **xrpl4j-core**: [![javadoc](https://javadoc.io/badge2/org.xrpl/xrpl4j-core/javadoc.svg?color=blue)](https://javadoc.io/doc/org.xrpl/xrpl4j-core)
229229
- Provides core primitives like seeds, public/private keys definitions (supports secp256k1 and ed25519 key types
230230
and signing algorithms), signature interfaces, address and binary codecs etc. Also provides Java objects which model XRP Ledger objects,
231231
as well as request parameters and response results for the `rippled` websocket and JSON RPC APIs.

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@
181181
<dependency>
182182
<groupId>org.bouncycastle</groupId>
183183
<artifactId>bcprov-jdk18on</artifactId>
184-
<version>1.75</version>
184+
<version>1.78.1</version>
185185
</dependency>
186186
<dependency>
187187
<groupId>org.awaitility</groupId>
@@ -293,7 +293,7 @@
293293
<jackson.version>2.14.2</jackson.version>
294294
<feign.version>12.3</feign.version>
295295
<slf4j.version>2.0.7</slf4j.version>
296-
<junit-jupiter.version>5.10.0</junit-jupiter.version>
296+
<junit-jupiter.version>5.10.1</junit-jupiter.version>
297297
<guava.version>32.1.1-jre</guava.version>
298298
</properties>
299299

@@ -375,7 +375,7 @@
375375
<!-- org.apache.maven.plugins:maven-source-plugin -->
376376
<plugin>
377377
<artifactId>maven-source-plugin</artifactId>
378-
<version>3.1.0</version>
378+
<version>3.3.0</version>
379379
<executions>
380380
<execution>
381381
<id>attach-sources</id>

xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/JsonRpcClient.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.common.annotations.Beta;
2828
import feign.Feign;
2929
import feign.Headers;
30+
import feign.Request.Options;
3031
import feign.RequestLine;
3132
import feign.jackson.JacksonDecoder;
3233
import feign.jackson.JacksonEncoder;
@@ -71,18 +72,34 @@ public interface JsonRpcClient {
7172
/**
7273
* Constructs a new client for the given url.
7374
*
74-
* @param rippledUrl url for the faucet server.
75+
* @param rippledUrl The {@link HttpUrl} of the node to connect to.
7576
*
7677
* @return A {@link JsonRpcClient} that can make request to {@code rippledUrl}
7778
*/
7879
static JsonRpcClient construct(final HttpUrl rippledUrl) {
7980
Objects.requireNonNull(rippledUrl);
8081

82+
return construct(rippledUrl, new Options());
83+
}
84+
85+
/**
86+
* Constructs a new client for the given url with the given client options.
87+
*
88+
* @param rippledUrl The {@link HttpUrl} of the node to connect to.
89+
* @param options An {@link Options}.
90+
*
91+
* @return A {@link JsonRpcClient}.
92+
*/
93+
static JsonRpcClient construct(HttpUrl rippledUrl, Options options) {
94+
Objects.requireNonNull(rippledUrl);
95+
Objects.requireNonNull(options);
96+
8197
return Feign.builder()
8298
.encoder(new JacksonEncoder(objectMapper))
8399
// rate limiting will return a 503 status that can be retried
84100
.errorDecoder(new RetryStatusDecoder(RETRY_INTERVAL, SERVICE_UNAVAILABLE_STATUS))
85101
.decode404()
102+
.options(options)
86103
.decoder(new OptionalDecoder(new JacksonDecoder(objectMapper)))
87104
.target(JsonRpcClient.class, rippledUrl.toString());
88105
}

xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import com.google.common.collect.Range;
2929
import com.google.common.primitives.UnsignedInteger;
3030
import com.google.common.primitives.UnsignedLong;
31+
import feign.Request;
32+
import feign.Request.Options;
3133
import okhttp3.HttpUrl;
3234
import org.slf4j.Logger;
3335
import org.slf4j.LoggerFactory;
@@ -99,8 +101,10 @@
99101
import org.xrpl.xrpl4j.model.transactions.Transaction;
100102
import org.xrpl.xrpl4j.model.transactions.TransactionMetadata;
101103

104+
import java.time.Duration;
102105
import java.util.Objects;
103106
import java.util.Optional;
107+
import java.util.concurrent.TimeUnit;
104108

105109
/**
106110
* <p>A client which wraps a rippled network client and is responsible for higher order functionality such as signing
@@ -127,6 +131,31 @@ public XrplClient(final HttpUrl rippledUrl) {
127131
this(JsonRpcClient.construct(rippledUrl));
128132
}
129133

134+
/**
135+
* Public constructor that allows for configuration of connect and read timeouts.
136+
*
137+
* <p>Note that any {@link Duration} passed in that is less than one millisecond will result in the actual timeout
138+
* being zero milliseconds. It is therefore advised to never set {@code connectTimeout} or {@code readTimeout} to a
139+
* {@link Duration} less than one millisecond.
140+
*
141+
* @param rippledUrl The {@link HttpUrl} of the node to connect to.
142+
* @param connectTimeout A {@link Duration} indicating the client's connect timeout.
143+
* @param readTimeout A {@link Duration} indicating the client's read timeout.
144+
*/
145+
public XrplClient(
146+
HttpUrl rippledUrl,
147+
Duration connectTimeout,
148+
Duration readTimeout
149+
) {
150+
this(
151+
JsonRpcClient.construct(
152+
rippledUrl,
153+
new Options(connectTimeout.toMillis(), TimeUnit.MILLISECONDS, readTimeout.toMillis(), TimeUnit.MILLISECONDS,
154+
true)
155+
)
156+
);
157+
}
158+
130159
/**
131160
* Required-args constructor (exists for testing purposes only).
132161
*

xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,11 @@
122122
import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount;
123123

124124
import java.math.BigDecimal;
125+
import java.time.Duration;
125126
import java.time.ZonedDateTime;
126127
import java.util.Optional;
127128
import java.util.Set;
129+
import java.util.concurrent.TimeUnit;
128130
import java.util.concurrent.atomic.AtomicReference;
129131

130132
/**
@@ -599,6 +601,18 @@ public void getXrplClientTest() {
599601
assertThat(new XrplClient(rippledUrl) instanceof JsonRpcClient).isFalse();
600602
}
601603

604+
@Test
605+
void createXrplClientWithDurationTimeouts() {
606+
HttpUrl rippledUrl = HttpUrl.parse("https://s.altnet.rippletest.net:51234");
607+
XrplClient client = new XrplClient(
608+
rippledUrl,
609+
Duration.ofSeconds(1),
610+
Duration.ofMinutes(2)
611+
);
612+
613+
assertThat(client).isInstanceOf(XrplClient.class);
614+
}
615+
602616
@Test
603617
public void submitSingleSignedTransaction() {
604618
BcSignatureService bcSignatureService = new BcSignatureService();

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/AmountType.java

Lines changed: 35 additions & 20 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.
@@ -31,6 +31,7 @@
3131
import org.xrpl.xrpl4j.codec.binary.BinaryCodecObjectMapperFactory;
3232
import org.xrpl.xrpl4j.codec.binary.math.MathUtils;
3333
import org.xrpl.xrpl4j.codec.binary.serdes.BinaryParser;
34+
import org.xrpl.xrpl4j.model.immutables.FluentCompareTo;
3435

3536
import java.math.BigDecimal;
3637
import java.math.BigInteger;
@@ -41,7 +42,9 @@
4142
class AmountType extends SerializedType<AmountType> {
4243

4344
public static final BigDecimal MAX_DROPS = new BigDecimal("1e17");
45+
public static final BigDecimal MIN_DROPS = new BigDecimal("-1e17");
4446
public static final BigDecimal MIN_XRP = new BigDecimal("1e-6");
47+
public static final BigDecimal MAX_NEGATIVE_XRP = new BigDecimal("-1e-6");
4548

4649
public static final String DEFAULT_AMOUNT_HEX = "4000000000000000";
4750
public static final String ZERO_CURRENCY_AMOUNT_HEX = "8000000000000000";
@@ -50,18 +53,18 @@ class AmountType extends SerializedType<AmountType> {
5053
private static final int MAX_IOU_PRECISION = 16;
5154

5255
/**
53-
* According to <a href=https://xrpl.org/currency-formats.html#currency-formats>xrpl.org</a>,
54-
* the minimum token value exponent is -96. However, because the value field is converted from a {@link String}
55-
* to a {@link BigDecimal} when encoding/decoding, and because {@link BigDecimal} defaults to using single
56-
* digit number, the minimum exponent in this context is -96 + 15, as XRPL amounts have a precision of 15 digits.
56+
* According to <a href=https://xrpl.org/currency-formats.html#currency-formats>xrpl.org</a>, the minimum token value
57+
* exponent is -96. However, because the value field is converted from a {@link String} to a {@link BigDecimal} when
58+
* encoding/decoding, and because {@link BigDecimal} defaults to using single digit number, the minimum exponent in
59+
* this context is -96 + 15, as XRPL amounts have a precision of 15 digits.
5760
*/
5861
private static final int MIN_IOU_EXPONENT = -81;
5962

6063
/**
61-
* According to <a href=https://xrpl.org/currency-formats.html#currency-formats>xrpl.org</a>,
62-
* the maximum token value exponent is 80. However, because the value field is converted from a {@link String}
63-
* to a {@link BigDecimal} when encoding/decoding, and because {@link BigDecimal} defaults to using single
64-
* digit number, the maximum exponent in this context is 80 + 15, as XRPL amounts have a precision of 15 digits.
64+
* According to <a href=https://xrpl.org/currency-formats.html#currency-formats>xrpl.org</a>, the maximum token value
65+
* exponent is 80. However, because the value field is converted from a {@link String} to a {@link BigDecimal} when
66+
* encoding/decoding, and because {@link BigDecimal} defaults to using single digit number, the maximum exponent in
67+
* this context is 80 + 15, as XRPL amounts have a precision of 15 digits.
6568
*/
6669
private static final int MAX_IOU_EXPONENT = 95;
6770

@@ -88,8 +91,15 @@ private static void assertXrpIsValid(String amount) {
8891
}
8992
BigDecimal value = new BigDecimal(amount);
9093
if (!value.equals(BigDecimal.ZERO)) {
91-
if (value.compareTo(MIN_XRP) < 0 || value.compareTo(MAX_DROPS) > 0) {
92-
throw new IllegalArgumentException(amount + " is an illegal amount");
94+
final FluentCompareTo<BigDecimal> fluentValue = FluentCompareTo.is(value);
95+
if (value.signum() < 0) { // `value` is negative
96+
if (fluentValue.greaterThan(MAX_NEGATIVE_XRP) || fluentValue.lessThan(MIN_DROPS)) {
97+
throw new IllegalArgumentException(String.format("%s is an illegal amount", amount));
98+
}
99+
} else { // `value` is positive
100+
if (fluentValue.lessThan(MIN_XRP) || fluentValue.greaterThan(MAX_DROPS)) {
101+
throw new IllegalArgumentException(String.format("%s is an illegal amount", amount));
102+
}
93103
}
94104
}
95105
}
@@ -142,14 +152,19 @@ public AmountType fromJson(JsonNode value) throws JsonProcessingException {
142152
if (value.isValueNode()) {
143153
assertXrpIsValid(value.asText());
144154

145-
UnsignedByteArray number = UnsignedByteArray.fromHex(
155+
final boolean isValueNegative = value.asText().startsWith("-");
156+
final UnsignedByteArray number = UnsignedByteArray.fromHex(
146157
ByteUtils.padded(
147-
UnsignedLong.valueOf(value.asText()).toString(16),
148-
64 / 4
158+
UnsignedLong
159+
.valueOf(isValueNegative ? value.asText().substring(1) : value.asText())
160+
.toString(16),
161+
16 // <-- 64 / 4
149162
)
150163
);
151-
byte[] rawBytes = number.toByteArray();
152-
rawBytes[0] |= 0x40;
164+
final byte[] rawBytes = number.toByteArray();
165+
if (!isValueNegative) {
166+
rawBytes[0] |= 0x40;
167+
}
153168
return new AmountType(UnsignedByteArray.of(rawBytes));
154169
}
155170

@@ -172,7 +187,7 @@ public AmountType fromJson(JsonNode value) throws JsonProcessingException {
172187
private UnsignedByteArray getAmountBytes(BigDecimal number) {
173188
BigInteger paddedNumber = MathUtils.toPaddedBigInteger(number, 16);
174189
byte[] amountBytes = ByteUtils.toByteArray(paddedNumber, 8);
175-
amountBytes[0] |= 0x80;
190+
amountBytes[0] |= (byte) 0x80;
176191
if (number.compareTo(BigDecimal.ZERO) > 0) {
177192
amountBytes[0] |= 0x40;
178193
}
@@ -182,8 +197,8 @@ private UnsignedByteArray getAmountBytes(BigDecimal number) {
182197
throw new IllegalArgumentException("exponent out of range");
183198
}
184199
UnsignedByte exponentByte = UnsignedByte.of(97 + exponent - 15);
185-
amountBytes[0] |= exponentByte.asInt() >>> 2;
186-
amountBytes[1] |= (exponentByte.asInt() & 0x03) << 6;
200+
amountBytes[0] |= (byte) (exponentByte.asInt() >>> 2);
201+
amountBytes[1] |= (byte) ((exponentByte.asInt() & 0x03) << 6);
187202

188203
return UnsignedByteArray.of(amountBytes);
189204
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public interface LedgerEntryResult<T extends LedgerObject> extends XrplResult {
2626
/**
2727
* Construct a {@code LedgerEntryResult} builder.
2828
*
29+
* @param <T> The type of {@link LedgerObject}.
30+
*
2931
* @return An {@link ImmutableLedgerEntryResult.Builder}.
3032
*/
3133
static <T extends LedgerObject> ImmutableLedgerEntryResult.Builder<T> builder() {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.xrpl.xrpl4j.model.jackson.modules;
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.core.JsonParser;
24+
import com.fasterxml.jackson.databind.DeserializationContext;
25+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
26+
import com.google.common.primitives.UnsignedLong;
27+
import org.xrpl.xrpl4j.model.transactions.SetFee;
28+
import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount;
29+
30+
import java.io.IOException;
31+
32+
/**
33+
* Custom Jackson deserializer for {@link XrpCurrencyAmount} instances found in {@link SetFee}.
34+
*
35+
* <p>Before the <a href="https://xrpl.org/resources/known-amendments/#xrpfees">XRPFees amendment</a>, a {@link SetFee}
36+
* transaction serializes its `BaseFee` to a hex string. After the
37+
* <a href="https://xrpl.org/resources/known-amendments/#xrpfees">XRPFees amendment</a>, a {@link SetFee} transaction
38+
* serializes its `BaseFee` to a decimal string.
39+
*
40+
* @see "https://xrpl.org/resources/known-amendments/#xrpfees"
41+
*/
42+
public class BaseFeeDropsDeserializer extends StdDeserializer<XrpCurrencyAmount> {
43+
44+
/**
45+
* No-args constructor.
46+
*/
47+
public BaseFeeDropsDeserializer() {
48+
super(XrpCurrencyAmount.class);
49+
}
50+
51+
@Override
52+
public XrpCurrencyAmount deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
53+
// Pre-XRPFees, SetFee transactions serialize `BaseFee` to a hex string. Post XRPFees SetFee transactions
54+
// have a `BaseFeeDrops` field which is a decimal string.
55+
if (jsonParser.currentName().equals("BaseFee")) {
56+
return XrpCurrencyAmount.of(UnsignedLong.valueOf(jsonParser.getText(), 16));
57+
} else {
58+
return XrpCurrencyAmount.ofDrops(jsonParser.getValueAsLong());
59+
}
60+
}
61+
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,14 @@ default LedgerEntryType ledgerEntryType() {
239239
@JsonProperty("AMMID")
240240
Optional<Hash256> ammId();
241241

242+
/**
243+
* An arbitrary 256-bit value that users can set.
244+
*
245+
* @return An {@link Optional} {@link String}.
246+
*/
247+
@JsonProperty("WalletLocator")
248+
Optional<String> walletLocator();
249+
242250
/**
243251
* The unique ID of this {@link AccountRootObject} ledger object.
244252
*

0 commit comments

Comments
 (0)