Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/maven/jackson.version-2.15.2
Browse files Browse the repository at this point in the history
  • Loading branch information
sappenin authored Sep 20, 2024
2 parents a11340c + be893dd commit f407891
Show file tree
Hide file tree
Showing 30 changed files with 25,299 additions and 117 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ current [BOM](https://howtodoinjava.com/maven/maven-bom-bill-of-materials-depend
<dependency>
<groupId>org.xrpl</groupId>
<artifactId>xrpl4j-bom</artifactId>
<version>3.3.0</version>
<version>3.5.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.75</version>
<version>1.78.1</version>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.common.annotations.Beta;
import feign.Feign;
import feign.Headers;
import feign.Request.Options;
import feign.RequestLine;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
Expand Down Expand Up @@ -71,18 +72,34 @@ public interface JsonRpcClient {
/**
* Constructs a new client for the given url.
*
* @param rippledUrl url for the faucet server.
* @param rippledUrl The {@link HttpUrl} of the node to connect to.
*
* @return A {@link JsonRpcClient} that can make request to {@code rippledUrl}
*/
static JsonRpcClient construct(final HttpUrl rippledUrl) {
Objects.requireNonNull(rippledUrl);

return construct(rippledUrl, new Options());
}

/**
* Constructs a new client for the given url with the given client options.
*
* @param rippledUrl The {@link HttpUrl} of the node to connect to.
* @param options An {@link Options}.
*
* @return A {@link JsonRpcClient}.
*/
static JsonRpcClient construct(HttpUrl rippledUrl, Options options) {
Objects.requireNonNull(rippledUrl);
Objects.requireNonNull(options);

return Feign.builder()
.encoder(new JacksonEncoder(objectMapper))
// rate limiting will return a 503 status that can be retried
.errorDecoder(new RetryStatusDecoder(RETRY_INTERVAL, SERVICE_UNAVAILABLE_STATUS))
.decode404()
.options(options)
.decoder(new OptionalDecoder(new JacksonDecoder(objectMapper)))
.target(JsonRpcClient.class, rippledUrl.toString());
}
Expand Down
29 changes: 29 additions & 0 deletions xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import com.google.common.collect.Range;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.primitives.UnsignedLong;
import feign.Request;
import feign.Request.Options;
import okhttp3.HttpUrl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -99,8 +101,10 @@
import org.xrpl.xrpl4j.model.transactions.Transaction;
import org.xrpl.xrpl4j.model.transactions.TransactionMetadata;

import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

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

/**
* Public constructor that allows for configuration of connect and read timeouts.
*
* <p>Note that any {@link Duration} passed in that is less than one millisecond will result in the actual timeout
* being zero milliseconds. It is therefore advised to never set {@code connectTimeout} or {@code readTimeout} to a
* {@link Duration} less than one millisecond.
*
* @param rippledUrl The {@link HttpUrl} of the node to connect to.
* @param connectTimeout A {@link Duration} indicating the client's connect timeout.
* @param readTimeout A {@link Duration} indicating the client's read timeout.
*/
public XrplClient(
HttpUrl rippledUrl,
Duration connectTimeout,
Duration readTimeout
) {
this(
JsonRpcClient.construct(
rippledUrl,
new Options(connectTimeout.toMillis(), TimeUnit.MILLISECONDS, readTimeout.toMillis(), TimeUnit.MILLISECONDS,
true)
)
);
}

/**
* Required-args constructor (exists for testing purposes only).
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,11 @@
import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
Expand Down Expand Up @@ -599,6 +601,18 @@ public void getXrplClientTest() {
assertThat(new XrplClient(rippledUrl) instanceof JsonRpcClient).isFalse();
}

@Test
void createXrplClientWithDurationTimeouts() {
HttpUrl rippledUrl = HttpUrl.parse("https://s.altnet.rippletest.net:51234");
XrplClient client = new XrplClient(
rippledUrl,
Duration.ofSeconds(1),
Duration.ofMinutes(2)
);

assertThat(client).isInstanceOf(XrplClient.class);
}

@Test
public void submitSingleSignedTransaction() {
BcSignatureService bcSignatureService = new BcSignatureService();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Expand All @@ -31,6 +31,7 @@
import org.xrpl.xrpl4j.codec.binary.BinaryCodecObjectMapperFactory;
import org.xrpl.xrpl4j.codec.binary.math.MathUtils;
import org.xrpl.xrpl4j.codec.binary.serdes.BinaryParser;
import org.xrpl.xrpl4j.model.immutables.FluentCompareTo;

import java.math.BigDecimal;
import java.math.BigInteger;
Expand All @@ -41,7 +42,9 @@
class AmountType extends SerializedType<AmountType> {

public static final BigDecimal MAX_DROPS = new BigDecimal("1e17");
public static final BigDecimal MIN_DROPS = new BigDecimal("-1e17");
public static final BigDecimal MIN_XRP = new BigDecimal("1e-6");
public static final BigDecimal MAX_NEGATIVE_XRP = new BigDecimal("-1e-6");

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

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

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

Expand All @@ -88,8 +91,15 @@ private static void assertXrpIsValid(String amount) {
}
BigDecimal value = new BigDecimal(amount);
if (!value.equals(BigDecimal.ZERO)) {
if (value.compareTo(MIN_XRP) < 0 || value.compareTo(MAX_DROPS) > 0) {
throw new IllegalArgumentException(amount + " is an illegal amount");
final FluentCompareTo<BigDecimal> fluentValue = FluentCompareTo.is(value);
if (value.signum() < 0) { // `value` is negative
if (fluentValue.greaterThan(MAX_NEGATIVE_XRP) || fluentValue.lessThan(MIN_DROPS)) {
throw new IllegalArgumentException(String.format("%s is an illegal amount", amount));
}
} else { // `value` is positive
if (fluentValue.lessThan(MIN_XRP) || fluentValue.greaterThan(MAX_DROPS)) {
throw new IllegalArgumentException(String.format("%s is an illegal amount", amount));
}
}
}
}
Expand Down Expand Up @@ -142,14 +152,19 @@ public AmountType fromJson(JsonNode value) throws JsonProcessingException {
if (value.isValueNode()) {
assertXrpIsValid(value.asText());

UnsignedByteArray number = UnsignedByteArray.fromHex(
final boolean isValueNegative = value.asText().startsWith("-");
final UnsignedByteArray number = UnsignedByteArray.fromHex(
ByteUtils.padded(
UnsignedLong.valueOf(value.asText()).toString(16),
64 / 4
UnsignedLong
.valueOf(isValueNegative ? value.asText().substring(1) : value.asText())
.toString(16),
16 // <-- 64 / 4
)
);
byte[] rawBytes = number.toByteArray();
rawBytes[0] |= 0x40;
final byte[] rawBytes = number.toByteArray();
if (!isValueNegative) {
rawBytes[0] |= 0x40;
}
return new AmountType(UnsignedByteArray.of(rawBytes));
}

Expand All @@ -172,7 +187,7 @@ public AmountType fromJson(JsonNode value) throws JsonProcessingException {
private UnsignedByteArray getAmountBytes(BigDecimal number) {
BigInteger paddedNumber = MathUtils.toPaddedBigInteger(number, 16);
byte[] amountBytes = ByteUtils.toByteArray(paddedNumber, 8);
amountBytes[0] |= 0x80;
amountBytes[0] |= (byte) 0x80;
if (number.compareTo(BigDecimal.ZERO) > 0) {
amountBytes[0] |= 0x40;
}
Expand All @@ -182,8 +197,8 @@ private UnsignedByteArray getAmountBytes(BigDecimal number) {
throw new IllegalArgumentException("exponent out of range");
}
UnsignedByte exponentByte = UnsignedByte.of(97 + exponent - 15);
amountBytes[0] |= exponentByte.asInt() >>> 2;
amountBytes[1] |= (exponentByte.asInt() & 0x03) << 6;
amountBytes[0] |= (byte) (exponentByte.asInt() >>> 2);
amountBytes[1] |= (byte) ((exponentByte.asInt() & 0x03) << 6);

return UnsignedByteArray.of(amountBytes);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* =========================LICENSE_END==================================
*/

import java.math.BigDecimal;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
Expand All @@ -34,9 +35,17 @@ public interface CurrencyAmount {
long ONE_XRP_IN_DROPS = 1_000_000L;
long MAX_XRP = 100_000_000_000L; // <-- per https://xrpl.org/rippleapi-reference.html#value
long MAX_XRP_IN_DROPS = MAX_XRP * ONE_XRP_IN_DROPS;
BigDecimal MAX_XRP_BD = BigDecimal.valueOf(MAX_XRP);

/**
* Handle this {@link CurrencyAmount} depending on its actual polymorphic sub-type.
* Indicates whether this amount is positive or negative.
*
* @return {@code true} if this amount is negative; {@code false} otherwise (i.e., if the value is 0 or positive).
*/
boolean isNegative();

/**
* Handle this {@link CurrencyAmount} depending on its actual polymorphic subtype.
*
* @param xrpCurrencyAmountHandler A {@link Consumer} that is called if this instance is of type
* {@link XrpCurrencyAmount}.
Expand All @@ -60,7 +69,7 @@ default void handle(
}

/**
* Map this {@link CurrencyAmount} to an instance of {@link R}, depending on its actualy polymorphic sub-type.
* Map this {@link CurrencyAmount} to an instance of {@link R}, depending on its actual polymorphic subtype.
*
* @param xrpCurrencyAmountMapper A {@link Function} that is called if this instance is of type
* {@link XrpCurrencyAmount}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Expand All @@ -20,9 +20,13 @@
* =========================LICENSE_END==================================
*/

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;
import org.immutables.value.Value.Auxiliary;
import org.immutables.value.Value.Default;
import org.immutables.value.Value.Derived;

/**
* A {@link CurrencyAmount} for Issued Currencies on the XRP Ledger.
Expand All @@ -45,14 +49,14 @@ public interface IssuedCurrencyAmount extends CurrencyAmount {
String MIN_VALUE = "-9999999999999999e80";

/**
* The smallest possible positive value that an {@link IssuedCurrencyAmount} can have. Put another way,
* this value is the closest an {@link IssuedCurrencyAmount}'s {@link #value()} can be to zero if it is positive.
* The smallest possible positive value that an {@link IssuedCurrencyAmount} can have. Put another way, this value is
* the closest an {@link IssuedCurrencyAmount}'s {@link #value()} can be to zero if it is positive.
*/
String MIN_POSITIVE_VALUE = "1000000000000000e-96";

/**
* The largest possible negative value that an {@link IssuedCurrencyAmount} can have. Put another way,
* this value is the closest an {@link IssuedCurrencyAmount}'s {@link #value()} can be to zero if it is negative.
* The largest possible negative value that an {@link IssuedCurrencyAmount} can have. Put another way, this value is
* the closest an {@link IssuedCurrencyAmount}'s {@link #value()} can be to zero if it is negative.
*/
String MAX_NEGATIVE_VALUE = "-1000000000000000e-96";

Expand All @@ -67,10 +71,10 @@ static ImmutableIssuedCurrencyAmount.Builder builder() {

/**
* Quoted decimal representation of the amount of currency. This can include scientific notation, such as 1.23e11
* meaning 123,000,000,000. Both e and E may be used. Note that while this implementation merely holds a {@link
* String} with no value restrictions, the XRP Ledger does not tolerate unlimited precision values. Instead, non-XRP
* values (i.e., values held in this object) can have up to 16 decimal digits of precision, with a maximum value of
* 9999999999999999e80. The smallest positive non-XRP value is 1e-81.
* meaning 123,000,000,000. Both e and E may be used. Note that while this implementation merely holds a
* {@link String} with no value restrictions, the XRP Ledger does not tolerate unlimited precision values. Instead,
* non-XRP values (i.e., values held in this object) can have up to 16 decimal digits of precision, with a maximum
* value of 9999999999999999e80. The smallest positive non-XRP value is 1e-81.
*
* @return A {@link String} containing the amount of this issued currency.
*/
Expand All @@ -91,4 +95,16 @@ static ImmutableIssuedCurrencyAmount.Builder builder() {
*/
Address issuer();

/**
* Indicates whether this amount is positive or negative.
*
* @return {@code true} if this amount is negative; {@code false} otherwise (i.e., if the value is 0 or positive).
*/
@Derived
@JsonIgnore // <-- This is not actually part of the binary serialization format, so exclude from JSON
@Auxiliary
default boolean isNegative() {
return value().startsWith("-");
}

}
Loading

0 comments on commit f407891

Please sign in to comment.