From c9f226cf88c6e0027368fe4496d5dc1352c7f133 Mon Sep 17 00:00:00 2001 From: catbref Date: Sun, 2 Jun 2019 17:07:07 +0100 Subject: [PATCH] group approval tests and fixes --- src/main/java/org/qora/block/Block.java | 6 +- .../transaction/GroupApprovalTransaction.java | 10 +- .../org/qora/transaction/Transaction.java | 2 + .../org/qora/test/GroupApprovalTests.java | 101 ---------- .../java/org/qora/test/common/BlockUtils.java | 25 +++ .../java/org/qora/test/common/GroupUtils.java | 66 +++++++ .../org/qora/test/common/TestAccount.java | 2 + .../org/qora/test/forging/RewardTests.java | 14 +- .../qora/test/group/GroupApprovalTests.java | 184 ++++++++++++++++++ 9 files changed, 296 insertions(+), 114 deletions(-) delete mode 100644 src/test/java/org/qora/test/GroupApprovalTests.java create mode 100644 src/test/java/org/qora/test/common/BlockUtils.java create mode 100644 src/test/java/org/qora/test/common/GroupUtils.java create mode 100644 src/test/java/org/qora/test/group/GroupApprovalTests.java diff --git a/src/main/java/org/qora/block/Block.java b/src/main/java/org/qora/block/Block.java index 7194dc69..cafa44c8 100644 --- a/src/main/java/org/qora/block/Block.java +++ b/src/main/java/org/qora/block/Block.java @@ -1090,7 +1090,7 @@ public class Block { } protected void processBlockRewards() throws DataException { - BigDecimal reward = getRewardAtHeight(this.blockData.getHeight()); + BigDecimal reward = Block.getRewardAtHeight(this.blockData.getHeight()); // No reward for our height? if (reward == null) @@ -1335,7 +1335,7 @@ public class Block { } protected void orphanBlockRewards() throws DataException { - BigDecimal reward = getRewardAtHeight(this.blockData.getHeight()); + BigDecimal reward = Block.getRewardAtHeight(this.blockData.getHeight()); // No reward for our height? if (reward == null) @@ -1397,7 +1397,7 @@ public class Block { atRepository.deleteATStates(this.blockData.getHeight()); } - protected BigDecimal getRewardAtHeight(int ourHeight) { + public static BigDecimal getRewardAtHeight(int ourHeight) { List rewardsByHeight = BlockChain.getInstance().getBlockRewardsByHeight(); // No rewards configured? diff --git a/src/main/java/org/qora/transaction/GroupApprovalTransaction.java b/src/main/java/org/qora/transaction/GroupApprovalTransaction.java index e3220d53..59ef97a9 100644 --- a/src/main/java/org/qora/transaction/GroupApprovalTransaction.java +++ b/src/main/java/org/qora/transaction/GroupApprovalTransaction.java @@ -68,9 +68,13 @@ public class GroupApprovalTransaction extends Transaction { if (pendingTransactionData == null) return ValidationResult.TRANSACTION_UNKNOWN; - // Check pending transaction is not already in a block - if (this.repository.getTransactionRepository().getHeightFromSignature(groupApprovalTransactionData.getPendingSignature()) != 0) - return ValidationResult.TRANSACTION_ALREADY_CONFIRMED; + // Check pending transaction is actually needs group approval + if (pendingTransactionData.getApprovalStatus() == ApprovalStatus.NOT_REQUIRED) + return ValidationResult.GROUP_APPROVAL_NOT_REQUIRED; + + // Check pending transaction is actually pending + if (pendingTransactionData.getApprovalStatus() != ApprovalStatus.PENDING) + return ValidationResult.GROUP_APPROVAL_DECIDED; Account admin = getAdmin(); diff --git a/src/main/java/org/qora/transaction/Transaction.java b/src/main/java/org/qora/transaction/Transaction.java index 8e3f6b93..5962ebd9 100644 --- a/src/main/java/org/qora/transaction/Transaction.java +++ b/src/main/java/org/qora/transaction/Transaction.java @@ -230,6 +230,8 @@ public abstract class Transaction { INVALID_PUBLIC_KEY(79), AT_UNKNOWN(80), AT_ALREADY_EXISTS(81), + GROUP_APPROVAL_NOT_REQUIRED(82), + GROUP_APPROVAL_DECIDED(83), NOT_YET_RELEASED(1000); public final int value; diff --git a/src/test/java/org/qora/test/GroupApprovalTests.java b/src/test/java/org/qora/test/GroupApprovalTests.java deleted file mode 100644 index 5144d171..00000000 --- a/src/test/java/org/qora/test/GroupApprovalTests.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.qora.test; - -import org.junit.Test; -import org.qora.account.PrivateKeyAccount; -import org.qora.block.BlockChain; -import org.qora.block.BlockGenerator; -import org.qora.data.transaction.BaseTransactionData; -import org.qora.data.transaction.CreateGroupTransactionData; -import org.qora.data.transaction.PaymentTransactionData; -import org.qora.data.transaction.TransactionData; -import org.qora.group.Group; -import org.qora.group.Group.ApprovalThreshold; -import org.qora.repository.DataException; -import org.qora.repository.Repository; -import org.qora.repository.RepositoryManager; -import org.qora.test.common.Common; -import org.qora.transaction.CreateGroupTransaction; -import org.qora.transaction.PaymentTransaction; -import org.qora.transaction.Transaction; -import org.qora.transaction.Transaction.ValidationResult; - -import static org.junit.Assert.*; - -import java.math.BigDecimal; - -public class GroupApprovalTests extends Common { - - /** Check that a tx type that doesn't need approval doesn't accept txGroupId apart from NO_GROUP */ - @Test - public void testNonApprovalTxGroupId() throws DataException { - try (final Repository repository = RepositoryManager.getRepository()) { - BlockChain.validate(); - - TransactionData transactionData = buildPayment(repository, Group.NO_GROUP); - Transaction transaction = new PaymentTransaction(repository, transactionData); - assertEquals(ValidationResult.OK, transaction.isValidUnconfirmed()); - - int groupId = createGroup(repository); - - transactionData = buildPayment(repository, groupId); - transaction = new PaymentTransaction(repository, transactionData); - assertEquals(ValidationResult.INVALID_TX_GROUP_ID, transaction.isValidUnconfirmed()); - } - } - - private PaymentTransactionData buildPayment(Repository repository, int txGroupId) throws DataException { - long timestamp = System.currentTimeMillis() - 1000L; - byte[] reference = repository.getAccountRepository().getLastReference(v2testAddress); - byte[] senderPublicKey = v2testPublicKey; - String recipient = v2testAddress; - BigDecimal amount = BigDecimal.ONE.setScale(8); - BigDecimal fee = BigDecimal.ONE.setScale(8); - - BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, senderPublicKey, fee, null); - return new PaymentTransactionData(baseTransactionData, recipient, amount); - } - - private int createGroup(Repository repository) throws DataException { - long timestamp = System.currentTimeMillis() - 1000L; - int txGroupId = Group.NO_GROUP; - byte[] reference = repository.getAccountRepository().getLastReference(v2testAddress); - byte[] creatorPublicKey = v2testPublicKey; - String owner = v2testAddress; - String groupName = "test-group"; - String description = "test group description"; - boolean isOpen = false; - ApprovalThreshold approvalThreshold = ApprovalThreshold.ONE; - int minimumBlockDelay = 0; - int maximumBlockDelay = 1440; - BigDecimal fee = BigDecimal.ONE.setScale(8); - - BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, creatorPublicKey, fee, null); - TransactionData transactionData = new CreateGroupTransactionData(baseTransactionData, owner, groupName, description, - isOpen, approvalThreshold, minimumBlockDelay, maximumBlockDelay); - Transaction transaction = new CreateGroupTransaction(repository, transactionData); - - // Sign transaction - PrivateKeyAccount signer = new PrivateKeyAccount(repository, v2testPrivateKey); - transaction.sign(signer); - - // Add to unconfirmed - if (!transaction.isSignatureValid()) - throw new RuntimeException("CREATE_GROUP transaction's signature invalid"); - - ValidationResult result = transaction.isValidUnconfirmed(); - if (result != ValidationResult.OK) - throw new RuntimeException(String.format("CREATE_GROUP transaction invalid: %s", result.name())); - - repository.getTransactionRepository().save(transactionData); - repository.getTransactionRepository().unconfirmTransaction(transactionData); - repository.saveChanges(); - - // Generate block - BlockGenerator.generateTestingBlock(repository, signer); - - // Return assigned groupId - transactionData = repository.getTransactionRepository().fromSignature(transactionData.getSignature()); - return ((CreateGroupTransactionData) transactionData).getGroupId(); - } - -} diff --git a/src/test/java/org/qora/test/common/BlockUtils.java b/src/test/java/org/qora/test/common/BlockUtils.java new file mode 100644 index 00000000..2364f618 --- /dev/null +++ b/src/test/java/org/qora/test/common/BlockUtils.java @@ -0,0 +1,25 @@ +package org.qora.test.common; + +import java.math.BigDecimal; + +import org.qora.block.Block; +import org.qora.data.block.BlockData; +import org.qora.repository.DataException; +import org.qora.repository.Repository; + +public class BlockUtils { + + public static BigDecimal getNextBlockReward(Repository repository) throws DataException { + int currentHeight = repository.getBlockRepository().getBlockchainHeight(); + + return Block.getRewardAtHeight(currentHeight + 1); + } + + public static void orphanLastBlock(Repository repository) throws DataException { + BlockData blockData = repository.getBlockRepository().getLastBlock(); + Block block = new Block(repository, blockData); + block.orphan(); + repository.saveChanges(); + } + +} diff --git a/src/test/java/org/qora/test/common/GroupUtils.java b/src/test/java/org/qora/test/common/GroupUtils.java new file mode 100644 index 00000000..ab46d69f --- /dev/null +++ b/src/test/java/org/qora/test/common/GroupUtils.java @@ -0,0 +1,66 @@ +package org.qora.test.common; + +import java.math.BigDecimal; + +import org.qora.account.PrivateKeyAccount; +import org.qora.data.transaction.BaseTransactionData; +import org.qora.data.transaction.CreateGroupTransactionData; +import org.qora.data.transaction.GroupApprovalTransactionData; +import org.qora.data.transaction.JoinGroupTransactionData; +import org.qora.data.transaction.TransactionData; +import org.qora.group.Group; +import org.qora.group.Group.ApprovalThreshold; +import org.qora.repository.DataException; +import org.qora.repository.Repository; +import org.qora.transaction.Transaction.ApprovalStatus; + +public class GroupUtils { + + public static final int txGroupId = Group.NO_GROUP; + public static final BigDecimal fee = BigDecimal.ONE.setScale(8); + + public static int createGroup(Repository repository, String creatorAccountName, String groupName, boolean isOpen, ApprovalThreshold approvalThreshold, + int minimumBlockDelay, int maximumBlockDelay) throws DataException { + PrivateKeyAccount account = Common.getTestAccount(repository, creatorAccountName); + + byte[] reference = account.getLastReference(); + long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1; + String groupDescription = groupName + " (test group)"; + + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, account.getPublicKey(), GroupUtils.fee, null); + TransactionData transactionData = new CreateGroupTransactionData(baseTransactionData, account.getAddress(), groupName, groupDescription, isOpen, approvalThreshold, minimumBlockDelay, maximumBlockDelay); + + TransactionUtils.signAndForge(repository, transactionData, account); + + return repository.getGroupRepository().fromGroupName(groupName).getGroupId(); + } + + public static void joinGroup(Repository repository, String joinerAccountName, int groupId) throws DataException { + PrivateKeyAccount account = Common.getTestAccount(repository, joinerAccountName); + + byte[] reference = account.getLastReference(); + long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1; + + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, account.getPublicKey(), GroupUtils.fee, null); + TransactionData transactionData = new JoinGroupTransactionData(baseTransactionData, groupId); + + TransactionUtils.signAndForge(repository, transactionData, account); + } + + public static void approveTransaction(Repository repository, String accountName, byte[] pendingSignature, boolean decision) throws DataException { + PrivateKeyAccount account = Common.getTestAccount(repository, accountName); + + byte[] reference = account.getLastReference(); + long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1; + + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, account.getPublicKey(), GroupUtils.fee, null); + TransactionData transactionData = new GroupApprovalTransactionData(baseTransactionData, pendingSignature, decision); + + TransactionUtils.signAndForge(repository, transactionData, account); + } + + public static ApprovalStatus getApprovalStatus(Repository repository, byte[] signature) throws DataException { + return repository.getTransactionRepository().fromSignature(signature).getApprovalStatus(); + } + +} diff --git a/src/test/java/org/qora/test/common/TestAccount.java b/src/test/java/org/qora/test/common/TestAccount.java index 48269ce2..26a9d570 100644 --- a/src/test/java/org/qora/test/common/TestAccount.java +++ b/src/test/java/org/qora/test/common/TestAccount.java @@ -5,6 +5,7 @@ import org.qora.repository.Repository; import org.qora.utils.Base58; public class TestAccount extends PrivateKeyAccount { + public final String accountName; public TestAccount(Repository repository, String accountName, byte[] privateKey) { @@ -16,4 +17,5 @@ public class TestAccount extends PrivateKeyAccount { public TestAccount(Repository repository, String accountName, String privateKey) { this(repository, accountName, Base58.decode(privateKey)); } + } diff --git a/src/test/java/org/qora/test/forging/RewardTests.java b/src/test/java/org/qora/test/forging/RewardTests.java index c908496a..c9704307 100644 --- a/src/test/java/org/qora/test/forging/RewardTests.java +++ b/src/test/java/org/qora/test/forging/RewardTests.java @@ -17,6 +17,7 @@ import org.qora.repository.DataException; import org.qora.repository.Repository; import org.qora.repository.RepositoryManager; import org.qora.test.common.AccountUtils; +import org.qora.test.common.BlockUtils; import org.qora.test.common.Common; public class RewardTests extends Common { @@ -38,11 +39,11 @@ public class RewardTests extends Common { PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, "alice"); - BigDecimal firstReward = BlockChain.getInstance().getBlockRewardsByHeight().get(0).reward; + BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); BlockGenerator.generateTestingBlock(repository, forgingAccount); - BigDecimal expectedBalance = initialBalances.get("alice").get(Asset.QORA).add(firstReward); + BigDecimal expectedBalance = initialBalances.get("alice").get(Asset.QORA).add(blockReward); AccountUtils.assertBalance(repository, "alice", Asset.QORA, expectedBalance); } } @@ -84,16 +85,15 @@ public class RewardTests extends Common { PrivateKeyAccount proxyAccount = new PrivateKeyAccount(repository, proxyPrivateKey); Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORA); + BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); BlockGenerator.generateTestingBlock(repository, proxyAccount); - // We're expected reward * 12.8% to Bob, the rest to Alice - // (first reward is good for first 10 blocks) - BigDecimal firstReward = BlockChain.getInstance().getBlockRewardsByHeight().get(0).reward; + // We're expecting reward * 12.8% to Bob, the rest to Alice - BigDecimal bobShare = firstReward.multiply(share.movePointLeft(2)).setScale(8, RoundingMode.DOWN); + BigDecimal bobShare = blockReward.multiply(share.movePointLeft(2)).setScale(8, RoundingMode.DOWN); AccountUtils.assertBalance(repository, "bob", Asset.QORA, initialBalances.get("bob").get(Asset.QORA).add(bobShare)); - BigDecimal aliceShare = firstReward.subtract(bobShare); + BigDecimal aliceShare = blockReward.subtract(bobShare); AccountUtils.assertBalance(repository, "alice", Asset.QORA, initialBalances.get("alice").get(Asset.QORA).add(aliceShare)); } } diff --git a/src/test/java/org/qora/test/group/GroupApprovalTests.java b/src/test/java/org/qora/test/group/GroupApprovalTests.java new file mode 100644 index 00000000..eac1aa97 --- /dev/null +++ b/src/test/java/org/qora/test/group/GroupApprovalTests.java @@ -0,0 +1,184 @@ +package org.qora.test.group; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.qora.account.PrivateKeyAccount; +import org.qora.asset.Asset; +import org.qora.block.BlockGenerator; +import org.qora.data.transaction.BaseTransactionData; +import org.qora.data.transaction.IssueAssetTransactionData; +import org.qora.data.transaction.PaymentTransactionData; +import org.qora.data.transaction.TransactionData; +import org.qora.group.Group; +import org.qora.group.Group.ApprovalThreshold; +import org.qora.repository.DataException; +import org.qora.repository.Repository; +import org.qora.repository.RepositoryManager; +import org.qora.test.common.BlockUtils; +import org.qora.test.common.Common; +import org.qora.test.common.GroupUtils; +import org.qora.test.common.TransactionUtils; +import org.qora.transaction.Transaction; +import org.qora.transaction.Transaction.ApprovalStatus; +import org.qora.transaction.Transaction.ValidationResult; + +import static org.junit.Assert.*; + +import java.math.BigDecimal; +import java.util.Arrays; + +public class GroupApprovalTests extends Common { + + private static final BigDecimal amount = BigDecimal.valueOf(5000L).setScale(8); + private static final BigDecimal fee = BigDecimal.ONE.setScale(8); + + @Before + public void beforeTest() throws DataException { + Common.useDefaultSettings(); + } + + @After + public void afterTest() throws DataException { + Common.orphanCheck(); + } + + @Test + /** Check that a transaction type that doesn't need approval doesn't accept txGroupId apart from NO_GROUP */ + public void testNonApprovalTxGroupId() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + Transaction transaction = buildPaymentTransaction(repository, "alice", "bob", amount, Group.NO_GROUP); + assertEquals(ValidationResult.OK, transaction.isValidUnconfirmed()); + + int groupId = GroupUtils.createGroup(repository, "alice", "test", true, ApprovalThreshold.NONE, 0, 10); + + transaction = buildPaymentTransaction(repository, "alice", "bob", amount, groupId); + assertEquals(ValidationResult.INVALID_TX_GROUP_ID, transaction.isValidUnconfirmed()); + } + } + + @Test + /** Check that a transaction, that requires approval, updates references and fees properly. */ + public void testReferencesAndFees() throws DataException { + final int minBlockDelay = 5; + final int maxBlockDelay = 20; + + try (final Repository repository = RepositoryManager.getRepository()) { + PrivateKeyAccount aliceAccount = Common.getTestAccount(repository, "alice"); + + int groupId = GroupUtils.createGroup(repository, "alice", "test", true, ApprovalThreshold.ONE, minBlockDelay, maxBlockDelay); + + GroupUtils.joinGroup(repository, "bob", groupId); + + PrivateKeyAccount bobAccount = Common.getTestAccount(repository, "bob"); + byte[] bobOriginalReference = bobAccount.getLastReference(); + + BigDecimal aliceOriginalBalance = aliceAccount.getConfirmedBalance(Asset.QORA); + BigDecimal bobOriginalBalance = bobAccount.getConfirmedBalance(Asset.QORA); + + BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); + Transaction bobAssetTransaction = buildIssueAssetTransaction(repository, "bob", groupId); + TransactionUtils.signAndForge(repository, bobAssetTransaction.getTransactionData(), bobAccount); + + // Confirm transaction needs approval, and hasn't been approved + ApprovalStatus approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); + assertEquals("incorrect transaction approval status", ApprovalStatus.PENDING, approvalStatus); + + // Bob's last-reference should have changed, even though the transaction itself hasn't been approved yet + byte[] bobPostAssetReference = bobAccount.getLastReference(); + assertFalse("reference should have changed", Arrays.equals(bobOriginalReference, bobPostAssetReference)); + + // Bob's balance should have the fee removed, even though the transaction itself hasn't been approved yet + BigDecimal bobPostAssetBalance = bobAccount.getConfirmedBalance(Asset.QORA); + Common.assertEqualBigDecimals("approval-pending transaction creator's balance incorrect", bobOriginalBalance.subtract(fee), bobPostAssetBalance); + + // Transaction fee should have ended up in forging account + BigDecimal alicePostAssetBalance = aliceAccount.getConfirmedBalance(Asset.QORA); + Common.assertEqualBigDecimals("block forger's balance incorrect", aliceOriginalBalance.add(blockReward).add(fee), alicePostAssetBalance); + + // Have Bob do a non-approval transaction to change his last-reference + Transaction bobPaymentTransaction = buildPaymentTransaction(repository, "bob", "chloe", amount, Group.NO_GROUP); + TransactionUtils.signAsUnconfirmed(repository, bobPaymentTransaction.getTransactionData(), bobAccount); + BlockGenerator.generateTestingBlock(repository, aliceAccount); + + byte[] bobPostPaymentReference = bobAccount.getLastReference(); + assertFalse("reference should have changed", Arrays.equals(bobPostAssetReference, bobPostPaymentReference)); + + // Have Alice approve Bob's approval-needed transaction + GroupUtils.approveTransaction(repository, "alice", bobAssetTransaction.getTransactionData().getSignature(), true); + + // Now forge a few blocks so transaction is approved + for (int blockCount = 0; blockCount < minBlockDelay; ++blockCount) + BlockGenerator.generateTestingBlock(repository, aliceAccount); + + // Confirm transaction now approved + approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); + assertEquals("incorrect transaction approval status", ApprovalStatus.APPROVED, approvalStatus); + + // Check Bob's last reference hasn't been changed by transaction approval + byte[] bobPostApprovalReference = bobAccount.getLastReference(); + assertTrue("reference should be unchanged", Arrays.equals(bobPostPaymentReference, bobPostApprovalReference)); + + // Ok, now unwind/orphan all the above to double-check + + // Orphan blocks that decided transaction approval + for (int blockCount = 0; blockCount < minBlockDelay; ++blockCount) + BlockUtils.orphanLastBlock(repository); + + // Check Bob's last reference is still correct + byte[] bobReference = bobAccount.getLastReference(); + assertTrue("reference should be unchanged", Arrays.equals(bobPostPaymentReference, bobReference)); + + // Orphan block containing Alice's group-approval transaction + BlockUtils.orphanLastBlock(repository); + + // Check Bob's last reference is still correct + bobReference = bobAccount.getLastReference(); + assertTrue("reference should be unchanged", Arrays.equals(bobPostPaymentReference, bobReference)); + + // Orphan block containing Bob's non-approval payment transaction + BlockUtils.orphanLastBlock(repository); + + // Check Bob's last reference has reverted to pre-payment value + bobReference = bobAccount.getLastReference(); + assertTrue("reference should be pre-payment", Arrays.equals(bobPostAssetReference, bobReference)); + + // Orphan block containing Bob's issue-asset approval-needed transaction + BlockUtils.orphanLastBlock(repository); + + // Check Bob's last reference has reverted to original value + bobReference = bobAccount.getLastReference(); + assertTrue("reference should be pre-payment", Arrays.equals(bobOriginalReference, bobReference)); + + // Also check Bob's balance is back to original value + BigDecimal bobBalance = bobAccount.getConfirmedBalance(Asset.QORA); + Common.assertEqualBigDecimals("reverted balance doesn't match original", bobOriginalBalance, bobBalance); + } + } + + private Transaction buildPaymentTransaction(Repository repository, String sender, String recipient, BigDecimal amount, int txGroupId) throws DataException { + PrivateKeyAccount sendingAccount = Common.getTestAccount(repository, sender); + PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient); + + byte[] reference = sendingAccount.getLastReference(); + long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1; + + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, sendingAccount.getPublicKey(), fee, null); + PaymentTransactionData transactionData = new PaymentTransactionData(baseTransactionData, recipientAccount.getAddress(), amount); + + return Transaction.fromData(repository, transactionData); + } + + private Transaction buildIssueAssetTransaction(Repository repository, String testAccountName, int txGroupId) throws DataException { + PrivateKeyAccount account = Common.getTestAccount(repository, testAccountName); + + byte[] reference = account.getLastReference(); + long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1; + + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, account.getPublicKey(), fee, null); + TransactionData transactionData = new IssueAssetTransactionData(baseTransactionData, account.getAddress(), "test asset", "test asset desc", 1000L, true, "{}"); + + return Transaction.fromData(repository, transactionData); + } + +}