diff --git a/CHANGELOG.md b/CHANGELOG.md index 50b7c3181..b1f4ae0c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ As this project is pre 1.0, breaking changes may happen for minor version bumps. A breaking change will get clearly notified in this log. +## 0.16.0 + +* Update XDR definitions and auto-generated classes to support upcoming protocol 13 release ([#276](https://github.com/stellar/java-stellar-sdk/pull/276)). +* Extend StrKey implementation to handle [CAP 27 Muxed Accounts](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0027.md) ([#276](https://github.com/stellar/java-stellar-sdk/pull/276)). +* Update `TransactionResponse` to include new fields which are relevant to [CAP 15 Fee-Bump Transactions](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0015.md) ([#275](https://github.com/stellar/java-stellar-sdk/pull/275)). +* Update `AccountResponse.Balance`, `AllowTrustOperationResponse`, and create `TrustlineAuthorizedToMaintainLiabilitiesEffectResponse` to support [CAP 18 Fine-Grained Control of Authorization](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0018.md) ([#274](https://github.com/stellar/java-stellar-sdk/pull/274)). +* Add `FeeBumpTransaction` and `FeeBumpTransaction.Builder` for parsing and creating [CAP 15 Fee-Bump Transactions](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0015.md) ([#278](https://github.com/stellar/java-stellar-sdk/pull/278)). +* Add methods to `Server` for submitting [CAP 15 Fee-Bump Transactions](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0015.md) ([#278](https://github.com/stellar/java-stellar-sdk/pull/278)). +* Update SEP 10 implementation to reject fee-bump transactions and transactions with multiplexed addresses ([#278] (https://github.com/stellar/java-stellar-sdk/pull/278)). +* Update SEP 29 implementation to handle bump transactions ([#278](https://github.com/stellar/java-stellar-sdk/pull/278)). + + ## 0.15.0 - Add SEP0029 (memo required) support. (https://github.com/stellar/java-stellar-sdk/issues/272) diff --git a/build.gradle b/build.gradle index d33c92fb7..9eb66e711 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ apply plugin: 'com.github.ben-manes.versions' // gradle dependencyUpdates -Drevi apply plugin: 'project-report' // gradle htmlDependencyReport sourceCompatibility = 1.6 -version = '0.15.0' +version = '0.16.0' group = 'stellar' jar { diff --git a/src/main/java/org/stellar/sdk/AbstractTransaction.java b/src/main/java/org/stellar/sdk/AbstractTransaction.java new file mode 100644 index 000000000..556167a05 --- /dev/null +++ b/src/main/java/org/stellar/sdk/AbstractTransaction.java @@ -0,0 +1,134 @@ +package org.stellar.sdk; + +import com.google.common.io.BaseEncoding; +import org.stellar.sdk.xdr.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +public abstract class AbstractTransaction { + protected final Network mNetwork; + protected List mSignatures; + public static final int MIN_BASE_FEE = 100; + + + AbstractTransaction(Network network) { + this.mNetwork = checkNotNull(network, "network cannot be null"); + this.mSignatures = new ArrayList(); + } + + /** + * Adds a new signature ed25519PublicKey to this transaction. + * @param signer {@link KeyPair} object representing a signer + */ + public void sign(KeyPair signer) { + checkNotNull(signer, "signer cannot be null"); + byte[] txHash = this.hash(); + mSignatures.add(signer.signDecorated(txHash)); + } + + /** + * Adds a new sha256Hash signature to this transaction by revealing preimage. + * @param preimage the sha256 hash of preimage should be equal to signer hash + */ + public void sign(byte[] preimage) { + checkNotNull(preimage, "preimage cannot be null"); + org.stellar.sdk.xdr.Signature signature = new org.stellar.sdk.xdr.Signature(); + signature.setSignature(preimage); + + byte[] hash = Util.hash(preimage); + byte[] signatureHintBytes = Arrays.copyOfRange(hash, hash.length - 4, hash.length); + SignatureHint signatureHint = new SignatureHint(); + signatureHint.setSignatureHint(signatureHintBytes); + + DecoratedSignature decoratedSignature = new DecoratedSignature(); + decoratedSignature.setHint(signatureHint); + decoratedSignature.setSignature(signature); + + mSignatures.add(decoratedSignature); + } + + /** + * Returns transaction hash. + */ + public byte[] hash() { + return Util.hash(this.signatureBase()); + } + + /** + * Returns transaction hash encoded as a hexadecimal string. + */ + public String hashHex() { + return BaseEncoding.base16().lowerCase().encode(this.hash()); + } + + /** + * Returns signature base. + */ + public abstract byte[] signatureBase(); + + public Network getNetwork() { + return mNetwork; + } + + public List getSignatures() { + return mSignatures; + } + + public abstract TransactionEnvelope toEnvelopeXdr(); + + /** + * Returns base64-encoded TransactionEnvelope XDR object. Transaction need to have at least one signature. + */ + public String toEnvelopeXdrBase64() { + try { + TransactionEnvelope envelope = this.toEnvelopeXdr(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(outputStream); + TransactionEnvelope.encode(xdrOutputStream, envelope); + + BaseEncoding base64Encoding = BaseEncoding.base64(); + return base64Encoding.encode(outputStream.toByteArray()); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + /** + * Creates a AbstractTransaction instance from previously build TransactionEnvelope + * @param envelope + * @return + */ + public static AbstractTransaction fromEnvelopeXdr(TransactionEnvelope envelope, Network network) { + switch (envelope.getDiscriminant()) { + case ENVELOPE_TYPE_TX: + return Transaction.fromV1EnvelopeXdr(envelope.getV1(), network); + case ENVELOPE_TYPE_TX_V0: + return Transaction.fromV0EnvelopeXdr(envelope.getV0(), network); + case ENVELOPE_TYPE_TX_FEE_BUMP: + return FeeBumpTransaction.fromFeeBumpTransactionEnvelope(envelope.getFeeBump(), network); + default: + throw new IllegalArgumentException("transaction type is not supported: "+envelope.getDiscriminant()); + } + } + + /** + * Creates a Transaction instance from previously build TransactionEnvelope + * @param envelope Base-64 encoded TransactionEnvelope + * @return + * @throws IOException + */ + public static AbstractTransaction fromEnvelopeXdr(String envelope, Network network) throws IOException { + BaseEncoding base64Encoding = BaseEncoding.base64(); + byte[] bytes = base64Encoding.decode(envelope); + + TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); + return fromEnvelopeXdr(transactionEnvelope, network); + } +} diff --git a/src/main/java/org/stellar/sdk/AccountMergeOperation.java b/src/main/java/org/stellar/sdk/AccountMergeOperation.java index 4c0a0baa5..3be2246d4 100644 --- a/src/main/java/org/stellar/sdk/AccountMergeOperation.java +++ b/src/main/java/org/stellar/sdk/AccountMergeOperation.java @@ -28,7 +28,7 @@ public String getDestination() { @Override OperationBody toOperationBody() { OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody(); - body.setDestination(StrKey.encodeToXDRAccountId(this.destination)); + body.setDestination(StrKey.encodeToXDRMuxedAccount(this.destination)); body.setDiscriminant(OperationType.ACCOUNT_MERGE); return body; } @@ -43,8 +43,8 @@ public static class Builder { private String mSourceAccount; Builder(OperationBody op) { - destination = StrKey.encodeStellarAccountId( - op.getDestination().getAccountID().getEd25519().getUint256() + destination = StrKey.encodeStellarMuxedAccount( + op.getDestination() ); } diff --git a/src/main/java/org/stellar/sdk/AllowTrustOperation.java b/src/main/java/org/stellar/sdk/AllowTrustOperation.java index 21ec896a2..02a682d8c 100644 --- a/src/main/java/org/stellar/sdk/AllowTrustOperation.java +++ b/src/main/java/org/stellar/sdk/AllowTrustOperation.java @@ -14,11 +14,13 @@ public class AllowTrustOperation extends Operation { private final String trustor; private final String assetCode; private final boolean authorize; + private final boolean authorizeToMaintainLiabilities; - private AllowTrustOperation(String trustor, String assetCode, boolean authorize) { + private AllowTrustOperation(String trustor, String assetCode, boolean authorize, boolean authorizeToMaintainLiabilities) { this.trustor = checkNotNull(trustor, "trustor cannot be null"); this.assetCode = checkNotNull(assetCode, "assetCode cannot be null"); this.authorize = authorize; + this.authorizeToMaintainLiabilities = authorizeToMaintainLiabilities; } /** @@ -62,8 +64,16 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { asset.setAssetCode12(assetCode12); } op.setAsset(asset); + Uint32 flag = new Uint32(); // authorize - op.setAuthorize(authorize); + if (authorize) { + flag.setUint32(TrustLineFlags.AUTHORIZED_FLAG.getValue()); + } else if (authorizeToMaintainLiabilities) { + flag.setUint32(TrustLineFlags.AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG.getValue()); + } else { + flag.setUint32(0); + } + op.setAuthorize(flag); org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody(); body.setDiscriminant(OperationType.ALLOW_TRUST); @@ -79,11 +89,12 @@ public static class Builder { private final String trustor; private final String assetCode; private final boolean authorize; + private boolean authorizeToMaintainLiabilities; private String mSourceAccount; Builder(AllowTrustOp op) { - trustor = StrKey.encodeStellarAccountId(op.getTrustor().getAccountID().getEd25519().getUint256()); + trustor = StrKey.encodeStellarAccountId(op.getTrustor()); switch (op.getAsset().getDiscriminant()) { case ASSET_TYPE_CREDIT_ALPHANUM4: assetCode = new String(op.getAsset().getAssetCode4().getAssetCode4()).trim(); @@ -94,7 +105,20 @@ public static class Builder { default: throw new RuntimeException("Unknown asset code"); } - authorize = op.getAuthorize(); + + int flag = op.getAuthorize().getUint32().intValue(); + if (flag == TrustLineFlags.AUTHORIZED_FLAG.getValue()) { + authorize = true; + authorizeToMaintainLiabilities = false; + } else if (flag == TrustLineFlags.AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG.getValue()) { + authorize = false; + authorizeToMaintainLiabilities = true; + } else if (flag != 0) { + throw new IllegalArgumentException("invalid authorize flag "+flag); + } else { + authorize = false; + authorizeToMaintainLiabilities = false; + } } /** @@ -123,7 +147,9 @@ public Builder setSourceAccount(String sourceAccount) { * Builds an operation */ public AllowTrustOperation build() { - AllowTrustOperation operation = new AllowTrustOperation(trustor, assetCode, authorize); + AllowTrustOperation operation = new AllowTrustOperation( + trustor, assetCode, authorize, authorizeToMaintainLiabilities + ); if (mSourceAccount != null) { operation.setSourceAccount(mSourceAccount); } @@ -133,7 +159,13 @@ public AllowTrustOperation build() { @Override public int hashCode() { - return Objects.hashCode(this.getSourceAccount(), this.assetCode, this.authorize, this.trustor); + return Objects.hashCode( + this.getSourceAccount(), + this.assetCode, + this.authorize, + this.authorizeToMaintainLiabilities, + this.trustor + ); } @Override @@ -145,6 +177,7 @@ public boolean equals(Object object) { AllowTrustOperation other = (AllowTrustOperation) object; return Objects.equal(this.assetCode, other.assetCode) && Objects.equal(this.authorize, other.authorize) && + Objects.equal(this.authorizeToMaintainLiabilities, other.authorizeToMaintainLiabilities) && Objects.equal(this.trustor, other.trustor) && Objects.equal(this.getSourceAccount(), other.getSourceAccount()); } diff --git a/src/main/java/org/stellar/sdk/Asset.java b/src/main/java/org/stellar/sdk/Asset.java index 24629645d..1fe37a741 100644 --- a/src/main/java/org/stellar/sdk/Asset.java +++ b/src/main/java/org/stellar/sdk/Asset.java @@ -41,15 +41,11 @@ public static Asset fromXdr(org.stellar.sdk.xdr.Asset xdr) { return new AssetTypeNative(); case ASSET_TYPE_CREDIT_ALPHANUM4: String assetCode4 = Util.paddedByteArrayToString(xdr.getAlphaNum4().getAssetCode().getAssetCode4()); - accountId = StrKey.encodeStellarAccountId( - xdr.getAlphaNum4().getIssuer().getAccountID().getEd25519().getUint256() - ); + accountId = StrKey.encodeStellarAccountId(xdr.getAlphaNum4().getIssuer()); return new AssetTypeCreditAlphaNum4(assetCode4, accountId); case ASSET_TYPE_CREDIT_ALPHANUM12: String assetCode12 = Util.paddedByteArrayToString(xdr.getAlphaNum12().getAssetCode().getAssetCode12()); - accountId = StrKey.encodeStellarAccountId( - xdr.getAlphaNum12().getIssuer().getAccountID().getEd25519().getUint256() - ); + accountId = StrKey.encodeStellarAccountId(xdr.getAlphaNum12().getIssuer()); return new AssetTypeCreditAlphaNum12(assetCode12, accountId); default: throw new IllegalArgumentException("Unknown asset type " + xdr.getDiscriminant()); diff --git a/src/main/java/org/stellar/sdk/CreateAccountOperation.java b/src/main/java/org/stellar/sdk/CreateAccountOperation.java index 938ed2e04..40726c209 100644 --- a/src/main/java/org/stellar/sdk/CreateAccountOperation.java +++ b/src/main/java/org/stellar/sdk/CreateAccountOperation.java @@ -64,7 +64,7 @@ public static class Builder { * @param op {@link CreateAccountOp} */ Builder(CreateAccountOp op) { - destination = StrKey.encodeStellarAccountId(op.getDestination().getAccountID().getEd25519().getUint256()); + destination = StrKey.encodeStellarAccountId(op.getDestination()); startingBalance = Operation.fromXdrAmount(op.getStartingBalance().getInt64().longValue()); } diff --git a/src/main/java/org/stellar/sdk/FeeBumpTransaction.java b/src/main/java/org/stellar/sdk/FeeBumpTransaction.java new file mode 100644 index 000000000..77a328152 --- /dev/null +++ b/src/main/java/org/stellar/sdk/FeeBumpTransaction.java @@ -0,0 +1,204 @@ +package org.stellar.sdk; + +import com.google.common.base.Objects; +import org.stellar.sdk.xdr.*; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Represents Fee Bump Transaction in Stellar network. + */ +public class FeeBumpTransaction extends AbstractTransaction { + private final long mFee; + private final String mFeeAccount; + private final Transaction mInner; + + FeeBumpTransaction(String feeAccount, long fee, Transaction innerTransaction) { + super(innerTransaction.getNetwork()); + this.mFeeAccount = checkNotNull(feeAccount, "feeAccount cannot be null"); + this.mInner = checkNotNull(innerTransaction, "innerTransaction cannot be null"); + this.mFee = fee; + } + + public long getFee() { + return mFee; + } + + public String getFeeAccount() { + return mFeeAccount; + } + + + public Transaction getInnerTransaction() { + return mInner; + } + + public static FeeBumpTransaction fromFeeBumpTransactionEnvelope(FeeBumpTransactionEnvelope envelope, Network network) { + Transaction inner = Transaction.fromV1EnvelopeXdr(envelope.getTx().getInnerTx().getV1(), network); + String feeAccount = StrKey.encodeStellarMuxedAccount(envelope.getTx().getFeeSource()); + long fee = envelope.getTx().getFee().getInt64(); + + FeeBumpTransaction feeBump = new FeeBumpTransaction(feeAccount, fee, inner); + for (DecoratedSignature signature : envelope.getSignatures()) { + feeBump.mSignatures.add(signature); + } + + return feeBump; + } + + private org.stellar.sdk.xdr.FeeBumpTransaction toXdr() { + org.stellar.sdk.xdr.FeeBumpTransaction xdr = new org.stellar.sdk.xdr.FeeBumpTransaction(); + xdr.setExt(new org.stellar.sdk.xdr.FeeBumpTransaction.FeeBumpTransactionExt()); + xdr.getExt().setDiscriminant(0); + + Int64 xdrFee = new Int64(); + xdrFee.setInt64(mFee); + xdr.setFee(xdrFee); + + xdr.setFeeSource(StrKey.encodeToXDRMuxedAccount(this.mFeeAccount)); + + org.stellar.sdk.xdr.FeeBumpTransaction.FeeBumpTransactionInnerTx innerXDR = new org.stellar.sdk.xdr.FeeBumpTransaction.FeeBumpTransactionInnerTx(); + innerXDR.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX); + innerXDR.setV1(this.mInner.toEnvelopeXdr().getV1()); + xdr.setInnerTx(innerXDR); + return xdr; + } + + @Override + public byte[] signatureBase() { + try { + TransactionSignaturePayload payload = new TransactionSignaturePayload(); + TransactionSignaturePayload.TransactionSignaturePayloadTaggedTransaction taggedTransaction = new TransactionSignaturePayload.TransactionSignaturePayloadTaggedTransaction(); + taggedTransaction.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX_FEE_BUMP); + taggedTransaction.setFeeBump(this.toXdr()); + Hash hash = new Hash(); + hash.setHash(mNetwork.getNetworkId()); + payload.setNetworkId(hash); + payload.setTaggedTransaction(taggedTransaction); + ByteArrayOutputStream txOutputStream = new ByteArrayOutputStream(); + XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(txOutputStream); + payload.encode(xdrOutputStream); + return txOutputStream.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Generates TransactionEnvelope XDR object. + */ + @Override + public TransactionEnvelope toEnvelopeXdr() { + TransactionEnvelope xdr = new TransactionEnvelope(); + FeeBumpTransactionEnvelope feeBumpEnvelope = new FeeBumpTransactionEnvelope(); + xdr.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX_FEE_BUMP); + + feeBumpEnvelope.setTx(this.toXdr()); + + DecoratedSignature[] signatures = new DecoratedSignature[mSignatures.size()]; + signatures = mSignatures.toArray(signatures); + feeBumpEnvelope.setSignatures(signatures); + + xdr.setFeeBump(feeBumpEnvelope); + return xdr; + } + + /** + * Builds a new FeeBumpTransaction object. + */ + public static class Builder { + private final Transaction mInner; + private Long mBaseFee; + private String mFeeAccount; + + + /** + * Construct a new fee bump transaction builder. + * + * @param inner The inner transaction which will be fee bumped. + */ + public Builder(Transaction inner) { + this.mInner = checkNotNull(inner, "inner cannot be null"); + EnvelopeType txType = this.mInner.toEnvelopeXdr().getDiscriminant(); + if (this.mInner.toEnvelopeXdr().getDiscriminant() != EnvelopeType.ENVELOPE_TYPE_TX) { + throw new IllegalArgumentException("invalid transaction type: " + txType); + } + } + + public FeeBumpTransaction.Builder setBaseFee(long baseFee) { + if (this.mBaseFee != null) { + throw new RuntimeException("base fee has been already set."); + } + + if (baseFee < MIN_BASE_FEE) { + throw new IllegalArgumentException("baseFee cannot be smaller than the BASE_FEE (" + MIN_BASE_FEE + "): " + baseFee); + } + + long innerBaseFee = this.mInner.getFee(); + long numOperations = this.mInner.getOperations().length; + if (numOperations > 0) { + innerBaseFee = innerBaseFee / numOperations; + } + + if (baseFee < innerBaseFee) { + throw new IllegalArgumentException("base fee cannot be lower than provided inner transaction base fee"); + } + + long maxFee = baseFee * (numOperations + 1); + if (maxFee < 0) { + throw new IllegalArgumentException("fee overflows 64 bit int"); + } + + this.mBaseFee = maxFee; + return this; + } + + public FeeBumpTransaction.Builder setFeeAccount(String feeAccount) { + if (this.mFeeAccount != null) { + throw new RuntimeException("fee account has been already been set."); + } + + this.mFeeAccount = checkNotNull(feeAccount, "feeAccount cannot be null"); + return this; + } + + public FeeBumpTransaction build() { + return new FeeBumpTransaction( + checkNotNull(this.mFeeAccount, "fee account has to be set. you must call setFeeAccount()."), + checkNotNull(this.mBaseFee, "base fee has to be set. you must call setBaseFee()."), + this.mInner + ); + } + + } + + @Override + public int hashCode() { + return Objects.hashCode( + this.mFee, + this.mInner, + this.mNetwork, + this.mFeeAccount, + this.mSignatures + ); + } + + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof FeeBumpTransaction)) { + return false; + } + + FeeBumpTransaction other = (FeeBumpTransaction) object; + return Objects.equal(this.mFee, other.mFee) && + Objects.equal(this.mFeeAccount, other.mFeeAccount) && + Objects.equal(this.mInner, other.mInner) && + Objects.equal(this.mNetwork, other.mNetwork) && + Objects.equal(this.mSignatures, other.mSignatures); + } + +} diff --git a/src/main/java/org/stellar/sdk/Operation.java b/src/main/java/org/stellar/sdk/Operation.java index 55cbc3c0b..25261dc0e 100644 --- a/src/main/java/org/stellar/sdk/Operation.java +++ b/src/main/java/org/stellar/sdk/Operation.java @@ -37,7 +37,7 @@ protected static String fromXdrAmount(long value) { public org.stellar.sdk.xdr.Operation toXdr() { org.stellar.sdk.xdr.Operation xdr = new org.stellar.sdk.xdr.Operation(); if (getSourceAccount() != null) { - xdr.setSourceAccount(StrKey.encodeToXDRAccountId(mSourceAccount)); + xdr.setSourceAccount(StrKey.encodeToXDRMuxedAccount(mSourceAccount)); } xdr.setBody(toOperationBody()); return xdr; @@ -115,7 +115,7 @@ public static Operation fromXdr(org.stellar.sdk.xdr.Operation xdr) { } if (xdr.getSourceAccount() != null) { operation.setSourceAccount( - StrKey.encodeStellarAccountId(xdr.getSourceAccount().getAccountID().getEd25519().getUint256()) + StrKey.encodeStellarMuxedAccount(xdr.getSourceAccount()) ); } return operation; diff --git a/src/main/java/org/stellar/sdk/PathPaymentStrictReceiveOperation.java b/src/main/java/org/stellar/sdk/PathPaymentStrictReceiveOperation.java index cf678e63a..13d2fca89 100644 --- a/src/main/java/org/stellar/sdk/PathPaymentStrictReceiveOperation.java +++ b/src/main/java/org/stellar/sdk/PathPaymentStrictReceiveOperation.java @@ -92,7 +92,7 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { sendMax.setInt64(Operation.toXdrAmount(this.sendMax)); op.setSendMax(sendMax); // destination - op.setDestination(StrKey.encodeToXDRAccountId(this.destination)); + op.setDestination(StrKey.encodeToXDRMuxedAccount(this.destination)); // destAsset op.setDestAsset(destAsset.toXdr()); // destAmount @@ -130,7 +130,7 @@ public static class Builder { Builder(PathPaymentStrictReceiveOp op) { sendAsset = Asset.fromXdr(op.getSendAsset()); sendMax = Operation.fromXdrAmount(op.getSendMax().getInt64().longValue()); - destination = StrKey.encodeStellarAccountId(op.getDestination().getAccountID().getEd25519().getUint256()); + destination = StrKey.encodeStellarMuxedAccount(op.getDestination()); destAsset = Asset.fromXdr(op.getDestAsset()); destAmount = Operation.fromXdrAmount(op.getDestAmount().getInt64().longValue()); path = new Asset[op.getPath().length]; diff --git a/src/main/java/org/stellar/sdk/PathPaymentStrictSendOperation.java b/src/main/java/org/stellar/sdk/PathPaymentStrictSendOperation.java index 38ed62bb0..f0b5dfefa 100644 --- a/src/main/java/org/stellar/sdk/PathPaymentStrictSendOperation.java +++ b/src/main/java/org/stellar/sdk/PathPaymentStrictSendOperation.java @@ -92,7 +92,7 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { sendAmount.setInt64(Operation.toXdrAmount(this.sendAmount)); op.setSendAmount(sendAmount); // destination - op.setDestination(StrKey.encodeToXDRAccountId(this.destination)); + op.setDestination(StrKey.encodeToXDRMuxedAccount(this.destination)); // destAsset op.setDestAsset(destAsset.toXdr()); // destMin @@ -130,7 +130,7 @@ public static class Builder { Builder(PathPaymentStrictSendOp op) { sendAsset = Asset.fromXdr(op.getSendAsset()); sendAmount = Operation.fromXdrAmount(op.getSendAmount().getInt64().longValue()); - destination = StrKey.encodeStellarAccountId(op.getDestination().getAccountID().getEd25519().getUint256()); + destination = StrKey.encodeStellarMuxedAccount(op.getDestination()); destAsset = Asset.fromXdr(op.getDestAsset()); destMin = Operation.fromXdrAmount(op.getDestMin().getInt64().longValue()); path = new Asset[op.getPath().length]; diff --git a/src/main/java/org/stellar/sdk/PaymentOperation.java b/src/main/java/org/stellar/sdk/PaymentOperation.java index d8256b300..28b65b560 100644 --- a/src/main/java/org/stellar/sdk/PaymentOperation.java +++ b/src/main/java/org/stellar/sdk/PaymentOperation.java @@ -51,7 +51,7 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { PaymentOp op = new PaymentOp(); // destination - op.setDestination(StrKey.encodeToXDRAccountId(this.destination)); + op.setDestination(StrKey.encodeToXDRMuxedAccount(this.destination)); // asset op.setAsset(asset.toXdr()); // amount @@ -81,7 +81,7 @@ public static class Builder { * @param op {@link PaymentOp} */ Builder(PaymentOp op) { - destination = StrKey.encodeStellarAccountId(op.getDestination().getAccountID().getEd25519().getUint256()); + destination = StrKey.encodeStellarMuxedAccount(op.getDestination()); asset = Asset.fromXdr(op.getAsset()); amount = Operation.fromXdrAmount(op.getAmount().getInt64().longValue()); } diff --git a/src/main/java/org/stellar/sdk/Sep10Challenge.java b/src/main/java/org/stellar/sdk/Sep10Challenge.java index 30d2e3954..47ffd8243 100644 --- a/src/main/java/org/stellar/sdk/Sep10Challenge.java +++ b/src/main/java/org/stellar/sdk/Sep10Challenge.java @@ -21,32 +21,37 @@ public class Sep10Challenge { * @param anchorName The name of the anchor which will be included in the ManageData operation. * @param timebounds The lifetime of the challenge token. */ - public static String newChallenge( + public static Transaction newChallenge( KeyPair signer, Network network, String clientAccountId, String anchorName, TimeBounds timebounds - ) { + ) throws InvalidSep10ChallengeException { byte[] nonce = new byte[48]; SecureRandom random = new SecureRandom(); random.nextBytes(nonce); BaseEncoding base64Encoding = BaseEncoding.base64(); byte[] encodedNonce = base64Encoding.encode(nonce).getBytes(); + + if (StrKey.decodeVersionByte(clientAccountId) != StrKey.VersionByte.ACCOUNT_ID) { + throw new InvalidSep10ChallengeException(clientAccountId+" is not a valid account id"); + } + Account sourceAccount = new Account(signer.getAccountId(), -1L); ManageDataOperation operation = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) .setSourceAccount(clientAccountId) .build(); Transaction transaction = new Transaction.Builder(sourceAccount, network) .addTimeBounds(timebounds) - .setOperationFee(100) + .setBaseFee(100) .addOperation(operation) .build(); transaction.sign(signer); - return transaction.toEnvelopeXdrBase64(); + return transaction; } /** @@ -69,7 +74,15 @@ public static String newChallenge( */ public static ChallengeTransaction readChallengeTransaction(String challengeXdr, String serverAccountId, Network network) throws InvalidSep10ChallengeException, IOException { // decode the received input as a base64-urlencoded XDR representation of Stellar transaction envelope - Transaction transaction = Transaction.fromEnvelopeXdr(challengeXdr, network); + AbstractTransaction parsed = Transaction.fromEnvelopeXdr(challengeXdr, network); + if (!(parsed instanceof Transaction)) { + throw new InvalidSep10ChallengeException("Transaction cannot be a fee bump transaction"); + } + Transaction transaction = (Transaction)parsed; + + if (StrKey.decodeVersionByte(serverAccountId) != StrKey.VersionByte.ACCOUNT_ID) { + throw new InvalidSep10ChallengeException("serverAccountId: "+serverAccountId+" is not a valid account id"); + } // verify that transaction source account is equal to the server's signing key if (!serverAccountId.equals(transaction.getSourceAccount())) { @@ -113,6 +126,10 @@ public static ChallengeTransaction readChallengeTransaction(String challengeXdr, throw new InvalidSep10ChallengeException("Operation should have a source account."); } + if (StrKey.decodeVersionByte(clientAccountId) != StrKey.VersionByte.ACCOUNT_ID) { + throw new InvalidSep10ChallengeException("clientAccountId: "+clientAccountId+" is not a valid account id"); + } + // verify manage data value if (manageDataOperation.getValue().length != 64) { throw new InvalidSep10ChallengeException("Random nonce encoded as base64 should be 64 bytes long."); diff --git a/src/main/java/org/stellar/sdk/Server.java b/src/main/java/org/stellar/sdk/Server.java index 8e54e5d03..578d89f48 100644 --- a/src/main/java/org/stellar/sdk/Server.java +++ b/src/main/java/org/stellar/sdk/Server.java @@ -6,6 +6,8 @@ import okhttp3.Response; import org.stellar.sdk.requests.*; import org.stellar.sdk.responses.*; +import org.stellar.sdk.xdr.CryptoKeyType; +import org.stellar.sdk.xdr.MuxedAccount; import java.io.Closeable; import java.io.IOException; @@ -228,19 +230,7 @@ private void setNetwork(Network network) { } } - /** - * Submits transaction to the network - * - * @param transaction transaction to submit to the network - * @param skipMemoRequiredCheck set to true to skip memoRequiredCheck - * @return {@link SubmitTransactionResponse} - * @throws SubmitTransactionTimeoutResponseException When Horizon returns a Timeout or connection timeout occured. - * @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned. - * @throws AccountRequiresMemoException when a transaction is trying to submit an operation to an - * account which requires a memo. - * @throws IOException - */ - public SubmitTransactionResponse submitTransaction(Transaction transaction, boolean skipMemoRequiredCheck) throws IOException, AccountRequiresMemoException { + private void checkTransactionNetwork(AbstractTransaction transaction) throws IOException { Optional network = getNetwork(); if (!network.isPresent()) { this.root(); @@ -250,13 +240,20 @@ public SubmitTransactionResponse submitTransaction(Transaction transaction, bool if (!network.get().equals(transaction.getNetwork())) { throw new NetworkMismatchException(network.get(), transaction.getNetwork()); } + } - if (!skipMemoRequiredCheck) { - checkMemoRequired(transaction); - } - + /** + * Submits a base64 encoded transaction envelope to the network + * + * @param transactionXdr base64 encoded transaction envelope to submit to the network + * @return {@link SubmitTransactionResponse} + * @throws SubmitTransactionTimeoutResponseException When Horizon returns a Timeout or connection timeout occured. + * @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned. + * @throws IOException + */ + public SubmitTransactionResponse submitTransactionXdr(String transactionXdr) throws IOException { HttpUrl transactionsURI = serverURI.newBuilder().addPathSegment("transactions").build(); - RequestBody requestBody = new FormBody.Builder().add("tx", transaction.toEnvelopeXdrBase64()).build(); + RequestBody requestBody = new FormBody.Builder().add("tx", transactionXdr).build(); Request submitTransactionRequest = new Request.Builder().url(transactionsURI).post(requestBody).build(); Response response = null; @@ -285,7 +282,51 @@ public SubmitTransactionResponse submitTransaction(Transaction transaction, bool } /** - * Submits transaction to the network + * Submits a transaction to the network + * + * @param transaction transaction to submit to the network + * @param skipMemoRequiredCheck set to true to skip memoRequiredCheck + * @return {@link SubmitTransactionResponse} + * @throws SubmitTransactionTimeoutResponseException When Horizon returns a Timeout or connection timeout occured. + * @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned. + * @throws AccountRequiresMemoException when a transaction is trying to submit an operation to an + * account which requires a memo. + * @throws IOException + */ + public SubmitTransactionResponse submitTransaction(Transaction transaction, boolean skipMemoRequiredCheck) throws IOException, AccountRequiresMemoException { + this.checkTransactionNetwork(transaction); + + if (!skipMemoRequiredCheck) { + checkMemoRequired(transaction); + } + + return this.submitTransactionXdr(transaction.toEnvelopeXdrBase64()); + } + + /** + * Submits a fee bump transaction to the network + * + * @param transaction transaction to submit to the network + * @param skipMemoRequiredCheck set to true to skip memoRequiredCheck + * @return {@link SubmitTransactionResponse} + * @throws SubmitTransactionTimeoutResponseException When Horizon returns a Timeout or connection timeout occured. + * @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned. + * @throws AccountRequiresMemoException when a transaction is trying to submit an operation to an + * account which requires a memo. + * @throws IOException + */ + public SubmitTransactionResponse submitTransaction(FeeBumpTransaction transaction, boolean skipMemoRequiredCheck) throws IOException, AccountRequiresMemoException { + this.checkTransactionNetwork(transaction); + + if (!skipMemoRequiredCheck) { + checkMemoRequired(transaction.getInnerTransaction()); + } + + return this.submitTransactionXdr(transaction.toEnvelopeXdrBase64()); + } + + /** + * Submits a transaction to the network * * This function will always check if the destination account requires a memo in the transaction as * defined in SEP-0029 @@ -303,6 +344,29 @@ public SubmitTransactionResponse submitTransaction(Transaction transaction) thro return submitTransaction(transaction, false); } + /** + * Submits a fee bump transaction to the network + * + * This function will always check if the destination account requires a memo in the transaction as + * defined in SEP-0029 + * If you want to skip this check, use {@link Server#submitTransaction(Transaction, boolean)}. + * + * @param transaction transaction to submit to the network. + * @return {@link SubmitTransactionResponse} + * @throws SubmitTransactionTimeoutResponseException When Horizon returns a Timeout or connection timeout occured. + * @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned. + * @throws AccountRequiresMemoException when a transaction is trying to submit an operation to an + * account which requires a memo. + * @throws IOException + */ + public SubmitTransactionResponse submitTransaction(FeeBumpTransaction transaction) throws IOException, AccountRequiresMemoException { + return submitTransaction(transaction, false); + } + + private boolean hashMemoId(String muxedAccount) { + return StrKey.encodeToXDRMuxedAccount(muxedAccount).getDiscriminant() == CryptoKeyType.KEY_TYPE_MUXED_ED25519; + } + /** * checkMemoRequired implements a memo required check as defined in * SEP-0029 @@ -332,9 +396,10 @@ private void checkMemoRequired(Transaction transaction) throws IOException, Acco } else { continue; } - if (destinations.contains(destination)) { + if (destinations.contains(destination) || hashMemoId(destination)) { continue; } + destinations.add(destination); AccountResponse.Data data; try { diff --git a/src/main/java/org/stellar/sdk/SetOptionsOperation.java b/src/main/java/org/stellar/sdk/SetOptionsOperation.java index ecbda7f81..073c342ca 100644 --- a/src/main/java/org/stellar/sdk/SetOptionsOperation.java +++ b/src/main/java/org/stellar/sdk/SetOptionsOperation.java @@ -190,7 +190,7 @@ public static class Builder { Builder(SetOptionsOp op) { if (op.getInflationDest() != null) { - inflationDestination = StrKey.encodeStellarAccountId(op.getInflationDest().getAccountID().getEd25519().getUint256()); + inflationDestination = StrKey.encodeStellarAccountId(op.getInflationDest()); } if (op.getClearFlags() != null) { clearFlags = op.getClearFlags().getUint32(); diff --git a/src/main/java/org/stellar/sdk/StrKey.java b/src/main/java/org/stellar/sdk/StrKey.java index be866b4d8..222622a3e 100644 --- a/src/main/java/org/stellar/sdk/StrKey.java +++ b/src/main/java/org/stellar/sdk/StrKey.java @@ -2,17 +2,19 @@ import com.google.common.io.BaseEncoding; import com.google.common.base.Optional; -import org.stellar.sdk.xdr.AccountID; -import org.stellar.sdk.xdr.PublicKey; -import org.stellar.sdk.xdr.PublicKeyType; -import org.stellar.sdk.xdr.Uint256; +import org.stellar.sdk.xdr.*; import java.io.*; import java.util.Arrays; class StrKey { + + public static final int ACCOUNT_ID_ADDRESS_LENGTH = 56; + public static final int MUXED_ACCOUNT_ADDRESS_LENGTH = 69; + public enum VersionByte { ACCOUNT_ID((byte)(6 << 3)), // G + MUXED_ACCOUNT((byte)(12 << 3)), // M SEED((byte)(18 << 3)), // S PRE_AUTH_TX((byte)(19 << 3)), // T SHA256_HASH((byte)(23 << 3)); // X @@ -41,17 +43,74 @@ public static String encodeStellarAccountId(byte[] data) { return String.valueOf(encoded); } + public static String encodeStellarAccountId(AccountID accountID) { + char[] encoded = encodeCheck(VersionByte.ACCOUNT_ID, accountID.getAccountID().getEd25519().getUint256()); + return String.valueOf(encoded); + } + + + public static String encodeStellarMuxedAccount(MuxedAccount account) { + if (account.getDiscriminant().equals(CryptoKeyType.KEY_TYPE_ED25519)) { + return encodeStellarAccountId(account.getEd25519().getUint256()); + } else if (account.getDiscriminant().equals(CryptoKeyType.KEY_TYPE_MUXED_ED25519)) { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + + try { + account.getMed25519().encode(new XdrDataOutputStream(byteStream)); + } catch (IOException e) { + throw new IllegalArgumentException("invalid muxed account", e); + } + + char[] encoded = encodeCheck(VersionByte.MUXED_ACCOUNT, byteStream.toByteArray()); + return String.valueOf(encoded); + } + throw new IllegalArgumentException("invalid muxed account type: "+account.getDiscriminant()); + } + + public static AccountID encodeToXDRAccountId(String data) { AccountID accountID = new AccountID(); PublicKey publicKey = new PublicKey(); publicKey.setDiscriminant(PublicKeyType.PUBLIC_KEY_TYPE_ED25519); - Uint256 uint256 = new Uint256(); - uint256.setUint256(decodeStellarAccountId(data)); - publicKey.setEd25519(uint256); + try { + publicKey.setEd25519(Uint256.decode( + new XdrDataInputStream(new ByteArrayInputStream(decodeStellarAccountId(data))) + )); + } catch (IOException e) { + throw new IllegalArgumentException("invalid address: "+data, e); + } accountID.setAccountID(publicKey); return accountID; } + public static MuxedAccount encodeToXDRMuxedAccount(String data) { + if (data.length() == ACCOUNT_ID_ADDRESS_LENGTH) { + MuxedAccount accountID = new MuxedAccount(); + accountID.setDiscriminant(CryptoKeyType.KEY_TYPE_ED25519); + try { + accountID.setEd25519(Uint256.decode( + new XdrDataInputStream(new ByteArrayInputStream(decodeStellarAccountId(data))) + )); + } catch (IOException e) { + throw new IllegalArgumentException("invalid address: "+data, e); + } + return accountID; + } else if (data.length() == MUXED_ACCOUNT_ADDRESS_LENGTH) { + MuxedAccount muxedAccount = new MuxedAccount(); + muxedAccount.setDiscriminant(CryptoKeyType.KEY_TYPE_MUXED_ED25519); + try { + muxedAccount.setMed25519(MuxedAccount.MuxedAccountMed25519.decode( + new XdrDataInputStream(new ByteArrayInputStream(decodeStellarMuxedAccount(data))) + )); + } catch (IOException e) { + throw new IllegalArgumentException("invalid address: "+data, e); + } + return muxedAccount; + } + throw new IllegalArgumentException("invalid address length: "+data); + } + + public static VersionByte decodeVersionByte(String data) { byte[] decoded = StrKey.base32Encoding.decode(java.nio.CharBuffer.wrap(data.toCharArray())); byte decodedVersionByte = decoded[0]; @@ -66,6 +125,11 @@ public static byte[] decodeStellarAccountId(String data) { return decodeCheck(VersionByte.ACCOUNT_ID, data.toCharArray()); } + public static byte[] decodeStellarMuxedAccount(String data) { + return decodeCheck(VersionByte.MUXED_ACCOUNT, data.toCharArray()); + } + + public static char[] encodeStellarSecretSeed(byte[] data) { return encodeCheck(VersionByte.SEED, data); } @@ -102,6 +166,10 @@ protected static char[] encodeCheck(VersionByte versionByte, byte[] data) { outputStream.write(checksum); byte unencoded[] = outputStream.toByteArray(); + if (VersionByte.SEED != versionByte) { + return StrKey.base32Encoding.encode(unencoded).toCharArray(); + } + // Why not use base32Encoding.encode here? // We don't want secret seed to be stored as String in memory because of security reasons. It's impossible // to erase it from memory when we want it to be erased (ASAP). @@ -110,18 +178,16 @@ protected static char[] encodeCheck(VersionByte versionByte, byte[] data) { charOutputStream.write(unencoded); char[] charsEncoded = charArrayWriter.toCharArray(); - if (VersionByte.SEED == versionByte) { - Arrays.fill(unencoded, (byte) 0); - Arrays.fill(payload, (byte) 0); - Arrays.fill(checksum, (byte) 0); - - // Clean charArrayWriter internal buffer - int bufferSize = charArrayWriter.size(); - char[] zeros = new char[bufferSize]; - Arrays.fill(zeros, '0'); - charArrayWriter.reset(); - charArrayWriter.write(zeros); - } + Arrays.fill(unencoded, (byte) 0); + Arrays.fill(payload, (byte) 0); + Arrays.fill(checksum, (byte) 0); + + // Clean charArrayWriter internal buffer + int bufferSize = charArrayWriter.size(); + char[] zeros = new char[bufferSize]; + Arrays.fill(zeros, '0'); + charArrayWriter.reset(); + charArrayWriter.write(zeros); return charsEncoded; } catch (IOException e) { @@ -132,12 +198,34 @@ protected static char[] encodeCheck(VersionByte versionByte, byte[] data) { protected static byte[] decodeCheck(VersionByte versionByte, char[] encoded) { byte[] bytes = new byte[encoded.length]; for (int i = 0; i < encoded.length; i++) { - if (encoded[i] > 127) { - throw new IllegalArgumentException("Illegal characters in encoded char array."); - } bytes[i] = (byte) encoded[i]; } + // The minimal binary decoded length is 3 bytes (version byte and 2-byte CRC) which, + // in unpadded base32 (since each character provides 5 bits) corresponds to ceiling(8*3/5) = 5 + if (bytes.length < 5) { + throw new IllegalArgumentException("Encoded char array must have a length of at least 5."); + } + + int leftoverBits = (bytes.length * 5) % 8; + // 1. Make sure there is no full unused leftover byte at the end + // (i.e. there shouldn't be 5 or more leftover bits) + if (leftoverBits >= 5) { + throw new IllegalArgumentException("Encoded char array has leftover character."); + } + + if (leftoverBits > 0) { + byte lastChar = bytes[bytes.length-1]; + byte decodedLastChar = b32Table[lastChar]; + + + + byte leftoverBitsMask = (byte)(0x0f >> (4 - leftoverBits)); + if ((decodedLastChar & leftoverBitsMask) != 0) { + throw new IllegalArgumentException("Unused bits should be set to 0."); + } + } + byte[] decoded = StrKey.base32Encoding.decode(java.nio.CharBuffer.wrap(encoded)); byte decodedVersionByte = decoded[0]; byte[] payload = Arrays.copyOfRange(decoded, 0, decoded.length-2); @@ -189,4 +277,17 @@ protected static byte[] calculateChecksum(byte[] bytes) { (byte)crc, (byte)(crc >>> 8)}; } + + private static final byte[] b32Table = decodingTable(); + private static byte[] decodingTable() { + byte[] table = new byte[256]; + for (int i=0; i <256; i++) { + table[i] = (byte)0xff; + } + String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + for(int i=0; i < alphabet.length(); i++) { + table[(int)alphabet.charAt(i)] = (byte)i; + } + return table; + } } diff --git a/src/main/java/org/stellar/sdk/Transaction.java b/src/main/java/org/stellar/sdk/Transaction.java index c653879ff..43a4f29cb 100644 --- a/src/main/java/org/stellar/sdk/Transaction.java +++ b/src/main/java/org/stellar/sdk/Transaction.java @@ -1,10 +1,8 @@ package org.stellar.sdk; import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; import org.stellar.sdk.xdr.*; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; @@ -19,100 +17,60 @@ /** * Represents Transaction in Stellar network. */ -public class Transaction { - private final int mFee; +public class Transaction extends AbstractTransaction { + private final long mFee; private final String mSourceAccount; private final long mSequenceNumber; private final Operation[] mOperations; private final Memo mMemo; private final TimeBounds mTimeBounds; - private final Network mNetwork; - private List mSignatures; + private EnvelopeType envelopeType = EnvelopeType.ENVELOPE_TYPE_TX_V0; Transaction( String sourceAccount, - int fee, + long fee, long sequenceNumber, Operation[] operations, Memo memo, TimeBounds timeBounds, Network network ) { + super(network); this.mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null"); this.mSequenceNumber = checkNotNull(sequenceNumber, "sequenceNumber cannot be null"); this.mOperations = checkNotNull(operations, "operations cannot be null"); checkArgument(operations.length > 0, "At least one operation required"); this.mFee = fee; - this.mSignatures = new ArrayList(); this.mMemo = memo != null ? memo : Memo.none(); this.mTimeBounds = timeBounds; - this.mNetwork = checkNotNull(network, "network cannot be null"); } - /** - * Adds a new signature ed25519PublicKey to this transaction. - * @param signer {@link KeyPair} object representing a signer - */ - public void sign(KeyPair signer) { - checkNotNull(signer, "signer cannot be null"); - byte[] txHash = this.hash(); - mSignatures.add(signer.signDecorated(txHash)); - } - - /** - * Adds a new sha256Hash signature to this transaction by revealing preimage. - * @param preimage the sha256 hash of preimage should be equal to signer hash - */ - public void sign(byte[] preimage) { - checkNotNull(preimage, "preimage cannot be null"); - org.stellar.sdk.xdr.Signature signature = new org.stellar.sdk.xdr.Signature(); - signature.setSignature(preimage); - - byte[] hash = Util.hash(preimage); - byte[] signatureHintBytes = Arrays.copyOfRange(hash, hash.length - 4, hash.length); - SignatureHint signatureHint = new SignatureHint(); - signatureHint.setSignatureHint(signatureHintBytes); - - DecoratedSignature decoratedSignature = new DecoratedSignature(); - decoratedSignature.setHint(signatureHint); - decoratedSignature.setSignature(signature); - - mSignatures.add(decoratedSignature); + // setEnvelopeType is only used in tests which is why this method is package protected + void setEnvelopeType(EnvelopeType envelopeType) { + this.envelopeType = envelopeType; } - /** - * Returns transaction hash. - */ - public byte[] hash() { - return Util.hash(this.signatureBase()); - } - - /** - * Returns signature base. - */ + @Override public byte[] signatureBase() { try { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - // Hashed NetworkID - outputStream.write(mNetwork.getNetworkId()); - // Envelope Type - 4 bytes - outputStream.write(ByteBuffer.allocate(4).putInt(EnvelopeType.ENVELOPE_TYPE_TX.getValue()).array()); - // Transaction XDR bytes + TransactionSignaturePayload payload = new TransactionSignaturePayload(); + TransactionSignaturePayload.TransactionSignaturePayloadTaggedTransaction taggedTransaction = new TransactionSignaturePayload.TransactionSignaturePayloadTaggedTransaction(); + taggedTransaction.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX); + taggedTransaction.setTx(this.toV1Xdr()); + Hash hash = new Hash(); + hash.setHash(mNetwork.getNetworkId()); + payload.setNetworkId(hash); + payload.setTaggedTransaction(taggedTransaction); ByteArrayOutputStream txOutputStream = new ByteArrayOutputStream(); XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(txOutputStream); - org.stellar.sdk.xdr.Transaction.encode(xdrOutputStream, this.toXdr()); - outputStream.write(txOutputStream.toByteArray()); - - return outputStream.toByteArray(); - } catch (IOException exception) { - return null; + payload.encode(xdrOutputStream); + return txOutputStream.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); } } - public Network getNetwork() { - return mNetwork; - } public String getSourceAccount() { return mSourceAccount; @@ -122,10 +80,6 @@ public long getSequenceNumber() { return mSequenceNumber; } - public List getSignatures() { - return mSignatures; - } - public Memo getMemo() { return mMemo; } @@ -140,7 +94,7 @@ public TimeBounds getTimeBounds() { /** * Returns fee paid for transaction in stroops (1 stroop = 0.0000001 XLM). */ - public int getFee() { + public long getFee() { return mFee; } @@ -154,14 +108,14 @@ public Operation[] getOperations() { /** * Generates Transaction XDR object. */ - public org.stellar.sdk.xdr.Transaction toXdr() { + private TransactionV0 toXdr() { // fee - org.stellar.sdk.xdr.Uint32 fee = new org.stellar.sdk.xdr.Uint32(); - fee.setUint32(mFee); + Uint32 fee = new Uint32(); + fee.setUint32((int)mFee); // sequenceNumber - org.stellar.sdk.xdr.Int64 sequenceNumberUint = new org.stellar.sdk.xdr.Int64(); + Int64 sequenceNumberUint = new Int64(); sequenceNumberUint.setInt64(mSequenceNumber); - org.stellar.sdk.xdr.SequenceNumber sequenceNumber = new org.stellar.sdk.xdr.SequenceNumber(); + SequenceNumber sequenceNumber = new SequenceNumber(); sequenceNumber.setSequenceNumber(sequenceNumberUint); // operations org.stellar.sdk.xdr.Operation[] operations = new org.stellar.sdk.xdr.Operation[mOperations.length]; @@ -169,13 +123,13 @@ public org.stellar.sdk.xdr.Transaction toXdr() { operations[i] = mOperations[i].toXdr(); } // ext - org.stellar.sdk.xdr.Transaction.TransactionExt ext = new org.stellar.sdk.xdr.Transaction.TransactionExt(); + TransactionV0.TransactionV0Ext ext = new TransactionV0.TransactionV0Ext(); ext.setDiscriminant(0); - org.stellar.sdk.xdr.Transaction transaction = new org.stellar.sdk.xdr.Transaction(); + TransactionV0 transaction = new TransactionV0(); transaction.setFee(fee); transaction.setSeqNum(sequenceNumber); - transaction.setSourceAccount(StrKey.encodeToXDRAccountId(this.mSourceAccount)); + transaction.setSourceAccountEd25519(StrKey.encodeToXDRAccountId(this.mSourceAccount).getAccountID().getEd25519()); transaction.setOperations(operations); transaction.setMemo(mMemo.toXdr()); transaction.setTimeBounds(mTimeBounds == null ? null : mTimeBounds.toXdr()); @@ -183,45 +137,57 @@ public org.stellar.sdk.xdr.Transaction toXdr() { return transaction; } - /** - * Creates a Transaction instance from previously build TransactionEnvelope - * @param envelope Base-64 encoded TransactionEnvelope - * @return - * @throws IOException - */ - public static Transaction fromEnvelopeXdr(String envelope, Network network) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(envelope); + private org.stellar.sdk.xdr.Transaction toV1Xdr() { + + // fee + Uint32 fee = new Uint32(); + fee.setUint32((int)mFee); + // sequenceNumber + Int64 sequenceNumberUint = new Int64(); + sequenceNumberUint.setInt64(mSequenceNumber); + SequenceNumber sequenceNumber = new SequenceNumber(); + sequenceNumber.setSequenceNumber(sequenceNumberUint); + // operations + org.stellar.sdk.xdr.Operation[] operations = new org.stellar.sdk.xdr.Operation[mOperations.length]; + for (int i = 0; i < mOperations.length; i++) { + operations[i] = mOperations[i].toXdr(); + } + // ext + org.stellar.sdk.xdr.Transaction.TransactionExt ext = new org.stellar.sdk.xdr.Transaction.TransactionExt(); + ext.setDiscriminant(0); + - TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - return fromEnvelopeXdr(transactionEnvelope, network); + org.stellar.sdk.xdr.Transaction v1Tx = new org.stellar.sdk.xdr.Transaction(); + v1Tx.setFee(fee); + v1Tx.setSeqNum(sequenceNumber); + v1Tx.setSourceAccount(StrKey.encodeToXDRMuxedAccount(mSourceAccount)); + v1Tx.setOperations(operations); + v1Tx.setMemo(mMemo.toXdr()); + v1Tx.setTimeBounds(mTimeBounds == null ? null : mTimeBounds.toXdr()); + v1Tx.setExt(ext); + + return v1Tx; } - /** - * Creates a Transaction instance from previously build TransactionEnvelope - * @param envelope - * @return - */ - public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope, Network network) { - org.stellar.sdk.xdr.Transaction tx = envelope.getTx(); - int mFee = tx.getFee().getUint32(); - Long mSequenceNumber = tx.getSeqNum().getSequenceNumber().getInt64(); - Memo mMemo = Memo.fromXdr(tx.getMemo()); - TimeBounds mTimeBounds = TimeBounds.fromXdr(tx.getTimeBounds()); - - Operation[] mOperations = new Operation[tx.getOperations().length]; - for (int i = 0; i < tx.getOperations().length; i++) { - mOperations[i] = Operation.fromXdr(tx.getOperations()[i]); + public static Transaction fromV0EnvelopeXdr(TransactionV0Envelope envelope, Network network) { + int mFee = envelope.getTx().getFee().getUint32(); + Long mSequenceNumber = envelope.getTx().getSeqNum().getSequenceNumber().getInt64(); + Memo mMemo = Memo.fromXdr(envelope.getTx().getMemo()); + TimeBounds mTimeBounds = TimeBounds.fromXdr(envelope.getTx().getTimeBounds()); + + Operation[] mOperations = new Operation[envelope.getTx().getOperations().length]; + for (int i = 0; i < envelope.getTx().getOperations().length; i++) { + mOperations[i] = Operation.fromXdr(envelope.getTx().getOperations()[i]); } Transaction transaction = new Transaction( - StrKey.encodeStellarAccountId(tx.getSourceAccount().getAccountID().getEd25519().getUint256()), - mFee, - mSequenceNumber, - mOperations, - mMemo, - mTimeBounds, - network + StrKey.encodeStellarAccountId(envelope.getTx().getSourceAccountEd25519().getUint256()), + mFee, + mSequenceNumber, + mOperations, + mMemo, + mTimeBounds, + network ); for (DecoratedSignature signature : envelope.getSignatures()) { @@ -231,61 +197,76 @@ public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope, Network return transaction; } - /** - * Generates TransactionEnvelope XDR object. - */ - public org.stellar.sdk.xdr.TransactionEnvelope toEnvelopeXdr() { - org.stellar.sdk.xdr.TransactionEnvelope xdr = new org.stellar.sdk.xdr.TransactionEnvelope(); - org.stellar.sdk.xdr.Transaction transaction = this.toXdr(); - xdr.setTx(transaction); + public static Transaction fromV1EnvelopeXdr(TransactionV1Envelope envelope, Network network) { + int mFee = envelope.getTx().getFee().getUint32(); + Long mSequenceNumber = envelope.getTx().getSeqNum().getSequenceNumber().getInt64(); + Memo mMemo = Memo.fromXdr(envelope.getTx().getMemo()); + TimeBounds mTimeBounds = TimeBounds.fromXdr(envelope.getTx().getTimeBounds()); - DecoratedSignature[] signatures = new DecoratedSignature[mSignatures.size()]; - signatures = mSignatures.toArray(signatures); - xdr.setSignatures(signatures); - return xdr; + Operation[] mOperations = new Operation[envelope.getTx().getOperations().length]; + for (int i = 0; i < envelope.getTx().getOperations().length; i++) { + mOperations[i] = Operation.fromXdr(envelope.getTx().getOperations()[i]); + } + + Transaction transaction = new Transaction( + StrKey.encodeStellarMuxedAccount(envelope.getTx().getSourceAccount()), + mFee, + mSequenceNumber, + mOperations, + mMemo, + mTimeBounds, + network + ); + + for (DecoratedSignature signature : envelope.getSignatures()) { + transaction.mSignatures.add(signature); + } + + return transaction; } /** - * Returns base64-encoded TransactionEnvelope XDR object. Transaction need to have at least one signature. + * Generates TransactionEnvelope XDR object. */ - public String toEnvelopeXdrBase64() { - try { - org.stellar.sdk.xdr.TransactionEnvelope envelope = this.toEnvelopeXdr(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(outputStream); - org.stellar.sdk.xdr.TransactionEnvelope.encode(xdrOutputStream, envelope); + @Override + public TransactionEnvelope toEnvelopeXdr() { + TransactionEnvelope xdr = new TransactionEnvelope(); + DecoratedSignature[] signatures = new DecoratedSignature[mSignatures.size()]; + signatures = mSignatures.toArray(signatures); - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(outputStream.toByteArray()); - } catch (IOException e) { - throw new AssertionError(e); + if (this.envelopeType == EnvelopeType.ENVELOPE_TYPE_TX) { + TransactionV1Envelope v1Envelope = new TransactionV1Envelope(); + xdr.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX); + v1Envelope.setTx(this.toV1Xdr()); + v1Envelope.setSignatures(signatures); + xdr.setV1(v1Envelope); + } else if (this.envelopeType == EnvelopeType.ENVELOPE_TYPE_TX_V0) { + TransactionV0Envelope v0Envelope = new TransactionV0Envelope(); + xdr.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX_V0); + v0Envelope.setTx(this.toXdr()); + v0Envelope.setSignatures(signatures); + xdr.setV0(v0Envelope); + } else { + throw new RuntimeException("invalid envelope type: "+this.envelopeType); } + + return xdr; } /** * Builds a new Transaction object. */ public static class Builder { - private static final int BASE_FEE = 100; private final TransactionBuilderAccount mSourceAccount; private Memo mMemo; private TimeBounds mTimeBounds; List mOperations; private boolean timeoutSet; - private static Integer defaultOperationFee; - private Integer operationFee; + private Integer mBaseFee; private Network mNetwork; public static final long TIMEOUT_INFINITE = 0; - public static void setDefaultOperationFee(int opFee) { - if (opFee < BASE_FEE) { - throw new IllegalArgumentException("DefaultOperationFee cannot be smaller than the BASE_FEE (" + BASE_FEE + "): " + opFee); - } - - defaultOperationFee = opFee; - } - /** * Construct a new transaction builder. * @param sourceAccount The source account for this transaction. This account is the account @@ -297,7 +278,6 @@ public Builder(TransactionBuilderAccount sourceAccount, Network network) { mSourceAccount = sourceAccount; mOperations = Collections.synchronizedList(new ArrayList()); mNetwork = checkNotNull(network, "Network cannot be null"); - operationFee = defaultOperationFee; } public int getOperationsCount() { @@ -384,12 +364,12 @@ public Builder setTimeout(long timeout) { return this; } - public Builder setOperationFee(int operationFee) { - if (operationFee < BASE_FEE) { - throw new IllegalArgumentException("OperationFee cannot be smaller than the BASE_FEE (" + BASE_FEE + "): " + operationFee); + public Builder setBaseFee(int baseFee) { + if (baseFee < MIN_BASE_FEE) { + throw new IllegalArgumentException("baseFee cannot be smaller than the BASE_FEE (" + MIN_BASE_FEE + "): " + baseFee); } - this.operationFee = operationFee; + this.mBaseFee = baseFee; return this; } @@ -402,9 +382,8 @@ public Transaction build() { throw new RuntimeException("TimeBounds has to be set or you must call setTimeout(TIMEOUT_INFINITE)."); } - if (operationFee == null) { - System.out.println("[TransactionBuilder] The `operationFee` parameter of `TransactionBuilder` is required. Setting to BASE_FEE=" + BASE_FEE + ". Future versions of this library will error if not provided."); - operationFee = BASE_FEE; + if (mBaseFee == null) { + throw new RuntimeException("mBaseFee has to be set. you must call setBaseFee()."); } if (mNetwork == null) { @@ -415,7 +394,7 @@ public Transaction build() { operations = mOperations.toArray(operations); Transaction transaction = new Transaction( mSourceAccount.getAccountId(), - operations.length * operationFee, + operations.length * mBaseFee, mSourceAccount.getIncrementedSequenceNumber(), operations, mMemo, @@ -437,7 +416,8 @@ public int hashCode() { Arrays.hashCode(this.mOperations), this.mMemo, this.mTimeBounds, - this.mSignatures + this.mSignatures, + this.mNetwork ); } @@ -454,6 +434,7 @@ public boolean equals(Object object) { Arrays.equals(this.mOperations, other.mOperations) && Objects.equal(this.mMemo, other.mMemo) && Objects.equal(this.mTimeBounds, other.mTimeBounds) && + Objects.equal(this.mNetwork, other.mNetwork) && Objects.equal(this.mSignatures, other.mSignatures); } } diff --git a/src/main/java/org/stellar/sdk/responses/AccountResponse.java b/src/main/java/org/stellar/sdk/responses/AccountResponse.java index 6535ad869..fe119b21e 100644 --- a/src/main/java/org/stellar/sdk/responses/AccountResponse.java +++ b/src/main/java/org/stellar/sdk/responses/AccountResponse.java @@ -193,10 +193,12 @@ public static class Balance { private final String sellingLiabilities; @SerializedName("is_authorized") private final Boolean isAuthorized; + @SerializedName("is_authorized_to_maintain_liabilities") + private final Boolean isAuthorizedToMaintainLiabilities; @SerializedName("last_modified_ledger") private final Integer lastModifiedLedger; - Balance(String assetType, String assetCode, String assetIssuer, String balance, String limit, String buyingLiabilities, String sellingLiabilities, Boolean isAuthorized, Integer lastModifiedLedger) { + Balance(String assetType, String assetCode, String assetIssuer, String balance, String limit, String buyingLiabilities, String sellingLiabilities, Boolean isAuthorized, Boolean isAuthorizedToMaintainLiabilities, Integer lastModifiedLedger) { this.assetType = checkNotNull(assetType, "assertType cannot be null"); this.balance = checkNotNull(balance, "balance cannot be null"); this.limit = limit; @@ -205,6 +207,7 @@ public static class Balance { this.buyingLiabilities = checkNotNull(buyingLiabilities, "buyingLiabilities cannot be null"); this.sellingLiabilities = checkNotNull(sellingLiabilities, "sellingLiabilities cannot be null"); this.isAuthorized = isAuthorized; + this.isAuthorizedToMaintainLiabilities = isAuthorizedToMaintainLiabilities; this.lastModifiedLedger = lastModifiedLedger; } @@ -248,6 +251,10 @@ public Boolean getAuthorized() { return isAuthorized; } + public Boolean getAuthorizedToMaintainLiabilities() { + return isAuthorizedToMaintainLiabilities; + } + public Integer getLastModifiedLedger() { return lastModifiedLedger; } diff --git a/src/main/java/org/stellar/sdk/responses/EffectDeserializer.java b/src/main/java/org/stellar/sdk/responses/EffectDeserializer.java index 6d67550db..52000d08a 100644 --- a/src/main/java/org/stellar/sdk/responses/EffectDeserializer.java +++ b/src/main/java/org/stellar/sdk/responses/EffectDeserializer.java @@ -55,6 +55,8 @@ public EffectResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializ return gson.fromJson(json, TrustlineAuthorizedEffectResponse.class); case 24: return gson.fromJson(json, TrustlineDeauthorizedEffectResponse.class); + case 25: + return gson.fromJson(json, TrustlineAuthorizedToMaintainLiabilitiesEffectResponse.class); // Trading effects case 30: return gson.fromJson(json, OfferCreatedEffectResponse.class); diff --git a/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java b/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java index 428ef7e93..771d0f762 100644 --- a/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java +++ b/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java @@ -1,12 +1,7 @@ package org.stellar.sdk.responses; import com.google.common.io.BaseEncoding; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; +import com.google.gson.*; import org.stellar.sdk.Memo; import org.stellar.sdk.xdr.TransactionEnvelope; @@ -17,6 +12,23 @@ import java.lang.reflect.Type; public class TransactionDeserializer implements JsonDeserializer { + + private Memo extractTextMemo(TransactionEnvelope transactionEnvelope) { + switch (transactionEnvelope.getDiscriminant()) { + case ENVELOPE_TYPE_TX: + return Memo.text(transactionEnvelope.getV1().getTx().getMemo().getText().getBytes()); + case ENVELOPE_TYPE_TX_V0: + return Memo.text(transactionEnvelope.getV0().getTx().getMemo().getText().getBytes()); + case ENVELOPE_TYPE_TX_FEE_BUMP: + return Memo.text( + transactionEnvelope.getFeeBump().getTx().getInnerTx() + .getV1().getTx().getMemo().getText().getBytes() + ); + default: + throw new IllegalArgumentException("invalid transaction type: "+transactionEnvelope.getDiscriminant()); + } + } + @Override public TransactionResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { // Create new Gson object with adapters needed in Transaction @@ -35,19 +47,28 @@ public TransactionResponse deserialize(JsonElement json, Type typeOfT, JsonDeser // representation of a transaction. That's why we need to handle a special case // here. if (memoType.equals("text")) { - // we obtain the memo text from the xdr because the bytes may not be valid utf8 - String envelopeXdr = json.getAsJsonObject().get("envelope_xdr").getAsString(); BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(envelopeXdr); - TransactionEnvelope transactionEnvelope = null; - try { - transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - } catch (IOException e) { - // JsonDeserializer cannot throw IOExceptions - // so we must throw it as a runtime exception - throw new RuntimeException(e); + JsonObject jsonResponse = json.getAsJsonObject(); + + if (jsonResponse.has("memo_bytes")) { + // we obtain the memo text from the "memo_bytes" field because the original byte sequence may not be valid utf8 + String memoBase64 = json.getAsJsonObject().get("memo_bytes").getAsString(); + memo = Memo.text(base64Encoding.decode(memoBase64)); + } else { + // memo_bytes is not available because horizon is running a version older than 1.2.0 + // so we will recover the bytes from the xdr + String envelopeXdr = json.getAsJsonObject().get("envelope_xdr").getAsString(); + byte[] bytes = base64Encoding.decode(envelopeXdr); + TransactionEnvelope transactionEnvelope = null; + try { + transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); + } catch (IOException e) { + // JsonDeserializer cannot throw IOExceptions + // so we must throw it as a runtime exception + throw new RuntimeException(e); + } + memo = extractTextMemo(transactionEnvelope); } - memo = Memo.text(transactionEnvelope.getTx().getMemo().getText().getBytes()); } else { String memoValue = json.getAsJsonObject().get("memo").getAsString(); BaseEncoding base64Encoding = BaseEncoding.base64(); diff --git a/src/main/java/org/stellar/sdk/responses/TransactionResponse.java b/src/main/java/org/stellar/sdk/responses/TransactionResponse.java index 1a7de478d..4d5c9c08d 100644 --- a/src/main/java/org/stellar/sdk/responses/TransactionResponse.java +++ b/src/main/java/org/stellar/sdk/responses/TransactionResponse.java @@ -1,9 +1,12 @@ package org.stellar.sdk.responses; +import com.google.common.base.Optional; import com.google.gson.annotations.SerializedName; import org.stellar.sdk.Memo; +import java.util.List; + import static com.google.common.base.Preconditions.checkNotNull; /** @@ -21,6 +24,8 @@ public class TransactionResponse extends Response implements Pageable { private final String createdAt; @SerializedName("source_account") private final String sourceAccount; + @SerializedName("fee_account") + private final String feeAccount; @SerializedName("successful") private final Boolean successful; @SerializedName("paging_token") @@ -39,6 +44,12 @@ public class TransactionResponse extends Response implements Pageable { private final String resultXdr; @SerializedName("result_meta_xdr") private final String resultMetaXdr; + @SerializedName("signatures") + private final List signatures; + @SerializedName("fee_bump_transaction") + private final FeeBumpTransaction feeBumpTransaction; + @SerializedName("inner_transaction") + private final InnerTransaction innerTransaction; @SerializedName("_links") private final Links links; @@ -51,6 +62,7 @@ public class TransactionResponse extends Response implements Pageable { Long ledger, String createdAt, String sourceAccount, + String feeAccount, Boolean successful, String pagingToken, Long sourceAccountSequence, @@ -61,12 +73,16 @@ public class TransactionResponse extends Response implements Pageable { String resultXdr, String resultMetaXdr, Memo memo, + List signatures, + FeeBumpTransaction feeBumpTransaction, + InnerTransaction innerTransaction, Links links ) { this.hash = hash; this.ledger = ledger; this.createdAt = createdAt; this.sourceAccount = sourceAccount; + this.feeAccount = feeAccount; this.successful = successful; this.pagingToken = pagingToken; this.sourceAccountSequence = sourceAccountSequence; @@ -77,6 +93,9 @@ public class TransactionResponse extends Response implements Pageable { this.resultXdr = resultXdr; this.resultMetaXdr = resultMetaXdr; this.memo = memo; + this.signatures = signatures; + this.feeBumpTransaction = feeBumpTransaction; + this.innerTransaction = innerTransaction; this.links = links; } @@ -96,6 +115,22 @@ public String getSourceAccount() { return sourceAccount; } + public String getFeeAccount() { + return feeAccount; + } + + public List getSignatures() { + return signatures; + } + + public Optional getFeeBump() { + return Optional.fromNullable(this.feeBumpTransaction); + } + + public Optional getInner() { + return Optional.fromNullable(this.innerTransaction); + } + public String getPagingToken() { return pagingToken; } @@ -148,6 +183,66 @@ public Links getLinks() { return links; } + /** + * FeeBumpTransaction is only present in a TransactionResponse if the transaction is a fee bump transaction or is + * wrapped by a fee bump transaction. The object has two fields: the hash of the fee bump transaction and the + * signatures present in the fee bump transaction envelope. + */ + public static class FeeBumpTransaction { + @SerializedName("hash") + private final String hash; + @SerializedName("signatures") + private final List signatures; + + FeeBumpTransaction(String hash, List signatures) { + this.hash = hash; + this.signatures = signatures; + } + + public String getHash() { + return hash; + } + + public List getSignatures() { + return signatures; + } + } + + /** + * InnerTransaction is only present in a TransactionResponse if the transaction is a fee bump transaction or is + * wrapped by a fee bump transaction. The object has three fields: the hash of the inner transaction wrapped by the + * fee bump transaction, the max fee set in the inner transaction, and the signatures present in the inner + * transaction envelope. + */ + public static class InnerTransaction { + @SerializedName("hash") + private final String hash; + @SerializedName("signatures") + private final List signatures; + @SerializedName("max_fee") + private final Long maxFee; + + + InnerTransaction(String hash, List signatures, Long maxFee) { + this.hash = hash; + this.signatures = signatures; + this.maxFee = maxFee; + } + + public String getHash() { + return hash; + } + + public List getSignatures() { + return signatures; + } + + public Long getMaxFee() { + return maxFee; + } + + } + /** * Links connected to transaction. */ diff --git a/src/main/java/org/stellar/sdk/responses/effects/EffectResponse.java b/src/main/java/org/stellar/sdk/responses/effects/EffectResponse.java index 25e8ce2ba..83f9f37fe 100644 --- a/src/main/java/org/stellar/sdk/responses/effects/EffectResponse.java +++ b/src/main/java/org/stellar/sdk/responses/effects/EffectResponse.java @@ -52,6 +52,7 @@ public String getAccount() { *
  • trustline_removed
  • *
  • trustline_updated
  • *
  • trustline_authorized
  • + *
  • trustline_authorized_to_maintain_liabilities
  • *
  • trustline_deauthorized
  • *
  • offer_created
  • *
  • offer_removed
  • diff --git a/src/main/java/org/stellar/sdk/responses/effects/TrustlineAuthorizedToMaintainLiabilitiesEffectResponse.java b/src/main/java/org/stellar/sdk/responses/effects/TrustlineAuthorizedToMaintainLiabilitiesEffectResponse.java new file mode 100644 index 000000000..65d019a38 --- /dev/null +++ b/src/main/java/org/stellar/sdk/responses/effects/TrustlineAuthorizedToMaintainLiabilitiesEffectResponse.java @@ -0,0 +1,13 @@ +package org.stellar.sdk.responses.effects; + +/** + * Represents trustline_authorized_to_maintain_liabilities effect response. + * @see Effect documentation + * @see org.stellar.sdk.requests.EffectsRequestBuilder + * @see org.stellar.sdk.Server#effects() + */ +public class TrustlineAuthorizedToMaintainLiabilitiesEffectResponse extends TrustlineAuthorizationResponse { + TrustlineAuthorizedToMaintainLiabilitiesEffectResponse(String trustor, String assetType, String assetCode) { + super(trustor, assetType, assetCode); + } +} diff --git a/src/main/java/org/stellar/sdk/responses/operations/AllowTrustOperationResponse.java b/src/main/java/org/stellar/sdk/responses/operations/AllowTrustOperationResponse.java index 991283960..9d648ffa5 100644 --- a/src/main/java/org/stellar/sdk/responses/operations/AllowTrustOperationResponse.java +++ b/src/main/java/org/stellar/sdk/responses/operations/AllowTrustOperationResponse.java @@ -24,9 +24,12 @@ public class AllowTrustOperationResponse extends OperationResponse { protected final String assetIssuer; @SerializedName("authorize") protected final boolean authorize; + @SerializedName("authorize_to_maintain_liabilities") + protected final boolean authorizeToMaintainLiabilities; - AllowTrustOperationResponse(boolean authorize, String assetIssuer, String assetCode, String assetType, String trustee, String trustor) { + AllowTrustOperationResponse(boolean authorize, boolean authorizeToMaintainLiabilities, String assetIssuer, String assetCode, String assetType, String trustee, String trustor) { this.authorize = authorize; + this.authorizeToMaintainLiabilities = authorizeToMaintainLiabilities; this.assetIssuer = assetIssuer; this.assetCode = assetCode; this.assetType = assetType; @@ -46,6 +49,11 @@ public boolean isAuthorize() { return authorize; } + public boolean isAuthorizedToMaintainLiabilities() { + return authorizeToMaintainLiabilities; + } + + public Asset getAsset() { if (assetType.equals("native")) { return new AssetTypeNative(); diff --git a/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java b/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java index 6cd680e19..e836b2fae 100644 --- a/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java +++ b/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java @@ -26,7 +26,8 @@ // } // asset; // -// bool authorize; +// // 0, or any bitwise combination of TrustLineFlags +// uint32 authorize; // }; // =========================================================================== @@ -46,17 +47,17 @@ public AllowTrustOpAsset getAsset() { public void setAsset(AllowTrustOpAsset value) { this.asset = value; } - private Boolean authorize; - public Boolean getAuthorize() { + private Uint32 authorize; + public Uint32 getAuthorize() { return this.authorize; } - public void setAuthorize(Boolean value) { + public void setAuthorize(Uint32 value) { this.authorize = value; } public static void encode(XdrDataOutputStream stream, AllowTrustOp encodedAllowTrustOp) throws IOException{ AccountID.encode(stream, encodedAllowTrustOp.trustor); AllowTrustOpAsset.encode(stream, encodedAllowTrustOp.asset); - stream.writeInt(encodedAllowTrustOp.authorize ? 1 : 0); + Uint32.encode(stream, encodedAllowTrustOp.authorize); } public void encode(XdrDataOutputStream stream) throws IOException { encode(stream, this); @@ -65,7 +66,7 @@ public static AllowTrustOp decode(XdrDataInputStream stream) throws IOException AllowTrustOp decodedAllowTrustOp = new AllowTrustOp(); decodedAllowTrustOp.trustor = AccountID.decode(stream); decodedAllowTrustOp.asset = AllowTrustOpAsset.decode(stream); - decodedAllowTrustOp.authorize = stream.readInt() == 1 ? true : false; + decodedAllowTrustOp.authorize = Uint32.decode(stream); return decodedAllowTrustOp; } @Override diff --git a/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java b/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java index 73d1c7855..559567c30 100644 --- a/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java @@ -14,10 +14,10 @@ // { // case 0: // struct -// { -// uint64 sequence; -// StellarMessage message; -// HmacSha256Mac mac; +// { +// uint64 sequence; +// StellarMessage message; +// HmacSha256Mac mac; // } v0; // }; diff --git a/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java b/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java index 52f3523ab..e351dae2e 100644 --- a/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java @@ -20,7 +20,7 @@ // // cannot create with a limit of 0 // CHANGE_TRUST_LOW_RESERVE = // -4, // not enough funds to create a new trust line, -// CHANGE_TRUST_SELF_NOT_ALLOWED = -5 // trusting self is not allowed +// CHANGE_TRUST_SELF_NOT_ALLOWED = -5 // trusting self is not allowed // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java b/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java index bdbfab00d..bfaf14e5c 100644 --- a/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java +++ b/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java @@ -13,7 +13,10 @@ // { // KEY_TYPE_ED25519 = 0, // KEY_TYPE_PRE_AUTH_TX = 1, -// KEY_TYPE_HASH_X = 2 +// KEY_TYPE_HASH_X = 2, +// // MUXED enum values for supported type are derived from the enum values +// // above by ORing them with 0x100 +// KEY_TYPE_MUXED_ED25519 = 0x100 // }; // =========================================================================== @@ -21,6 +24,7 @@ public enum CryptoKeyType implements XdrElement { KEY_TYPE_ED25519(0), KEY_TYPE_PRE_AUTH_TX(1), KEY_TYPE_HASH_X(2), + KEY_TYPE_MUXED_ED25519(256), ; private int mValue; @@ -38,6 +42,7 @@ public static CryptoKeyType decode(XdrDataInputStream stream) throws IOException case 0: return KEY_TYPE_ED25519; case 1: return KEY_TYPE_PRE_AUTH_TX; case 2: return KEY_TYPE_HASH_X; + case 256: return KEY_TYPE_MUXED_ED25519; default: throw new RuntimeException("Unknown enum value: " + value); } diff --git a/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java b/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java index e3c00fccd..5d70e454d 100644 --- a/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java +++ b/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java @@ -12,7 +12,7 @@ // struct Curve25519Public // { -// opaque key[32]; +// opaque key[32]; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java b/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java index 68677bc24..491c1b404 100644 --- a/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java +++ b/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java @@ -12,7 +12,7 @@ // struct Curve25519Secret // { -// opaque key[32]; +// opaque key[32]; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java b/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java index 3ff079281..90bfba5e6 100644 --- a/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java +++ b/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java @@ -11,18 +11,22 @@ // enum EnvelopeType // { +// ENVELOPE_TYPE_TX_V0 = 0, // ENVELOPE_TYPE_SCP = 1, // ENVELOPE_TYPE_TX = 2, // ENVELOPE_TYPE_AUTH = 3, -// ENVELOPE_TYPE_SCPVALUE = 4 +// ENVELOPE_TYPE_SCPVALUE = 4, +// ENVELOPE_TYPE_TX_FEE_BUMP = 5 // }; // =========================================================================== public enum EnvelopeType implements XdrElement { + ENVELOPE_TYPE_TX_V0(0), ENVELOPE_TYPE_SCP(1), ENVELOPE_TYPE_TX(2), ENVELOPE_TYPE_AUTH(3), ENVELOPE_TYPE_SCPVALUE(4), + ENVELOPE_TYPE_TX_FEE_BUMP(5), ; private int mValue; @@ -37,10 +41,12 @@ public int getValue() { public static EnvelopeType decode(XdrDataInputStream stream) throws IOException { int value = stream.readInt(); switch (value) { + case 0: return ENVELOPE_TYPE_TX_V0; case 1: return ENVELOPE_TYPE_SCP; case 2: return ENVELOPE_TYPE_TX; case 3: return ENVELOPE_TYPE_AUTH; case 4: return ENVELOPE_TYPE_SCPVALUE; + case 5: return ENVELOPE_TYPE_TX_FEE_BUMP; default: throw new RuntimeException("Unknown enum value: " + value); } diff --git a/src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java new file mode 100644 index 000000000..69c65bd4a --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java @@ -0,0 +1,194 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; + +// === xdr source ============================================================ + +// struct FeeBumpTransaction +// { +// MuxedAccount feeSource; +// int64 fee; +// union switch (EnvelopeType type) +// { +// case ENVELOPE_TYPE_TX: +// TransactionV1Envelope v1; +// } +// innerTx; +// union switch (int v) +// { +// case 0: +// void; +// } +// ext; +// }; + +// =========================================================================== +public class FeeBumpTransaction implements XdrElement { + public FeeBumpTransaction () {} + private MuxedAccount feeSource; + public MuxedAccount getFeeSource() { + return this.feeSource; + } + public void setFeeSource(MuxedAccount value) { + this.feeSource = value; + } + private Int64 fee; + public Int64 getFee() { + return this.fee; + } + public void setFee(Int64 value) { + this.fee = value; + } + private FeeBumpTransactionInnerTx innerTx; + public FeeBumpTransactionInnerTx getInnerTx() { + return this.innerTx; + } + public void setInnerTx(FeeBumpTransactionInnerTx value) { + this.innerTx = value; + } + private FeeBumpTransactionExt ext; + public FeeBumpTransactionExt getExt() { + return this.ext; + } + public void setExt(FeeBumpTransactionExt value) { + this.ext = value; + } + public static void encode(XdrDataOutputStream stream, FeeBumpTransaction encodedFeeBumpTransaction) throws IOException{ + MuxedAccount.encode(stream, encodedFeeBumpTransaction.feeSource); + Int64.encode(stream, encodedFeeBumpTransaction.fee); + FeeBumpTransactionInnerTx.encode(stream, encodedFeeBumpTransaction.innerTx); + FeeBumpTransactionExt.encode(stream, encodedFeeBumpTransaction.ext); + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static FeeBumpTransaction decode(XdrDataInputStream stream) throws IOException { + FeeBumpTransaction decodedFeeBumpTransaction = new FeeBumpTransaction(); + decodedFeeBumpTransaction.feeSource = MuxedAccount.decode(stream); + decodedFeeBumpTransaction.fee = Int64.decode(stream); + decodedFeeBumpTransaction.innerTx = FeeBumpTransactionInnerTx.decode(stream); + decodedFeeBumpTransaction.ext = FeeBumpTransactionExt.decode(stream); + return decodedFeeBumpTransaction; + } + @Override + public int hashCode() { + return Objects.hashCode(this.feeSource, this.fee, this.innerTx, this.ext); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof FeeBumpTransaction)) { + return false; + } + + FeeBumpTransaction other = (FeeBumpTransaction) object; + return Objects.equal(this.feeSource, other.feeSource) && Objects.equal(this.fee, other.fee) && Objects.equal(this.innerTx, other.innerTx) && Objects.equal(this.ext, other.ext); + } + + public static class FeeBumpTransactionInnerTx { + public FeeBumpTransactionInnerTx () {} + EnvelopeType type; + public EnvelopeType getDiscriminant() { + return this.type; + } + public void setDiscriminant(EnvelopeType value) { + this.type = value; + } + private TransactionV1Envelope v1; + public TransactionV1Envelope getV1() { + return this.v1; + } + public void setV1(TransactionV1Envelope value) { + this.v1 = value; + } + public static void encode(XdrDataOutputStream stream, FeeBumpTransactionInnerTx encodedFeeBumpTransactionInnerTx) throws IOException { + //Xdrgen::AST::Identifier + //EnvelopeType + stream.writeInt(encodedFeeBumpTransactionInnerTx.getDiscriminant().getValue()); + switch (encodedFeeBumpTransactionInnerTx.getDiscriminant()) { + case ENVELOPE_TYPE_TX: + TransactionV1Envelope.encode(stream, encodedFeeBumpTransactionInnerTx.v1); + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static FeeBumpTransactionInnerTx decode(XdrDataInputStream stream) throws IOException { + FeeBumpTransactionInnerTx decodedFeeBumpTransactionInnerTx = new FeeBumpTransactionInnerTx(); + EnvelopeType discriminant = EnvelopeType.decode(stream); + decodedFeeBumpTransactionInnerTx.setDiscriminant(discriminant); + switch (decodedFeeBumpTransactionInnerTx.getDiscriminant()) { + case ENVELOPE_TYPE_TX: + decodedFeeBumpTransactionInnerTx.v1 = TransactionV1Envelope.decode(stream); + break; + } + return decodedFeeBumpTransactionInnerTx; + } + @Override + public int hashCode() { + return Objects.hashCode(this.v1, this.type); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof FeeBumpTransactionInnerTx)) { + return false; + } + + FeeBumpTransactionInnerTx other = (FeeBumpTransactionInnerTx) object; + return Objects.equal(this.v1, other.v1) && Objects.equal(this.type, other.type); + } + + } + public static class FeeBumpTransactionExt { + public FeeBumpTransactionExt () {} + Integer v; + public Integer getDiscriminant() { + return this.v; + } + public void setDiscriminant(Integer value) { + this.v = value; + } + public static void encode(XdrDataOutputStream stream, FeeBumpTransactionExt encodedFeeBumpTransactionExt) throws IOException { + //Xdrgen::AST::Typespecs::Int + //Integer + stream.writeInt(encodedFeeBumpTransactionExt.getDiscriminant().intValue()); + switch (encodedFeeBumpTransactionExt.getDiscriminant()) { + case 0: + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static FeeBumpTransactionExt decode(XdrDataInputStream stream) throws IOException { + FeeBumpTransactionExt decodedFeeBumpTransactionExt = new FeeBumpTransactionExt(); + Integer discriminant = stream.readInt(); + decodedFeeBumpTransactionExt.setDiscriminant(discriminant); + switch (decodedFeeBumpTransactionExt.getDiscriminant()) { + case 0: + break; + } + return decodedFeeBumpTransactionExt; + } + @Override + public int hashCode() { + return Objects.hashCode(this.v); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof FeeBumpTransactionExt)) { + return false; + } + + FeeBumpTransactionExt other = (FeeBumpTransactionExt) object; + return Objects.equal(this.v, other.v); + } + + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java new file mode 100644 index 000000000..d19e67cc6 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java @@ -0,0 +1,73 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; +import java.util.Arrays; + +// === xdr source ============================================================ + +// struct FeeBumpTransactionEnvelope +// { +// FeeBumpTransaction tx; +// /* Each decorated signature is a signature over the SHA256 hash of +// * a TransactionSignaturePayload */ +// DecoratedSignature signatures<20>; +// }; + +// =========================================================================== +public class FeeBumpTransactionEnvelope implements XdrElement { + public FeeBumpTransactionEnvelope () {} + private FeeBumpTransaction tx; + public FeeBumpTransaction getTx() { + return this.tx; + } + public void setTx(FeeBumpTransaction value) { + this.tx = value; + } + private DecoratedSignature[] signatures; + public DecoratedSignature[] getSignatures() { + return this.signatures; + } + public void setSignatures(DecoratedSignature[] value) { + this.signatures = value; + } + public static void encode(XdrDataOutputStream stream, FeeBumpTransactionEnvelope encodedFeeBumpTransactionEnvelope) throws IOException{ + FeeBumpTransaction.encode(stream, encodedFeeBumpTransactionEnvelope.tx); + int signaturessize = encodedFeeBumpTransactionEnvelope.getSignatures().length; + stream.writeInt(signaturessize); + for (int i = 0; i < signaturessize; i++) { + DecoratedSignature.encode(stream, encodedFeeBumpTransactionEnvelope.signatures[i]); + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static FeeBumpTransactionEnvelope decode(XdrDataInputStream stream) throws IOException { + FeeBumpTransactionEnvelope decodedFeeBumpTransactionEnvelope = new FeeBumpTransactionEnvelope(); + decodedFeeBumpTransactionEnvelope.tx = FeeBumpTransaction.decode(stream); + int signaturessize = stream.readInt(); + decodedFeeBumpTransactionEnvelope.signatures = new DecoratedSignature[signaturessize]; + for (int i = 0; i < signaturessize; i++) { + decodedFeeBumpTransactionEnvelope.signatures[i] = DecoratedSignature.decode(stream); + } + return decodedFeeBumpTransactionEnvelope; + } + @Override + public int hashCode() { + return Objects.hashCode(this.tx, Arrays.hashCode(this.signatures)); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof FeeBumpTransactionEnvelope)) { + return false; + } + + FeeBumpTransactionEnvelope other = (FeeBumpTransactionEnvelope) object; + return Objects.equal(this.tx, other.tx) && Arrays.equals(this.signatures, other.signatures); + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java b/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java index fc2b0e716..248437704 100644 --- a/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java +++ b/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java @@ -12,7 +12,7 @@ // struct HmacSha256Key // { -// opaque key[32]; +// opaque key[32]; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java b/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java index 1bfbcfd8f..f20647c21 100644 --- a/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java +++ b/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java @@ -12,7 +12,7 @@ // struct HmacSha256Mac // { -// opaque mac[32]; +// opaque mac[32]; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java new file mode 100644 index 000000000..6da5783c3 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java @@ -0,0 +1,238 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; +import java.util.Arrays; + +// === xdr source ============================================================ + +// struct InnerTransactionResult +// { +// // Always 0. Here for binary compatibility. +// int64 feeCharged; +// +// union switch (TransactionResultCode code) +// { +// // txFEE_BUMP_INNER_SUCCESS is not included +// case txSUCCESS: +// case txFAILED: +// OperationResult results<>; +// case txTOO_EARLY: +// case txTOO_LATE: +// case txMISSING_OPERATION: +// case txBAD_SEQ: +// case txBAD_AUTH: +// case txINSUFFICIENT_BALANCE: +// case txNO_ACCOUNT: +// case txINSUFFICIENT_FEE: +// case txBAD_AUTH_EXTRA: +// case txINTERNAL_ERROR: +// case txNOT_SUPPORTED: +// // txFEE_BUMP_INNER_FAILED is not included +// void; +// } +// result; +// +// // reserved for future use +// union switch (int v) +// { +// case 0: +// void; +// } +// ext; +// }; + +// =========================================================================== +public class InnerTransactionResult implements XdrElement { + public InnerTransactionResult () {} + private Int64 feeCharged; + public Int64 getFeeCharged() { + return this.feeCharged; + } + public void setFeeCharged(Int64 value) { + this.feeCharged = value; + } + private InnerTransactionResultResult result; + public InnerTransactionResultResult getResult() { + return this.result; + } + public void setResult(InnerTransactionResultResult value) { + this.result = value; + } + private InnerTransactionResultExt ext; + public InnerTransactionResultExt getExt() { + return this.ext; + } + public void setExt(InnerTransactionResultExt value) { + this.ext = value; + } + public static void encode(XdrDataOutputStream stream, InnerTransactionResult encodedInnerTransactionResult) throws IOException{ + Int64.encode(stream, encodedInnerTransactionResult.feeCharged); + InnerTransactionResultResult.encode(stream, encodedInnerTransactionResult.result); + InnerTransactionResultExt.encode(stream, encodedInnerTransactionResult.ext); + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static InnerTransactionResult decode(XdrDataInputStream stream) throws IOException { + InnerTransactionResult decodedInnerTransactionResult = new InnerTransactionResult(); + decodedInnerTransactionResult.feeCharged = Int64.decode(stream); + decodedInnerTransactionResult.result = InnerTransactionResultResult.decode(stream); + decodedInnerTransactionResult.ext = InnerTransactionResultExt.decode(stream); + return decodedInnerTransactionResult; + } + @Override + public int hashCode() { + return Objects.hashCode(this.feeCharged, this.result, this.ext); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof InnerTransactionResult)) { + return false; + } + + InnerTransactionResult other = (InnerTransactionResult) object; + return Objects.equal(this.feeCharged, other.feeCharged) && Objects.equal(this.result, other.result) && Objects.equal(this.ext, other.ext); + } + + public static class InnerTransactionResultResult { + public InnerTransactionResultResult () {} + TransactionResultCode code; + public TransactionResultCode getDiscriminant() { + return this.code; + } + public void setDiscriminant(TransactionResultCode value) { + this.code = value; + } + private OperationResult[] results; + public OperationResult[] getResults() { + return this.results; + } + public void setResults(OperationResult[] value) { + this.results = value; + } + public static void encode(XdrDataOutputStream stream, InnerTransactionResultResult encodedInnerTransactionResultResult) throws IOException { + //Xdrgen::AST::Identifier + //TransactionResultCode + stream.writeInt(encodedInnerTransactionResultResult.getDiscriminant().getValue()); + switch (encodedInnerTransactionResultResult.getDiscriminant()) { + case txSUCCESS: + case txFAILED: + int resultssize = encodedInnerTransactionResultResult.getResults().length; + stream.writeInt(resultssize); + for (int i = 0; i < resultssize; i++) { + OperationResult.encode(stream, encodedInnerTransactionResultResult.results[i]); + } + break; + case txTOO_EARLY: + case txTOO_LATE: + case txMISSING_OPERATION: + case txBAD_SEQ: + case txBAD_AUTH: + case txINSUFFICIENT_BALANCE: + case txNO_ACCOUNT: + case txINSUFFICIENT_FEE: + case txBAD_AUTH_EXTRA: + case txINTERNAL_ERROR: + case txNOT_SUPPORTED: + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static InnerTransactionResultResult decode(XdrDataInputStream stream) throws IOException { + InnerTransactionResultResult decodedInnerTransactionResultResult = new InnerTransactionResultResult(); + TransactionResultCode discriminant = TransactionResultCode.decode(stream); + decodedInnerTransactionResultResult.setDiscriminant(discriminant); + switch (decodedInnerTransactionResultResult.getDiscriminant()) { + case txSUCCESS: + case txFAILED: + int resultssize = stream.readInt(); + decodedInnerTransactionResultResult.results = new OperationResult[resultssize]; + for (int i = 0; i < resultssize; i++) { + decodedInnerTransactionResultResult.results[i] = OperationResult.decode(stream); + } + break; + case txTOO_EARLY: + case txTOO_LATE: + case txMISSING_OPERATION: + case txBAD_SEQ: + case txBAD_AUTH: + case txINSUFFICIENT_BALANCE: + case txNO_ACCOUNT: + case txINSUFFICIENT_FEE: + case txBAD_AUTH_EXTRA: + case txINTERNAL_ERROR: + case txNOT_SUPPORTED: + break; + } + return decodedInnerTransactionResultResult; + } + @Override + public int hashCode() { + return Objects.hashCode(Arrays.hashCode(this.results), this.code); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof InnerTransactionResultResult)) { + return false; + } + + InnerTransactionResultResult other = (InnerTransactionResultResult) object; + return Arrays.equals(this.results, other.results) && Objects.equal(this.code, other.code); + } + + } + public static class InnerTransactionResultExt { + public InnerTransactionResultExt () {} + Integer v; + public Integer getDiscriminant() { + return this.v; + } + public void setDiscriminant(Integer value) { + this.v = value; + } + public static void encode(XdrDataOutputStream stream, InnerTransactionResultExt encodedInnerTransactionResultExt) throws IOException { + //Xdrgen::AST::Typespecs::Int + //Integer + stream.writeInt(encodedInnerTransactionResultExt.getDiscriminant().intValue()); + switch (encodedInnerTransactionResultExt.getDiscriminant()) { + case 0: + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static InnerTransactionResultExt decode(XdrDataInputStream stream) throws IOException { + InnerTransactionResultExt decodedInnerTransactionResultExt = new InnerTransactionResultExt(); + Integer discriminant = stream.readInt(); + decodedInnerTransactionResultExt.setDiscriminant(discriminant); + switch (decodedInnerTransactionResultExt.getDiscriminant()) { + case 0: + break; + } + return decodedInnerTransactionResultExt; + } + @Override + public int hashCode() { + return Objects.hashCode(this.v); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof InnerTransactionResultExt)) { + return false; + } + + InnerTransactionResultExt other = (InnerTransactionResultExt) object; + return Objects.equal(this.v, other.v); + } + + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java new file mode 100644 index 000000000..d8edc64ef --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java @@ -0,0 +1,62 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; + +// === xdr source ============================================================ + +// struct InnerTransactionResultPair +// { +// Hash transactionHash; // hash of the inner transaction +// InnerTransactionResult result; // result for the inner transaction +// }; + +// =========================================================================== +public class InnerTransactionResultPair implements XdrElement { + public InnerTransactionResultPair () {} + private Hash transactionHash; + public Hash getTransactionHash() { + return this.transactionHash; + } + public void setTransactionHash(Hash value) { + this.transactionHash = value; + } + private InnerTransactionResult result; + public InnerTransactionResult getResult() { + return this.result; + } + public void setResult(InnerTransactionResult value) { + this.result = value; + } + public static void encode(XdrDataOutputStream stream, InnerTransactionResultPair encodedInnerTransactionResultPair) throws IOException{ + Hash.encode(stream, encodedInnerTransactionResultPair.transactionHash); + InnerTransactionResult.encode(stream, encodedInnerTransactionResultPair.result); + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static InnerTransactionResultPair decode(XdrDataInputStream stream) throws IOException { + InnerTransactionResultPair decodedInnerTransactionResultPair = new InnerTransactionResultPair(); + decodedInnerTransactionResultPair.transactionHash = Hash.decode(stream); + decodedInnerTransactionResultPair.result = InnerTransactionResult.decode(stream); + return decodedInnerTransactionResultPair; + } + @Override + public int hashCode() { + return Objects.hashCode(this.transactionHash, this.result); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof InnerTransactionResultPair)) { + return false; + } + + InnerTransactionResultPair other = (InnerTransactionResultPair) object; + return Objects.equal(this.transactionHash, other.transactionHash) && Objects.equal(this.result, other.result); + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java index 9aac2d562..991db5d71 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java @@ -13,7 +13,7 @@ // union LedgerCloseMeta switch (int v) // { // case 0: -// LedgerCloseMetaV0 v0; +// LedgerCloseMetaV0 v0; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java b/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java index 1e8c7a882..d96b0a4ee 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java @@ -20,14 +20,15 @@ // MANAGE_BUY_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying // MANAGE_BUY_OFFER_SELL_NOT_AUTHORIZED = -4, // not authorized to sell // MANAGE_BUY_OFFER_BUY_NOT_AUTHORIZED = -5, // not authorized to buy -// MANAGE_BUY_OFFER_LINE_FULL = -6, // can't receive more of what it's buying -// MANAGE_BUY_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell -// MANAGE_BUY_OFFER_CROSS_SELF = -8, // would cross an offer from the same user +// MANAGE_BUY_OFFER_LINE_FULL = -6, // can't receive more of what it's buying +// MANAGE_BUY_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell +// MANAGE_BUY_OFFER_CROSS_SELF = -8, // would cross an offer from the same user // MANAGE_BUY_OFFER_SELL_NO_ISSUER = -9, // no issuer for what we're selling // MANAGE_BUY_OFFER_BUY_NO_ISSUER = -10, // no issuer for what we're buying // // // update errors -// MANAGE_BUY_OFFER_NOT_FOUND = -11, // offerID does not match an existing offer +// MANAGE_BUY_OFFER_NOT_FOUND = +// -11, // offerID does not match an existing offer // // MANAGE_BUY_OFFER_LOW_RESERVE = -12 // not enough funds to create a new Offer // }; diff --git a/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java b/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java index c5d501b53..c7e990600 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java @@ -15,21 +15,25 @@ // MANAGE_SELL_OFFER_SUCCESS = 0, // // // codes considered as "failure" for the operation -// MANAGE_SELL_OFFER_MALFORMED = -1, // generated offer would be invalid -// MANAGE_SELL_OFFER_SELL_NO_TRUST = -2, // no trust line for what we're selling -// MANAGE_SELL_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying +// MANAGE_SELL_OFFER_MALFORMED = -1, // generated offer would be invalid +// MANAGE_SELL_OFFER_SELL_NO_TRUST = +// -2, // no trust line for what we're selling +// MANAGE_SELL_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying // MANAGE_SELL_OFFER_SELL_NOT_AUTHORIZED = -4, // not authorized to sell // MANAGE_SELL_OFFER_BUY_NOT_AUTHORIZED = -5, // not authorized to buy -// MANAGE_SELL_OFFER_LINE_FULL = -6, // can't receive more of what it's buying -// MANAGE_SELL_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell -// MANAGE_SELL_OFFER_CROSS_SELF = -8, // would cross an offer from the same user +// MANAGE_SELL_OFFER_LINE_FULL = -6, // can't receive more of what it's buying +// MANAGE_SELL_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell +// MANAGE_SELL_OFFER_CROSS_SELF = +// -8, // would cross an offer from the same user // MANAGE_SELL_OFFER_SELL_NO_ISSUER = -9, // no issuer for what we're selling // MANAGE_SELL_OFFER_BUY_NO_ISSUER = -10, // no issuer for what we're buying // // // update errors -// MANAGE_SELL_OFFER_NOT_FOUND = -11, // offerID does not match an existing offer +// MANAGE_SELL_OFFER_NOT_FOUND = +// -11, // offerID does not match an existing offer // -// MANAGE_SELL_OFFER_LOW_RESERVE = -12 // not enough funds to create a new Offer +// MANAGE_SELL_OFFER_LOW_RESERVE = +// -12 // not enough funds to create a new Offer // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/MuxedAccount.java b/src/main/java/org/stellar/sdk/xdr/MuxedAccount.java new file mode 100644 index 000000000..b44095e34 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/MuxedAccount.java @@ -0,0 +1,137 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; + +// === xdr source ============================================================ + +// union MuxedAccount switch (CryptoKeyType type) +// { +// case KEY_TYPE_ED25519: +// uint256 ed25519; +// case KEY_TYPE_MUXED_ED25519: +// struct +// { +// uint64 id; +// uint256 ed25519; +// } med25519; +// }; + +// =========================================================================== +public class MuxedAccount implements XdrElement { + public MuxedAccount () {} + CryptoKeyType type; + public CryptoKeyType getDiscriminant() { + return this.type; + } + public void setDiscriminant(CryptoKeyType value) { + this.type = value; + } + private Uint256 ed25519; + public Uint256 getEd25519() { + return this.ed25519; + } + public void setEd25519(Uint256 value) { + this.ed25519 = value; + } + private MuxedAccountMed25519 med25519; + public MuxedAccountMed25519 getMed25519() { + return this.med25519; + } + public void setMed25519(MuxedAccountMed25519 value) { + this.med25519 = value; + } + public static void encode(XdrDataOutputStream stream, MuxedAccount encodedMuxedAccount) throws IOException { + //Xdrgen::AST::Identifier + //CryptoKeyType + stream.writeInt(encodedMuxedAccount.getDiscriminant().getValue()); + switch (encodedMuxedAccount.getDiscriminant()) { + case KEY_TYPE_ED25519: + Uint256.encode(stream, encodedMuxedAccount.ed25519); + break; + case KEY_TYPE_MUXED_ED25519: + MuxedAccountMed25519.encode(stream, encodedMuxedAccount.med25519); + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static MuxedAccount decode(XdrDataInputStream stream) throws IOException { + MuxedAccount decodedMuxedAccount = new MuxedAccount(); + CryptoKeyType discriminant = CryptoKeyType.decode(stream); + decodedMuxedAccount.setDiscriminant(discriminant); + switch (decodedMuxedAccount.getDiscriminant()) { + case KEY_TYPE_ED25519: + decodedMuxedAccount.ed25519 = Uint256.decode(stream); + break; + case KEY_TYPE_MUXED_ED25519: + decodedMuxedAccount.med25519 = MuxedAccountMed25519.decode(stream); + break; + } + return decodedMuxedAccount; + } + @Override + public int hashCode() { + return Objects.hashCode(this.ed25519, this.med25519, this.type); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof MuxedAccount)) { + return false; + } + + MuxedAccount other = (MuxedAccount) object; + return Objects.equal(this.ed25519, other.ed25519) && Objects.equal(this.med25519, other.med25519) && Objects.equal(this.type, other.type); + } + + public static class MuxedAccountMed25519 { + public MuxedAccountMed25519 () {} + private Uint64 id; + public Uint64 getId() { + return this.id; + } + public void setId(Uint64 value) { + this.id = value; + } + private Uint256 ed25519; + public Uint256 getEd25519() { + return this.ed25519; + } + public void setEd25519(Uint256 value) { + this.ed25519 = value; + } + public static void encode(XdrDataOutputStream stream, MuxedAccountMed25519 encodedMuxedAccountMed25519) throws IOException{ + Uint64.encode(stream, encodedMuxedAccountMed25519.id); + Uint256.encode(stream, encodedMuxedAccountMed25519.ed25519); + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static MuxedAccountMed25519 decode(XdrDataInputStream stream) throws IOException { + MuxedAccountMed25519 decodedMuxedAccountMed25519 = new MuxedAccountMed25519(); + decodedMuxedAccountMed25519.id = Uint64.decode(stream); + decodedMuxedAccountMed25519.ed25519 = Uint256.decode(stream); + return decodedMuxedAccountMed25519; + } + @Override + public int hashCode() { + return Objects.hashCode(this.id, this.ed25519); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof MuxedAccountMed25519)) { + return false; + } + + MuxedAccountMed25519 other = (MuxedAccountMed25519) object; + return Objects.equal(this.id, other.id) && Objects.equal(this.ed25519, other.ed25519); + } + + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/Operation.java b/src/main/java/org/stellar/sdk/xdr/Operation.java index 7dd4f5681..016280f5b 100644 --- a/src/main/java/org/stellar/sdk/xdr/Operation.java +++ b/src/main/java/org/stellar/sdk/xdr/Operation.java @@ -15,7 +15,7 @@ // // sourceAccount is the account used to run the operation // // if not set, the runtime defaults to "sourceAccount" specified at // // the transaction level -// AccountID* sourceAccount; +// MuxedAccount* sourceAccount; // // union switch (OperationType type) // { @@ -36,7 +36,7 @@ // case ALLOW_TRUST: // AllowTrustOp allowTrustOp; // case ACCOUNT_MERGE: -// AccountID destination; +// MuxedAccount destination; // case INFLATION: // void; // case MANAGE_DATA: @@ -54,11 +54,11 @@ // =========================================================================== public class Operation implements XdrElement { public Operation () {} - private AccountID sourceAccount; - public AccountID getSourceAccount() { + private MuxedAccount sourceAccount; + public MuxedAccount getSourceAccount() { return this.sourceAccount; } - public void setSourceAccount(AccountID value) { + public void setSourceAccount(MuxedAccount value) { this.sourceAccount = value; } private OperationBody body; @@ -71,7 +71,7 @@ public void setBody(OperationBody value) { public static void encode(XdrDataOutputStream stream, Operation encodedOperation) throws IOException{ if (encodedOperation.sourceAccount != null) { stream.writeInt(1); - AccountID.encode(stream, encodedOperation.sourceAccount); + MuxedAccount.encode(stream, encodedOperation.sourceAccount); } else { stream.writeInt(0); } @@ -84,7 +84,7 @@ public static Operation decode(XdrDataInputStream stream) throws IOException { Operation decodedOperation = new Operation(); int sourceAccountPresent = stream.readInt(); if (sourceAccountPresent != 0) { - decodedOperation.sourceAccount = AccountID.decode(stream); + decodedOperation.sourceAccount = MuxedAccount.decode(stream); } decodedOperation.body = OperationBody.decode(stream); return decodedOperation; @@ -168,11 +168,11 @@ public AllowTrustOp getAllowTrustOp() { public void setAllowTrustOp(AllowTrustOp value) { this.allowTrustOp = value; } - private AccountID destination; - public AccountID getDestination() { + private MuxedAccount destination; + public MuxedAccount getDestination() { return this.destination; } - public void setDestination(AccountID value) { + public void setDestination(MuxedAccount value) { this.destination = value; } private ManageDataOp manageDataOp; @@ -233,7 +233,7 @@ public static void encode(XdrDataOutputStream stream, OperationBody encodedOpera AllowTrustOp.encode(stream, encodedOperationBody.allowTrustOp); break; case ACCOUNT_MERGE: - AccountID.encode(stream, encodedOperationBody.destination); + MuxedAccount.encode(stream, encodedOperationBody.destination); break; case INFLATION: break; @@ -284,7 +284,7 @@ public static OperationBody decode(XdrDataInputStream stream) throws IOException decodedOperationBody.allowTrustOp = AllowTrustOp.decode(stream); break; case ACCOUNT_MERGE: - decodedOperationBody.destination = AccountID.decode(stream); + decodedOperationBody.destination = MuxedAccount.decode(stream); break; case INFLATION: break; diff --git a/src/main/java/org/stellar/sdk/xdr/OperationResult.java b/src/main/java/org/stellar/sdk/xdr/OperationResult.java index b7c5e6133..392aefdcf 100644 --- a/src/main/java/org/stellar/sdk/xdr/OperationResult.java +++ b/src/main/java/org/stellar/sdk/xdr/OperationResult.java @@ -40,7 +40,7 @@ // case BUMP_SEQUENCE: // BumpSequenceResult bumpSeqResult; // case MANAGE_BUY_OFFER: -// ManageBuyOfferResult manageBuyOfferResult; +// ManageBuyOfferResult manageBuyOfferResult; // case PATH_PAYMENT_STRICT_SEND: // PathPaymentStrictSendResult pathPaymentStrictSendResult; // } diff --git a/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java b/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java index f432d7fcd..91185fe7a 100644 --- a/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java @@ -13,9 +13,9 @@ // { // opINNER = 0, // inner object result is valid // -// opBAD_AUTH = -1, // too few valid signatures / wrong network -// opNO_ACCOUNT = -2, // source account was not found -// opNOT_SUPPORTED = -3, // operation not supported at this time +// opBAD_AUTH = -1, // too few valid signatures / wrong network +// opNO_ACCOUNT = -2, // source account was not found +// opNOT_SUPPORTED = -3, // operation not supported at this time // opTOO_MANY_SUBENTRIES = -4, // max number of subentries already reached // opEXCEEDED_WORK_LIMIT = -5 // operation did too much work // }; diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java index 07cae0689..14e543105 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java @@ -18,9 +18,9 @@ // // send (excluding fees). // // The operation will fail if can't be met // -// AccountID destination; // recipient of the payment -// Asset destAsset; // what they end up with -// int64 destAmount; // amount they end up with +// MuxedAccount destination; // recipient of the payment +// Asset destAsset; // what they end up with +// int64 destAmount; // amount they end up with // // Asset path<5>; // additional hops it must go through to get there // }; @@ -42,11 +42,11 @@ public Int64 getSendMax() { public void setSendMax(Int64 value) { this.sendMax = value; } - private AccountID destination; - public AccountID getDestination() { + private MuxedAccount destination; + public MuxedAccount getDestination() { return this.destination; } - public void setDestination(AccountID value) { + public void setDestination(MuxedAccount value) { this.destination = value; } private Asset destAsset; @@ -73,7 +73,7 @@ public void setPath(Asset[] value) { public static void encode(XdrDataOutputStream stream, PathPaymentStrictReceiveOp encodedPathPaymentStrictReceiveOp) throws IOException{ Asset.encode(stream, encodedPathPaymentStrictReceiveOp.sendAsset); Int64.encode(stream, encodedPathPaymentStrictReceiveOp.sendMax); - AccountID.encode(stream, encodedPathPaymentStrictReceiveOp.destination); + MuxedAccount.encode(stream, encodedPathPaymentStrictReceiveOp.destination); Asset.encode(stream, encodedPathPaymentStrictReceiveOp.destAsset); Int64.encode(stream, encodedPathPaymentStrictReceiveOp.destAmount); int pathsize = encodedPathPaymentStrictReceiveOp.getPath().length; @@ -89,7 +89,7 @@ public static PathPaymentStrictReceiveOp decode(XdrDataInputStream stream) throw PathPaymentStrictReceiveOp decodedPathPaymentStrictReceiveOp = new PathPaymentStrictReceiveOp(); decodedPathPaymentStrictReceiveOp.sendAsset = Asset.decode(stream); decodedPathPaymentStrictReceiveOp.sendMax = Int64.decode(stream); - decodedPathPaymentStrictReceiveOp.destination = AccountID.decode(stream); + decodedPathPaymentStrictReceiveOp.destination = MuxedAccount.decode(stream); decodedPathPaymentStrictReceiveOp.destAsset = Asset.decode(stream); decodedPathPaymentStrictReceiveOp.destAmount = Int64.decode(stream); int pathsize = stream.readInt(); diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java index 6053e2f88..d05eb229d 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java @@ -15,18 +15,27 @@ // PATH_PAYMENT_STRICT_RECEIVE_SUCCESS = 0, // success // // // codes considered as "failure" for the operation -// PATH_PAYMENT_STRICT_RECEIVE_MALFORMED = -1, // bad input -// PATH_PAYMENT_STRICT_RECEIVE_UNDERFUNDED = -2, // not enough funds in source account -// PATH_PAYMENT_STRICT_RECEIVE_SRC_NO_TRUST = -3, // no trust line on source account -// PATH_PAYMENT_STRICT_RECEIVE_SRC_NOT_AUTHORIZED = -4, // source not authorized to transfer -// PATH_PAYMENT_STRICT_RECEIVE_NO_DESTINATION = -5, // destination account does not exist -// PATH_PAYMENT_STRICT_RECEIVE_NO_TRUST = -6, // dest missing a trust line for asset -// PATH_PAYMENT_STRICT_RECEIVE_NOT_AUTHORIZED = -7, // dest not authorized to hold asset -// PATH_PAYMENT_STRICT_RECEIVE_LINE_FULL = -8, // dest would go above their limit -// PATH_PAYMENT_STRICT_RECEIVE_NO_ISSUER = -9, // missing issuer on one asset -// PATH_PAYMENT_STRICT_RECEIVE_TOO_FEW_OFFERS = -10, // not enough offers to satisfy path -// PATH_PAYMENT_STRICT_RECEIVE_OFFER_CROSS_SELF = -11, // would cross one of its own offers -// PATH_PAYMENT_STRICT_RECEIVE_OVER_SENDMAX = -12 // could not satisfy sendmax +// PATH_PAYMENT_STRICT_RECEIVE_MALFORMED = -1, // bad input +// PATH_PAYMENT_STRICT_RECEIVE_UNDERFUNDED = +// -2, // not enough funds in source account +// PATH_PAYMENT_STRICT_RECEIVE_SRC_NO_TRUST = +// -3, // no trust line on source account +// PATH_PAYMENT_STRICT_RECEIVE_SRC_NOT_AUTHORIZED = +// -4, // source not authorized to transfer +// PATH_PAYMENT_STRICT_RECEIVE_NO_DESTINATION = +// -5, // destination account does not exist +// PATH_PAYMENT_STRICT_RECEIVE_NO_TRUST = +// -6, // dest missing a trust line for asset +// PATH_PAYMENT_STRICT_RECEIVE_NOT_AUTHORIZED = +// -7, // dest not authorized to hold asset +// PATH_PAYMENT_STRICT_RECEIVE_LINE_FULL = +// -8, // dest would go above their limit +// PATH_PAYMENT_STRICT_RECEIVE_NO_ISSUER = -9, // missing issuer on one asset +// PATH_PAYMENT_STRICT_RECEIVE_TOO_FEW_OFFERS = +// -10, // not enough offers to satisfy path +// PATH_PAYMENT_STRICT_RECEIVE_OFFER_CROSS_SELF = +// -11, // would cross one of its own offers +// PATH_PAYMENT_STRICT_RECEIVE_OVER_SENDMAX = -12 // could not satisfy sendmax // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java index 8380c756c..242f0cec2 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java @@ -16,11 +16,11 @@ // Asset sendAsset; // asset we pay with // int64 sendAmount; // amount of sendAsset to send (excluding fees) // -// AccountID destination; // recipient of the payment -// Asset destAsset; // what they end up with -// int64 destMin; // the minimum amount of dest asset to -// // be received -// // The operation will fail if it can't be met +// MuxedAccount destination; // recipient of the payment +// Asset destAsset; // what they end up with +// int64 destMin; // the minimum amount of dest asset to +// // be received +// // The operation will fail if it can't be met // // Asset path<5>; // additional hops it must go through to get there // }; @@ -42,11 +42,11 @@ public Int64 getSendAmount() { public void setSendAmount(Int64 value) { this.sendAmount = value; } - private AccountID destination; - public AccountID getDestination() { + private MuxedAccount destination; + public MuxedAccount getDestination() { return this.destination; } - public void setDestination(AccountID value) { + public void setDestination(MuxedAccount value) { this.destination = value; } private Asset destAsset; @@ -73,7 +73,7 @@ public void setPath(Asset[] value) { public static void encode(XdrDataOutputStream stream, PathPaymentStrictSendOp encodedPathPaymentStrictSendOp) throws IOException{ Asset.encode(stream, encodedPathPaymentStrictSendOp.sendAsset); Int64.encode(stream, encodedPathPaymentStrictSendOp.sendAmount); - AccountID.encode(stream, encodedPathPaymentStrictSendOp.destination); + MuxedAccount.encode(stream, encodedPathPaymentStrictSendOp.destination); Asset.encode(stream, encodedPathPaymentStrictSendOp.destAsset); Int64.encode(stream, encodedPathPaymentStrictSendOp.destMin); int pathsize = encodedPathPaymentStrictSendOp.getPath().length; @@ -89,7 +89,7 @@ public static PathPaymentStrictSendOp decode(XdrDataInputStream stream) throws I PathPaymentStrictSendOp decodedPathPaymentStrictSendOp = new PathPaymentStrictSendOp(); decodedPathPaymentStrictSendOp.sendAsset = Asset.decode(stream); decodedPathPaymentStrictSendOp.sendAmount = Int64.decode(stream); - decodedPathPaymentStrictSendOp.destination = AccountID.decode(stream); + decodedPathPaymentStrictSendOp.destination = MuxedAccount.decode(stream); decodedPathPaymentStrictSendOp.destAsset = Asset.decode(stream); decodedPathPaymentStrictSendOp.destMin = Int64.decode(stream); int pathsize = stream.readInt(); diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java index e95ca29f2..375941336 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java @@ -15,18 +15,26 @@ // PATH_PAYMENT_STRICT_SEND_SUCCESS = 0, // success // // // codes considered as "failure" for the operation -// PATH_PAYMENT_STRICT_SEND_MALFORMED = -1, // bad input -// PATH_PAYMENT_STRICT_SEND_UNDERFUNDED = -2, // not enough funds in source account -// PATH_PAYMENT_STRICT_SEND_SRC_NO_TRUST = -3, // no trust line on source account -// PATH_PAYMENT_STRICT_SEND_SRC_NOT_AUTHORIZED = -4, // source not authorized to transfer -// PATH_PAYMENT_STRICT_SEND_NO_DESTINATION = -5, // destination account does not exist -// PATH_PAYMENT_STRICT_SEND_NO_TRUST = -6, // dest missing a trust line for asset -// PATH_PAYMENT_STRICT_SEND_NOT_AUTHORIZED = -7, // dest not authorized to hold asset -// PATH_PAYMENT_STRICT_SEND_LINE_FULL = -8, // dest would go above their limit -// PATH_PAYMENT_STRICT_SEND_NO_ISSUER = -9, // missing issuer on one asset -// PATH_PAYMENT_STRICT_SEND_TOO_FEW_OFFERS = -10, // not enough offers to satisfy path -// PATH_PAYMENT_STRICT_SEND_OFFER_CROSS_SELF = -11, // would cross one of its own offers -// PATH_PAYMENT_STRICT_SEND_UNDER_DESTMIN = -12 // could not satisfy destMin +// PATH_PAYMENT_STRICT_SEND_MALFORMED = -1, // bad input +// PATH_PAYMENT_STRICT_SEND_UNDERFUNDED = +// -2, // not enough funds in source account +// PATH_PAYMENT_STRICT_SEND_SRC_NO_TRUST = +// -3, // no trust line on source account +// PATH_PAYMENT_STRICT_SEND_SRC_NOT_AUTHORIZED = +// -4, // source not authorized to transfer +// PATH_PAYMENT_STRICT_SEND_NO_DESTINATION = +// -5, // destination account does not exist +// PATH_PAYMENT_STRICT_SEND_NO_TRUST = +// -6, // dest missing a trust line for asset +// PATH_PAYMENT_STRICT_SEND_NOT_AUTHORIZED = +// -7, // dest not authorized to hold asset +// PATH_PAYMENT_STRICT_SEND_LINE_FULL = -8, // dest would go above their limit +// PATH_PAYMENT_STRICT_SEND_NO_ISSUER = -9, // missing issuer on one asset +// PATH_PAYMENT_STRICT_SEND_TOO_FEW_OFFERS = +// -10, // not enough offers to satisfy path +// PATH_PAYMENT_STRICT_SEND_OFFER_CROSS_SELF = +// -11, // would cross one of its own offers +// PATH_PAYMENT_STRICT_SEND_UNDER_DESTMIN = -12 // could not satisfy destMin // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/PaymentOp.java b/src/main/java/org/stellar/sdk/xdr/PaymentOp.java index 101058a50..940b8adb2 100644 --- a/src/main/java/org/stellar/sdk/xdr/PaymentOp.java +++ b/src/main/java/org/stellar/sdk/xdr/PaymentOp.java @@ -12,19 +12,19 @@ // struct PaymentOp // { -// AccountID destination; // recipient of the payment -// Asset asset; // what they end up with -// int64 amount; // amount they end up with +// MuxedAccount destination; // recipient of the payment +// Asset asset; // what they end up with +// int64 amount; // amount they end up with // }; // =========================================================================== public class PaymentOp implements XdrElement { public PaymentOp () {} - private AccountID destination; - public AccountID getDestination() { + private MuxedAccount destination; + public MuxedAccount getDestination() { return this.destination; } - public void setDestination(AccountID value) { + public void setDestination(MuxedAccount value) { this.destination = value; } private Asset asset; @@ -42,7 +42,7 @@ public void setAmount(Int64 value) { this.amount = value; } public static void encode(XdrDataOutputStream stream, PaymentOp encodedPaymentOp) throws IOException{ - AccountID.encode(stream, encodedPaymentOp.destination); + MuxedAccount.encode(stream, encodedPaymentOp.destination); Asset.encode(stream, encodedPaymentOp.asset); Int64.encode(stream, encodedPaymentOp.amount); } @@ -51,7 +51,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } public static PaymentOp decode(XdrDataInputStream stream) throws IOException { PaymentOp decodedPaymentOp = new PaymentOp(); - decodedPaymentOp.destination = AccountID.decode(stream); + decodedPaymentOp.destination = MuxedAccount.decode(stream); decodedPaymentOp.asset = Asset.decode(stream); decodedPaymentOp.amount = Int64.decode(stream); return decodedPaymentOp; diff --git a/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java b/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java index 25e5556b2..852308b82 100644 --- a/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java +++ b/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java @@ -12,8 +12,8 @@ // union SurveyResponseBody switch (SurveyMessageCommandType type) // { -// case SURVEY_TOPOLOGY: -// TopologyResponseBody topologyResponseBody; +// case SURVEY_TOPOLOGY: +// TopologyResponseBody topologyResponseBody; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/Transaction.java b/src/main/java/org/stellar/sdk/xdr/Transaction.java index cc72a3392..f9f9c435a 100644 --- a/src/main/java/org/stellar/sdk/xdr/Transaction.java +++ b/src/main/java/org/stellar/sdk/xdr/Transaction.java @@ -14,7 +14,7 @@ // struct Transaction // { // // account used to run the transaction -// AccountID sourceAccount; +// MuxedAccount sourceAccount; // // // the fee the sourceAccount will pay // uint32 fee; @@ -41,11 +41,11 @@ // =========================================================================== public class Transaction implements XdrElement { public Transaction () {} - private AccountID sourceAccount; - public AccountID getSourceAccount() { + private MuxedAccount sourceAccount; + public MuxedAccount getSourceAccount() { return this.sourceAccount; } - public void setSourceAccount(AccountID value) { + public void setSourceAccount(MuxedAccount value) { this.sourceAccount = value; } private Uint32 fee; @@ -91,7 +91,7 @@ public void setExt(TransactionExt value) { this.ext = value; } public static void encode(XdrDataOutputStream stream, Transaction encodedTransaction) throws IOException{ - AccountID.encode(stream, encodedTransaction.sourceAccount); + MuxedAccount.encode(stream, encodedTransaction.sourceAccount); Uint32.encode(stream, encodedTransaction.fee); SequenceNumber.encode(stream, encodedTransaction.seqNum); if (encodedTransaction.timeBounds != null) { @@ -113,7 +113,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } public static Transaction decode(XdrDataInputStream stream) throws IOException { Transaction decodedTransaction = new Transaction(); - decodedTransaction.sourceAccount = AccountID.decode(stream); + decodedTransaction.sourceAccount = MuxedAccount.decode(stream); decodedTransaction.fee = Uint32.decode(stream); decodedTransaction.seqNum = SequenceNumber.decode(stream); int timeBoundsPresent = stream.readInt(); diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java b/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java index ff55d0a09..4d966c830 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java @@ -7,59 +7,89 @@ import java.io.IOException; import com.google.common.base.Objects; -import java.util.Arrays; // === xdr source ============================================================ -// struct TransactionEnvelope +// union TransactionEnvelope switch (EnvelopeType type) // { -// Transaction tx; -// /* Each decorated signature is a signature over the SHA256 hash of -// * a TransactionSignaturePayload */ -// DecoratedSignature signatures<20>; +// case ENVELOPE_TYPE_TX_V0: +// TransactionV0Envelope v0; +// case ENVELOPE_TYPE_TX: +// TransactionV1Envelope v1; +// case ENVELOPE_TYPE_TX_FEE_BUMP: +// FeeBumpTransactionEnvelope feeBump; // }; // =========================================================================== public class TransactionEnvelope implements XdrElement { public TransactionEnvelope () {} - private Transaction tx; - public Transaction getTx() { - return this.tx; + EnvelopeType type; + public EnvelopeType getDiscriminant() { + return this.type; } - public void setTx(Transaction value) { - this.tx = value; + public void setDiscriminant(EnvelopeType value) { + this.type = value; } - private DecoratedSignature[] signatures; - public DecoratedSignature[] getSignatures() { - return this.signatures; + private TransactionV0Envelope v0; + public TransactionV0Envelope getV0() { + return this.v0; } - public void setSignatures(DecoratedSignature[] value) { - this.signatures = value; + public void setV0(TransactionV0Envelope value) { + this.v0 = value; + } + private TransactionV1Envelope v1; + public TransactionV1Envelope getV1() { + return this.v1; + } + public void setV1(TransactionV1Envelope value) { + this.v1 = value; + } + private FeeBumpTransactionEnvelope feeBump; + public FeeBumpTransactionEnvelope getFeeBump() { + return this.feeBump; + } + public void setFeeBump(FeeBumpTransactionEnvelope value) { + this.feeBump = value; + } + public static void encode(XdrDataOutputStream stream, TransactionEnvelope encodedTransactionEnvelope) throws IOException { + //Xdrgen::AST::Identifier + //EnvelopeType + stream.writeInt(encodedTransactionEnvelope.getDiscriminant().getValue()); + switch (encodedTransactionEnvelope.getDiscriminant()) { + case ENVELOPE_TYPE_TX_V0: + TransactionV0Envelope.encode(stream, encodedTransactionEnvelope.v0); + break; + case ENVELOPE_TYPE_TX: + TransactionV1Envelope.encode(stream, encodedTransactionEnvelope.v1); + break; + case ENVELOPE_TYPE_TX_FEE_BUMP: + FeeBumpTransactionEnvelope.encode(stream, encodedTransactionEnvelope.feeBump); + break; } - public static void encode(XdrDataOutputStream stream, TransactionEnvelope encodedTransactionEnvelope) throws IOException{ - Transaction.encode(stream, encodedTransactionEnvelope.tx); - int signaturessize = encodedTransactionEnvelope.getSignatures().length; - stream.writeInt(signaturessize); - for (int i = 0; i < signaturessize; i++) { - DecoratedSignature.encode(stream, encodedTransactionEnvelope.signatures[i]); - } } public void encode(XdrDataOutputStream stream) throws IOException { encode(stream, this); } public static TransactionEnvelope decode(XdrDataInputStream stream) throws IOException { - TransactionEnvelope decodedTransactionEnvelope = new TransactionEnvelope(); - decodedTransactionEnvelope.tx = Transaction.decode(stream); - int signaturessize = stream.readInt(); - decodedTransactionEnvelope.signatures = new DecoratedSignature[signaturessize]; - for (int i = 0; i < signaturessize; i++) { - decodedTransactionEnvelope.signatures[i] = DecoratedSignature.decode(stream); - } + TransactionEnvelope decodedTransactionEnvelope = new TransactionEnvelope(); + EnvelopeType discriminant = EnvelopeType.decode(stream); + decodedTransactionEnvelope.setDiscriminant(discriminant); + switch (decodedTransactionEnvelope.getDiscriminant()) { + case ENVELOPE_TYPE_TX_V0: + decodedTransactionEnvelope.v0 = TransactionV0Envelope.decode(stream); + break; + case ENVELOPE_TYPE_TX: + decodedTransactionEnvelope.v1 = TransactionV1Envelope.decode(stream); + break; + case ENVELOPE_TYPE_TX_FEE_BUMP: + decodedTransactionEnvelope.feeBump = FeeBumpTransactionEnvelope.decode(stream); + break; + } return decodedTransactionEnvelope; } @Override public int hashCode() { - return Objects.hashCode(this.tx, Arrays.hashCode(this.signatures)); + return Objects.hashCode(this.v0, this.v1, this.feeBump, this.type); } @Override public boolean equals(Object object) { @@ -68,6 +98,6 @@ public boolean equals(Object object) { } TransactionEnvelope other = (TransactionEnvelope) object; - return Objects.equal(this.tx, other.tx) && Arrays.equals(this.signatures, other.signatures); + return Objects.equal(this.v0, other.v0) && Objects.equal(this.v1, other.v1) && Objects.equal(this.feeBump, other.feeBump) && Objects.equal(this.type, other.type); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionResult.java b/src/main/java/org/stellar/sdk/xdr/TransactionResult.java index 7ce372e11..f0c210268 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionResult.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionResult.java @@ -17,6 +17,9 @@ // // union switch (TransactionResultCode code) // { +// case txFEE_BUMP_INNER_SUCCESS: +// case txFEE_BUMP_INNER_FAILED: +// InnerTransactionResultPair innerResultPair; // case txSUCCESS: // case txFAILED: // OperationResult results<>; @@ -96,6 +99,13 @@ public TransactionResultCode getDiscriminant() { public void setDiscriminant(TransactionResultCode value) { this.code = value; } + private InnerTransactionResultPair innerResultPair; + public InnerTransactionResultPair getInnerResultPair() { + return this.innerResultPair; + } + public void setInnerResultPair(InnerTransactionResultPair value) { + this.innerResultPair = value; + } private OperationResult[] results; public OperationResult[] getResults() { return this.results; @@ -108,6 +118,10 @@ public static void encode(XdrDataOutputStream stream, TransactionResultResult en //TransactionResultCode stream.writeInt(encodedTransactionResultResult.getDiscriminant().getValue()); switch (encodedTransactionResultResult.getDiscriminant()) { + case txFEE_BUMP_INNER_SUCCESS: + case txFEE_BUMP_INNER_FAILED: + InnerTransactionResultPair.encode(stream, encodedTransactionResultResult.innerResultPair); + break; case txSUCCESS: case txFAILED: int resultssize = encodedTransactionResultResult.getResults().length; @@ -128,6 +142,10 @@ public static TransactionResultResult decode(XdrDataInputStream stream) throws I TransactionResultCode discriminant = TransactionResultCode.decode(stream); decodedTransactionResultResult.setDiscriminant(discriminant); switch (decodedTransactionResultResult.getDiscriminant()) { + case txFEE_BUMP_INNER_SUCCESS: + case txFEE_BUMP_INNER_FAILED: + decodedTransactionResultResult.innerResultPair = InnerTransactionResultPair.decode(stream); + break; case txSUCCESS: case txFAILED: int resultssize = stream.readInt(); @@ -143,7 +161,7 @@ public static TransactionResultResult decode(XdrDataInputStream stream) throws I } @Override public int hashCode() { - return Objects.hashCode(Arrays.hashCode(this.results), this.code); + return Objects.hashCode(this.innerResultPair, Arrays.hashCode(this.results), this.code); } @Override public boolean equals(Object object) { @@ -152,7 +170,7 @@ public boolean equals(Object object) { } TransactionResultResult other = (TransactionResultResult) object; - return Arrays.equals(this.results, other.results) && Objects.equal(this.code, other.code); + return Objects.equal(this.innerResultPair, other.innerResultPair) && Arrays.equals(this.results, other.results) && Objects.equal(this.code, other.code); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java b/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java index 914ca9ff9..ab6763de8 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java @@ -11,7 +11,8 @@ // enum TransactionResultCode // { -// txSUCCESS = 0, // all operations succeeded +// txFEE_BUMP_INNER_SUCCESS = 1, // fee bump inner transaction succeeded +// txSUCCESS = 0, // all operations succeeded // // txFAILED = -1, // one of the operations failed (none were applied) // @@ -25,11 +26,15 @@ // txNO_ACCOUNT = -8, // source account not found // txINSUFFICIENT_FEE = -9, // fee is too small // txBAD_AUTH_EXTRA = -10, // unused signatures attached to transaction -// txINTERNAL_ERROR = -11 // an unknown error occured +// txINTERNAL_ERROR = -11, // an unknown error occured +// +// txNOT_SUPPORTED = -12, // transaction type not supported +// txFEE_BUMP_INNER_FAILED = -13 // fee bump inner transaction failed // }; // =========================================================================== public enum TransactionResultCode implements XdrElement { + txFEE_BUMP_INNER_SUCCESS(1), txSUCCESS(0), txFAILED(-1), txTOO_EARLY(-2), @@ -42,6 +47,8 @@ public enum TransactionResultCode implements XdrElement { txINSUFFICIENT_FEE(-9), txBAD_AUTH_EXTRA(-10), txINTERNAL_ERROR(-11), + txNOT_SUPPORTED(-12), + txFEE_BUMP_INNER_FAILED(-13), ; private int mValue; @@ -56,6 +63,7 @@ public int getValue() { public static TransactionResultCode decode(XdrDataInputStream stream) throws IOException { int value = stream.readInt(); switch (value) { + case 1: return txFEE_BUMP_INNER_SUCCESS; case 0: return txSUCCESS; case -1: return txFAILED; case -2: return txTOO_EARLY; @@ -68,6 +76,8 @@ public static TransactionResultCode decode(XdrDataInputStream stream) throws IOE case -9: return txINSUFFICIENT_FEE; case -10: return txBAD_AUTH_EXTRA; case -11: return txINTERNAL_ERROR; + case -12: return txNOT_SUPPORTED; + case -13: return txFEE_BUMP_INNER_FAILED; default: throw new RuntimeException("Unknown enum value: " + value); } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java b/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java index d549cca34..916faee0c 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java @@ -15,9 +15,11 @@ // Hash networkId; // union switch (EnvelopeType type) // { +// // Backwards Compatibility: Use ENVELOPE_TYPE_TX to sign ENVELOPE_TYPE_TX_V0 // case ENVELOPE_TYPE_TX: // Transaction tx; -// /* All other values of type are invalid */ +// case ENVELOPE_TYPE_TX_FEE_BUMP: +// FeeBumpTransaction feeBump; // } // taggedTransaction; // }; @@ -82,6 +84,13 @@ public Transaction getTx() { public void setTx(Transaction value) { this.tx = value; } + private FeeBumpTransaction feeBump; + public FeeBumpTransaction getFeeBump() { + return this.feeBump; + } + public void setFeeBump(FeeBumpTransaction value) { + this.feeBump = value; + } public static void encode(XdrDataOutputStream stream, TransactionSignaturePayloadTaggedTransaction encodedTransactionSignaturePayloadTaggedTransaction) throws IOException { //Xdrgen::AST::Identifier //EnvelopeType @@ -90,6 +99,9 @@ public static void encode(XdrDataOutputStream stream, TransactionSignaturePayloa case ENVELOPE_TYPE_TX: Transaction.encode(stream, encodedTransactionSignaturePayloadTaggedTransaction.tx); break; + case ENVELOPE_TYPE_TX_FEE_BUMP: + FeeBumpTransaction.encode(stream, encodedTransactionSignaturePayloadTaggedTransaction.feeBump); + break; } } public void encode(XdrDataOutputStream stream) throws IOException { @@ -103,12 +115,15 @@ public static TransactionSignaturePayloadTaggedTransaction decode(XdrDataInputSt case ENVELOPE_TYPE_TX: decodedTransactionSignaturePayloadTaggedTransaction.tx = Transaction.decode(stream); break; + case ENVELOPE_TYPE_TX_FEE_BUMP: + decodedTransactionSignaturePayloadTaggedTransaction.feeBump = FeeBumpTransaction.decode(stream); + break; } return decodedTransactionSignaturePayloadTaggedTransaction; } @Override public int hashCode() { - return Objects.hashCode(this.tx, this.type); + return Objects.hashCode(this.tx, this.feeBump, this.type); } @Override public boolean equals(Object object) { @@ -117,7 +132,7 @@ public boolean equals(Object object) { } TransactionSignaturePayloadTaggedTransaction other = (TransactionSignaturePayloadTaggedTransaction) object; - return Objects.equal(this.tx, other.tx) && Objects.equal(this.type, other.type); + return Objects.equal(this.tx, other.tx) && Objects.equal(this.feeBump, other.feeBump) && Objects.equal(this.type, other.type); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionV0.java b/src/main/java/org/stellar/sdk/xdr/TransactionV0.java new file mode 100644 index 000000000..265650718 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/TransactionV0.java @@ -0,0 +1,181 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; +import java.util.Arrays; + +// === xdr source ============================================================ + +// struct TransactionV0 +// { +// uint256 sourceAccountEd25519; +// uint32 fee; +// SequenceNumber seqNum; +// TimeBounds* timeBounds; +// Memo memo; +// Operation operations; +// union switch (int v) +// { +// case 0: +// void; +// } +// ext; +// }; + +// =========================================================================== +public class TransactionV0 implements XdrElement { + public TransactionV0 () {} + private Uint256 sourceAccountEd25519; + public Uint256 getSourceAccountEd25519() { + return this.sourceAccountEd25519; + } + public void setSourceAccountEd25519(Uint256 value) { + this.sourceAccountEd25519 = value; + } + private Uint32 fee; + public Uint32 getFee() { + return this.fee; + } + public void setFee(Uint32 value) { + this.fee = value; + } + private SequenceNumber seqNum; + public SequenceNumber getSeqNum() { + return this.seqNum; + } + public void setSeqNum(SequenceNumber value) { + this.seqNum = value; + } + private TimeBounds timeBounds; + public TimeBounds getTimeBounds() { + return this.timeBounds; + } + public void setTimeBounds(TimeBounds value) { + this.timeBounds = value; + } + private Memo memo; + public Memo getMemo() { + return this.memo; + } + public void setMemo(Memo value) { + this.memo = value; + } + private Operation[] operations; + public Operation[] getOperations() { + return this.operations; + } + public void setOperations(Operation[] value) { + this.operations = value; + } + private TransactionV0Ext ext; + public TransactionV0Ext getExt() { + return this.ext; + } + public void setExt(TransactionV0Ext value) { + this.ext = value; + } + public static void encode(XdrDataOutputStream stream, TransactionV0 encodedTransactionV0) throws IOException{ + Uint256.encode(stream, encodedTransactionV0.sourceAccountEd25519); + Uint32.encode(stream, encodedTransactionV0.fee); + SequenceNumber.encode(stream, encodedTransactionV0.seqNum); + if (encodedTransactionV0.timeBounds != null) { + stream.writeInt(1); + TimeBounds.encode(stream, encodedTransactionV0.timeBounds); + } else { + stream.writeInt(0); + } + Memo.encode(stream, encodedTransactionV0.memo); + int operationssize = encodedTransactionV0.getOperations().length; + stream.writeInt(operationssize); + for (int i = 0; i < operationssize; i++) { + Operation.encode(stream, encodedTransactionV0.operations[i]); + } + TransactionV0Ext.encode(stream, encodedTransactionV0.ext); + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static TransactionV0 decode(XdrDataInputStream stream) throws IOException { + TransactionV0 decodedTransactionV0 = new TransactionV0(); + decodedTransactionV0.sourceAccountEd25519 = Uint256.decode(stream); + decodedTransactionV0.fee = Uint32.decode(stream); + decodedTransactionV0.seqNum = SequenceNumber.decode(stream); + int timeBoundsPresent = stream.readInt(); + if (timeBoundsPresent != 0) { + decodedTransactionV0.timeBounds = TimeBounds.decode(stream); + } + decodedTransactionV0.memo = Memo.decode(stream); + int operationssize = stream.readInt(); + decodedTransactionV0.operations = new Operation[operationssize]; + for (int i = 0; i < operationssize; i++) { + decodedTransactionV0.operations[i] = Operation.decode(stream); + } + decodedTransactionV0.ext = TransactionV0Ext.decode(stream); + return decodedTransactionV0; + } + @Override + public int hashCode() { + return Objects.hashCode(this.sourceAccountEd25519, this.fee, this.seqNum, this.timeBounds, this.memo, Arrays.hashCode(this.operations), this.ext); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof TransactionV0)) { + return false; + } + + TransactionV0 other = (TransactionV0) object; + return Objects.equal(this.sourceAccountEd25519, other.sourceAccountEd25519) && Objects.equal(this.fee, other.fee) && Objects.equal(this.seqNum, other.seqNum) && Objects.equal(this.timeBounds, other.timeBounds) && Objects.equal(this.memo, other.memo) && Arrays.equals(this.operations, other.operations) && Objects.equal(this.ext, other.ext); + } + + public static class TransactionV0Ext { + public TransactionV0Ext () {} + Integer v; + public Integer getDiscriminant() { + return this.v; + } + public void setDiscriminant(Integer value) { + this.v = value; + } + public static void encode(XdrDataOutputStream stream, TransactionV0Ext encodedTransactionV0Ext) throws IOException { + //Xdrgen::AST::Typespecs::Int + //Integer + stream.writeInt(encodedTransactionV0Ext.getDiscriminant().intValue()); + switch (encodedTransactionV0Ext.getDiscriminant()) { + case 0: + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static TransactionV0Ext decode(XdrDataInputStream stream) throws IOException { + TransactionV0Ext decodedTransactionV0Ext = new TransactionV0Ext(); + Integer discriminant = stream.readInt(); + decodedTransactionV0Ext.setDiscriminant(discriminant); + switch (decodedTransactionV0Ext.getDiscriminant()) { + case 0: + break; + } + return decodedTransactionV0Ext; + } + @Override + public int hashCode() { + return Objects.hashCode(this.v); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof TransactionV0Ext)) { + return false; + } + + TransactionV0Ext other = (TransactionV0Ext) object; + return Objects.equal(this.v, other.v); + } + + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java b/src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java new file mode 100644 index 000000000..90577e8e9 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java @@ -0,0 +1,73 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; +import java.util.Arrays; + +// === xdr source ============================================================ + +// struct TransactionV0Envelope +// { +// TransactionV0 tx; +// /* Each decorated signature is a signature over the SHA256 hash of +// * a TransactionSignaturePayload */ +// DecoratedSignature signatures<20>; +// }; + +// =========================================================================== +public class TransactionV0Envelope implements XdrElement { + public TransactionV0Envelope () {} + private TransactionV0 tx; + public TransactionV0 getTx() { + return this.tx; + } + public void setTx(TransactionV0 value) { + this.tx = value; + } + private DecoratedSignature[] signatures; + public DecoratedSignature[] getSignatures() { + return this.signatures; + } + public void setSignatures(DecoratedSignature[] value) { + this.signatures = value; + } + public static void encode(XdrDataOutputStream stream, TransactionV0Envelope encodedTransactionV0Envelope) throws IOException{ + TransactionV0.encode(stream, encodedTransactionV0Envelope.tx); + int signaturessize = encodedTransactionV0Envelope.getSignatures().length; + stream.writeInt(signaturessize); + for (int i = 0; i < signaturessize; i++) { + DecoratedSignature.encode(stream, encodedTransactionV0Envelope.signatures[i]); + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static TransactionV0Envelope decode(XdrDataInputStream stream) throws IOException { + TransactionV0Envelope decodedTransactionV0Envelope = new TransactionV0Envelope(); + decodedTransactionV0Envelope.tx = TransactionV0.decode(stream); + int signaturessize = stream.readInt(); + decodedTransactionV0Envelope.signatures = new DecoratedSignature[signaturessize]; + for (int i = 0; i < signaturessize; i++) { + decodedTransactionV0Envelope.signatures[i] = DecoratedSignature.decode(stream); + } + return decodedTransactionV0Envelope; + } + @Override + public int hashCode() { + return Objects.hashCode(this.tx, Arrays.hashCode(this.signatures)); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof TransactionV0Envelope)) { + return false; + } + + TransactionV0Envelope other = (TransactionV0Envelope) object; + return Objects.equal(this.tx, other.tx) && Arrays.equals(this.signatures, other.signatures); + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java b/src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java new file mode 100644 index 000000000..f11777a5b --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java @@ -0,0 +1,73 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; +import java.util.Arrays; + +// === xdr source ============================================================ + +// struct TransactionV1Envelope +// { +// Transaction tx; +// /* Each decorated signature is a signature over the SHA256 hash of +// * a TransactionSignaturePayload */ +// DecoratedSignature signatures<20>; +// }; + +// =========================================================================== +public class TransactionV1Envelope implements XdrElement { + public TransactionV1Envelope () {} + private Transaction tx; + public Transaction getTx() { + return this.tx; + } + public void setTx(Transaction value) { + this.tx = value; + } + private DecoratedSignature[] signatures; + public DecoratedSignature[] getSignatures() { + return this.signatures; + } + public void setSignatures(DecoratedSignature[] value) { + this.signatures = value; + } + public static void encode(XdrDataOutputStream stream, TransactionV1Envelope encodedTransactionV1Envelope) throws IOException{ + Transaction.encode(stream, encodedTransactionV1Envelope.tx); + int signaturessize = encodedTransactionV1Envelope.getSignatures().length; + stream.writeInt(signaturessize); + for (int i = 0; i < signaturessize; i++) { + DecoratedSignature.encode(stream, encodedTransactionV1Envelope.signatures[i]); + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static TransactionV1Envelope decode(XdrDataInputStream stream) throws IOException { + TransactionV1Envelope decodedTransactionV1Envelope = new TransactionV1Envelope(); + decodedTransactionV1Envelope.tx = Transaction.decode(stream); + int signaturessize = stream.readInt(); + decodedTransactionV1Envelope.signatures = new DecoratedSignature[signaturessize]; + for (int i = 0; i < signaturessize; i++) { + decodedTransactionV1Envelope.signatures[i] = DecoratedSignature.decode(stream); + } + return decodedTransactionV1Envelope; + } + @Override + public int hashCode() { + return Objects.hashCode(this.tx, Arrays.hashCode(this.signatures)); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof TransactionV1Envelope)) { + return false; + } + + TransactionV1Envelope other = (TransactionV1Envelope) object; + return Objects.equal(this.tx, other.tx) && Arrays.equals(this.signatures, other.signatures); + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java b/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java index ccc1a962d..5148ea545 100644 --- a/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java +++ b/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java @@ -12,12 +12,16 @@ // enum TrustLineFlags // { // // issuer has authorized account to perform transactions with its credit -// AUTHORIZED_FLAG = 1 +// AUTHORIZED_FLAG = 1, +// // issuer has authorized account to maintain and reduce liabilities for its +// // credit +// AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG = 2 // }; // =========================================================================== public enum TrustLineFlags implements XdrElement { AUTHORIZED_FLAG(1), + AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG(2), ; private int mValue; @@ -33,6 +37,7 @@ public static TrustLineFlags decode(XdrDataInputStream stream) throws IOExceptio int value = stream.readInt(); switch (value) { case 1: return AUTHORIZED_FLAG; + case 2: return AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG; default: throw new RuntimeException("Unknown enum value: " + value); } diff --git a/src/test/java/org/stellar/sdk/FeeBumpTransactionTest.java b/src/test/java/org/stellar/sdk/FeeBumpTransactionTest.java new file mode 100644 index 000000000..e000b8af4 --- /dev/null +++ b/src/test/java/org/stellar/sdk/FeeBumpTransactionTest.java @@ -0,0 +1,197 @@ +package org.stellar.sdk; + +import org.junit.Test; +import org.stellar.sdk.xdr.EnvelopeType; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class FeeBumpTransactionTest { + + private Transaction createInnerTransaction(int baseFee) { + KeyPair source = KeyPair.fromSecretSeed("SCH27VUZZ6UAKB67BDNF6FA42YMBMQCBKXWGMFD5TZ6S5ZZCZFLRXKHS"); + + Account account = new Account(source.getAccountId(), 2908908335136768L); + Transaction inner = new Transaction.Builder(account, Network.TESTNET) + .addOperation(new PaymentOperation.Builder( + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + new AssetTypeNative(), + "200" + ).build()) + .setBaseFee(baseFee) + .addTimeBounds(new TimeBounds(10, 11)) + .build(); + + inner.setEnvelopeType(EnvelopeType.ENVELOPE_TYPE_TX); + inner.sign(source); + return inner; + } + + private Transaction createInnerTransaction() { + return createInnerTransaction(Transaction.MIN_BASE_FEE); + } + + @Test + public void testRequiresFeeAccount() { + Transaction inner = createInnerTransaction(); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE * 2) + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("fee account has to be set. you must call setFeeAccount().", e.getMessage()); + } + } + + @Test + public void testSetFeeAccountMultipleTimes() { + Transaction inner = createInnerTransaction(); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE * 2) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .setFeeAccount("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR") + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("fee account has been already been set.", e.getMessage()); + } + } + + @Test + public void testRequiresBaseFee() { + Transaction inner = createInnerTransaction(); + + try { + new FeeBumpTransaction.Builder(inner) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("base fee has to be set. you must call setBaseFee().", e.getMessage()); + } + } + + @Test + public void testSetBaseFeeMultipleTimes() { + Transaction inner = createInnerTransaction(); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE * 2) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .setBaseFee(Transaction.MIN_BASE_FEE) + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("base fee has been already set.", e.getMessage()); + } + } + + @Test + public void testSetBaseFeeBelowNetworkMinimum() { + Transaction inner = createInnerTransaction(); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE -1) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("baseFee cannot be smaller than the BASE_FEE (100): 99", e.getMessage()); + } + } + + @Test + public void testSetBaseFeeBelowInner() { + Transaction inner = createInnerTransaction(Transaction.MIN_BASE_FEE+1); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("base fee cannot be lower than provided inner transaction base fee", e.getMessage()); + } + } + + + @Test + public void testSetBaseFeeOverflowsLong() { + Transaction inner = createInnerTransaction(Transaction.MIN_BASE_FEE+1); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Long.MAX_VALUE) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("fee overflows 64 bit int", e.getMessage()); + } + } + + @Test + public void testSetBaseFeeEqualToInner() { + Transaction inner = createInnerTransaction(); + + FeeBumpTransaction feeBump = new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + + assertEquals(Transaction.MIN_BASE_FEE*2, feeBump.getFee()); + } + + @Test + public void testHash() { + Transaction inner = createInnerTransaction(); + assertEquals("95dcf35a43a1a05bcd50f3eb148b31127829a9460dc32a17c4a7f7c4677409d4", inner.hashHex()); + + FeeBumpTransaction feeBump = new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE * 2) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + + assertEquals("382b1588ee8b315177a34ae96ebcaeb81c0ad3e04fee7c6b5a583b826517e1e4", feeBump.hashHex()); + } + + @Test + public void testRoundTripXdr() throws IOException { + Transaction inner = createInnerTransaction(); + + FeeBumpTransaction feeBump = new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE * 2) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + + assertEquals(Transaction.MIN_BASE_FEE * 4, feeBump.getFee()); + assertEquals("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", feeBump.getFeeAccount()); + assertEquals(inner, feeBump.getInnerTransaction()); + assertEquals(0, feeBump.getSignatures().size()); + + FeeBumpTransaction fromXdr = (FeeBumpTransaction) AbstractTransaction.fromEnvelopeXdr( + feeBump.toEnvelopeXdrBase64(), Network.TESTNET + ); + + assertEquals(feeBump, fromXdr); + + + KeyPair signer = KeyPair.random(); + feeBump.sign(signer); + fromXdr = (FeeBumpTransaction) AbstractTransaction.fromEnvelopeXdr( + feeBump.toEnvelopeXdrBase64(), Network.TESTNET + ); + assertEquals(feeBump, fromXdr); + + assertEquals(1, feeBump.getSignatures().size()); + signer.verify(feeBump.hash(), feeBump.getSignatures().get(0).getSignature().getSignature()); + } +} diff --git a/src/test/java/org/stellar/sdk/OperationTest.java b/src/test/java/org/stellar/sdk/OperationTest.java index 5a0800d9d..9ad3b091b 100644 --- a/src/test/java/org/stellar/sdk/OperationTest.java +++ b/src/test/java/org/stellar/sdk/OperationTest.java @@ -438,6 +438,7 @@ public void testSetOptionsOperationPreAuthTxSigner() { Transaction transaction = new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); // GC5SIC4E3V56VOHJ3OZAX5SJDTWY52JYI2AFK6PUGSXFVRJQYQXXZBZF @@ -551,9 +552,9 @@ public void testManageSellOfferOperation_BadArithmeticRegression() throws IOExce byte[] bytes = base64Encoding.decode(transactionEnvelopeToDecode); org.stellar.sdk.xdr.TransactionEnvelope transactionEnvelope = org.stellar.sdk.xdr.TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - assertEquals(1, transactionEnvelope.getTx().getOperations().length); + assertEquals(1, transactionEnvelope.getV0().getTx().getOperations().length); - ManageSellOfferOperation op = (ManageSellOfferOperation) Operation.fromXdr(transactionEnvelope.getTx().getOperations()[0]); + ManageSellOfferOperation op = (ManageSellOfferOperation) Operation.fromXdr(transactionEnvelope.getV0().getTx().getOperations()[0]); assertEquals("3397.893306099996", op.getPrice()); } @@ -567,9 +568,9 @@ public void testManageBuyOfferOperation_BadArithmeticRegression() throws IOExcep byte[] bytes = base64Encoding.decode(transactionEnvelopeToDecode); org.stellar.sdk.xdr.TransactionEnvelope transactionEnvelope = org.stellar.sdk.xdr.TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - assertEquals(1, transactionEnvelope.getTx().getOperations().length); + assertEquals(1, transactionEnvelope.getV0().getTx().getOperations().length); - ManageBuyOfferOperation op = (ManageBuyOfferOperation) Operation.fromXdr(transactionEnvelope.getTx().getOperations()[0]); + ManageBuyOfferOperation op = (ManageBuyOfferOperation) Operation.fromXdr(transactionEnvelope.getV0().getTx().getOperations()[0]); assertEquals("3397.893306099996", op.getPrice()); } diff --git a/src/test/java/org/stellar/sdk/Sep10ChallengeTest.java b/src/test/java/org/stellar/sdk/Sep10ChallengeTest.java index 42878c49a..2d7b759f9 100644 --- a/src/test/java/org/stellar/sdk/Sep10ChallengeTest.java +++ b/src/test/java/org/stellar/sdk/Sep10ChallengeTest.java @@ -1,7 +1,10 @@ package org.stellar.sdk; +import com.google.common.collect.ImmutableList; import com.google.common.io.BaseEncoding; import org.junit.Test; +import org.stellar.sdk.xdr.EnvelopeType; +import org.stellar.sdk.xdr.TransactionEnvelope; import java.io.IOException; import java.security.SecureRandom; @@ -15,7 +18,7 @@ public class Sep10ChallengeTest { @Test - public void testChallenge() throws IOException { + public void testChallenge() throws InvalidSep10ChallengeException { KeyPair server = KeyPair.random(); KeyPair client = KeyPair.random(); @@ -23,15 +26,14 @@ public void testChallenge() throws IOException { long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - Network.TESTNET, - client.getAccountId(), - "angkor wat", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + Network.TESTNET, + client.getAccountId(), + "angkor wat", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); assertEquals(Network.TESTNET, transaction.getNetwork()); assertEquals(100, transaction.getFee()); @@ -50,10 +52,33 @@ public void testChallenge() throws IOException { assertEquals(1, transaction.getSignatures().size()); assertTrue( - server.verify(transaction.hash(), transaction.getSignatures().get(0).getSignature().getSignature()) + server.verify(transaction.hash(), transaction.getSignatures().get(0).getSignature().getSignature()) ); } + @Test + public void testNewChallengeRejectsMuxedClientAccount() throws InvalidSep10ChallengeException { + try { + KeyPair server = KeyPair.random(); + + long now = System.currentTimeMillis() / 1000L; + long end = now + 300; + TimeBounds timeBounds = new TimeBounds(now, end); + + Sep10Challenge.newChallenge( + server, + Network.TESTNET, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + "angkor wat", + timeBounds + ); + fail(); + } catch (InvalidSep10ChallengeException e) { + assertEquals("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG is not a valid account id", e.getMessage()); + } + + } + @Test public void testReadChallengeTransactionValidSignedByServer() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); @@ -64,16 +89,126 @@ public void testReadChallengeTransactionValidSignedByServer() throws InvalidSep1 long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds ); - Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET); - assertEquals(new Sep10Challenge.ChallengeTransaction(Transaction.fromEnvelopeXdr(challenge, Network.TESTNET), client.getAccountId()), challengeTransaction); + Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), server.getAccountId(), Network.TESTNET); + assertEquals(new Sep10Challenge.ChallengeTransaction(transaction, client.getAccountId()), challengeTransaction); + } + + @Test + public void testReadChallengeTransactionAcceptsBothV0AndV1() throws InvalidSep10ChallengeException, IOException { + KeyPair server = KeyPair.random(); + KeyPair client = KeyPair.random(); + Network network = Network.TESTNET; + + long now = System.currentTimeMillis() / 1000L; + long end = now + 300; + TimeBounds timeBounds = new TimeBounds(now, end); + + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds + ); + + transaction.setEnvelopeType(EnvelopeType.ENVELOPE_TYPE_TX_V0); + TransactionEnvelope v0 = transaction.toEnvelopeXdr(); + String v0Base64 = transaction.toEnvelopeXdrBase64(); + assertEquals(EnvelopeType.ENVELOPE_TYPE_TX_V0, v0.getDiscriminant()); + + transaction.setEnvelopeType(EnvelopeType.ENVELOPE_TYPE_TX); + TransactionEnvelope v1 = transaction.toEnvelopeXdr(); + String v1Base64 = transaction.toEnvelopeXdrBase64(); + assertEquals(EnvelopeType.ENVELOPE_TYPE_TX, v1.getDiscriminant()); + + for (String envelopeBase64 : ImmutableList.of(v0Base64, v1Base64)) { + Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction( + envelopeBase64, + server.getAccountId(), + Network.TESTNET + ); + assertEquals(new Sep10Challenge.ChallengeTransaction(transaction, client.getAccountId()), challengeTransaction); + } + } + + @Test + public void testReadChallengeTransactionRejectsMuxedServer() throws InvalidSep10ChallengeException, IOException { + KeyPair server = KeyPair.random(); + KeyPair client = KeyPair.random(); + Network network = Network.TESTNET; + + long now = System.currentTimeMillis() / 1000L; + long end = now + 300; + TimeBounds timeBounds = new TimeBounds(now, end); + + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds + ); + + try { + Sep10Challenge.readChallengeTransaction( + transaction.toEnvelopeXdrBase64(), + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + Network.TESTNET + ); + fail(); + } catch (InvalidSep10ChallengeException e) { + assertEquals("serverAccountId: MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG is not a valid account id", e.getMessage()); + } + } + + @Test + public void testReadChallengeTransactionRejectsMuxedClient() throws InvalidSep10ChallengeException, IOException { + KeyPair server = KeyPair.random(); + KeyPair client = KeyPair.random(); + Network network = Network.TESTNET; + + long now = System.currentTimeMillis() / 1000L; + long end = now + 300; + TimeBounds timeBounds = new TimeBounds(now, end); + + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds + ); + Operation[] operations = transaction.getOperations(); + operations[0].setSourceAccount("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG"); + Transaction withMuxedClient = new Transaction( + transaction.getSourceAccount(), + transaction.getFee(), + transaction.getSequenceNumber(), + operations, + transaction.getMemo(), + transaction.getTimeBounds(), + transaction.getNetwork() + ); + withMuxedClient.getSignatures().addAll(transaction.mSignatures); + + try { + Sep10Challenge.readChallengeTransaction( + withMuxedClient.toEnvelopeXdrBase64(), + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + Network.TESTNET + ); + fail(); + } catch (InvalidSep10ChallengeException e) { + assertEquals("serverAccountId: MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG is not a valid account id", e.getMessage()); + } } @Test @@ -86,19 +221,18 @@ public void testReadChallengeTransactionValidSignedByServerAndClient() throws In long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(client); Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), server.getAccountId(), Network.TESTNET); - assertEquals(new Sep10Challenge.ChallengeTransaction(Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdrBase64(), Network.TESTNET), client.getAccountId()), challengeTransaction); + assertEquals(new Sep10Challenge.ChallengeTransaction(transaction, client.getAccountId()), challengeTransaction); } @Test @@ -120,18 +254,18 @@ public void testReadChallengeTransactionInvalidNotSignedByServer() throws IOExce Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(client); @@ -153,15 +287,15 @@ public void testReadChallengeTransactionInvalidCorrupted() throws InvalidSep10Ch long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds ); - challenge = challenge.replace("A", "B"); + String challenge = transaction.toEnvelopeXdrBase64().replace("A", "B"); try { Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET); @@ -172,7 +306,7 @@ public void testReadChallengeTransactionInvalidCorrupted() throws InvalidSep10Ch } @Test - public void testReadChallengeTransactionInvalidServerAccountIDMismatch() throws IOException { + public void testReadChallengeTransactionInvalidServerAccountIDMismatch() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair client = KeyPair.random(); @@ -180,17 +314,17 @@ public void testReadChallengeTransactionInvalidServerAccountIDMismatch() throws long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - Network.TESTNET, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + Network.TESTNET, + client.getAccountId(), + "Stellar Test", + timeBounds ); // assertThrows requires Java 8+ try { - Sep10Challenge.readChallengeTransaction(challenge, KeyPair.random().getAccountId(), Network.TESTNET); + Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), KeyPair.random().getAccountId(), Network.TESTNET); fail(); } catch (InvalidSep10ChallengeException e) { assertEquals("Transaction source account is not equal to server's account.", e.getMessage()); @@ -216,18 +350,18 @@ public void testReadChallengeTransactionInvalidSeqNoNotZero() throws IOException Account sourceAccount = new Account(server.getAccountId(), 100L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -259,17 +393,17 @@ public void testReadChallengeTransactionInvalidTimeboundsInfinite() throws IOExc Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation operation = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{operation}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -297,17 +431,17 @@ public void testReadChallengeTransactionInvalidNoTimeBounds() throws IOException Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation operation = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{operation}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - null, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + null, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -321,7 +455,7 @@ public void testReadChallengeTransactionInvalidNoTimeBounds() throws IOException } @Test - public void testReadChallengeTransactionInvalidTimeBoundsTooEarly() throws IOException { + public void testReadChallengeTransactionInvalidTimeBoundsTooEarly() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair client = KeyPair.random(); @@ -330,16 +464,16 @@ public void testReadChallengeTransactionInvalidTimeBoundsTooEarly() throws IOExc long end = current + 600; TimeBounds timeBounds = new TimeBounds(start, end); - String challenge = Sep10Challenge.newChallenge( - server, - Network.TESTNET, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + Network.TESTNET, + client.getAccountId(), + "Stellar Test", + timeBounds ); try { - Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET); + Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), server.getAccountId(), Network.TESTNET); fail(); } catch (InvalidSep10ChallengeException e) { assertEquals("Transaction is not within range of the specified timebounds.", e.getMessage()); @@ -347,7 +481,7 @@ public void testReadChallengeTransactionInvalidTimeBoundsTooEarly() throws IOExc } @Test - public void testReadChallengeTransactionInvalidTimeBoundsTooLate() throws IOException { + public void testReadChallengeTransactionInvalidTimeBoundsTooLate() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair client = KeyPair.random(); @@ -356,16 +490,16 @@ public void testReadChallengeTransactionInvalidTimeBoundsTooLate() throws IOExce long end = current - 300; TimeBounds timeBounds = new TimeBounds(start, end); - String challenge = Sep10Challenge.newChallenge( - server, - Network.TESTNET, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + Network.TESTNET, + client.getAccountId(), + "Stellar Test", + timeBounds ); try { - Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET); + Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), server.getAccountId(), Network.TESTNET); fail(); } catch (InvalidSep10ChallengeException e) { assertEquals("Transaction is not within range of the specified timebounds.", e.getMessage()); @@ -391,22 +525,22 @@ public void testReadChallengeTransactionInvalidTooManyOperations() throws IOExce Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); ManageDataOperation manageDataOperation2 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1, manageDataOperation2}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -431,18 +565,18 @@ public void testReadChallengeTransactionInvalidOperationWrongType() throws IOExc Account sourceAccount = new Account(server.getAccountId(), -1L); SetOptionsOperation setOptionsOperation = new SetOptionsOperation.Builder() - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{setOptionsOperation}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -473,17 +607,17 @@ public void testReadChallengeTransactionInvalidOperationNoSourceAccount() throws Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .build(); + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -515,18 +649,18 @@ public void testReadChallengeTransactionInvalidDataValueWrongEncodedLength() thr Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -553,18 +687,18 @@ public void testReadChallengeTransactionInvalidDataValueCorruptBase64() throws I byte[] encodedNonce = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?AAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes("UTF-8"); Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -597,18 +731,18 @@ public void testReadChallengeTransactionInvalidDataValueWrongByteLength() throws Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -642,26 +776,26 @@ public void testVerifyChallengeTransactionThresholdInvalidNotSignedByServer() th Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(masterClient.getAccountId()) - .build(); + .setSourceAccount(masterClient.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(masterClient); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) )); int threshold = 6; @@ -684,19 +818,18 @@ public void testVerifyChallengeTransactionThresholdValidServerAndClientKeyMeetin long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); Set signers = new HashSet(Collections.singletonList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 255) + new Sep10Challenge.Signer(masterClient.getAccountId(), 255) )); int threshold = 255; @@ -715,23 +848,22 @@ public void testVerifyChallengeTransactionThresholdValidServerAndMultipleClientK long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); transaction.sign(signerClient2); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) )); int threshold = 7; @@ -750,22 +882,21 @@ public void testVerifyChallengeTransactionThresholdValidServerAndMultipleClientK long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) )); int threshold = 3; @@ -785,15 +916,14 @@ public void testVerifyChallengeTransactionThresholdValidServerAndMultipleClientK long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); @@ -801,12 +931,12 @@ public void testVerifyChallengeTransactionThresholdValidServerAndMultipleClientK String xHash = "XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; String unknownSignerType = "?ARPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4), - new Sep10Challenge.Signer(preauthTxHash, 10), - new Sep10Challenge.Signer(xHash, 10), - new Sep10Challenge.Signer(unknownSignerType, 10) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4), + new Sep10Challenge.Signer(preauthTxHash, 10), + new Sep10Challenge.Signer(xHash, 10), + new Sep10Challenge.Signer(unknownSignerType, 10) )); int threshold = 3; @@ -815,7 +945,7 @@ public void testVerifyChallengeTransactionThresholdValidServerAndMultipleClientK } @Test - public void testVerifyChallengeTransactionThresholdInvalidServerAndMultipleClientKeyNotMeetingThreshold() throws IOException { + public void testVerifyChallengeTransactionThresholdInvalidServerAndMultipleClientKeyNotMeetingThreshold() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -826,22 +956,21 @@ public void testVerifyChallengeTransactionThresholdInvalidServerAndMultipleClien long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) )); int threshold = 7; @@ -854,7 +983,7 @@ public void testVerifyChallengeTransactionThresholdInvalidServerAndMultipleClien } @Test - public void testVerifyChallengeTransactionThresholdInvalidClientKeyUnrecognized() throws IOException { + public void testVerifyChallengeTransactionThresholdInvalidClientKeyUnrecognized() throws InvalidSep10ChallengeException, IOException { Network network = Network.TESTNET; KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); @@ -865,24 +994,23 @@ public void testVerifyChallengeTransactionThresholdInvalidClientKeyUnrecognized( long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); transaction.sign(signerClient2); transaction.sign(KeyPair.random()); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) )); int threshold = 7; @@ -895,7 +1023,7 @@ public void testVerifyChallengeTransactionThresholdInvalidClientKeyUnrecognized( } @Test - public void testVerifyChallengeTransactionThresholdInvalidNoSigners() throws IOException { + public void testVerifyChallengeTransactionThresholdInvalidNoSigners() throws InvalidSep10ChallengeException, IOException { Network network = Network.TESTNET; KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); @@ -906,15 +1034,14 @@ public void testVerifyChallengeTransactionThresholdInvalidNoSigners() throws IOE long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); transaction.sign(signerClient2); @@ -930,7 +1057,7 @@ public void testVerifyChallengeTransactionThresholdInvalidNoSigners() throws IOE } @Test - public void testVerifyChallengeTransactionThresholdInvalidNoPublicKeySigners() throws IOException { + public void testVerifyChallengeTransactionThresholdInvalidNoPublicKeySigners() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -941,15 +1068,14 @@ public void testVerifyChallengeTransactionThresholdInvalidNoPublicKeySigners() t long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); transaction.sign(signerClient2); @@ -958,9 +1084,9 @@ public void testVerifyChallengeTransactionThresholdInvalidNoPublicKeySigners() t String xHash = "XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; String unknownSignerType = "?ARPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(preauthTxHash, 1), - new Sep10Challenge.Signer(xHash, 2), - new Sep10Challenge.Signer(unknownSignerType, 2) + new Sep10Challenge.Signer(preauthTxHash, 1), + new Sep10Challenge.Signer(xHash, 2), + new Sep10Challenge.Signer(unknownSignerType, 2) )); int threshold = 3; @@ -982,21 +1108,20 @@ public void testVerifyChallengeTransactionThresholdWeightsAddToMoreThan8Bits() t long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 255), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 1) + new Sep10Challenge.Signer(masterClient.getAccountId(), 255), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 1) )); int threshold = 1; @@ -1025,18 +1150,18 @@ public void testVerifyChallengeTransactionSignersInvalidServer() throws IOExcept Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(masterClient.getAccountId()) - .build(); + .setSourceAccount(masterClient.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(masterClient); @@ -1062,15 +1187,14 @@ public void testVerifyChallengeTransactionSignersValidServerAndClientMasterKey() long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(masterClient); Set signers = new HashSet(Collections.singletonList(masterClient.getAccountId())); @@ -1079,7 +1203,7 @@ public void testVerifyChallengeTransactionSignersValidServerAndClientMasterKey() } @Test - public void testVerifyChallengeTransactionSignersInvalidServerAndNoClient() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidServerAndNoClient() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1090,15 +1214,14 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndNoClient() thro long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); Set signers = new HashSet(Arrays.asList(masterClient.getAccountId(), signerClient1.getAccountId(), signerClient2.getAccountId(), KeyPair.random().getAccountId())); try { Sep10Challenge.verifyChallengeTransactionSigners(transaction.toEnvelopeXdrBase64(), server.getAccountId(), network, signers); @@ -1109,7 +1232,7 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndNoClient() thro } @Test - public void testVerifyChallengeTransactionSignersInvalidServerAndClientKeyUnrecognized() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidServerAndClientKeyUnrecognized() throws InvalidSep10ChallengeException, IOException { Network network = Network.TESTNET; KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); @@ -1120,15 +1243,14 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientKeyUnreco long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); transaction.sign(signerClient2); @@ -1155,15 +1277,14 @@ public void testVerifyChallengeTransactionSignersValidServerAndMultipleClientSig long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); transaction.sign(signerClient2); @@ -1184,15 +1305,14 @@ public void testVerifyChallengeTransactionSignersValidServerAndMultipleClientSig long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient2); transaction.sign(signerClient1); @@ -1213,15 +1333,14 @@ public void testVerifyChallengeTransactionSignersValidServerAndClientSignersNotM long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); Set signers = new HashSet(Arrays.asList(masterClient.getAccountId(), signerClient1.getAccountId())); @@ -1240,15 +1359,14 @@ public void testVerifyChallengeTransactionSignersValidServerAndClientSignersIgno long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); Set signers = new HashSet(Arrays.asList(masterClient.getAccountId(), signerClient1.getAccountId(), server.getAccountId())); @@ -1258,7 +1376,7 @@ public void testVerifyChallengeTransactionSignersValidServerAndClientSignersIgno @Test - public void testVerifyChallengeTransactionSignersInvalidServerNoClientSignersIgnoresServerSigner() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidServerNoClientSignersIgnoresServerSigner() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1268,17 +1386,17 @@ public void testVerifyChallengeTransactionSignersInvalidServerNoClientSignersIgn long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); Set signers = new HashSet(Arrays.asList(masterClient.getAccountId(), signerClient1.getAccountId(), server.getAccountId())); try { - Sep10Challenge.verifyChallengeTransactionSigners(challenge, server.getAccountId(), network, signers); + Sep10Challenge.verifyChallengeTransactionSigners(transaction.toEnvelopeXdrBase64(), server.getAccountId(), network, signers); fail(); } catch (InvalidSep10ChallengeException e) { assertEquals("Transaction not signed by any client signer.", e.getMessage()); @@ -1297,28 +1415,27 @@ public void testVerifyChallengeTransactionSignersValidIgnorePreauthTxHashAndXHas long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); String preauthTxHash = "TAQCSRX2RIDJNHFIFHWD63X7D7D6TRT5Y2S6E3TEMXTG5W3OECHZ2OG4"; String xHash = "XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; String unknownSignerType = "?ARPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; Set signers = new HashSet(Arrays.asList(masterClient.getAccountId(), - signerClient1.getAccountId(), preauthTxHash, xHash, unknownSignerType)); + signerClient1.getAccountId(), preauthTxHash, xHash, unknownSignerType)); Set signersFound = Sep10Challenge.verifyChallengeTransactionSigners(transaction.toEnvelopeXdrBase64(), server.getAccountId(), network, signers); assertEquals(new HashSet(Collections.singletonList(signerClient1.getAccountId())), signersFound); } @Test - public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFailsDuplicateSignatures() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFailsDuplicateSignatures() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1328,15 +1445,14 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFa long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); transaction.sign(signerClient1); @@ -1350,7 +1466,7 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFa } @Test - public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFailsSignerSeed() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFailsSignerSeed() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1360,15 +1476,14 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFa long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); Set signers = new HashSet(Collections.singletonList(new String(signerClient1.getSecretSeed()))); @@ -1381,7 +1496,7 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFa } @Test - public void testVerifyChallengeTransactionSignersInvalidNoSignersNull() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidNoSignersNull() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1391,15 +1506,14 @@ public void testVerifyChallengeTransactionSignersInvalidNoSignersNull() throws I long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); try { @@ -1411,7 +1525,7 @@ public void testVerifyChallengeTransactionSignersInvalidNoSignersNull() throws I } @Test - public void testVerifyChallengeTransactionSignersInvalidNoSignersEmptySet() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidNoSignersEmptySet() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1421,15 +1535,14 @@ public void testVerifyChallengeTransactionSignersInvalidNoSignersEmptySet() thro long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); try { diff --git a/src/test/java/org/stellar/sdk/ServerTest.java b/src/test/java/org/stellar/sdk/ServerTest.java index d5b33a03b..dfa1656a7 100644 --- a/src/test/java/org/stellar/sdk/ServerTest.java +++ b/src/test/java/org/stellar/sdk/ServerTest.java @@ -13,6 +13,7 @@ import org.stellar.sdk.responses.SubmitTransactionTimeoutResponseException; import org.stellar.sdk.responses.SubmitTransactionUnknownResponseException; import org.stellar.sdk.responses.operations.OperationResponse; +import org.stellar.sdk.xdr.EnvelopeType; import java.io.IOException; import java.net.URISyntaxException; @@ -173,6 +174,7 @@ Transaction buildTransaction(Network network) throws IOException { Transaction.Builder builder = new Transaction.Builder(account, network) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .addMemo(Memo.text("Hello world!")) + .setBaseFee(Transaction.MIN_BASE_FEE) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE); assertEquals(1, builder.getOperationsCount()); @@ -332,6 +334,18 @@ public void testNextPage() throws IOException, URISyntaxException { public static final String DESTINATION_ACCOUNT_NO_MEMO_REQUIRED = "GDYC2D4P2SRC5DCEDDK2OUFESSPCTZYLDOEF6NYHR2T7X5GUTEABCQC2"; public static final String DESTINATION_ACCOUNT_NO_FOUND = "GD2OVSQPGD5FBJPMW4YN3FGDJ7JDFKNOMJT35T4H52FLHXJK5MFSR5RA"; public static final String DESTINATION_ACCOUNT_FETCH_ERROR = "GB7WNQUTDLD6YJ4MR3KQN3Y6ZIDIGTA7GRKNH47HOGMP2ETFGRSLD6OG"; + public static final String DESTINATION_ACCOUNT_MEMO_ID = "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG"; + + private FeeBumpTransaction feeBump(Transaction inner) { + inner.setEnvelopeType(EnvelopeType.ENVELOPE_TYPE_TX); + KeyPair signer = KeyPair.fromSecretSeed("SA5ZEFDVFZ52GRU7YUGR6EDPBNRU2WLA6IQFQ7S2IH2DG3VFV3DOMV2Q"); + FeeBumpTransaction tx = new FeeBumpTransaction.Builder(inner) + .setFeeAccount(signer.getAccountId()) + .setBaseFee(FeeBumpTransaction.MIN_BASE_FEE*10) + .build(); + tx.sign(signer); + return tx; + } @Test public void testCheckMemoRequiredWithMemo() throws IOException, AccountRequiresMemoException { @@ -350,10 +364,34 @@ public void testCheckMemoRequiredWithMemo() throws IOException, AccountRequiresM .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_MEMO_REQUIRED_D).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) .addMemo(new MemoText("Hello, Stellar.")) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); server.submitTransaction(transaction); + server.submitTransaction(feeBump(transaction)); + } + + @Test + public void testCheckMemoRequiredWithMemoIdAddress() throws IOException, AccountRequiresMemoException { + MockWebServer mockWebServer = new MockWebServer(); + mockWebServer.setDispatcher(buildTestCheckMemoRequiredMockDispatcher()); + mockWebServer.start(); + HttpUrl baseUrl = mockWebServer.url(""); + Server server = new Server(baseUrl.toString()); + + KeyPair source = KeyPair.fromSecretSeed("SDQXFKA32UVQHUTLYJ42N56ZUEM5PNVVI4XE7EA5QFMLA2DHDCQX3GPY"); + Account account = new Account(source.getAccountId(), 1L); + Transaction transaction = new Transaction.Builder(account, Network.PUBLIC) + .addOperation(new PaymentOperation.Builder(DESTINATION_ACCOUNT_MEMO_ID, new AssetTypeNative(), "10").build()) + .addOperation(new PathPaymentStrictReceiveOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_ID, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) + .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_ID, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) + .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_MEMO_ID).build()) + .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(100) + .build(); + transaction.sign(source); + server.submitTransaction(transaction); + server.submitTransaction(feeBump(transaction)); } @Test @@ -372,10 +410,11 @@ public void testCheckMemoRequiredWithSkipCheck() throws IOException, AccountRequ .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); server.submitTransaction(transaction, true); + server.submitTransaction(feeBump(transaction), true); } @Test @@ -394,7 +433,7 @@ public void testCheckMemoRequiredWithPaymentOperationNoMemo() throws IOException .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -405,6 +444,15 @@ public void testCheckMemoRequiredWithPaymentOperationNoMemo() throws IOException assertEquals(0, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_A, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(0, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_A, e.getAccountId()); + } } @Test @@ -423,7 +471,7 @@ public void testCheckMemoRequiredWithPathPaymentStrictReceiveOperationNoMemo() t .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -434,6 +482,15 @@ public void testCheckMemoRequiredWithPathPaymentStrictReceiveOperationNoMemo() t assertEquals(1, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_B, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(1, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_B, e.getAccountId()); + } } @Test @@ -452,7 +509,7 @@ public void testCheckMemoRequiredWithPathPaymentStrictSendOperationNoMemo() thro .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_REQUIRED_C, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -463,6 +520,15 @@ public void testCheckMemoRequiredWithPathPaymentStrictSendOperationNoMemo() thro assertEquals(2, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_C, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(2, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_C, e.getAccountId()); + } } @Test @@ -481,7 +547,7 @@ public void testCheckMemoRequiredWithAccountMergeOperationNoMemo() throws IOExce .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_MEMO_REQUIRED_D).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -492,6 +558,15 @@ public void testCheckMemoRequiredWithAccountMergeOperationNoMemo() throws IOExce assertEquals(3, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_D, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(3, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_D, e.getAccountId()); + } } @Test @@ -510,7 +585,7 @@ public void testCheckMemoRequiredTwoOperationsWithSameDestination() throws IOExc .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_REQUIRED_C, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_MEMO_REQUIRED_D).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -521,6 +596,15 @@ public void testCheckMemoRequiredTwoOperationsWithSameDestination() throws IOExc assertEquals(2, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_C, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(2, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_C, e.getAccountId()); + } } @Test @@ -540,7 +624,7 @@ public void testCheckMemoRequiredNoDestinationOperation() throws IOException { .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_REQUIRED_C, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -551,6 +635,15 @@ public void testCheckMemoRequiredNoDestinationOperation() throws IOException { assertEquals(1, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_A, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(1, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_A, e.getAccountId()); + } } @Test @@ -569,10 +662,11 @@ public void testCheckMemoRequiredAccountNotFound() throws IOException, AccountRe .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_NO_FOUND, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_FOUND).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); server.submitTransaction(transaction); + server.submitTransaction(feeBump(transaction)); } @Test @@ -593,7 +687,7 @@ public void testCheckMemoRequiredFetchAccountError() throws IOException, Account .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_REQUIRED_D, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_MEMO_REQUIRED_D).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -602,6 +696,13 @@ public void testCheckMemoRequiredFetchAccountError() throws IOException, Account } catch (ErrorResponse e) { assertEquals(400, e.getCode()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (ErrorResponse e) { + assertEquals(400, e.getCode()); + } } private Dispatcher buildTestCheckMemoRequiredMockDispatcher() { diff --git a/src/test/java/org/stellar/sdk/StrKeyTest.java b/src/test/java/org/stellar/sdk/StrKeyTest.java index 3937a4028..cace981be 100644 --- a/src/test/java/org/stellar/sdk/StrKeyTest.java +++ b/src/test/java/org/stellar/sdk/StrKeyTest.java @@ -1,56 +1,355 @@ package org.stellar.sdk; +import com.google.common.primitives.UnsignedLongs; import org.junit.Test; +import org.stellar.sdk.xdr.AccountID; +import org.stellar.sdk.xdr.CryptoKeyType; +import org.stellar.sdk.xdr.MuxedAccount; import java.io.IOException; import static org.junit.Assert.*; public class StrKeyTest { - @Test - public void testDecodeEncode() throws IOException, FormatException { - String seed = "SDJHRQF4GCMIIKAAAQ6IHY42X73FQFLHUULAPSKKD4DFDM7UXWWCRHBE"; - byte[] secret = StrKey.decodeCheck(StrKey.VersionByte.SEED, seed.toCharArray()); - char[] encoded = StrKey.encodeCheck(StrKey.VersionByte.SEED, secret); - assertEquals(seed, String.valueOf(encoded)); + @Test + public void testDecodeEncode() throws IOException, FormatException { + String seed = "SDJHRQF4GCMIIKAAAQ6IHY42X73FQFLHUULAPSKKD4DFDM7UXWWCRHBE"; + byte[] secret = StrKey.decodeCheck(StrKey.VersionByte.SEED, seed.toCharArray()); + char[] encoded = StrKey.encodeCheck(StrKey.VersionByte.SEED, secret); + assertEquals(seed, String.valueOf(encoded)); + } + + @Test() + public void testDecodeInvalidVersionByte() { + String address = "GCZHXL5HXQX5ABDM26LHYRCQZ5OJFHLOPLZX47WEBP3V2PF5AVFK2A5D"; + try { + StrKey.decodeCheck(StrKey.VersionByte.SEED, address.toCharArray()); + fail(); + } catch (FormatException e) { + } + } + + @Test() + public void testDecodeInvalidSeed() { + String seed = "SAA6NXOBOXP3RXGAXBW6PGFI5BPK4ODVAWITS4VDOMN5C2M4B66ZML"; + try { + StrKey.decodeCheck(StrKey.VersionByte.SEED, seed.toCharArray()); + fail(); + } catch (Exception e) { + } + } + + @Test() + public void testDecodedVersionByte() { + assertEquals(StrKey.decodeVersionByte("MBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWGTOG"), StrKey.VersionByte.MUXED_ACCOUNT); + assertEquals(StrKey.decodeVersionByte("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR"), StrKey.VersionByte.ACCOUNT_ID); + assertEquals(StrKey.decodeVersionByte("SDJHRQF4GCMIIKAAAQ6IHY42X73FQFLHUULAPSKKD4DFDM7UXWWCRHBE"), StrKey.VersionByte.SEED); + assertEquals(StrKey.decodeVersionByte("TAQCSRX2RIDJNHFIFHWD63X7D7D6TRT5Y2S6E3TEMXTG5W3OECHZ2OG4"), StrKey.VersionByte.PRE_AUTH_TX); + assertEquals(StrKey.decodeVersionByte("XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"), StrKey.VersionByte.SHA256_HASH); + } + + @Test() + public void testDecodedVersionByteThrows() { + try { + StrKey.decodeVersionByte("INVALIDBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6INVALID"); + fail(); + } catch (FormatException e) { + assertEquals("Version byte is invalid", e.getMessage()); + } + } + + private byte[] rawBytes(int... values) { + byte[] data = new byte[values.length]; + for (int i = 0; i < values.length; i++) { + data[i] = (byte) values[i]; + } + return data; + } + + @Test + public void testRoundTripAccountIdFromBytes() { + byte[] data = rawBytes( + 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, + 0xd0, 0xf4, 0xed, 0x88, 0xc7, 0x79, 0xe4, 0xfe, + 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, + 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03 + ); + String accountId = "GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5"; + assertEquals( + accountId, + String.valueOf(StrKey.encodeCheck(StrKey.VersionByte.ACCOUNT_ID, data)) + ); + assertArrayEquals(data, StrKey.decodeCheck(StrKey.VersionByte.ACCOUNT_ID, accountId.toCharArray())); + } + + @Test + public void testRoundTripSeedFromBytes() { + byte[] data = rawBytes( + 0x69, 0xa8, 0xc4, 0xcb, 0xb9, 0xf6, 0x4e, 0x8a, + 0x07, 0x98, 0xf6, 0xe1, 0xac, 0x65, 0xd0, 0x6c, + 0x31, 0x62, 0x92, 0x90, 0x56, 0xbc, 0xf4, 0xcd, + 0xb7, 0xd3, 0x73, 0x8d, 0x18, 0x55, 0xf3, 0x63 + ); + String seed = "SBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWHOKR"; + assertEquals( + seed, + String.valueOf(StrKey.encodeCheck(StrKey.VersionByte.SEED, data)) + ); + assertArrayEquals(data, StrKey.decodeCheck(StrKey.VersionByte.SEED, seed.toCharArray())); + } + + @Test + public void testRoundTripHashTxFromBytes() { + byte[] data = rawBytes( + 0x69, 0xa8, 0xc4, 0xcb, 0xb9, 0xf6, 0x4e, 0x8a, + 0x07, 0x98, 0xf6, 0xe1, 0xac, 0x65, 0xd0, 0x6c, + 0x31, 0x62, 0x92, 0x90, 0x56, 0xbc, 0xf4, 0xcd, + 0xb7, 0xd3, 0x73, 0x8d, 0x18, 0x55, 0xf3, 0x63 + ); + String hashTx = "TBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWHXL7"; + assertEquals( + hashTx, + String.valueOf(StrKey.encodeCheck(StrKey.VersionByte.PRE_AUTH_TX, data)) + ); + assertArrayEquals(data, StrKey.decodeCheck(StrKey.VersionByte.PRE_AUTH_TX, hashTx.toCharArray())); + } + + @Test + public void testRoundTripHashXFromBytes() { + byte[] data = rawBytes( + 0x69, 0xa8, 0xc4, 0xcb, 0xb9, 0xf6, 0x4e, 0x8a, + 0x07, 0x98, 0xf6, 0xe1, 0xac, 0x65, 0xd0, 0x6c, + 0x31, 0x62, 0x92, 0x90, 0x56, 0xbc, 0xf4, 0xcd, + 0xb7, 0xd3, 0x73, 0x8d, 0x18, 0x55, 0xf3, 0x63 + ); + String hashX = "XBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWGTOG"; + assertEquals( + hashX, + String.valueOf(StrKey.encodeCheck(StrKey.VersionByte.SHA256_HASH, data)) + ); + assertArrayEquals(data, StrKey.decodeCheck(StrKey.VersionByte.SHA256_HASH, hashX.toCharArray())); + } + + @Test + public void testRoundTripMuxedFromBytes() { + byte[] data = rawBytes( + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x0c, 0x34, 0xbf, 0x93, 0xad, 0x0d, 0x99, + 0x71, 0xd0, 0x4c, 0xcc, 0x90, 0xf7, 0x05, 0x51, + 0x1c, 0x83, 0x8a, 0xad, 0x97, 0x34, 0xa4, 0xa2, + 0xfb, 0x0d, 0x7a, 0x03, 0xfc, 0x7f, 0xe8, 0x9a + ); + String muxed = "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG"; + assertEquals( + muxed, + String.valueOf(StrKey.encodeCheck(StrKey.VersionByte.MUXED_ACCOUNT, data)) + ); + assertArrayEquals(data, StrKey.decodeCheck(StrKey.VersionByte.MUXED_ACCOUNT, muxed.toCharArray())); + } + + + @Test + public void testDecodeEmpty() { + try { + StrKey.decodeCheck(StrKey.VersionByte.ACCOUNT_ID, "".toCharArray()); + fail(); + } catch (Exception e) { + assertEquals("Encoded char array must have a length of at least 5.", e.getMessage()); + } + } + + @Test + public void testCorruptedChecksum() { + try { + StrKey.decodeCheck(StrKey.VersionByte.ACCOUNT_ID, "GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHE55".toCharArray()); + fail(); + } catch (Exception e) { + assertEquals("Checksum invalid", e.getMessage()); + } + } + + @Test + public void testCorruptedPayload() { + try { + StrKey.decodeCheck(StrKey.VersionByte.ACCOUNT_ID, "GA3D5KRYM6CB7OWOOOORR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5".toCharArray()); + fail(); + } catch (Exception e) { + assertEquals("Unused bits should be set to 0.", e.getMessage()); + } + } + + + @Test + public void testMuxedDecodeErrors() { + // non-canonical representation due to extra character + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOGA".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Encoded char array has leftover character.")); + + } + + // non-canonical representation due to leftover bits set to 1 (some of the test strkeys are too short for a muxed account + + // 1 unused bit (length 69) + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOH".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + + } + + + // 4 unused bits (length 68) + + // 'B' is equivalent to 0b00001 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNB".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + + } + + // 'C' is equivalent to 0b00010 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNC".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + + } + + // 'E' is equivalent to 0b00100 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNE".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + } - @Test() - public void testDecodeInvalidVersionByte() { - String address = "GCZHXL5HXQX5ABDM26LHYRCQZ5OJFHLOPLZX47WEBP3V2PF5AVFK2A5D"; - try { - StrKey.decodeCheck(StrKey.VersionByte.SEED, address.toCharArray()); - fail(); - } catch (FormatException e) {} + // 'I' is equivalent to 0b01000 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNI".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + } - @Test() - public void testDecodeInvalidSeed() { - String seed = "SAA6NXOBOXP3RXGAXBW6PGFI5BPK4ODVAWITS4VDOMN5C2M4B66ZML"; - try { - StrKey.decodeCheck(StrKey.VersionByte.SEED, seed.toCharArray()); - fail(); - } catch (Exception e) {} + // '7' is equivalent to 0b11111 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKN7".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + } - @Test() - public void testDecodedVersionByte() { - assertEquals(StrKey.decodeVersionByte("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR"), StrKey.VersionByte.ACCOUNT_ID); - assertEquals(StrKey.decodeVersionByte("SDJHRQF4GCMIIKAAAQ6IHY42X73FQFLHUULAPSKKD4DFDM7UXWWCRHBE"), StrKey.VersionByte.SEED); - assertEquals(StrKey.decodeVersionByte("TAQCSRX2RIDJNHFIFHWD63X7D7D6TRT5Y2S6E3TEMXTG5W3OECHZ2OG4"), StrKey.VersionByte.PRE_AUTH_TX); - assertEquals(StrKey.decodeVersionByte("XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"), StrKey.VersionByte.SHA256_HASH); + // '6' is equivalent to 0b11110 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKN6".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + } - @Test() - public void testDecodedVersionByteThrows() { - try { - StrKey.decodeVersionByte("INVALIDBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6INVALID"); - fail(); - } catch (FormatException e) { - assertEquals("Version byte is invalid", e.getMessage()); - } + // '4' is equivalent to 0b11100 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKN4".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + + } + + // 'Y' is equivalent to 0b11000 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNY".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + } - // TODO more tests + // 'Q' is equivalent to 0b10000, so there should be no unused bits error + // However, there will be a checksum error + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNQ".toCharArray() + ); + } catch (Exception e) { + assertEquals("Checksum invalid", e.getMessage()); + } + } + + @Test + public void testEncodeToXdrRoundTrip() { + String address = "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ"; + byte[] ed25519 = rawBytes( + 63, 12, 52, 191, 147, 173, 13, 153, 113, 208, + 76, 204, 144, 247, 5, 81, 28, 131, 138, 173, 151, 52, + 164, 162, 251, 13, 122, 3, 252, 127, 232, 154 + ); + AccountID account = StrKey.encodeToXDRAccountId(address); + assertArrayEquals(ed25519, account.getAccountID().getEd25519().getUint256()); + assertEquals(address, StrKey.encodeStellarAccountId(account)); + + MuxedAccount muxedAccount = StrKey.encodeToXDRMuxedAccount(address); + assertEquals(CryptoKeyType.KEY_TYPE_ED25519, muxedAccount.getDiscriminant()); + assertArrayEquals(ed25519, muxedAccount.getEd25519().getUint256()); + assertEquals(address, StrKey.encodeStellarMuxedAccount(muxedAccount)); + + String muxedAddress = "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG"; + muxedAccount = StrKey.encodeToXDRMuxedAccount(muxedAddress); + assertEquals(CryptoKeyType.KEY_TYPE_MUXED_ED25519, muxedAccount.getDiscriminant()); + assertArrayEquals(ed25519, muxedAccount.getMed25519().getEd25519().getUint256()); + long memoId = UnsignedLongs.parseUnsignedLong("9223372036854775808"); + assertEquals(memoId, muxedAccount.getMed25519().getId().getUint64().longValue()); + assertEquals(muxedAddress, StrKey.encodeStellarMuxedAccount(muxedAccount)); + + muxedAddress = "MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6"; + muxedAccount = StrKey.encodeToXDRMuxedAccount(muxedAddress); + assertEquals(CryptoKeyType.KEY_TYPE_MUXED_ED25519, muxedAccount.getDiscriminant()); + assertArrayEquals(ed25519, muxedAccount.getMed25519().getEd25519().getUint256()); + assertEquals(0, muxedAccount.getMed25519().getId().getUint64().longValue()); + assertEquals(muxedAddress, StrKey.encodeStellarMuxedAccount(muxedAccount)); + } + } diff --git a/src/test/java/org/stellar/sdk/TransactionTest.java b/src/test/java/org/stellar/sdk/TransactionTest.java index aa122b4cd..2fb469d28 100644 --- a/src/test/java/org/stellar/sdk/TransactionTest.java +++ b/src/test/java/org/stellar/sdk/TransactionTest.java @@ -13,53 +13,21 @@ public class TransactionTest { @Test - public void testDefaultBaseFee() throws FormatException { - // GBPMKIRA2OQW2XZZQUCQILI5TMVZ6JNRKM423BSAISDM7ZFWQ6KWEBC4 - KeyPair source = KeyPair.fromSecretSeed("SCH27VUZZ6UAKB67BDNF6FA42YMBMQCBKXWGMFD5TZ6S5ZZCZFLRXKHS"); - KeyPair destination = KeyPair.fromAccountId("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR"); - - Transaction.Builder.setDefaultOperationFee(2345); - - Account account = new Account(source.getAccountId(), 2908908335136768L); - Transaction transaction = new Transaction.Builder(account, Network.TESTNET) - .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) + public void testMissingOperationFee() { + long sequenceNumber = 2908908335136768L; + Account account = new Account("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR", sequenceNumber); + try { + new Transaction.Builder(account, Network.TESTNET) + .addOperation(new CreateAccountOperation.Builder("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR", "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) .build(); - - transaction.sign(source); - - assertEquals( - "AAAAAF7FIiDToW1fOYUFBC0dmyufJbFTOa2GQESGz+S2h5ViAAAJKQAKVaMAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA7eBSYbzcL5UKo7oXO24y1ckX+XuCtkDsyNHOp1n1bxAAAAAEqBfIAAAAAAAAAAABtoeVYgAAAEDf8XAGz9uOmfL0KJBP29eSXz/CZqZtl0Mm8jHye3xwLgo2HJDfvCJdijGKsx34AfNl6hvX+Cq3IVk062sLSuoK", - transaction.toEnvelopeXdrBase64()); - - Transaction transaction2 = Transaction.fromEnvelopeXdr( - transaction.toEnvelopeXdr(), - Network.TESTNET - ); - - assertEquals(transaction.getSourceAccount(), transaction2.getSourceAccount()); - assertEquals(transaction.getSequenceNumber(), transaction2.getSequenceNumber()); - assertEquals(transaction.getFee(), transaction2.getFee()); - assertEquals( - ((CreateAccountOperation) transaction.getOperations()[0]).getStartingBalance(), - ((CreateAccountOperation) transaction2.getOperations()[0]).getStartingBalance() - ); - } - - @Test - public void testDefaultBaseFeeThrows() { - try { - Transaction.Builder.setDefaultOperationFee(99); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { + fail("expected RuntimeException"); + } catch (RuntimeException e) { // expected } - - // should succeed - Transaction.Builder.setDefaultOperationFee(100); } - @Test + @Test public void testBuilderSuccessTestnet() throws FormatException { // GBPMKIRA2OQW2XZZQUCQILI5TMVZ6JNRKM423BSAISDM7ZFWQ6KWEBC4 KeyPair source = KeyPair.fromSecretSeed("SCH27VUZZ6UAKB67BDNF6FA42YMBMQCBKXWGMFD5TZ6S5ZZCZFLRXKHS"); @@ -70,6 +38,7 @@ public void testBuilderSuccessTestnet() throws FormatException { Transaction transaction = new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); transaction.sign(source); @@ -82,7 +51,7 @@ public void testBuilderSuccessTestnet() throws FormatException { assertEquals(transaction.getSequenceNumber(), sequenceNumber + 1); assertEquals(transaction.getFee(), 100); - Transaction transaction2 = Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); + Transaction transaction2 = (Transaction)Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); assertEquals(transaction.getSourceAccount(), transaction2.getSourceAccount()); assertEquals(transaction.getSequenceNumber(), transaction2.getSequenceNumber()); @@ -106,6 +75,7 @@ public void testBuilderMemoText() throws FormatException { .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .addMemo(Memo.text("Hello world!")) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); transaction.sign(source); @@ -114,7 +84,7 @@ public void testBuilderMemoText() throws FormatException { "AAAAAF7FIiDToW1fOYUFBC0dmyufJbFTOa2GQESGz+S2h5ViAAAAZAAKVaMAAAABAAAAAAAAAAEAAAAMSGVsbG8gd29ybGQhAAAAAQAAAAAAAAAAAAAAAO3gUmG83C+VCqO6FztuMtXJF/l7grZA7MjRzqdZ9W8QAAAABKgXyAAAAAAAAAAAAbaHlWIAAABAxzofBhoayuUnz8t0T1UNWrTgmJ+lCh9KaeOGu2ppNOz9UGw0abGLhv+9oWQsstaHx6YjwWxL+8GBvwBUVWRlBQ==", transaction.toEnvelopeXdrBase64()); - Transaction transaction2 = Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); + Transaction transaction2 = (Transaction)Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); assertEquals(transaction.getSourceAccount(), transaction2.getSourceAccount()); assertEquals(transaction.getSequenceNumber(), transaction2.getSequenceNumber()); @@ -137,6 +107,7 @@ public void testBuilderTimeBounds() throws FormatException, IOException { .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .addTimeBounds(new TimeBounds(42, 1337)) .addMemo(Memo.hash("abcdef")) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); transaction.sign(source); @@ -152,7 +123,7 @@ public void testBuilderTimeBounds() throws FormatException, IOException { assertEquals(decodedTransaction.getTimeBounds().getMinTime().getTimePoint().getUint64().longValue(), 42); assertEquals(decodedTransaction.getTimeBounds().getMaxTime().getTimePoint().getUint64().longValue(), 1337); - Transaction transaction2 = Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); + Transaction transaction2 = (Transaction)Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); assertEquals(transaction.getSourceAccount(), transaction2.getSourceAccount()); assertEquals(transaction.getSequenceNumber(), transaction2.getSequenceNumber()); @@ -174,7 +145,7 @@ public void testBuilderBaseFee() throws FormatException { Account account = new Account(source.getAccountId(), 2908908335136768L); Transaction transaction = new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) - .setOperationFee(200) + .setBaseFee(200) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) .build(); @@ -184,7 +155,7 @@ public void testBuilderBaseFee() throws FormatException { "AAAAAF7FIiDToW1fOYUFBC0dmyufJbFTOa2GQESGz+S2h5ViAAAAyAAKVaMAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA7eBSYbzcL5UKo7oXO24y1ckX+XuCtkDsyNHOp1n1bxAAAAAEqBfIAAAAAAAAAAABtoeVYgAAAED1Mbd0oou0sfNFRuxsSqvrwJ9RzzTSnX8sTmlbMKcV0V3Kl1eDKoerD+xZ1pNQwOJZrAG2yapXyg60PQfDUcMN", transaction.toEnvelopeXdrBase64()); - Transaction transaction2 = Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); + Transaction transaction2 = (Transaction)Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); assertEquals(transaction.getSourceAccount(), transaction2.getSourceAccount()); assertEquals(transaction.getSequenceNumber(), transaction2.getSequenceNumber()); @@ -203,7 +174,7 @@ public void testBuilderBaseFeeThrows() throws FormatException { Account account = new Account(source.getAccountId(), 2908908335136768L); Transaction.Builder builder = new Transaction.Builder(account, Network.TESTNET); try { - builder.setOperationFee(99); + builder.setBaseFee(99); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException e) { // expected @@ -217,6 +188,7 @@ public void testBuilderWithTimeBoundsButNoTimeout() throws IOException { .addOperation(new CreateAccountOperation.Builder(KeyPair.random().getAccountId(), "2000").build()) .addTimeBounds(new TimeBounds(42, 1337)) .addMemo(Memo.hash("abcdef")) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); } @@ -227,6 +199,7 @@ public void testBuilderRequiresTimeoutOrTimeBounds() throws IOException { new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(KeyPair.random().getAccountId(), "2000").build()) .addMemo(Memo.hash("abcdef")) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); fail(); } catch (RuntimeException exception) { @@ -260,6 +233,7 @@ public void testBuilderTimeoutSetsTimeBounds() throws IOException { Transaction transaction = new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(KeyPair.random().getAccountId(), "2000").build()) .setTimeout(10) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); assertEquals(0, transaction.getTimeBounds().getMinTime()); @@ -290,6 +264,7 @@ public void testBuilderFailsWhenSettingTimeoutAndMaxTimeNotSet() throws IOExcept .addOperation(new CreateAccountOperation.Builder(KeyPair.random().getAccountId(), "2000").build()) .addTimeBounds(new TimeBounds(42, 0)) .setTimeout(10) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); assertEquals(42, transaction.getTimeBounds().getMinTime()); @@ -310,7 +285,7 @@ public void testBuilderTimeBoundsNoMaxTime() throws FormatException, IOException .addTimeBounds(new TimeBounds(42, 0)) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) .addMemo(Memo.hash("abcdef")) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); @@ -338,6 +313,7 @@ public void testBuilderSuccessPublic() throws FormatException { Transaction transaction = new Transaction.Builder(account, Network.PUBLIC) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); transaction.sign(source); @@ -356,6 +332,7 @@ public void testSha256HashSigning() throws FormatException { Transaction transaction = new Transaction.Builder(account, Network.PUBLIC) .addOperation(new PaymentOperation.Builder(destination.getAccountId(), new AssetTypeNative(), "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); byte[] preimage = new byte[64]; @@ -381,6 +358,7 @@ public void testToBase64EnvelopeXdrBuilderNoSignatures() throws FormatException, Transaction transaction = new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); assertEquals( @@ -396,9 +374,10 @@ public void testNoOperations() throws FormatException, IOException { Account account = new Account(source.getAccountId(), 2908908335136768L); try { - Transaction transaction = new Transaction.Builder(account, Network.TESTNET).setTimeout( - Transaction.Builder.TIMEOUT_INFINITE - ).build(); + Transaction transaction = new Transaction.Builder(account, Network.TESTNET) + .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) + .build(); fail(); } catch (RuntimeException exception) { assertTrue(exception.getMessage().contains("At least one operation required")); diff --git a/src/test/java/org/stellar/sdk/responses/AccountDeserializerTest.java b/src/test/java/org/stellar/sdk/responses/AccountDeserializerTest.java index 42f1b83b6..8c6ab314d 100644 --- a/src/test/java/org/stellar/sdk/responses/AccountDeserializerTest.java +++ b/src/test/java/org/stellar/sdk/responses/AccountDeserializerTest.java @@ -7,6 +7,27 @@ import java.util.Arrays; public class AccountDeserializerTest extends TestCase { + @Test + public void testDeserializeBalanceAuth() { + AccountResponse account = GsonSingleton.getInstance().fromJson(jsonAuthorizedToMaintainLiabilities, AccountResponse.class); + + assertEquals(account.getBalances()[0].getAssetType(), "credit_alphanum4"); + assertEquals(account.getBalances()[0].getAssetCode(), "ABC"); + assertEquals(account.getBalances()[0].getAssetIssuer(), "GCRA6COW27CY5MTKIA7POQ2326C5ABYCXODBN4TFF5VL4FMBRHOT3YHU"); + assertEquals(account.getBalances()[0].getBalance(), "1001.0000000"); + assertEquals(account.getBalances()[0].getLimit(), "12000.4775807"); + assertEquals(account.getBalances()[0].getBuyingLiabilities(), "100.1234567"); + assertEquals(account.getBalances()[0].getSellingLiabilities(), "100.7654321"); + assertEquals(account.getBalances()[0].getAuthorized(), Boolean.FALSE); + assertEquals(account.getBalances()[0].getAuthorizedToMaintainLiabilities(), Boolean.TRUE); + + assertEquals(account.getBalances()[1].getAssetType(), "native"); + assertEquals(account.getBalances()[1].getBalance(), "20.0000300"); + assertEquals(account.getBalances()[1].getBuyingLiabilities(), "5.1234567"); + assertEquals(account.getBalances()[1].getSellingLiabilities(), "1.7654321"); + assertEquals(account.getBalances()[1].getLimit(), null); + } + @Test public void testDeserialize() { AccountResponse account = GsonSingleton.getInstance().fromJson(json, AccountResponse.class); @@ -77,6 +98,84 @@ public void testDeserializeV9() { assertEquals(account.getBalances()[1].getLimit(), null); } + String jsonAuthorizedToMaintainLiabilities = "{\n" + + " \"_links\": {\n" + + " \"effects\": {\n" + + " \"href\": \"/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7/effects{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " },\n" + + " \"offers\": {\n" + + " \"href\": \"/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7/offers{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " },\n" + + " \"operations\": {\n" + + " \"href\": \"/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7/operations{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " },\n" + + " \"self\": {\n" + + " \"href\": \"/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7\"\n" + + " },\n" + + " \"transactions\": {\n" + + " \"href\": \"/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7/transactions{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " }\n" + + " },"+ + " \"id\": \"GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7\",\n" + + " \"paging_token\": \"1\",\n" + + " \"account_id\": \"GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7\",\n" + + " \"sequence\": 2319149195853854,\n" + + " \"subentry_count\": 0,\n" + + " \"inflation_destination\": \"GAGRSA6QNQJN2OQYCBNQGMFLO4QLZFNEHIFXOMTQVSUTWVTWT66TOFSC\",\n" + + " \"home_domain\": \"stellar.org\",\n" + + " \"thresholds\": {\n" + + " \"low_threshold\": 10,\n" + + " \"med_threshold\": 20,\n" + + " \"high_threshold\": 30\n" + + " },\n" + + " \"flags\": {\n" + + " \"auth_required\": false,\n" + + " \"auth_revocable\": true,\n" + + " \"auth_immutable\": true\n" + + " },\n" + + " \"balances\": [\n" + + " {\n" + + " \"balance\": \"1001.0000000\",\n" + + " \"buying_liabilities\": \"100.1234567\",\n" + + " \"selling_liabilities\": \"100.7654321\",\n" + + " \"limit\": \"12000.4775807\",\n" + + " \"asset_type\": \"credit_alphanum4\",\n" + + " \"asset_code\": \"ABC\",\n" + + " \"asset_issuer\": \"GCRA6COW27CY5MTKIA7POQ2326C5ABYCXODBN4TFF5VL4FMBRHOT3YHU\",\n" + + " \"is_authorized\": false,\n" + + " \"is_authorized_to_maintain_liabilities\": true\n" + + " },"+ + " {\n" + + " \"asset_type\": \"native\",\n" + + " \"balance\": \"20.0000300\",\n" + + " \"buying_liabilities\": \"5.1234567\",\n" + + " \"selling_liabilities\": \"1.7654321\"\n" + + " }\n" + + " ],\n" + + " \"signers\": [\n" + + " {\n" + + " \"public_key\": \"GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7\",\n" + + " \"key\": \"GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7\",\n" + + " \"weight\": 0,\n" + + " \"type\": \"ed25519_public_key\"\n" + + " },\n" + + " {\n" + + " \"public_key\": \"GCR2KBCIU6KQXSQY5F5GZYC4WLNHCHCKW4NEGXNEZRYWLTNZIRJJY7D2\",\n" + + " \"key\": \"GCR2KBCIU6KQXSQY5F5GZYC4WLNHCHCKW4NEGXNEZRYWLTNZIRJJY7D2\",\n" + + " \"weight\": 1,\n" + + " \"type\": \"ed25519_public_key\"\n" + + " }\n" + + " ],\n" + + " \"data\": {\n" + + " \"entry1\": \"dGVzdA==\",\n" + + " \"entry2\": \"dGVzdDI=\"\n" + + " }" + + "}"; + String json = "{\n" + " \"_links\": {\n" + " \"effects\": {\n" + diff --git a/src/test/java/org/stellar/sdk/responses/EffectDeserializerTest.java b/src/test/java/org/stellar/sdk/responses/EffectDeserializerTest.java index 5346b2155..6122697e1 100644 --- a/src/test/java/org/stellar/sdk/responses/EffectDeserializerTest.java +++ b/src/test/java/org/stellar/sdk/responses/EffectDeserializerTest.java @@ -516,6 +516,43 @@ public void testDeserializeTrustlineAuthorizedEffect() { assertEquals(effect.getLinks().getPrecedes().getHref(), "http://horizon-testnet.stellar.org/effects?order=asc&cursor=33788507721730-2"); } + @Test + public void testDeserializeTrustlineAuthorizedToMaintainLiabilitiesEffect() { + String json = "{\n" + + " \"_links\": {\n" + + " \"operation\": {\n" + + " \"href\": \"http://horizon-testnet.stellar.org/operations/33788507721730\"\n" + + " },\n" + + " \"succeeds\": {\n" + + " \"href\": \"http://horizon-testnet.stellar.org/effects?order=desc\\u0026cursor=33788507721730-2\"\n" + + " },\n" + + " \"precedes\": {\n" + + " \"href\": \"http://horizon-testnet.stellar.org/effects?order=asc\\u0026cursor=33788507721730-2\"\n" + + " }\n" + + " },\n" + + " \"id\": \"0000033788507721730-0000000002\",\n" + + " \"paging_token\": \"33788507721730-2\",\n" + + " \"account\": \"GA6U5X6WOPNKKDKQULBR7IDHDBAQKOWPHYEC7WSXHZBFEYFD3XVZAKOO\",\n" + + " \"type\": \"trustline_authorized_to_maintain_liabilities\",\n" + + " \"type_i\": 25,\n" + + " \"asset_type\": \"credit_alphanum12\",\n" + + " \"asset_code\": \"TESTTEST\",\n" + + " \"trustor\": \"GB3E4AB4VWXJDUVN4Z3CPBU5HTMWVEQXONZYVDFMHQD6333KHCOL3UBR\"\n" + + " }"; + + TrustlineAuthorizedToMaintainLiabilitiesEffectResponse effect = (TrustlineAuthorizedToMaintainLiabilitiesEffectResponse) GsonSingleton.getInstance().fromJson(json, EffectResponse.class); + + assertEquals(effect.getAccount(), "GA6U5X6WOPNKKDKQULBR7IDHDBAQKOWPHYEC7WSXHZBFEYFD3XVZAKOO"); + assertEquals(effect.getAssetType(), "credit_alphanum12"); + assertEquals(effect.getAssetCode(), "TESTTEST"); + assertEquals(effect.getTrustor(), "GB3E4AB4VWXJDUVN4Z3CPBU5HTMWVEQXONZYVDFMHQD6333KHCOL3UBR"); + + assertEquals(effect.getLinks().getOperation().getHref(), "http://horizon-testnet.stellar.org/operations/33788507721730"); + assertEquals(effect.getLinks().getSucceeds().getHref(), "http://horizon-testnet.stellar.org/effects?order=desc&cursor=33788507721730-2"); + assertEquals(effect.getLinks().getPrecedes().getHref(), "http://horizon-testnet.stellar.org/effects?order=asc&cursor=33788507721730-2"); + } + + @Test public void testDeserializeTrustlineDeauthorizedEffect() { String json = "{\n" + diff --git a/src/test/java/org/stellar/sdk/responses/OperationDeserializerTest.java b/src/test/java/org/stellar/sdk/responses/OperationDeserializerTest.java index dd822e80e..b56cbcef6 100644 --- a/src/test/java/org/stellar/sdk/responses/OperationDeserializerTest.java +++ b/src/test/java/org/stellar/sdk/responses/OperationDeserializerTest.java @@ -175,7 +175,8 @@ public void testDeserializeAllowTrustOperation() { " \"asset_issuer\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" + " \"trustee\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" + " \"trustor\": \"GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F\",\n" + - " \"authorize\": true\n" + + " \"authorize\": true\n," + + " \"authorize_to_maintain_liabilities\": false\n" + " }"; AllowTrustOperationResponse operation = (AllowTrustOperationResponse) GsonSingleton.getInstance().fromJson(json, OperationResponse.class); @@ -183,6 +184,50 @@ public void testDeserializeAllowTrustOperation() { assertEquals(operation.getTrustee(), "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM"); assertEquals(operation.getTrustor(), "GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F"); assertEquals(operation.isAuthorize(), true); + assertEquals(operation.isAuthorizedToMaintainLiabilities(), false); + assertEquals(operation.getAsset(), Asset.createNonNativeAsset("EUR", "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM")); + } + + @Test + public void testDeserializeAllowTrustOperationAuthorizeToMaintainLiabilities() { + String json = "{\n" + + " \"_links\": {\n" + + " \"self\": {\n" + + " \"href\": \"//horizon-testnet.stellar.org/operations/3602979345141761\"\n" + + " },\n" + + " \"transaction\": {\n" + + " \"href\": \"//horizon-testnet.stellar.org/transactions/1f265c075e8559ee4c21a32ae53337658e52d7504841adad4144c37143ea01a2\"\n" + + " },\n" + + " \"effects\": {\n" + + " \"href\": \"//horizon-testnet.stellar.org/operations/3602979345141761/effects\"\n" + + " },\n" + + " \"succeeds\": {\n" + + " \"href\": \"//horizon-testnet.stellar.org/effects?order=desc\\u0026cursor=3602979345141761\"\n" + + " },\n" + + " \"precedes\": {\n" + + " \"href\": \"//horizon-testnet.stellar.org/effects?order=asc\\u0026cursor=3602979345141761\"\n" + + " }\n" + + " },\n" + + " \"id\": \"3602979345141761\",\n" + + " \"paging_token\": \"3602979345141761\",\n" + + " \"source_account\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" + + " \"type\": \"allow_trust\",\n" + + " \"type_i\": 7,\n" + + " \"asset_type\": \"credit_alphanum4\",\n" + + " \"asset_code\": \"EUR\",\n" + + " \"asset_issuer\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" + + " \"trustee\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" + + " \"trustor\": \"GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F\",\n" + + " \"authorize\": false\n," + + " \"authorize_to_maintain_liabilities\": true\n" + + " }"; + + AllowTrustOperationResponse operation = (AllowTrustOperationResponse) GsonSingleton.getInstance().fromJson(json, OperationResponse.class); + + assertEquals(operation.getTrustee(), "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM"); + assertEquals(operation.getTrustor(), "GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F"); + assertEquals(operation.isAuthorize(), false); + assertEquals(operation.isAuthorizedToMaintainLiabilities(), true); assertEquals(operation.getAsset(), Asset.createNonNativeAsset("EUR", "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM")); } diff --git a/src/test/java/org/stellar/sdk/responses/TransactionDeserializerTest.java b/src/test/java/org/stellar/sdk/responses/TransactionDeserializerTest.java index 38a74e27e..ff61680ef 100644 --- a/src/test/java/org/stellar/sdk/responses/TransactionDeserializerTest.java +++ b/src/test/java/org/stellar/sdk/responses/TransactionDeserializerTest.java @@ -1,5 +1,7 @@ package org.stellar.sdk.responses; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; import junit.framework.TestCase; import org.junit.Test; @@ -7,6 +9,30 @@ import org.stellar.sdk.MemoNone; public class TransactionDeserializerTest extends TestCase { + @Test + public void testDeserializeFeeBump() { + TransactionResponse transaction = GsonSingleton.getInstance().fromJson(jsonFeeBump, TransactionResponse.class); + assertEquals(transaction.getHash(), "3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352"); + assertEquals(transaction.getLedger(), Long.valueOf(123)); + assertEquals(transaction.isSuccessful(), Boolean.TRUE); + assertEquals(transaction.getSourceAccount(), "GABQGAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2MX"); + assertEquals(transaction.getFeeAccount(), "GABAEAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGKJ"); + assertEquals(transaction.getSourceAccountSequence(), Long.valueOf(97)); + assertEquals(transaction.getMaxFee(), Long.valueOf(776)); + assertEquals(transaction.getFeeCharged(), Long.valueOf(123)); + assertEquals(transaction.getOperationCount(), Integer.valueOf(1)); + assertEquals(transaction.getSignatures(), ImmutableList.of("Hh4e")); + + TransactionResponse.FeeBumpTransaction feeBumpTransaction = transaction.getFeeBump().get(); + assertEquals(feeBumpTransaction.getHash(), "3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352"); + assertEquals(feeBumpTransaction.getSignatures(), ImmutableList.of("Hh4e")); + + TransactionResponse.InnerTransaction innerTransaction = transaction.getInner().get(); + assertEquals(innerTransaction.getHash(), "e98869bba8bce08c10b78406202127f3888c25454cd37b02600862452751f526"); + assertEquals(innerTransaction.getMaxFee(), Long.valueOf(99)); + assertEquals(innerTransaction.getSignatures(), ImmutableList.of("FBQU")); + } + @Test public void testDeserialize() { TransactionResponse transaction = GsonSingleton.getInstance().fromJson(json, TransactionResponse.class); @@ -23,7 +49,9 @@ public void testDeserialize() { assertEquals(transaction.getEnvelopeXdr(), "AAAAAKgfpXwD1fWpPmZL+GkzWcBmhRQH7ouPsoTN3RoaGCfrAAAAZAAIbkcAAB9WAAAAAAAAAANRBBZE6D1qyGjISUGLY5Ldvp31PwAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAP1qe44j+i4uIT+arbD4QDQBt8ryEeJd7a0jskQ3nwDeAAAAAAAAAADA7RnarSzCwj3OT+M2btCMFpVBdqxJS+Sr00qBjtFv7gAAAABLCs/QAAAAAAAAAAEaGCfrAAAAQG/56Cj2J8W/KCZr+oC4sWND1CTGWfaccHNtuibQH8kZIb+qBSDY94g7hiaAXrlIeg9b7oz/XuP3x9MWYw2jtwM="); assertEquals(transaction.getResultXdr(), "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA="); assertEquals(transaction.getResultMetaXdr(), "AAAAAAAAAAEAAAACAAAAAAAN+SAAAAAAAAAAAMDtGdqtLMLCPc5P4zZu0IwWlUF2rElL5KvTSoGO0W/uAAAAAEsKz9AADfkgAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAN+SAAAAAAAAAAAP1qe44j+i4uIT+arbD4QDQBt8ryEeJd7a0jskQ3nwDeAAHp6WMr55YACD1BAAAAHgAAAAoAAAAAAAAAAAAAAAABAAAAAAAACgAAAAARC07BokpLTOF+/vVKBwiAlop7hHGJTNeGGlY4MoPykwAAAAEAAAAAK+Lzfd3yDD+Ov0GbYu1g7SaIBrKZeBUxoCunkLuI7aoAAAABAAAAAERmsKL73CyLV/HvjyQCERDXXpWE70Xhyb6MR5qPO3yQAAAAAQAAAABSORGwAdyuanN3sNOHqNSpACyYdkUM3L8VafUu69EvEgAAAAEAAAAAeCzqJNkMM/jLvyuMIfyFHljBlLCtDyj17RMycPuNtRMAAAABAAAAAIEi4R7juq15ymL00DNlAddunyFT4FyUD4muC4t3bobdAAAAAQAAAACaNpLL5YMfjOTdXVEqrAh99LM12sN6He6pHgCRAa1f1QAAAAEAAAAAqB+lfAPV9ak+Zkv4aTNZwGaFFAfui4+yhM3dGhoYJ+sAAAABAAAAAMNJrEvdMg6M+M+n4BDIdzsVSj/ZI9SvAp7mOOsvAD/WAAAAAQAAAADbHA6xiKB1+G79mVqpsHMOleOqKa5mxDpP5KEp/Xdz9wAAAAEAAAAAAAAAAA=="); - + assertEquals(transaction.getSignatures(), ImmutableList.of("b/noKPYnxb8oJmv6gLixY0PUJMZZ9pxwc226JtAfyRkhv6oFINj3iDuGJoBeuUh6D1vujP9e4/fH0xZjDaO3Aw==")); + assertEquals(transaction.getFeeBump(), Optional.absent()); + assertEquals(transaction.getInner(), Optional.absent()); assertTrue(transaction.getMemo() instanceof MemoHash); MemoHash memo = (MemoHash) transaction.getMemo(); assertEquals("51041644e83d6ac868c849418b6392ddbe9df53f000000000000000000000000", memo.getHexValue()); @@ -136,4 +164,70 @@ public void testDeserializeWithoutMemo() { " \"b/noKPYnxb8oJmv6gLixY0PUJMZZ9pxwc226JtAfyRkhv6oFINj3iDuGJoBeuUh6D1vujP9e4/fH0xZjDaO3Aw==\"\n" + " ]\n" + "}"; + + String jsonFeeBump = "{\n" + + " \"_links\": {\n" + + " \"self\": {\n" + + " \"href\": \"http://localhost/transactions/3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352\"\n" + + " },\n" + + " \"account\": {\n" + + " \"href\": \"http://localhost/accounts/GABQGAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2MX\"\n" + + " },\n" + + " \"ledger\": {\n" + + " \"href\": \"http://localhost/ledgers/123\"\n" + + " },\n" + + " \"operations\": {\n" + + " \"href\": \"http://localhost/transactions/3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352/operations{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " },\n" + + " \"effects\": {\n" + + " \"href\": \"http://localhost/transactions/3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352/effects{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " },\n" + + " \"precedes\": {\n" + + " \"href\": \"http://localhost/transactions?order=asc\\u0026cursor=528280981504\"\n" + + " },\n" + + " \"succeeds\": {\n" + + " \"href\": \"http://localhost/transactions?order=desc\\u0026cursor=528280981504\"\n" + + " },\n" + + " \"transaction\": {\n" + + " \"href\": \"http://localhost/transactions/3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352\"\n" + + " }\n" + + " },\n" + + " \"id\": \"3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352\",\n" + + " \"paging_token\": \"528280981504\",\n" + + " \"successful\": true,\n" + + " \"hash\": \"3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352\",\n" + + " \"ledger\": 123,\n" + + " \"created_at\": \"2020-04-21T10:21:26Z\",\n" + + " \"source_account\": \"GABQGAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2MX\",\n" + + " \"source_account_sequence\": \"97\",\n" + + " \"fee_account\": \"GABAEAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGKJ\",\n" + + " \"fee_charged\": 123,\n" + + " \"max_fee\": 776,\n" + + " \"operation_count\": 1,\n" + + " \"envelope_xdr\": \"AAAABQAAAAACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMIAAAAAgAAAAADAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGMAAAAAAAAAYQAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAEAAAAAAAAACwAAAAAAAABiAAAAAAAAAAECAgICAAAAAxQUFAAAAAAAAAAAAQMDAwMAAAADHh4eAA==\",\n" + + " \"result_xdr\": \"AAAAAAAAAHsAAAAB6Yhpu6i84IwQt4QGICEn84iMJUVM03sCYAhiRSdR9SYAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAsAAAAAAAAAAAAAAAA=\",\n" + + " \"result_meta_xdr\": \"AAAAAQAAAAAAAAAA\",\n" + + " \"fee_meta_xdr\": \"AAAAAA==\",\n" + + " \"memo_type\": \"none\",\n" + + " \"signatures\": [\n" + + " \"Hh4e\"\n" + + " ],\n" + + " \"valid_after\": \"1970-01-01T00:00:02Z\",\n" + + " \"valid_before\": \"1970-01-01T00:00:04Z\",\n" + + " \"fee_bump_transaction\": {\n" + + " \"hash\": \"3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352\",\n" + + " \"signatures\": [\n" + + " \"Hh4e\"\n" + + " ]\n" + + " },\n" + + " \"inner_transaction\": {\n" + + " \"hash\": \"e98869bba8bce08c10b78406202127f3888c25454cd37b02600862452751f526\",\n" + + " \"signatures\": [\n" + + " \"FBQU\"\n" + + " ],\n" + + " \"max_fee\": \"99\"\n" + + " }\n" + + "}"; } diff --git a/src/test/java/org/stellar/sdk/responses/TransactionPageDeserializerTest.java b/src/test/java/org/stellar/sdk/responses/TransactionPageDeserializerTest.java index 1d8db01a1..4f5a5cd0e 100644 --- a/src/test/java/org/stellar/sdk/responses/TransactionPageDeserializerTest.java +++ b/src/test/java/org/stellar/sdk/responses/TransactionPageDeserializerTest.java @@ -20,6 +20,12 @@ public void testDeserialize() { assertEquals(transactionsPage.getRecords().get(0).getLinks().getAccount().getHref(), "/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7"); assertEquals(transactionsPage.getRecords().get(9).getSourceAccount(), "GAENIE5LBJIXLMJIAJ7225IUPA6CX7EGHUXRX5FLCZFFAQSG2ZUYSWFK"); + // Transaction without memo_bytes field + assertTrue(transactionsPage.getRecords().get(7).getMemo() instanceof MemoText); + memoText = (MemoText) transactionsPage.getRecords().get(7).getMemo(); + assertEquals(memoText.getText(), "helpsdf"); + + // Empty memo_text assertTrue(transactionsPage.getRecords().get(2).getMemo() instanceof MemoText); memoText = (MemoText) transactionsPage.getRecords().get(2).getMemo(); @@ -73,6 +79,7 @@ public void testDeserialize() { " \"result_meta_xdr\": \"AAAAAAAAAAMAAAACAAAAAAAAAAMAAAAAAAAAABbxCy3mLg3hiTqX4VUEEp60pFOrJNxYM1JtxXTwXhY2AAAAAAvrwgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAAAAAAAAAGUcmKO5465JxTSLQOQljwk2SfqAJmZSG6JH6wtqpwhDeC2s5t4PNQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAEAAAADAAAAAAAAAAABlHJijueOuScU0i0DkJY8JNkn6gCZmUhuiR+sLaqcIQAAAAAL68IAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAAADAAAAAAAAAAAW8Qst5i4N4Yk6l+FVBBKetKRTqyTcWDNSbcV08F4WNg3gtrObeDzUAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAwAAAAAAAAAAAZRyYo7njrknFNItA5CWPCTZJ+oAmZlIbokfrC2qnCEAAAAAC+vCAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\n" + " \"memo_type\": \"text\",\n" + " \"memo\": \"hello world\",\n" + + " \"memo_bytes\": \"aGVsbG8gd29ybGQ=\",\n" + " \"signatures\": [\n" + " \"SsKlst02jsaBp35vXlI300Qbz39+HbwwNDX/gfM+MwaG/dFF7bgqpkKsDAsh/qJDiN3NOW695IZB9Mj+JAsHBA==\"\n" + " ]\n" + @@ -117,6 +124,7 @@ public void testDeserialize() { " \"result_meta_xdr\": \"AAAAAAAAAAMAAAACAAAAAAAAHqEAAAAAAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAAAAAAvrwgAAAB6hAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAAHqEAAAAAAAAAABbxCy3mLg3hiTqX4VUEEp60pFOrJNxYM1JtxXTwXhY2DeC2s4+MeHwAAAADAAAAAgAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAB6hAAAAAAAAAACzMOD+8iU8qo+qbTYewT8lxKE/s1cE3FOCVWxsqJ74GwAAAAAL68IAAAAeoQAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAB6hAAAAAAAAAAAW8Qst5i4N4Yk6l+FVBBKetKRTqyTcWDNSbcV08F4WNg3gtrODoLZ8AAAAAwAAAAIAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAeoQAAAAAAAAAASZcLtOTqf+cdbsq8HmLMkeqU06LN94UTWXuSBem5Z88AAAAAC+vCAAAAHqEAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAeoQAAAAAAAAAAFvELLeYuDeGJOpfhVQQSnrSkU6sk3FgzUm3FdPBeFjYN4Lazd7T0fAAAAAMAAAACAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAA=\",\n" + " \"memo_type\": \"text\",\n" + " \"memo\": \"testpool,faucet,sdf\",\n" + + " \"memo_bytes\": \"dGVzdHBvb2wsZmF1Y2V0LHNkZg==\",\n" + " \"signatures\": [\n" + " \"NIEHl40QqRYihBtnVhBpsm4TmZb8AqGsdPHD0feb+xqL3RhxpmWwB7e5472MTGNJkXbz1lxHi9zTAXDWn9bIBw==\"\n" + " ]\n" + @@ -160,6 +168,7 @@ public void testDeserialize() { " \"result_xdr\": \"AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAA=\",\n" + " \"result_meta_xdr\": \"AAAAAAAAAAEAAAABAAAAAQAAHq8AAAAAAAAAABbxCy3mLg3hiTqX4VUEEp60pFOrJNxYM1JtxXTwXhY2DeC2s3e09BgAAAADAAAAAwAAAAAAAAABAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\",\n" + " \"memo_type\": \"text\",\n" + + " \"memo_bytes\": \"\",\n" + " \"signatures\": [\n" + " \"1sNxaxHezfPxDOpq1x1z+TYvgtdYkwDp5dpzx1teQB1zimkfbmKwYLk7C8bTpXC9zcJ7f+vathwE/e/GHHXwDw==\"\n" + " ]\n" + @@ -203,6 +212,7 @@ public void testDeserialize() { " \"result_xdr\": \"AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAEAAAAAH6Ue1GOPj6Hb/ROPyIFCJpQPMujihEIvJSfK0UfMDIgAAK11sXJ6SgAAAAA=\",\n" + " \"result_meta_xdr\": \"AAAAAAAAAAEAAAABAAAAAQAAHrcAAAAAAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAACtdb1ePEoAAB6hAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\",\n" + " \"memo_type\": \"text\",\n" + + " \"memo_bytes\": \"\",\n" + " \"signatures\": [\n" + " \"UQc3yHhlEA5DbWt8xu8R9EqujHH2uSvHeErZdfWPqY3uooA0qbPA8yVxFxvnLOyQ+qhYKy6pf8yRRgt8Nl7tBg==\"\n" + " ]\n" + @@ -246,6 +256,7 @@ public void testDeserialize() { " \"result_xdr\": \"AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAEAAAAAH6Ue1GOPj6Hb/ROPyIFCJpQPMujihEIvJSfK0UfMDIgAAK11sXTKRwAAAAA=\",\n" + " \"result_meta_xdr\": \"AAAAAAAAAAEAAAABAAAAAQAAHr8AAAAAAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAAFa627TBpEAAB6hAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\",\n" + " \"memo_type\": \"text\",\n" + + " \"memo_bytes\": \"\",\n" + " \"signatures\": [\n" + " \"YLL32xpSbpqVchHKnQ02HY4VILJunUkY8M3alv/4XDTv9fvLiU8Sn8HpYhXXfRr7Z5j7lPe6NuIprpMhxRo3BA==\"\n" + " ]\n" + @@ -289,6 +300,7 @@ public void testDeserialize() { " \"result_xdr\": \"AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAEAAAAAH6Ue1GOPj6Hb/ROPyIFCJpQPMujihEIvJSfK0UfMDIgAAK1+KLf6bgAAAAA=\",\n" + " \"result_meta_xdr\": \"AAAAAAAAAAEAAAABAAAAAQAAHsIAAAAAAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAAIIaZeLAP8AAB6hAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\",\n" + " \"memo_type\": \"text\",\n" + + " \"memo_bytes\": \"\",\n" + " \"signatures\": [\n" + " \"I8CjxDCxl70UDSzP914x2GW7r/OuTtKCQEbbYCm+2kRMGGcaRfi2tIUPPyxWUqq/B6oZRWC+NTIzQBwfHJdlDA==\"\n" + " ]\n" + @@ -332,6 +344,7 @@ public void testDeserialize() { " \"result_xdr\": \"AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=\",\n" + " \"result_meta_xdr\": \"AAAAAAAAAAEAAAACAAAAAQAAHzYAAAAAAAAAABbxCy3mLg3hiTqX4VUEEp60pFOrJNxYM1JtxXTwXhY2AAAABJwsBgAAAAADAAAABwAAAAAAAAABAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAAHzYAAAAAAAAAAEmXC7Tk6n/nHW7KvB5izJHqlNOizfeFE1l7kgXpuWfPDeC2rud0rogAAB6hAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\",\n" + " \"memo_type\": \"text\",\n" + + " \"memo_bytes\": \"\",\n" + " \"signatures\": [\n" + " \"NkV853/bo6msX7/TKL1m1+4de4vhO7fp4Ci4ZwBLuIerqX5ozLbPf0il3/JEBBQd1HtpyS5ZMHhvbqEQTLXHAA==\"\n" + " ]\n" + diff --git a/src/test/java/org/stellar/sdk/xdr/InflationDecodeTest.java b/src/test/java/org/stellar/sdk/xdr/InflationDecodeTest.java index 4506e33b4..be34a716f 100644 --- a/src/test/java/org/stellar/sdk/xdr/InflationDecodeTest.java +++ b/src/test/java/org/stellar/sdk/xdr/InflationDecodeTest.java @@ -11,7 +11,7 @@ public class InflationDecodeTest { @Test public void testDecodeInflationOperation() throws Exception { - org.stellar.sdk.Transaction tx = org.stellar.sdk.Transaction.fromEnvelopeXdr( + org.stellar.sdk.Transaction tx = (org.stellar.sdk.Transaction) org.stellar.sdk.Transaction.fromEnvelopeXdr( "AAAAAALC+FwxReetNDfMNvY5LOS1qSe7QqrfQPS28dnIV95NAAAAZAAAAAAAAATSAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAA=", Network.TESTNET ); diff --git a/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java b/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java index 442e31879..9f14070df 100644 --- a/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java +++ b/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java @@ -22,7 +22,7 @@ public void testDecodeTxBody() throws IOException { byte[] bytes = base64Encoding.decode(txBody); TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - assertEquals(new Long(2373025265623291L), transactionEnvelope.getTx().getSeqNum().getSequenceNumber().getInt64()); + assertEquals(new Long(2373025265623291L), transactionEnvelope.getV0().getTx().getSeqNum().getSequenceNumber().getInt64()); } @Test @@ -56,8 +56,8 @@ public void testTransactionEnvelopeWithMemo() throws IOException { byte[] bytes = base64Encoding.decode(transactionEnvelopeToDecode); TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - assertEquals(1, transactionEnvelope.getTx().getOperations().length); - assertTrue(Arrays.equals(new byte[]{'G', 'O', 'L', 'D'}, transactionEnvelope.getTx().getOperations()[0].getBody().getPaymentOp().getAsset().getAlphaNum4().getAssetCode().getAssetCode4())); + assertEquals(1, transactionEnvelope.getV0().getTx().getOperations().length); + assertTrue(Arrays.equals(new byte[]{'G', 'O', 'L', 'D'}, transactionEnvelope.getV0().getTx().getOperations()[0].getBody().getPaymentOp().getAsset().getAlphaNum4().getAssetCode().getAssetCode4())); } @Test diff --git a/xdr/Stellar-ledger-entries.x b/xdr/Stellar-ledger-entries.x index b511de272..433d4b656 100644 --- a/xdr/Stellar-ledger-entries.x +++ b/xdr/Stellar-ledger-entries.x @@ -160,11 +160,15 @@ struct AccountEntry enum TrustLineFlags { // issuer has authorized account to perform transactions with its credit - AUTHORIZED_FLAG = 1 + AUTHORIZED_FLAG = 1, + // issuer has authorized account to maintain and reduce liabilities for its + // credit + AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG = 2 }; // mask for all trustline flags const MASK_TRUSTLINE_FLAGS = 1; +const MASK_TRUSTLINE_FLAGS_V13 = 3; struct TrustLineEntry { @@ -287,9 +291,11 @@ struct LedgerEntry // the respective envelopes enum EnvelopeType { + ENVELOPE_TYPE_TX_V0 = 0, ENVELOPE_TYPE_SCP = 1, ENVELOPE_TYPE_TX = 2, ENVELOPE_TYPE_AUTH = 3, - ENVELOPE_TYPE_SCPVALUE = 4 + ENVELOPE_TYPE_SCPVALUE = 4, + ENVELOPE_TYPE_TX_FEE_BUMP = 5 }; } diff --git a/xdr/Stellar-ledger.x b/xdr/Stellar-ledger.x index b8eafa2a8..6c2bb88e4 100644 --- a/xdr/Stellar-ledger.x +++ b/xdr/Stellar-ledger.x @@ -366,7 +366,6 @@ struct LedgerCloseMetaV0 union LedgerCloseMeta switch (int v) { case 0: - LedgerCloseMetaV0 v0; + LedgerCloseMetaV0 v0; }; - } diff --git a/xdr/Stellar-overlay.x b/xdr/Stellar-overlay.x index 5e59acd7d..a4ab6b077 100644 --- a/xdr/Stellar-overlay.x +++ b/xdr/Stellar-overlay.x @@ -172,8 +172,8 @@ struct TopologyResponseBody union SurveyResponseBody switch (SurveyMessageCommandType type) { - case SURVEY_TOPOLOGY: - TopologyResponseBody topologyResponseBody; +case SURVEY_TOPOLOGY: + TopologyResponseBody topologyResponseBody; }; union StellarMessage switch (MessageType type) @@ -220,10 +220,10 @@ union AuthenticatedMessage switch (uint32 v) { case 0: struct -{ - uint64 sequence; - StellarMessage message; - HmacSha256Mac mac; + { + uint64 sequence; + StellarMessage message; + HmacSha256Mac mac; } v0; }; } diff --git a/xdr/Stellar-transaction.x b/xdr/Stellar-transaction.x index 74d7a9baf..bee28cbad 100644 --- a/xdr/Stellar-transaction.x +++ b/xdr/Stellar-transaction.x @@ -7,6 +7,19 @@ namespace stellar { +// Source or destination of a payment operation +union MuxedAccount switch (CryptoKeyType type) +{ +case KEY_TYPE_ED25519: + uint256 ed25519; +case KEY_TYPE_MUXED_ED25519: + struct + { + uint64 id; + uint256 ed25519; + } med25519; +}; + struct DecoratedSignature { SignatureHint hint; // last 4 bytes of the public key, used as a hint @@ -55,9 +68,9 @@ struct CreateAccountOp */ struct PaymentOp { - AccountID destination; // recipient of the payment - Asset asset; // what they end up with - int64 amount; // amount they end up with + MuxedAccount destination; // recipient of the payment + Asset asset; // what they end up with + int64 amount; // amount they end up with }; /* PathPaymentStrictReceive @@ -78,9 +91,9 @@ struct PathPaymentStrictReceiveOp // send (excluding fees). // The operation will fail if can't be met - AccountID destination; // recipient of the payment - Asset destAsset; // what they end up with - int64 destAmount; // amount they end up with + MuxedAccount destination; // recipient of the payment + Asset destAsset; // what they end up with + int64 destAmount; // amount they end up with Asset path<5>; // additional hops it must go through to get there }; @@ -101,16 +114,15 @@ struct PathPaymentStrictSendOp Asset sendAsset; // asset we pay with int64 sendAmount; // amount of sendAsset to send (excluding fees) - AccountID destination; // recipient of the payment - Asset destAsset; // what they end up with - int64 destMin; // the minimum amount of dest asset to - // be received - // The operation will fail if it can't be met + MuxedAccount destination; // recipient of the payment + Asset destAsset; // what they end up with + int64 destMin; // the minimum amount of dest asset to + // be received + // The operation will fail if it can't be met Asset path<5>; // additional hops it must go through to get there }; - /* Creates, updates or deletes an offer Threshold: med @@ -232,7 +244,8 @@ struct AllowTrustOp } asset; - bool authorize; + // 0, or any bitwise combination of TrustLineFlags + uint32 authorize; }; /* Inflation @@ -285,7 +298,7 @@ struct Operation // sourceAccount is the account used to run the operation // if not set, the runtime defaults to "sourceAccount" specified at // the transaction level - AccountID* sourceAccount; + MuxedAccount* sourceAccount; union switch (OperationType type) { @@ -306,7 +319,7 @@ struct Operation case ALLOW_TRUST: AllowTrustOp allowTrustOp; case ACCOUNT_MERGE: - AccountID destination; + MuxedAccount destination; case INFLATION: void; case MANAGE_DATA: @@ -353,6 +366,36 @@ struct TimeBounds // maximum number of operations per transaction const MAX_OPS_PER_TX = 100; +// TransactionV0 is a transaction with the AccountID discriminant stripped off, +// leaving a raw ed25519 public key to identify the source account. This is used +// for backwards compatibility starting from the protocol 12/13 boundary. If an +// "old-style" TransactionEnvelope containing a Transaction is parsed with this +// XDR definition, it will be parsed as a "new-style" TransactionEnvelope +// containing a TransactionV0. +struct TransactionV0 +{ + uint256 sourceAccountEd25519; + uint32 fee; + SequenceNumber seqNum; + TimeBounds* timeBounds; + Memo memo; + Operation operations; + union switch (int v) + { + case 0: + void; + } + ext; +}; + +struct TransactionV0Envelope +{ + TransactionV0 tx; + /* Each decorated signature is a signature over the SHA256 hash of + * a TransactionSignaturePayload */ + DecoratedSignature signatures<20>; +}; + /* a transaction is a container for a set of operations - is executed by an account - fees are collected from the account @@ -363,7 +406,7 @@ const MAX_OPS_PER_TX = 100; struct Transaction { // account used to run the transaction - AccountID sourceAccount; + MuxedAccount sourceAccount; // the fee the sourceAccount will pay uint32 fee; @@ -387,27 +430,65 @@ struct Transaction ext; }; -struct TransactionSignaturePayload +struct TransactionV1Envelope { - Hash networkId; + Transaction tx; + /* Each decorated signature is a signature over the SHA256 hash of + * a TransactionSignaturePayload */ + DecoratedSignature signatures<20>; +}; + +struct FeeBumpTransaction +{ + MuxedAccount feeSource; + int64 fee; union switch (EnvelopeType type) { case ENVELOPE_TYPE_TX: - Transaction tx; - /* All other values of type are invalid */ + TransactionV1Envelope v1; } - taggedTransaction; + innerTx; + union switch (int v) + { + case 0: + void; + } + ext; }; -/* A TransactionEnvelope wraps a transaction with signatures. */ -struct TransactionEnvelope +struct FeeBumpTransactionEnvelope { - Transaction tx; + FeeBumpTransaction tx; /* Each decorated signature is a signature over the SHA256 hash of * a TransactionSignaturePayload */ DecoratedSignature signatures<20>; }; +/* A TransactionEnvelope wraps a transaction with signatures. */ +union TransactionEnvelope switch (EnvelopeType type) +{ +case ENVELOPE_TYPE_TX_V0: + TransactionV0Envelope v0; +case ENVELOPE_TYPE_TX: + TransactionV1Envelope v1; +case ENVELOPE_TYPE_TX_FEE_BUMP: + FeeBumpTransactionEnvelope feeBump; +}; + +struct TransactionSignaturePayload +{ + Hash networkId; + union switch (EnvelopeType type) + { + // Backwards Compatibility: Use ENVELOPE_TYPE_TX to sign ENVELOPE_TYPE_TX_V0 + case ENVELOPE_TYPE_TX: + Transaction tx; + case ENVELOPE_TYPE_TX_FEE_BUMP: + FeeBumpTransaction feeBump; + } + taggedTransaction; +}; + /* Operation Results section */ /* This result is used when offers are taken during an operation */ @@ -484,18 +565,27 @@ enum PathPaymentStrictReceiveResultCode PATH_PAYMENT_STRICT_RECEIVE_SUCCESS = 0, // success // codes considered as "failure" for the operation - PATH_PAYMENT_STRICT_RECEIVE_MALFORMED = -1, // bad input - PATH_PAYMENT_STRICT_RECEIVE_UNDERFUNDED = -2, // not enough funds in source account - PATH_PAYMENT_STRICT_RECEIVE_SRC_NO_TRUST = -3, // no trust line on source account - PATH_PAYMENT_STRICT_RECEIVE_SRC_NOT_AUTHORIZED = -4, // source not authorized to transfer - PATH_PAYMENT_STRICT_RECEIVE_NO_DESTINATION = -5, // destination account does not exist - PATH_PAYMENT_STRICT_RECEIVE_NO_TRUST = -6, // dest missing a trust line for asset - PATH_PAYMENT_STRICT_RECEIVE_NOT_AUTHORIZED = -7, // dest not authorized to hold asset - PATH_PAYMENT_STRICT_RECEIVE_LINE_FULL = -8, // dest would go above their limit - PATH_PAYMENT_STRICT_RECEIVE_NO_ISSUER = -9, // missing issuer on one asset - PATH_PAYMENT_STRICT_RECEIVE_TOO_FEW_OFFERS = -10, // not enough offers to satisfy path - PATH_PAYMENT_STRICT_RECEIVE_OFFER_CROSS_SELF = -11, // would cross one of its own offers - PATH_PAYMENT_STRICT_RECEIVE_OVER_SENDMAX = -12 // could not satisfy sendmax + PATH_PAYMENT_STRICT_RECEIVE_MALFORMED = -1, // bad input + PATH_PAYMENT_STRICT_RECEIVE_UNDERFUNDED = + -2, // not enough funds in source account + PATH_PAYMENT_STRICT_RECEIVE_SRC_NO_TRUST = + -3, // no trust line on source account + PATH_PAYMENT_STRICT_RECEIVE_SRC_NOT_AUTHORIZED = + -4, // source not authorized to transfer + PATH_PAYMENT_STRICT_RECEIVE_NO_DESTINATION = + -5, // destination account does not exist + PATH_PAYMENT_STRICT_RECEIVE_NO_TRUST = + -6, // dest missing a trust line for asset + PATH_PAYMENT_STRICT_RECEIVE_NOT_AUTHORIZED = + -7, // dest not authorized to hold asset + PATH_PAYMENT_STRICT_RECEIVE_LINE_FULL = + -8, // dest would go above their limit + PATH_PAYMENT_STRICT_RECEIVE_NO_ISSUER = -9, // missing issuer on one asset + PATH_PAYMENT_STRICT_RECEIVE_TOO_FEW_OFFERS = + -10, // not enough offers to satisfy path + PATH_PAYMENT_STRICT_RECEIVE_OFFER_CROSS_SELF = + -11, // would cross one of its own offers + PATH_PAYMENT_STRICT_RECEIVE_OVER_SENDMAX = -12 // could not satisfy sendmax }; struct SimplePaymentResult @@ -527,18 +617,26 @@ enum PathPaymentStrictSendResultCode PATH_PAYMENT_STRICT_SEND_SUCCESS = 0, // success // codes considered as "failure" for the operation - PATH_PAYMENT_STRICT_SEND_MALFORMED = -1, // bad input - PATH_PAYMENT_STRICT_SEND_UNDERFUNDED = -2, // not enough funds in source account - PATH_PAYMENT_STRICT_SEND_SRC_NO_TRUST = -3, // no trust line on source account - PATH_PAYMENT_STRICT_SEND_SRC_NOT_AUTHORIZED = -4, // source not authorized to transfer - PATH_PAYMENT_STRICT_SEND_NO_DESTINATION = -5, // destination account does not exist - PATH_PAYMENT_STRICT_SEND_NO_TRUST = -6, // dest missing a trust line for asset - PATH_PAYMENT_STRICT_SEND_NOT_AUTHORIZED = -7, // dest not authorized to hold asset - PATH_PAYMENT_STRICT_SEND_LINE_FULL = -8, // dest would go above their limit - PATH_PAYMENT_STRICT_SEND_NO_ISSUER = -9, // missing issuer on one asset - PATH_PAYMENT_STRICT_SEND_TOO_FEW_OFFERS = -10, // not enough offers to satisfy path - PATH_PAYMENT_STRICT_SEND_OFFER_CROSS_SELF = -11, // would cross one of its own offers - PATH_PAYMENT_STRICT_SEND_UNDER_DESTMIN = -12 // could not satisfy destMin + PATH_PAYMENT_STRICT_SEND_MALFORMED = -1, // bad input + PATH_PAYMENT_STRICT_SEND_UNDERFUNDED = + -2, // not enough funds in source account + PATH_PAYMENT_STRICT_SEND_SRC_NO_TRUST = + -3, // no trust line on source account + PATH_PAYMENT_STRICT_SEND_SRC_NOT_AUTHORIZED = + -4, // source not authorized to transfer + PATH_PAYMENT_STRICT_SEND_NO_DESTINATION = + -5, // destination account does not exist + PATH_PAYMENT_STRICT_SEND_NO_TRUST = + -6, // dest missing a trust line for asset + PATH_PAYMENT_STRICT_SEND_NOT_AUTHORIZED = + -7, // dest not authorized to hold asset + PATH_PAYMENT_STRICT_SEND_LINE_FULL = -8, // dest would go above their limit + PATH_PAYMENT_STRICT_SEND_NO_ISSUER = -9, // missing issuer on one asset + PATH_PAYMENT_STRICT_SEND_TOO_FEW_OFFERS = + -10, // not enough offers to satisfy path + PATH_PAYMENT_STRICT_SEND_OFFER_CROSS_SELF = + -11, // would cross one of its own offers + PATH_PAYMENT_STRICT_SEND_UNDER_DESTMIN = -12 // could not satisfy destMin }; union PathPaymentStrictSendResult switch (PathPaymentStrictSendResultCode code) @@ -563,21 +661,25 @@ enum ManageSellOfferResultCode MANAGE_SELL_OFFER_SUCCESS = 0, // codes considered as "failure" for the operation - MANAGE_SELL_OFFER_MALFORMED = -1, // generated offer would be invalid - MANAGE_SELL_OFFER_SELL_NO_TRUST = -2, // no trust line for what we're selling - MANAGE_SELL_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying + MANAGE_SELL_OFFER_MALFORMED = -1, // generated offer would be invalid + MANAGE_SELL_OFFER_SELL_NO_TRUST = + -2, // no trust line for what we're selling + MANAGE_SELL_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying MANAGE_SELL_OFFER_SELL_NOT_AUTHORIZED = -4, // not authorized to sell MANAGE_SELL_OFFER_BUY_NOT_AUTHORIZED = -5, // not authorized to buy - MANAGE_SELL_OFFER_LINE_FULL = -6, // can't receive more of what it's buying - MANAGE_SELL_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell - MANAGE_SELL_OFFER_CROSS_SELF = -8, // would cross an offer from the same user + MANAGE_SELL_OFFER_LINE_FULL = -6, // can't receive more of what it's buying + MANAGE_SELL_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell + MANAGE_SELL_OFFER_CROSS_SELF = + -8, // would cross an offer from the same user MANAGE_SELL_OFFER_SELL_NO_ISSUER = -9, // no issuer for what we're selling MANAGE_SELL_OFFER_BUY_NO_ISSUER = -10, // no issuer for what we're buying // update errors - MANAGE_SELL_OFFER_NOT_FOUND = -11, // offerID does not match an existing offer + MANAGE_SELL_OFFER_NOT_FOUND = + -11, // offerID does not match an existing offer - MANAGE_SELL_OFFER_LOW_RESERVE = -12 // not enough funds to create a new Offer + MANAGE_SELL_OFFER_LOW_RESERVE = + -12 // not enough funds to create a new Offer }; enum ManageOfferEffect @@ -624,14 +726,15 @@ enum ManageBuyOfferResultCode MANAGE_BUY_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying MANAGE_BUY_OFFER_SELL_NOT_AUTHORIZED = -4, // not authorized to sell MANAGE_BUY_OFFER_BUY_NOT_AUTHORIZED = -5, // not authorized to buy - MANAGE_BUY_OFFER_LINE_FULL = -6, // can't receive more of what it's buying - MANAGE_BUY_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell - MANAGE_BUY_OFFER_CROSS_SELF = -8, // would cross an offer from the same user + MANAGE_BUY_OFFER_LINE_FULL = -6, // can't receive more of what it's buying + MANAGE_BUY_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell + MANAGE_BUY_OFFER_CROSS_SELF = -8, // would cross an offer from the same user MANAGE_BUY_OFFER_SELL_NO_ISSUER = -9, // no issuer for what we're selling MANAGE_BUY_OFFER_BUY_NO_ISSUER = -10, // no issuer for what we're buying // update errors - MANAGE_BUY_OFFER_NOT_FOUND = -11, // offerID does not match an existing offer + MANAGE_BUY_OFFER_NOT_FOUND = + -11, // offerID does not match an existing offer MANAGE_BUY_OFFER_LOW_RESERVE = -12 // not enough funds to create a new Offer }; @@ -683,7 +786,7 @@ enum ChangeTrustResultCode // cannot create with a limit of 0 CHANGE_TRUST_LOW_RESERVE = -4, // not enough funds to create a new trust line, - CHANGE_TRUST_SELF_NOT_ALLOWED = -5 // trusting self is not allowed + CHANGE_TRUST_SELF_NOT_ALLOWED = -5 // trusting self is not allowed }; union ChangeTrustResult switch (ChangeTrustResultCode code) @@ -811,9 +914,9 @@ enum OperationResultCode { opINNER = 0, // inner object result is valid - opBAD_AUTH = -1, // too few valid signatures / wrong network - opNO_ACCOUNT = -2, // source account was not found - opNOT_SUPPORTED = -3, // operation not supported at this time + opBAD_AUTH = -1, // too few valid signatures / wrong network + opNO_ACCOUNT = -2, // source account was not found + opNOT_SUPPORTED = -3, // operation not supported at this time opTOO_MANY_SUBENTRIES = -4, // max number of subentries already reached opEXCEEDED_WORK_LIMIT = -5 // operation did too much work }; @@ -848,7 +951,7 @@ case opINNER: case BUMP_SEQUENCE: BumpSequenceResult bumpSeqResult; case MANAGE_BUY_OFFER: - ManageBuyOfferResult manageBuyOfferResult; + ManageBuyOfferResult manageBuyOfferResult; case PATH_PAYMENT_STRICT_SEND: PathPaymentStrictSendResult pathPaymentStrictSendResult; } @@ -859,7 +962,8 @@ default: enum TransactionResultCode { - txSUCCESS = 0, // all operations succeeded + txFEE_BUMP_INNER_SUCCESS = 1, // fee bump inner transaction succeeded + txSUCCESS = 0, // all operations succeeded txFAILED = -1, // one of the operations failed (none were applied) @@ -873,7 +977,54 @@ enum TransactionResultCode txNO_ACCOUNT = -8, // source account not found txINSUFFICIENT_FEE = -9, // fee is too small txBAD_AUTH_EXTRA = -10, // unused signatures attached to transaction - txINTERNAL_ERROR = -11 // an unknown error occured + txINTERNAL_ERROR = -11, // an unknown error occured + + txNOT_SUPPORTED = -12, // transaction type not supported + txFEE_BUMP_INNER_FAILED = -13 // fee bump inner transaction failed +}; + +// InnerTransactionResult must be binary compatible with TransactionResult +// because it is be used to represent the result of a Transaction. +struct InnerTransactionResult +{ + // Always 0. Here for binary compatibility. + int64 feeCharged; + + union switch (TransactionResultCode code) + { + // txFEE_BUMP_INNER_SUCCESS is not included + case txSUCCESS: + case txFAILED: + OperationResult results<>; + case txTOO_EARLY: + case txTOO_LATE: + case txMISSING_OPERATION: + case txBAD_SEQ: + case txBAD_AUTH: + case txINSUFFICIENT_BALANCE: + case txNO_ACCOUNT: + case txINSUFFICIENT_FEE: + case txBAD_AUTH_EXTRA: + case txINTERNAL_ERROR: + case txNOT_SUPPORTED: + // txFEE_BUMP_INNER_FAILED is not included + void; + } + result; + + // reserved for future use + union switch (int v) + { + case 0: + void; + } + ext; +}; + +struct InnerTransactionResultPair +{ + Hash transactionHash; // hash of the inner transaction + InnerTransactionResult result; // result for the inner transaction }; struct TransactionResult @@ -882,6 +1033,9 @@ struct TransactionResult union switch (TransactionResultCode code) { + case txFEE_BUMP_INNER_SUCCESS: + case txFEE_BUMP_INNER_FAILED: + InnerTransactionResultPair innerResultPair; case txSUCCESS: case txFAILED: OperationResult results<>; diff --git a/xdr/Stellar-types.x b/xdr/Stellar-types.x index b7a616aef..8f7d5c206 100644 --- a/xdr/Stellar-types.x +++ b/xdr/Stellar-types.x @@ -18,7 +18,10 @@ enum CryptoKeyType { KEY_TYPE_ED25519 = 0, KEY_TYPE_PRE_AUTH_TX = 1, - KEY_TYPE_HASH_X = 2 + KEY_TYPE_HASH_X = 2, + // MUXED enum values for supported type are derived from the enum values + // above by ORing them with 0x100 + KEY_TYPE_MUXED_ED25519 = 0x100 }; enum PublicKeyType @@ -60,22 +63,21 @@ typedef PublicKey NodeID; struct Curve25519Secret { - opaque key[32]; + opaque key[32]; }; struct Curve25519Public { - opaque key[32]; + opaque key[32]; }; struct HmacSha256Key { - opaque key[32]; + opaque key[32]; }; struct HmacSha256Mac { - opaque mac[32]; + opaque mac[32]; }; - }