Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance Transaction serialization and deserialization #595

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
import java.io.IOException;

/**
* Custom deserializer for {@link Transaction}s, which deserializes to a specific {@link Transaction} type
* based on the TransactionType JSON field.
* Custom deserializer for instances of {@link Transaction} that deserialize to a specific {@link Transaction} type
* based on the`TransactionType` JSON field.
*/
public class TransactionDeserializer extends StdDeserializer<Transaction> {

Expand All @@ -52,12 +52,12 @@ public Transaction deserialize(JsonParser jsonParser, DeserializationContext ctx
TransactionType transactionType = TransactionType.forValue(objectNode.get("TransactionType").asText());
final Class<? extends Transaction> transactionTypeClass = Transaction.typeMap.inverse().get(transactionType);

// Fixes #590 by removing the `Account` property from any incoming `UnlModify` JSON about to be deserialized.
// This fixes #590 because the JSON returned by the rippled/clio API v1 has a bug where the account value in
// `UnlModify` transactions is an empty string. When this value is deserialized, an exception is thrown because
// the empty string value is not a valid `Address`. By removing the property from incoming JSON, the Java value
// for the `Account` property is always set to ACCOUNT_ZERO via a default method. One other side effect of this
// fix is that `Account` property will not be errantly added to `unknownFields map of the ultimate Java object,
// Fixes #590 by removing the `Account` property from any incoming `UnlModify` JSON about to be deserialized.
// This fixes #590 because the JSON returned by the rippled/clio API v1 has a bug where the account value in
// `UnlModify` transactions is an empty string. When this value is deserialized, an exception is thrown because
// the empty string value is not a valid `Address`. By removing the property from incoming JSON, the Java value
// for the `Account` property is always set to ACCOUNT_ZERO via a default method. One other side effect of this
// fix is that `Account` property will not be errantly added to `unknownFields` map of the ultimate Java object,
// which is incorrect.
if (UnlModify.class.isAssignableFrom(transactionTypeClass)) {
objectNode.remove("Account");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray;
import org.xrpl.xrpl4j.model.client.common.LedgerIndex;
import org.xrpl.xrpl4j.model.client.path.BookOffersOffer;
import org.xrpl.xrpl4j.model.client.serverinfo.ServerInfo;
import org.xrpl.xrpl4j.model.flags.Flags;
import org.xrpl.xrpl4j.model.transactions.CurrencyAmount;
import org.xrpl.xrpl4j.model.transactions.Transaction;
import org.xrpl.xrpl4j.model.transactions.metadata.AffectedNode;
import org.xrpl.xrpl4j.model.transactions.metadata.MetaLedgerEntryType;

/**
* Jackson module for the xrpl4j-model project.
Expand Down Expand Up @@ -63,7 +61,7 @@ public Xrpl4jModule() {
addDeserializer(Transaction.class, new TransactionDeserializer());

addDeserializer(ServerInfo.class, new ServerInfoDeserializer());

addSerializer(UnsignedByteArray.class, new UnsignedByteArraySerializer());
addDeserializer(UnsignedByteArray.class, new UnsignedByteArrayDeserializer());

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 @@ -23,9 +23,9 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
import org.immutables.value.Value;
import org.immutables.value.Value.Derived;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;

import java.util.Optional;
Expand Down Expand Up @@ -81,4 +81,14 @@ default TransactionFlags flags() {
@JsonProperty("DestinationTag")
Optional<UnsignedInteger> destinationTag();

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default AccountDelete normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.ACCOUNT_DELETE);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -517,4 +517,15 @@ public int getValue() {
return value;
}
}

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default AccountSet normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.ACCOUNT_SET);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import org.immutables.value.Value;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;
import org.xrpl.xrpl4j.model.ledger.AuthAccountWrapper;
Expand Down Expand Up @@ -107,4 +108,14 @@ default TransactionFlags flags() {
@JsonProperty("AuthAccounts")
List<AuthAccountWrapper> authAccounts();

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default AmmBid normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.AMM_BID);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import org.immutables.value.Value;
import org.xrpl.xrpl4j.model.flags.Flags;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;

/**
Expand Down Expand Up @@ -68,4 +68,14 @@ default TransactionFlags flags() {
@JsonProperty("TradingFee")
TradingFee tradingFee();

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default AmmCreate normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.AMM_CREATE);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import org.immutables.value.Value;
import org.immutables.value.Value.Immutable;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;
Expand Down Expand Up @@ -58,4 +59,14 @@ default TransactionFlags flags() {
@JsonProperty("Asset2")
Issue asset2();

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default AmmDelete normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.AMM_DELETE);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import org.immutables.value.Value;
import org.xrpl.xrpl4j.model.flags.AmmDepositFlags;
import org.xrpl.xrpl4j.model.ledger.Issue;
Expand Down Expand Up @@ -105,4 +106,15 @@ static ImmutableAmmDeposit.Builder builder() {
*/
@JsonProperty("TradingFee")
Optional<TradingFee> tradingFee();

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default AmmDeposit normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.AMM_DEPOSIT);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import org.immutables.value.Value;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;
import org.xrpl.xrpl4j.model.ledger.Issue;
Expand Down Expand Up @@ -68,5 +69,15 @@ default TransactionFlags flags() {
@JsonProperty("TradingFee")
TradingFee tradingFee();

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default AmmVote normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.AMM_VOTE);
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,14 @@ static ImmutableAmmWithdraw.Builder builder() {
@JsonProperty("LPTokenIn")
Optional<CurrencyAmount> lpTokensIn();

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default AmmWithdraw normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.AMM_WITHDRAW);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Preconditions;
import org.immutables.value.Value;
import org.immutables.value.Value.Derived;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;

/**
Expand Down Expand Up @@ -68,4 +68,15 @@ default TransactionFlags flags() {
*/
@JsonProperty("CheckID")
Hash256 checkId();

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default CheckCancel normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.CHECK_CANCEL);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,15 @@ default void validateOnlyOneAmountSet() {
!(amount().isPresent() && deliverMin().isPresent()),
"The CheckCash transaction must include either amount or deliverMin, but not both.");
}

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default CheckCash normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.CHECK_CASH);
return this;
}
}
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 @@ -23,9 +23,9 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
import org.immutables.value.Value;
import org.immutables.value.Value.Derived;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;

import java.util.Optional;
Expand Down Expand Up @@ -106,4 +106,14 @@ default TransactionFlags flags() {
@JsonProperty("InvoiceID")
Optional<Hash256> invoiceId();

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default CheckCreate normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.CHECK_CREATE);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import org.immutables.value.Value;
import org.immutables.value.Value.Immutable;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;
Expand Down Expand Up @@ -51,5 +52,15 @@ default TransactionFlags flags() {
@JsonProperty("Amount")
IssuedCurrencyAmount amount();

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default Clawback normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.CLAWBACK);
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Preconditions;
import org.immutables.value.Value;
import org.immutables.value.Value.Derived;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;

import java.util.Optional;
Expand Down Expand Up @@ -93,4 +92,15 @@ default void validateFieldPresence() {
!(authorize().isPresent() && unauthorize().isPresent()),
"The DepositPreAuth transaction must include either Authorize or Unauthorize, but not both.");
}

/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default DepositPreAuth normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.DEPOSIT_PRE_AUTH);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import org.immutables.value.Value;
import org.immutables.value.Value.Immutable;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;

import java.util.Optional;

/**
* Object mapping for the {@code DIDDelete} transaction.
*
Expand Down Expand Up @@ -46,5 +45,14 @@ default TransactionFlags flags() {
return TransactionFlags.EMPTY;
}


/**
* Immutables Check to ensure property state after construction.
*/
@Value.Check
default DidDelete normalize() {
Preconditions.checkState(!unknownFields().containsKey("TransactionType"));
Preconditions.checkState(!unknownFields().containsKey("Account"));
Preconditions.checkState(transactionType() == TransactionType.DID_DELETE);
return this;
}
}
Loading
Loading