Fix incorrect declared transaction length for SET_GROUP and VOTE_ON_POLL transactions.

Added transaction [de]serialization test, along with corresponding random transaction generators.

Minor typo fix in Transaction.

Minor clarification in MessageTransactionTransformer.

Added debugging to Account.
This commit is contained in:
catbref 2019-06-11 11:27:52 +01:00
parent 02e8bdb034
commit 03f60ef898
48 changed files with 881 additions and 174 deletions

View File

@ -16,6 +16,7 @@ import org.qora.repository.BlockRepository;
import org.qora.repository.DataException; import org.qora.repository.DataException;
import org.qora.repository.Repository; import org.qora.repository.Repository;
import org.qora.transaction.Transaction; import org.qora.transaction.Transaction;
import org.qora.utils.Base58;
public class Account { public class Account {
@ -159,7 +160,9 @@ public class Account {
* @throws DataException * @throws DataException
*/ */
public byte[] getLastReference() throws DataException { public byte[] getLastReference() throws DataException {
return this.repository.getAccountRepository().getLastReference(this.address); byte[] reference = this.repository.getAccountRepository().getLastReference(this.address);
LOGGER.trace(() -> String.format("Last reference for %s is %s", this.address, reference == null ? "null" : Base58.encode(reference)));
return reference;
} }
/** /**
@ -184,6 +187,8 @@ public class Account {
reference = transactionData.getSignature(); reference = transactionData.getSignature();
} }
final byte[] loggingReference = reference;
LOGGER.trace(() -> String.format("Last unconfirmed reference for %s is %s", this.address, loggingReference == null ? "null" : Base58.encode(loggingReference)));
return reference; return reference;
} }
@ -195,6 +200,8 @@ public class Account {
* @throws DataException * @throws DataException
*/ */
public void setLastReference(byte[] reference) throws DataException { public void setLastReference(byte[] reference) throws DataException {
LOGGER.trace(() -> String.format("Setting last reference for %s to %s", this.address, Base58.encode(reference)));
AccountData accountData = this.buildAccountData(); AccountData accountData = this.buildAccountData();
accountData.setReference(reference); accountData.setReference(reference);
this.repository.getAccountRepository().setLastReference(accountData); this.repository.getAccountRepository().setLastReference(accountData);

View File

@ -562,7 +562,7 @@ public abstract class Transaction {
if (!this.hasValidReference()) if (!this.hasValidReference())
return ValidationResult.INVALID_REFERENCE; return ValidationResult.INVALID_REFERENCE;
// Check transction is processable // Check transaction is processable
result = this.isProcessable(); result = this.isProcessable();
return result; return result;

View File

@ -28,7 +28,7 @@ public class MessageTransactionTransformer extends TransactionTransformer {
private static final int IS_TEXT_LENGTH = BOOLEAN_LENGTH; private static final int IS_TEXT_LENGTH = BOOLEAN_LENGTH;
private static final int IS_ENCRYPTED_LENGTH = BOOLEAN_LENGTH; private static final int IS_ENCRYPTED_LENGTH = BOOLEAN_LENGTH;
private static final int EXTRAS_LENGTH = RECIPIENT_LENGTH + AMOUNT_LENGTH + DATA_SIZE_LENGTH + IS_TEXT_LENGTH + IS_ENCRYPTED_LENGTH; private static final int EXTRAS_LENGTH = RECIPIENT_LENGTH + AMOUNT_LENGTH + DATA_SIZE_LENGTH + IS_ENCRYPTED_LENGTH + IS_TEXT_LENGTH;
protected static final TransactionLayout layout; protected static final TransactionLayout layout;
@ -99,7 +99,7 @@ public class MessageTransactionTransformer extends TransactionTransformer {
public static int getDataLength(TransactionData transactionData) throws TransformationException { public static int getDataLength(TransactionData transactionData) throws TransformationException {
MessageTransactionData messageTransactionData = (MessageTransactionData) transactionData; MessageTransactionData messageTransactionData = (MessageTransactionData) transactionData;
int dataLength = getBaseLength(transactionData) + EXTRAS_LENGTH; int dataLength = getBaseLength(transactionData) + EXTRAS_LENGTH + messageTransactionData.getData().length;
// V3+ has assetID for amount // V3+ has assetID for amount
if (messageTransactionData.getVersion() != 1) if (messageTransactionData.getVersion() != 1)

View File

@ -18,7 +18,7 @@ import com.google.common.primitives.Ints;
public class SetGroupTransactionTransformer extends TransactionTransformer { public class SetGroupTransactionTransformer extends TransactionTransformer {
// Property lengths // Property lengths
private static final int GROUPID_LENGTH = SIGNATURE_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH;
private static final int EXTRAS_LENGTH = GROUPID_LENGTH; private static final int EXTRAS_LENGTH = GROUPID_LENGTH;

View File

@ -20,10 +20,10 @@ import com.google.common.primitives.Ints;
public class VoteOnPollTransactionTransformer extends TransactionTransformer { public class VoteOnPollTransactionTransformer extends TransactionTransformer {
// Property lengths // Property lengths
private static final int VOTER_LENGTH = ADDRESS_LENGTH;
private static final int NAME_SIZE_LENGTH = INT_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH;
private static final int POLL_OPTION_LENGTH = INT_LENGTH;
private static final int EXTRAS_LENGTH = VOTER_LENGTH + NAME_SIZE_LENGTH; private static final int EXTRAS_LENGTH = NAME_SIZE_LENGTH + POLL_OPTION_LENGTH;
protected static final TransactionLayout layout; protected static final TransactionLayout layout;

View File

@ -84,21 +84,20 @@ public class BlockTests extends Common {
try (final Repository repository = RepositoryManager.getRepository()) { try (final Repository repository = RepositoryManager.getRepository()) {
PrivateKeyAccount signingAccount = Common.getTestAccount(repository, "alice"); PrivateKeyAccount signingAccount = Common.getTestAccount(repository, "alice");
// TODO: Fill block with random, valid transactions of every type (except GENESIS or AT) // TODO: Fill block with random, valid transactions of every type (except GENESIS, ACCOUNT_FLAGS or AT)
// This isn't as trivial as it seems as some transactions rely on others.
// e.g. CANCEL_ASSET_ORDER needs a prior CREATE_ASSET_ORDER
for (Transaction.TransactionType txType : Transaction.TransactionType.values()) { for (Transaction.TransactionType txType : Transaction.TransactionType.values()) {
if (txType == TransactionType.GENESIS || txType == TransactionType.AT) if (txType == TransactionType.GENESIS || txType == TransactionType.ACCOUNT_FLAGS || txType == TransactionType.AT)
continue; continue;
TransactionData transactionData = TransactionUtils.randomTransaction(repository, signingAccount, txType, true); TransactionData transactionData = TransactionUtils.randomTransaction(repository, signingAccount, txType, true);
Transaction transaction = Transaction.fromData(repository, transactionData); Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(signingAccount); transaction.sign(signingAccount);
repository.getTransactionRepository().save(transactionData); Transaction.ValidationResult validationResult = transaction.importAsUnconfirmed();
repository.getTransactionRepository().unconfirmTransaction(transactionData); if (validationResult != Transaction.ValidationResult.OK)
repository.saveChanges(); fail(String.format("Invalid (%s) test transaction, type %s", validationResult.name(), txType.name()));
// TODO: more transactions
break;
} }
// We might need to wait until transactions' timestamps are valid for the block we're about to generate // We might need to wait until transactions' timestamps are valid for the block we're about to generate

View File

@ -1,157 +1,73 @@
package org.qora.test; package org.qora.test;
import org.junit.Test; import org.junit.Test;
import org.qora.block.Block; import org.qora.account.PrivateKeyAccount;
import org.qora.block.GenesisBlock;
import org.qora.data.block.BlockData;
import org.qora.data.transaction.GenesisTransactionData;
import org.qora.data.transaction.TransactionData; import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException; import org.qora.repository.DataException;
import org.qora.repository.Repository; import org.qora.repository.Repository;
import org.qora.repository.RepositoryManager; import org.qora.repository.RepositoryManager;
import org.qora.test.common.Common; import org.qora.test.common.Common;
import org.qora.transaction.GenesisTransaction; import org.qora.test.common.TransactionUtils;
import org.qora.transaction.Transaction; import org.qora.transaction.Transaction;
import org.qora.transaction.Transaction.TransactionType;
import org.qora.transform.TransformationException; import org.qora.transform.TransformationException;
import org.qora.transform.transaction.TransactionTransformer; import org.qora.transform.transaction.TransactionTransformer;
import org.qora.utils.Base58;
import com.google.common.hash.HashCode;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.Arrays; import org.junit.Before;
import java.util.List;
public class SerializationTests extends Common { public class SerializationTests extends Common {
@Before
public void beforeTest() throws DataException {
Common.useDefaultSettings();
}
@Test @Test
public void testGenesisSerialization() throws TransformationException, DataException { public void testTransactions() throws DataException, TransformationException {
try (final Repository repository = RepositoryManager.getRepository()) { try (final Repository repository = RepositoryManager.getRepository()) {
GenesisBlock block = GenesisBlock.getInstance(repository); PrivateKeyAccount signingAccount = Common.getTestAccount(repository, "alice");
GenesisTransaction transaction = (GenesisTransaction) block.getTransactions().get(1); // Check serialization/deserialization of transactions of every type (except GENESIS, ACCOUNT_FLAGS or AT)
assertNotNull(transaction); for (Transaction.TransactionType txType : Transaction.TransactionType.values()) {
switch (txType) {
case GENESIS:
case ACCOUNT_FLAGS:
case AT:
case DELEGATION:
case SUPERNODE:
case AIRDROP:
continue;
GenesisTransactionData genesisTransactionData = (GenesisTransactionData) transaction.getTransactionData(); default:
// fall-through
}
System.out.println(genesisTransactionData.getTimestamp() + ": " + genesisTransactionData.getRecipient() + " received " TransactionData transactionData = TransactionUtils.randomTransaction(repository, signingAccount, txType, true);
+ genesisTransactionData.getAmount().toPlainString()); Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(signingAccount);
byte[] bytes = TransactionTransformer.toBytes(genesisTransactionData); final int claimedLength = TransactionTransformer.getDataLength(transactionData);
byte[] serializedTransaction = TransactionTransformer.toBytes(transactionData);
assertEquals(String.format("Serialized %s transaction length differs from declared length", txType.name()), claimedLength, serializedTransaction.length);
GenesisTransactionData parsedTransactionData = (GenesisTransactionData) TransactionTransformer.fromBytes(bytes); TransactionData deserializedTransactionData = TransactionTransformer.fromBytes(serializedTransaction);
// Re-sign
Transaction deserializedTransaction = Transaction.fromData(repository, deserializedTransactionData);
deserializedTransaction.sign(signingAccount);
assertEquals(String.format("Deserialized %s transaction signature differs", txType.name()), Base58.encode(transactionData.getSignature()), Base58.encode(deserializedTransactionData.getSignature()));
System.out.println(parsedTransactionData.getTimestamp() + ": " + parsedTransactionData.getRecipient() + " received " // Re-serialize to check new length and bytes
+ parsedTransactionData.getAmount().toPlainString()); final int reclaimedLength = TransactionTransformer.getDataLength(deserializedTransactionData);
assertEquals(String.format("Reserialized %s transaction declared length differs", txType.name()), claimedLength, reclaimedLength);
/* byte[] reserializedTransaction = TransactionTransformer.toBytes(deserializedTransactionData);
* NOTE: parsedTransactionData.getSignature() will be null as no signature is present in serialized bytes and calculating the signature is performed assertEquals(String.format("Reserialized %s transaction bytes differ", txType.name()), HashCode.fromBytes(serializedTransaction).toString(), HashCode.fromBytes(reserializedTransaction).toString());
* by GenesisTransaction, not GenesisTransactionData }
*/
// Not applicable: assertTrue(Arrays.equals(genesisTransactionData.getSignature(), parsedTransactionData.getSignature()));
GenesisTransaction parsedTransaction = new GenesisTransaction(repository, parsedTransactionData);
assertTrue(Arrays.equals(genesisTransactionData.getSignature(), parsedTransaction.getTransactionData().getSignature()));
} }
} }
private void testGenericSerialization(TransactionData transactionData) throws TransformationException {
assertNotNull(transactionData);
byte[] bytes = TransactionTransformer.toBytes(transactionData);
TransactionData parsedTransactionData = TransactionTransformer.fromBytes(bytes);
assertTrue("Transaction signature mismatch", Arrays.equals(transactionData.getSignature(), parsedTransactionData.getSignature()));
assertEquals("Data length mismatch", bytes.length, TransactionTransformer.getDataLength(transactionData));
}
private void testSpecificBlockTransactions(int height, TransactionType type) throws DataException, TransformationException {
try (final Repository repository = RepositoryManager.getRepository()) {
BlockData blockData = repository.getBlockRepository().fromHeight(height);
assertNotNull("Block " + height + " is required for this test", blockData);
Block block = new Block(repository, blockData);
List<Transaction> transactions = block.getTransactions();
assertNotNull(transactions);
for (Transaction transaction : transactions)
if (transaction.getTransactionData().getType() == type)
testGenericSerialization(transaction.getTransactionData());
}
}
@Test
public void testPaymentSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(754, TransactionType.PAYMENT);
}
@Test
public void testRegisterNameSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(120, TransactionType.REGISTER_NAME);
}
@Test
public void testUpdateNameSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(673, TransactionType.UPDATE_NAME);
}
@Test
public void testSellNameSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(200, TransactionType.SELL_NAME);
}
@Test
public void testCancelSellNameSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(741, TransactionType.CANCEL_SELL_NAME);
}
@Test
public void testBuyNameSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(973, TransactionType.BUY_NAME);
}
@Test
public void testCreatePollSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(10537, TransactionType.CREATE_POLL);
}
@Test
public void testVoteOnPollSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(10540, TransactionType.CREATE_POLL);
}
@Test
public void testIssueAssetSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(33661, TransactionType.ISSUE_ASSET);
}
@Test
public void testTransferAssetSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(39039, TransactionType.TRANSFER_ASSET);
}
@Test
public void testCreateAssetOrderSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(35611, TransactionType.CREATE_ASSET_ORDER);
}
@Test
public void testCancelAssetOrderSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(36176, TransactionType.CANCEL_ASSET_ORDER);
}
@Test
public void testMultiPaymentSerialization() throws TransformationException, DataException {
testSpecificBlockTransactions(34500, TransactionType.MULTI_PAYMENT);
}
@Test
public void testMessageSerialization() throws TransformationException, DataException {
// Message transactions went live block 99000
// Some transactions to be found in block 99001/2/5/6
testSpecificBlockTransactions(99001, TransactionType.MESSAGE);
}
} }

View File

@ -3,7 +3,10 @@ package org.qora.test.common;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.Map; import java.util.Map;
import java.util.Random;
import org.qora.account.PrivateKeyAccount; import org.qora.account.PrivateKeyAccount;
import org.qora.block.BlockChain; import org.qora.block.BlockChain;
@ -134,4 +137,13 @@ public class AssetUtils {
} }
} }
public static String randomData() {
Random random = new Random();
byte[] rawData = new byte[1024];
random.nextBytes(rawData);
Encoder base64Encoder = Base64.getEncoder();
return "{ \"logo\": \"data:image/png;base64," + base64Encoder.encodeToString(rawData) + "\" }";
}
} }

View File

@ -180,6 +180,8 @@ public class Common {
RepositoryManager.closeRepositoryFactory(); RepositoryManager.closeRepositoryFactory();
} }
// Test assertions
public static void assertEmptyBlockchain(Repository repository) throws DataException { public static void assertEmptyBlockchain(Repository repository) throws DataException {
assertEquals("Blockchain should be empty for this test", 0, repository.getBlockRepository().getBlockchainHeight()); assertEquals("Blockchain should be empty for this test", 0, repository.getBlockRepository().getBlockchainHeight());
} }

View File

@ -11,6 +11,7 @@ import org.qora.block.BlockGenerator;
import org.qora.data.transaction.TransactionData; import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException; import org.qora.repository.DataException;
import org.qora.repository.Repository; import org.qora.repository.Repository;
import org.qora.test.common.transaction.TestTransaction;
import org.qora.transaction.Transaction; import org.qora.transaction.Transaction;
import org.qora.transaction.Transaction.TransactionType; import org.qora.transaction.Transaction.TransactionType;
import org.qora.transaction.Transaction.ValidationResult; import org.qora.transaction.Transaction.ValidationResult;
@ -46,7 +47,7 @@ public class TransactionUtils {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, TransactionType txType, boolean wantValid) throws DataException { public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, TransactionType txType, boolean wantValid) throws DataException {
try { try {
Class <?> clazz = Class.forName(String.join("", org.qora.test.common.transaction.Transaction.class.getPackage().getName(), ".", txType.className, "Transaction")); Class <?> clazz = Class.forName(String.join("", TestTransaction.class.getPackage().getName(), ".", txType.className, "TestTransaction"));
try { try {
Method method = clazz.getDeclaredMethod("randomTransaction", Repository.class, PrivateKeyAccount.class, boolean.class); Method method = clazz.getDeclaredMethod("randomTransaction", Repository.class, PrivateKeyAccount.class, boolean.class);

View File

@ -0,0 +1,19 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.AccountFlagsTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class AccountFlagsTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int andMask = -1;
final int orMask = 0;
final int xorMask = 0;
return new AccountFlagsTransactionData(generateBase(account), account.getAddress(), andMask, orMask, xorMask);
}
}

View File

@ -0,0 +1,18 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.AddGroupAdminTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class AddGroupAdminTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int groupId = 1;
String member = account.getAddress();
return new AddGroupAdminTransactionData(generateBase(account), groupId, member);
}
}

View File

@ -0,0 +1,37 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.qora.account.PrivateKeyAccount;
import org.qora.asset.Asset;
import org.qora.data.PaymentData;
import org.qora.data.transaction.ArbitraryTransactionData;
import org.qora.data.transaction.ArbitraryTransactionData.DataType;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class ArbitraryTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int version = 4;
final int service = 123;
byte[] data = new byte[1024];
random.nextBytes(data);
DataType dataType = DataType.RAW_DATA;
String recipient = account.getAddress();
final long assetId = Asset.QORA;
BigDecimal amount = BigDecimal.valueOf(123L);
List<PaymentData> payments = new ArrayList<>();
payments.add(new PaymentData(recipient, assetId, amount));
return new ArbitraryTransactionData(generateBase(account), version, service, data, dataType, payments);
}
}

View File

@ -0,0 +1,28 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import org.qora.account.PrivateKeyAccount;
import org.qora.asset.Asset;
import org.qora.crypto.Crypto;
import org.qora.data.transaction.ATTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class AtTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
byte[] signature = new byte[64];
random.nextBytes(signature);
String atAddress = Crypto.toATAddress(signature);
String recipient = account.getAddress();
BigDecimal amount = BigDecimal.valueOf(123);
final long assetId = Asset.QORA;
byte[] message = new byte[32];
random.nextBytes(message);
return new ATTransactionData(generateBase(account), atAddress, recipient, amount, assetId, message);
}
}

View File

@ -0,0 +1,24 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.BuyNameTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class BuyNameTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String name = "test name";
if (!wantValid)
name += " " + random.nextInt(1_000_000);
BigDecimal amount = BigDecimal.valueOf(123);
String seller = account.getAddress();
return new BuyNameTransactionData(generateBase(account), name, amount, seller);
}
}

View File

@ -0,0 +1,21 @@
package org.qora.test.common.transaction;
import java.util.Random;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.CancelAssetOrderTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class CancelAssetOrderTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
Random random = new Random();
byte[] orderId = new byte[64];
random.nextBytes(orderId);
return new CancelAssetOrderTransactionData(generateBase(account), orderId);
}
}

View File

@ -0,0 +1,18 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.CancelGroupBanTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class CancelGroupBanTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int groupId = 1;
String member = account.getAddress();
return new CancelGroupBanTransactionData(generateBase(account), groupId, member);
}
}

View File

@ -0,0 +1,18 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.CancelGroupInviteTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class CancelGroupInviteTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int groupId = 1;
String member = account.getAddress();
return new CancelGroupInviteTransactionData(generateBase(account), groupId, member);
}
}

View File

@ -0,0 +1,17 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.CancelSellNameTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class CancelSellNameTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String name = "name-for-sale";
return new CancelSellNameTransactionData(generateBase(account), name);
}
}

View File

@ -0,0 +1,23 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import org.qora.account.PrivateKeyAccount;
import org.qora.asset.Asset;
import org.qora.data.transaction.CreateAssetOrderTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class CreateAssetOrderTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final long haveAssetId = Asset.QORA;
final long wantAssetId = 1;
BigDecimal amount = BigDecimal.valueOf(123);
BigDecimal price = BigDecimal.valueOf(123);
return new CreateAssetOrderTransactionData(generateBase(account), haveAssetId, wantAssetId, amount, price);
}
}

View File

@ -0,0 +1,28 @@
package org.qora.test.common.transaction;
import java.util.Random;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.CreateGroupTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.group.Group.ApprovalThreshold;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class CreateGroupTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
Random random = new Random();
String owner = account.getAddress();
String groupName = "test group " + random.nextInt(1_000_000);
String description = "random test group";
final boolean isOpen = false;
ApprovalThreshold approvalThreshold = ApprovalThreshold.PCT40;
final int minimumBlockDelay = 5;
final int maximumBlockDelay = 20;
return new CreateGroupTransactionData(generateBase(account), owner, groupName, description, isOpen, approvalThreshold, minimumBlockDelay, maximumBlockDelay);
}
}

View File

@ -0,0 +1,31 @@
package org.qora.test.common.transaction;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.CreatePollTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.data.voting.PollOptionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class CreatePollTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
Random random = new Random();
String owner = account.getAddress();
String pollName = "test poll " + random.nextInt(1_000_000);
String description = "Not ready reading drive A";
List<PollOptionData> pollOptions = new ArrayList<>();
pollOptions.add(new PollOptionData("Abort"));
pollOptions.add(new PollOptionData("Retry"));
pollOptions.add(new PollOptionData("Fail"));
return new CreatePollTransactionData(generateBase(account), owner, pollName, description, pollOptions);
}
}

View File

@ -0,0 +1,30 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import java.util.Random;
import org.qora.account.PrivateKeyAccount;
import org.qora.asset.Asset;
import org.qora.data.transaction.DeployAtTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class DeployAtTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
Random random = new Random();
String name = "test AT " + random.nextInt(1_000_000);
String description = "random test AT";
String atType = "random AT type";
String tags = "random AT tags";
byte[] creationBytes = new byte[1024];
random.nextBytes(creationBytes);
BigDecimal amount = BigDecimal.valueOf(123);
final long assetId = Asset.QORA;
return new DeployAtTransactionData(generateBase(account), name, description, atType, tags, creationBytes, amount, assetId);
}
}

View File

@ -0,0 +1,17 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.EnableForgingTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class EnableForgingTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String target = account.getAddress();
return new EnableForgingTransactionData(generateBase(account), target);
}
}

View File

@ -0,0 +1,20 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.GenesisTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class GenesisTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String recipient = account.getAddress();
BigDecimal amount = BigDecimal.valueOf(123);
return new GenesisTransactionData(generateBase(account), recipient, amount);
}
}

View File

@ -0,0 +1,23 @@
package org.qora.test.common.transaction;
import java.util.Random;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.GroupApprovalTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class GroupApprovalTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
Random random = new Random();
byte[] pendingSignature = new byte[64];
random.nextBytes(pendingSignature);
final boolean approval = true;
return new GroupApprovalTransactionData(generateBase(account), pendingSignature, approval);
}
}

View File

@ -0,0 +1,20 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.GroupBanTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class GroupBanTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int groupId = 1;
String member = account.getAddress();
String reason = "banned for testing";
final int timeToLive = 3600;
return new GroupBanTransactionData(generateBase(account), groupId, member, reason, timeToLive);
}
}

View File

@ -0,0 +1,19 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.GroupInviteTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class GroupInviteTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int groupId = 1;
String invitee = account.getAddress();
final int timeToLive = 3600;
return new GroupInviteTransactionData(generateBase(account), groupId, invitee, timeToLive);
}
}

View File

@ -0,0 +1,19 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.GroupKickTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class GroupKickTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int groupId = 1;
String member = account.getAddress();
String reason = "banned for testing";
return new GroupKickTransactionData(generateBase(account), groupId, member, reason);
}
}

View File

@ -0,0 +1,27 @@
package org.qora.test.common.transaction;
import java.util.Random;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.IssueAssetTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.test.common.AssetUtils;
public class IssueAssetTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
Random random = new Random();
String owner = account.getAddress();
String assetName = "test-asset-" + random.nextInt(1_000_000);
String description = "random test asset";
final long quantity = 1_000_000L;
final boolean isDivisible = true;
String data = AssetUtils.randomData();
return new IssueAssetTransactionData(generateBase(account), owner, assetName, description, quantity, isDivisible, data);
}
}

View File

@ -0,0 +1,17 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.JoinGroupTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class JoinGroupTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int groupId = 1;
return new JoinGroupTransactionData(generateBase(account), groupId);
}
}

View File

@ -0,0 +1,17 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.LeaveGroupTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class LeaveGroupTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int groupId = 1;
return new LeaveGroupTransactionData(generateBase(account), groupId);
}
}

View File

@ -0,0 +1,26 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import org.qora.account.PrivateKeyAccount;
import org.qora.asset.Asset;
import org.qora.data.transaction.MessageTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class MessageTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int version = 3;
String recipient = account.getAddress();
final long assetId = Asset.QORA;
BigDecimal amount = BigDecimal.valueOf(123L);
byte[] data = "message contents".getBytes();
final boolean isText = true;
final boolean isEncrypted = false;
return new MessageTransactionData(generateBase(account), version, recipient, assetId, amount, data, isText, isEncrypted);
}
}

View File

@ -0,0 +1,28 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.qora.account.PrivateKeyAccount;
import org.qora.asset.Asset;
import org.qora.data.PaymentData;
import org.qora.data.transaction.MultiPaymentTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class MultiPaymentTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String recipient = account.getAddress();
final long assetId = Asset.QORA;
BigDecimal amount = BigDecimal.valueOf(123L);
List<PaymentData> payments = new ArrayList<>();
payments.add(new PaymentData(recipient, assetId, amount));
return new MultiPaymentTransactionData(generateBase(account), payments);
}
}

View File

@ -0,0 +1,20 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.PaymentTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class PaymentTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String recipient = account.getAddress();
BigDecimal amount = BigDecimal.valueOf(123L);
return new PaymentTransactionData(generateBase(account), recipient, amount);
}
}

View File

@ -1,16 +0,0 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.PaymentTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.Repository;
public class PaymentTransaction extends org.qora.test.common.transaction.Transaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) {
return new PaymentTransactionData(generateBase(account), account.getAddress(), BigDecimal.valueOf(123L));
}
}

View File

@ -0,0 +1,21 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.ProxyForgingTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class ProxyForgingTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String recipient = account.getAddress();
byte[] proxyPublicKey = account.getProxyPrivateKey(account.getPublicKey());
BigDecimal share = BigDecimal.valueOf(50);
return new ProxyForgingTransactionData(generateBase(account), recipient, proxyPublicKey, share);
}
}

View File

@ -0,0 +1,22 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.RegisterNameTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class RegisterNameTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String owner = account.getAddress();
String name = "test name";
if (!wantValid)
name += " " + random.nextInt(1_000_000);
String data = "{ \"key\": \"value\" }";
return new RegisterNameTransactionData(generateBase(account), owner, name, data);
}
}

View File

@ -0,0 +1,18 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class RemoveGroupAdminTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int groupId = 1;
String admin = account.getAddress();
return new RemoveGroupAdminTransactionData(generateBase(account), groupId, admin);
}
}

View File

@ -0,0 +1,23 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.SellNameTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class SellNameTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String name = "test name";
if (!wantValid)
name += " " + random.nextInt(1_000_000);
BigDecimal amount = BigDecimal.valueOf(123);
return new SellNameTransactionData(generateBase(account), name, amount);
}
}

View File

@ -0,0 +1,17 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.SetGroupTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class SetGroupTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int defaultGroupId = 1;
return new SetGroupTransactionData(generateBase(account), defaultGroupId);
}
}

View File

@ -0,0 +1,24 @@
package org.qora.test.common.transaction;
import java.util.Random;
import org.qora.account.PrivateKeyAccount;
import org.qora.block.BlockChain;
import org.qora.data.transaction.BaseTransactionData;
import org.qora.group.Group;
import org.qora.repository.DataException;
import org.qora.utils.NTP;
public abstract class TestTransaction {
protected static final Random random = new Random();
protected static BaseTransactionData generateBase(PrivateKeyAccount account) throws DataException {
byte[] lastReference = account.getUnconfirmedLastReference();
if (lastReference == null)
lastReference = account.getLastReference();
return new BaseTransactionData(NTP.getTime(), Group.NO_GROUP, lastReference, account.getPublicKey(), BlockChain.getInstance().getUnitFee(), null);
}
}

View File

@ -1,15 +0,0 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.block.BlockChain;
import org.qora.data.transaction.BaseTransactionData;
import org.qora.group.Group;
import org.qora.utils.NTP;
public abstract class Transaction {
protected static BaseTransactionData generateBase(PrivateKeyAccount account) {
return new BaseTransactionData(NTP.getTime(), Group.NO_GROUP, new byte[32], account.getPublicKey(), BlockChain.getInstance().getUnitFee(), null);
}
}

View File

@ -0,0 +1,22 @@
package org.qora.test.common.transaction;
import java.math.BigDecimal;
import org.qora.account.PrivateKeyAccount;
import org.qora.asset.Asset;
import org.qora.data.transaction.TransferAssetTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class TransferAssetTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String recipient = account.getAddress();
final long assetId = Asset.QORA;
BigDecimal amount = BigDecimal.valueOf(123);
return new TransferAssetTransactionData(generateBase(account), recipient, amount, assetId);
}
}

View File

@ -0,0 +1,21 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.UpdateAssetTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.test.common.AssetUtils;
public class UpdateAssetTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final long assetId = 1;
String newOwner = account.getAddress();
String newDescription = "updated random test asset";
String newData = AssetUtils.randomData();
return new UpdateAssetTransactionData(generateBase(account), assetId, newOwner, newDescription, newData);
}
}

View File

@ -0,0 +1,24 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.UpdateGroupTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.group.Group.ApprovalThreshold;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class UpdateGroupTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int groupId = 1;
String newOwner = account.getAddress();
String newDescription = "updated random test group";
final boolean newIsOpen = false;
ApprovalThreshold newApprovalThreshold = ApprovalThreshold.PCT20;
final int newMinimumBlockDelay = 10;
final int newMaximumBlockDelay = 60;
return new UpdateGroupTransactionData(generateBase(account), groupId, newOwner, newDescription, newIsOpen, newApprovalThreshold, newMinimumBlockDelay, newMaximumBlockDelay);
}
}

View File

@ -0,0 +1,22 @@
package org.qora.test.common.transaction;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.UpdateNameTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class UpdateNameTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
String newOwner = account.getAddress();
String name = "test name";
if (!wantValid)
name += " " + random.nextInt(1_000_000);
String newData = "{ \"key\": \"updated value\" }";
return new UpdateNameTransactionData(generateBase(account), newOwner, name, newData);
}
}

View File

@ -0,0 +1,22 @@
package org.qora.test.common.transaction;
import java.util.Random;
import org.qora.account.PrivateKeyAccount;
import org.qora.data.transaction.VoteOnPollTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class VoteOnPollTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
Random random = new Random();
String pollName = "test poll " + random.nextInt(1_000_000);
final int optionIndex = random.nextInt(3);
return new VoteOnPollTransactionData(generateBase(account), pollName, optionIndex);
}
}