From faa6405d5f92249ef291be1b603597c35ff5d7ee Mon Sep 17 00:00:00 2001 From: catbref Date: Wed, 24 Jun 2020 17:15:17 +0100 Subject: [PATCH] Reference fixes for MESSAGE transactions & tests to cover --- .../transaction/MessageTransaction.java | 33 +++++++++++ .../java/org/qortal/test/MessageTests.java | 56 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/src/main/java/org/qortal/transaction/MessageTransaction.java b/src/main/java/org/qortal/transaction/MessageTransaction.java index acc46164..ef6e6c76 100644 --- a/src/main/java/org/qortal/transaction/MessageTransaction.java +++ b/src/main/java/org/qortal/transaction/MessageTransaction.java @@ -2,13 +2,16 @@ package org.qortal.transaction; import java.util.Collections; import java.util.List; +import java.util.Random; import org.qortal.account.Account; +import org.qortal.account.PrivateKeyAccount; import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.crypto.MemoryPoW; import org.qortal.data.PaymentData; +import org.qortal.data.transaction.BaseTransactionData; import org.qortal.data.transaction.MessageTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.group.Group; @@ -20,6 +23,7 @@ import org.qortal.transform.TransformationException; import org.qortal.transform.transaction.ChatTransactionTransformer; import org.qortal.transform.transaction.MessageTransactionTransformer; import org.qortal.transform.transaction.TransactionTransformer; +import org.qortal.utils.NTP; public class MessageTransaction extends Transaction { @@ -45,6 +49,22 @@ public class MessageTransaction extends Transaction { this.messageTransactionData = (MessageTransactionData) this.transactionData; } + /** Constructs non-payment MessageTransaction. Caller will need to compute nonce/set fee and then sign. */ + public static MessageTransaction build(Repository repository, PrivateKeyAccount sender, int txGroupId, String recipient, byte[] data, boolean isText, boolean isEncrypted) throws DataException { + long timestamp = NTP.getTime(); + byte[] reference = sender.getLastReference(); + if (reference == null) { + reference = new byte[64]; + new Random().nextBytes(reference); + } + + long fee = 0L; + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, sender.getPublicKey(), fee, null); + int version = 4; + MessageTransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, 0, recipient, 0, null, data, isText, isEncrypted); + return new MessageTransaction(repository, messageTransactionData); + } + // More information @Override @@ -140,6 +160,19 @@ public class MessageTransaction extends Transaction { return ValidationResult.OK; } + @Override + public boolean hasValidReference() throws DataException { + // We shouldn't really get this far, but just in case: + if (this.messageTransactionData.getReference() == null) + return false; + + // If zero fee, then we rely on nonce and reference isn't important + if (this.messageTransactionData.getFee() == 0) + return true; + + return super.hasValidReference(); + } + @Override public ValidationResult isValid() throws DataException { // Nonce checking is done via isSignatureValid() as that method is only called once per import diff --git a/src/test/java/org/qortal/test/MessageTests.java b/src/test/java/org/qortal/test/MessageTests.java index 6aa4f908..f08c7b2f 100644 --- a/src/test/java/org/qortal/test/MessageTests.java +++ b/src/test/java/org/qortal/test/MessageTests.java @@ -3,7 +3,10 @@ package org.qortal.test; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.qortal.account.PrivateKeyAccount; import org.qortal.asset.Asset; +import org.qortal.block.BlockChain; +import org.qortal.data.transaction.BaseTransactionData; import org.qortal.data.transaction.MessageTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.group.Group; @@ -24,9 +27,12 @@ import org.qortal.transaction.Transaction.ValidationResult; import org.qortal.transform.TransformationException; import org.qortal.transform.transaction.MessageTransactionTransformer; import org.qortal.transform.transaction.TransactionTransformer; +import org.qortal.utils.NTP; import static org.junit.Assert.*; +import java.util.Random; + public class MessageTests extends Common { private static final int version = 4; @@ -69,6 +75,44 @@ public class MessageTests extends Common { assertFalse(isValid(newGroupId, null, 0L, null)); } + @Test + public void referenceTests() throws DataException { + Random random = new Random(); + + byte[] randomPrivateKey = new byte[32]; + random.nextBytes(randomPrivateKey); + + byte[] randomReference = new byte[64]; + random.nextBytes(randomReference); + + long minimumFee = BlockChain.getInstance().getUnitFee(); + + try (final Repository repository = RepositoryManager.getRepository()) { + PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); + PrivateKeyAccount newbie = new PrivateKeyAccount(repository, randomPrivateKey); + + byte[] aliceReference = alice.getLastReference(); + + // real account, correct reference, real fee: OK + assertTrue(hasValidReference(repository, alice, aliceReference, minimumFee)); + // real account, random reference, real fee: INVALID + assertFalse(hasValidReference(repository, alice, randomReference, minimumFee)); + // real account, correct reference, zero fee: OK + assertTrue(hasValidReference(repository, alice, aliceReference, 0)); + // real account, random reference, zero fee: OK + assertTrue(hasValidReference(repository, alice, randomReference, 0)); + + // new account, null reference, real fee: INVALID: new accounts don't have a reference! + assertFalse(hasValidReference(repository, newbie, null, minimumFee)); + // new account, wrong reference, real fee: INVALID: new accounts don't have a reference! + assertFalse(hasValidReference(repository, newbie, randomReference, minimumFee)); + // new account, null reference, zero fee: INVALID + assertFalse(hasValidReference(repository, newbie, null, 0)); + // new account, random reference, zero fee: OK + assertTrue(hasValidReference(repository, newbie, randomReference, 0)); + } + } + @Test public void noFeeNoNonce() throws DataException { testFeeNonce(false, false, false); @@ -139,6 +183,18 @@ public class MessageTests extends Common { } } + private boolean hasValidReference(Repository repository, PrivateKeyAccount sender, byte[] reference, long fee) throws DataException { + long timestamp = NTP.getTime(); + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, sender.getPublicKey(), fee, null); + int version = 4; + byte[] data = "test".getBytes(); + boolean isText = true; + boolean isEncrypted = false; + MessageTransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, 0, recipient, 0, null, data, isText, isEncrypted); + MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData); + return messageTransaction.hasValidReference(); + } + private void testFeeNonce(boolean withFee, boolean withNonce, boolean isValid) throws DataException { try (final Repository repository = RepositoryManager.getRepository()) { TestAccount alice = Common.getTestAccount(repository, "alice");