forked from Qortal/qortal
3829630b29
Converted AddressesResource to full base64, removing base58. Narrowed range of API errors returnable while there. Added support for looking up public key of address. Added support for converting public key TO address. Added API endpoint for returning a range of block signatures, to aid block explorers. Added API support for fetching unconfirmed transactions. Added API endpoint for searching transactions to meet criteria like: - participating address (only recipients supported ATM) - block height range - transaction type - result count limit/offset --- Added storage of account's public key in repository along with supporting code in AccountData and Account business object to save public key where possible.
1188 lines
48 KiB
Java
1188 lines
48 KiB
Java
package test;
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
import org.junit.jupiter.api.AfterEach;
|
|
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.math.BigDecimal;
|
|
import java.math.RoundingMode;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
import org.json.simple.JSONObject;
|
|
|
|
import com.google.common.hash.HashCode;
|
|
|
|
import data.PaymentData;
|
|
import data.account.AccountBalanceData;
|
|
import data.account.AccountData;
|
|
import data.assets.AssetData;
|
|
import data.assets.OrderData;
|
|
import data.assets.TradeData;
|
|
import data.block.BlockData;
|
|
import data.naming.NameData;
|
|
import data.transaction.BuyNameTransactionData;
|
|
import data.transaction.CancelOrderTransactionData;
|
|
import data.transaction.CancelSellNameTransactionData;
|
|
import data.transaction.CreateOrderTransactionData;
|
|
import data.transaction.CreatePollTransactionData;
|
|
import data.transaction.IssueAssetTransactionData;
|
|
import data.transaction.MessageTransactionData;
|
|
import data.transaction.MultiPaymentTransactionData;
|
|
import data.transaction.PaymentTransactionData;
|
|
import data.transaction.RegisterNameTransactionData;
|
|
import data.transaction.SellNameTransactionData;
|
|
import data.transaction.TransferAssetTransactionData;
|
|
import data.transaction.UpdateNameTransactionData;
|
|
import data.transaction.VoteOnPollTransactionData;
|
|
import data.voting.PollData;
|
|
import data.voting.PollOptionData;
|
|
import data.voting.VoteOnPollData;
|
|
import qora.account.Account;
|
|
import qora.account.PrivateKeyAccount;
|
|
import qora.account.PublicKeyAccount;
|
|
import qora.assets.Asset;
|
|
import qora.block.Block;
|
|
import qora.block.BlockChain;
|
|
import qora.transaction.BuyNameTransaction;
|
|
import qora.transaction.CancelOrderTransaction;
|
|
import qora.transaction.CancelSellNameTransaction;
|
|
import qora.transaction.CreateOrderTransaction;
|
|
import qora.transaction.CreatePollTransaction;
|
|
import qora.transaction.IssueAssetTransaction;
|
|
import qora.transaction.MessageTransaction;
|
|
import qora.transaction.MultiPaymentTransaction;
|
|
import qora.transaction.PaymentTransaction;
|
|
import qora.transaction.RegisterNameTransaction;
|
|
import qora.transaction.SellNameTransaction;
|
|
import qora.transaction.Transaction;
|
|
import qora.transaction.Transaction.ValidationResult;
|
|
import qora.transaction.TransferAssetTransaction;
|
|
import qora.transaction.UpdateNameTransaction;
|
|
import qora.transaction.VoteOnPollTransaction;
|
|
import repository.AccountRepository;
|
|
import repository.AssetRepository;
|
|
import repository.DataException;
|
|
import repository.Repository;
|
|
import repository.RepositoryFactory;
|
|
import repository.RepositoryManager;
|
|
import repository.hsqldb.HSQLDBRepositoryFactory;
|
|
import settings.Settings;
|
|
|
|
// Don't extend Common as we want to use an in-memory database
|
|
public class TransactionTests {
|
|
|
|
private static final String connectionUrl = "jdbc:hsqldb:mem:db/test;create=true;close_result=true;sql.strict_exec=true;sql.enforce_names=true;sql.syntax_mys=true";
|
|
|
|
private static final byte[] generatorSeed = HashCode.fromString("0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210").asBytes();
|
|
private static final byte[] senderSeed = HashCode.fromString("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").asBytes();
|
|
private static final byte[] recipientSeed = HashCode.fromString("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210").asBytes();
|
|
|
|
private static final BigDecimal initialGeneratorBalance = BigDecimal.valueOf(1_000_000_000L).setScale(8);
|
|
private static final BigDecimal initialSenderBalance = BigDecimal.valueOf(1_000_000L).setScale(8);
|
|
private static final BigDecimal genericPaymentAmount = BigDecimal.valueOf(1_000L).setScale(8);
|
|
|
|
private Repository repository;
|
|
private AccountRepository accountRepository;
|
|
private BlockData parentBlockData;
|
|
private PrivateKeyAccount sender;
|
|
private PrivateKeyAccount generator;
|
|
private byte[] reference;
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public void createTestAccounts(Long genesisTimestamp) throws DataException {
|
|
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
|
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
assertEquals(0, repository.getBlockRepository().getBlockchainHeight(), "Blockchain should be empty for this test");
|
|
}
|
|
|
|
// [Un]set genesis timestamp as required by test
|
|
JSONObject settingsJSON = new JSONObject();
|
|
if (genesisTimestamp != null)
|
|
settingsJSON.put("testnetstamp", genesisTimestamp);
|
|
|
|
Settings.test(settingsJSON);
|
|
|
|
// This needs to be called outside of acquiring our own repository or it will deadlock
|
|
BlockChain.validate();
|
|
|
|
// Grab repository for further use, including during test itself
|
|
repository = RepositoryManager.getRepository();
|
|
|
|
// Grab genesis block
|
|
parentBlockData = repository.getBlockRepository().fromHeight(1);
|
|
|
|
accountRepository = repository.getAccountRepository();
|
|
|
|
// Create test generator account
|
|
generator = new PrivateKeyAccount(repository, generatorSeed);
|
|
accountRepository.save(new AccountData(generator.getAddress(), generatorSeed, generator.getPublicKey()));
|
|
accountRepository.save(new AccountBalanceData(generator.getAddress(), Asset.QORA, initialGeneratorBalance));
|
|
|
|
// Create test sender account
|
|
sender = new PrivateKeyAccount(repository, senderSeed);
|
|
|
|
// Mock account
|
|
reference = senderSeed;
|
|
accountRepository.save(new AccountData(sender.getAddress(), reference, sender.getPublicKey()));
|
|
|
|
// Mock balance
|
|
accountRepository.save(new AccountBalanceData(sender.getAddress(), Asset.QORA, initialSenderBalance));
|
|
|
|
repository.saveChanges();
|
|
}
|
|
|
|
@AfterEach
|
|
public void closeRepository() throws DataException {
|
|
RepositoryManager.closeRepositoryFactory();
|
|
}
|
|
|
|
private Transaction createPayment(PrivateKeyAccount sender, String recipient) throws DataException {
|
|
// Make a new payment transaction
|
|
BigDecimal amount = genericPaymentAmount;
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), recipient, amount, fee, timestamp, reference);
|
|
|
|
Transaction paymentTransaction = new PaymentTransaction(repository, paymentTransactionData);
|
|
paymentTransaction.sign(sender);
|
|
|
|
return paymentTransaction;
|
|
}
|
|
|
|
@Test
|
|
public void testPaymentTransaction() throws DataException {
|
|
createTestAccounts(null);
|
|
|
|
// Make a new payment transaction
|
|
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
|
BigDecimal amount = BigDecimal.valueOf(1_000L);
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), recipient.getAddress(), amount, fee, timestamp,
|
|
reference);
|
|
|
|
Transaction paymentTransaction = new PaymentTransaction(repository, paymentTransactionData);
|
|
paymentTransaction.sign(sender);
|
|
assertTrue(paymentTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, paymentTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(paymentTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
BigDecimal expectedBalance = initialSenderBalance.subtract(amount).subtract(fee);
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
|
|
|
// Fee should be in generator's balance
|
|
expectedBalance = initialGeneratorBalance.add(fee);
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
|
|
|
// Amount should be in recipient's balance
|
|
expectedBalance = amount;
|
|
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Recipient's new balance incorrect");
|
|
|
|
// Check recipient's reference
|
|
byte[] recipientsReference = recipient.getLastReference();
|
|
assertTrue(Arrays.equals(paymentTransaction.getTransactionData().getSignature(), recipientsReference), "Recipient's new reference incorrect");
|
|
|
|
// Orphan block
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(initialSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
|
|
|
// Check generator's balance
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(initialGeneratorBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
|
}
|
|
|
|
@Test
|
|
public void testRegisterNameTransaction() throws DataException {
|
|
createTestAccounts(null);
|
|
|
|
// Make a new register name transaction
|
|
String name = "test name";
|
|
String data = "{\"key\":\"value\"}";
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
RegisterNameTransactionData registerNameTransactionData = new RegisterNameTransactionData(sender.getPublicKey(), sender.getAddress(), name, data, fee,
|
|
timestamp, reference);
|
|
|
|
Transaction registerNameTransaction = new RegisterNameTransaction(repository, registerNameTransactionData);
|
|
registerNameTransaction.sign(sender);
|
|
assertTrue(registerNameTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, registerNameTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(registerNameTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
|
|
|
// Fee should be in generator's balance
|
|
expectedBalance = initialGeneratorBalance.add(fee);
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
|
|
|
// Check name was registered
|
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
|
assertNotNull(actualNameData);
|
|
|
|
// Check sender's reference
|
|
assertTrue(Arrays.equals(registerNameTransactionData.getSignature(), sender.getLastReference()), "Sender's new reference incorrect");
|
|
|
|
// Update variables for use by other tests
|
|
reference = sender.getLastReference();
|
|
parentBlockData = block.getBlockData();
|
|
}
|
|
|
|
@Test
|
|
public void testUpdateNameTransaction() throws DataException {
|
|
// Register name using another test
|
|
testRegisterNameTransaction();
|
|
|
|
String name = "test name";
|
|
NameData originalNameData = this.repository.getNameRepository().fromName(name);
|
|
|
|
// Update name's owner and data
|
|
Account newOwner = new PublicKeyAccount(repository, recipientSeed);
|
|
String newData = "{\"newKey\":\"newValue\"}";
|
|
byte[] nameReference = reference;
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
UpdateNameTransactionData updateNameTransactionData = new UpdateNameTransactionData(sender.getPublicKey(), newOwner.getAddress(), name, newData,
|
|
nameReference, fee, timestamp, reference);
|
|
|
|
Transaction updateNameTransaction = new UpdateNameTransaction(repository, updateNameTransactionData);
|
|
updateNameTransaction.sign(sender);
|
|
assertTrue(updateNameTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, updateNameTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(updateNameTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check name was updated
|
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
|
assertEquals(newOwner.getAddress(), actualNameData.getOwner());
|
|
assertEquals(newData, actualNameData.getData());
|
|
|
|
// Now orphan block
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check name has been reverted correctly
|
|
actualNameData = this.repository.getNameRepository().fromName(name);
|
|
assertEquals(originalNameData.getOwner(), actualNameData.getOwner());
|
|
assertEquals(originalNameData.getData(), actualNameData.getData());
|
|
}
|
|
|
|
@Test
|
|
public void testSellNameTransaction() throws DataException {
|
|
// Register name using another test
|
|
testRegisterNameTransaction();
|
|
|
|
String name = "test name";
|
|
|
|
// Sale price
|
|
BigDecimal amount = BigDecimal.valueOf(1234L).setScale(8);
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
SellNameTransactionData sellNameTransactionData = new SellNameTransactionData(sender.getPublicKey(), name, amount, fee, timestamp, reference);
|
|
|
|
Transaction sellNameTransaction = new SellNameTransaction(repository, sellNameTransactionData);
|
|
sellNameTransaction.sign(sender);
|
|
assertTrue(sellNameTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, sellNameTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(sellNameTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check name was updated
|
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
|
assertTrue(actualNameData.getIsForSale());
|
|
assertEquals(amount, actualNameData.getSalePrice());
|
|
|
|
// Now orphan block
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check name has been reverted correctly
|
|
actualNameData = this.repository.getNameRepository().fromName(name);
|
|
assertFalse(actualNameData.getIsForSale());
|
|
assertNull(actualNameData.getSalePrice());
|
|
|
|
// Re-process block for use by other tests
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Update variables for use by other tests
|
|
reference = sender.getLastReference();
|
|
parentBlockData = block.getBlockData();
|
|
}
|
|
|
|
@Test
|
|
public void testCancelSellNameTransaction() throws DataException {
|
|
// Register and sell name using another test
|
|
testSellNameTransaction();
|
|
|
|
String name = "test name";
|
|
NameData originalNameData = this.repository.getNameRepository().fromName(name);
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
CancelSellNameTransactionData cancelSellNameTransactionData = new CancelSellNameTransactionData(sender.getPublicKey(), name, fee, timestamp, reference);
|
|
|
|
Transaction cancelSellNameTransaction = new CancelSellNameTransaction(repository, cancelSellNameTransactionData);
|
|
cancelSellNameTransaction.sign(sender);
|
|
assertTrue(cancelSellNameTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, cancelSellNameTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(cancelSellNameTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check name was updated
|
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
|
assertFalse(actualNameData.getIsForSale());
|
|
assertEquals(originalNameData.getSalePrice(), actualNameData.getSalePrice());
|
|
|
|
// Now orphan block
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check name has been reverted correctly
|
|
actualNameData = this.repository.getNameRepository().fromName(name);
|
|
assertTrue(actualNameData.getIsForSale());
|
|
assertEquals(originalNameData.getSalePrice(), actualNameData.getSalePrice());
|
|
|
|
// Update variables for use by other tests
|
|
reference = sender.getLastReference();
|
|
parentBlockData = block.getBlockData();
|
|
}
|
|
|
|
@Test
|
|
public void testBuyNameTransaction() throws DataException {
|
|
// Register and sell name using another test
|
|
testSellNameTransaction();
|
|
|
|
String name = "test name";
|
|
NameData originalNameData = this.repository.getNameRepository().fromName(name);
|
|
String seller = originalNameData.getOwner();
|
|
|
|
// Buyer
|
|
PrivateKeyAccount buyer = new PrivateKeyAccount(repository, recipientSeed);
|
|
byte[] nameReference = reference;
|
|
|
|
// Send buyer some funds so they have a reference
|
|
Transaction somePaymentTransaction = createPayment(sender, buyer.getAddress());
|
|
byte[] buyersReference = somePaymentTransaction.getTransactionData().getSignature();
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(somePaymentTransaction.getTransactionData());
|
|
block.sign();
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
parentBlockData = block.getBlockData();
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
BuyNameTransactionData buyNameTransactionData = new BuyNameTransactionData(buyer.getPublicKey(), name, originalNameData.getSalePrice(), seller,
|
|
nameReference, fee, timestamp, buyersReference);
|
|
|
|
Transaction buyNameTransaction = new BuyNameTransaction(repository, buyNameTransactionData);
|
|
buyNameTransaction.sign(buyer);
|
|
assertTrue(buyNameTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, buyNameTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(buyNameTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check name was updated
|
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
|
assertFalse(actualNameData.getIsForSale());
|
|
assertEquals(originalNameData.getSalePrice(), actualNameData.getSalePrice());
|
|
assertEquals(buyer.getAddress(), actualNameData.getOwner());
|
|
|
|
// Now orphan block
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check name has been reverted correctly
|
|
actualNameData = this.repository.getNameRepository().fromName(name);
|
|
assertTrue(actualNameData.getIsForSale());
|
|
assertEquals(originalNameData.getSalePrice(), actualNameData.getSalePrice());
|
|
assertEquals(originalNameData.getOwner(), actualNameData.getOwner());
|
|
}
|
|
|
|
@Test
|
|
public void testCreatePollTransaction() throws DataException {
|
|
// This test requires GenesisBlock's timestamp is set to something after BlockChain.VOTING_RELEASE_TIMESTAMP
|
|
createTestAccounts(BlockChain.getVotingReleaseTimestamp() + 1_000L);
|
|
|
|
// Make a new create poll transaction
|
|
String pollName = "test poll";
|
|
String description = "test poll description";
|
|
|
|
List<PollOptionData> pollOptions = new ArrayList<PollOptionData>();
|
|
pollOptions.add(new PollOptionData("abort"));
|
|
pollOptions.add(new PollOptionData("retry"));
|
|
pollOptions.add(new PollOptionData("fail"));
|
|
|
|
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
CreatePollTransactionData createPollTransactionData = new CreatePollTransactionData(sender.getPublicKey(), recipient.getAddress(), pollName,
|
|
description, pollOptions, fee, timestamp, reference);
|
|
|
|
Transaction createPollTransaction = new CreatePollTransaction(repository, createPollTransactionData);
|
|
createPollTransaction.sign(sender);
|
|
assertTrue(createPollTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, createPollTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(createPollTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
|
|
|
// Fee should be in generator's balance
|
|
expectedBalance = initialGeneratorBalance.add(fee);
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
|
|
|
// Check poll was created
|
|
PollData actualPollData = this.repository.getVotingRepository().fromPollName(pollName);
|
|
assertNotNull(actualPollData);
|
|
|
|
// Check sender's reference
|
|
assertTrue(Arrays.equals(createPollTransactionData.getSignature(), sender.getLastReference()), "Sender's new reference incorrect");
|
|
|
|
// Update variables for use by other tests
|
|
reference = sender.getLastReference();
|
|
parentBlockData = block.getBlockData();
|
|
}
|
|
|
|
@Test
|
|
public void testVoteOnPollTransaction() throws DataException {
|
|
// Create poll using another test
|
|
testCreatePollTransaction();
|
|
|
|
// Try all options, plus invalid optionIndex (note use of <= for this)
|
|
String pollName = "test poll";
|
|
int pollOptionsSize = 3;
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
for (int optionIndex = 0; optionIndex <= pollOptionsSize; ++optionIndex) {
|
|
// Make a vote-on-poll transaction
|
|
VoteOnPollTransactionData voteOnPollTransactionData = new VoteOnPollTransactionData(sender.getPublicKey(), pollName, optionIndex, fee, timestamp,
|
|
reference);
|
|
|
|
Transaction voteOnPollTransaction = new VoteOnPollTransaction(repository, voteOnPollTransactionData);
|
|
voteOnPollTransaction.sign(sender);
|
|
assertTrue(voteOnPollTransaction.isSignatureValid());
|
|
|
|
if (optionIndex == pollOptionsSize) {
|
|
assertEquals(ValidationResult.POLL_OPTION_DOES_NOT_EXIST, voteOnPollTransaction.isValid());
|
|
break;
|
|
}
|
|
assertEquals(ValidationResult.OK, voteOnPollTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(voteOnPollTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check vote was registered properly
|
|
VoteOnPollData actualVoteOnPollData = repository.getVotingRepository().getVote(pollName, sender.getPublicKey());
|
|
assertNotNull(actualVoteOnPollData);
|
|
assertEquals(optionIndex, actualVoteOnPollData.getOptionIndex());
|
|
|
|
// update variables for next round
|
|
parentBlockData = block.getBlockData();
|
|
timestamp += 1_000;
|
|
reference = voteOnPollTransaction.getTransactionData().getSignature();
|
|
}
|
|
|
|
// Check poll's votes
|
|
List<VoteOnPollData> votes = repository.getVotingRepository().getVotes(pollName);
|
|
assertNotNull(votes);
|
|
|
|
assertEquals(1, votes.size(), "Only one vote expected");
|
|
|
|
assertEquals(pollOptionsSize - 1, votes.get(0).getOptionIndex(), "Wrong vote option index");
|
|
assertTrue(Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey()), "Wrong voter public key");
|
|
|
|
// Orphan last block
|
|
BlockData lastBlockData = repository.getBlockRepository().getLastBlock();
|
|
Block lastBlock = new Block(repository, lastBlockData);
|
|
lastBlock.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Re-check poll's votes
|
|
votes = repository.getVotingRepository().getVotes(pollName);
|
|
assertNotNull(votes);
|
|
|
|
assertEquals(1, votes.size(), "Only one vote expected");
|
|
|
|
assertEquals(pollOptionsSize - 1 - 1, votes.get(0).getOptionIndex(), "Wrong vote option index");
|
|
assertTrue(Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey()), "Wrong voter public key");
|
|
}
|
|
|
|
@Test
|
|
public void testIssueAssetTransaction() throws DataException {
|
|
createTestAccounts(null);
|
|
|
|
// Create new asset
|
|
String assetName = "test asset";
|
|
String description = "test asset description";
|
|
long quantity = 1_000_000L;
|
|
boolean isDivisible = true;
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
IssueAssetTransactionData issueAssetTransactionData = new IssueAssetTransactionData(sender.getPublicKey(), sender.getAddress(), assetName, description,
|
|
quantity, isDivisible, fee, timestamp, reference);
|
|
|
|
Transaction issueAssetTransaction = new IssueAssetTransaction(repository, issueAssetTransactionData);
|
|
issueAssetTransaction.sign(sender);
|
|
assertTrue(issueAssetTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, issueAssetTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(issueAssetTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
|
|
|
// Fee should be in generator's balance
|
|
expectedBalance = initialGeneratorBalance.add(fee);
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
|
|
|
// Check we now have an assetId
|
|
Long assetId = issueAssetTransactionData.getAssetId();
|
|
assertNotNull(assetId);
|
|
// Should NOT collide with Asset.QORA
|
|
assertFalse(assetId == Asset.QORA);
|
|
|
|
// Check asset now exists
|
|
AssetRepository assetRepo = this.repository.getAssetRepository();
|
|
assertTrue(assetRepo.assetExists(assetId));
|
|
assertTrue(assetRepo.assetExists(assetName));
|
|
// Check asset data
|
|
AssetData assetData = assetRepo.fromAssetId(assetId);
|
|
assertNotNull(assetData);
|
|
assertEquals(assetName, assetData.getName());
|
|
assertEquals(description, assetData.getDescription());
|
|
|
|
// Orphan block
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(initialSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
|
|
|
// Check generator's balance
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(initialGeneratorBalance.compareTo(actualBalance) == 0, "Generator's reverted balance incorrect");
|
|
|
|
// Check asset no longer exists
|
|
assertFalse(assetRepo.assetExists(assetId));
|
|
assertFalse(assetRepo.assetExists(assetName));
|
|
assetData = assetRepo.fromAssetId(assetId);
|
|
assertNull(assetData);
|
|
|
|
// Re-process block for use by other tests
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Update variables for use by other tests
|
|
reference = sender.getLastReference();
|
|
parentBlockData = block.getBlockData();
|
|
}
|
|
|
|
@Test
|
|
public void testTransferAssetTransaction() throws DataException {
|
|
// Issue asset using another test
|
|
testIssueAssetTransaction();
|
|
|
|
String assetName = "test asset";
|
|
AssetRepository assetRepo = this.repository.getAssetRepository();
|
|
AssetData originalAssetData = assetRepo.fromAssetName(assetName);
|
|
long assetId = originalAssetData.getAssetId();
|
|
BigDecimal originalSenderBalance = sender.getConfirmedBalance(Asset.QORA);
|
|
BigDecimal originalGeneratorBalance = generator.getConfirmedBalance(Asset.QORA);
|
|
|
|
// Transfer asset to new recipient
|
|
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
|
BigDecimal amount = BigDecimal.valueOf(1_000L).setScale(8);
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
TransferAssetTransactionData transferAssetTransactionData = new TransferAssetTransactionData(sender.getPublicKey(), recipient.getAddress(), amount,
|
|
assetId, fee, timestamp, reference);
|
|
|
|
Transaction transferAssetTransaction = new TransferAssetTransaction(repository, transferAssetTransactionData);
|
|
transferAssetTransaction.sign(sender);
|
|
assertTrue(transferAssetTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, transferAssetTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(transferAssetTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
BigDecimal expectedBalance = originalSenderBalance.subtract(fee);
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
|
|
|
// Fee should be in generator's balance
|
|
expectedBalance = originalGeneratorBalance.add(fee);
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
|
|
|
// Check asset balances
|
|
BigDecimal actualSenderAssetBalance = sender.getConfirmedBalance(assetId);
|
|
assertNotNull(actualSenderAssetBalance);
|
|
BigDecimal expectedSenderAssetBalance = BigDecimal.valueOf(originalAssetData.getQuantity()).setScale(8).subtract(amount);
|
|
assertEquals(expectedSenderAssetBalance, actualSenderAssetBalance);
|
|
|
|
BigDecimal actualRecipientAssetBalance = recipient.getConfirmedBalance(assetId);
|
|
assertNotNull(actualRecipientAssetBalance);
|
|
assertEquals(amount, actualRecipientAssetBalance);
|
|
|
|
// Orphan block
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(originalSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
|
|
|
// Check generator's balance
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(originalGeneratorBalance.compareTo(actualBalance) == 0, "Generator's reverted balance incorrect");
|
|
|
|
// Check asset balances
|
|
actualSenderAssetBalance = sender.getConfirmedBalance(assetId);
|
|
assertNotNull(actualSenderAssetBalance);
|
|
expectedSenderAssetBalance = BigDecimal.valueOf(originalAssetData.getQuantity()).setScale(8);
|
|
assertEquals(expectedSenderAssetBalance, actualSenderAssetBalance);
|
|
|
|
actualRecipientAssetBalance = recipient.getConfirmedBalance(assetId);
|
|
if (actualRecipientAssetBalance != null)
|
|
assertEquals(BigDecimal.ZERO.setScale(8), actualRecipientAssetBalance);
|
|
|
|
// Re-process block for use by other tests
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Update variables for use by other tests
|
|
reference = sender.getLastReference();
|
|
parentBlockData = block.getBlockData();
|
|
}
|
|
|
|
@Test
|
|
public void testCreateAssetOrderTransaction() throws DataException {
|
|
// Issue asset using another test
|
|
testIssueAssetTransaction();
|
|
|
|
// Asset info
|
|
String assetName = "test asset";
|
|
AssetRepository assetRepo = this.repository.getAssetRepository();
|
|
AssetData originalAssetData = assetRepo.fromAssetName(assetName);
|
|
long assetId = originalAssetData.getAssetId();
|
|
|
|
// Buyer
|
|
PrivateKeyAccount buyer = new PrivateKeyAccount(repository, recipientSeed);
|
|
|
|
// Send buyer some funds so they have a reference
|
|
Transaction somePaymentTransaction = createPayment(sender, buyer.getAddress());
|
|
byte[] buyersReference = somePaymentTransaction.getTransactionData().getSignature();
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(somePaymentTransaction.getTransactionData());
|
|
block.sign();
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
parentBlockData = block.getBlockData();
|
|
|
|
// Order: buyer has 10 QORA and wants to buy "test asset" at a price of 50 "test asset" per QORA.
|
|
long haveAssetId = Asset.QORA;
|
|
BigDecimal amount = BigDecimal.valueOf(10).setScale(8);
|
|
long wantAssetId = assetId;
|
|
BigDecimal price = BigDecimal.valueOf(50).setScale(8);
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
CreateOrderTransactionData createOrderTransactionData = new CreateOrderTransactionData(buyer.getPublicKey(), haveAssetId, wantAssetId, amount, price,
|
|
fee, timestamp, buyersReference);
|
|
Transaction createOrderTransaction = new CreateOrderTransaction(this.repository, createOrderTransactionData);
|
|
createOrderTransaction.sign(buyer);
|
|
assertTrue(createOrderTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, createOrderTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(createOrderTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check order was created
|
|
byte[] orderId = createOrderTransactionData.getSignature();
|
|
OrderData orderData = assetRepo.fromOrderId(orderId);
|
|
assertNotNull(orderData);
|
|
|
|
// Check buyer's balance reduced
|
|
BigDecimal expectedBalance = genericPaymentAmount.subtract(amount).subtract(fee);
|
|
BigDecimal actualBalance = buyer.getConfirmedBalance(haveAssetId);
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
// Orphan transaction
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check order no longer exists
|
|
orderData = assetRepo.fromOrderId(orderId);
|
|
assertNull(orderData);
|
|
|
|
// Check buyer's balance restored
|
|
expectedBalance = genericPaymentAmount;
|
|
actualBalance = buyer.getConfirmedBalance(haveAssetId);
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
// Re-process to allow use by other tests
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Update variables for use by other tests
|
|
reference = sender.getLastReference();
|
|
parentBlockData = block.getBlockData();
|
|
}
|
|
|
|
@Test
|
|
public void testCancelAssetOrderTransaction() throws DataException {
|
|
// Issue asset and create order using another test
|
|
testCreateAssetOrderTransaction();
|
|
|
|
// Asset info
|
|
String assetName = "test asset";
|
|
AssetRepository assetRepo = this.repository.getAssetRepository();
|
|
AssetData originalAssetData = assetRepo.fromAssetName(assetName);
|
|
long assetId = originalAssetData.getAssetId();
|
|
|
|
// Buyer
|
|
PrivateKeyAccount buyer = new PrivateKeyAccount(repository, recipientSeed);
|
|
|
|
// Fetch orders
|
|
long haveAssetId = Asset.QORA;
|
|
long wantAssetId = assetId;
|
|
List<OrderData> orders = assetRepo.getOpenOrders(haveAssetId, wantAssetId);
|
|
|
|
assertNotNull(orders);
|
|
assertEquals(1, orders.size());
|
|
|
|
OrderData originalOrderData = orders.get(0);
|
|
assertNotNull(originalOrderData);
|
|
assertFalse(originalOrderData.getIsClosed());
|
|
|
|
// Create cancel order transaction
|
|
byte[] orderId = originalOrderData.getOrderId();
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
byte[] buyersReference = buyer.getLastReference();
|
|
CancelOrderTransactionData cancelOrderTransactionData = new CancelOrderTransactionData(buyer.getPublicKey(), orderId, fee, timestamp, buyersReference);
|
|
|
|
Transaction cancelOrderTransaction = new CancelOrderTransaction(this.repository, cancelOrderTransactionData);
|
|
cancelOrderTransaction.sign(buyer);
|
|
assertTrue(cancelOrderTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, cancelOrderTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(cancelOrderTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check order is marked as cancelled
|
|
OrderData cancelledOrderData = assetRepo.fromOrderId(orderId);
|
|
assertNotNull(cancelledOrderData);
|
|
assertTrue(cancelledOrderData.getIsClosed());
|
|
|
|
// Orphan
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check order is no longer marked as cancelled
|
|
OrderData uncancelledOrderData = assetRepo.fromOrderId(orderId);
|
|
assertNotNull(uncancelledOrderData);
|
|
assertFalse(uncancelledOrderData.getIsClosed());
|
|
|
|
}
|
|
|
|
@Test
|
|
public void testMatchingCreateAssetOrderTransaction() throws DataException {
|
|
// Issue asset and create order using another test
|
|
testCreateAssetOrderTransaction();
|
|
|
|
// Asset info
|
|
String assetName = "test asset";
|
|
AssetRepository assetRepo = this.repository.getAssetRepository();
|
|
AssetData originalAssetData = assetRepo.fromAssetName(assetName);
|
|
long assetId = originalAssetData.getAssetId();
|
|
|
|
// Buyer
|
|
PrivateKeyAccount buyer = new PrivateKeyAccount(repository, recipientSeed);
|
|
|
|
// Fetch orders
|
|
long originalHaveAssetId = Asset.QORA;
|
|
long originalWantAssetId = assetId;
|
|
List<OrderData> orders = assetRepo.getOpenOrders(originalHaveAssetId, originalWantAssetId);
|
|
|
|
assertNotNull(orders);
|
|
assertEquals(1, orders.size());
|
|
|
|
OrderData originalOrderData = orders.get(0);
|
|
assertNotNull(originalOrderData);
|
|
assertFalse(originalOrderData.getIsClosed());
|
|
|
|
// Unfulfilled order: "buyer" has 10 QORA and wants to buy "test asset" at a price of 50 "test asset" per QORA.
|
|
// buyer's order: have=QORA, amount=10, want=test-asset, price=50 (test-asset per QORA, so max return is 500 test-asset)
|
|
|
|
// Original asset owner (sender) will sell asset to "buyer"
|
|
|
|
// Order: seller has 40 "test asset" and wants to buy QORA at a price of 1/60 QORA per "test asset".
|
|
// This order should be a partial match for original order, and at a better price than asked
|
|
long haveAssetId = assetId;
|
|
BigDecimal amount = BigDecimal.valueOf(40).setScale(8);
|
|
long wantAssetId = Asset.QORA;
|
|
BigDecimal price = BigDecimal.ONE.setScale(8).divide(BigDecimal.valueOf(60).setScale(8), RoundingMode.DOWN);
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
BigDecimal senderPreTradeWantBalance = sender.getConfirmedBalance(wantAssetId);
|
|
|
|
CreateOrderTransactionData createOrderTransactionData = new CreateOrderTransactionData(sender.getPublicKey(), haveAssetId, wantAssetId, amount, price,
|
|
fee, timestamp, reference);
|
|
Transaction createOrderTransaction = new CreateOrderTransaction(this.repository, createOrderTransactionData);
|
|
createOrderTransaction.sign(sender);
|
|
assertTrue(createOrderTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, createOrderTransaction.isValid());
|
|
|
|
// Forge new block with transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(createOrderTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check order was created
|
|
byte[] orderId = createOrderTransactionData.getSignature();
|
|
OrderData orderData = assetRepo.fromOrderId(orderId);
|
|
assertNotNull(orderData);
|
|
|
|
// Check order has trades
|
|
List<TradeData> trades = assetRepo.getOrdersTrades(orderId);
|
|
assertNotNull(trades);
|
|
assertEquals(1, trades.size(), "Trade didn't happen");
|
|
TradeData tradeData = trades.get(0);
|
|
|
|
// Check trade has correct values
|
|
BigDecimal expectedAmount = amount.divide(originalOrderData.getPrice()).setScale(8);
|
|
BigDecimal actualAmount = tradeData.getAmount();
|
|
assertTrue(expectedAmount.compareTo(actualAmount) == 0);
|
|
|
|
BigDecimal expectedPrice = amount;
|
|
BigDecimal actualPrice = tradeData.getPrice();
|
|
assertTrue(expectedPrice.compareTo(actualPrice) == 0);
|
|
|
|
// Check seller's "test asset" balance
|
|
BigDecimal expectedBalance = BigDecimal.valueOf(1_000_000L).setScale(8).subtract(amount);
|
|
BigDecimal actualBalance = sender.getConfirmedBalance(haveAssetId);
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
// Check buyer's "test asset" balance
|
|
expectedBalance = amount;
|
|
actualBalance = buyer.getConfirmedBalance(haveAssetId);
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
// Check seller's QORA balance
|
|
expectedBalance = senderPreTradeWantBalance.subtract(BigDecimal.ONE).add(expectedAmount);
|
|
actualBalance = sender.getConfirmedBalance(wantAssetId);
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
// Check seller's order is correctly fulfilled
|
|
assertTrue(orderData.getIsFulfilled());
|
|
|
|
// Check buyer's order is still not fulfilled
|
|
OrderData buyersOrderData = assetRepo.fromOrderId(originalOrderData.getOrderId());
|
|
assertFalse(buyersOrderData.getIsFulfilled());
|
|
|
|
// Orphan transaction
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check order no longer exists
|
|
orderData = assetRepo.fromOrderId(orderId);
|
|
assertNull(orderData);
|
|
|
|
// Check trades no longer exist
|
|
trades = assetRepo.getOrdersTrades(orderId);
|
|
assertNotNull(trades);
|
|
assertEquals(0, trades.size());
|
|
|
|
// Check seller's "test asset" balance restored
|
|
expectedBalance = BigDecimal.valueOf(1_000_000L).setScale(8);
|
|
actualBalance = sender.getConfirmedBalance(haveAssetId);
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
// Check buyer's "test asset" balance restored
|
|
expectedBalance = BigDecimal.ZERO.setScale(8);
|
|
actualBalance = buyer.getConfirmedBalance(haveAssetId);
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
}
|
|
|
|
@Test
|
|
public void testMultiPaymentTransaction() throws DataException {
|
|
createTestAccounts(null);
|
|
|
|
// Make a new multi-payment transaction
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
// Payments
|
|
BigDecimal expectedSenderBalance = initialSenderBalance.subtract(fee);
|
|
List<PaymentData> payments = new ArrayList<PaymentData>();
|
|
for (int i = 0; i < 5; ++i) {
|
|
byte[] seed = recipientSeed.clone();
|
|
seed[0] += i;
|
|
Account recipient = new PublicKeyAccount(repository, seed);
|
|
long assetId = Asset.QORA;
|
|
|
|
BigDecimal amount = BigDecimal.valueOf(1_000L + i).setScale(8);
|
|
expectedSenderBalance = expectedSenderBalance.subtract(amount);
|
|
|
|
PaymentData paymentData = new PaymentData(recipient.getAddress(), assetId, amount);
|
|
|
|
payments.add(paymentData);
|
|
}
|
|
|
|
MultiPaymentTransactionData multiPaymentTransactionData = new MultiPaymentTransactionData(sender.getPublicKey(), payments, fee, timestamp, reference);
|
|
|
|
Transaction multiPaymentTransaction = new MultiPaymentTransaction(repository, multiPaymentTransactionData);
|
|
multiPaymentTransaction.sign(sender);
|
|
assertTrue(multiPaymentTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, multiPaymentTransaction.isValid());
|
|
|
|
// Forge new block with payment transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(multiPaymentTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedSenderBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
|
|
|
// Fee should be in generator's balance
|
|
BigDecimal expectedBalance = initialGeneratorBalance.add(fee);
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
|
|
|
// Check recipients
|
|
for (int i = 0; i < payments.size(); ++i) {
|
|
PaymentData paymentData = payments.get(i);
|
|
Account recipient = new Account(this.repository, paymentData.getRecipient());
|
|
|
|
byte[] recipientsReference = recipient.getLastReference();
|
|
assertTrue(Arrays.equals(multiPaymentTransaction.getTransactionData().getSignature(), recipientsReference), "Recipient's new reference incorrect");
|
|
|
|
// Amount should be in recipient's balance
|
|
expectedBalance = paymentData.getAmount();
|
|
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Recipient's new balance incorrect");
|
|
|
|
}
|
|
|
|
// Orphan block
|
|
block.orphan();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(initialSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
|
|
|
// Check generator's balance
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(initialGeneratorBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
|
}
|
|
|
|
@Test
|
|
public void testMessageTransaction() throws DataException, UnsupportedEncodingException {
|
|
createTestAccounts(1431861220336L); // timestamp taken from main blockchain block 99000
|
|
|
|
// Make a new message transaction
|
|
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
|
BigDecimal amount = BigDecimal.valueOf(1_000L);
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
int version = Transaction.getVersionByTimestamp(timestamp);
|
|
byte[] data = "test".getBytes("UTF-8");
|
|
boolean isText = true;
|
|
boolean isEncrypted = false;
|
|
|
|
MessageTransactionData messageTransactionData = new MessageTransactionData(version, sender.getPublicKey(), recipient.getAddress(), Asset.QORA, amount,
|
|
data, isText, isEncrypted, fee, timestamp, reference);
|
|
|
|
Transaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
|
|
messageTransaction.sign(sender);
|
|
assertTrue(messageTransaction.isSignatureValid());
|
|
assertEquals(ValidationResult.OK, messageTransaction.isValid());
|
|
|
|
// Forge new block with message transaction
|
|
Block block = new Block(repository, parentBlockData, generator);
|
|
block.addTransaction(messageTransactionData);
|
|
block.sign();
|
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
|
|
|
block.process();
|
|
repository.saveChanges();
|
|
|
|
// Check sender's balance
|
|
BigDecimal expectedBalance = initialSenderBalance.subtract(amount).subtract(fee);
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
|
|
|
// Fee should be in generator's balance
|
|
expectedBalance = initialGeneratorBalance.add(fee);
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
|
|
|
// Amount should be in recipient's balance
|
|
expectedBalance = amount;
|
|
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Recipient's new balance incorrect");
|
|
}
|
|
|
|
} |