From 610b56746a501d88e4ed57d97522cd656c7b916d Mon Sep 17 00:00:00 2001 From: tamirms Date: Mon, 4 May 2020 19:57:31 +0200 Subject: [PATCH] Implement FeeBumpTransaction builder, update SEP10 and SEP29 (#278) Implement FeeBumpTransaction builder Update SEP 10 implementation to reject fee bump transactions and muxed accounts Update SEP 29 implementation to support fee bump transactions --- .../org/stellar/sdk/AbstractTransaction.java | 134 +++ .../org/stellar/sdk/FeeBumpTransaction.java | 204 +++++ .../java/org/stellar/sdk/Sep10Challenge.java | 27 +- src/main/java/org/stellar/sdk/Server.java | 105 ++- .../java/org/stellar/sdk/Transaction.java | 240 ++--- .../stellar/sdk/FeeBumpTransactionTest.java | 197 +++++ .../java/org/stellar/sdk/OperationTest.java | 1 + .../org/stellar/sdk/Sep10ChallengeTest.java | 829 ++++++++++-------- src/test/java/org/stellar/sdk/ServerTest.java | 121 ++- .../java/org/stellar/sdk/TransactionTest.java | 81 +- .../stellar/sdk/xdr/InflationDecodeTest.java | 2 +- 11 files changed, 1336 insertions(+), 605 deletions(-) create mode 100644 src/main/java/org/stellar/sdk/AbstractTransaction.java create mode 100644 src/main/java/org/stellar/sdk/FeeBumpTransaction.java create mode 100644 src/test/java/org/stellar/sdk/FeeBumpTransactionTest.java 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/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/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/Transaction.java b/src/main/java/org/stellar/sdk/Transaction.java index 71742939a..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,121 +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); - } - - /** - * Returns transaction hash. - */ - public byte[] hash() { - return Util.hash(this.signatureBase()); - } - - private org.stellar.sdk.xdr.Transaction convertV0Tx(TransactionV0 v0Tx) { - org.stellar.sdk.xdr.Transaction v1Tx = new org.stellar.sdk.xdr.Transaction(); - - v1Tx.setMemo(v0Tx.getMemo()); - v1Tx.setFee(v0Tx.getFee()); - v1Tx.setOperations(v0Tx.getOperations()); - v1Tx.setSeqNum(v0Tx.getSeqNum()); - v1Tx.setTimeBounds(v0Tx.getTimeBounds()); - - org.stellar.sdk.xdr.Transaction.TransactionExt ext = new org.stellar.sdk.xdr.Transaction.TransactionExt(); - ext.setDiscriminant(0); - v1Tx.setExt(ext); - - MuxedAccount sourceAccount = new MuxedAccount(); - sourceAccount.setDiscriminant(CryptoKeyType.KEY_TYPE_ED25519); - sourceAccount.setEd25519(v0Tx.getSourceAccountEd25519()); - v1Tx.setSourceAccount(sourceAccount); - - return v1Tx; + // setEnvelopeType is only used in tests which is why this method is package protected + void setEnvelopeType(EnvelopeType envelopeType) { + this.envelopeType = envelopeType; } - /** - * 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); - convertV0Tx(this.toXdr()).encode(xdrOutputStream); - 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; @@ -143,10 +80,6 @@ public long getSequenceNumber() { return mSequenceNumber; } - public List getSignatures() { - return mSignatures; - } - public Memo getMemo() { return mMemo; } @@ -161,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; } @@ -178,7 +111,7 @@ public Operation[] getOperations() { private TransactionV0 toXdr() { // fee Uint32 fee = new Uint32(); - fee.setUint32(mFee); + fee.setUint32((int)mFee); // sequenceNumber Int64 sequenceNumberUint = new Int64(); sequenceNumberUint.setInt64(mSequenceNumber); @@ -204,18 +137,36 @@ private TransactionV0 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() { - TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - return fromEnvelopeXdr(transactionEnvelope, network); + // 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); + + + 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; } public static Transaction fromV0EnvelopeXdr(TransactionV0Envelope envelope, Network network) { @@ -274,79 +225,48 @@ public static Transaction fromV1EnvelopeXdr(TransactionV1Envelope envelope, Netw return transaction; } - /** - * Creates a Transaction instance from previously build TransactionEnvelope - * @param envelope - * @return - */ - public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope, Network network) { - switch (envelope.getDiscriminant()) { - case ENVELOPE_TYPE_TX: - return fromV1EnvelopeXdr(envelope.getV1(), network); - case ENVELOPE_TYPE_TX_V0: - return fromV0EnvelopeXdr(envelope.getV0(), network); - default: - throw new IllegalArgumentException("transaction type is not supported: "+envelope.getDiscriminant()); - } - } - /** * Generates TransactionEnvelope XDR object. */ + @Override public TransactionEnvelope toEnvelopeXdr() { TransactionEnvelope xdr = new TransactionEnvelope(); - TransactionV0Envelope v0Envelope = new TransactionV0Envelope(); - xdr.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX_V0); - v0Envelope.setTx(this.toXdr()); - DecoratedSignature[] signatures = new DecoratedSignature[mSignatures.size()]; signatures = mSignatures.toArray(signatures); - v0Envelope.setSignatures(signatures); - xdr.setV0(v0Envelope); - return xdr; - } - - /** - * 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); + 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 @@ -358,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() { @@ -445,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; } @@ -463,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) { @@ -476,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, @@ -498,7 +416,8 @@ public int hashCode() { Arrays.hashCode(this.mOperations), this.mMemo, this.mTimeBounds, - this.mSignatures + this.mSignatures, + this.mNetwork ); } @@ -515,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/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 be1a5249c..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 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/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/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 );