Work on Polls

Added CreatePollTransactionData constructor that doesn't need signature (for creating new transactions).

Added missing "published" timestamp support to PollData and Poll.

Removed extraneous timestamp test from Block.isValid.

Corrected inconsistent column names in poll-related tables in HSQLDBDatabaseUpdates.

HSQLDBRepository.checkedExecute(PreparedStatement) now takes extra Object... params
so that values can be bound to placeholders. Code moved from checkedExecute(String, Object...).
This fixes an issue with exists(String, String, Object...) where placeholders weren't having
any values bound to them. (Also removed "ORDER BY NULL" clause which isn't supported by HSQLDB).

HSQLDBVotingRepository method stubs fleshed out with real code.

TransactionTests rejigged but more work needed to test various transactions before/after their feature
release. e.g. testing create poll transactions before & after they supposedly went live.

Could do with a GenesisBlock constructor that takes a timestamp for testing purposes?

CreatePollTransactionTransformer now skips serializing a null signature,
in the same way PaymentTransactionTransformer does, to aid getBytesLessSignature().
This will probably need to be rolled out to all other transaction types.
This commit is contained in:
catbref
2018-06-21 12:38:45 +01:00
parent 795da06505
commit 05e0fd92b9
10 changed files with 267 additions and 88 deletions

View File

@@ -3,8 +3,12 @@ package test;
import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -13,13 +17,17 @@ import com.google.common.hash.HashCode;
import data.account.AccountBalanceData;
import data.account.AccountData;
import data.block.BlockData;
import data.transaction.CreatePollTransactionData;
import data.transaction.PaymentTransactionData;
import data.voting.PollData;
import data.voting.PollOptionData;
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.CreatePollTransaction;
import qora.transaction.PaymentTransaction;
import qora.transaction.Transaction;
import qora.transaction.Transaction.ValidationResult;
@@ -39,19 +47,21 @@ public class TransactionTests {
private static final byte[] senderSeed = HashCode.fromString("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").asBytes();
private static final byte[] recipientSeed = HashCode.fromString("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210").asBytes();
@BeforeClass
public static void setRepository() throws DataException {
private static final BigDecimal generatorBalance = BigDecimal.valueOf(1_000_000_000L);
private static final BigDecimal senderBalance = BigDecimal.valueOf(1_000_000L);
private Repository repository;
private AccountRepository accountRepository;
private BlockData genesisBlockData;
private PrivateKeyAccount sender;
private PrivateKeyAccount generator;
private byte[] reference;
@Before
public void createTestAccounts() throws DataException {
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
RepositoryManager.setRepositoryFactory(repositoryFactory);
}
@AfterClass
public static void closeRepository() throws DataException {
RepositoryManager.closeRepositoryFactory();
}
@Test
public void testPaymentTransactions() throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
assertEquals("Blockchain should be empty for this test", 0, repository.getBlockRepository().getBlockchainHeight());
}
@@ -59,70 +69,127 @@ public class TransactionTests {
// This needs to be called outside of acquiring our own repository or it will deadlock
BlockChain.validate();
try (final Repository repository = RepositoryManager.getRepository()) {
// Grab genesis block
BlockData genesisBlockData = repository.getBlockRepository().fromHeight(1);
// Grab repository for further use, including during test itself
repository = RepositoryManager.getRepository();
AccountRepository accountRepository = repository.getAccountRepository();
// Grab genesis block
genesisBlockData = repository.getBlockRepository().fromHeight(1);
// Create test generator account
BigDecimal generatorBalance = BigDecimal.valueOf(1_000_000_000L);
PrivateKeyAccount generator = new PrivateKeyAccount(repository, generatorSeed);
accountRepository.save(new AccountData(generator.getAddress(), generatorSeed));
accountRepository.save(new AccountBalanceData(generator.getAddress(), Asset.QORA, generatorBalance));
accountRepository = repository.getAccountRepository();
// Create test sender account
PrivateKeyAccount sender = new PrivateKeyAccount(repository, senderSeed);
// Create test generator account
generator = new PrivateKeyAccount(repository, generatorSeed);
accountRepository.save(new AccountData(generator.getAddress(), generatorSeed));
accountRepository.save(new AccountBalanceData(generator.getAddress(), Asset.QORA, generatorBalance));
// Mock account
byte[] reference = senderSeed;
accountRepository.save(new AccountData(sender.getAddress(), reference));
// Create test sender account
sender = new PrivateKeyAccount(repository, senderSeed);
// Mock balance
BigDecimal initialBalance = BigDecimal.valueOf(1_000_000L);
accountRepository.save(new AccountBalanceData(sender.getAddress(), Asset.QORA, initialBalance));
// Mock account
reference = senderSeed;
accountRepository.save(new AccountData(sender.getAddress(), reference));
repository.saveChanges();
// Mock balance
accountRepository.save(new AccountBalanceData(sender.getAddress(), Asset.QORA, senderBalance));
// Make a new payment transaction
Account recipient = new PublicKeyAccount(repository, recipientSeed);
BigDecimal amount = BigDecimal.valueOf(1_000L);
BigDecimal fee = BigDecimal.ONE;
long timestamp = genesisBlockData.getTimestamp() + 1_000;
PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), recipient.getAddress(), amount, fee, timestamp,
reference);
repository.saveChanges();
}
Transaction paymentTransaction = new PaymentTransaction(repository, paymentTransactionData);
paymentTransaction.calcSignature(sender);
assertTrue(paymentTransaction.isSignatureValid());
assertEquals(ValidationResult.OK, paymentTransaction.isValid());
@After
public void closeRepository() throws DataException {
RepositoryManager.closeRepositoryFactory();
}
// Forge new block with payment transaction
Block block = new Block(repository, genesisBlockData, generator, null, null);
block.addTransaction(paymentTransactionData);
block.sign();
@Test
public void testPaymentTransaction() throws DataException {
// Make a new payment transaction
Account recipient = new PublicKeyAccount(repository, recipientSeed);
BigDecimal amount = BigDecimal.valueOf(1_000L);
BigDecimal fee = BigDecimal.ONE;
long timestamp = genesisBlockData.getTimestamp() + 1_000;
PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), recipient.getAddress(), amount, fee, timestamp,
reference);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
Transaction paymentTransaction = new PaymentTransaction(repository, paymentTransactionData);
paymentTransaction.calcSignature(sender);
assertTrue(paymentTransaction.isSignatureValid());
assertEquals(ValidationResult.OK, paymentTransaction.isValid());
block.process();
repository.saveChanges();
// Forge new block with payment transaction
Block block = new Block(repository, genesisBlockData, generator, null, null);
block.addTransaction(paymentTransactionData);
block.sign();
// Check sender's balance
BigDecimal expectedBalance = initialBalance.subtract(amount).subtract(fee);
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
// Fee should be in generator's balance
expectedBalance = generatorBalance.add(fee);
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
block.process();
repository.saveChanges();
// Amount should be in recipient's balance
expectedBalance = amount;
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
assertTrue("Recipient's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
}
// Check sender's balance
BigDecimal expectedBalance = senderBalance.subtract(amount).subtract(fee);
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Fee should be in generator's balance
expectedBalance = generatorBalance.add(fee);
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Amount should be in recipient's balance
expectedBalance = amount;
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
assertTrue("Recipient's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
}
@Test
public void testCreatePollTransaction() throws DataException {
// XXX This test fails unless GenesisBlock's timestamp is set to something after BlockChain.VOTING_RELEASE_TIMESTAMP so we need better testing setup
// 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 = genesisBlockData.getTimestamp() + 1_000;
CreatePollTransactionData createPollTransactionData = new CreatePollTransactionData(sender.getPublicKey(), recipient.getAddress(), pollName,
description, pollOptions, fee, timestamp, reference);
Transaction createPollTransaction = new CreatePollTransaction(repository, createPollTransactionData);
createPollTransaction.calcSignature(sender);
assertTrue(createPollTransaction.isSignatureValid());
assertEquals(ValidationResult.OK, createPollTransaction.isValid());
// Forge new block with payment transaction
Block block = new Block(repository, genesisBlockData, generator, null, null);
block.addTransaction(createPollTransactionData);
block.sign();
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
block.process();
repository.saveChanges();
// Check sender's balance
BigDecimal expectedBalance = senderBalance.subtract(fee);
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Fee should be in generator's balance
expectedBalance = generatorBalance.add(fee);
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Check poll was created
PollData actualPollData = this.repository.getVotingRepository().fromPollName(pollName);
assertNotNull(actualPollData);
}
}