From 22a9ab8e3b737a200cd94f573bc448e43bd69a69 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Sun, 26 May 2019 13:48:21 +0300 Subject: [PATCH 01/56] Add half of the code for the account linking api. 1. include tests(uncompleted yet. 2. getTransactionBuilder methods and TransactionBuilder class 3. Refactor Transaction class. 4. move 'addAppIdToMemo' method to utils class 5. increase timeout for the account creation listener in the sample app onboarding class --- .../java/kin/sdk/KinAccountTest.java | 1 + .../kin/sdk/KinAccountIntegrationTest.kt | 62 ++++++++++--- .../main/java/kin/sdk/AbstractKinAccount.java | 16 +++- kin-sdk/src/main/java/kin/sdk/KinAccount.java | 24 +++++ .../src/main/java/kin/sdk/KinAccountImpl.java | 12 ++- .../src/main/java/kin/sdk/Transaction.java | 58 ++++++------ .../main/java/kin/sdk/TransactionBuilder.java | 90 +++++++++++++++++++ .../main/java/kin/sdk/TransactionSender.java | 80 +++++++---------- kin-sdk/src/main/java/kin/sdk/Utils.java | 22 ++++- .../main/java/kin/sdk/sample/OnBoarding.java | 3 +- 10 files changed, 270 insertions(+), 98 deletions(-) create mode 100644 kin-sdk/src/main/java/kin/sdk/TransactionBuilder.java diff --git a/kin-sdk/src/androidTest/java/kin/sdk/KinAccountTest.java b/kin-sdk/src/androidTest/java/kin/sdk/KinAccountTest.java index c05be559..f93c7994 100644 --- a/kin-sdk/src/androidTest/java/kin/sdk/KinAccountTest.java +++ b/kin-sdk/src/androidTest/java/kin/sdk/KinAccountTest.java @@ -1,6 +1,7 @@ package kin.sdk; import static junit.framework.Assert.assertNull; + import android.support.test.InstrumentationRegistry; import java.math.BigDecimal; import kin.sdk.exception.AccountDeletedException; diff --git a/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index 0833bfbf..38fe0b2c 100644 --- a/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -2,9 +2,7 @@ package kin.sdk import android.support.test.InstrumentationRegistry import android.support.test.filters.LargeTest -import kin.base.Memo -import kin.base.MemoText -import kin.base.Server +import kin.base.* import kin.sdk.IntegConsts.TEST_NETWORK_URL import kin.sdk.exception.AccountNotFoundException import kin.sdk.exception.InsufficientFeeException @@ -82,7 +80,7 @@ class KinAccountIntegrationTest { val latch = CountDownLatch(1) - var listenerRegistration : ListenerRegistration? = null + var listenerRegistration: ListenerRegistration? = null listenerRegistration = kinAccount.addAccountCreationListener { listenerRegistration?.remove() latch.countDown() @@ -105,7 +103,7 @@ class KinAccountIntegrationTest { val latch = CountDownLatch(1) - var listenerRegistration : ListenerRegistration? = null + var listenerRegistration: ListenerRegistration? = null listenerRegistration = kinAccount.addAccountCreationListener { listenerRegistration?.remove() latch.countDown() @@ -156,7 +154,7 @@ class KinAccountIntegrationTest { val latch = CountDownLatch(1) - var listenerRegistration : ListenerRegistration? = null + var listenerRegistration: ListenerRegistration? = null listenerRegistration = kinAccountSender.addAccountCreationListener { listenerRegistration?.remove() latch.countDown() @@ -182,7 +180,7 @@ class KinAccountIntegrationTest { val latch = CountDownLatch(1) - var listenerRegistration : ListenerRegistration? = null + var listenerRegistration: ListenerRegistration? = null listenerRegistration = kinAccountReceiver.addAccountCreationListener { listenerRegistration?.remove() latch.countDown() @@ -207,7 +205,7 @@ class KinAccountIntegrationTest { val latch = CountDownLatch(1) - var listenerRegistration : ListenerRegistration? = null + var listenerRegistration: ListenerRegistration? = null listenerRegistration = kinAccountReceiver.addAccountCreationListener { listenerRegistration?.remove() latch.countDown() @@ -234,7 +232,7 @@ class KinAccountIntegrationTest { val latch = CountDownLatch(1) - var listenerRegistration : ListenerRegistration? = null + var listenerRegistration: ListenerRegistration? = null listenerRegistration = kinAccountSender.addAccountCreationListener { listenerRegistration?.remove() latch.countDown() @@ -260,7 +258,7 @@ class KinAccountIntegrationTest { val (kinAccountSender, kinAccountReceiver) = onboardAccounts() expectedEx.expect(InsufficientFeeException::class.java) - val minFee : Int = Math.toIntExact(kinClient.minimumFeeSync) + val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), minFee - 1) kinAccountSender.sendTransactionSync(transaction) } @@ -271,7 +269,7 @@ class KinAccountIntegrationTest { fun sendWhitelistTransaction_FeeNotReduce() { val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100) - val minFee : Int = Math.toIntExact(kinClient.minimumFeeSync) + val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("20"), minFee + 100000) val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction) @@ -285,7 +283,7 @@ class KinAccountIntegrationTest { fun sendWhitelistTransaction_Success() { val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100) - val minFee : Int = Math.toIntExact(kinClient.minimumFeeSync) + val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("20"), minFee) val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction) @@ -393,7 +391,7 @@ class KinAccountIntegrationTest { } listenerRegistration.remove() - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee,null) + val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee, null) kinAccountSender.sendTransactionSync(transaction) latch.await(timeoutDurationSecondsLong, TimeUnit.SECONDS) } @@ -409,6 +407,42 @@ class KinAccountIntegrationTest { kinAccountSender.sendTransactionSync(transaction) } + @Test + @Throws(Exception::class) + fun accountLinking_SendTransaction_TransactionSuccess() { + val controlledAccount = kinClient.addAccount() + val masterAccount = kinClient.addAccount() + val destinationAccount = kinClient.addAccount() + onboardSingleAccount(destinationAccount, 100) + Thread.sleep(15000) // sleep for 15 seconds in order to wait for all the account ot be created and funded + linkAccount(controlledAccount, masterAccount) + val transaction = masterAccount.buildTransactionSync(destinationAccount.publicAddress.orEmpty(), + BigDecimal("21.123"), fee) + val transactionId = kinAccountSender.sendTransactionSync(transaction) + assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin))) + assertThat(kinAccountReceiver.balanceSync.value(), equalTo(BigDecimal("21.12300"))) + } + + private fun linkAccount(controlledAccount: KinAccount, masterAccount: KinAccount) { + onboardSingleAccount(controlledAccount, 100) + onboardSingleAccount(masterAccount, 100) + + val transactionBuilder = controlledAccount.transactionBuilderSync + val signerKey = Signer.ed25519PublicKey(KeyPair.fromAccountId(masterAccount.publicAddress)) + val managerDataKey = controlledAccount.publicAddress + val managerDataValue = "some package id" + val transaction = transactionBuilder.setFee(100) + .setMemo("test", "account linking") + .addOperation(SetOptionsOperation.Builder().setSigner(signerKey, 1).build()) + .addOperation(ManageDataOperation.Builder(managerDataKey, managerDataValue.toByteArray()).build()) + .build() + // simulate transaction getting a transaction envelope and decode it. + val transactionEnvelope = transaction.transactionEnvelope + val externalTransaction = Transaction.decodeTransaction(transactionEnvelope) + // Sending the linking transaction from the master account + val transactionId = masterAccount.sendTransactionSync(externalTransaction) + } + private fun onboardAccounts(senderFundAmount: Int = 0, receiverFundAmount: Int = 0): Pair { val kinAccountSender = kinClient.addAccount() @@ -421,7 +455,7 @@ class KinAccountIntegrationTest { private fun onboardSingleAccount(account: KinAccount, fundAmount: Int) { val latch = CountDownLatch(1) - var listenerRegistration : ListenerRegistration? = null + var listenerRegistration: ListenerRegistration? = null listenerRegistration = account.addAccountCreationListener { listenerRegistration?.remove() if (fundAmount > 0) { diff --git a/kin-sdk/src/main/java/kin/sdk/AbstractKinAccount.java b/kin-sdk/src/main/java/kin/sdk/AbstractKinAccount.java index 7e0d4c8c..8fd6fda2 100644 --- a/kin-sdk/src/main/java/kin/sdk/AbstractKinAccount.java +++ b/kin-sdk/src/main/java/kin/sdk/AbstractKinAccount.java @@ -4,7 +4,6 @@ import android.support.annotation.Nullable; import java.math.BigDecimal; import java.util.concurrent.Callable; - import kin.utils.Request; abstract class AbstractKinAccount implements KinAccount { @@ -19,7 +18,9 @@ public Transaction call() throws Exception { return buildTransactionSync(publicAddress, amount, fee); } }); - }@NonNull + } + + @NonNull @Override public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, final int fee, @Nullable final String memo) { @@ -31,6 +32,17 @@ public Transaction call() throws Exception { }); } + @NonNull + @Override + public Request getTransactionBuilder() { + return new Request<>(new Callable() { + @Override + public TransactionBuilder call() throws Exception { + return getTransactionBuilderSync(); + } + }); + } + @NonNull @Override public Request sendTransaction(final Transaction transaction) { diff --git a/kin-sdk/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/src/main/java/kin/sdk/KinAccount.java index 1bf386b3..7592e55d 100644 --- a/kin-sdk/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/src/main/java/kin/sdk/KinAccount.java @@ -42,6 +42,12 @@ public interface KinAccount { */ Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo); + + Request getTransactionBuilder(); + + TransactionBuilder getTransactionBuilderSync() throws OperationFailedException; + + /** * Create {@link Request} for signing and sending a transaction *

See {@link KinAccount#sendTransactionSync(Transaction)} for possibles errors

@@ -74,6 +80,23 @@ public interface KinAccount { */ Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; + /** + * Build a Transaction object of the given amount in kin, from an account to the specified public address. + *

Note: This method accesses the network, and should not be called on the android main thread.

+ * + * @param sourceAccountAddress the account address from which you want to send from. You can use one of the other + * methods if you want to use the default account + * @param publicAddress the account address to send the specified kin amount. + * @param amount the amount of kin to transfer. + * @param fee the amount of fee(in stroops) for this transfer. + * @return a Transaction object which also includes the transaction id. + * @throws AccountNotFoundException if the sender or destination account was not created. + * @throws OperationFailedException other error occurred. + */ + Transaction buildTransactionSync(@NonNull String sourceAccountAddress, @NonNull String publicAddress, + @NonNull BigDecimal amount, int fee) throws OperationFailedException; + + /** * Build a Transaction object of the given amount in kin, to the specified public address and with a memo(that can be empty or null). *

Note: This method accesses the network, and should not be called on the android main thread.

@@ -187,4 +210,5 @@ public interface KinAccount { * @return A JSON representation of the data as a string */ String export(@NonNull String passphrase) throws CryptoException; + } diff --git a/kin-sdk/src/main/java/kin/sdk/KinAccountImpl.java b/kin-sdk/src/main/java/kin/sdk/KinAccountImpl.java index 4b7def7e..f89d2abc 100644 --- a/kin-sdk/src/main/java/kin/sdk/KinAccountImpl.java +++ b/kin-sdk/src/main/java/kin/sdk/KinAccountImpl.java @@ -3,10 +3,10 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.math.BigDecimal; +import kin.base.KeyPair; import kin.sdk.exception.AccountDeletedException; import kin.sdk.exception.CryptoException; import kin.sdk.exception.OperationFailedException; -import kin.base.KeyPair; final class KinAccountImpl extends AbstractKinAccount { @@ -49,6 +49,12 @@ public Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull return transactionSender.buildTransaction(account, publicAddress, amount, fee, memo); } + @Override + public TransactionBuilder getTransactionBuilderSync() throws OperationFailedException { + checkValidAccount(); + return transactionSender.getTransactionBuilder(account); + } + @NonNull @Override public TransactionId sendTransactionSync(Transaction transaction) throws OperationFailedException { @@ -96,6 +102,10 @@ public String export(@NonNull String passphrase) throws CryptoException { return backupRestore.exportWallet(account, passphrase); } + KeyPair getKeyPair() { + return account; + } + void markAsDeleted() { isDeleted = true; } diff --git a/kin-sdk/src/main/java/kin/sdk/Transaction.java b/kin-sdk/src/main/java/kin/sdk/Transaction.java index a32db277..ddf852c2 100644 --- a/kin-sdk/src/main/java/kin/sdk/Transaction.java +++ b/kin-sdk/src/main/java/kin/sdk/Transaction.java @@ -1,66 +1,60 @@ package kin.sdk; +import java.io.IOException; import kin.base.KeyPair; - -import java.math.BigDecimal; +import kin.base.Network; public class Transaction { - private final KeyPair destination; - private final KeyPair source; - private final BigDecimal amount; - private final int fee; - private final String memo; - /** * The transaction hash */ private final TransactionId id; - private final kin.base.Transaction stellarTransaction; - private final WhitelistableTransaction whitelistableTransaction; + private final kin.base.Transaction baseTransaction; - Transaction(KeyPair destination, KeyPair source, BigDecimal amount, int fee, String memo, - TransactionId id, kin.base.Transaction stellarTransaction, WhitelistableTransaction whitelistableTransaction) { - this.destination = destination; - this.source = source; - this.amount = amount; - this.fee = fee; - this.memo = memo; + public Transaction(TransactionId id, kin.base.Transaction baseTransaction) { this.id = id; - this.stellarTransaction = stellarTransaction; - this.whitelistableTransaction = whitelistableTransaction; + this.baseTransaction = baseTransaction; } - public KeyPair getDestination() { - return destination; + public static Transaction decodeTransaction(String transactionEnvelope) throws IOException { + kin.base.Transaction transaction = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); + TransactionId id = new TransactionIdImpl(Utils.byteArrayToHex(transaction.hash())); + return new Transaction(id, transaction); } public KeyPair getSource() { - return source; - } - - public BigDecimal getAmount() { - return amount; + return baseTransaction.getSourceAccount(); } public int getFee() { - return fee; + return baseTransaction.getFee(); } public String getMemo() { - return memo; - } + return baseTransaction.getMemo().toString(); + } // TODO: 2019-05-23 check if ok public TransactionId getId() { return id; } - kin.base.Transaction getStellarTransaction() { - return stellarTransaction; + kin.base.Transaction getBaseTransaction() { + return baseTransaction; } public WhitelistableTransaction getWhitelistableTransaction() { - return whitelistableTransaction; + return new WhitelistableTransaction(baseTransaction.toEnvelopeXdrBase64(), + Network.current().getNetworkPassphrase()); } + + public String getTransactionEnvelope() { + return baseTransaction.toEnvelopeXdrBase64(); + } + + public void addSignature(KinAccount account) { + baseTransaction.sign(((KinAccountImpl) account).getKeyPair()); + } + } diff --git a/kin-sdk/src/main/java/kin/sdk/TransactionBuilder.java b/kin-sdk/src/main/java/kin/sdk/TransactionBuilder.java new file mode 100644 index 00000000..acb631fb --- /dev/null +++ b/kin-sdk/src/main/java/kin/sdk/TransactionBuilder.java @@ -0,0 +1,90 @@ +package kin.sdk; + +import kin.base.KeyPair; +import kin.base.Memo; +import kin.base.Operation; +import kin.base.TimeBounds; +import kin.base.Transaction; +import kin.base.Transaction.Builder; +import kin.base.TransactionBuilderAccount; + +public class TransactionBuilder { + + private final Builder builder; + private final KeyPair account; + + /** + * Construct a new transaction builder. + * + * @param account The source account for this transaction. This account is the account + * @param sourceAccount The source account for this transaction. This account is the account who will use a sequence + * number. When build() is called, the account object's sequence number will be incremented. + */ + TransactionBuilder(KeyPair account, TransactionBuilderAccount sourceAccount) { + this.account = account; + builder = new Builder(sourceAccount); + } + + public int getOperationsCount() { + return builder.getOperationsCount(); + } + + /** + * Adds a new operation to this transaction. + * + * @return Builder object so you can chain methods. + * @see Operation + */ + public TransactionBuilder addOperation(Operation operation) { + builder.addOperation(operation); + return this; + } + + /** + * @param fee this transaction fee + * @return Builder object so you can chain methods. + */ + public TransactionBuilder setFee(int fee) { + builder.addFee(fee); + return this; + } + + /** + * Adds a memo to + * this transaction. + * + * @param appId is a 3-4 character string which is added to each transaction memo to identify your application. + * appId must contain only digits and upper and/or lower case letters. String length must be 3 or 4. + * @return Builder object so you can chain methods. + * @see Memo + */ + public TransactionBuilder setMemo(String appId, String memo) { + builder.addMemo(Memo.text(Utils.addAppIdToMemo(appId, memo))); + // TODO: 2019-05-23 should we validate memo like we are doing in transaction sender class? + return this; + } + + /** + * Adds a time-bounds to this transaction. + * + * @return Builder object so you can chain methods. + * @see TimeBounds + */ + public TransactionBuilder setTimeBounds(TimeBounds timeBounds) { + builder.addTimeBounds(timeBounds); + return this; + } + + /** + * Builds a transaction. It will increment sequence number of the source account. + */ + public kin.sdk.Transaction build() { + Transaction baseTransaction = builder.build(); + TransactionIdImpl transactionId = new TransactionIdImpl(Utils.byteArrayToHex(baseTransaction.hash())); + baseTransaction.sign(account); + return new kin.sdk.Transaction(transactionId, baseTransaction); + } + +} diff --git a/kin-sdk/src/main/java/kin/sdk/TransactionSender.java b/kin-sdk/src/main/java/kin/sdk/TransactionSender.java index 946991e0..f446946c 100644 --- a/kin-sdk/src/main/java/kin/sdk/TransactionSender.java +++ b/kin-sdk/src/main/java/kin/sdk/TransactionSender.java @@ -3,20 +3,11 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; - import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.util.List; - import kin.base.AssetTypeNative; -import kin.base.Network; -import kin.sdk.exception.AccountNotFoundException; -import kin.sdk.exception.InsufficientFeeException; -import kin.sdk.exception.IllegalAmountException; -import kin.sdk.exception.InsufficientKinException; -import kin.sdk.exception.OperationFailedException; -import kin.sdk.exception.TransactionFailedException; import kin.base.KeyPair; import kin.base.Memo; import kin.base.PaymentOperation; @@ -25,13 +16,17 @@ import kin.base.responses.AccountResponse; import kin.base.responses.HttpResponseException; import kin.base.responses.SubmitTransactionResponse; +import kin.sdk.exception.AccountNotFoundException; +import kin.sdk.exception.IllegalAmountException; +import kin.sdk.exception.InsufficientFeeException; +import kin.sdk.exception.InsufficientKinException; +import kin.sdk.exception.OperationFailedException; +import kin.sdk.exception.TransactionFailedException; class TransactionSender { private static final int MEMO_BYTES_LENGTH_LIMIT = 21; //Memo length limitation(in bytes) is 28 but we add 7 more bytes which includes the appId and some characters. - private static final int MAX_NUM_OF_DECIMAL_PLACES = 4 ; - private static String MEMO_APP_ID_VERSION_PREFIX = "1"; - private static String MEMO_DELIMITER = "-"; + private static final int MAX_NUM_OF_DECIMAL_PLACES = 4; private static final String INSUFFICIENT_KIN_RESULT_CODE = "op_underfunded"; private static final String INSUFFICIENT_FEE_RESULT_CODE = "tx_insufficient_fee"; private static final String INSUFFICIENT_BALANCE_RESULT_CODE = "tx_insufficient_balance"; @@ -44,27 +39,32 @@ class TransactionSender { } Transaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, - int fee) throws OperationFailedException { + int fee) throws OperationFailedException { return buildTransaction(from, publicAddress, amount, fee, null); } Transaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, - int fee, @Nullable String memo) throws OperationFailedException { + int fee, @Nullable String memo) throws OperationFailedException { checkParams(from, publicAddress, amount, fee, memo); - memo = addAppIdToMemo(memo); + memo = Utils.addAppIdToMemo(appId, memo); KeyPair addressee = generateAddresseeKeyPair(publicAddress); AccountResponse sourceAccount = loadSourceAccount(from); verifyAddresseeAccount(generateAddresseeKeyPair(addressee.getAccountId())); - kin.base.Transaction stellarTransaction = buildStellarTransaction(from, amount, addressee, sourceAccount, fee, memo); + kin.base.Transaction stellarTransaction = buildStellarTransaction(from, amount, addressee, sourceAccount, fee, + memo); TransactionId id = new TransactionIdImpl(Utils.byteArrayToHex(stellarTransaction.hash())); - WhitelistableTransaction whitelistableTransaction = - new WhitelistableTransaction(stellarTransaction.toEnvelopeXdrBase64(), Network.current().getNetworkPassphrase()); - return new Transaction(addressee, from, amount, fee, memo, id, stellarTransaction, whitelistableTransaction); + return new Transaction(id, stellarTransaction); + } + + TransactionBuilder getTransactionBuilder(@NonNull KeyPair from) throws OperationFailedException { + Utils.checkNotNull(from, "from"); + AccountResponse sourceAccount = loadSourceAccount(from); + return new TransactionBuilder(from, sourceAccount); } TransactionId sendTransaction(Transaction transaction) throws OperationFailedException { - return sendTransaction(transaction.getStellarTransaction()); + return sendTransaction(transaction.getBaseTransaction()); } TransactionId sendWhitelistTransaction(String whitelist) throws OperationFailedException { @@ -76,25 +76,9 @@ TransactionId sendWhitelistTransaction(String whitelist) throws OperationFailedE } } - @NonNull - private String addAppIdToMemo(@Nullable String memo) { - if (memo == null) { - memo = ""; - } else { - memo = memo.trim(); // remove leading and trailing whitespaces. - } - StringBuilder sb = new StringBuilder(); - sb.append(MEMO_APP_ID_VERSION_PREFIX) - .append(MEMO_DELIMITER) - .append(appId) - .append(MEMO_DELIMITER) - .append(memo); - return sb.toString(); - } - private void checkParams(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, - int fee, @Nullable String memo) throws OperationFailedException { - Utils.checkNotNull(from, "account"); + int fee, @Nullable String memo) throws OperationFailedException { + Utils.checkNotNull(from, "from"); Utils.checkNotNull(amount, "amount"); validateAmountDecimalPoint(amount); checkForNegativeFee(fee); @@ -134,7 +118,8 @@ private void checkForNegativeFee(int fee) { private void checkMemo(String memo) { try { if (memo != null && memo.getBytes("UTF-8").length > MEMO_BYTES_LENGTH_LIMIT) { - throw new IllegalArgumentException("Memo cannot be longer that " + MEMO_BYTES_LENGTH_LIMIT + " bytes(UTF-8 characters)"); + throw new IllegalArgumentException( + "Memo cannot be longer that " + MEMO_BYTES_LENGTH_LIMIT + " bytes(UTF-8 characters)"); } } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException("Memo text have unsupported characters encoding"); @@ -151,11 +136,11 @@ private KeyPair generateAddresseeKeyPair(@NonNull String publicAddress) throws O } @NonNull - private kin.base.Transaction buildStellarTransaction(@NonNull KeyPair from, @NonNull BigDecimal amount, KeyPair addressee, - AccountResponse sourceAccount, int fee, @Nullable String memo) { + private kin.base.Transaction buildStellarTransaction(@NonNull KeyPair from, @NonNull BigDecimal amount, + KeyPair addressee, + AccountResponse sourceAccount, int fee, @Nullable String memo) { Builder transactionBuilder = new Builder(sourceAccount) - .addOperation( - new PaymentOperation.Builder(addressee, new AssetTypeNative(), amount.toString()).build()); + .addOperation(new PaymentOperation.Builder(addressee, new AssetTypeNative(), amount.toString()).build()); transactionBuilder.addFee(fee); if (memo != null) { transactionBuilder.addMemo(Memo.text(memo)); @@ -212,7 +197,7 @@ private TransactionId sendTransaction(kin.base.Transaction transaction) throws O } private TransactionId createFailureException(SubmitTransactionResponse response) - throws TransactionFailedException, InsufficientKinException, InsufficientFeeException { + throws TransactionFailedException, InsufficientKinException, InsufficientFeeException { TransactionFailedException transactionException = Utils.createTransactionException(response); if (isInsufficientKinException(transactionException)) { throw new InsufficientKinException(); @@ -226,8 +211,11 @@ private TransactionId createFailureException(SubmitTransactionResponse response) private boolean isInsufficientKinException(TransactionFailedException transactionException) { List resultCodes = transactionException.getOperationsResultCodes(); String transactionResultCode = transactionException.getTransactionResultCode(); - return ((resultCodes != null && resultCodes.size() > 0 && INSUFFICIENT_KIN_RESULT_CODE.equals(resultCodes.get(0))) || - !TextUtils.isEmpty(transactionResultCode) && INSUFFICIENT_BALANCE_RESULT_CODE.equals(transactionResultCode)); + return ( + (resultCodes != null && resultCodes.size() > 0 && INSUFFICIENT_KIN_RESULT_CODE.equals(resultCodes.get(0))) + || + !TextUtils.isEmpty(transactionResultCode) && INSUFFICIENT_BALANCE_RESULT_CODE + .equals(transactionResultCode)); } private boolean isInsufficientFeeException(TransactionFailedException transactionException) { diff --git a/kin-sdk/src/main/java/kin/sdk/Utils.java b/kin-sdk/src/main/java/kin/sdk/Utils.java index c7783859..a8a67561 100644 --- a/kin-sdk/src/main/java/kin/sdk/Utils.java +++ b/kin-sdk/src/main/java/kin/sdk/Utils.java @@ -2,13 +2,17 @@ import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import java.util.ArrayList; -import kin.sdk.exception.TransactionFailedException; import kin.base.responses.SubmitTransactionResponse; import kin.base.responses.SubmitTransactionResponse.Extras.ResultCodes; +import kin.sdk.exception.TransactionFailedException; final class Utils { + private static String MEMO_APP_ID_VERSION_PREFIX = "1"; + private static String MEMO_DELIMITER = "-"; + private Utils() { //no instances } @@ -42,4 +46,20 @@ static void checkNotEmpty(String string, String paramName) { throw new IllegalArgumentException(paramName + " cannot be null or empty."); } } + + @NonNull + static String addAppIdToMemo(String appId, @Nullable String memo) { + if (memo == null) { + memo = ""; + } else { + memo = memo.trim(); // remove leading and trailing whitespaces. + } + StringBuilder sb = new StringBuilder(); + sb.append(MEMO_APP_ID_VERSION_PREFIX) + .append(MEMO_DELIMITER) + .append(appId) + .append(MEMO_DELIMITER) + .append(memo); + return sb.toString(); + } } diff --git a/sample/src/main/java/kin/sdk/sample/OnBoarding.java b/sample/src/main/java/kin/sdk/sample/OnBoarding.java index 9add0c80..dd33ee9c 100644 --- a/sample/src/main/java/kin/sdk/sample/OnBoarding.java +++ b/sample/src/main/java/kin/sdk/sample/OnBoarding.java @@ -5,7 +5,6 @@ import android.os.Looper; import android.support.annotation.NonNull; import android.text.format.DateUtils; - import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -50,7 +49,7 @@ void onBoard(@NonNull KinAccount account, @NonNull Callbacks callbacks) { handler.removeCallbacks(accountCreationListeningTimeout); fireOnSuccess(callbacks); }); - handler.postDelayed(accountCreationListeningTimeout, 10 * DateUtils.SECOND_IN_MILLIS); + handler.postDelayed(accountCreationListeningTimeout, 20 * DateUtils.SECOND_IN_MILLIS); createAccount(account, callbacks); } From 9c84105d7ea79f4a8a1811e86e8b6f9ecdf8b982 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Tue, 28 May 2019 18:07:49 +0300 Subject: [PATCH 02/56] Add more code for the account linking api. 1. add more tests(uncompleted yet). 2. Add support for testing final class with mockito 3. fix the bug in which when decode a transaction then it was multiple the fee by num of operations again 4. format TransactionSenderTest 5. Add Transaction tests --- .../src/main/java/kin/base/Transaction.java | 7 + kin-sdk/build.gradle | 1 + .../java/kin/sdk/KinAccountTest.java | 14 ++ .../kotlin/kin/sdk/FakeKinOnBoard.kt | 16 +- .../kin/sdk/KinAccountIntegrationTest.kt | 49 +++--- kin-sdk/src/main/java/kin/sdk/KinAccount.java | 19 +-- .../src/main/java/kin/sdk/Transaction.java | 7 +- .../main/java/kin/sdk/TransactionSender.java | 3 +- .../java/kin/sdk/TransactionSenderTest.java | 148 +++++++++++++----- .../test/java/kin/sdk/TransactionTest.java | 104 ++++++++++++ 10 files changed, 278 insertions(+), 90 deletions(-) create mode 100644 kin-sdk/src/test/java/kin/sdk/TransactionTest.java diff --git a/kin-base/src/main/java/kin/base/Transaction.java b/kin-base/src/main/java/kin/base/Transaction.java index 5ac24886..e7677506 100644 --- a/kin-base/src/main/java/kin/base/Transaction.java +++ b/kin-base/src/main/java/kin/base/Transaction.java @@ -228,7 +228,14 @@ public static Transaction fromEnvelopeXdr(String envelope) throws IOException { */ public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope) { kin.base.xdr.Transaction tx = envelope.getTx(); + // Although currently there couldn't be a transaction with no operations we still check it. int mFee = tx.getFee().getUint32(); + if (tx.getOperations().length > 1) { + // Because the fee was already multiplied by number of operation then divide by it because when reCreate this + // transaction then we will multiple it again. + mFee = mFee / tx.getOperations().length; + } + KeyPair mSourceAccount = KeyPair.fromXdrPublicKey(tx.getSourceAccount().getAccountID()); Long mSequenceNumber = tx.getSeqNum().getSequenceNumber().getUint64(); Memo mMemo = Memo.fromXdr(tx.getMemo()); diff --git a/kin-sdk/build.gradle b/kin-sdk/build.gradle index f549c05e..ff0a2148 100644 --- a/kin-sdk/build.gradle +++ b/kin-sdk/build.gradle @@ -47,6 +47,7 @@ dependencies { implementation 'com.github.joshjdevl.libsodiumjni:libsodium-jni-aar:2.0.1' api 'com.github.kinecosystem:kin-utils-android:1.1' + testImplementation 'org.mockito:mockito-inline:2.10.0' testImplementation testingDependencies.junit testImplementation testingDependencies.mockito testImplementation testingDependencies.mockWebServer diff --git a/kin-sdk/src/androidTest/java/kin/sdk/KinAccountTest.java b/kin-sdk/src/androidTest/java/kin/sdk/KinAccountTest.java index f93c7994..b5eb68a8 100644 --- a/kin-sdk/src/androidTest/java/kin/sdk/KinAccountTest.java +++ b/kin-sdk/src/androidTest/java/kin/sdk/KinAccountTest.java @@ -33,6 +33,20 @@ public void teardown() { kinClient.clearAllAccounts(); } + @Test(expected = AccountDeletedException.class) + public void buildTransactionSync_DeletedAccount_AccountDeletedException() throws Exception { + KinAccount kinAccount = kinClient.addAccount(); + kinClient.deleteAccount(0); + kinAccount.buildTransactionSync(kinAccount.getPublicAddress(), new BigDecimal(200), 100); + } + + @Test(expected = AccountDeletedException.class) + public void getTransactionBuilderSync_DeletedAccount_AccountDeletedException() throws Exception { + KinAccount kinAccount = kinClient.addAccount(); + kinClient.deleteAccount(0); + kinAccount.getTransactionBuilderSync(); + } + @Test(expected = AccountDeletedException.class) public void getBalanceSync_DeletedAccount_AccountDeletedException() throws Exception { KinAccount kinAccount = kinClient.addAccount(); diff --git a/kin-sdk/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt b/kin-sdk/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt index 8b05603c..8c551ebd 100644 --- a/kin-sdk/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt +++ b/kin-sdk/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt @@ -1,10 +1,12 @@ package kin.sdk import android.util.Log -import kin.sdk.IntegConsts.* +import kin.sdk.IntegConsts.URL_CREATE_ACCOUNT +import kin.sdk.IntegConsts.URL_FUND import okhttp3.OkHttpClient import okhttp3.Request import java.io.IOException +import java.util.concurrent.TimeUnit /** * Fake on board for integration test, support creating and funding accounts on stellar test net @@ -14,7 +16,11 @@ constructor() { @Throws(Exception::class) fun createAccount(destinationAccount: String) { - val client = OkHttpClient() + val client = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build() val request = Request.Builder() .url(String.format(URL_CREATE_ACCOUNT, destinationAccount)).build() client.newCall(request).execute()?.let { @@ -25,7 +31,11 @@ constructor() { } fun fundWithKin(destinationAccount: String, amount: String) { - val client = OkHttpClient() + val client = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build() val request = Request.Builder() .url(String.format(URL_FUND + amount, destinationAccount)) .get() diff --git a/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index 38fe0b2c..bc505388 100644 --- a/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -9,6 +9,7 @@ import kin.sdk.exception.InsufficientFeeException import kin.sdk.exception.InsufficientKinException import org.hamcrest.CoreMatchers.* import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.isEmptyOrNullString import org.hamcrest.Matchers.isEmptyString import org.junit.* import org.junit.rules.ExpectedException @@ -414,33 +415,43 @@ class KinAccountIntegrationTest { val masterAccount = kinClient.addAccount() val destinationAccount = kinClient.addAccount() onboardSingleAccount(destinationAccount, 100) - Thread.sleep(15000) // sleep for 15 seconds in order to wait for all the account ot be created and funded linkAccount(controlledAccount, masterAccount) - val transaction = masterAccount.buildTransactionSync(destinationAccount.publicAddress.orEmpty(), - BigDecimal("21.123"), fee) - val transactionId = kinAccountSender.sendTransactionSync(transaction) - assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin))) - assertThat(kinAccountReceiver.balanceSync.value(), equalTo(BigDecimal("21.12300"))) + val transaction = masterAccount.transactionBuilderSync + .setFee(fee) + .setMemo(appId, "master to destination") + .addOperation(PaymentOperation.Builder( + KeyPair.fromAccountId(destinationAccount.publicAddress), AssetTypeNative(), "21.12300") + .setSourceAccount(KeyPair.fromAccountId(controlledAccount.publicAddress)) + .build()) + .build() + //TODO add a check to see if indeed the manage data the same for master and 2 signatures in controlled + val transactionId = masterAccount.sendTransactionSync(transaction) + assertThat(transactionId.id(), not(isEmptyOrNullString())) + // The controlled account need to reduced the fee from the link account transaction. + assertThat(controlledAccount.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin.multiply(BigDecimal(2))))) + assertThat(masterAccount.balanceSync.value(), equalTo(BigDecimal("100.00000").subtract(feeInKin))) + assertThat(destinationAccount.balanceSync.value(), equalTo(BigDecimal("121.12300"))) + masterAccount.statusSync } private fun linkAccount(controlledAccount: KinAccount, masterAccount: KinAccount) { onboardSingleAccount(controlledAccount, 100) onboardSingleAccount(masterAccount, 100) - val transactionBuilder = controlledAccount.transactionBuilderSync val signerKey = Signer.ed25519PublicKey(KeyPair.fromAccountId(masterAccount.publicAddress)) val managerDataKey = controlledAccount.publicAddress - val managerDataValue = "some package id" - val transaction = transactionBuilder.setFee(100) + val managerDataValue = "pId" + val transaction = transactionBuilder + .setFee(fee)//TODO should we pay twice the fee because 2 operations??? .setMemo("test", "account linking") .addOperation(SetOptionsOperation.Builder().setSigner(signerKey, 1).build()) - .addOperation(ManageDataOperation.Builder(managerDataKey, managerDataValue.toByteArray()).build()) + .addOperation(ManageDataOperation.Builder(managerDataKey, kin.base.codec.Base64().decode(managerDataValue)).build()) .build() // simulate transaction getting a transaction envelope and decode it. val transactionEnvelope = transaction.transactionEnvelope val externalTransaction = Transaction.decodeTransaction(transactionEnvelope) // Sending the linking transaction from the master account - val transactionId = masterAccount.sendTransactionSync(externalTransaction) + masterAccount.sendTransactionSync(externalTransaction) } private fun onboardAccounts(senderFundAmount: Int = 0, @@ -453,20 +464,10 @@ class KinAccountIntegrationTest { } private fun onboardSingleAccount(account: KinAccount, fundAmount: Int) { - val latch = CountDownLatch(1) - - var listenerRegistration: ListenerRegistration? = null - listenerRegistration = account.addAccountCreationListener { - listenerRegistration?.remove() - if (fundAmount > 0) { - fakeKinOnBoard.fundWithKin(account.publicAddress.orEmpty(), fundAmount.toString()) - } - latch.countDown() - } - fakeKinOnBoard.createAccount(account.publicAddress.orEmpty()) - - assertTrue(latch.await(timeoutDurationSecondsLong, TimeUnit.SECONDS)) + Thread.sleep(5000) // sleep for 5 seconds in order to be sure that all the account were created and funded + fakeKinOnBoard.fundWithKin(account.publicAddress.orEmpty(), fundAmount.toString()) + Thread.sleep(5000) // sleep for 5 seconds in order to be sure that all the account were created and funded } private fun addAppIdToMemo(memo: String): String { diff --git a/kin-sdk/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/src/main/java/kin/sdk/KinAccount.java index 7592e55d..4e6449da 100644 --- a/kin-sdk/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/src/main/java/kin/sdk/KinAccount.java @@ -45,9 +45,9 @@ public interface KinAccount { Request getTransactionBuilder(); + // TODO: 2019-05-26 add java doc TransactionBuilder getTransactionBuilderSync() throws OperationFailedException; - /** * Create {@link Request} for signing and sending a transaction *

See {@link KinAccount#sendTransactionSync(Transaction)} for possibles errors

@@ -80,23 +80,6 @@ public interface KinAccount { */ Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; - /** - * Build a Transaction object of the given amount in kin, from an account to the specified public address. - *

Note: This method accesses the network, and should not be called on the android main thread.

- * - * @param sourceAccountAddress the account address from which you want to send from. You can use one of the other - * methods if you want to use the default account - * @param publicAddress the account address to send the specified kin amount. - * @param amount the amount of kin to transfer. - * @param fee the amount of fee(in stroops) for this transfer. - * @return a Transaction object which also includes the transaction id. - * @throws AccountNotFoundException if the sender or destination account was not created. - * @throws OperationFailedException other error occurred. - */ - Transaction buildTransactionSync(@NonNull String sourceAccountAddress, @NonNull String publicAddress, - @NonNull BigDecimal amount, int fee) throws OperationFailedException; - - /** * Build a Transaction object of the given amount in kin, to the specified public address and with a memo(that can be empty or null). *

Note: This method accesses the network, and should not be called on the android main thread.

diff --git a/kin-sdk/src/main/java/kin/sdk/Transaction.java b/kin-sdk/src/main/java/kin/sdk/Transaction.java index ddf852c2..40aebdd8 100644 --- a/kin-sdk/src/main/java/kin/sdk/Transaction.java +++ b/kin-sdk/src/main/java/kin/sdk/Transaction.java @@ -2,6 +2,7 @@ import java.io.IOException; import kin.base.KeyPair; +import kin.base.Memo; import kin.base.Network; public class Transaction { @@ -32,9 +33,9 @@ public int getFee() { return baseTransaction.getFee(); } - public String getMemo() { - return baseTransaction.getMemo().toString(); - } // TODO: 2019-05-23 check if ok + public Memo getMemo() { + return baseTransaction.getMemo(); + } public TransactionId getId() { return id; diff --git a/kin-sdk/src/main/java/kin/sdk/TransactionSender.java b/kin-sdk/src/main/java/kin/sdk/TransactionSender.java index f446946c..a212eac0 100644 --- a/kin-sdk/src/main/java/kin/sdk/TransactionSender.java +++ b/kin-sdk/src/main/java/kin/sdk/TransactionSender.java @@ -60,6 +60,7 @@ Transaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddres TransactionBuilder getTransactionBuilder(@NonNull KeyPair from) throws OperationFailedException { Utils.checkNotNull(from, "from"); AccountResponse sourceAccount = loadSourceAccount(from); + // TODO: 2019-05-27 need to decide if we do use make the fee "must" in the constructor... return new TransactionBuilder(from, sourceAccount); } @@ -78,7 +79,7 @@ TransactionId sendWhitelistTransaction(String whitelist) throws OperationFailedE private void checkParams(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { - Utils.checkNotNull(from, "from"); + Utils.checkNotNull(from, "account"); Utils.checkNotNull(amount, "amount"); validateAmountDecimalPoint(amount); checkForNegativeFee(fee); diff --git a/kin-sdk/src/test/java/kin/sdk/TransactionSenderTest.java b/kin-sdk/src/test/java/kin/sdk/TransactionSenderTest.java index 32e133b7..7579deec 100644 --- a/kin-sdk/src/test/java/kin/sdk/TransactionSenderTest.java +++ b/kin-sdk/src/test/java/kin/sdk/TransactionSenderTest.java @@ -9,6 +9,7 @@ import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isA; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertThat; import java.io.IOException; @@ -17,9 +18,16 @@ import java.util.concurrent.TimeUnit; import kin.base.FormatException; import kin.base.KeyPair; +import kin.base.ManageDataOperation; +import kin.base.MemoText; import kin.base.Network; import kin.base.Server; +import kin.base.SetOptionsOperation.Builder; +import kin.base.Signer; +import kin.base.TimeBounds; +import kin.base.codec.Base64; import kin.base.responses.HttpResponseException; +import kin.base.xdr.SignerKey; import kin.sdk.exception.AccountNotFoundException; import kin.sdk.exception.IllegalAmountException; import kin.sdk.exception.InsufficientFeeException; @@ -59,6 +67,7 @@ public class TransactionSenderTest { @Mock private KeyStore mockKeyStore; + private Server server; private MockWebServer mockWebServer; private TransactionSender transactionSender; @@ -90,7 +99,8 @@ public void sendTransaction_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); TransactionId transactionId = transactionSender.sendTransaction(transaction); assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); @@ -101,6 +111,41 @@ public void sendTransaction_success() throws Exception { assertThat(mockWebServer.takeRequest().getBody().readUtf8(), equalTo(TX_BODY)); } + @Test + public void getTransactionBuilder_buildSuccess() throws Exception { + // here it should really be some other account(master account) but for tests it doesn't really matter. + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + SignerKey signerKey = Signer.ed25519PublicKey(account); + String managerDataKey = "controlled account public address"; + String managerDataValue = "some package id"; + + TransactionBuilder transactionBuilder = transactionSender.getTransactionBuilder(account); + TimeBounds timeBounds = new TimeBounds(42, 1337); + Transaction transaction = getLinkingTransaction(signerKey, managerDataKey, managerDataValue, transactionBuilder, + timeBounds); + + assertThat(transactionBuilder.getOperationsCount(), equalTo(2)); + assertThat(transaction.getFee(), equalTo(200)); + assertThat(((MemoText) transaction.getMemo()).getText(), equalTo(("1-" + APP_ID + "-fake memo"))); + assertThat(transaction.getSource().getAccountId(), equalTo(account.getAccountId())); + assertThat(transaction.getBaseTransaction().getTimeBounds(), equalTo(timeBounds)); + assertArrayEquals(account.getSignatureHint().getSignatureHint(), + transaction.getBaseTransaction().getSignatures().get(0).getHint().getSignatureHint()); + assertThat(transaction.getBaseTransaction().getSignatures().size(), equalTo(1)); + } + + private Transaction getLinkingTransaction(SignerKey signerKey, String managerDataKey, String managerDataValue, + TransactionBuilder transactionBuilder, TimeBounds timeBounds) { + return transactionBuilder + .addOperation(new Builder().setSigner(signerKey, 1).build()) + .addOperation( + new ManageDataOperation.Builder(managerDataKey, new Base64().decode(managerDataValue)).build()) + .setMemo(APP_ID, "fake memo") + .setFee(100) + .setTimeBounds(timeBounds) + .build(); + } + @Test public void sendTransaction_WithMemo_success() throws Exception { //send transaction fetch first to account details, then from account details, and finally perform tx, @@ -110,7 +155,8 @@ public void sendTransaction_WithMemo_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); String fakeMemo = "fake memo"; - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, fakeMemo); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, fakeMemo); TransactionId transactionId = transactionSender.sendTransaction(transaction); assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); @@ -129,37 +175,38 @@ public void sendTransaction_ToAccountNotExist() throws Exception { expectedEx.expect(AccountNotFoundException.class); expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_TO))); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); transactionSender.sendTransaction(transaction); } - @Test - public void sendTransaction_Http_307_Response_Success() throws Exception { + @Test + public void sendTransaction_Http_307_Response_Success() throws Exception { MockWebServer mockWebServerHttp307 = new MockWebServer(); mockWebServerHttp307.start(); String location = mockWebServerHttp307.url("").toString(); - //send transaction fetch first to account details, then from account details, and finally perform tx which will return 307 - // and then 200. here we mock all 4 responses from server to achieve success operation - mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); - mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); - // No need for a real location because any way it is local host + //send transaction fetch first to account details, then from account details, and finally perform tx which will return 307 + // and then 200. here we mock all 4 responses from server to achieve success operation + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + // No need for a real location because any way it is local host mockWebServer .enqueue(TestUtils.generateSuccessHttp307MockResponse(location)); mockWebServerHttp307.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); - Transaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); - TransactionId transactionId = transactionSender.sendTransaction(transaction); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + TransactionId transactionId = transactionSender.sendTransaction(transaction); - assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); + assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); - //verify sent requests data - assertThat(mockWebServer.takeRequest().getRequestUrl().toString(), containsString(ACCOUNT_ID_FROM)); - assertThat(mockWebServer.takeRequest().getRequestUrl().toString(), containsString(ACCOUNT_ID_TO)); - assertThat(mockWebServer.takeRequest().getBody().readUtf8(), equalTo(TX_BODY)); + //verify sent requests data + assertThat(mockWebServer.takeRequest().getRequestUrl().toString(), containsString(ACCOUNT_ID_FROM)); + assertThat(mockWebServer.takeRequest().getRequestUrl().toString(), containsString(ACCOUNT_ID_TO)); + assertThat(mockWebServer.takeRequest().getBody().readUtf8(), equalTo(TX_BODY)); assertThat(mockWebServerHttp307.takeRequest().getBody().readUtf8(), equalTo(TX_BODY)); - } + } @Test public void sendTransaction_FromAccountNotExist() throws Exception { @@ -168,7 +215,8 @@ public void sendTransaction_FromAccountNotExist() throws Exception { expectedEx.expect(AccountNotFoundException.class); expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_FROM))); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); transactionSender.sendTransaction(transaction); } @@ -186,7 +234,8 @@ public void sendTransaction_GeneralStellarError() throws Exception { expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", contains("op_malformed"))); expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", hasSize(1))); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -202,7 +251,8 @@ public void sendTransaction_Underfunded_InsufficientKinException() throws Except expectedEx.expect(InsufficientKinException.class); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -211,13 +261,14 @@ public void sendTransaction_InsufficientFeeException() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); mockWebServer.enqueue(new MockResponse() - .setBody(TestUtils.loadResource(this.getClass(), "tx_failure_res_insufficient_fee.json")) - .setResponseCode(400) + .setBody(TestUtils.loadResource(this.getClass(), "tx_failure_res_insufficient_fee.json")) + .setResponseCode(400) ); expectedEx.expect(InsufficientFeeException.class); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -226,13 +277,14 @@ public void sendTransaction_InsufficientBalanceException() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); mockWebServer.enqueue(new MockResponse() - .setBody(TestUtils.loadResource(this.getClass(), "tx_failure_res_insufficient_balance.json")) - .setResponseCode(400) + .setBody(TestUtils.loadResource(this.getClass(), "tx_failure_res_insufficient_balance.json")) + .setResponseCode(400) ); expectedEx.expect(InsufficientKinException.class); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -260,7 +312,8 @@ public void sendTransaction_FirstQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(IOException.class)); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -271,7 +324,8 @@ public void sendTransaction_SecondQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -283,7 +337,8 @@ public void sendTransaction_ThirdQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -299,7 +354,8 @@ public void sendTransaction_changeTimeOut() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(SocketTimeoutException.class)); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -310,7 +366,8 @@ public void sendTransaction_FirstQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage(ACCOUNT_ID_FROM); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -322,7 +379,8 @@ public void sendTransaction_SecondQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage(ACCOUNT_ID_TO); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -335,7 +393,8 @@ public void sendTransaction_ThirdQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage("transaction"); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -345,7 +404,8 @@ public void sendTransaction_TooLongMemo() throws Exception { String tooLongMemo = "memo string can be only 21 characters"; expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Memo cannot be longer that 21 bytes(UTF-8 characters)"); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, tooLongMemo); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, tooLongMemo); transactionSender.sendTransaction(transaction); assertThat(mockWebServer.getRequestCount(), equalTo(0)); } @@ -389,7 +449,8 @@ public void sendTransaction_EmptyPublicAddress() throws Exception { public void sendTransaction_NegativeAmount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Amount"); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("-200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("-200"), FEE); transactionSender.sendTransaction(transaction); } @@ -397,7 +458,8 @@ public void sendTransaction_NegativeAmount() throws Exception { public void sendTransaction_NegativeFee() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Fee"); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), -FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), -FEE); transactionSender.sendTransaction(transaction); } @@ -405,7 +467,8 @@ public void sendTransaction_NegativeFee() throws Exception { public void sendTransaction_AmountExceedNumOfDecimalPlaces() throws Exception { expectedEx.expect(IllegalAmountException.class); expectedEx.expectMessage("amount can't have more then 5 digits after the decimal point"); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("20.012345"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("20.012345"), FEE); transactionSender.sendTransaction(transaction); } @@ -425,14 +488,17 @@ public void sendTransaction_InvalidChecksumPublicId() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(FormatException.class)); expectedEx.expectMessage("public address"); - Transaction transaction = transactionSender.buildTransaction(account, "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OG3", new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OG3", + new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @SuppressWarnings("SameParameterValue") private void testHttpResponseCode(int resCode) { try { - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); fail("Expected OperationFailedException"); } catch (Exception ex) { diff --git a/kin-sdk/src/test/java/kin/sdk/TransactionTest.java b/kin-sdk/src/test/java/kin/sdk/TransactionTest.java new file mode 100644 index 00000000..61eca445 --- /dev/null +++ b/kin-sdk/src/test/java/kin/sdk/TransactionTest.java @@ -0,0 +1,104 @@ +package kin.sdk; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import kin.base.Account; +import kin.base.CreateAccountOperation; +import kin.base.KeyPair; +import kin.base.Network; +import kin.base.Server; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = 23, manifest = Config.NONE) +public class TransactionTest { + + private static final String ACCOUNT_ID_FROM = "GDJVKIY3UQKMTQZHXR36ZQUNFYO45XLM6IHWO6TYQ53M5KEXBNMJYWVR"; + private static final String SECRET_SEED_FROM = "SB73L5FFTZMN6FHTOOWYEBVFTLUWQEWBLSCI4WLZADRJWENDBYL6QD6P"; + + private MockWebServer mockWebServer; + private Server server; + private Transaction transaction; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + mockServer(); + Network.useTestNetwork(); + + createTransaction(); + + } + + private void createTransaction() { + KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); + KeyPair destination = KeyPair.fromAccountId(ACCOUNT_ID_FROM); + long sequenceNumber = 2908908335136768L; + Account account = new Account(source, sequenceNumber); + kin.base.Transaction baseTransaction = new kin.base.Transaction.Builder(account) + .addOperation(new CreateAccountOperation.Builder(destination, "2000").build()) + .addFee(100) + .build(); + baseTransaction.sign(source); + TransactionId id = new TransactionIdImpl(Utils.byteArrayToHex(baseTransaction.hash())); + transaction = new Transaction(id, baseTransaction); + } + + private void mockServer() throws IOException { + mockWebServer = new MockWebServer(); + mockWebServer.start(); + String url = mockWebServer.url("").toString(); + server = new Server(url); + } + + @Test + public void getTransactionEnvelope_success() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + + String transactionEnvelope = transaction.getTransactionEnvelope(); + kin.base.Transaction transaction2 = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); + + assertThat(transaction.getBaseTransaction().getSourceAccount().getAccountId(), + equalTo(transaction2.getSourceAccount().getAccountId())); + assertThat(transaction.getBaseTransaction().getSequenceNumber(), equalTo(transaction2.getSequenceNumber())); + assertThat(transaction.getBaseTransaction().getFee(), equalTo(transaction2.getFee())); + assertThat(transaction.getTransactionEnvelope(), equalTo(transaction2.toEnvelopeXdrBase64())); + } + + @Test + public void decodeTransaction_success() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + + String transactionEnvelope = transaction.getTransactionEnvelope(); + Transaction transaction2 = Transaction.decodeTransaction(transactionEnvelope); + assertThat(transaction.getSource().getAccountId(), equalTo(transaction2.getSource().getAccountId())); + assertThat(transaction.getBaseTransaction().getSequenceNumber(), + equalTo(transaction2.getBaseTransaction().getSequenceNumber())); + assertThat(transaction.getFee(), equalTo(transaction2.getFee())); + assertThat(transaction.getTransactionEnvelope(), equalTo(transaction2.getTransactionEnvelope())); + } + + @Test + public void addSignature_success() { + KinAccountImpl mockKinAccount = mock(KinAccountImpl.class); + when(mockKinAccount.getKeyPair()).thenReturn(KeyPair.fromSecretSeed(SECRET_SEED_FROM)); + + assertThat(transaction.getBaseTransaction().getSignatures().size(), equalTo(1)); + transaction.addSignature(mockKinAccount); + assertThat(transaction.getBaseTransaction().getSignatures().size(), equalTo(2)); + } + + +} From dfecec8efe3e6c72bf324c76a510a3b4b074e92c Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Wed, 29 May 2019 09:08:43 +0300 Subject: [PATCH 03/56] 1. Fix the transaction exception message because it was printing the wrong message in case of more then one operation. 2. Add ManageDataOperation to the integration linkAccount test. --- .../kotlin/kin/sdk/KinAccountIntegrationTest.kt | 14 ++++++++------ .../sdk/exception/TransactionFailedException.java | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index bc505388..73f077a5 100644 --- a/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -431,7 +431,6 @@ class KinAccountIntegrationTest { assertThat(controlledAccount.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin.multiply(BigDecimal(2))))) assertThat(masterAccount.balanceSync.value(), equalTo(BigDecimal("100.00000").subtract(feeInKin))) assertThat(destinationAccount.balanceSync.value(), equalTo(BigDecimal("121.12300"))) - masterAccount.statusSync } private fun linkAccount(controlledAccount: KinAccount, masterAccount: KinAccount) { @@ -440,17 +439,20 @@ class KinAccountIntegrationTest { val transactionBuilder = controlledAccount.transactionBuilderSync val signerKey = Signer.ed25519PublicKey(KeyPair.fromAccountId(masterAccount.publicAddress)) val managerDataKey = controlledAccount.publicAddress - val managerDataValue = "pId" + val managerDataValue = "pIda" val transaction = transactionBuilder - .setFee(fee)//TODO should we pay twice the fee because 2 operations??? + .setFee(fee) .setMemo("test", "account linking") .addOperation(SetOptionsOperation.Builder().setSigner(signerKey, 1).build()) - .addOperation(ManageDataOperation.Builder(managerDataKey, kin.base.codec.Base64().decode(managerDataValue)).build()) + .addOperation(ManageDataOperation.Builder(managerDataKey, kin.base.codec.Base64().decode(managerDataValue)) + .setSourceAccount(KeyPair.fromAccountId(masterAccount.publicAddress)) + .build()) .build() - // simulate transaction getting a transaction envelope and decode it. + // simulate getting a transaction envelope and decode it. val transactionEnvelope = transaction.transactionEnvelope val externalTransaction = Transaction.decodeTransaction(transactionEnvelope) - // Sending the linking transaction from the master account + // Sign with the master and sending the linking transaction from the master account + externalTransaction.addSignature(masterAccount) masterAccount.sendTransactionSync(externalTransaction) } diff --git a/kin-sdk/src/main/java/kin/sdk/exception/TransactionFailedException.java b/kin-sdk/src/main/java/kin/sdk/exception/TransactionFailedException.java index 8d5097e9..9d80cdbd 100644 --- a/kin-sdk/src/main/java/kin/sdk/exception/TransactionFailedException.java +++ b/kin-sdk/src/main/java/kin/sdk/exception/TransactionFailedException.java @@ -15,16 +15,16 @@ public class TransactionFailedException extends OperationFailedException { public TransactionFailedException(@Nullable String txResultCode, @Nullable List opResultCode) { - super(getMessage(opResultCode)); + super(getMessage(txResultCode, opResultCode)); this.txResultCode = txResultCode; this.opResultCode = opResultCode; } @NonNull - private static String getMessage(@Nullable List opResultCode) { + private static String getMessage(@Nullable String txResultCode, @Nullable List opResultCode) { return opResultCode != null && !opResultCode.isEmpty() ? - "Transaction failed with the error = " + opResultCode.get(0) : + "Transaction failed with the error = " + txResultCode + ", operations = " + opResultCode : "Transaction failed"; } From bfb111c00ac9b9f6fb7bc0b05dfd92c67d0929c3 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Sun, 2 Jun 2019 15:39:53 +0300 Subject: [PATCH 04/56] 1. Add 'aggregateBalance', 'controlledAccounts' 'accountData' 2. --- .../base/requests/AccountsRequestBuilder.java | 53 ++++++ .../kin/base/responses/AccountResponse.java | 16 +- .../responses/AggregatedBalanceResponse.java | 91 ++++++++++ .../responses/ControlledAccountsResponse.java | 92 ++++++++++ .../responses/AccountDeserializerTest.java | 4 +- .../androidTest/java/kin/sdk/IntegConsts.java | 6 +- .../kotlin/kin/sdk/FakeKinOnBoard.kt | 11 +- .../kin/sdk/KinAccountIntegrationTest.kt | 171 ++++++++++++++++-- .../main/java/kin/sdk/AbstractKinAccount.java | 34 ++++ .../src/main/java/kin/sdk/AccountData.java | 106 +++++++++++ .../java/kin/sdk/AccountInfoRetriever.java | 100 ++++++++++ .../main/java/kin/sdk/ControlledAccount.java | 21 +++ .../src/main/java/kin/sdk/Environment.java | 2 +- .../src/main/java/kin/sdk/KinAccount.java | 77 +++++++- .../src/main/java/kin/sdk/KinAccountImpl.java | 22 +++ .../test/java/kin/sdk/TransactionTest.java | 4 - .../src/main/java/sdk/sample/OnBoarding.java | 9 +- 17 files changed, 783 insertions(+), 36 deletions(-) create mode 100644 kin-sdk/kin-base/src/main/java/kin/base/responses/AggregatedBalanceResponse.java create mode 100644 kin-sdk/kin-base/src/main/java/kin/base/responses/ControlledAccountsResponse.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountData.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java diff --git a/kin-sdk/kin-base/src/main/java/kin/base/requests/AccountsRequestBuilder.java b/kin-sdk/kin-base/src/main/java/kin/base/requests/AccountsRequestBuilder.java index 019c2b79..f486ec45 100644 --- a/kin-sdk/kin-base/src/main/java/kin/base/requests/AccountsRequestBuilder.java +++ b/kin-sdk/kin-base/src/main/java/kin/base/requests/AccountsRequestBuilder.java @@ -6,6 +6,8 @@ import java.net.URI; import kin.base.KeyPair; import kin.base.responses.AccountResponse; +import kin.base.responses.AggregatedBalanceResponse; +import kin.base.responses.ControlledAccountsResponse; import kin.base.responses.Page; import okhttp3.OkHttpClient; @@ -40,6 +42,56 @@ public AccountResponse account(KeyPair account) throws IOException { return this.account(this.buildUri()); } + // TODO: 2019-05-29 maybe blockchain need to add the description in their docs. + + /** + * Requests GET /accounts/{account}/aggregate_balance + * + * @param account Account to fetch + * @see Account Details + */ + public AggregatedBalanceResponse aggregateBalance(KeyPair account) throws IOException { + this.setSegments("accounts", account.getAccountId(), "aggregate_balance"); + return this.aggregateBalance(this.buildUri()); + } + + /** + * Requests specific uri and returns {@link AggregatedBalanceResponse}. This method is helpful for + * getting the links. + */ + public AggregatedBalanceResponse aggregateBalance(URI uri) throws IOException { + TypeToken type = new TypeToken() { + }; + ResponseHandler responseHandler = new ResponseHandler( + httpClient, type); + return responseHandler.handleGetRequest(uri); + } + + // TODO: 2019-05-29 maybe blockchain need to add the description in their docs. + + /** + * Requests GET /accounts/{account}/controlled_balances + * + * @param account Account to fetch + * @see Account Details + */ + public ControlledAccountsResponse controlledAccounts(KeyPair account) throws IOException { + this.setSegments("accounts", account.getAccountId(), "controlled_balances"); + return this.controlledAccounts(this.buildUri()); + } + + /** + * Requests specific uri and returns {@link ControlledAccountsResponse}. This method is helpful for + * getting the links. + */ + public ControlledAccountsResponse controlledAccounts(URI uri) throws IOException { + TypeToken type = new TypeToken() { + }; + ResponseHandler responseHandler = new ResponseHandler( + httpClient, type); + return responseHandler.handleGetRequest(uri); + } + /** * Requests specific uri and returns {@link Page} of {@link AccountResponse}. * This method is helpful for getting the next set of results. @@ -97,4 +149,5 @@ public AccountsRequestBuilder order(Order direction) { super.order(direction); return this; } + } diff --git a/kin-sdk/kin-base/src/main/java/kin/base/responses/AccountResponse.java b/kin-sdk/kin-base/src/main/java/kin/base/responses/AccountResponse.java index efc8c831..8851ecbb 100644 --- a/kin-sdk/kin-base/src/main/java/kin/base/responses/AccountResponse.java +++ b/kin-sdk/kin-base/src/main/java/kin/base/responses/AccountResponse.java @@ -3,6 +3,7 @@ import static kin.base.Util.checkNotNull; import com.google.gson.annotations.SerializedName; +import java.util.Map; import kin.base.Asset; import kin.base.AssetTypeNative; import kin.base.KeyPair; @@ -38,6 +39,8 @@ public class AccountResponse extends Response implements TransactionBuilderAccou private Signer[] signers; @SerializedName("_links") private Links links; + @SerializedName("data") + private Map data; AccountResponse(KeyPair keypair) { this.keypair = keypair; @@ -100,6 +103,10 @@ public Signer[] getSigners() { return signers; } + public Map getData() { + return data; + } + /** * Represents account thresholds. */ @@ -138,10 +145,13 @@ public static class Flags { private final boolean authRequired; @SerializedName("auth_revocable") private final boolean authRevocable; + @SerializedName("auth_immutable") + private final boolean authImmutable; - Flags(boolean authRequired, boolean authRevocable) { + Flags(boolean authRequired, boolean authRevocable, boolean authImmutable) { this.authRequired = authRequired; this.authRevocable = authRevocable; + this.authImmutable = authImmutable; } public boolean getAuthRequired() { @@ -151,6 +161,10 @@ public boolean getAuthRequired() { public boolean getAuthRevocable() { return authRevocable; } + + public boolean getAuthImmutable() { + return authImmutable; + } } /** diff --git a/kin-sdk/kin-base/src/main/java/kin/base/responses/AggregatedBalanceResponse.java b/kin-sdk/kin-base/src/main/java/kin/base/responses/AggregatedBalanceResponse.java new file mode 100644 index 00000000..de4f1597 --- /dev/null +++ b/kin-sdk/kin-base/src/main/java/kin/base/responses/AggregatedBalanceResponse.java @@ -0,0 +1,91 @@ +package kin.base.responses; + +import com.google.gson.annotations.SerializedName; +import kin.base.Server; + +/** + * Represents account aggregated balance response. + * + * @see Account + * documentation + * @see Server#accounts() + */ +public class AggregatedBalanceResponse extends Response { + + @SerializedName("_embedded") + private Records records; + @SerializedName("_links") + private Links links; + + public AggregatedBalance getAggregatedBalance() { + return records.getAggregateBalances()[0]; + } + + /** + * Represents records of aggregated balances. + */ + public static class Records { + + @SerializedName("records") + private AggregatedBalance[] aggregatedBalances; + + public AggregatedBalance[] getAggregateBalances() { + return aggregatedBalances; + } + } + + /** + * Represents account aggregated balance. + */ + public static class AggregatedBalance { + + @SerializedName("account_id") + private String accountId; + @SerializedName("aggregate_balance") + private String aggregateBalance; + + public String getAccountId() { + return accountId; + } + + public String getAggregateBalance() { + return aggregateBalance; + } + } + + public Links getLinks() { + return links; + } + + /** + * Links connected to account. + */ + public static class Links { + + @SerializedName("self") + private final Link self; + @SerializedName("next") + private final Link next; + @SerializedName("prev") + private final Link prev; + + Links(Link self, Link next, Link prev) { + this.self = self; + this.next = next; + this.prev = prev; + } + + public Link getSelf() { + return self; + } + + public Link getNext() { + return next; + } + + public Link getPrev() { + return prev; + } + } +} + diff --git a/kin-sdk/kin-base/src/main/java/kin/base/responses/ControlledAccountsResponse.java b/kin-sdk/kin-base/src/main/java/kin/base/responses/ControlledAccountsResponse.java new file mode 100644 index 00000000..5bcc9515 --- /dev/null +++ b/kin-sdk/kin-base/src/main/java/kin/base/responses/ControlledAccountsResponse.java @@ -0,0 +1,92 @@ +package kin.base.responses; + +import com.google.gson.annotations.SerializedName; +import kin.base.Server; + +/** + * Represents controlled account response. + * + * @see Account + * documentation + * @see Server#accounts() + */ +public class ControlledAccountsResponse { + + + @SerializedName("_embedded") + private Records records; + @SerializedName("_links") + private Links links; + + public ControlledAccount[] getControlledAccounts() { + return records.getControlledAccounts(); + } + + /** + * Represents records of aggregated balances. + */ + public static class Records { + + @SerializedName("records") + private ControlledAccount[] controlledAccounts; + + public ControlledAccount[] getControlledAccounts() { + return controlledAccounts; + } + } + + /** + * Represents account aggregated balance. + */ + public static class ControlledAccount { + + @SerializedName("account_id") + private String accountId; + @SerializedName("balance") + private String balance; + + public String getAccountId() { + return accountId; + } + + public String getBalance() { + return balance; + } + } + + public Links getLinks() { + return links; + } + + /** + * Links connected to account. + */ + public static class Links { + + @SerializedName("self") + private final Link self; + @SerializedName("next") + private final Link next; + @SerializedName("prev") + private final Link prev; + + Links(Link self, Link next, Link prev) { + this.self = self; + this.next = next; + this.prev = prev; + } + + public Link getSelf() { + return self; + } + + public Link getNext() { + return next; + } + + public Link getPrev() { + return prev; + } + } +} + diff --git a/kin-sdk/kin-base/src/test/java/kin/base/responses/AccountDeserializerTest.java b/kin-sdk/kin-base/src/test/java/kin/base/responses/AccountDeserializerTest.java index 66a156f7..3b44f46b 100644 --- a/kin-sdk/kin-base/src/test/java/kin/base/responses/AccountDeserializerTest.java +++ b/kin-sdk/kin-base/src/test/java/kin/base/responses/AccountDeserializerTest.java @@ -20,6 +20,7 @@ public void testDeserialize() { assertEquals(account.getFlags().getAuthRequired(), false); assertEquals(account.getFlags().getAuthRevocable(), true); + assertEquals(account.getFlags().getAuthImmutable(), false); assertEquals(account.getBalances()[0].getAssetType(), "credit_alphanum4"); assertEquals(account.getBalances()[0].getAssetCode(), "ABC"); @@ -79,7 +80,8 @@ public void testDeserialize() { " },\n" + " \"flags\": {\n" + " \"auth_required\": false,\n" + - " \"auth_revocable\": true\n" + + " \"auth_revocable\": true,\n" + + " \"auth_immutable\": false\n" + " },\n" + " \"balances\": [\n" + " {\n" + diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java index 529d12a4..6b3f329a 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java +++ b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java @@ -3,9 +3,9 @@ final class IntegConsts { - static final String TEST_NETWORK_URL = "https://horizon-testnet.kininfrastructure.com/"; + static final String TEST_NETWORK_URL = "http://horizon-testnet-one-wallet.kininfrastructure.com/"; static final String TEST_NETWORK_ID = "Kin Testnet ; December 2018"; - static final String URL_CREATE_ACCOUNT = "https://friendbot-testnet.kininfrastructure.com?addr=%s&amount=%d"; - static final String URL_FUND = "https://friendbot-testnet.kininfrastructure.com/fund?addr=%s&amount="; // faucet + static final String URL_CREATE_ACCOUNT = "https://friendbot.developers.kinecosystem.com?addr=%s&amount=%s"; + static final String URL_FUND = "https://friendbot.developers.kinecosystem.com/fund?addr=%s&amount="; // faucet } diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt index b3635188..bead1128 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt @@ -11,17 +11,16 @@ import java.util.concurrent.TimeUnit /** * Fake on board for integration test, support creating and funding accounts on stellar test net */ -internal class FakeKinOnBoard @Throws(IOException::class) -constructor() { +internal class FakeKinOnBoard @Throws(IOException::class) constructor() { private val client: OkHttpClient = OkHttpClient.Builder() - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .writeTimeout(30, TimeUnit.SECONDS) + .connectTimeout(40, TimeUnit.SECONDS) + .readTimeout(40, TimeUnit.SECONDS) + .writeTimeout(40, TimeUnit.SECONDS) .build() @Throws(Exception::class) - fun createAccount(destinationAccount: String, amount: Int = 0) { + fun createAccount(destinationAccount: String, amount: String = "0") { val request = Request.Builder() .url(String.format(URL_CREATE_ACCOUNT, destinationAccount, amount)).build() client.newCall(request).execute()?.let { diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index 74ee25a7..04de8f7f 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -7,10 +7,9 @@ import kin.sdk.IntegConsts.TEST_NETWORK_URL import kin.sdk.exception.AccountNotFoundException import kin.sdk.exception.InsufficientFeeException import kin.sdk.exception.InsufficientKinException -import org.hamcrest.CoreMatchers.* +import kin.sdk.exception.TransactionFailedException import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.isEmptyOrNullString -import org.hamcrest.Matchers.isEmptyString +import org.hamcrest.Matchers.* import org.junit.* import org.junit.rules.ExpectedException import java.io.IOException @@ -18,6 +17,7 @@ import java.math.BigDecimal import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit +import kotlin.test.assertFailsWith import kotlin.test.assertNotNull import kotlin.test.assertTrue import kotlin.test.fail @@ -87,6 +87,148 @@ class KinAccountIntegrationTest { } + @Test + @LargeTest + fun getAggregatedBalance_FundAccounts_GotAggregatedBalance() { + val kinAccount1 = kinClient.addAccount() + val kinAccount2 = kinClient.addAccount() + val masterAccount = kinClient.addAccount() + + fakeKinOnBoard.createAccount(kinAccount1.publicAddress.orEmpty(), "10.14159") + fakeKinOnBoard.createAccount(kinAccount2.publicAddress.orEmpty(), "100.00301") + fakeKinOnBoard.createAccount(masterAccount.publicAddress.orEmpty(), "20.02500") + + linkAccount(kinAccount1, masterAccount, "some package id 1") + linkAccount(kinAccount2, masterAccount, "some package id 2") + + // multiply the fee in 4 because there were 4 operations(2 in each linkAccount call) + assertThat(masterAccount.aggregatedBalanceSync.value(), equalTo(BigDecimal("130.16960") + .subtract(feeInKin.multiply(BigDecimal(4))))) + } + + @Test + @LargeTest + fun getAggregatedBalance_OneOfTheAccountsWasNotCreated_AccountNotFoundException() { + val kinAccount1 = kinClient.addAccount() + val kinAccount2 = kinClient.addAccount() + val masterAccount = kinClient.addAccount() + + fakeKinOnBoard.createAccount(kinAccount1.publicAddress.orEmpty(), "10.14159") + fakeKinOnBoard.createAccount(masterAccount.publicAddress.orEmpty(), "20.02500") + + + expectedEx.expect(AccountNotFoundException::class.java) + expectedEx.expectMessage(kinAccount2.publicAddress.orEmpty()) + + linkAccount(kinAccount1, masterAccount, "some package id 1") + linkAccount(kinAccount2, masterAccount, "some package id 2") + } + + @Test + @LargeTest + fun linkAccount_MasterAccountNotCreated_OpNoSourceAccount() { + val exception = assertFailsWith { + + val kinAccount = kinClient.addAccount() + val masterAccount = kinClient.addAccount() + + onboardSingleAccount(kinAccount, 10.14159) + + linkAccount(kinAccount, masterAccount, "some package id") + } + + assertThat(exception.transactionResultCode, equalTo("tx_failed")) + assertThat(exception.operationsResultCodes?.get(1), equalTo("op_no_source_account")) + } + + @Test + @LargeTest + fun getControlledAccounts_LinkAccounts_GotAllControlledAccounts() { + val kinAccount1 = kinClient.addAccount() + val kinAccount2 = kinClient.addAccount() + val kinAccount3 = kinClient.addAccount() + val masterAccount = kinClient.addAccount() + + fakeKinOnBoard.createAccount(kinAccount1.publicAddress.orEmpty(), "10.14159") + fakeKinOnBoard.createAccount(kinAccount2.publicAddress.orEmpty(), "100.00301") + fakeKinOnBoard.createAccount(kinAccount3.publicAddress.orEmpty(), "5.00000") + fakeKinOnBoard.createAccount(masterAccount.publicAddress.orEmpty(), "20.02500") + + linkAccount(kinAccount1, masterAccount, "some package id 1") + linkAccount(kinAccount2, masterAccount, "some package id 2") + linkAccount(kinAccount3, masterAccount, "some package id 3") + + val controlledAccounts = masterAccount.controlledAccountsSync + assertThat(controlledAccounts, hasSize(4)) + // sort the array because we don't know in what order we are getting it + controlledAccounts.sortBy { it.balance.value() } + assertThat(controlledAccounts[0].balance.value(), equalTo(BigDecimal("5.00000").subtract(feeInKin.multiply(BigDecimal(2))))) + assertThat(controlledAccounts[0].publicAddress, equalTo(kinAccount3.publicAddress.orEmpty())) + assertThat(controlledAccounts[1].balance.value(), equalTo(BigDecimal("10.14159").subtract(feeInKin.multiply(BigDecimal(2))))) + assertThat(controlledAccounts[1].publicAddress, equalTo(kinAccount1.publicAddress.orEmpty())) + assertThat(controlledAccounts[2].balance.value(), equalTo(BigDecimal("20.02500"))) + assertThat(controlledAccounts[2].publicAddress, equalTo(masterAccount.publicAddress.orEmpty())) + assertThat(controlledAccounts[3].balance.value(), equalTo(BigDecimal("100.00301").subtract(feeInKin.multiply(BigDecimal(2))))) + assertThat(controlledAccounts[3].publicAddress, equalTo(kinAccount2.publicAddress.orEmpty())) + } + + @Test + @LargeTest + fun getControlledAccounts_NoAccounts_EmptyListOfAccounts() { + val masterAccount = kinClient.addAccount() + + fakeKinOnBoard.createAccount(masterAccount.publicAddress.orEmpty(), "20.02500") + + val controlledAccounts = masterAccount.controlledAccountsSync + assertThat(controlledAccounts, hasSize(0)) + } + + @Test + @LargeTest + fun getControlledAccounts_NoAccounts_AccountNotFoundException() { + val masterAccount = kinClient.addAccount() + + expectedEx.expect(AccountNotFoundException::class.java) + expectedEx.expectMessage(masterAccount.publicAddress.orEmpty()) + + masterAccount.controlledAccountsSync + } + + @Test + @LargeTest + fun getAccountData_CreateAccount_DataCorrect() { + val kinAccount = kinClient.addAccount() + + fakeKinOnBoard.createAccount(kinAccount.publicAddress.orEmpty(), "10.14159") + val accountData = kinAccount.accountDataSync + assertThat(accountData.publicAddress, equalTo(kinAccount.publicAddress)) + assertThat(accountData.balances[0].balance, equalTo("10.14159")) + } + + @Test + @LargeTest + fun getAccountData_NoAccount_AccountNotFoundException() { + val kinAccount = kinClient.addAccount() + + expectedEx.expect(AccountNotFoundException::class.java) + expectedEx.expectMessage(kinAccount.publicAddress.orEmpty()) + kinAccount.accountDataSync + } + + @Test + @LargeTest + fun linkAccount_AccountNotCreated_AccountNotFoundException() { + val kinAccount = kinClient.addAccount() + val masterAccount = kinClient.addAccount() + + onboardSingleAccount(masterAccount, 20.02500) + + expectedEx.expect(AccountNotFoundException::class.java) + expectedEx.expectMessage(kinAccount.publicAddress.orEmpty()) + + linkAccount(kinAccount, masterAccount, "some package id") + } + @Test @LargeTest @Throws(Exception::class) @@ -356,8 +498,10 @@ class KinAccountIntegrationTest { val controlledAccount = kinClient.addAccount() val masterAccount = kinClient.addAccount() val destinationAccount = kinClient.addAccount() - onboardSingleAccount(destinationAccount, 100) - linkAccount(controlledAccount, masterAccount) + onboardSingleAccount(controlledAccount, 100.0) + onboardSingleAccount(masterAccount, 100.0) + onboardSingleAccount(destinationAccount, 100.0) + linkAccount(controlledAccount, masterAccount, "some package id ") val transaction = masterAccount.transactionBuilderSync .setFee(fee) .setMemo(appId, "master to destination") @@ -373,24 +517,23 @@ class KinAccountIntegrationTest { assertThat(controlledAccount.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin.multiply(BigDecimal(2))))) assertThat(masterAccount.balanceSync.value(), equalTo(BigDecimal("100.00000").subtract(feeInKin))) assertThat(destinationAccount.balanceSync.value(), equalTo(BigDecimal("121.12300"))) + val packageIdInBase64 = masterAccount.accountDataSync.data[controlledAccount.publicAddress] + assertThat(String(kin.base.codec.Base64.decodeBase64(packageIdInBase64)), containsString("some package id")) } - private fun linkAccount(controlledAccount: KinAccount, masterAccount: KinAccount) { - onboardSingleAccount(controlledAccount, 100) - onboardSingleAccount(masterAccount, 100) + private fun linkAccount(controlledAccount: KinAccount, masterAccount: KinAccount, managerDataValue: String) { val transactionBuilder = controlledAccount.transactionBuilderSync val signerKey = Signer.ed25519PublicKey(KeyPair.fromAccountId(masterAccount.publicAddress)) val managerDataKey = controlledAccount.publicAddress - val managerDataValue = "pIda" val transaction = transactionBuilder .setFee(fee) .setMemo("test", "account linking") .addOperation(SetOptionsOperation.Builder().setSigner(signerKey, 1).build()) - .addOperation(ManageDataOperation.Builder(managerDataKey, kin.base.codec.Base64().decode(managerDataValue)) + .addOperation(ManageDataOperation.Builder(managerDataKey, managerDataValue.toByteArray()) .setSourceAccount(KeyPair.fromAccountId(masterAccount.publicAddress)) .build()) .build() - // simulate getting a transaction envelope and decode it. + // Simulate getting a transaction envelope and decode it. val transactionEnvelope = transaction.transactionEnvelope val externalTransaction = Transaction.decodeTransaction(transactionEnvelope) // Sign with the master and sending the linking transaction from the master account @@ -402,12 +545,12 @@ class KinAccountIntegrationTest { receiverFundAmount: Int = 0): Pair { val kinAccountSender = kinClient.addAccount() val kinAccountReceiver = kinClient.addAccount() - fakeKinOnBoard.createAccount(kinAccountSender.publicAddress.orEmpty(), senderFundAmount) - fakeKinOnBoard.createAccount(kinAccountReceiver.publicAddress.orEmpty(), receiverFundAmount) + fakeKinOnBoard.createAccount(kinAccountSender.publicAddress.orEmpty(), senderFundAmount.toString()) + fakeKinOnBoard.createAccount(kinAccountReceiver.publicAddress.orEmpty(), receiverFundAmount.toString()) return Pair(kinAccountSender, kinAccountReceiver) } - private fun onboardSingleAccount(account: KinAccount, fundAmount: Int) { + private fun onboardSingleAccount(account: KinAccount, fundAmount: Double) { fakeKinOnBoard.createAccount(account.publicAddress.orEmpty()) fakeKinOnBoard.fundWithKin(account.publicAddress.orEmpty(), fundAmount.toString()) } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java index 8fd6fda2..1aed0514 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java @@ -3,6 +3,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.math.BigDecimal; +import java.util.List; import java.util.concurrent.Callable; import kin.utils.Request; @@ -76,6 +77,39 @@ public Balance call() throws Exception { }); } + @NonNull + @Override + public Request getAggregatedBalance() { + return new Request<>(new Callable() { + @Override + public Balance call() throws Exception { + return getAggregatedBalanceSync(); + } + }); + } + + @NonNull + @Override + public Request> getControlledAccounts() { + return new Request<>(new Callable>() { + @Override + public List call() throws Exception { + return getControlledAccountsSync(); + } + }); + } + + @NonNull + @Override + public Request getAccountData() { + return new Request<>(new Callable() { + @Override + public AccountData call() throws Exception { + return getAccountDataSync(); + } + }); + } + @NonNull @Override public Request getStatus() { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountData.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountData.java new file mode 100644 index 00000000..592f6333 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountData.java @@ -0,0 +1,106 @@ +package kin.sdk; + +import java.util.Arrays; +import java.util.Map; +import kin.base.responses.AccountResponse; +import kin.base.responses.AccountResponse.Flags; +import kin.base.responses.AccountResponse.Signer; + +public class AccountData { + + private String publicAddress; + private long sequenceNumber; + private String pagingToken; + private Integer subentryCount; + private AccountResponse.Thresholds thresholds; + private AccountResponse.Flags flags; + private AccountResponse.Balance[] balances; + private AccountResponse.Signer[] signers; + private Map data; + + public AccountData(String publicAddress, long sequenceNumber, String pagingToken, Integer subentryCount, + AccountResponse.Thresholds thresholds, Flags flags, AccountResponse.Balance[] balances, + Signer[] signers, Map data) { + this.publicAddress = publicAddress; + this.sequenceNumber = sequenceNumber; + this.pagingToken = pagingToken; + this.subentryCount = subentryCount; + this.thresholds = thresholds; + this.flags = flags; + this.balances = balances; + this.signers = signers; + this.data = data; + } + + public String getPublicAddress() { + return publicAddress; + } + + public long getSequenceNumber() { + return sequenceNumber; + } + + public String getPagingToken() { + return pagingToken; + } + + public Integer getSubentryCount() { + return subentryCount; + } + + public AccountResponse.Thresholds getThresholds() { + return thresholds; + } + + public Flags getFlags() { + return flags; + } + + public AccountResponse.Balance[] getBalances() { + return balances; + } + + public Signer[] getSigners() { + return signers; + } + + public Map getData() { + return data; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AccountData that = (AccountData) o; + return sequenceNumber == that.sequenceNumber && + publicAddress.equals(that.publicAddress) && + pagingToken.equals(that.pagingToken) && + subentryCount.equals(that.subentryCount) && + thresholds.equals(that.thresholds) && + flags.equals(that.flags) && + Arrays.equals(balances, that.balances) && + Arrays.equals(signers, that.signers) && + data.equals(that.data); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((publicAddress == null) ? 0 : publicAddress.hashCode()); + result = prime * result + Long.valueOf(sequenceNumber).hashCode(); + result = prime * result + ((pagingToken == null) ? 0 : pagingToken.hashCode()); + result = prime * result + ((subentryCount == null) ? 0 : subentryCount.hashCode()); + result = prime * result + ((thresholds == null) ? 0 : thresholds.hashCode()); + result = prime * result + ((flags == null) ? 0 : flags.hashCode()); + result = prime * result + ((data == null) ? 0 : data.hashCode()); + result = prime * result + Arrays.hashCode(balances); + result = prime * result + Arrays.hashCode(signers); + return result; + } +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountInfoRetriever.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountInfoRetriever.java index 32edda7d..4f7a4fd7 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountInfoRetriever.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountInfoRetriever.java @@ -4,9 +4,13 @@ import android.support.annotation.NonNull; import java.io.IOException; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; import kin.base.KeyPair; import kin.base.Server; import kin.base.responses.AccountResponse; +import kin.base.responses.AggregatedBalanceResponse; +import kin.base.responses.ControlledAccountsResponse; import kin.base.responses.HttpResponseException; import kin.sdk.exception.AccountNotFoundException; import kin.sdk.exception.OperationFailedException; @@ -58,6 +62,101 @@ Balance getBalance(@NonNull String accountId) throws OperationFailedException { return balance; } + Balance getAggregatedBalance(@NonNull String accountId) throws OperationFailedException { + Utils.checkNotNull(accountId, "account"); + Balance balance = null; + + try { + AggregatedBalanceResponse aggregatedBalanceResponse = server.accounts() + .aggregateBalance(KeyPair.fromAccountId(accountId)); + if (aggregatedBalanceResponse == null || aggregatedBalanceResponse.getAggregatedBalance() == null) { + throw new OperationFailedException("can't retrieve data for account " + accountId); + } + String aggregateBalance = aggregatedBalanceResponse.getAggregatedBalance().getAggregateBalance(); + if (aggregateBalance != null && !aggregateBalance.isEmpty()) { + balance = new BalanceImpl(new BigDecimal(aggregateBalance)); + } + } catch (HttpResponseException httpError) { + if (httpError.getStatusCode() == 404) { + throw new AccountNotFoundException(accountId); + } else { + throw new OperationFailedException(httpError); + } + } catch (IOException e) { + throw new OperationFailedException(e); + } + if (balance == null) { + throw new OperationFailedException(accountId); + } + + return balance; + } + + List getControlledAccounts(@NonNull String accountId) throws OperationFailedException { + Utils.checkNotNull(accountId, "account"); + List controlledAccounts = new ArrayList<>(); + + try { + ControlledAccountsResponse controlledAccountsResponse = server.accounts() + .controlledAccounts(KeyPair.fromAccountId(accountId)); + if (controlledAccountsResponse == null || controlledAccountsResponse.getControlledAccounts() == null) { + throw new OperationFailedException("can't retrieve data for account " + accountId); + } + + for (kin.base.responses.ControlledAccountsResponse.ControlledAccount account : + controlledAccountsResponse.getControlledAccounts()) { + ControlledAccount controlledAccount = new ControlledAccount( + new BalanceImpl(new BigDecimal(account.getBalance())), account.getAccountId()); + controlledAccounts.add(controlledAccount); + } + } catch (HttpResponseException httpError) { + if (httpError.getStatusCode() == 404) { + throw new AccountNotFoundException(accountId); + } else { + throw new OperationFailedException(httpError); + } + } catch (IOException e) { + throw new OperationFailedException(e); + } + + return controlledAccounts; + } + + /** + * Get the account data for the specified account. + * + * @param accountId the account ID to check balance + * @return the account data {@link AccountData} + * @throws AccountNotFoundException if account not created yet + * @throws OperationFailedException any other error + */ + AccountData getAccountData(@NonNull String accountId) throws OperationFailedException { + Utils.checkNotNull(accountId, "account"); + AccountData accountData; + + try { + AccountResponse accountResponse = server.accounts().account(KeyPair.fromAccountId(accountId)); + if (accountResponse == null) { + throw new OperationFailedException("can't retrieve data for account " + accountId); + } + accountData = new AccountData(accountResponse.getKeypair().getAccountId(), + accountResponse.getSequenceNumber(), accountResponse.getPagingToken(), + accountResponse.getSubentryCount(), accountResponse.getThresholds(), + accountResponse.getFlags(), accountResponse.getBalances(), accountResponse.getSigners(), + accountResponse.getData()); + } catch (HttpResponseException httpError) { + if (httpError.getStatusCode() == 404) { + throw new AccountNotFoundException(accountId); + } else { + throw new OperationFailedException(httpError); + } + } catch (IOException e) { + throw new OperationFailedException(e); + } + + return accountData; + } + @AccountStatus int getStatus(@NonNull String accountId) throws OperationFailedException { try { @@ -67,4 +166,5 @@ int getStatus(@NonNull String accountId) throws OperationFailedException { return AccountStatus.NOT_CREATED; } } + } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java new file mode 100644 index 00000000..4ce63396 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java @@ -0,0 +1,21 @@ +package kin.sdk; + +public class ControlledAccount { + + private Balance balance; + private String publicAddress; + + ControlledAccount(Balance balance, String publicAddress) { + this.balance = balance; + this.publicAddress = publicAddress; + } + + public Balance getBalance() { + return balance; + } + + public String getPublicAddress() { + return publicAddress; + } + +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Environment.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Environment.java index baa6e55e..ab572020 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Environment.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Environment.java @@ -14,7 +14,7 @@ public class Environment { "Kin Mainnet ; December 2018"); public static final Environment TEST = - new Environment("https://horizon-testnet.kininfrastructure.com/", + new Environment("http://horizon-testnet-one-wallet.kininfrastructure.com//", "Kin Testnet ; December 2018"); private final String networkUrl; diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java index 4e6449da..aa5dfb28 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java @@ -3,6 +3,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.math.BigDecimal; +import java.util.List; import kin.sdk.exception.AccountNotFoundException; import kin.sdk.exception.CryptoException; import kin.sdk.exception.InsufficientKinException; @@ -42,10 +43,22 @@ public interface KinAccount { */ Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo); - + /** + * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. + *

See {@link KinAccount#getTransactionBuilderSync()} for possibles errors

+ * + * @return {@code TransactionBuilder}, TransactionBuilder - the builder for the transaction + */ Request getTransactionBuilder(); - // TODO: 2019-05-26 add java doc + /** + * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. + *

Note: This method accesses the network, and should not be called on the android main thread.

+ * + * @return a Transaction Builder object. + * @throws AccountNotFoundException if the sender or destination account was not created. + * @throws OperationFailedException other error occurred. + */ TransactionBuilder getTransactionBuilderSync() throws OperationFailedException; /** @@ -143,6 +156,66 @@ public interface KinAccount { @NonNull Balance getBalanceSync() throws OperationFailedException; + /** + * Create {@link Request} for getting the current confirmed aggregated balance in kin + *

See {@link KinAccount#getAggregatedBalanceSync()} for possibles errors

+ * + * @return {@code Request} Balance - the aggregated balance in kin + */ + @NonNull + Request getAggregatedBalance(); + + /** + * Get the current confirmed aggregated balance in kin + *

Note: This method accesses the network, and should not be called on the android main thread.

+ * + * @return the aggregated balance in kin + * @throws AccountNotFoundException if account was not created + * @throws OperationFailedException any other error + */ + @NonNull + Balance getAggregatedBalanceSync() throws OperationFailedException; + + /** + * Create {@link Request} for getting list of controlled accounts + *

See {@link KinAccount#getControlledAccountsSync()} for possibles errors

+ * + * @return {@code Request>} List - the list of controlled accounts + */ + @NonNull + Request> getControlledAccounts(); + + /** + * Get the current list of controlled accounts + *

Note: This method accesses the network, and should not be called on the android main thread.

+ * + * @return the list of controlled accounts + * @throws AccountNotFoundException if account was not created + * @throws OperationFailedException any other error + */ + @NonNull + List getControlledAccountsSync() throws OperationFailedException; + + /** + * Create {@link Request} for getting the current account data + *

See {@link KinAccount#getAccountDataSync()} for possibles errors

+ * + * @return {@code Request>} AccountData - the current account data + */ + @NonNull + Request getAccountData() throws OperationFailedException; + + /** + * Get the current account data + *

Note: This method accesses the network, and should not be called on the android main thread.

+ * + * @return the current account data + * @throws AccountNotFoundException if account was not created + * @throws OperationFailedException any other error + */ + @NonNull + AccountData getAccountDataSync() throws OperationFailedException; + /** * Get current account status on blockchain network. * diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java index f89d2abc..30150af1 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java @@ -3,6 +3,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.math.BigDecimal; +import java.util.List; import kin.base.KeyPair; import kin.sdk.exception.AccountDeletedException; import kin.sdk.exception.CryptoException; @@ -76,6 +77,27 @@ public Balance getBalanceSync() throws OperationFailedException { return accountInfoRetriever.getBalance(account.getAccountId()); } + @NonNull + @Override + public Balance getAggregatedBalanceSync() throws OperationFailedException { + checkValidAccount(); + return accountInfoRetriever.getAggregatedBalance(account.getAccountId()); + } + + @NonNull + @Override + public List getControlledAccountsSync() throws OperationFailedException { + checkValidAccount(); + return accountInfoRetriever.getControlledAccounts(account.getAccountId()); + } + + @NonNull + @Override + public AccountData getAccountDataSync() throws OperationFailedException { + checkValidAccount(); + return accountInfoRetriever.getAccountData(account.getAccountId()); + } + @Override public int getStatusSync() throws OperationFailedException { checkValidAccount(); diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java index 61eca445..ca78426f 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java @@ -10,7 +10,6 @@ import kin.base.CreateAccountOperation; import kin.base.KeyPair; import kin.base.Network; -import kin.base.Server; import okhttp3.mockwebserver.MockWebServer; import org.junit.Before; import org.junit.Test; @@ -27,7 +26,6 @@ public class TransactionTest { private static final String SECRET_SEED_FROM = "SB73L5FFTZMN6FHTOOWYEBVFTLUWQEWBLSCI4WLZADRJWENDBYL6QD6P"; private MockWebServer mockWebServer; - private Server server; private Transaction transaction; @Before @@ -57,8 +55,6 @@ private void createTransaction() { private void mockServer() throws IOException { mockWebServer = new MockWebServer(); mockWebServer.start(); - String url = mockWebServer.url("").toString(); - server = new Server(url); } @Test diff --git a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/OnBoarding.java b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/OnBoarding.java index 7fa4e92f..ece24727 100644 --- a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/OnBoarding.java +++ b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/OnBoarding.java @@ -19,7 +19,8 @@ class OnBoarding { private static final int FUND_KIN_AMOUNT = 6000; - private static final String URL_CREATE_ACCOUNT = "https://friendbot-testnet.kininfrastructure.com?addr=%s&amount=" + String.valueOf(FUND_KIN_AMOUNT); + private static final String URL_CREATE_ACCOUNT = + "https://friendbot.developers.kinecosystem.com?addr=%s&amount=" + String.valueOf(FUND_KIN_AMOUNT); private final OkHttpClient okHttpClient; private final Handler handler; private ListenerRegistration listenerRegistration; @@ -34,8 +35,8 @@ public interface Callbacks { OnBoarding() { handler = new Handler(Looper.getMainLooper()); okHttpClient = new OkHttpClient.Builder() - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + .connectTimeout(40, TimeUnit.SECONDS) + .readTimeout(40, TimeUnit.SECONDS) .build(); } @@ -49,7 +50,7 @@ void onBoard(@NonNull KinAccount account, @NonNull Callbacks callbacks) { handler.removeCallbacks(accountCreationListeningTimeout); fireOnSuccess(callbacks); }); - handler.postDelayed(accountCreationListeningTimeout, 20 * DateUtils.SECOND_IN_MILLIS); + handler.postDelayed(accountCreationListeningTimeout, 40 * DateUtils.SECOND_IN_MILLIS); createAccount(account, callbacks); } From 619340ff8d6c940e0c97672e7331eeab7d72f80f Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Sun, 2 Jun 2019 15:40:53 +0300 Subject: [PATCH 05/56] Add a TODO --- kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Environment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Environment.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Environment.java index ab572020..06ffd937 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Environment.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Environment.java @@ -13,6 +13,7 @@ public class Environment { new Environment("https://horizon.kinfederation.com", "Kin Mainnet ; December 2018"); + // TODO: 2019-06-02 change this and other test places to the regular test-net public static final Environment TEST = new Environment("http://horizon-testnet-one-wallet.kininfrastructure.com//", "Kin Testnet ; December 2018"); From 6179ebb2b8fcafab48d98268618db037ac85633c Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 3 Jun 2019 09:24:14 +0300 Subject: [PATCH 06/56] Add more java doc and some more unit tests --- .../androidTest/java/kin/sdk/IntegConsts.java | 2 +- .../src/main/java/kin/sdk/KinAccount.java | 22 ++--- .../test/java/kin/sdk/KinAccountImplTest.java | 91 ++++++++++++++++++- 3 files changed, 102 insertions(+), 13 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java index 9f4e53ad..6b3f329a 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java +++ b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java @@ -5,7 +5,7 @@ final class IntegConsts { static final String TEST_NETWORK_URL = "http://horizon-testnet-one-wallet.kininfrastructure.com/"; static final String TEST_NETWORK_ID = "Kin Testnet ; December 2018"; - static final String URL_CREATE_ACCOUNT = "https://friendbot.developers.kinecosystem.com?addr=%s&amount=%d"; + static final String URL_CREATE_ACCOUNT = "https://friendbot.developers.kinecosystem.com?addr=%s&amount=%s"; static final String URL_FUND = "https://friendbot.developers.kinecosystem.com/fund?addr=%s&amount="; // faucet } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java index aa5dfb28..af453e5b 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java @@ -47,20 +47,10 @@ public interface KinAccount { * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. *

See {@link KinAccount#getTransactionBuilderSync()} for possibles errors

* - * @return {@code TransactionBuilder}, TransactionBuilder - the builder for the transaction + * @return {@code Request}, TransactionBuilder - the builder for the transaction */ Request getTransactionBuilder(); - /** - * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. - *

Note: This method accesses the network, and should not be called on the android main thread.

- * - * @return a Transaction Builder object. - * @throws AccountNotFoundException if the sender or destination account was not created. - * @throws OperationFailedException other error occurred. - */ - TransactionBuilder getTransactionBuilderSync() throws OperationFailedException; - /** * Create {@link Request} for signing and sending a transaction *

See {@link KinAccount#sendTransactionSync(Transaction)} for possibles errors

@@ -107,6 +97,16 @@ public interface KinAccount { */ Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException; + /** + * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. + *

Note: This method accesses the network, and should not be called on the android main thread.

+ * + * @return a Transaction Builder object. + * @throws AccountNotFoundException if the sender or destination account was not created. + * @throws OperationFailedException other error occurred. + */ + TransactionBuilder getTransactionBuilderSync() throws OperationFailedException; + /** * send a transaction. *

Note: This method accesses the network, and should not be called on the android main thread.

diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java index 7c724ede..da53e78d 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java @@ -2,12 +2,18 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import kin.base.KeyPair; import kin.sdk.exception.AccountDeletedException; import org.junit.Before; @@ -92,6 +98,62 @@ public void getBalanceSync() throws Exception { verify(mockAccountInfoRetriever).getBalance(expectedRandomAccount.getAccountId()); } + @Test + public void getAggregatedBalanceSync() throws Exception { + initWithRandomAccount(); + + Balance expectedAggregatedBalance = new BalanceImpl(new BigDecimal("150.0")); + when(mockAccountInfoRetriever.getAggregatedBalance(anyString())).thenReturn(expectedAggregatedBalance); + + Balance balance = kinAccount.getAggregatedBalanceSync(); + + assertEquals(expectedAggregatedBalance, balance); + verify(mockAccountInfoRetriever).getAggregatedBalance(expectedRandomAccount.getAccountId()); + } + + @Test + public void getControlledAccountsSync() throws Exception { + initWithRandomAccount(); + + ControlledAccount controlledAccount1 = new ControlledAccount(new BalanceImpl(new BigDecimal("150.0")), + "first account public Address"); + ControlledAccount controlledAccount2 = new ControlledAccount(new BalanceImpl(new BigDecimal("10.0")), + "second account public Address"); + ControlledAccount controlledAccount3 = new ControlledAccount(new BalanceImpl(new BigDecimal("55.0")), + "third account public Address"); + List expectedControlledAccounts = new ArrayList<>(); + expectedControlledAccounts.add(controlledAccount1); + expectedControlledAccounts.add(controlledAccount2); + expectedControlledAccounts.add(controlledAccount3); + + when(mockAccountInfoRetriever.getControlledAccounts(anyString())).thenReturn(expectedControlledAccounts); + + List controlledAccounts = kinAccount.getControlledAccountsSync(); + + assertThat(expectedControlledAccounts, equalTo(controlledAccounts)); + verify(mockAccountInfoRetriever).getControlledAccounts(expectedRandomAccount.getAccountId()); + } + + @Test + public void getAccountDataSync() throws Exception { + initWithRandomAccount(); + + Map data = new HashMap<>(); + data.put("public address 1", "package id 1"); + data.put("public address 2", "package id 2"); + data.put("public address 3", "package id 3"); + AccountData expectedAccountData = new AccountData(expectedRandomAccount.getAccountId(), + 123456789L, "paging token,", 1, + null, null, null, null, data); + + when(mockAccountInfoRetriever.getAccountData(anyString())).thenReturn(expectedAccountData); + + AccountData accountData = kinAccount.getAccountDataSync(); + + assertThat(expectedAccountData, equalTo(accountData)); + verify(mockAccountInfoRetriever).getAccountData(expectedRandomAccount.getAccountId()); + } + @Test public void getStatusSync() throws Exception { initWithRandomAccount(); @@ -109,7 +171,9 @@ public void sendTransactionSync_DeletedAccount_Exception() throws Exception { initWithRandomAccount(); kinAccount.markAsDeleted(); - Transaction transaction = kinAccount.buildTransactionSync("GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX", new BigDecimal("12.2"), 100); + Transaction transaction = kinAccount + .buildTransactionSync("GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX", new BigDecimal("12.2"), + 100); kinAccount.sendTransactionSync(transaction); } @@ -138,6 +202,31 @@ public void getStatusSync_DeletedAccount_Exception() throws Exception { kinAccount.getStatusSync(); } + @Test(expected = AccountDeletedException.class) + public void getAggregatedBalanceSync_DeletedAccount_Exception() throws Exception { + initWithRandomAccount(); + kinAccount.markAsDeleted(); + + kinAccount.getAggregatedBalanceSync(); + } + + @Test(expected = AccountDeletedException.class) + public void getControlledAccountsSync_DeletedAccount_Exception() throws Exception { + initWithRandomAccount(); + kinAccount.markAsDeleted(); + + kinAccount.getControlledAccountsSync(); + + } + + @Test(expected = AccountDeletedException.class) + public void getAccountDataSync_DeletedAccount_Exception() throws Exception { + initWithRandomAccount(); + kinAccount.markAsDeleted(); + + kinAccount.getAccountDataSync(); + } + @Test public void getPublicAddress_DeletedAccount_Empty() throws Exception { initWithRandomAccount(); From 15f4883ba3d91528af134945fc83a0d80f73a6ac Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 3 Jun 2019 09:28:50 +0300 Subject: [PATCH 07/56] Add and change some java docs --- .../kin/base/requests/AccountsRequestBuilder.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/kin-sdk/kin-base/src/main/java/kin/base/requests/AccountsRequestBuilder.java b/kin-sdk/kin-base/src/main/java/kin/base/requests/AccountsRequestBuilder.java index f486ec45..bc92d068 100644 --- a/kin-sdk/kin-base/src/main/java/kin/base/requests/AccountsRequestBuilder.java +++ b/kin-sdk/kin-base/src/main/java/kin/base/requests/AccountsRequestBuilder.java @@ -42,13 +42,10 @@ public AccountResponse account(KeyPair account) throws IOException { return this.account(this.buildUri()); } - // TODO: 2019-05-29 maybe blockchain need to add the description in their docs. - /** * Requests GET /accounts/{account}/aggregate_balance * - * @param account Account to fetch - * @see Account Details + * @param account Account to fetch the aggregated balance for. */ public AggregatedBalanceResponse aggregateBalance(KeyPair account) throws IOException { this.setSegments("accounts", account.getAccountId(), "aggregate_balance"); @@ -67,13 +64,11 @@ public AggregatedBalanceResponse aggregateBalance(URI uri) throws IOException { return responseHandler.handleGetRequest(uri); } - // TODO: 2019-05-29 maybe blockchain need to add the description in their docs. - + // TODO: 2019-05-29 we may need a url for the method description, need to check if blockchain team can even add it. /** * Requests GET /accounts/{account}/controlled_balances * - * @param account Account to fetch - * @see Account Details + * @param account the account in which we get all his controlled accounts */ public ControlledAccountsResponse controlledAccounts(KeyPair account) throws IOException { this.setSegments("accounts", account.getAccountId(), "controlled_balances"); From 6fc037751e22eb2bd68b990041c315075b8dc08f Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 3 Jun 2019 09:32:01 +0300 Subject: [PATCH 08/56] remove 2 TODOs --- .../androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt | 4 +++- .../kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index 04de8f7f..1a7978cb 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -501,7 +501,9 @@ class KinAccountIntegrationTest { onboardSingleAccount(controlledAccount, 100.0) onboardSingleAccount(masterAccount, 100.0) onboardSingleAccount(destinationAccount, 100.0) + linkAccount(controlledAccount, masterAccount, "some package id ") + val transaction = masterAccount.transactionBuilderSync .setFee(fee) .setMemo(appId, "master to destination") @@ -510,8 +512,8 @@ class KinAccountIntegrationTest { .setSourceAccount(KeyPair.fromAccountId(controlledAccount.publicAddress)) .build()) .build() - //TODO add a check to see if indeed the manage data the same for master and 2 signatures in controlled val transactionId = masterAccount.sendTransactionSync(transaction) + assertThat(transactionId.id(), not(isEmptyOrNullString())) // The controlled account need to reduced the fee from the link account transaction. assertThat(controlledAccount.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin.multiply(BigDecimal(2))))) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java index a212eac0..49720282 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java @@ -60,7 +60,7 @@ Transaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddres TransactionBuilder getTransactionBuilder(@NonNull KeyPair from) throws OperationFailedException { Utils.checkNotNull(from, "from"); AccountResponse sourceAccount = loadSourceAccount(from); - // TODO: 2019-05-27 need to decide if we do use make the fee "must" in the constructor... + // TODO: 2019-05-27 need to decide if we make the fee a "must" in the constructor... return new TransactionBuilder(from, sourceAccount); } From 3f60ff790250632dbd2ced8350a4290f8a7a3113 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 3 Jun 2019 16:50:38 +0300 Subject: [PATCH 09/56] Add another test which was written in the tech design --- .../kin/sdk/KinAccountIntegrationTest.kt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index 1a7978cb..437534a8 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -492,6 +492,38 @@ class KinAccountIntegrationTest { kinAccountSender.sendTransactionSync(transaction) } + @Test + @Throws(Exception::class) + fun accountLinkingUsingDifferentKinClients_SendTransaction_TransactionSuccess() { + var anotherKinClient = KinClient(InstrumentationRegistry.getTargetContext(), environment, "zxcv") + val masterAccount = anotherKinClient.addAccount() + val controlledAccount = kinClient.addAccount() + val destinationAccount = kinClient.addAccount() + onboardSingleAccount(controlledAccount, 100.0) + onboardSingleAccount(masterAccount, 100.0) + onboardSingleAccount(destinationAccount, 100.0) + + linkAccount(controlledAccount, masterAccount, "some package id ") + + val transaction = masterAccount.transactionBuilderSync + .setFee(fee) + .setMemo("zxcv", "master to destination") + .addOperation(PaymentOperation.Builder( + KeyPair.fromAccountId(destinationAccount.publicAddress), AssetTypeNative(), "21.12300") + .setSourceAccount(KeyPair.fromAccountId(controlledAccount.publicAddress)) + .build()) + .build() + val transactionId = masterAccount.sendTransactionSync(transaction) + + assertThat(transactionId.id(), not(isEmptyOrNullString())) + // The controlled account need to reduced the fee from the link account transaction. + assertThat(controlledAccount.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin.multiply(BigDecimal(2))))) + assertThat(masterAccount.balanceSync.value(), equalTo(BigDecimal("100.00000").subtract(feeInKin))) + assertThat(destinationAccount.balanceSync.value(), equalTo(BigDecimal("121.12300"))) + val packageIdInBase64 = masterAccount.accountDataSync.data[controlledAccount.publicAddress] + assertThat(String(kin.base.codec.Base64.decodeBase64(packageIdInBase64)), containsString("some package id")) + } + @Test @Throws(Exception::class) fun accountLinking_SendTransaction_TransactionSuccess() { From 0ccfcb4099f4aab754ac41dcdea998f27724702e Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 6 Jun 2019 11:49:36 +0300 Subject: [PATCH 10/56] Show txResultCode even if no opResultCode --- .../kin/sdk/exception/TransactionFailedException.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/exception/TransactionFailedException.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/exception/TransactionFailedException.java index 9d80cdbd..2a989466 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/exception/TransactionFailedException.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/exception/TransactionFailedException.java @@ -23,9 +23,11 @@ public TransactionFailedException(@Nullable String txResultCode, @NonNull private static String getMessage(@Nullable String txResultCode, @Nullable List opResultCode) { - return opResultCode != null && !opResultCode.isEmpty() ? - "Transaction failed with the error = " + txResultCode + ", operations = " + opResultCode : - "Transaction failed"; + String opResultCodeErrorMessage = + opResultCode != null && !opResultCode.isEmpty() ? ", operations = " + opResultCode : ""; + return "Transaction failed with the error = " + txResultCode + opResultCodeErrorMessage; + + } @Nullable From d59e43c8ad7a6519cb70c8b0bd74d0a26ce5c159 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 6 Jun 2019 14:44:05 +0300 Subject: [PATCH 11/56] Add more java doc --- kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java index af453e5b..201c11f8 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java @@ -189,7 +189,7 @@ public interface KinAccount { * Get the current list of controlled accounts *

Note: This method accesses the network, and should not be called on the android main thread.

* - * @return the list of controlled accounts + * @return the list of controlled accounts. Could be empty if no controlled accounts. * @throws AccountNotFoundException if account was not created * @throws OperationFailedException any other error */ From 5a6cd3359bd59219979ecedcddeda814f24e4d57 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 6 Jun 2019 15:21:22 +0300 Subject: [PATCH 12/56] Add java doc in order to explain a bit on c=aggregated balance, controlled account and master account --- .../kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java | 5 +++++ kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java index 4ce63396..029c15e9 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java @@ -1,5 +1,10 @@ package kin.sdk; +/** + * This class represents a controlled account. Controlled Account is an account that other account has also control over + * him, for example, to send transactions with a payment operation on behalf of him. The account that has the control is + * often called the master account. Master account can control more than one account and can sign for another. + */ public class ControlledAccount { private Balance balance; diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java index 201c11f8..6231b011 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java @@ -158,6 +158,9 @@ public interface KinAccount { /** * Create {@link Request} for getting the current confirmed aggregated balance in kin + * An aggregated balance is a sum of balances of all accounts controlled by the master account. + * This includes the balance of the master account. + *

See {@link ControlledAccount} for more information on a controlled account

*

See {@link KinAccount#getAggregatedBalanceSync()} for possibles errors

* * @return {@code Request} Balance - the aggregated balance in kin @@ -178,6 +181,8 @@ public interface KinAccount { /** * Create {@link Request} for getting list of controlled accounts + * It is a list of all accounts controlled by the master account. + *

See {@link ControlledAccount} for more information on a controlled account

*

See {@link KinAccount#getControlledAccountsSync()} for possibles errors

* * @return {@code Request>} List - the list of controlled accounts From e6cbd3e21acedb612e39b25bfadb3fc13ff138ad Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 6 Jun 2019 15:21:53 +0300 Subject: [PATCH 13/56] Indent the java doc --- .../kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java index 029c15e9..62222519 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/ControlledAccount.java @@ -3,7 +3,8 @@ /** * This class represents a controlled account. Controlled Account is an account that other account has also control over * him, for example, to send transactions with a payment operation on behalf of him. The account that has the control is - * often called the master account. Master account can control more than one account and can sign for another. + * often called the master account. + * Master account can control more than one account and can sign for another. */ public class ControlledAccount { From d4589e6f303e107fbafa38ce4ddef15788c97327 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 6 Jun 2019 15:33:40 +0300 Subject: [PATCH 14/56] Add a DecodeTransactionException and throw it if decode transaction failed with IOException --- .../src/main/java/kin/sdk/Transaction.java | 14 ++++++++++---- .../exception/DecodeTransactionException.java | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/exception/DecodeTransactionException.java diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java index 40aebdd8..612163eb 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java @@ -4,6 +4,7 @@ import kin.base.KeyPair; import kin.base.Memo; import kin.base.Network; +import kin.sdk.exception.DecodeTransactionException; public class Transaction { @@ -19,10 +20,15 @@ public Transaction(TransactionId id, kin.base.Transaction baseTransaction) { this.baseTransaction = baseTransaction; } - public static Transaction decodeTransaction(String transactionEnvelope) throws IOException { - kin.base.Transaction transaction = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); - TransactionId id = new TransactionIdImpl(Utils.byteArrayToHex(transaction.hash())); - return new Transaction(id, transaction); + public static Transaction decodeTransaction(String transactionEnvelope) throws DecodeTransactionException { + try { + kin.base.Transaction transaction = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); + TransactionId id = new TransactionIdImpl(Utils.byteArrayToHex(transaction.hash())); + return new Transaction(id, transaction); + } catch (IOException e) { + throw new DecodeTransactionException(e.getMessage(), e.getCause()); + } + } public KeyPair getSource() { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/exception/DecodeTransactionException.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/exception/DecodeTransactionException.java new file mode 100644 index 00000000..b054c03c --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/exception/DecodeTransactionException.java @@ -0,0 +1,17 @@ +package kin.sdk.exception; + +public class DecodeTransactionException extends Exception { + + public DecodeTransactionException(String message) { + super(message); + } + + public DecodeTransactionException(String message, Throwable cause) { + super(message, cause); + } + + public DecodeTransactionException(Throwable cause) { + super(cause); + } + +} From 66e2ccd871efa1e986f64c28ecd874b497934d14 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 6 Jun 2019 15:58:21 +0300 Subject: [PATCH 15/56] Add javadocs to our Transaction object and also remove the id/hash from the constructor. --- .../src/main/java/kin/sdk/Transaction.java | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java index 612163eb..be657b3e 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java @@ -8,23 +8,21 @@ public class Transaction { - /** - * The transaction hash - */ - private final TransactionId id; - private final kin.base.Transaction baseTransaction; - public Transaction(TransactionId id, kin.base.Transaction baseTransaction) { - this.id = id; + public Transaction(kin.base.Transaction baseTransaction) { this.baseTransaction = baseTransaction; } + /** + * Creates a Transaction instance from previously build TransactionEnvelope + * + * @param transactionEnvelope a Base-64 encoded TransactionEnvelope + */ public static Transaction decodeTransaction(String transactionEnvelope) throws DecodeTransactionException { try { kin.base.Transaction transaction = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); - TransactionId id = new TransactionIdImpl(Utils.byteArrayToHex(transaction.hash())); - return new Transaction(id, transaction); + return new Transaction(transaction); } catch (IOException e) { throw new DecodeTransactionException(e.getMessage(), e.getCause()); } @@ -35,6 +33,9 @@ public KeyPair getSource() { return baseTransaction.getSourceAccount(); } + /** + * Returns fee paid for transaction in kin base unit (1 base unit = 0.00001 KIN). + */ public int getFee() { return baseTransaction.getFee(); } @@ -43,23 +44,37 @@ public Memo getMemo() { return baseTransaction.getMemo(); } + /** + * return The transaction hash + */ public TransactionId getId() { - return id; + return new TransactionIdImpl(Utils.byteArrayToHex(baseTransaction.hash())); } kin.base.Transaction getBaseTransaction() { return baseTransaction; } + /** + * see {@link WhitelistableTransaction} for more information on a whitelistable transaction

+ */ public WhitelistableTransaction getWhitelistableTransaction() { return new WhitelistableTransaction(baseTransaction.toEnvelopeXdrBase64(), Network.current().getNetworkPassphrase()); } + /** + * Returns base64-encoded TransactionEnvelope object. + * Transaction need to have at least one signature. + */ public String getTransactionEnvelope() { return baseTransaction.toEnvelopeXdrBase64(); } + /** + * Adds a new signature to this transaction. + * @param account {@link KinAccount} object which is the account who we add his signature. + */ public void addSignature(KinAccount account) { baseTransaction.sign(((KinAccountImpl) account).getKeyPair()); } From 46b2fec69ab716c3c8110bd65d6ba2d8aeb17007 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 6 Jun 2019 16:22:45 +0300 Subject: [PATCH 16/56] 1. Add validation to the memo when build method is called 2. Move the validate memo to Utils 3. Fix the tests because of transaction constructor change --- .../main/java/kin/sdk/TransactionBuilder.java | 6 +++--- .../main/java/kin/sdk/TransactionSender.java | 18 ++---------------- .../src/main/java/kin/sdk/Utils.java | 14 ++++++++++++++ .../src/test/java/kin/sdk/TransactionTest.java | 3 +-- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java index acb631fb..cc8fb307 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java @@ -2,6 +2,7 @@ import kin.base.KeyPair; import kin.base.Memo; +import kin.base.MemoText; import kin.base.Operation; import kin.base.TimeBounds; import kin.base.Transaction; @@ -61,7 +62,6 @@ public TransactionBuilder setFee(int fee) { */ public TransactionBuilder setMemo(String appId, String memo) { builder.addMemo(Memo.text(Utils.addAppIdToMemo(appId, memo))); - // TODO: 2019-05-23 should we validate memo like we are doing in transaction sender class? return this; } @@ -82,9 +82,9 @@ public TransactionBuilder setTimeBounds(TimeBounds timeBounds) { */ public kin.sdk.Transaction build() { Transaction baseTransaction = builder.build(); - TransactionIdImpl transactionId = new TransactionIdImpl(Utils.byteArrayToHex(baseTransaction.hash())); + Utils.validateMemo(((MemoText) baseTransaction.getMemo()).getText()); baseTransaction.sign(account); - return new kin.sdk.Transaction(transactionId, baseTransaction); + return new kin.sdk.Transaction(baseTransaction); } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java index 49720282..0a0af26d 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java @@ -4,7 +4,6 @@ import android.support.annotation.Nullable; import android.text.TextUtils; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.util.List; import kin.base.AssetTypeNative; @@ -25,7 +24,6 @@ class TransactionSender { - private static final int MEMO_BYTES_LENGTH_LIMIT = 21; //Memo length limitation(in bytes) is 28 but we add 7 more bytes which includes the appId and some characters. private static final int MAX_NUM_OF_DECIMAL_PLACES = 4; private static final String INSUFFICIENT_KIN_RESULT_CODE = "op_underfunded"; private static final String INSUFFICIENT_FEE_RESULT_CODE = "tx_insufficient_fee"; @@ -53,8 +51,7 @@ Transaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddres verifyAddresseeAccount(generateAddresseeKeyPair(addressee.getAccountId())); kin.base.Transaction stellarTransaction = buildStellarTransaction(from, amount, addressee, sourceAccount, fee, memo); - TransactionId id = new TransactionIdImpl(Utils.byteArrayToHex(stellarTransaction.hash())); - return new Transaction(id, stellarTransaction); + return new Transaction(stellarTransaction); } TransactionBuilder getTransactionBuilder(@NonNull KeyPair from) throws OperationFailedException { @@ -85,7 +82,7 @@ private void checkParams(@NonNull KeyPair from, @NonNull String publicAddress, @ checkForNegativeFee(fee); checkAddressNotEmpty(publicAddress); checkForNegativeAmount(amount); - checkMemo(memo); + Utils.validateMemo(memo); } @@ -116,17 +113,6 @@ private void checkForNegativeFee(int fee) { } } - private void checkMemo(String memo) { - try { - if (memo != null && memo.getBytes("UTF-8").length > MEMO_BYTES_LENGTH_LIMIT) { - throw new IllegalArgumentException( - "Memo cannot be longer that " + MEMO_BYTES_LENGTH_LIMIT + " bytes(UTF-8 characters)"); - } - } catch (UnsupportedEncodingException e) { - throw new IllegalArgumentException("Memo text have unsupported characters encoding"); - } - } - @NonNull private KeyPair generateAddresseeKeyPair(@NonNull String publicAddress) throws OperationFailedException { try { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Utils.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Utils.java index a8a67561..0a06905b 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Utils.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Utils.java @@ -3,6 +3,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; import kin.base.responses.SubmitTransactionResponse; import kin.base.responses.SubmitTransactionResponse.Extras.ResultCodes; @@ -10,8 +11,10 @@ final class Utils { + private static final int MEMO_BYTES_LENGTH_LIMIT = 21; //Memo length limitation(in bytes) is 28 but we add 7 more bytes which includes the appId and some characters. private static String MEMO_APP_ID_VERSION_PREFIX = "1"; private static String MEMO_DELIMITER = "-"; + private static String UTF_8 = "UTF-8"; private Utils() { //no instances @@ -62,4 +65,15 @@ static String addAppIdToMemo(String appId, @Nullable String memo) { .append(memo); return sb.toString(); } + + static void validateMemo(String memo) { + try { + if (memo != null && memo.getBytes(UTF_8).length > MEMO_BYTES_LENGTH_LIMIT) { + throw new IllegalArgumentException( + "Memo cannot be longer that " + MEMO_BYTES_LENGTH_LIMIT + " bytes(UTF-8 characters)"); + } + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Memo text have unsupported characters encoding"); + } + } } diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java index ca78426f..4a076f4b 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java @@ -48,8 +48,7 @@ private void createTransaction() { .addFee(100) .build(); baseTransaction.sign(source); - TransactionId id = new TransactionIdImpl(Utils.byteArrayToHex(baseTransaction.hash())); - transaction = new Transaction(id, baseTransaction); + transaction = new Transaction(baseTransaction); } private void mockServer() throws IOException { From 5ce995971df3c216dd929dff8b8ccd6d54535054 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Fri, 7 Jun 2019 06:44:24 +0300 Subject: [PATCH 17/56] Remove and add some java docs and also validate fee is not negative --- .../src/main/java/kin/sdk/TransactionBuilder.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java index cc8fb307..9c60c31c 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java @@ -43,6 +43,9 @@ public TransactionBuilder addOperation(Operation operation) { } /** + * Each transaction sets a fee that is paid by the source account. + * If this fee is below the network minimum the transaction will fail. + * The more operations in the transaction, the greater the required fee. * @param fee this transaction fee * @return Builder object so you can chain methods. */ @@ -52,8 +55,8 @@ public TransactionBuilder setFee(int fee) { } /** - * Adds a memo to - * this transaction. + * Adds a memo to this transaction. + * It's an optional parameter and should contain extra information * * @param appId is a 3-4 character string which is added to each transaction memo to identify your application. * appId must contain only digits and upper and/or lower case letters. String length must be 3 or 4. @@ -82,6 +85,9 @@ public TransactionBuilder setTimeBounds(TimeBounds timeBounds) { */ public kin.sdk.Transaction build() { Transaction baseTransaction = builder.build(); + if (baseTransaction.getFee() < 0) { + throw new IllegalArgumentException("Fee can't be negative"); + } Utils.validateMemo(((MemoText) baseTransaction.getMemo()).getText()); baseTransaction.sign(account); return new kin.sdk.Transaction(baseTransaction); From c1b404f0af85a05ac20d726e764068f25606e8fa Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Fri, 7 Jun 2019 06:51:01 +0300 Subject: [PATCH 18/56] Remove aappId form setMemo and add it to the constructor so it will be used internally and not be added by the client again. --- .../src/main/java/kin/sdk/TransactionBuilder.java | 12 +++++++----- .../src/main/java/kin/sdk/TransactionSender.java | 7 +++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java index 9c60c31c..cfb0c4c7 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java @@ -13,6 +13,7 @@ public class TransactionBuilder { private final Builder builder; private final KeyPair account; + private final String appId; /** * Construct a new transaction builder. @@ -20,10 +21,13 @@ public class TransactionBuilder { * @param account The source account for this transaction. This account is the account * @param sourceAccount The source account for this transaction. This account is the account who will use a sequence * number. When build() is called, the account object's sequence number will be incremented. + * @param appId is a 3-4 character string which is added to each transaction memo to identify your application. + * * appId must contain only digits and upper and/or lower case letters. String length must be 3 or 4. */ - TransactionBuilder(KeyPair account, TransactionBuilderAccount sourceAccount) { + TransactionBuilder(KeyPair account, TransactionBuilderAccount sourceAccount, String appId) { this.account = account; builder = new Builder(sourceAccount); + this.appId = appId; } public int getOperationsCount() { @@ -56,14 +60,12 @@ public TransactionBuilder setFee(int fee) { /** * Adds a memo to this transaction. - * It's an optional parameter and should contain extra information + * @param memo It's an optional parameter and should contain extra information * - * @param appId is a 3-4 character string which is added to each transaction memo to identify your application. - * appId must contain only digits and upper and/or lower case letters. String length must be 3 or 4. * @return Builder object so you can chain methods. * @see Memo */ - public TransactionBuilder setMemo(String appId, String memo) { + public TransactionBuilder setMemo(String memo) { builder.addMemo(Memo.text(Utils.addAppIdToMemo(appId, memo))); return this; } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java index 0a0af26d..a1b9a003 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java @@ -58,7 +58,7 @@ TransactionBuilder getTransactionBuilder(@NonNull KeyPair from) throws Operation Utils.checkNotNull(from, "from"); AccountResponse sourceAccount = loadSourceAccount(from); // TODO: 2019-05-27 need to decide if we make the fee a "must" in the constructor... - return new TransactionBuilder(from, sourceAccount); + return new TransactionBuilder(from, sourceAccount, appId); } TransactionId sendTransaction(Transaction transaction) throws OperationFailedException { @@ -200,9 +200,8 @@ private boolean isInsufficientKinException(TransactionFailedException transactio String transactionResultCode = transactionException.getTransactionResultCode(); return ( (resultCodes != null && resultCodes.size() > 0 && INSUFFICIENT_KIN_RESULT_CODE.equals(resultCodes.get(0))) - || - !TextUtils.isEmpty(transactionResultCode) && INSUFFICIENT_BALANCE_RESULT_CODE - .equals(transactionResultCode)); + || !TextUtils.isEmpty(transactionResultCode) && INSUFFICIENT_BALANCE_RESULT_CODE + .equals(transactionResultCode)); } private boolean isInsufficientFeeException(TransactionFailedException transactionException) { From 5bf88a80b08693cf61105a17318ca9928a5eb5fc Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Fri, 7 Jun 2019 07:20:45 +0300 Subject: [PATCH 19/56] Update and fix tests --- .../kin/sdk/KinAccountIntegrationTest.kt | 48 ++----------------- .../java/kin/sdk/TransactionSenderTest.java | 2 +- .../test/java/kin/sdk/TransactionTest.java | 26 +++++----- 3 files changed, 17 insertions(+), 59 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index 437534a8..faf62237 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -108,20 +108,13 @@ class KinAccountIntegrationTest { @Test @LargeTest - fun getAggregatedBalance_OneOfTheAccountsWasNotCreated_AccountNotFoundException() { - val kinAccount1 = kinClient.addAccount() - val kinAccount2 = kinClient.addAccount() + fun getAggregatedBalance_AccountNotCreated_AccountNotFoundException() { val masterAccount = kinClient.addAccount() - fakeKinOnBoard.createAccount(kinAccount1.publicAddress.orEmpty(), "10.14159") - fakeKinOnBoard.createAccount(masterAccount.publicAddress.orEmpty(), "20.02500") - - expectedEx.expect(AccountNotFoundException::class.java) - expectedEx.expectMessage(kinAccount2.publicAddress.orEmpty()) + expectedEx.expectMessage(masterAccount.publicAddress.orEmpty()) - linkAccount(kinAccount1, masterAccount, "some package id 1") - linkAccount(kinAccount2, masterAccount, "some package id 2") + masterAccount.aggregatedBalanceSync } @Test @@ -507,38 +500,7 @@ class KinAccountIntegrationTest { val transaction = masterAccount.transactionBuilderSync .setFee(fee) - .setMemo("zxcv", "master to destination") - .addOperation(PaymentOperation.Builder( - KeyPair.fromAccountId(destinationAccount.publicAddress), AssetTypeNative(), "21.12300") - .setSourceAccount(KeyPair.fromAccountId(controlledAccount.publicAddress)) - .build()) - .build() - val transactionId = masterAccount.sendTransactionSync(transaction) - - assertThat(transactionId.id(), not(isEmptyOrNullString())) - // The controlled account need to reduced the fee from the link account transaction. - assertThat(controlledAccount.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin.multiply(BigDecimal(2))))) - assertThat(masterAccount.balanceSync.value(), equalTo(BigDecimal("100.00000").subtract(feeInKin))) - assertThat(destinationAccount.balanceSync.value(), equalTo(BigDecimal("121.12300"))) - val packageIdInBase64 = masterAccount.accountDataSync.data[controlledAccount.publicAddress] - assertThat(String(kin.base.codec.Base64.decodeBase64(packageIdInBase64)), containsString("some package id")) - } - - @Test - @Throws(Exception::class) - fun accountLinking_SendTransaction_TransactionSuccess() { - val controlledAccount = kinClient.addAccount() - val masterAccount = kinClient.addAccount() - val destinationAccount = kinClient.addAccount() - onboardSingleAccount(controlledAccount, 100.0) - onboardSingleAccount(masterAccount, 100.0) - onboardSingleAccount(destinationAccount, 100.0) - - linkAccount(controlledAccount, masterAccount, "some package id ") - - val transaction = masterAccount.transactionBuilderSync - .setFee(fee) - .setMemo(appId, "master to destination") + .setMemo("master to destination") .addOperation(PaymentOperation.Builder( KeyPair.fromAccountId(destinationAccount.publicAddress), AssetTypeNative(), "21.12300") .setSourceAccount(KeyPair.fromAccountId(controlledAccount.publicAddress)) @@ -561,7 +523,7 @@ class KinAccountIntegrationTest { val managerDataKey = controlledAccount.publicAddress val transaction = transactionBuilder .setFee(fee) - .setMemo("test", "account linking") + .setMemo("account linking") .addOperation(SetOptionsOperation.Builder().setSigner(signerKey, 1).build()) .addOperation(ManageDataOperation.Builder(managerDataKey, managerDataValue.toByteArray()) .setSourceAccount(KeyPair.fromAccountId(masterAccount.publicAddress)) diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java index 7579deec..638d15b1 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java @@ -140,7 +140,7 @@ private Transaction getLinkingTransaction(SignerKey signerKey, String managerDat .addOperation(new Builder().setSigner(signerKey, 1).build()) .addOperation( new ManageDataOperation.Builder(managerDataKey, new Base64().decode(managerDataValue)).build()) - .setMemo(APP_ID, "fake memo") + .setMemo("fake memo") .setFee(100) .setTimeBounds(timeBounds) .build(); diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java index 4a076f4b..d185f697 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java @@ -56,19 +56,15 @@ private void mockServer() throws IOException { mockWebServer.start(); } + @Test public void getTransactionEnvelope_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); - String transactionEnvelope = transaction.getTransactionEnvelope(); - kin.base.Transaction transaction2 = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); - - assertThat(transaction.getBaseTransaction().getSourceAccount().getAccountId(), - equalTo(transaction2.getSourceAccount().getAccountId())); - assertThat(transaction.getBaseTransaction().getSequenceNumber(), equalTo(transaction2.getSequenceNumber())); - assertThat(transaction.getBaseTransaction().getFee(), equalTo(transaction2.getFee())); - assertThat(transaction.getTransactionEnvelope(), equalTo(transaction2.toEnvelopeXdrBase64())); + String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA01UjG6QUycMnvHfswo0uHc7dbPIPZ3p4h3bOqJcLWJwAAAAAC+vCAAAAAAAAAAABlwtYnAAAAEBEIhSY0BYSMy5yrYkIYes9AmH0v729nVGZ1nH8CukJ5rfWoYf6Ebo4hismcjv51xlulVuTRYILuW67ENoHn1YB"; + assertThat(kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope).toEnvelopeXdrBase64(), + equalTo(transactionEnvelope)); } @Test @@ -76,13 +72,13 @@ public void decodeTransaction_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); - String transactionEnvelope = transaction.getTransactionEnvelope(); - Transaction transaction2 = Transaction.decodeTransaction(transactionEnvelope); - assertThat(transaction.getSource().getAccountId(), equalTo(transaction2.getSource().getAccountId())); - assertThat(transaction.getBaseTransaction().getSequenceNumber(), - equalTo(transaction2.getBaseTransaction().getSequenceNumber())); - assertThat(transaction.getFee(), equalTo(transaction2.getFee())); - assertThat(transaction.getTransactionEnvelope(), equalTo(transaction2.getTransactionEnvelope())); + String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA01UjG6QUycMnvHfswo0uHc7dbPIPZ3p4h3bOqJcLWJwAAAAAC+vCAAAAAAAAAAABlwtYnAAAAEBEIhSY0BYSMy5yrYkIYes9AmH0v729nVGZ1nH8CukJ5rfWoYf6Ebo4hismcjv51xlulVuTRYILuW67ENoHn1YB"; + Transaction transaction = Transaction.decodeTransaction(transactionEnvelope); + assertThat("GDJVKIY3UQKMTQZHXR36ZQUNFYO45XLM6IHWO6TYQ53M5KEXBNMJYWVR", + equalTo(transaction.getSource().getAccountId())); + assertThat(2908908335136769L, equalTo(transaction.getBaseTransaction().getSequenceNumber())); + assertThat(100, equalTo(transaction.getFee())); + assertThat(transactionEnvelope, equalTo(transaction.getTransactionEnvelope())); } @Test From e05b46eab65f210f9e8c68c56cdd9011bf3a4916 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Fri, 7 Jun 2019 07:31:36 +0300 Subject: [PATCH 20/56] Rename the fee in some places --- .../kin-base/src/main/java/kin/base/Transaction.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/kin-sdk/kin-base/src/main/java/kin/base/Transaction.java b/kin-sdk/kin-base/src/main/java/kin/base/Transaction.java index e7677506..3255a783 100644 --- a/kin-sdk/kin-base/src/main/java/kin/base/Transaction.java +++ b/kin-sdk/kin-base/src/main/java/kin/base/Transaction.java @@ -31,13 +31,14 @@ public class Transaction { private final TimeBounds mTimeBounds; private List mSignatures; - Transaction(KeyPair sourceAccount, int fee, long sequenceNumber, Operation[] operations, Memo memo, TimeBounds timeBounds) { + Transaction(KeyPair sourceAccount, int feePerOperation, long sequenceNumber, Operation[] operations, Memo memo, + TimeBounds timeBounds) { mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null"); mSequenceNumber = checkNotNull(sequenceNumber, "sequenceNumber cannot be null"); mOperations = checkNotNull(operations, "operations cannot be null"); checkArgument(operations.length > 0, "At least one operation required"); - mFee = operations.length * fee ; + mFee = operations.length * feePerOperation; mSignatures = new ArrayList(); mMemo = memo != null ? memo : Memo.none(); mTimeBounds = timeBounds; @@ -229,11 +230,11 @@ public static Transaction fromEnvelopeXdr(String envelope) throws IOException { public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope) { kin.base.xdr.Transaction tx = envelope.getTx(); // Although currently there couldn't be a transaction with no operations we still check it. - int mFee = tx.getFee().getUint32(); + int feePerOperation = tx.getFee().getUint32(); if (tx.getOperations().length > 1) { // Because the fee was already multiplied by number of operation then divide by it because when reCreate this // transaction then we will multiple it again. - mFee = mFee / tx.getOperations().length; + feePerOperation = feePerOperation / tx.getOperations().length; } KeyPair mSourceAccount = KeyPair.fromXdrPublicKey(tx.getSourceAccount().getAccountID()); @@ -246,7 +247,8 @@ public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope) { mOperations[i] = Operation.fromXdr(tx.getOperations()[i]); } - Transaction transaction = new Transaction(mSourceAccount, mFee, mSequenceNumber, mOperations, mMemo, mTimeBounds); + Transaction transaction = new Transaction(mSourceAccount, feePerOperation, mSequenceNumber, mOperations, mMemo, + mTimeBounds); for (DecoratedSignature signature : envelope.getSignatures()) { transaction.mSignatures.add(signature); From 739e47ba40d9fa1e3d9997c2a0aa27ddb9e855f1 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 10 Jun 2019 10:45:11 +0300 Subject: [PATCH 21/56] remove the get prefix --- .../src/main/java/kin/sdk/AccountData.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountData.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountData.java index 592f6333..bb985b74 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountData.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AccountData.java @@ -32,39 +32,39 @@ public AccountData(String publicAddress, long sequenceNumber, String pagingToken this.data = data; } - public String getPublicAddress() { + public String publicAddress() { return publicAddress; } - public long getSequenceNumber() { + public long sequenceNumber() { return sequenceNumber; } - public String getPagingToken() { + public String pagingToken() { return pagingToken; } - public Integer getSubentryCount() { + public Integer subentryCount() { return subentryCount; } - public AccountResponse.Thresholds getThresholds() { + public AccountResponse.Thresholds thresholds() { return thresholds; } - public Flags getFlags() { + public Flags flags() { return flags; } - public AccountResponse.Balance[] getBalances() { + public AccountResponse.Balance[] balances() { return balances; } - public Signer[] getSigners() { + public Signer[] signers() { return signers; } - public Map getData() { + public Map data() { return data; } From 9d74c9303c1cf660b2e8c893fb68fe4ea42ad7b2 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 10 Jun 2019 10:46:18 +0300 Subject: [PATCH 22/56] As a result from removing the get prefix then the code here changed --- .../kotlin/kin/sdk/KinAccountIntegrationTest.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index faf62237..3e9977c3 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -194,8 +194,8 @@ class KinAccountIntegrationTest { fakeKinOnBoard.createAccount(kinAccount.publicAddress.orEmpty(), "10.14159") val accountData = kinAccount.accountDataSync - assertThat(accountData.publicAddress, equalTo(kinAccount.publicAddress)) - assertThat(accountData.balances[0].balance, equalTo("10.14159")) + assertThat(accountData.publicAddress(), equalTo(kinAccount.publicAddress)) + assertThat(accountData.balances()[0].balance, equalTo("10.14159")) } @Test @@ -307,7 +307,7 @@ class KinAccountIntegrationTest { expectedEx.expectMessage(kinAccountSender.publicAddress) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) - val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction) + val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()) kinAccountSender.sendWhitelistTransactionSync(whitelist) } @@ -324,7 +324,7 @@ class KinAccountIntegrationTest { expectedEx.expectMessage(kinAccountReceiver.publicAddress) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) - val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction) + val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()) kinAccountSender.sendWhitelistTransactionSync(whitelist) } @@ -350,7 +350,7 @@ class KinAccountIntegrationTest { val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("20"), minFee + 100000) - val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction) + val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()) kinAccountSender.sendWhitelistTransactionSync(whitelist) assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("80.00000"))) } @@ -364,7 +364,7 @@ class KinAccountIntegrationTest { val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("20"), minFee) - val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction) + val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()) kinAccountSender.sendWhitelistTransactionSync(whitelist) assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("80.00000"))) assertThat(kinAccountReceiver.balanceSync.value(), equalTo(BigDecimal("20.00000"))) @@ -513,7 +513,7 @@ class KinAccountIntegrationTest { assertThat(controlledAccount.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin.multiply(BigDecimal(2))))) assertThat(masterAccount.balanceSync.value(), equalTo(BigDecimal("100.00000").subtract(feeInKin))) assertThat(destinationAccount.balanceSync.value(), equalTo(BigDecimal("121.12300"))) - val packageIdInBase64 = masterAccount.accountDataSync.data[controlledAccount.publicAddress] + val packageIdInBase64 = masterAccount.accountDataSync.data()[controlledAccount.publicAddress] assertThat(String(kin.base.codec.Base64.decodeBase64(packageIdInBase64)), containsString("some package id")) } @@ -530,7 +530,7 @@ class KinAccountIntegrationTest { .build()) .build() // Simulate getting a transaction envelope and decode it. - val transactionEnvelope = transaction.transactionEnvelope + val transactionEnvelope = transaction.transactionEnvelope() val externalTransaction = Transaction.decodeTransaction(transactionEnvelope) // Sign with the master and sending the linking transaction from the master account externalTransaction.addSignature(masterAccount) From ed2b90c8288c8492594cad633a8ca736da8d8354 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 10 Jun 2019 10:47:00 +0300 Subject: [PATCH 23/56] Add missing methods nad remove the get prefix --- .../src/main/java/kin/sdk/Transaction.java | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java index be657b3e..e82530e8 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java @@ -1,9 +1,13 @@ package kin.sdk; import java.io.IOException; +import java.util.List; import kin.base.KeyPair; import kin.base.Memo; import kin.base.Network; +import kin.base.Operation; +import kin.base.TimeBounds; +import kin.base.xdr.DecoratedSignature; import kin.sdk.exception.DecodeTransactionException; public class Transaction { @@ -26,39 +30,38 @@ public static Transaction decodeTransaction(String transactionEnvelope) throws D } catch (IOException e) { throw new DecodeTransactionException(e.getMessage(), e.getCause()); } - } - public KeyPair getSource() { + public KeyPair source() { return baseTransaction.getSourceAccount(); } /** * Returns fee paid for transaction in kin base unit (1 base unit = 0.00001 KIN). */ - public int getFee() { + public int fee() { return baseTransaction.getFee(); } - public Memo getMemo() { + public Memo memo() { return baseTransaction.getMemo(); } /** * return The transaction hash */ - public TransactionId getId() { + public TransactionId id() { return new TransactionIdImpl(Utils.byteArrayToHex(baseTransaction.hash())); } - kin.base.Transaction getBaseTransaction() { + kin.base.Transaction baseTransaction() { return baseTransaction; } /** * see {@link WhitelistableTransaction} for more information on a whitelistable transaction

*/ - public WhitelistableTransaction getWhitelistableTransaction() { + public WhitelistableTransaction whitelistableTransaction() { return new WhitelistableTransaction(baseTransaction.toEnvelopeXdrBase64(), Network.current().getNetworkPassphrase()); } @@ -67,10 +70,29 @@ public WhitelistableTransaction getWhitelistableTransaction() { * Returns base64-encoded TransactionEnvelope object. * Transaction need to have at least one signature. */ - public String getTransactionEnvelope() { + public String transactionEnvelope() { return baseTransaction.toEnvelopeXdrBase64(); } + public long sequenceNumber() { + return baseTransaction.getSequenceNumber(); + } + + public Operation[] operations() { + return baseTransaction.getOperations(); + } + + public List signatures() { + return baseTransaction.getSignatures(); + } + + /** + * @return TimeBounds, or null (representing no time restrictions) + */ + public TimeBounds timeBounds() { + return baseTransaction.getTimeBounds(); + } + /** * Adds a new signature to this transaction. * @param account {@link KinAccount} object which is the account who we add his signature. From 229e92c81a254703119b97b9610405cf7b68c2bb Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 10 Jun 2019 10:47:24 +0300 Subject: [PATCH 24/56] As a result from removing the get prefix then the code here changed --- .../src/main/java/sdk/sample/TransactionActivity.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java index ca7e8f5d..f1e53ef5 100644 --- a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java +++ b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java @@ -250,7 +250,8 @@ private void buildTransaction(String toAddress, BigDecimal amount, int fee, Stri private void handleWhitelistTransaction(Transaction transaction) { try { - whitelistService.whitelistTransaction(transaction.getWhitelistableTransaction(), new WhitelistServiceListener()); + whitelistService + .whitelistTransaction(transaction.whitelistableTransaction(), new WhitelistServiceListener()); } catch (JSONException e) { Utils.logError(e, "handleWhitelistTransaction"); KinAlertDialog.createErrorDialog(TransactionActivity.this, e.getMessage()).show(); @@ -279,7 +280,7 @@ private class BuildTransactionCallback implements ResultCallback { @Override public void onResult(Transaction transaction) { - Log.d(TAG, "buildTransaction: build transaction " + transaction.getId().id() + " succeeded"); + Log.d(TAG, "buildTransaction: build transaction " + transaction.id().id() + " succeeded"); // This is just to differentiate between whitelist transaction and regular transaction if (switchButton.isChecked()) { From 4a598e450eabbc3a352d255fc3285c6a01c88c97 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 10 Jun 2019 10:47:37 +0300 Subject: [PATCH 25/56] As a result from removing the get prefix then the code here changed --- .../kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java index a1b9a003..be0f7ccf 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java @@ -62,7 +62,7 @@ TransactionBuilder getTransactionBuilder(@NonNull KeyPair from) throws Operation } TransactionId sendTransaction(Transaction transaction) throws OperationFailedException { - return sendTransaction(transaction.getBaseTransaction()); + return sendTransaction(transaction.baseTransaction()); } TransactionId sendWhitelistTransaction(String whitelist) throws OperationFailedException { From 51d71f405891649148f4613799beb7c8ab49f0be Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 10 Jun 2019 10:47:52 +0300 Subject: [PATCH 26/56] As a result from removing the get prefix then the code here changed --- .../src/test/java/kin/sdk/TransactionSenderTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java index 638d15b1..21cfc4e3 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java @@ -125,13 +125,13 @@ public void getTransactionBuilder_buildSuccess() throws Exception { timeBounds); assertThat(transactionBuilder.getOperationsCount(), equalTo(2)); - assertThat(transaction.getFee(), equalTo(200)); - assertThat(((MemoText) transaction.getMemo()).getText(), equalTo(("1-" + APP_ID + "-fake memo"))); - assertThat(transaction.getSource().getAccountId(), equalTo(account.getAccountId())); - assertThat(transaction.getBaseTransaction().getTimeBounds(), equalTo(timeBounds)); + assertThat(transaction.fee(), equalTo(200)); + assertThat(((MemoText) transaction.memo()).getText(), equalTo(("1-" + APP_ID + "-fake memo"))); + assertThat(transaction.source().getAccountId(), equalTo(account.getAccountId())); + assertThat(transaction.baseTransaction().getTimeBounds(), equalTo(timeBounds)); assertArrayEquals(account.getSignatureHint().getSignatureHint(), - transaction.getBaseTransaction().getSignatures().get(0).getHint().getSignatureHint()); - assertThat(transaction.getBaseTransaction().getSignatures().size(), equalTo(1)); + transaction.baseTransaction().getSignatures().get(0).getHint().getSignatureHint()); + assertThat(transaction.baseTransaction().getSignatures().size(), equalTo(1)); } private Transaction getLinkingTransaction(SignerKey signerKey, String managerDataKey, String managerDataValue, From 2e4e703a0951623503e236fa39a3784b6e53fd39 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 10 Jun 2019 10:48:07 +0300 Subject: [PATCH 27/56] As a result from removing the get prefix then the code here changed --- .../src/androidTest/java/kin/sdk/KinAccountTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java index b5eb68a8..db93e709 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java +++ b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java @@ -77,7 +77,7 @@ public void sendWhitelistTransaction_DeletedAccount_AccountDeletedException() th Transaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", new BigDecimal(10), 0); - String whitelist = new WhitelistServiceForTest().whitelistTransaction(transaction.getWhitelistableTransaction()); + String whitelist = new WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()); kinAccount.sendWhitelistTransactionSync(whitelist); } From d4405df3b5eff0f6f73b3a38a5b11f875698a513 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 10 Jun 2019 10:50:00 +0300 Subject: [PATCH 28/56] Remove stellar docs --- .../src/main/java/kin/sdk/TransactionBuilder.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java index cfb0c4c7..3290dc27 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java @@ -35,8 +35,7 @@ public int getOperationsCount() { } /** - * Adds a new operation to this transaction. + * Adds a new operation to this transaction. * * @return Builder object so you can chain methods. * @see Operation @@ -71,8 +70,7 @@ public TransactionBuilder setMemo(String memo) { } /** - * Adds a time-bounds to this transaction. + * Adds a time-bounds to this transaction. * * @return Builder object so you can chain methods. * @see TimeBounds From bf80b371cfd23976a4f6701a23dca0b2867a47f0 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 10 Jun 2019 15:59:42 +0300 Subject: [PATCH 29/56] 1. Add base transaction class and change th older one to be payment transaction and a nw one which is called raw transaction 2. Change the way we save and validate the memo in the transaction builder 3. Fix the tests according to the PR --- .../java/kin/sdk/KinAccountTest.java | 4 +- .../kin/sdk/KinAccountIntegrationTest.kt | 2 +- .../main/java/kin/sdk/AbstractKinAccount.java | 14 ++--- .../src/main/java/kin/sdk/KinAccount.java | 14 ++--- .../src/main/java/kin/sdk/KinAccountImpl.java | 6 +- .../main/java/kin/sdk/PaymentTransaction.java | 32 ++++++++++ .../{Transaction.java => RawTransaction.java} | 57 ++++-------------- .../main/java/kin/sdk/TransactionBase.java | 45 ++++++++++++++ .../main/java/kin/sdk/TransactionBuilder.java | 13 ++-- .../main/java/kin/sdk/TransactionSender.java | 8 +-- .../test/java/kin/sdk/KinAccountImplTest.java | 10 ++-- .../java/kin/sdk/TransactionSenderTest.java | 60 +++++++++---------- .../test/java/kin/sdk/TransactionTest.java | 57 +++++++++--------- .../java/sdk/sample/TransactionActivity.java | 12 ++-- 14 files changed, 191 insertions(+), 143 deletions(-) create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java rename kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/{Transaction.java => RawTransaction.java} (50%) create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java index db93e709..82baab28 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java +++ b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java @@ -65,7 +65,7 @@ public void getStatusSync_DeletedAccount_AccountDeletedException() throws Except public void sendTransactionSync_DeletedAccount_AccountDeletedException() throws Exception { KinAccount kinAccount = kinClient.addAccount(); kinClient.deleteAccount(0); - Transaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", + RawTransaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", new BigDecimal(10), FEE); kinAccount.sendTransactionSync(transaction); } @@ -74,7 +74,7 @@ public void sendTransactionSync_DeletedAccount_AccountDeletedException() throws public void sendWhitelistTransaction_DeletedAccount_AccountDeletedException() throws Exception { KinAccount kinAccount = kinClient.addAccount(); kinClient.deleteAccount(0); - Transaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", + RawTransaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", new BigDecimal(10), 0); String whitelist = new WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()); diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index 3e9977c3..a942c747 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -531,7 +531,7 @@ class KinAccountIntegrationTest { .build() // Simulate getting a transaction envelope and decode it. val transactionEnvelope = transaction.transactionEnvelope() - val externalTransaction = Transaction.decodeTransaction(transactionEnvelope) + val externalTransaction = RawTransaction.decodeTransaction(transactionEnvelope) // Sign with the master and sending the linking transaction from the master account externalTransaction.addSignature(masterAccount) masterAccount.sendTransactionSync(externalTransaction) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java index 1aed0514..3f0ee599 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java @@ -11,11 +11,11 @@ abstract class AbstractKinAccount implements KinAccount { @NonNull @Override - public Request buildTransaction(@NonNull final String publicAddress, + public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, final int fee) { - return new Request<>(new Callable() { + return new Request<>(new Callable() { @Override - public Transaction call() throws Exception { + public RawTransaction call() throws Exception { return buildTransactionSync(publicAddress, amount, fee); } }); @@ -23,11 +23,11 @@ public Transaction call() throws Exception { @NonNull @Override - public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, + public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, final int fee, @Nullable final String memo) { - return new Request<>(new Callable() { + return new Request<>(new Callable() { @Override - public Transaction call() throws Exception { + public RawTransaction call() throws Exception { return buildTransactionSync(publicAddress, amount, fee, memo); } }); @@ -46,7 +46,7 @@ public TransactionBuilder call() throws Exception { @NonNull @Override - public Request sendTransaction(final Transaction transaction) { + public Request sendTransaction(final RawTransaction transaction) { return new Request<>(new Callable() { @Override public TransactionId call() throws Exception { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java index 6231b011..e752d99c 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java @@ -30,7 +30,7 @@ public interface KinAccount { * @param fee the amount of fee(in stroops) for this transfer. * @return {@code Request}, TransactionId - the transaction identifier. */ - Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee); + Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee); /** * Build a Transaction object of the given amount in kin, to the specified public address and with a memo(that can be empty or null). @@ -41,7 +41,7 @@ public interface KinAccount { * @param memo An optional string, can contain a utf-8 string up to 21 bytes in length, included on the transaction record. * @return {@code Request}, TransactionId - the transaction identifier */ - Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo); + Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo); /** * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. @@ -53,12 +53,12 @@ public interface KinAccount { /** * Create {@link Request} for signing and sending a transaction - *

See {@link KinAccount#sendTransactionSync(Transaction)} for possibles errors

+ *

See {@link KinAccount#sendTransactionSync(RawTransaction)} for possibles errors

* @param transaction is the transaction object to send. * @return {@code Request}, TransactionId - the transaction identifier. */ @NonNull - Request sendTransaction(Transaction transaction); + Request sendTransaction(RawTransaction transaction); /** * Create {@link Request} for signing and sending a transaction from a whitelist. @@ -81,7 +81,7 @@ public interface KinAccount { * @throws AccountNotFoundException if the sender or destination account was not created. * @throws OperationFailedException other error occurred. */ - Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; + RawTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; /** * Build a Transaction object of the given amount in kin, to the specified public address and with a memo(that can be empty or null). @@ -95,7 +95,7 @@ public interface KinAccount { * @throws AccountNotFoundException if the sender or destination account was not created. * @throws OperationFailedException other error occurred. */ - Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException; + RawTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException; /** * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. @@ -119,7 +119,7 @@ public interface KinAccount { * @throws OperationFailedException other error occurred. */ @NonNull - TransactionId sendTransactionSync(Transaction transaction) throws OperationFailedException; + TransactionId sendTransactionSync(RawTransaction transaction) throws OperationFailedException; /** * send a whitelist transaction. diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java index 30150af1..5587bc1b 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java @@ -37,14 +37,14 @@ public String getPublicAddress() { } @Override - public Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, + public RawTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException { checkValidAccount(); return transactionSender.buildTransaction(account, publicAddress, amount, fee); } @Override - public Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, + public RawTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { checkValidAccount(); return transactionSender.buildTransaction(account, publicAddress, amount, fee, memo); @@ -58,7 +58,7 @@ public TransactionBuilder getTransactionBuilderSync() throws OperationFailedExce @NonNull @Override - public TransactionId sendTransactionSync(Transaction transaction) throws OperationFailedException { + public TransactionId sendTransactionSync(RawTransaction transaction) throws OperationFailedException { checkValidAccount(); return transactionSender.sendTransaction(transaction); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java new file mode 100644 index 00000000..9ec7147f --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java @@ -0,0 +1,32 @@ +package kin.sdk; + +import java.math.BigDecimal; + +public class PaymentTransaction extends TransactionBase { + + private final String destination; + private final BigDecimal amount; + private final String memo; + + public PaymentTransaction(kin.base.Transaction baseTransaction, String destination, + BigDecimal amount, String memo) { + + super(baseTransaction); + this.destination = destination; + this.amount = amount; + this.memo = memo; + } + + public String destination() { + return destination; + } + + public BigDecimal amount() { + return amount; + } + + public String memo() { + return memo; + } + +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java similarity index 50% rename from kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java rename to kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java index e82530e8..d0684f13 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java @@ -1,21 +1,16 @@ package kin.sdk; import java.io.IOException; -import java.util.List; -import kin.base.KeyPair; import kin.base.Memo; import kin.base.Network; import kin.base.Operation; import kin.base.TimeBounds; -import kin.base.xdr.DecoratedSignature; import kin.sdk.exception.DecodeTransactionException; -public class Transaction { +public class RawTransaction extends TransactionBase { - private final kin.base.Transaction baseTransaction; - - public Transaction(kin.base.Transaction baseTransaction) { - this.baseTransaction = baseTransaction; + public RawTransaction(kin.base.Transaction baseTransaction) { + super(baseTransaction); } /** @@ -23,46 +18,24 @@ public Transaction(kin.base.Transaction baseTransaction) { * * @param transactionEnvelope a Base-64 encoded TransactionEnvelope */ - public static Transaction decodeTransaction(String transactionEnvelope) throws DecodeTransactionException { + public static RawTransaction decodeTransaction(String transactionEnvelope) throws DecodeTransactionException { try { kin.base.Transaction transaction = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); - return new Transaction(transaction); + return new RawTransaction(transaction); } catch (IOException e) { throw new DecodeTransactionException(e.getMessage(), e.getCause()); } } - public KeyPair source() { - return baseTransaction.getSourceAccount(); - } - - /** - * Returns fee paid for transaction in kin base unit (1 base unit = 0.00001 KIN). - */ - public int fee() { - return baseTransaction.getFee(); - } - public Memo memo() { - return baseTransaction.getMemo(); - } - - /** - * return The transaction hash - */ - public TransactionId id() { - return new TransactionIdImpl(Utils.byteArrayToHex(baseTransaction.hash())); - } - - kin.base.Transaction baseTransaction() { - return baseTransaction; + return baseTransaction().getMemo(); } /** * see {@link WhitelistableTransaction} for more information on a whitelistable transaction

*/ public WhitelistableTransaction whitelistableTransaction() { - return new WhitelistableTransaction(baseTransaction.toEnvelopeXdrBase64(), + return new WhitelistableTransaction(baseTransaction().toEnvelopeXdrBase64(), Network.current().getNetworkPassphrase()); } @@ -71,26 +44,18 @@ public WhitelistableTransaction whitelistableTransaction() { * Transaction need to have at least one signature. */ public String transactionEnvelope() { - return baseTransaction.toEnvelopeXdrBase64(); - } - - public long sequenceNumber() { - return baseTransaction.getSequenceNumber(); + return baseTransaction().toEnvelopeXdrBase64(); } public Operation[] operations() { - return baseTransaction.getOperations(); - } - - public List signatures() { - return baseTransaction.getSignatures(); + return baseTransaction().getOperations(); } /** * @return TimeBounds, or null (representing no time restrictions) */ public TimeBounds timeBounds() { - return baseTransaction.getTimeBounds(); + return baseTransaction().getTimeBounds(); } /** @@ -98,7 +63,7 @@ public TimeBounds timeBounds() { * @param account {@link KinAccount} object which is the account who we add his signature. */ public void addSignature(KinAccount account) { - baseTransaction.sign(((KinAccountImpl) account).getKeyPair()); + baseTransaction().sign(((KinAccountImpl) account).getKeyPair()); } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java new file mode 100644 index 00000000..6ccba6ee --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java @@ -0,0 +1,45 @@ +package kin.sdk; + +import java.util.List; +import kin.base.Transaction; +import kin.base.xdr.DecoratedSignature; + +public class TransactionBase { + + private final Transaction baseTransaction; + + public TransactionBase(kin.base.Transaction baseTransaction) { + this.baseTransaction = baseTransaction; + } + + kin.base.Transaction baseTransaction() { + return baseTransaction; + } + + /** + * Returns fee paid for transaction in kin base unit (1 base unit = 0.00001 KIN). + */ + public int fee() { + return baseTransaction.getFee(); + } + + /** + * return The transaction hash + */ + public TransactionId id() { + return new TransactionIdImpl(Utils.byteArrayToHex(baseTransaction.hash())); + } + + public long sequenceNumber() { + return baseTransaction.getSequenceNumber(); + } + + public String source() { + return baseTransaction.getSourceAccount().getAccountId(); + } + + public List signatures() { + return baseTransaction.getSignatures(); + } + +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java index 3290dc27..fa9c75aa 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java @@ -2,7 +2,6 @@ import kin.base.KeyPair; import kin.base.Memo; -import kin.base.MemoText; import kin.base.Operation; import kin.base.TimeBounds; import kin.base.Transaction; @@ -14,6 +13,7 @@ public class TransactionBuilder { private final Builder builder; private final KeyPair account; private final String appId; + private String memo; /** * Construct a new transaction builder. @@ -65,7 +65,7 @@ public TransactionBuilder setFee(int fee) { * @see Memo */ public TransactionBuilder setMemo(String memo) { - builder.addMemo(Memo.text(Utils.addAppIdToMemo(appId, memo))); + this.memo = memo; return this; } @@ -83,14 +83,17 @@ public TransactionBuilder setTimeBounds(TimeBounds timeBounds) { /** * Builds a transaction. It will increment sequence number of the source account. */ - public kin.sdk.Transaction build() { + public RawTransaction build() { + Utils.validateMemo(memo); + builder.addMemo(Memo.text(Utils.addAppIdToMemo(appId, ""))); + Transaction baseTransaction = builder.build(); if (baseTransaction.getFee() < 0) { throw new IllegalArgumentException("Fee can't be negative"); } - Utils.validateMemo(((MemoText) baseTransaction.getMemo()).getText()); baseTransaction.sign(account); - return new kin.sdk.Transaction(baseTransaction); + return new RawTransaction(baseTransaction); } + } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java index be0f7ccf..0962145f 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java @@ -36,12 +36,12 @@ class TransactionSender { this.appId = appId; } - Transaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, + RawTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException { return buildTransaction(from, publicAddress, amount, fee, null); } - Transaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, + RawTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { checkParams(from, publicAddress, amount, fee, memo); memo = Utils.addAppIdToMemo(appId, memo); @@ -51,7 +51,7 @@ Transaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddres verifyAddresseeAccount(generateAddresseeKeyPair(addressee.getAccountId())); kin.base.Transaction stellarTransaction = buildStellarTransaction(from, amount, addressee, sourceAccount, fee, memo); - return new Transaction(stellarTransaction); + return new RawTransaction(stellarTransaction); } TransactionBuilder getTransactionBuilder(@NonNull KeyPair from) throws OperationFailedException { @@ -61,7 +61,7 @@ TransactionBuilder getTransactionBuilder(@NonNull KeyPair from) throws Operation return new TransactionBuilder(from, sourceAccount, appId); } - TransactionId sendTransaction(Transaction transaction) throws OperationFailedException { + TransactionId sendTransaction(RawTransaction transaction) throws OperationFailedException { return sendTransaction(transaction.baseTransaction()); } diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java index da53e78d..7ff62aea 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java @@ -58,9 +58,9 @@ public void sendTransactionSync() throws Exception { BigDecimal expectedAmount = new BigDecimal("12.2"); TransactionId expectedTransactionId = new TransactionIdImpl("myId"); - when(mockTransactionSender.sendTransaction((Transaction) any())).thenReturn(expectedTransactionId); + when(mockTransactionSender.sendTransaction((RawTransaction) any())).thenReturn(expectedTransactionId); - Transaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100); + RawTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); verify(mockTransactionSender).sendTransaction(transaction); @@ -76,9 +76,9 @@ public void sendTransactionSync_WithMemo() throws Exception { TransactionId expectedTransactionId = new TransactionIdImpl("myId"); String memo = "Dummy Memo"; - when(mockTransactionSender.sendTransaction((Transaction) any())).thenReturn(expectedTransactionId); + when(mockTransactionSender.sendTransaction((RawTransaction) any())).thenReturn(expectedTransactionId); - Transaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100, memo); + RawTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100, memo); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); verify(mockTransactionSender).sendTransaction(transaction); @@ -171,7 +171,7 @@ public void sendTransactionSync_DeletedAccount_Exception() throws Exception { initWithRandomAccount(); kinAccount.markAsDeleted(); - Transaction transaction = kinAccount + RawTransaction transaction = kinAccount .buildTransactionSync("GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX", new BigDecimal("12.2"), 100); kinAccount.sendTransactionSync(transaction); diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java index 21cfc4e3..e3146a2e 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java @@ -99,7 +99,7 @@ public void sendTransaction_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); TransactionId transactionId = transactionSender.sendTransaction(transaction); @@ -121,20 +121,20 @@ public void getTransactionBuilder_buildSuccess() throws Exception { TransactionBuilder transactionBuilder = transactionSender.getTransactionBuilder(account); TimeBounds timeBounds = new TimeBounds(42, 1337); - Transaction transaction = getLinkingTransaction(signerKey, managerDataKey, managerDataValue, transactionBuilder, + RawTransaction transaction = getLinkingTransaction(signerKey, managerDataKey, managerDataValue, transactionBuilder, timeBounds); assertThat(transactionBuilder.getOperationsCount(), equalTo(2)); assertThat(transaction.fee(), equalTo(200)); assertThat(((MemoText) transaction.memo()).getText(), equalTo(("1-" + APP_ID + "-fake memo"))); - assertThat(transaction.source().getAccountId(), equalTo(account.getAccountId())); + assertThat(transaction.source(), equalTo(account.getAccountId())); assertThat(transaction.baseTransaction().getTimeBounds(), equalTo(timeBounds)); assertArrayEquals(account.getSignatureHint().getSignatureHint(), transaction.baseTransaction().getSignatures().get(0).getHint().getSignatureHint()); assertThat(transaction.baseTransaction().getSignatures().size(), equalTo(1)); } - private Transaction getLinkingTransaction(SignerKey signerKey, String managerDataKey, String managerDataValue, + private RawTransaction getLinkingTransaction(SignerKey signerKey, String managerDataKey, String managerDataValue, TransactionBuilder transactionBuilder, TimeBounds timeBounds) { return transactionBuilder .addOperation(new Builder().setSigner(signerKey, 1).build()) @@ -155,7 +155,7 @@ public void sendTransaction_WithMemo_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); String fakeMemo = "fake memo"; - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, fakeMemo); TransactionId transactionId = transactionSender.sendTransaction(transaction); @@ -175,7 +175,7 @@ public void sendTransaction_ToAccountNotExist() throws Exception { expectedEx.expect(AccountNotFoundException.class); expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_TO))); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); transactionSender.sendTransaction(transaction); } @@ -195,7 +195,7 @@ public void sendTransaction_Http_307_Response_Success() throws Exception { .enqueue(TestUtils.generateSuccessHttp307MockResponse(location)); mockWebServerHttp307.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); TransactionId transactionId = transactionSender.sendTransaction(transaction); @@ -215,7 +215,7 @@ public void sendTransaction_FromAccountNotExist() throws Exception { expectedEx.expect(AccountNotFoundException.class); expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_FROM))); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); transactionSender.sendTransaction(transaction); } @@ -234,7 +234,7 @@ public void sendTransaction_GeneralStellarError() throws Exception { expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", contains("op_malformed"))); expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", hasSize(1))); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -251,7 +251,7 @@ public void sendTransaction_Underfunded_InsufficientKinException() throws Except expectedEx.expect(InsufficientKinException.class); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -267,7 +267,7 @@ public void sendTransaction_InsufficientFeeException() throws Exception { expectedEx.expect(InsufficientFeeException.class); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -283,7 +283,7 @@ public void sendTransaction_InsufficientBalanceException() throws Exception { expectedEx.expect(InsufficientKinException.class); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -312,7 +312,7 @@ public void sendTransaction_FirstQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(IOException.class)); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -324,7 +324,7 @@ public void sendTransaction_SecondQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -337,7 +337,7 @@ public void sendTransaction_ThirdQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -354,7 +354,7 @@ public void sendTransaction_changeTimeOut() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(SocketTimeoutException.class)); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -366,7 +366,7 @@ public void sendTransaction_FirstQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage(ACCOUNT_ID_FROM); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -379,7 +379,7 @@ public void sendTransaction_SecondQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage(ACCOUNT_ID_TO); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -393,7 +393,7 @@ public void sendTransaction_ThirdQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage("transaction"); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -404,7 +404,7 @@ public void sendTransaction_TooLongMemo() throws Exception { String tooLongMemo = "memo string can be only 21 characters"; expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Memo cannot be longer that 21 bytes(UTF-8 characters)"); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, tooLongMemo); transactionSender.sendTransaction(transaction); assertThat(mockWebServer.getRequestCount(), equalTo(0)); @@ -415,7 +415,7 @@ public void sendTransaction_TooLongMemo() throws Exception { public void sendTransaction_NullAccount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("account"); - Transaction transaction = transactionSender.buildTransaction(null, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + RawTransaction transaction = transactionSender.buildTransaction(null, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -424,7 +424,7 @@ public void sendTransaction_NullAccount() throws Exception { public void sendTransaction_NullPublicAddress() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("public address"); - Transaction transaction = transactionSender.buildTransaction(account, null, new BigDecimal("200"), FEE); + RawTransaction transaction = transactionSender.buildTransaction(account, null, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -433,7 +433,7 @@ public void sendTransaction_NullPublicAddress() throws Exception { public void sendTransaction_NullAmount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("amount"); - Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, null, FEE); + RawTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, null, FEE); transactionSender.sendTransaction(transaction); } @@ -441,7 +441,7 @@ public void sendTransaction_NullAmount() throws Exception { public void sendTransaction_EmptyPublicAddress() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("public address"); - Transaction transaction = transactionSender.buildTransaction(account, "", new BigDecimal("200"), FEE); + RawTransaction transaction = transactionSender.buildTransaction(account, "", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -449,7 +449,7 @@ public void sendTransaction_EmptyPublicAddress() throws Exception { public void sendTransaction_NegativeAmount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Amount"); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("-200"), FEE); transactionSender.sendTransaction(transaction); } @@ -458,7 +458,7 @@ public void sendTransaction_NegativeAmount() throws Exception { public void sendTransaction_NegativeFee() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Fee"); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), -FEE); transactionSender.sendTransaction(transaction); } @@ -467,7 +467,7 @@ public void sendTransaction_NegativeFee() throws Exception { public void sendTransaction_AmountExceedNumOfDecimalPlaces() throws Exception { expectedEx.expect(IllegalAmountException.class); expectedEx.expectMessage("amount can't have more then 5 digits after the decimal point"); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("20.012345"), FEE); transactionSender.sendTransaction(transaction); } @@ -478,7 +478,7 @@ public void sendTransaction_InvalidPublicIdLength() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(FormatException.class)); expectedEx.expectMessage("public address"); - Transaction transaction = transactionSender.buildTransaction(account, "ABCDEF", new BigDecimal("200"), FEE); + RawTransaction transaction = transactionSender.buildTransaction(account, "ABCDEF", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -488,7 +488,7 @@ public void sendTransaction_InvalidChecksumPublicId() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(FormatException.class)); expectedEx.expectMessage("public address"); - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OG3", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); @@ -497,7 +497,7 @@ public void sendTransaction_InvalidChecksumPublicId() throws Exception { @SuppressWarnings("SameParameterValue") private void testHttpResponseCode(int resCode) { try { - Transaction transaction = transactionSender + RawTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); fail("Expected OperationFailedException"); diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java index d185f697..2d4e53c6 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java @@ -9,6 +9,7 @@ import kin.base.Account; import kin.base.CreateAccountOperation; import kin.base.KeyPair; +import kin.base.MemoText; import kin.base.Network; import okhttp3.mockwebserver.MockWebServer; import org.junit.Before; @@ -26,29 +27,28 @@ public class TransactionTest { private static final String SECRET_SEED_FROM = "SB73L5FFTZMN6FHTOOWYEBVFTLUWQEWBLSCI4WLZADRJWENDBYL6QD6P"; private MockWebServer mockWebServer; - private Transaction transaction; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); mockServer(); Network.useTestNetwork(); - - createTransaction(); - } - private void createTransaction() { + private RawTransaction createTransaction() { KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); KeyPair destination = KeyPair.fromAccountId(ACCOUNT_ID_FROM); long sequenceNumber = 2908908335136768L; Account account = new Account(source, sequenceNumber); - kin.base.Transaction baseTransaction = new kin.base.Transaction.Builder(account) + RawTransaction transaction = new TransactionBuilder(source, account, "test") .addOperation(new CreateAccountOperation.Builder(destination, "2000").build()) - .addFee(100) + .setFee(100) .build(); - baseTransaction.sign(source); - transaction = new Transaction(baseTransaction); + + KinAccountImpl mockKinAccountImpl = mock(KinAccountImpl.class); + when(mockKinAccountImpl.getKeyPair()).thenReturn(source); + + return transaction; } private void mockServer() throws IOException { @@ -58,37 +58,40 @@ private void mockServer() throws IOException { @Test - public void getTransactionEnvelope_success() throws Exception { - mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); - mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + public void getTransactionEnvelope_success() { + RawTransaction transaction = createTransaction(); + String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAEAAAAHMS10ZXN0LQAAAAABAAAAAAAAAAAAAAAA01UjG6QUycMnvHfswo0uHc7dbPIPZ3p4h3bOqJcLWJwAAAAAC+vCAAAAAAAAAAABlwtYnAAAAEA639AzCBE9ROc1WEhKOPRilq4MJsgv+WWVB+EBTndDbUPM3v3FuKAMTVfQZA3amAclenBe04fW5xGBU6dqR3gE"; - String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA01UjG6QUycMnvHfswo0uHc7dbPIPZ3p4h3bOqJcLWJwAAAAAC+vCAAAAAAAAAAABlwtYnAAAAEBEIhSY0BYSMy5yrYkIYes9AmH0v729nVGZ1nH8CukJ5rfWoYf6Ebo4hismcjv51xlulVuTRYILuW67ENoHn1YB"; - assertThat(kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope).toEnvelopeXdrBase64(), - equalTo(transactionEnvelope)); + assertThat(transaction.transactionEnvelope(), equalTo(transactionEnvelope)); } @Test public void decodeTransaction_success() throws Exception { - mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); - mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); - - String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA01UjG6QUycMnvHfswo0uHc7dbPIPZ3p4h3bOqJcLWJwAAAAAC+vCAAAAAAAAAAABlwtYnAAAAEBEIhSY0BYSMy5yrYkIYes9AmH0v729nVGZ1nH8CukJ5rfWoYf6Ebo4hismcjv51xlulVuTRYILuW67ENoHn1YB"; - Transaction transaction = Transaction.decodeTransaction(transactionEnvelope); - assertThat("GDJVKIY3UQKMTQZHXR36ZQUNFYO45XLM6IHWO6TYQ53M5KEXBNMJYWVR", - equalTo(transaction.getSource().getAccountId())); - assertThat(2908908335136769L, equalTo(transaction.getBaseTransaction().getSequenceNumber())); - assertThat(100, equalTo(transaction.getFee())); - assertThat(transactionEnvelope, equalTo(transaction.getTransactionEnvelope())); + String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAEAAAAHMS10ZXN0LQAAAAABAAAAAAAAAAAAAAAA01UjG6QUycMnvHfswo0uHc7dbPIPZ3p4h3bOqJcLWJwAAAAAC+vCAAAAAAAAAAABlwtYnAAAAEA639AzCBE9ROc1WEhKOPRilq4MJsgv+WWVB+EBTndDbUPM3v3FuKAMTVfQZA3amAclenBe04fW5xGBU6dqR3gE"; + RawTransaction transaction = RawTransaction.decodeTransaction(transactionEnvelope); + + assertThat("GDJVKIY3UQKMTQZHXR36ZQUNFYO45XLM6IHWO6TYQ53M5KEXBNMJYWVR", equalTo(transaction.source())); + assertThat(2908908335136769L, equalTo(transaction.sequenceNumber())); + assertThat(100, equalTo(transaction.fee())); + assertThat("1-test-", equalTo(((MemoText)transaction.memo()).getText())); + assertThat("c3cfd6795a332cee3e427787852f3d167dec2c416ed26334a9b7ce634211a6cb", equalTo(transaction.id().id())); + assertThat(1, equalTo(transaction.operations().length)); + assertThat(1, equalTo(transaction.signatures().size())); + assertThat(transactionEnvelope, equalTo(transaction.transactionEnvelope())); + } + // TODO: 2019-06-10 add builder tests + @Test public void addSignature_success() { + RawTransaction transaction = createTransaction(); KinAccountImpl mockKinAccount = mock(KinAccountImpl.class); when(mockKinAccount.getKeyPair()).thenReturn(KeyPair.fromSecretSeed(SECRET_SEED_FROM)); - assertThat(transaction.getBaseTransaction().getSignatures().size(), equalTo(1)); + assertThat(transaction.signatures().size(), equalTo(1)); transaction.addSignature(mockKinAccount); - assertThat(transaction.getBaseTransaction().getSignatures().size(), equalTo(2)); + assertThat(transaction.signatures().size(), equalTo(2)); } diff --git a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java index f1e53ef5..fcd2d5d4 100644 --- a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java +++ b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java @@ -12,7 +12,7 @@ import android.widget.EditText; import java.math.BigDecimal; import kin.sdk.KinAccount; -import kin.sdk.Transaction; +import kin.sdk.RawTransaction; import kin.sdk.TransactionId; import kin.sdk.exception.AccountDeletedException; import kin.sdk.exception.OperationFailedException; @@ -37,7 +37,7 @@ public static Intent getIntent(Context context) { private EditText toAddressInput, amountInput, feeInput, memoInput; private SwitchCompat switchButton; private Request gertMinimumFeeRequest; - private Request buildTransactionRequest; + private Request buildTransactionRequest; private Request sendTransactionRequest; private WhitelistService whitelistService; @@ -248,7 +248,7 @@ private void buildTransaction(String toAddress, BigDecimal amount, int fee, Stri buildTransactionRequest.run(new BuildTransactionCallback()); } - private void handleWhitelistTransaction(Transaction transaction) { + private void handleWhitelistTransaction(RawTransaction transaction) { try { whitelistService .whitelistTransaction(transaction.whitelistableTransaction(), new WhitelistServiceListener()); @@ -258,7 +258,7 @@ private void handleWhitelistTransaction(Transaction transaction) { } } - private void sendTransaction(Transaction transaction, KinAccount account, DisplayCallback callback) { + private void sendTransaction(RawTransaction transaction, KinAccount account, DisplayCallback callback) { sendTransactionRequest = account.sendTransaction(transaction); sendTransactionRequest.run(callback); } @@ -276,10 +276,10 @@ public void displayResult(Context context, View view, TransactionId transactionI } } - private class BuildTransactionCallback implements ResultCallback { + private class BuildTransactionCallback implements ResultCallback { @Override - public void onResult(Transaction transaction) { + public void onResult(RawTransaction transaction) { Log.d(TAG, "buildTransaction: build transaction " + transaction.id().id() + " succeeded"); // This is just to differentiate between whitelist transaction and regular transaction From 8baa5dafb7f285df5cec3eeecd992539c6c980c1 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Tue, 11 Jun 2019 14:23:25 +0300 Subject: [PATCH 30/56] 1. Add base transaction class and change the older one to be payment transaction and a new one which is called raw transaction 2. Fix the tests according to the above changes --- .../src/main/java/kin/base/Transaction.java | 2 +- .../java/kin/sdk/KinAccountTest.java | 4 +- .../kin/sdk/KinAccountIntegrationTest.kt | 2 +- .../main/java/kin/sdk/AbstractKinAccount.java | 14 ++-- .../src/main/java/kin/sdk/KinAccount.java | 14 ++-- .../src/main/java/kin/sdk/KinAccountImpl.java | 6 +- .../src/main/java/kin/sdk/RawTransaction.java | 49 ------------ .../main/java/kin/sdk/TransactionBase.java | 79 ++++++++++++++++++- .../main/java/kin/sdk/TransactionSender.java | 13 ++- .../test/java/kin/sdk/KinAccountImplTest.java | 6 +- .../java/kin/sdk/TransactionSenderTest.java | 56 ++++++------- .../test/java/kin/sdk/TransactionTest.java | 2 +- .../java/sdk/sample/TransactionActivity.java | 12 +-- 13 files changed, 143 insertions(+), 116 deletions(-) diff --git a/kin-sdk/kin-base/src/main/java/kin/base/Transaction.java b/kin-sdk/kin-base/src/main/java/kin/base/Transaction.java index 3255a783..d93566a5 100644 --- a/kin-sdk/kin-base/src/main/java/kin/base/Transaction.java +++ b/kin-sdk/kin-base/src/main/java/kin/base/Transaction.java @@ -38,7 +38,7 @@ public class Transaction { mOperations = checkNotNull(operations, "operations cannot be null"); checkArgument(operations.length > 0, "At least one operation required"); - mFee = operations.length * feePerOperation; + mFee = operations.length * feePerOperation; mSignatures = new ArrayList(); mMemo = memo != null ? memo : Memo.none(); mTimeBounds = timeBounds; diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java index 82baab28..229724a5 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java +++ b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java @@ -65,7 +65,7 @@ public void getStatusSync_DeletedAccount_AccountDeletedException() throws Except public void sendTransactionSync_DeletedAccount_AccountDeletedException() throws Exception { KinAccount kinAccount = kinClient.addAccount(); kinClient.deleteAccount(0); - RawTransaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", + PaymentTransaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", new BigDecimal(10), FEE); kinAccount.sendTransactionSync(transaction); } @@ -74,7 +74,7 @@ public void sendTransactionSync_DeletedAccount_AccountDeletedException() throws public void sendWhitelistTransaction_DeletedAccount_AccountDeletedException() throws Exception { KinAccount kinAccount = kinClient.addAccount(); kinClient.deleteAccount(0); - RawTransaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", + PaymentTransaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", new BigDecimal(10), 0); String whitelist = new WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()); diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index a942c747..82b05792 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -531,7 +531,7 @@ class KinAccountIntegrationTest { .build() // Simulate getting a transaction envelope and decode it. val transactionEnvelope = transaction.transactionEnvelope() - val externalTransaction = RawTransaction.decodeTransaction(transactionEnvelope) + val externalTransaction = RawTransaction.decodeRawTransaction(transactionEnvelope) // Sign with the master and sending the linking transaction from the master account externalTransaction.addSignature(masterAccount) masterAccount.sendTransactionSync(externalTransaction) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java index 3f0ee599..bf97e977 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java @@ -11,11 +11,11 @@ abstract class AbstractKinAccount implements KinAccount { @NonNull @Override - public Request buildTransaction(@NonNull final String publicAddress, + public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, final int fee) { - return new Request<>(new Callable() { + return new Request<>(new Callable() { @Override - public RawTransaction call() throws Exception { + public PaymentTransaction call() throws Exception { return buildTransactionSync(publicAddress, amount, fee); } }); @@ -23,11 +23,11 @@ public RawTransaction call() throws Exception { @NonNull @Override - public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, + public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, final int fee, @Nullable final String memo) { - return new Request<>(new Callable() { + return new Request<>(new Callable() { @Override - public RawTransaction call() throws Exception { + public PaymentTransaction call() throws Exception { return buildTransactionSync(publicAddress, amount, fee, memo); } }); @@ -46,7 +46,7 @@ public TransactionBuilder call() throws Exception { @NonNull @Override - public Request sendTransaction(final RawTransaction transaction) { + public Request sendTransaction(final TransactionBase transaction) { return new Request<>(new Callable() { @Override public TransactionId call() throws Exception { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java index e752d99c..faa8fc9b 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java @@ -30,7 +30,7 @@ public interface KinAccount { * @param fee the amount of fee(in stroops) for this transfer. * @return {@code Request}, TransactionId - the transaction identifier. */ - Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee); + Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee); /** * Build a Transaction object of the given amount in kin, to the specified public address and with a memo(that can be empty or null). @@ -41,7 +41,7 @@ public interface KinAccount { * @param memo An optional string, can contain a utf-8 string up to 21 bytes in length, included on the transaction record. * @return {@code Request}, TransactionId - the transaction identifier */ - Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo); + Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo); /** * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. @@ -53,12 +53,12 @@ public interface KinAccount { /** * Create {@link Request} for signing and sending a transaction - *

See {@link KinAccount#sendTransactionSync(RawTransaction)} for possibles errors

+ *

See {@link KinAccount#sendTransactionSync(TransactionBase)} for possibles errors

* @param transaction is the transaction object to send. * @return {@code Request}, TransactionId - the transaction identifier. */ @NonNull - Request sendTransaction(RawTransaction transaction); + Request sendTransaction(TransactionBase transaction); /** * Create {@link Request} for signing and sending a transaction from a whitelist. @@ -81,7 +81,7 @@ public interface KinAccount { * @throws AccountNotFoundException if the sender or destination account was not created. * @throws OperationFailedException other error occurred. */ - RawTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; + PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; /** * Build a Transaction object of the given amount in kin, to the specified public address and with a memo(that can be empty or null). @@ -95,7 +95,7 @@ public interface KinAccount { * @throws AccountNotFoundException if the sender or destination account was not created. * @throws OperationFailedException other error occurred. */ - RawTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException; + PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException; /** * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. @@ -119,7 +119,7 @@ public interface KinAccount { * @throws OperationFailedException other error occurred. */ @NonNull - TransactionId sendTransactionSync(RawTransaction transaction) throws OperationFailedException; + TransactionId sendTransactionSync(TransactionBase transaction) throws OperationFailedException; /** * send a whitelist transaction. diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java index 5587bc1b..ced940a0 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java @@ -37,14 +37,14 @@ public String getPublicAddress() { } @Override - public RawTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, + public PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException { checkValidAccount(); return transactionSender.buildTransaction(account, publicAddress, amount, fee); } @Override - public RawTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, + public PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { checkValidAccount(); return transactionSender.buildTransaction(account, publicAddress, amount, fee, memo); @@ -58,7 +58,7 @@ public TransactionBuilder getTransactionBuilderSync() throws OperationFailedExce @NonNull @Override - public TransactionId sendTransactionSync(RawTransaction transaction) throws OperationFailedException { + public TransactionId sendTransactionSync(TransactionBase transaction) throws OperationFailedException { checkValidAccount(); return transactionSender.sendTransaction(transaction); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java index d0684f13..ae0f62cb 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java @@ -1,11 +1,7 @@ package kin.sdk; -import java.io.IOException; import kin.base.Memo; -import kin.base.Network; import kin.base.Operation; -import kin.base.TimeBounds; -import kin.sdk.exception.DecodeTransactionException; public class RawTransaction extends TransactionBase { @@ -13,57 +9,12 @@ public RawTransaction(kin.base.Transaction baseTransaction) { super(baseTransaction); } - /** - * Creates a Transaction instance from previously build TransactionEnvelope - * - * @param transactionEnvelope a Base-64 encoded TransactionEnvelope - */ - public static RawTransaction decodeTransaction(String transactionEnvelope) throws DecodeTransactionException { - try { - kin.base.Transaction transaction = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); - return new RawTransaction(transaction); - } catch (IOException e) { - throw new DecodeTransactionException(e.getMessage(), e.getCause()); - } - } - public Memo memo() { return baseTransaction().getMemo(); } - /** - * see {@link WhitelistableTransaction} for more information on a whitelistable transaction

- */ - public WhitelistableTransaction whitelistableTransaction() { - return new WhitelistableTransaction(baseTransaction().toEnvelopeXdrBase64(), - Network.current().getNetworkPassphrase()); - } - - /** - * Returns base64-encoded TransactionEnvelope object. - * Transaction need to have at least one signature. - */ - public String transactionEnvelope() { - return baseTransaction().toEnvelopeXdrBase64(); - } - public Operation[] operations() { return baseTransaction().getOperations(); } - /** - * @return TimeBounds, or null (representing no time restrictions) - */ - public TimeBounds timeBounds() { - return baseTransaction().getTimeBounds(); - } - - /** - * Adds a new signature to this transaction. - * @param account {@link KinAccount} object which is the account who we add his signature. - */ - public void addSignature(KinAccount account) { - baseTransaction().sign(((KinAccountImpl) account).getKeyPair()); - } - } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java index 6ccba6ee..14c9935a 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java @@ -1,10 +1,18 @@ package kin.sdk; +import java.io.IOException; +import java.math.BigDecimal; import java.util.List; +import kin.base.MemoText; +import kin.base.Network; +import kin.base.Operation; +import kin.base.PaymentOperation; +import kin.base.TimeBounds; import kin.base.Transaction; import kin.base.xdr.DecoratedSignature; +import kin.sdk.exception.DecodeTransactionException; -public class TransactionBase { +public abstract class TransactionBase { private final Transaction baseTransaction; @@ -12,6 +20,42 @@ public TransactionBase(kin.base.Transaction baseTransaction) { this.baseTransaction = baseTransaction; } + /** + * Creates a RawTransaction or a PaymentTransaction instance from previously build TransactionEnvelope + * + * @param transactionEnvelope a Base-64 encoded TransactionEnvelope + */ + public static TransactionBase decodeTransaction(String transactionEnvelope) throws DecodeTransactionException { + try { + kin.base.Transaction transaction = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); + Operation[] operations = transaction.getOperations(); + for (Operation operation : operations) { + if (operation instanceof PaymentOperation) { + PaymentOperation paymentOperation = (PaymentOperation) operation; + return new PaymentTransaction(transaction, paymentOperation.getDestination().getAccountId(), + new BigDecimal(paymentOperation.getAmount()), ((MemoText) transaction.getMemo()).getText()); + } + } + return new RawTransaction(transaction); + } catch (IOException e) { + throw new DecodeTransactionException(e.getMessage(), e.getCause()); + } + } + + /** + * Creates a RawTransaction instance from previously build TransactionEnvelope + * + * @param transactionEnvelope a Base-64 encoded TransactionEnvelope + */ + public static RawTransaction decodeRawTransaction(String transactionEnvelope) throws DecodeTransactionException { + try { + kin.base.Transaction transaction = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); + return new RawTransaction(transaction); + } catch (IOException e) { + throw new DecodeTransactionException(e.getMessage(), e.getCause()); + } + } + kin.base.Transaction baseTransaction() { return baseTransaction; } @@ -38,8 +82,41 @@ public String source() { return baseTransaction.getSourceAccount().getAccountId(); } + /** + * @return TimeBounds, or null (representing no time restrictions) + */ + public TimeBounds timeBounds() { + return baseTransaction.getTimeBounds(); + } + public List signatures() { return baseTransaction.getSignatures(); } + /** + * Returns base64-encoded TransactionEnvelope object. + * Transaction need to have at least one signature. + */ + public String transactionEnvelope() { + return baseTransaction.toEnvelopeXdrBase64(); + } + + /** + * see {@link WhitelistableTransaction} for more information on a whitelistable transaction

+ */ + public WhitelistableTransaction whitelistableTransaction() { + return new WhitelistableTransaction(baseTransaction.toEnvelopeXdrBase64(), + Network.current().getNetworkPassphrase()); + } + + /** + * Adds a new signature to this transaction. + * @param account {@link KinAccount} object which is the account who we add his signature. + */ + public void addSignature(KinAccount account) { + baseTransaction().sign(((KinAccountImpl) account).getKeyPair()); + } + + + } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java index 0962145f..cd30dbe3 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java @@ -36,12 +36,12 @@ class TransactionSender { this.appId = appId; } - RawTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, + PaymentTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException { return buildTransaction(from, publicAddress, amount, fee, null); } - RawTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, + PaymentTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { checkParams(from, publicAddress, amount, fee, memo); memo = Utils.addAppIdToMemo(appId, memo); @@ -49,19 +49,18 @@ RawTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAdd KeyPair addressee = generateAddresseeKeyPair(publicAddress); AccountResponse sourceAccount = loadSourceAccount(from); verifyAddresseeAccount(generateAddresseeKeyPair(addressee.getAccountId())); - kin.base.Transaction stellarTransaction = buildStellarTransaction(from, amount, addressee, sourceAccount, fee, + kin.base.Transaction stellarTransaction = buildBaseTransaction(from, amount, addressee, sourceAccount, fee, memo); - return new RawTransaction(stellarTransaction); + return new PaymentTransaction(stellarTransaction, addressee.getAccountId(), amount, memo); } TransactionBuilder getTransactionBuilder(@NonNull KeyPair from) throws OperationFailedException { Utils.checkNotNull(from, "from"); AccountResponse sourceAccount = loadSourceAccount(from); - // TODO: 2019-05-27 need to decide if we make the fee a "must" in the constructor... return new TransactionBuilder(from, sourceAccount, appId); } - TransactionId sendTransaction(RawTransaction transaction) throws OperationFailedException { + TransactionId sendTransaction(TransactionBase transaction) throws OperationFailedException { return sendTransaction(transaction.baseTransaction()); } @@ -123,7 +122,7 @@ private KeyPair generateAddresseeKeyPair(@NonNull String publicAddress) throws O } @NonNull - private kin.base.Transaction buildStellarTransaction(@NonNull KeyPair from, @NonNull BigDecimal amount, + private kin.base.Transaction buildBaseTransaction(@NonNull KeyPair from, @NonNull BigDecimal amount, KeyPair addressee, AccountResponse sourceAccount, int fee, @Nullable String memo) { Builder transactionBuilder = new Builder(sourceAccount) diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java index 7ff62aea..2acfd19e 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java @@ -60,7 +60,7 @@ public void sendTransactionSync() throws Exception { when(mockTransactionSender.sendTransaction((RawTransaction) any())).thenReturn(expectedTransactionId); - RawTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100); + PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); verify(mockTransactionSender).sendTransaction(transaction); @@ -78,7 +78,7 @@ public void sendTransactionSync_WithMemo() throws Exception { when(mockTransactionSender.sendTransaction((RawTransaction) any())).thenReturn(expectedTransactionId); - RawTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100, memo); + PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100, memo); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); verify(mockTransactionSender).sendTransaction(transaction); @@ -171,7 +171,7 @@ public void sendTransactionSync_DeletedAccount_Exception() throws Exception { initWithRandomAccount(); kinAccount.markAsDeleted(); - RawTransaction transaction = kinAccount + PaymentTransaction transaction = kinAccount .buildTransactionSync("GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX", new BigDecimal("12.2"), 100); kinAccount.sendTransactionSync(transaction); diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java index e3146a2e..fed7190c 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java @@ -99,7 +99,7 @@ public void sendTransaction_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); TransactionId transactionId = transactionSender.sendTransaction(transaction); @@ -126,7 +126,7 @@ public void getTransactionBuilder_buildSuccess() throws Exception { assertThat(transactionBuilder.getOperationsCount(), equalTo(2)); assertThat(transaction.fee(), equalTo(200)); - assertThat(((MemoText) transaction.memo()).getText(), equalTo(("1-" + APP_ID + "-fake memo"))); + assertThat(((MemoText) transaction.memo()).getText(), equalTo(("1-" + APP_ID + "-"))); assertThat(transaction.source(), equalTo(account.getAccountId())); assertThat(transaction.baseTransaction().getTimeBounds(), equalTo(timeBounds)); assertArrayEquals(account.getSignatureHint().getSignatureHint(), @@ -155,7 +155,7 @@ public void sendTransaction_WithMemo_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); String fakeMemo = "fake memo"; - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, fakeMemo); TransactionId transactionId = transactionSender.sendTransaction(transaction); @@ -175,7 +175,7 @@ public void sendTransaction_ToAccountNotExist() throws Exception { expectedEx.expect(AccountNotFoundException.class); expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_TO))); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); transactionSender.sendTransaction(transaction); } @@ -195,7 +195,7 @@ public void sendTransaction_Http_307_Response_Success() throws Exception { .enqueue(TestUtils.generateSuccessHttp307MockResponse(location)); mockWebServerHttp307.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); TransactionId transactionId = transactionSender.sendTransaction(transaction); @@ -215,7 +215,7 @@ public void sendTransaction_FromAccountNotExist() throws Exception { expectedEx.expect(AccountNotFoundException.class); expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_FROM))); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); transactionSender.sendTransaction(transaction); } @@ -234,7 +234,7 @@ public void sendTransaction_GeneralStellarError() throws Exception { expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", contains("op_malformed"))); expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", hasSize(1))); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -251,7 +251,7 @@ public void sendTransaction_Underfunded_InsufficientKinException() throws Except expectedEx.expect(InsufficientKinException.class); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -267,7 +267,7 @@ public void sendTransaction_InsufficientFeeException() throws Exception { expectedEx.expect(InsufficientFeeException.class); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -283,7 +283,7 @@ public void sendTransaction_InsufficientBalanceException() throws Exception { expectedEx.expect(InsufficientKinException.class); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -312,7 +312,7 @@ public void sendTransaction_FirstQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(IOException.class)); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -324,7 +324,7 @@ public void sendTransaction_SecondQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -337,7 +337,7 @@ public void sendTransaction_ThirdQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -354,7 +354,7 @@ public void sendTransaction_changeTimeOut() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(SocketTimeoutException.class)); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -366,7 +366,7 @@ public void sendTransaction_FirstQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage(ACCOUNT_ID_FROM); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -379,7 +379,7 @@ public void sendTransaction_SecondQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage(ACCOUNT_ID_TO); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -393,7 +393,7 @@ public void sendTransaction_ThirdQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage("transaction"); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -404,7 +404,7 @@ public void sendTransaction_TooLongMemo() throws Exception { String tooLongMemo = "memo string can be only 21 characters"; expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Memo cannot be longer that 21 bytes(UTF-8 characters)"); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, tooLongMemo); transactionSender.sendTransaction(transaction); assertThat(mockWebServer.getRequestCount(), equalTo(0)); @@ -415,7 +415,7 @@ public void sendTransaction_TooLongMemo() throws Exception { public void sendTransaction_NullAccount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("account"); - RawTransaction transaction = transactionSender.buildTransaction(null, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildTransaction(null, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -424,7 +424,7 @@ public void sendTransaction_NullAccount() throws Exception { public void sendTransaction_NullPublicAddress() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("public address"); - RawTransaction transaction = transactionSender.buildTransaction(account, null, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildTransaction(account, null, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -433,7 +433,7 @@ public void sendTransaction_NullPublicAddress() throws Exception { public void sendTransaction_NullAmount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("amount"); - RawTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, null, FEE); + PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, null, FEE); transactionSender.sendTransaction(transaction); } @@ -441,7 +441,7 @@ public void sendTransaction_NullAmount() throws Exception { public void sendTransaction_EmptyPublicAddress() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("public address"); - RawTransaction transaction = transactionSender.buildTransaction(account, "", new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildTransaction(account, "", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -449,7 +449,7 @@ public void sendTransaction_EmptyPublicAddress() throws Exception { public void sendTransaction_NegativeAmount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Amount"); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("-200"), FEE); transactionSender.sendTransaction(transaction); } @@ -458,7 +458,7 @@ public void sendTransaction_NegativeAmount() throws Exception { public void sendTransaction_NegativeFee() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Fee"); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), -FEE); transactionSender.sendTransaction(transaction); } @@ -467,7 +467,7 @@ public void sendTransaction_NegativeFee() throws Exception { public void sendTransaction_AmountExceedNumOfDecimalPlaces() throws Exception { expectedEx.expect(IllegalAmountException.class); expectedEx.expectMessage("amount can't have more then 5 digits after the decimal point"); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("20.012345"), FEE); transactionSender.sendTransaction(transaction); } @@ -478,7 +478,7 @@ public void sendTransaction_InvalidPublicIdLength() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(FormatException.class)); expectedEx.expectMessage("public address"); - RawTransaction transaction = transactionSender.buildTransaction(account, "ABCDEF", new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildTransaction(account, "ABCDEF", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -488,7 +488,7 @@ public void sendTransaction_InvalidChecksumPublicId() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(FormatException.class)); expectedEx.expectMessage("public address"); - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OG3", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); @@ -497,7 +497,7 @@ public void sendTransaction_InvalidChecksumPublicId() throws Exception { @SuppressWarnings("SameParameterValue") private void testHttpResponseCode(int resCode) { try { - RawTransaction transaction = transactionSender + PaymentTransaction transaction = transactionSender .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); fail("Expected OperationFailedException"); diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java index 2d4e53c6..8260dd95 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java @@ -68,7 +68,7 @@ public void getTransactionEnvelope_success() { @Test public void decodeTransaction_success() throws Exception { String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAEAAAAHMS10ZXN0LQAAAAABAAAAAAAAAAAAAAAA01UjG6QUycMnvHfswo0uHc7dbPIPZ3p4h3bOqJcLWJwAAAAAC+vCAAAAAAAAAAABlwtYnAAAAEA639AzCBE9ROc1WEhKOPRilq4MJsgv+WWVB+EBTndDbUPM3v3FuKAMTVfQZA3amAclenBe04fW5xGBU6dqR3gE"; - RawTransaction transaction = RawTransaction.decodeTransaction(transactionEnvelope); + RawTransaction transaction = RawTransaction.decodeRawTransaction(transactionEnvelope); assertThat("GDJVKIY3UQKMTQZHXR36ZQUNFYO45XLM6IHWO6TYQ53M5KEXBNMJYWVR", equalTo(transaction.source())); assertThat(2908908335136769L, equalTo(transaction.sequenceNumber())); diff --git a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java index fcd2d5d4..72fe8b5d 100644 --- a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java +++ b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java @@ -12,7 +12,7 @@ import android.widget.EditText; import java.math.BigDecimal; import kin.sdk.KinAccount; -import kin.sdk.RawTransaction; +import kin.sdk.PaymentTransaction; import kin.sdk.TransactionId; import kin.sdk.exception.AccountDeletedException; import kin.sdk.exception.OperationFailedException; @@ -37,7 +37,7 @@ public static Intent getIntent(Context context) { private EditText toAddressInput, amountInput, feeInput, memoInput; private SwitchCompat switchButton; private Request gertMinimumFeeRequest; - private Request buildTransactionRequest; + private Request buildTransactionRequest; private Request sendTransactionRequest; private WhitelistService whitelistService; @@ -248,7 +248,7 @@ private void buildTransaction(String toAddress, BigDecimal amount, int fee, Stri buildTransactionRequest.run(new BuildTransactionCallback()); } - private void handleWhitelistTransaction(RawTransaction transaction) { + private void handleWhitelistTransaction(PaymentTransaction transaction) { try { whitelistService .whitelistTransaction(transaction.whitelistableTransaction(), new WhitelistServiceListener()); @@ -258,7 +258,7 @@ private void handleWhitelistTransaction(RawTransaction transaction) { } } - private void sendTransaction(RawTransaction transaction, KinAccount account, DisplayCallback callback) { + private void sendTransaction(PaymentTransaction transaction, KinAccount account, DisplayCallback callback) { sendTransactionRequest = account.sendTransaction(transaction); sendTransactionRequest.run(callback); } @@ -276,10 +276,10 @@ public void displayResult(Context context, View view, TransactionId transactionI } } - private class BuildTransactionCallback implements ResultCallback { + private class BuildTransactionCallback implements ResultCallback { @Override - public void onResult(RawTransaction transaction) { + public void onResult(PaymentTransaction transaction) { Log.d(TAG, "buildTransaction: build transaction " + transaction.id().id() + " succeeded"); // This is just to differentiate between whitelist transaction and regular transaction From 0f3fdb94322ba69401ad7a73c35a12634d57223b Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Tue, 11 Jun 2019 16:32:32 +0300 Subject: [PATCH 31/56] 1. Add memo appId to memo only after validation of the memo 2. Add unit tests to the TransactionBuilder --- .../main/java/kin/sdk/TransactionBase.java | 2 - .../main/java/kin/sdk/TransactionBuilder.java | 3 +- .../test/java/kin/sdk/TransactionTest.java | 71 ++++++++++++++++--- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java index 14c9935a..db7b3c81 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java @@ -117,6 +117,4 @@ public void addSignature(KinAccount account) { baseTransaction().sign(((KinAccountImpl) account).getKeyPair()); } - - } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java index fa9c75aa..b46138fc 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBuilder.java @@ -1,5 +1,6 @@ package kin.sdk; +import android.text.TextUtils; import kin.base.KeyPair; import kin.base.Memo; import kin.base.Operation; @@ -85,7 +86,7 @@ public TransactionBuilder setTimeBounds(TimeBounds timeBounds) { */ public RawTransaction build() { Utils.validateMemo(memo); - builder.addMemo(Memo.text(Utils.addAppIdToMemo(appId, ""))); + builder.addMemo(Memo.text(Utils.addAppIdToMemo(appId, TextUtils.isEmpty(memo) ? "" : memo))); Transaction baseTransaction = builder.build(); if (baseTransaction.getFee() < 0) { diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java index 8260dd95..1625c63f 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java @@ -1,6 +1,7 @@ package kin.sdk; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -13,7 +14,9 @@ import kin.base.Network; import okhttp3.mockwebserver.MockWebServer; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -26,6 +29,9 @@ public class TransactionTest { private static final String ACCOUNT_ID_FROM = "GDJVKIY3UQKMTQZHXR36ZQUNFYO45XLM6IHWO6TYQ53M5KEXBNMJYWVR"; private static final String SECRET_SEED_FROM = "SB73L5FFTZMN6FHTOOWYEBVFTLUWQEWBLSCI4WLZADRJWENDBYL6QD6P"; + @Rule + public ExpectedException expectedEx = ExpectedException.none(); + private MockWebServer mockWebServer; @Before @@ -37,13 +43,7 @@ public void setup() throws Exception { private RawTransaction createTransaction() { KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); - KeyPair destination = KeyPair.fromAccountId(ACCOUNT_ID_FROM); - long sequenceNumber = 2908908335136768L; - Account account = new Account(source, sequenceNumber); - RawTransaction transaction = new TransactionBuilder(source, account, "test") - .addOperation(new CreateAccountOperation.Builder(destination, "2000").build()) - .setFee(100) - .build(); + RawTransaction transaction = buildTransaction(source, 100, "fake memo"); KinAccountImpl mockKinAccountImpl = mock(KinAccountImpl.class); when(mockKinAccountImpl.getKeyPair()).thenReturn(source); @@ -51,12 +51,23 @@ private RawTransaction createTransaction() { return transaction; } + private RawTransaction buildTransaction(KeyPair source, int fee, String memo) { + KeyPair destination = KeyPair.fromAccountId(ACCOUNT_ID_FROM); + long sequenceNumber = 2908908335136768L; + Account account = new Account(source, sequenceNumber); + return new TransactionBuilder(source, account, "test") + .addOperation(new CreateAccountOperation.Builder(destination, "2000").build()) + .setFee(fee) + .setMemo(memo) + .build(); + } + + private void mockServer() throws IOException { mockWebServer = new MockWebServer(); mockWebServer.start(); } - @Test public void getTransactionEnvelope_success() { RawTransaction transaction = createTransaction(); @@ -81,8 +92,6 @@ public void decodeTransaction_success() throws Exception { } - // TODO: 2019-06-10 add builder tests - @Test public void addSignature_success() { RawTransaction transaction = createTransaction(); @@ -94,5 +103,47 @@ public void addSignature_success() { assertThat(transaction.signatures().size(), equalTo(2)); } + @Test + public void getTransactionBuilder_buildSuccess() { + RawTransaction transaction = createTransaction(); + assertThat(((MemoText) transaction.memo()).getText(), equalTo("1-test-fake memo")); + assertThat(transaction.fee(), equalTo(100)); + assertThat(transaction.operations().length, equalTo(1)); + assertThat(transaction.operations()[0], instanceOf(CreateAccountOperation.class)); + assertThat(transaction.sequenceNumber(), equalTo(2908908335136769L)); + assertThat(transaction.signatures().size(), equalTo(1)); + } + + @Test + public void getTransactionBuilder_memoNotValid_IllegalArgumentException() { + expectedEx.expect(IllegalArgumentException.class); + expectedEx.expectMessage("Memo cannot be longer that 21 bytes(UTF-8 characters)"); + + KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); + buildTransaction(source, 100, "memo is not valid because it is too long"); + } + + @Test + public void getTransactionBuilder_feeIsNotValid_IllegalArgumentException() { + expectedEx.expect(IllegalArgumentException.class); + expectedEx.expectMessage("Fee can't be negative"); + + KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); + buildTransaction(source,-10, "fake memo"); + } + + @Test + public void getTransactionBuilder_noOperations_IllegalArgumentException() { + expectedEx.expect(IllegalArgumentException.class); + expectedEx.expectMessage("At least one operation required"); + + KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); + long sequenceNumber = 2908908335136768L; + Account account = new Account(source, sequenceNumber); + new TransactionBuilder(source, account, "test") + .setFee(100) + .setMemo("fake memo") + .build(); + } } From e3d281918f4ccf2289fea1c65dd0652204c60fe3 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Tue, 11 Jun 2019 16:38:14 +0300 Subject: [PATCH 32/56] 1. Fix the tests because change the transaction creation --- .../src/test/java/kin/sdk/TransactionSenderTest.java | 2 +- .../src/test/java/kin/sdk/TransactionTest.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java index fed7190c..cbb09286 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java @@ -126,7 +126,7 @@ public void getTransactionBuilder_buildSuccess() throws Exception { assertThat(transactionBuilder.getOperationsCount(), equalTo(2)); assertThat(transaction.fee(), equalTo(200)); - assertThat(((MemoText) transaction.memo()).getText(), equalTo(("1-" + APP_ID + "-"))); + assertThat(((MemoText) transaction.memo()).getText(), equalTo(("1-" + APP_ID + "-fake memo"))); assertThat(transaction.source(), equalTo(account.getAccountId())); assertThat(transaction.baseTransaction().getTimeBounds(), equalTo(timeBounds)); assertArrayEquals(account.getSignatureHint().getSignatureHint(), diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java index 1625c63f..3ccd526a 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java @@ -71,21 +71,21 @@ private void mockServer() throws IOException { @Test public void getTransactionEnvelope_success() { RawTransaction transaction = createTransaction(); - String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAEAAAAHMS10ZXN0LQAAAAABAAAAAAAAAAAAAAAA01UjG6QUycMnvHfswo0uHc7dbPIPZ3p4h3bOqJcLWJwAAAAAC+vCAAAAAAAAAAABlwtYnAAAAEA639AzCBE9ROc1WEhKOPRilq4MJsgv+WWVB+EBTndDbUPM3v3FuKAMTVfQZA3amAclenBe04fW5xGBU6dqR3gE"; + String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAEAAAAQMS10ZXN0LWZha2UgbWVtbwAAAAEAAAAAAAAAAAAAAADTVSMbpBTJwye8d+zCjS4dzt1s8g9neniHds6olwtYnAAAAAAL68IAAAAAAAAAAAGXC1icAAAAQFUTzwPlsEzibdVe1wjk9Bcz4TOsvY8FAc66aCfusHBEUa7vDyxwiV/ia79PWqhr+0vlXmkMI4xS14JVYcQ3Dwg="; assertThat(transaction.transactionEnvelope(), equalTo(transactionEnvelope)); } @Test public void decodeTransaction_success() throws Exception { - String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAEAAAAHMS10ZXN0LQAAAAABAAAAAAAAAAAAAAAA01UjG6QUycMnvHfswo0uHc7dbPIPZ3p4h3bOqJcLWJwAAAAAC+vCAAAAAAAAAAABlwtYnAAAAEA639AzCBE9ROc1WEhKOPRilq4MJsgv+WWVB+EBTndDbUPM3v3FuKAMTVfQZA3amAclenBe04fW5xGBU6dqR3gE"; + String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAEAAAAQMS10ZXN0LWZha2UgbWVtbwAAAAEAAAAAAAAAAAAAAADTVSMbpBTJwye8d+zCjS4dzt1s8g9neniHds6olwtYnAAAAAAL68IAAAAAAAAAAAGXC1icAAAAQFUTzwPlsEzibdVe1wjk9Bcz4TOsvY8FAc66aCfusHBEUa7vDyxwiV/ia79PWqhr+0vlXmkMI4xS14JVYcQ3Dwg="; RawTransaction transaction = RawTransaction.decodeRawTransaction(transactionEnvelope); assertThat("GDJVKIY3UQKMTQZHXR36ZQUNFYO45XLM6IHWO6TYQ53M5KEXBNMJYWVR", equalTo(transaction.source())); assertThat(2908908335136769L, equalTo(transaction.sequenceNumber())); assertThat(100, equalTo(transaction.fee())); - assertThat("1-test-", equalTo(((MemoText)transaction.memo()).getText())); - assertThat("c3cfd6795a332cee3e427787852f3d167dec2c416ed26334a9b7ce634211a6cb", equalTo(transaction.id().id())); + assertThat("1-test-fake memo", equalTo(((MemoText)transaction.memo()).getText())); + assertThat("bdf973d35e8f45c6d498b4c4f282f7d6e79942f58a3caa01944f620b8f776f92", equalTo(transaction.id().id())); assertThat(1, equalTo(transaction.operations().length)); assertThat(1, equalTo(transaction.signatures().size())); assertThat(transactionEnvelope, equalTo(transaction.transactionEnvelope())); From 9cf68abee7cf5502c88cf50f2ed96a043823eef9 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Wed, 12 Jun 2019 10:41:03 +0300 Subject: [PATCH 33/56] 1. Rename all the variations of buildTransaction to be buildPaymentTransaction. 2. Add docs and change readme according to the new naming of some methods. 3. Fix the decode transaction so it will return PaymentTransaction only if there was only one payment operation. Also add tests for this method. --- kin-sdk/README.md | 16 ++--- .../java/kin/sdk/KinAccountTest.java | 8 +-- .../kin/sdk/KinAccountIntegrationTest.kt | 24 ++++---- .../main/java/kin/sdk/AbstractKinAccount.java | 8 +-- .../src/main/java/kin/sdk/KinAccount.java | 12 ++-- .../src/main/java/kin/sdk/KinAccountImpl.java | 8 +-- .../main/java/kin/sdk/PaymentTransaction.java | 11 +++- .../src/main/java/kin/sdk/RawTransaction.java | 9 ++- .../main/java/kin/sdk/TransactionBase.java | 27 +++++++-- .../main/java/kin/sdk/TransactionSender.java | 6 +- .../test/java/kin/sdk/KinAccountImplTest.java | 6 +- .../java/kin/sdk/TransactionSenderTest.java | 54 ++++++++--------- .../test/java/kin/sdk/TransactionTest.java | 58 ++++++++++++++++--- .../java/sdk/sample/TransactionActivity.java | 22 +++---- 14 files changed, 170 insertions(+), 99 deletions(-) diff --git a/kin-sdk/README.md b/kin-sdk/README.md index 84e0d53f..329fb5a7 100644 --- a/kin-sdk/README.md +++ b/kin-sdk/README.md @@ -221,12 +221,12 @@ int fee = 100; Build the transaction and get a `Request` object. ```java -buildTransactionRequest = account.buildTransaction(toAddress, amountInKin, fee); +buildPaymentTransactionRequest = account.buildPaymentTransaction(toAddress, amountInKin, fee); ``` To keep the UI smooth we want to build and later send the transaction in background threads. To do that we also need to have some callbacks. First of all let's build the transaction and define the callback (the code below is incomplete, continue for the complete snippet): ```java -buildTransactionRequest.run(new ResultCallback() { +buildPaymentTransactionRequest.run(new ResultCallback() { @Override public void onResult(Transaction transaction) { @@ -282,9 +282,9 @@ BigDecimal amountInKin = new BigDecimal("20"); int fee = 100; // Build the transaction and get a Request object. -buildTransactionRequest = account.buildTransaction(toAddress, amountInKin, fee); +buildPaymentTransactionRequest = account.buildPaymentTransaction(toAddress, amountInKin, fee); // Actually run the build transaction code in a background thread -buildTransactionRequest.run(new ResultCallback() { +buildPaymentTransactionRequest.run(new ResultCallback() { @Override public void onResult(Transaction transaction) { @@ -335,8 +335,8 @@ BigDecimal amountInKin = new BigDecimal("20"); // even if the user enters an amount larger then zero it will not be deducted from their balance. int fee = 0 -buildTransactionRequest = account.buildTransaction(toAddress, amountInKin, fee); -buildTransactionRequest.run(new ResultCallback() { +buildPaymentTransactionRequest = account.buildPaymentTransaction(toAddress, amountInKin, fee); +buildPaymentTransactionRequest.run(new ResultCallback() { @Override public void onResult(Transaction transaction) { @@ -382,8 +382,8 @@ The value of `appID` is automatically added to transaction memo. This is require ```java String memo = "arbitrary data"; -buildTransactionRequest = account.buildTransaction(toAddress, amountInKin, fee, memo); -buildTransactionRequest.run(new ResultCallback() { +buildPaymentTransactionRequest = account.buildPaymentTransaction(toAddress, amountInKin, fee, memo); +buildPaymentTransactionRequest.run(new ResultCallback() { @Override public void onResult(Transaction transaction) { diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java index 229724a5..08692155 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java +++ b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java @@ -34,10 +34,10 @@ public void teardown() { } @Test(expected = AccountDeletedException.class) - public void buildTransactionSync_DeletedAccount_AccountDeletedException() throws Exception { + public void buildPaymentTransactionSync_DeletedAccount_AccountDeletedException() throws Exception { KinAccount kinAccount = kinClient.addAccount(); kinClient.deleteAccount(0); - kinAccount.buildTransactionSync(kinAccount.getPublicAddress(), new BigDecimal(200), 100); + kinAccount.buildPaymentTransactionSync(kinAccount.getPublicAddress(), new BigDecimal(200), 100); } @Test(expected = AccountDeletedException.class) @@ -65,7 +65,7 @@ public void getStatusSync_DeletedAccount_AccountDeletedException() throws Except public void sendTransactionSync_DeletedAccount_AccountDeletedException() throws Exception { KinAccount kinAccount = kinClient.addAccount(); kinClient.deleteAccount(0); - PaymentTransaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", + PaymentTransaction transaction = kinAccount.buildPaymentTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", new BigDecimal(10), FEE); kinAccount.sendTransactionSync(transaction); } @@ -74,7 +74,7 @@ public void sendTransactionSync_DeletedAccount_AccountDeletedException() throws public void sendWhitelistTransaction_DeletedAccount_AccountDeletedException() throws Exception { KinAccount kinAccount = kinClient.addAccount(); kinClient.deleteAccount(0); - PaymentTransaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", + PaymentTransaction transaction = kinAccount.buildPaymentTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", new BigDecimal(10), 0); String whitelist = new WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()); diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index 82b05792..17f58cbc 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -247,7 +247,7 @@ class KinAccountIntegrationTest { val latch = CountDownLatch(1) val listenerRegistration = kinAccountReceiver.addPaymentListener { _ -> latch.countDown() } - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee, memo) val transactionId = kinAccountSender.sendTransactionSync(transaction) assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin))) @@ -274,7 +274,7 @@ class KinAccountIntegrationTest { expectedEx.expect(AccountNotFoundException::class.java) expectedEx.expectMessage(kinAccountReceiver.publicAddress) - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) kinAccountSender.sendTransactionSync(transaction) } @@ -290,7 +290,7 @@ class KinAccountIntegrationTest { expectedEx.expect(AccountNotFoundException::class.java) expectedEx.expectMessage(kinAccountSender.publicAddress) - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) kinAccountSender.sendTransactionSync(transaction) } @@ -306,7 +306,7 @@ class KinAccountIntegrationTest { expectedEx.expect(AccountNotFoundException::class.java) expectedEx.expectMessage(kinAccountSender.publicAddress) - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()) kinAccountSender.sendWhitelistTransactionSync(whitelist) } @@ -323,7 +323,7 @@ class KinAccountIntegrationTest { expectedEx.expect(AccountNotFoundException::class.java) expectedEx.expectMessage(kinAccountReceiver.publicAddress) - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()) kinAccountSender.sendWhitelistTransactionSync(whitelist) @@ -337,7 +337,7 @@ class KinAccountIntegrationTest { expectedEx.expect(InsufficientFeeException::class.java) val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), minFee - 1) + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), minFee - 1) kinAccountSender.sendTransactionSync(transaction) } @@ -348,7 +348,7 @@ class KinAccountIntegrationTest { val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100) val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("20"), minFee + 100000) val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()) kinAccountSender.sendWhitelistTransactionSync(whitelist) @@ -362,7 +362,7 @@ class KinAccountIntegrationTest { val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100) val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("20"), minFee) val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistableTransaction()) kinAccountSender.sendWhitelistTransactionSync(whitelist) @@ -379,7 +379,7 @@ class KinAccountIntegrationTest { val latch = CountDownLatch(1) val listenerRegistration = kinAccountReceiver.addPaymentListener { _ -> latch.countDown() } - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) val transactionId = kinAccountSender.sendTransactionSync(transaction) assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("78.87700").subtract(feeInKin))) @@ -435,7 +435,7 @@ class KinAccountIntegrationTest { fakeKinOnBoard.fundWithKin(kinAccountSender.publicAddress.orEmpty(), "100") val memo = "memo" val expectedMemo = addAppIdToMemo(memo) - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), transactionAmount, fee, memo) + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), transactionAmount, fee, memo) val expectedTransactionId = kinAccountSender.sendTransactionSync(transaction) //verify data notified by listeners @@ -469,7 +469,7 @@ class KinAccountIntegrationTest { } listenerRegistration.remove() - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee, null) + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee, null) kinAccountSender.sendTransactionSync(transaction) latch.await(timeoutDurationSecondsLong, TimeUnit.SECONDS) } @@ -481,7 +481,7 @@ class KinAccountIntegrationTest { val (kinAccountSender, kinAccountReceiver) = onboardAccounts() expectedEx.expect(InsufficientKinException::class.java) - val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) + val transaction = kinAccountSender.buildPaymentTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) kinAccountSender.sendTransactionSync(transaction) } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java index bf97e977..80b2e582 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/AbstractKinAccount.java @@ -11,24 +11,24 @@ abstract class AbstractKinAccount implements KinAccount { @NonNull @Override - public Request buildTransaction(@NonNull final String publicAddress, + public Request buildPaymentTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, final int fee) { return new Request<>(new Callable() { @Override public PaymentTransaction call() throws Exception { - return buildTransactionSync(publicAddress, amount, fee); + return buildPaymentTransactionSync(publicAddress, amount, fee); } }); } @NonNull @Override - public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, + public Request buildPaymentTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, final int fee, @Nullable final String memo) { return new Request<>(new Callable() { @Override public PaymentTransaction call() throws Exception { - return buildTransactionSync(publicAddress, amount, fee, memo); + return buildPaymentTransactionSync(publicAddress, amount, fee, memo); } }); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java index faa8fc9b..df314841 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java @@ -24,24 +24,24 @@ public interface KinAccount { /** * Build a Transaction object of the given amount in kin, to the specified public address. - *

See {@link KinAccount#buildTransactionSync(String, BigDecimal, int)} for possibles errors

+ *

See {@link KinAccount#buildPaymentTransactionSync(String, BigDecimal, int)} for possibles errors

* @param publicAddress the account address to send the specified kin amount. * @param amount the amount of kin to transfer. * @param fee the amount of fee(in stroops) for this transfer. * @return {@code Request}, TransactionId - the transaction identifier. */ - Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee); + Request buildPaymentTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee); /** * Build a Transaction object of the given amount in kin, to the specified public address and with a memo(that can be empty or null). - *

See {@link KinAccount#buildTransactionSync(String, BigDecimal, int, String)} for possibles errors

+ *

See {@link KinAccount#buildPaymentTransactionSync(String, BigDecimal, int, String)} for possibles errors

* @param publicAddress the account address to send the specified kin amount. * @param amount the amount of kin to transfer. * @param fee the amount of fee(in stroops) for this transfer. * @param memo An optional string, can contain a utf-8 string up to 21 bytes in length, included on the transaction record. * @return {@code Request}, TransactionId - the transaction identifier */ - Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo); + Request buildPaymentTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo); /** * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. @@ -81,7 +81,7 @@ public interface KinAccount { * @throws AccountNotFoundException if the sender or destination account was not created. * @throws OperationFailedException other error occurred. */ - PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; + PaymentTransaction buildPaymentTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; /** * Build a Transaction object of the given amount in kin, to the specified public address and with a memo(that can be empty or null). @@ -95,7 +95,7 @@ public interface KinAccount { * @throws AccountNotFoundException if the sender or destination account was not created. * @throws OperationFailedException other error occurred. */ - PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException; + PaymentTransaction buildPaymentTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException; /** * Get a {@code TransactionBuilder} object, which let you fully customize your transaction before you send it. diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java index ced940a0..bb88b514 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccountImpl.java @@ -37,17 +37,17 @@ public String getPublicAddress() { } @Override - public PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, + public PaymentTransaction buildPaymentTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException { checkValidAccount(); - return transactionSender.buildTransaction(account, publicAddress, amount, fee); + return transactionSender.buildPaymentTransaction(account, publicAddress, amount, fee); } @Override - public PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, + public PaymentTransaction buildPaymentTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { checkValidAccount(); - return transactionSender.buildTransaction(account, publicAddress, amount, fee, memo); + return transactionSender.buildPaymentTransaction(account, publicAddress, amount, fee, memo); } @Override diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java index 9ec7147f..4cfaf3f5 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java @@ -8,7 +8,7 @@ public class PaymentTransaction extends TransactionBase { private final BigDecimal amount; private final String memo; - public PaymentTransaction(kin.base.Transaction baseTransaction, String destination, + PaymentTransaction(kin.base.Transaction baseTransaction, String destination, BigDecimal amount, String memo) { super(baseTransaction); @@ -17,14 +17,23 @@ public PaymentTransaction(kin.base.Transaction baseTransaction, String destinati this.memo = memo; } + /** + * @return the destination of this transaction. + */ public String destination() { return destination; } + /** + * @return the amount of this transaction. + */ public BigDecimal amount() { return amount; } + /** + * @return the amount of this transaction. + */ public String memo() { return memo; } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java index ae0f62cb..746dc845 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/RawTransaction.java @@ -5,14 +5,21 @@ public class RawTransaction extends TransactionBase { - public RawTransaction(kin.base.Transaction baseTransaction) { + RawTransaction(kin.base.Transaction baseTransaction) { super(baseTransaction); } + /** + * return the memo of this transaction. + * memo is an optional parameter the contain extra information + */ public Memo memo() { return baseTransaction().getMemo(); } + /** + * return the list of operation in this transaction. + */ public Operation[] operations() { return baseTransaction().getOperations(); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java index db7b3c81..1d004577 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java @@ -16,7 +16,7 @@ public abstract class TransactionBase { private final Transaction baseTransaction; - public TransactionBase(kin.base.Transaction baseTransaction) { + TransactionBase(kin.base.Transaction baseTransaction) { this.baseTransaction = baseTransaction; } @@ -29,14 +29,20 @@ public static TransactionBase decodeTransaction(String transactionEnvelope) thro try { kin.base.Transaction transaction = kin.base.Transaction.fromEnvelopeXdr(transactionEnvelope); Operation[] operations = transaction.getOperations(); + int paymentOperationCount = 0; + PaymentOperation paymentOperation = null; for (Operation operation : operations) { if (operation instanceof PaymentOperation) { - PaymentOperation paymentOperation = (PaymentOperation) operation; - return new PaymentTransaction(transaction, paymentOperation.getDestination().getAccountId(), - new BigDecimal(paymentOperation.getAmount()), ((MemoText) transaction.getMemo()).getText()); + paymentOperationCount ++; + paymentOperation = (PaymentOperation) operation; } } - return new RawTransaction(transaction); + if (paymentOperationCount == 1) { + return new PaymentTransaction(transaction, paymentOperation.getDestination().getAccountId(), + new BigDecimal(paymentOperation.getAmount()), ((MemoText) transaction.getMemo()).getText()); + } else { + return new RawTransaction(transaction); + } } catch (IOException e) { throw new DecodeTransactionException(e.getMessage(), e.getCause()); } @@ -74,21 +80,30 @@ public TransactionId id() { return new TransactionIdImpl(Utils.byteArrayToHex(baseTransaction.hash())); } + /** + * @return the sequence number of this transaction. + */ public long sequenceNumber() { return baseTransaction.getSequenceNumber(); } + /** + * @return the source account public address + */ public String source() { return baseTransaction.getSourceAccount().getAccountId(); } /** - * @return TimeBounds, or null (representing no time restrictions) + * @return the time bounds of this transaction, or null (representing no time restrictions) */ public TimeBounds timeBounds() { return baseTransaction.getTimeBounds(); } + /** + * @return the list of signatures + */ public List signatures() { return baseTransaction.getSignatures(); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java index cd30dbe3..ef17410e 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionSender.java @@ -36,12 +36,12 @@ class TransactionSender { this.appId = appId; } - PaymentTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, + PaymentTransaction buildPaymentTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException { - return buildTransaction(from, publicAddress, amount, fee, null); + return buildPaymentTransaction(from, publicAddress, amount, fee, null); } - PaymentTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, + PaymentTransaction buildPaymentTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { checkParams(from, publicAddress, amount, fee, memo); memo = Utils.addAppIdToMemo(appId, memo); diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java index 2acfd19e..84233cb2 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java @@ -60,7 +60,7 @@ public void sendTransactionSync() throws Exception { when(mockTransactionSender.sendTransaction((RawTransaction) any())).thenReturn(expectedTransactionId); - PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100); + PaymentTransaction transaction = kinAccount.buildPaymentTransactionSync(expectedAccountId, expectedAmount, 100); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); verify(mockTransactionSender).sendTransaction(transaction); @@ -78,7 +78,7 @@ public void sendTransactionSync_WithMemo() throws Exception { when(mockTransactionSender.sendTransaction((RawTransaction) any())).thenReturn(expectedTransactionId); - PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100, memo); + PaymentTransaction transaction = kinAccount.buildPaymentTransactionSync(expectedAccountId, expectedAmount, 100, memo); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); verify(mockTransactionSender).sendTransaction(transaction); @@ -172,7 +172,7 @@ public void sendTransactionSync_DeletedAccount_Exception() throws Exception { kinAccount.markAsDeleted(); PaymentTransaction transaction = kinAccount - .buildTransactionSync("GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX", new BigDecimal("12.2"), + .buildPaymentTransactionSync("GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX", new BigDecimal("12.2"), 100); kinAccount.sendTransactionSync(transaction); } diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java index cbb09286..f38cddab 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java @@ -100,7 +100,7 @@ public void sendTransaction_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); TransactionId transactionId = transactionSender.sendTransaction(transaction); assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); @@ -156,7 +156,7 @@ public void sendTransaction_WithMemo_success() throws Exception { String fakeMemo = "fake memo"; PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, fakeMemo); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, fakeMemo); TransactionId transactionId = transactionSender.sendTransaction(transaction); assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); @@ -176,7 +176,7 @@ public void sendTransaction_ToAccountNotExist() throws Exception { expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_TO))); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); transactionSender.sendTransaction(transaction); } @@ -196,7 +196,7 @@ public void sendTransaction_Http_307_Response_Success() throws Exception { mockWebServerHttp307.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); TransactionId transactionId = transactionSender.sendTransaction(transaction); assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); @@ -216,7 +216,7 @@ public void sendTransaction_FromAccountNotExist() throws Exception { expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_FROM))); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); transactionSender.sendTransaction(transaction); } @@ -235,7 +235,7 @@ public void sendTransaction_GeneralStellarError() throws Exception { expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", hasSize(1))); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -252,7 +252,7 @@ public void sendTransaction_Underfunded_InsufficientKinException() throws Except expectedEx.expect(InsufficientKinException.class); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -268,7 +268,7 @@ public void sendTransaction_InsufficientFeeException() throws Exception { expectedEx.expect(InsufficientFeeException.class); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -284,7 +284,7 @@ public void sendTransaction_InsufficientBalanceException() throws Exception { expectedEx.expect(InsufficientKinException.class); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -313,7 +313,7 @@ public void sendTransaction_FirstQuery_ConnectionException() throws Exception { expectedEx.expectCause(isA(IOException.class)); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -325,7 +325,7 @@ public void sendTransaction_SecondQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -338,7 +338,7 @@ public void sendTransaction_ThirdQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -355,7 +355,7 @@ public void sendTransaction_changeTimeOut() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(SocketTimeoutException.class)); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -367,7 +367,7 @@ public void sendTransaction_FirstQuery_NullResponse() throws Exception { expectedEx.expectMessage(ACCOUNT_ID_FROM); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -380,7 +380,7 @@ public void sendTransaction_SecondQuery_NullResponse() throws Exception { expectedEx.expectMessage(ACCOUNT_ID_TO); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -394,7 +394,7 @@ public void sendTransaction_ThirdQuery_NullResponse() throws Exception { expectedEx.expectMessage("transaction"); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -405,7 +405,7 @@ public void sendTransaction_TooLongMemo() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Memo cannot be longer that 21 bytes(UTF-8 characters)"); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, tooLongMemo); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, tooLongMemo); transactionSender.sendTransaction(transaction); assertThat(mockWebServer.getRequestCount(), equalTo(0)); } @@ -415,7 +415,7 @@ public void sendTransaction_TooLongMemo() throws Exception { public void sendTransaction_NullAccount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("account"); - PaymentTransaction transaction = transactionSender.buildTransaction(null, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(null, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -424,7 +424,7 @@ public void sendTransaction_NullAccount() throws Exception { public void sendTransaction_NullPublicAddress() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("public address"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, null, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, null, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -433,7 +433,7 @@ public void sendTransaction_NullPublicAddress() throws Exception { public void sendTransaction_NullAmount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("amount"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, null, FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, null, FEE); transactionSender.sendTransaction(transaction); } @@ -441,7 +441,7 @@ public void sendTransaction_NullAmount() throws Exception { public void sendTransaction_EmptyPublicAddress() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("public address"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, "", new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, "", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -450,7 +450,7 @@ public void sendTransaction_NegativeAmount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Amount"); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("-200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("-200"), FEE); transactionSender.sendTransaction(transaction); } @@ -459,7 +459,7 @@ public void sendTransaction_NegativeFee() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Fee"); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), -FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), -FEE); transactionSender.sendTransaction(transaction); } @@ -468,7 +468,7 @@ public void sendTransaction_AmountExceedNumOfDecimalPlaces() throws Exception { expectedEx.expect(IllegalAmountException.class); expectedEx.expectMessage("amount can't have more then 5 digits after the decimal point"); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("20.012345"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("20.012345"), FEE); transactionSender.sendTransaction(transaction); } @@ -478,7 +478,7 @@ public void sendTransaction_InvalidPublicIdLength() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(FormatException.class)); expectedEx.expectMessage("public address"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, "ABCDEF", new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, "ABCDEF", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -489,7 +489,7 @@ public void sendTransaction_InvalidChecksumPublicId() throws Exception { expectedEx.expectCause(isA(FormatException.class)); expectedEx.expectMessage("public address"); PaymentTransaction transaction = transactionSender - .buildTransaction(account, "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OG3", + .buildPaymentTransaction(account, "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OG3", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -498,7 +498,7 @@ public void sendTransaction_InvalidChecksumPublicId() throws Exception { private void testHttpResponseCode(int resCode) { try { PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); fail("Expected OperationFailedException"); } catch (Exception ex) { diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java index 3ccd526a..77ee3105 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionTest.java @@ -8,10 +8,12 @@ import java.io.IOException; import kin.base.Account; +import kin.base.AssetTypeNative; import kin.base.CreateAccountOperation; import kin.base.KeyPair; import kin.base.MemoText; import kin.base.Network; +import kin.base.PaymentOperation; import okhttp3.mockwebserver.MockWebServer; import org.junit.Before; import org.junit.Rule; @@ -43,15 +45,10 @@ public void setup() throws Exception { private RawTransaction createTransaction() { KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); - RawTransaction transaction = buildTransaction(source, 100, "fake memo"); - - KinAccountImpl mockKinAccountImpl = mock(KinAccountImpl.class); - when(mockKinAccountImpl.getKeyPair()).thenReturn(source); - - return transaction; + return buildRawTransaction(source, 100, "fake memo"); } - private RawTransaction buildTransaction(KeyPair source, int fee, String memo) { + private RawTransaction buildRawTransaction(KeyPair source, int fee, String memo) { KeyPair destination = KeyPair.fromAccountId(ACCOUNT_ID_FROM); long sequenceNumber = 2908908335136768L; Account account = new Account(source, sequenceNumber); @@ -92,6 +89,49 @@ public void decodeTransaction_success() throws Exception { } + @Test + public void decodeTransaction_OnePaymentOperation_getPaymentTransaction() throws Exception { + KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); + KeyPair destination = KeyPair.fromAccountId(ACCOUNT_ID_FROM); + long sequenceNumber = 2908908335136768L; + Account account = new Account(source, sequenceNumber); + TransactionBase transactionBase = new TransactionBuilder(source, account, "test") + .addOperation(new PaymentOperation.Builder(destination, new AssetTypeNative(), "1000").build()) + .setFee(100) + .setMemo("fake memo") + .build(); + + TransactionBase decodedTransaction = TransactionBase.decodeTransaction(transactionBase.transactionEnvelope()); + assertThat(decodedTransaction, instanceOf(PaymentTransaction.class)); + } + + @Test + public void decodeTransaction_moreThenOnePaymentOperation_getRawTransaction() throws Exception { + KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); + KeyPair destination = KeyPair.fromAccountId(ACCOUNT_ID_FROM); + long sequenceNumber = 2908908335136768L; + Account account = new Account(source, sequenceNumber); + TransactionBase transactionBase = new TransactionBuilder(source, account, "test") + .addOperation(new PaymentOperation.Builder(destination, new AssetTypeNative(), "1000").build()) + .addOperation(new PaymentOperation.Builder(destination, new AssetTypeNative(), "2000").build()) + .setFee(100) + .setMemo("fake memo") + .build(); + + TransactionBase decodedTransaction = TransactionBase.decodeRawTransaction(transactionBase.transactionEnvelope()); + assertThat(decodedTransaction, instanceOf(RawTransaction.class)); + } + + @Test + public void decodeTransaction_noPaymentOperation_getRawTransaction() throws Exception { + String transactionEnvelope = "AAAAANNVIxukFMnDJ7x37MKNLh3O3WzyD2d6eId2zqiXC1icAAAAZAAKVaMAAAABAAAAAAAAAAEAAAAQMS10ZXN0LWZha2UgbWVtbwAAAAEAAAAAAAAAAAAAAADTVSMbpBTJwye8d+zCjS4dzt1s8g9neniHds6olwtYnAAAAAAL68IAAAAAAAAAAAGXC1icAAAAQFUTzwPlsEzibdVe1wjk9Bcz4TOsvY8FAc66aCfusHBEUa7vDyxwiV/ia79PWqhr+0vlXmkMI4xS14JVYcQ3Dwg="; + RawTransaction transaction = TransactionBase.decodeRawTransaction(transactionEnvelope); + + assertThat(transaction, instanceOf(RawTransaction.class)); + assertThat(transactionEnvelope, equalTo(transaction.transactionEnvelope())); + + } + @Test public void addSignature_success() { RawTransaction transaction = createTransaction(); @@ -120,7 +160,7 @@ public void getTransactionBuilder_memoNotValid_IllegalArgumentException() { expectedEx.expectMessage("Memo cannot be longer that 21 bytes(UTF-8 characters)"); KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); - buildTransaction(source, 100, "memo is not valid because it is too long"); + buildRawTransaction(source, 100, "memo is not valid because it is too long"); } @Test @@ -129,7 +169,7 @@ public void getTransactionBuilder_feeIsNotValid_IllegalArgumentException() { expectedEx.expectMessage("Fee can't be negative"); KeyPair source = KeyPair.fromSecretSeed(SECRET_SEED_FROM); - buildTransaction(source,-10, "fake memo"); + buildRawTransaction(source,-10, "fake memo"); } @Test diff --git a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java index 72fe8b5d..6347faa8 100644 --- a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java +++ b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java @@ -37,7 +37,7 @@ public static Intent getIntent(Context context) { private EditText toAddressInput, amountInput, feeInput, memoInput; private SwitchCompat switchButton; private Request gertMinimumFeeRequest; - private Request buildTransactionRequest; + private Request buildPaymentTransactionRequest; private Request sendTransactionRequest; private WhitelistService whitelistService; @@ -60,8 +60,8 @@ protected void onDestroy() { if (gertMinimumFeeRequest != null) { gertMinimumFeeRequest.cancel(false); } - if (buildTransactionRequest != null) { - buildTransactionRequest.cancel(false); + if (buildPaymentTransactionRequest != null) { + buildPaymentTransactionRequest.cancel(false); } if (sendTransactionRequest != null) { sendTransactionRequest.cancel(false); @@ -232,20 +232,20 @@ private void handleSendTransaction(String toAddress, BigDecimal amount, int fee, progressBar.setVisibility(View.VISIBLE); KinAccount account = getKinClient().getAccount(0); if (account != null) { - buildTransaction(toAddress, amount, fee, memo, account); + buildPaymentTransaction(toAddress, amount, fee, memo, account); } else { progressBar.setVisibility(View.GONE); throw new AccountDeletedException(); } } - private void buildTransaction(String toAddress, BigDecimal amount, int fee, String memo, KinAccount account) { + private void buildPaymentTransaction(String toAddress, BigDecimal amount, int fee, String memo, KinAccount account) { if (memo == null) { - buildTransactionRequest = account.buildTransaction(toAddress, amount, fee); + buildPaymentTransactionRequest = account.buildPaymentTransaction(toAddress, amount, fee); } else { - buildTransactionRequest = account.buildTransaction(toAddress, amount, fee, memo); + buildPaymentTransactionRequest = account.buildPaymentTransaction(toAddress, amount, fee, memo); } - buildTransactionRequest.run(new BuildTransactionCallback()); + buildPaymentTransactionRequest.run(new BuildPaymentTransactionCallback()); } private void handleWhitelistTransaction(PaymentTransaction transaction) { @@ -276,11 +276,11 @@ public void displayResult(Context context, View view, TransactionId transactionI } } - private class BuildTransactionCallback implements ResultCallback { + private class BuildPaymentTransactionCallback implements ResultCallback { @Override public void onResult(PaymentTransaction transaction) { - Log.d(TAG, "buildTransaction: build transaction " + transaction.id().id() + " succeeded"); + Log.d(TAG, "buildPaymentTransaction: build transaction " + transaction.id().id() + " succeeded"); // This is just to differentiate between whitelist transaction and regular transaction if (switchButton.isChecked()) { @@ -295,7 +295,7 @@ public void onResult(PaymentTransaction transaction) { @Override public void onError(Exception e) { - Utils.logError(e, "buildTransaction"); + Utils.logError(e, "buildPaymentTransaction"); KinAlertDialog.createErrorDialog(TransactionActivity.this, e.getMessage()).show(); } } From e5cd9f3c77fed84d2096f9e2f1f5585a93ea94f9 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Wed, 12 Jun 2019 11:26:59 +0300 Subject: [PATCH 34/56] 1. change the base unit to quark in the docs --- kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java index 1d004577..75c2292c 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java @@ -67,7 +67,7 @@ kin.base.Transaction baseTransaction() { } /** - * Returns fee paid for transaction in kin base unit (1 base unit = 0.00001 KIN). + * Returns fee paid for transaction in kin base unit which is quark (1 quark = 0.00001 KIN). */ public int fee() { return baseTransaction.getFee(); From 9cdb1e4faed8ede7de6cc5e6e1d933b4cdf81883 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 13 Jun 2019 09:10:52 +0300 Subject: [PATCH 35/56] 1. format the class --- .../kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java index 4cfaf3f5..73b9ba9b 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentTransaction.java @@ -8,9 +8,7 @@ public class PaymentTransaction extends TransactionBase { private final BigDecimal amount; private final String memo; - PaymentTransaction(kin.base.Transaction baseTransaction, String destination, - BigDecimal amount, String memo) { - + PaymentTransaction(kin.base.Transaction baseTransaction, String destination, BigDecimal amount, String memo) { super(baseTransaction); this.destination = destination; this.amount = amount; From 1fc216d466335b03c331f28f721608887a9cc265 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 13 Jun 2019 09:38:15 +0300 Subject: [PATCH 36/56] 1. Change the code style settings --- .idea/codeStyleSettings.xml | 1125 ++++++++--------- .../main/java/kin/sdk/TransactionBase.java | 9 +- 2 files changed, 541 insertions(+), 593 deletions(-) diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml index 9f7f0b59..cb2ff321 100644 --- a/.idea/codeStyleSettings.xml +++ b/.idea/codeStyleSettings.xml @@ -1,592 +1,539 @@ - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .*:.*Style + + http://schemas.android.com/apk/res/android + + + BY_NAME + +
+
+ + + + .*:layout_width + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:layout_height + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:layout_weight + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:layout_margin + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:layout_marginTop + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:layout_marginBottom + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:layout_marginStart + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:layout_marginEnd + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:layout_marginLeft + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:layout_marginRight + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:layout_.* + + http://schemas.android.com/apk/res/android + + + BY_NAME + +
+
+ + + + .*:padding + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:paddingTop + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:paddingBottom + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:paddingStart + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:paddingEnd + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:paddingLeft + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:paddingRight + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .* + http://schemas.android.com/apk/res/android + + + BY_NAME + +
+
+ + + + .* + http://schemas.android.com/apk/res-auto + + + BY_NAME + +
+
+ + + + .* + http://schemas.android.com/tools + + + BY_NAME + +
+
+ + + + .* + .* + + + BY_NAME + +
+
+
+
+ + + \ No newline at end of file diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java index 75c2292c..3823b405 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionBase.java @@ -21,7 +21,8 @@ public abstract class TransactionBase { } /** - * Creates a RawTransaction or a PaymentTransaction instance from previously build TransactionEnvelope + * Creates a RawTransaction or a PaymentTransaction instance from previously build + * TransactionEnvelope * * @param transactionEnvelope a Base-64 encoded TransactionEnvelope */ @@ -33,7 +34,7 @@ public static TransactionBase decodeTransaction(String transactionEnvelope) thro PaymentOperation paymentOperation = null; for (Operation operation : operations) { if (operation instanceof PaymentOperation) { - paymentOperationCount ++; + paymentOperationCount++; paymentOperation = (PaymentOperation) operation; } } @@ -109,8 +110,7 @@ public List signatures() { } /** - * Returns base64-encoded TransactionEnvelope object. - * Transaction need to have at least one signature. + * Returns base64-encoded TransactionEnvelope object. Transaction need to have at least one signature. */ public String transactionEnvelope() { return baseTransaction.toEnvelopeXdrBase64(); @@ -126,6 +126,7 @@ public WhitelistableTransaction whitelistableTransaction() { /** * Adds a new signature to this transaction. + * * @param account {@link KinAccount} object which is the account who we add his signature. */ public void addSignature(KinAccount account) { From 0d98639695d0a6765c3ba8a5ece525ee6e5dbaba Mon Sep 17 00:00:00 2001 From: Amitai Efrati <42765949+drEfrati@users.noreply.github.com> Date: Thu, 13 Jun 2019 12:26:38 +0300 Subject: [PATCH 37/56] Update README.md Added all the new API methods --- kin-sdk/README.md | 155 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 3 deletions(-) diff --git a/kin-sdk/README.md b/kin-sdk/README.md index 329fb5a7..7a9a4c31 100644 --- a/kin-sdk/README.md +++ b/kin-sdk/README.md @@ -177,14 +177,99 @@ balanceRequest.run(new ResultCallback() { } @Override - public void onError(Exception e) { - e.printStackTrace(); - } + public void onError(Exception e) { + e.printStackTrace(); + } }); ``` By using `result.value(2)` in the example above we print the balance with a precision of 2 decimal points. This is a required parameter of `value()`. + +#### Retrieving Account Data + +The account data contains all the properties of an account. + +###### Snippet: Get Kin account data + +```java +Request accountDataRequest = account.getAccountData(); +accountDataRequest.run(new ResultCallback() { + + @Override + public void onResult(AccountData result) { + // Those are just examples of some of the properties that the AccountData has. + Log.d("example", "account address = " + account.publicAddress()); + Log.d("example", "account sequence number = " + account.sequenceNumber()); + Log.d("example", "account data = " + account.data()); + } + + @Override + public void onError(Exception e) { + e.printStackTrace(); + } +}); +``` + +### Linked Accounts + +Linked Accounts describes a pair of accounts in which one of the accounts(called the master account) can sign for another. +The SDK presents methods which a developer can use to link an account(s) with a master account provided by another app. +For example, a master account can be a wallet app, such as Kinit. +Once linked, the balance of all accounts controlled by the master account becomes accessible. +You can also retrieve the list of all controlled accounts. + +#### Retrieving Aggregated Balance + +The aggregated balance is the sum of balances of all accounts controlled by the master account. +This includes the balance of the master account. + +###### Snippet: Get Kin account Aggregated Balance + +```java +Request aggregatedBalanceRequest = account.getAggregatedBalance(); +aggregatedBalanceRequest.run(new ResultCallback() { + + @Override + public void onResult(Balance result) { + Log.d("example", "The balance is: " + result.value(2)); + } + + @Override + public void onError(Exception e) { + e.printStackTrace(); + } +}); +``` + +#### Retrieving the list of all controlled accounts + +It is a list of all accounts controlled by the master account. +It can be used, for example, for management of linked accounts by the app which controls the master account. +Note that The master account will be included in the list. +When the given account is not a master account, an empty list we be returned. + +###### Snippet: Get the list of all controlled accounts + +```java +Request> controlledAccountRequest = account.getControlledAccounts(); +controlledAccountRequest.run(new ResultCallback>() { + + @Override + public void onResult(List controlledAccounts) { + for (ControlledAccount account : controlledAccounts) { + Log.d("example", "account address = " + account.getPublicAddress() + + ", account balance = " + account.getBalance()); + } + } + + @Override + public void onError(Exception e) { + e.printStackTrace(); + } +}); +``` + ### Transactions Transactions are executed on the Kin blockchain in a two-step process. @@ -192,6 +277,10 @@ Transactions are executed on the Kin blockchain in a two-step process. - **Build** the transaction, including calculation of the transaction hash. The hash is used as a transaction ID and is necessary to query the status of the transaction. - **Send** the transaction to servers for execution on the blockchain. +Currently we have 2 type of transactions, the one that was used until now which is called `PaymentTransaction` (previously called `Transaction`) which is used to send a transaction to transafer kin to another account. +The second type is `RawTransaction` which can be any kind of transaction. +Both of them inherit from `TransactionBase` + Snippets [Transfer Kin](#snippet-transfer-kin) and [Whitelist service](#snippet-whitelist-service) illustrate this two-step process. #### Transaction fees @@ -316,6 +405,66 @@ buildPaymentTransactionRequest.run(new ResultCallback() { }); ``` + +#### Building a customize transaction + +You can create and build your own customize transaction(RawTransaction). + +###### Snippet: example of build and send customize transaction which will be used to link an account to another account. you can look at [Link Account](#linked-accounts) for more information. + +```java + +Request transactionBuilderRequest = controlledAccount.getTransactionBuilder(); +transactionBuilderRequest.run(new ResultCallback() { + + @Override + public void onResult(TransactionBuilder transactionBuilder) { + // Get a signerKey from the master account, this is necessary in order to later add the + // master account as a signer in the controlled account + SignerKey signerKey = Signer.ed25519PublicKey(KeyPair.fromAccountId(masterAccount.publicAddress)); + // getting a transactionBuilder from the controlled account + RawTransaction linkingTransaction = transactionBuilder + .setFee(100) + .setMemo("account linking") + // We use SetOptionsOperation to add the master + // account as a signer for the controlled account. + .addOperation(new SetOptionsOperation.Builder().setSigner(signerKey, 1).build()) + // We use ManageDataOperation to add the controlled account and its app name to + // the data of the master account. This is optional because it is only meta data. + .addOperation(new ManageDataOperation.Builder(controlledAccount.getPublicAddress(), + "our app id".getBytes(StandardCharsets.UTF_8.name()) + .setSourceAccount(KeyPair.fromAccountId(masterAccount.publicAddress)) + .build()) + .build(); + // We simulate that the master account is the one who send this transaction on behalf + // of the controlled account. For that we need to add the master account signature to the transaction + linkingTransaction.addSignature(masterAccount); + + // Sending the transaction + Request sendTransactionRequest = masterAccount.sendTransaction(linkingTransaction); + sendTransactionRequest.run(new ResultCallback() { + + @Override + public void onResult(TransactionId id) { + Log.d("example", "The transaction id: " + id); + } + + @Override + public void onError(Exception e) { + e.printStackTrace(); + } + }); + } + + @Override + public void onError(Exception e) { + e.printStackTrace(); + } +}); + +``` + + #### Transferring Kin to another account using whitelist service The flow is very similar to [Transfer Kin](#snippet-transfer-kin) but adds a step in which you: From 33d266b1a0ce261ac56e90d528dd503585f7484c Mon Sep 17 00:00:00 2001 From: sbeskin <46258570+sbeskin@users.noreply.github.com> Date: Thu, 13 Jun 2019 13:52:14 +0300 Subject: [PATCH 38/56] Edited by Sasha --- kin-sdk/README.md | 53 ++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/kin-sdk/README.md b/kin-sdk/README.md index 7a9a4c31..511c6c96 100644 --- a/kin-sdk/README.md +++ b/kin-sdk/README.md @@ -59,7 +59,7 @@ Adding Kin features to your Android client requires three steps. - Managing Kin accounts - Executing transactions against Kin accounts. -### Accessing the Kin blockchain +### Accessing the Kin Blockchain Android apps that allow users to earn, spend, and manage Kin are considered clients in the Kin architecture. The following statement creates `kinClient` which includes methods to manage accounts on the Kin blockchain. @@ -77,9 +77,9 @@ Each environment variable includes: `1acd` in the example is an `appId`, a 4(or 3)-character string which will be added automatically to each transaction to identify your application. `appId` must contain only digits and upper and/or lower case letters. String length must be 3 or 4. `appID` is automatically added to transaction memos. -### Managing accounts +### Managing Accounts -#### Creating and retrieving a Kin account +#### Creating and Retrieving a Kin Account The first time you use `KinClient` you need to create a new Kin wallet and an associated Kin account. The Kin wallet is stored on the user's client device and holds a public/private keypair. The private key remains securely stored in the local wallet while the public key will become the address of the Kin account added to the Kin blockchain. @@ -188,7 +188,7 @@ By using `result.value(2)` in the example above we print the balance with a prec #### Retrieving Account Data -The account data contains all the properties of an account. +The account data contains all the properties of the account. ###### Snippet: Get Kin account data @@ -213,18 +213,18 @@ accountDataRequest.run(new ResultCallback() { ### Linked Accounts -Linked Accounts describes a pair of accounts in which one of the accounts(called the master account) can sign for another. -The SDK presents methods which a developer can use to link an account(s) with a master account provided by another app. -For example, a master account can be a wallet app, such as Kinit. -Once linked, the balance of all accounts controlled by the master account becomes accessible. +Linked accounts are a pair of accounts, in which one of them (called the master account) can sign for the other. +The SDK provides methods that a developer can use to link an account(s) to a master account provided by another app. +For example, a master account can be in a wallet app, such as Kinit. +Once linked, the balances of all accounts controlled by the master account become accessible through the wallet app. You can also retrieve the list of all controlled accounts. #### Retrieving Aggregated Balance -The aggregated balance is the sum of balances of all accounts controlled by the master account. +The aggregated balance is the sum of the balances of all accounts controlled by the master account. This includes the balance of the master account. -###### Snippet: Get Kin account Aggregated Balance +###### Snippet: Get Kin Accounts' Aggregated Balance ```java Request aggregatedBalanceRequest = account.getAggregatedBalance(); @@ -242,11 +242,11 @@ aggregatedBalanceRequest.run(new ResultCallback() { }); ``` -#### Retrieving the list of all controlled accounts +#### Retrieving the List of All Controlled Accounts It is a list of all accounts controlled by the master account. -It can be used, for example, for management of linked accounts by the app which controls the master account. -Note that The master account will be included in the list. +It can be used, for example, for management of linked accounts by the app that controls the master account. +Note that the master account will be included in the list. When the given account is not a master account, an empty list we be returned. ###### Snippet: Get the list of all controlled accounts @@ -272,25 +272,26 @@ controlledAccountRequest.run(new ResultCallback>() { ### Transactions -Transactions are executed on the Kin blockchain in a two-step process. +Currently, we have 2 types of transactions: +- `PaymentTransaction` (previously called `Transaction`) - used for sending a transaction of transferring Kin to another account +- `RawTransaction` - can be used any kind of transaction +Both types inherit from `TransactionBase`. + +Transactions are executed on the Kin blockchain in a two-step process: - **Build** the transaction, including calculation of the transaction hash. The hash is used as a transaction ID and is necessary to query the status of the transaction. - **Send** the transaction to servers for execution on the blockchain. -Currently we have 2 type of transactions, the one that was used until now which is called `PaymentTransaction` (previously called `Transaction`) which is used to send a transaction to transafer kin to another account. -The second type is `RawTransaction` which can be any kind of transaction. -Both of them inherit from `TransactionBase` - Snippets [Transfer Kin](#snippet-transfer-kin) and [Whitelist service](#snippet-whitelist-service) illustrate this two-step process. -#### Transaction fees +#### Transaction Fees It is important to note that by default all transactions on the Kin blockchain are charged a fee. Fee for individual transactions are trivial (1 Kin = 10E5 Fee). Some apps can be added to the Kin whitelist, a set of pre-approved apps whose users will not be charged Fee to execute transactions. If your app is in the whitelist then refer to [transferring Kin to another account using whitelist service](#transferring-kin-to-another-account-using-whitelist-service). Whitelisting a transaction is a function provided by the Python SDK and should be implemented by developers as a back-end service. Developers (you) are responsible of creating and maintaining their back-end services. -#### Transferring Kin to another account +#### Transferring Kin to Another Account To transfer Kin to another account, you need the public address of the account to which you want to transfer Kin. @@ -406,11 +407,11 @@ buildPaymentTransactionRequest.run(new ResultCallback() { ``` -#### Building a customize transaction +#### Building a Customized Transaction -You can create and build your own customize transaction(RawTransaction). +You can create and build your own customized transaction (RawTransaction). -###### Snippet: example of build and send customize transaction which will be used to link an account to another account. you can look at [Link Account](#linked-accounts) for more information. +###### Snippet: Example of building and sending a customized transaction for linking an account to another account. For more information, see [Link Account](#linked-accounts) . ```java @@ -419,10 +420,10 @@ transactionBuilderRequest.run(new ResultCallback() { @Override public void onResult(TransactionBuilder transactionBuilder) { - // Get a signerKey from the master account, this is necessary in order to later add the - // master account as a signer in the controlled account + // Get a signerKey from the master account. This is necessary for adding the + // master account as a signer for the controlled account later on. SignerKey signerKey = Signer.ed25519PublicKey(KeyPair.fromAccountId(masterAccount.publicAddress)); - // getting a transactionBuilder from the controlled account + // Getting a transactionBuilder from the controlled account RawTransaction linkingTransaction = transactionBuilder .setFee(100) .setMemo("account linking") From d4c7eafb18fde01825a6fbf95578c353f43df201 Mon Sep 17 00:00:00 2001 From: sbeskin <46258570+sbeskin@users.noreply.github.com> Date: Thu, 13 Jun 2019 13:54:11 +0300 Subject: [PATCH 39/56] Edited by Sasha 2 --- kin-sdk/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kin-sdk/README.md b/kin-sdk/README.md index 511c6c96..5513f166 100644 --- a/kin-sdk/README.md +++ b/kin-sdk/README.md @@ -247,7 +247,7 @@ aggregatedBalanceRequest.run(new ResultCallback() { It is a list of all accounts controlled by the master account. It can be used, for example, for management of linked accounts by the app that controls the master account. Note that the master account will be included in the list. -When the given account is not a master account, an empty list we be returned. +When the given account is not a master account, an empty list will be returned. ###### Snippet: Get the list of all controlled accounts From dacfedaf6655698466e10b0a7e6533ef2d7d1d63 Mon Sep 17 00:00:00 2001 From: Amitai Efrati <42765949+drEfrati@users.noreply.github.com> Date: Thu, 13 Jun 2019 14:52:07 +0300 Subject: [PATCH 40/56] Update README.md --- kin-sdk/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kin-sdk/README.md b/kin-sdk/README.md index 5513f166..1879f5c6 100644 --- a/kin-sdk/README.md +++ b/kin-sdk/README.md @@ -409,7 +409,7 @@ buildPaymentTransactionRequest.run(new ResultCallback() { #### Building a Customized Transaction -You can create and build your own customized transaction (RawTransaction). +You can create and build your own customized transaction (`RawTransaction`). ###### Snippet: Example of building and sending a customized transaction for linking an account to another account. For more information, see [Link Account](#linked-accounts) . @@ -465,7 +465,6 @@ transactionBuilderRequest.run(new ResultCallback() { ``` - #### Transferring Kin to another account using whitelist service The flow is very similar to [Transfer Kin](#snippet-transfer-kin) but adds a step in which you: From 6edd97ed4679e26c1a25db4fa95e4f6d528735f8 Mon Sep 17 00:00:00 2001 From: Amitai Efrati <42765949+drEfrati@users.noreply.github.com> Date: Thu, 13 Jun 2019 14:55:31 +0300 Subject: [PATCH 41/56] Update README.md --- kin-sdk/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kin-sdk/README.md b/kin-sdk/README.md index 1879f5c6..dfcad054 100644 --- a/kin-sdk/README.md +++ b/kin-sdk/README.md @@ -411,7 +411,7 @@ buildPaymentTransactionRequest.run(new ResultCallback() { You can create and build your own customized transaction (`RawTransaction`). -###### Snippet: Example of building and sending a customized transaction for linking an account to another account. For more information, see [Link Account](#linked-accounts) . +###### Snippet: Example of building and sending a customized transaction for linking an account to another account. For more information, see [Link Account](#linked-accounts). Of course you can do many more different transactions. ```java From a8b1d1401b37ce72154c56a5a445a71310d38631 Mon Sep 17 00:00:00 2001 From: Amitai Efrati <42765949+drEfrati@users.noreply.github.com> Date: Thu, 13 Jun 2019 14:57:00 +0300 Subject: [PATCH 42/56] Update README.md --- kin-sdk/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kin-sdk/README.md b/kin-sdk/README.md index dfcad054..87bc42cf 100644 --- a/kin-sdk/README.md +++ b/kin-sdk/README.md @@ -411,8 +411,7 @@ buildPaymentTransactionRequest.run(new ResultCallback() { You can create and build your own customized transaction (`RawTransaction`). -###### Snippet: Example of building and sending a customized transaction for linking an account to another account. For more information, see [Link Account](#linked-accounts). Of course you can do many more different transactions. - +###### Snippet: One possible example of a customized transaction would be building and sending a transaction for linking an account to another account. For more information, see [Link Account](#linked-accounts). ```java Request transactionBuilderRequest = controlledAccount.getTransactionBuilder(); From 7bbe684225153a0cd89040ea73d149400a49dcce Mon Sep 17 00:00:00 2001 From: sbeskin <46258570+sbeskin@users.noreply.github.com> Date: Thu, 13 Jun 2019 15:04:22 +0300 Subject: [PATCH 43/56] Sasha --- kin-sdk/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kin-sdk/README.md b/kin-sdk/README.md index 87bc42cf..97990461 100644 --- a/kin-sdk/README.md +++ b/kin-sdk/README.md @@ -411,7 +411,7 @@ buildPaymentTransactionRequest.run(new ResultCallback() { You can create and build your own customized transaction (`RawTransaction`). -###### Snippet: One possible example of a customized transaction would be building and sending a transaction for linking an account to another account. For more information, see [Link Account](#linked-accounts). +###### Snippet: In the example below, a customized transaction is built and sent for linking an account to another account. For more information, see [Link Account](#linked-accounts). ```java Request transactionBuilderRequest = controlledAccount.getTransactionBuilder(); From cd63473da6360e44c187f7be30d088ee720505dd Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 13 Jun 2019 16:47:36 +0300 Subject: [PATCH 44/56] 1. Change the code style settings --- .idea/codeStyleSettings.xml | 72 ++++++------------- .../src/main/java/kin/sdk/AccountData.java | 25 +++---- 2 files changed, 34 insertions(+), 63 deletions(-) diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml index cb2ff321..bc3a764b 100644 --- a/.idea/codeStyleSettings.xml +++ b/.idea/codeStyleSettings.xml @@ -1,4 +1,4 @@ - +