From 24eb7c69339015e75052d708c8408b12c0f6c71e Mon Sep 17 00:00:00 2001 From: catbref Date: Mon, 18 May 2020 09:09:35 +0100 Subject: [PATCH] Allow MESSAGE transactions to have no recipient. This allows on-chain messages to a group, including NO_GROUP / groupID zero. No-recipient messages cannot have an amount - where would it go? Changed MESSAGE serialization layout to add boolean indicating whether recipient is present. Changed MESSAGE serialization layout so assetID is after amount, and only present if amount is non-zero. Changed DB table structures to cover above. Added unit tests to cover above. --- .../api/resource/CrossChainResource.java | 3 +- .../transaction/MessageTransactionData.java | 34 ++-- .../hsqldb/HSQLDBDatabaseUpdates.java | 4 +- .../HSQLDBMessageTransactionRepository.java | 4 +- .../transaction/MessageTransaction.java | 67 ++++++-- .../MessageTransactionTransformer.java | 43 +++-- .../java/org/qortal/test/MessageTests.java | 154 ++++++++++++++++++ .../java/org/qortal/test/btcacct/AtTests.java | 3 +- .../transaction/MessageTestTransaction.java | 2 +- .../common/transaction/TestTransaction.java | 6 +- 10 files changed, 273 insertions(+), 47 deletions(-) create mode 100644 src/test/java/org/qortal/test/MessageTests.java diff --git a/src/main/java/org/qortal/api/resource/CrossChainResource.java b/src/main/java/org/qortal/api/resource/CrossChainResource.java index 28d78226..bb60c372 100644 --- a/src/main/java/org/qortal/api/resource/CrossChainResource.java +++ b/src/main/java/org/qortal/api/resource/CrossChainResource.java @@ -815,9 +815,10 @@ public class CrossChainResource { Long fee = null; long amount = 0L; + Long assetId = null; // no assetId as amount is zero BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, senderPublicKey, fee, null); - TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, 4, atAddress, Asset.QORT, amount, messageData, false, false); + TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, 4, atAddress, amount, assetId, messageData, false, false); MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData); diff --git a/src/main/java/org/qortal/data/transaction/MessageTransactionData.java b/src/main/java/org/qortal/data/transaction/MessageTransactionData.java index 0c9b29f3..649687f6 100644 --- a/src/main/java/org/qortal/data/transaction/MessageTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/MessageTransactionData.java @@ -5,7 +5,6 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import org.qortal.asset.Asset; import org.qortal.transaction.Transaction.TransactionType; import io.swagger.v3.oas.annotations.media.Schema; @@ -16,14 +15,24 @@ import io.swagger.v3.oas.annotations.media.Schema; public class MessageTransactionData extends TransactionData { // Properties + private byte[] senderPublicKey; + private int version; + + // Not always present private String recipient; - private Long assetId; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) private long amount; + + // Not present if amount is zero + private Long assetId; + private byte[] data; + private boolean isText; + private boolean isEncrypted; // Constructors @@ -38,19 +47,14 @@ public class MessageTransactionData extends TransactionData { } public MessageTransactionData(BaseTransactionData baseTransactionData, - int version, String recipient, Long assetId, long amount, byte[] data, boolean isText, boolean isEncrypted) { + int version, String recipient, long amount, Long assetId, byte[] data, boolean isText, boolean isEncrypted) { super(TransactionType.MESSAGE, baseTransactionData); this.senderPublicKey = baseTransactionData.creatorPublicKey; this.version = version; this.recipient = recipient; - - if (assetId != null) - this.assetId = assetId; - else - this.assetId = Asset.QORT; - this.amount = amount; + this.assetId = assetId; this.data = data; this.isText = isText; this.isEncrypted = isEncrypted; @@ -70,23 +74,23 @@ public class MessageTransactionData extends TransactionData { return this.recipient; } - public Long getAssetId() { - return this.assetId; - } - public long getAmount() { return this.amount; } + public Long getAssetId() { + return this.assetId; + } + public byte[] getData() { return this.data; } - public boolean getIsText() { + public boolean isText() { return this.isText; } - public boolean getIsEncrypted() { + public boolean isEncrypted() { return this.isEncrypted; } diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java index 730740cd..09d38187 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -269,8 +269,8 @@ public class HSQLDBDatabaseUpdates { case 6: // Message Transactions stmt.execute("CREATE TABLE MessageTransactions (signature Signature, version TINYINT NOT NULL, " - + "sender QortalPublicKey NOT NULL, recipient QortalAddress NOT NULL, " - + "is_text BOOLEAN NOT NULL, is_encrypted BOOLEAN NOT NULL, amount QortalAmount NOT NULL, asset_id AssetID NOT NULL, data MessageData NOT NULL, " + + "sender QortalPublicKey NOT NULL, recipient QortalAddress, amount QortalAmount NOT NULL, asset_id AssetID, " + + "is_text BOOLEAN NOT NULL, is_encrypted BOOLEAN NOT NULL, data MessageData NOT NULL, " + TRANSACTION_KEYS + ")"); break; diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java index 1ef2a707..c33dc032 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java @@ -36,7 +36,7 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit byte[] data = resultSet.getBytes(7); - return new MessageTransactionData(baseTransactionData, version, recipient, assetId, amount, data, isText, isEncrypted); + return new MessageTransactionData(baseTransactionData, version, recipient, amount, assetId, data, isText, isEncrypted); } catch (SQLException e) { throw new DataException("Unable to fetch message transaction from repository", e); } @@ -50,7 +50,7 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit saveHelper.bind("signature", messageTransactionData.getSignature()).bind("version", messageTransactionData.getVersion()) .bind("sender", messageTransactionData.getSenderPublicKey()).bind("recipient", messageTransactionData.getRecipient()) - .bind("is_text", messageTransactionData.getIsText()).bind("is_encrypted", messageTransactionData.getIsEncrypted()) + .bind("is_text", messageTransactionData.isText()).bind("is_encrypted", messageTransactionData.isEncrypted()) .bind("amount", messageTransactionData.getAmount()).bind("asset_id", messageTransactionData.getAssetId()) .bind("data", messageTransactionData.getData()); diff --git a/src/main/java/org/qortal/transaction/MessageTransaction.java b/src/main/java/org/qortal/transaction/MessageTransaction.java index 463f47a2..2d70dce8 100644 --- a/src/main/java/org/qortal/transaction/MessageTransaction.java +++ b/src/main/java/org/qortal/transaction/MessageTransaction.java @@ -4,6 +4,7 @@ import java.util.Collections; import java.util.List; import org.qortal.account.Account; +import org.qortal.asset.Asset; import org.qortal.data.PaymentData; import org.qortal.data.transaction.MessageTransactionData; import org.qortal.data.transaction.TransactionData; @@ -13,14 +14,17 @@ import org.qortal.repository.Repository; public class MessageTransaction extends Transaction { + // Useful constants + + public static final int MAX_DATA_SIZE = 4000; + // Properties private MessageTransactionData messageTransactionData; + + /** Cached, lazy-instantiated payment data. Use {@link #getPaymentData()} instead! */ private PaymentData paymentData = null; - // Other useful constants - public static final int MAX_DATA_SIZE = 4000; - private static final boolean isZeroAmountValid = true; // Constructors @@ -34,6 +38,9 @@ public class MessageTransaction extends Transaction { @Override public List getRecipientAddresses() throws DataException { + if (this.messageTransactionData.getRecipient() == null) + return Collections.emptyList(); + return Collections.singletonList(this.messageTransactionData.getRecipient()); } @@ -62,42 +69,80 @@ public class MessageTransaction extends Transaction { if (this.messageTransactionData.getData().length < 1 || this.messageTransactionData.getData().length > MAX_DATA_SIZE) return ValidationResult.INVALID_DATA_LENGTH; + // If message has no recipient then it cannot have a payment + if (this.messageTransactionData.getRecipient() == null && this.messageTransactionData.getAmount() != 0) + return ValidationResult.INVALID_AMOUNT; + + // If message has no payment then we only need to do a simple balance check for fee + if (this.messageTransactionData.getAmount() == 0) { + if (getSender().getConfirmedBalance(Asset.QORT) < this.messageTransactionData.getFee()) + return ValidationResult.NO_BALANCE; + + return ValidationResult.OK; + } + // Wrap and delegate final payment checks to Payment class - return new Payment(this.repository).isValid(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(), - isZeroAmountValid); + return new Payment(this.repository).isValid(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), + this.messageTransactionData.getFee(), true); } @Override public ValidationResult isProcessable() throws DataException { + // If we have no amount then we can always process + if (this.messageTransactionData.getAmount() == 0L) + return ValidationResult.OK; + // Wrap and delegate final processable checks to Payment class - return new Payment(this.repository).isProcessable(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(), - isZeroAmountValid); + return new Payment(this.repository).isProcessable(this.messageTransactionData.getSenderPublicKey(), + getPaymentData(), this.messageTransactionData.getFee(), true); } @Override public void process() throws DataException { + // If we have no amount then there's nothing to do + if (this.messageTransactionData.getAmount() == 0L) + return; + // Wrap and delegate payment processing to Payment class. new Payment(this.repository).process(this.messageTransactionData.getSenderPublicKey(), getPaymentData()); } @Override public void processReferencesAndFees() throws DataException { + // If we have no amount then we only need to process sender's reference and fees + if (this.messageTransactionData.getAmount() == 0L) { + super.processReferencesAndFees(); + return; + } + // Wrap and delegate references processing to Payment class. Only update recipient's last reference if transferring QORT. - new Payment(this.repository).processReferencesAndFees(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(), - this.messageTransactionData.getSignature(), false); + new Payment(this.repository).processReferencesAndFees(this.messageTransactionData.getSenderPublicKey(), + getPaymentData(), this.messageTransactionData.getFee(), this.messageTransactionData.getSignature(), + false); } @Override public void orphan() throws DataException { + // If we have no amount then there's nothing to do + if (this.messageTransactionData.getAmount() == 0L) + return; + // Wrap and delegate payment processing to Payment class. new Payment(this.repository).orphan(this.messageTransactionData.getSenderPublicKey(), getPaymentData()); } @Override public void orphanReferencesAndFees() throws DataException { + // If we have no amount then we only need to orphan sender's reference and fees + if (this.messageTransactionData.getAmount() == 0L) { + super.orphanReferencesAndFees(); + return; + } + // Wrap and delegate references processing to Payment class. Only revert recipient's last reference if transferring QORT. - new Payment(this.repository).orphanReferencesAndFees(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(), - this.messageTransactionData.getSignature(), this.messageTransactionData.getReference(), false); + new Payment(this.repository).orphanReferencesAndFees(this.messageTransactionData.getSenderPublicKey(), + getPaymentData(), this.messageTransactionData.getFee(), this.messageTransactionData.getSignature(), + this.messageTransactionData.getReference(), false); } } diff --git a/src/main/java/org/qortal/transform/transaction/MessageTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/MessageTransactionTransformer.java index 7a008521..b3c41476 100644 --- a/src/main/java/org/qortal/transform/transaction/MessageTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/MessageTransactionTransformer.java @@ -19,12 +19,13 @@ import com.google.common.primitives.Longs; public class MessageTransactionTransformer extends TransactionTransformer { // Property lengths + private static final int HAS_RECIPIENT_LENGTH = BOOLEAN_LENGTH; private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; private static final int DATA_SIZE_LENGTH = INT_LENGTH; private static final int IS_TEXT_LENGTH = BOOLEAN_LENGTH; private static final int IS_ENCRYPTED_LENGTH = BOOLEAN_LENGTH; - private static final int EXTRAS_LENGTH = RECIPIENT_LENGTH + ASSET_ID_LENGTH + AMOUNT_LENGTH + DATA_SIZE_LENGTH + IS_ENCRYPTED_LENGTH + IS_TEXT_LENGTH; + private static final int EXTRAS_LENGTH = HAS_RECIPIENT_LENGTH + AMOUNT_LENGTH + DATA_SIZE_LENGTH + IS_ENCRYPTED_LENGTH + IS_TEXT_LENGTH; protected static final TransactionLayout layout; @@ -35,9 +36,10 @@ public class MessageTransactionTransformer extends TransactionTransformer { layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("sender's public key", TransformationType.PUBLIC_KEY); - layout.add("recipient", TransformationType.ADDRESS); - layout.add("asset ID of payment", TransformationType.LONG); + layout.add("has recipient?", TransformationType.BOOLEAN); + layout.add("? recipient", TransformationType.ADDRESS); layout.add("payment (can be zero)", TransformationType.AMOUNT); + layout.add("asset ID of payment (if payment not zero)", TransformationType.LONG); layout.add("message length", TransformationType.INT); layout.add("message", TransformationType.DATA); layout.add("is message encrypted?", TransformationType.BOOLEAN); @@ -58,12 +60,13 @@ public class MessageTransactionTransformer extends TransactionTransformer { byte[] senderPublicKey = Serialization.deserializePublicKey(byteBuffer); - String recipient = Serialization.deserializeAddress(byteBuffer); - - long assetId = byteBuffer.getLong(); + boolean hasRecipient = byteBuffer.get() != 0; + String recipient = hasRecipient ? Serialization.deserializeAddress(byteBuffer) : null; long amount = byteBuffer.getLong(); + Long assetId = amount != 0 ? byteBuffer.getLong() : null; + int dataSize = byteBuffer.getInt(); // Don't allow invalid dataSize here to avoid run-time issues if (dataSize > MessageTransaction.MAX_DATA_SIZE) @@ -83,13 +86,21 @@ public class MessageTransactionTransformer extends TransactionTransformer { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, senderPublicKey, fee, signature); - return new MessageTransactionData(baseTransactionData, version, recipient, assetId, amount, data, isText, isEncrypted); + return new MessageTransactionData(baseTransactionData, version, recipient, amount, assetId, data, isText, isEncrypted); } public static int getDataLength(TransactionData transactionData) throws TransformationException { MessageTransactionData messageTransactionData = (MessageTransactionData) transactionData; - return getBaseLength(transactionData) + EXTRAS_LENGTH + messageTransactionData.getData().length; + int dataLength = getBaseLength(transactionData) + EXTRAS_LENGTH + messageTransactionData.getData().length; + + if (messageTransactionData.getRecipient() != null) + dataLength += RECIPIENT_LENGTH; + + if (messageTransactionData.getAmount() != 0) + dataLength += ASSET_ID_LENGTH; + + return dataLength; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -100,19 +111,25 @@ public class MessageTransactionTransformer extends TransactionTransformer { transformCommonBytes(transactionData, bytes); - Serialization.serializeAddress(bytes, messageTransactionData.getRecipient()); - - bytes.write(Longs.toByteArray(messageTransactionData.getAssetId())); + if (messageTransactionData.getRecipient() != null) { + bytes.write((byte) 1); + Serialization.serializeAddress(bytes, messageTransactionData.getRecipient()); + } else { + bytes.write((byte) 0); + } bytes.write(Longs.toByteArray(messageTransactionData.getAmount())); + if (messageTransactionData.getAmount() != 0) + bytes.write(Longs.toByteArray(messageTransactionData.getAssetId())); + bytes.write(Ints.toByteArray(messageTransactionData.getData().length)); bytes.write(messageTransactionData.getData()); - bytes.write((byte) (messageTransactionData.getIsEncrypted() ? 1 : 0)); + bytes.write((byte) (messageTransactionData.isEncrypted() ? 1 : 0)); - bytes.write((byte) (messageTransactionData.getIsText() ? 1 : 0)); + bytes.write((byte) (messageTransactionData.isText() ? 1 : 0)); bytes.write(Longs.toByteArray(messageTransactionData.getFee())); diff --git a/src/test/java/org/qortal/test/MessageTests.java b/src/test/java/org/qortal/test/MessageTests.java new file mode 100644 index 00000000..c9c05050 --- /dev/null +++ b/src/test/java/org/qortal/test/MessageTests.java @@ -0,0 +1,154 @@ +package org.qortal.test; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.qortal.asset.Asset; +import org.qortal.data.transaction.MessageTransactionData; +import org.qortal.data.transaction.TransactionData; +import org.qortal.group.Group; +import org.qortal.group.Group.ApprovalThreshold; +import org.qortal.repository.DataException; +import org.qortal.repository.Repository; +import org.qortal.repository.RepositoryManager; +import org.qortal.test.common.BlockUtils; +import org.qortal.test.common.Common; +import org.qortal.test.common.GroupUtils; +import org.qortal.test.common.TestAccount; +import org.qortal.test.common.TransactionUtils; +import org.qortal.test.common.transaction.TestTransaction; +import org.qortal.transaction.MessageTransaction; +import org.qortal.transaction.Transaction; +import org.qortal.transaction.Transaction.TransactionType; +import org.qortal.transaction.Transaction.ValidationResult; +import org.qortal.transform.TransformationException; +import org.qortal.transform.transaction.MessageTransactionTransformer; +import org.qortal.transform.transaction.TransactionTransformer; + +import static org.junit.Assert.*; + +public class MessageTests extends Common { + + private static final int version = 3; + private static final String recipient = Common.getTestAccount(null, "bob").getAddress(); + + + @Before + public void beforeTest() throws DataException { + Common.useDefaultSettings(); + } + + @After + public void afterTest() throws DataException { + Common.orphanCheck(); + } + + @Test + public void validityTests() throws DataException { + // with recipient, with amount + assertTrue(isValid(Group.NO_GROUP, recipient, 123L, Asset.QORT)); + + // with recipient, no amount + assertTrue(isValid(Group.NO_GROUP, recipient, 0L, null)); + + // no recipient (message to group), no amount + assertTrue(isValid(Group.NO_GROUP, null, 0L, null)); + + // can't have amount if no recipient! + assertFalse(isValid(Group.NO_GROUP, null, 123L, Asset.QORT)); + + // Alice is part of group 1 + assertTrue(isValid(1, null, 0L, null)); + + int newGroupId; + try (final Repository repository = RepositoryManager.getRepository()) { + newGroupId = GroupUtils.createGroup(repository, "chloe", "non-alice-group", false, ApprovalThreshold.ONE, 10, 1440); + } + + // Alice is not part of new group + assertFalse(isValid(newGroupId, null, 0L, null)); + } + + @Test + public void withRecipentNoAmount() throws DataException { + testMessage(Group.NO_GROUP, recipient, 0L, null); + } + + @Test + public void withRecipentWithAmount() throws DataException { + testMessage(Group.NO_GROUP, recipient, 123L, Asset.QORT); + } + + @Test + public void noRecipentNoAmount() throws DataException { + testMessage(Group.NO_GROUP, null, 0L, null); + } + + @Test + public void noRecipentNoAmountWithGroup() throws DataException { + testMessage(1, null, 0L, null); + } + + @Test + public void serializationTests() throws DataException, TransformationException { + // with recipient, with amount + testSerialization(recipient, 123L, Asset.QORT); + + // with recipient, no amount + testSerialization(recipient, 0L, null); + + // no recipient (message to group), no amount + testSerialization(null, 0L, null); + } + + private boolean isValid(int txGroupId, String recipient, long amount, Long assetId) throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + TestAccount alice = Common.getTestAccount(repository, "alice"); + + MessageTransactionData transactionData = new MessageTransactionData(TestTransaction.generateBase(alice, txGroupId), + version, recipient, amount, assetId, new byte[1], false, false); + + Transaction transaction = new MessageTransaction(repository, transactionData); + + return transaction.isValidUnconfirmed() == ValidationResult.OK; + } + } + + private void testMessage(int txGroupId, String recipient, long amount, Long assetId) throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + TestAccount alice = Common.getTestAccount(repository, "alice"); + + MessageTransactionData transactionData = new MessageTransactionData(TestTransaction.generateBase(alice, txGroupId), + version, recipient, amount, assetId, new byte[1], false, false); + + TransactionUtils.signAndMint(repository, transactionData, alice); + + BlockUtils.orphanLastBlock(repository); + } + } + + private void testSerialization(String recipient, long amount, Long assetId) throws DataException, TransformationException { + try (final Repository repository = RepositoryManager.getRepository()) { + TestAccount alice = Common.getTestAccount(repository, "alice"); + + MessageTransactionData expectedTransactionData = new MessageTransactionData(TestTransaction.generateBase(alice), + version, recipient, amount, assetId, new byte[1], false, false); + + Transaction transaction = new MessageTransaction(repository, expectedTransactionData); + transaction.sign(alice); + + MessageTransactionTransformer.getDataLength(expectedTransactionData); + byte[] transactionBytes = MessageTransactionTransformer.toBytes(expectedTransactionData); + + TransactionData transactionData = TransactionTransformer.fromBytes(transactionBytes); + assertEquals(TransactionType.MESSAGE, transactionData.getType()); + + MessageTransactionData actualTransactionData = (MessageTransactionData) transactionData; + + assertEquals(expectedTransactionData.getRecipient(), actualTransactionData.getRecipient()); + assertEquals(expectedTransactionData.getAmount(), actualTransactionData.getAmount()); + assertEquals(expectedTransactionData.getAssetId(), actualTransactionData.getAssetId()); + } + } + +} diff --git a/src/test/java/org/qortal/test/btcacct/AtTests.java b/src/test/java/org/qortal/test/btcacct/AtTests.java index 1d4a6e0f..74771d62 100644 --- a/src/test/java/org/qortal/test/btcacct/AtTests.java +++ b/src/test/java/org/qortal/test/btcacct/AtTests.java @@ -474,9 +474,10 @@ public class AtTests extends Common { Long fee = null; long amount = 0; + Long assetId = null; // because amount is zero BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null); - TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, 4, recipient, Asset.QORT, amount, data, false, false); + TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, 4, recipient, amount, assetId, data, false, false); MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData); diff --git a/src/test/java/org/qortal/test/common/transaction/MessageTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/MessageTestTransaction.java index 7725bf3c..4bb4455e 100644 --- a/src/test/java/org/qortal/test/common/transaction/MessageTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/MessageTestTransaction.java @@ -19,7 +19,7 @@ public class MessageTestTransaction extends TestTransaction { final boolean isText = true; final boolean isEncrypted = false; - return new MessageTransactionData(generateBase(account), version, recipient, assetId, amount, data, isText, isEncrypted); + return new MessageTransactionData(generateBase(account), version, recipient, amount, assetId, data, isText, isEncrypted); } } diff --git a/src/test/java/org/qortal/test/common/transaction/TestTransaction.java b/src/test/java/org/qortal/test/common/transaction/TestTransaction.java index 00aa92de..11fdf58e 100644 --- a/src/test/java/org/qortal/test/common/transaction/TestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/TestTransaction.java @@ -12,8 +12,12 @@ public abstract class TestTransaction { protected static final Random random = new Random(); + public static BaseTransactionData generateBase(PrivateKeyAccount account, int txGroupId) throws DataException { + return new BaseTransactionData(System.currentTimeMillis(), txGroupId, account.getLastReference(), account.getPublicKey(), BlockChain.getInstance().getUnitFee(), null); + } + public static BaseTransactionData generateBase(PrivateKeyAccount account) throws DataException { - return new BaseTransactionData(System.currentTimeMillis(), Group.NO_GROUP, account.getLastReference(), account.getPublicKey(), BlockChain.getInstance().getUnitFee(), null); + return generateBase(account, Group.NO_GROUP); } }