forked from Qortal/qortal
Fix HSQLDB*Transaction save() methods
Added RepositoryManager.closeRepositoryFactory() to allow swapping out of repositories during unit testing Added some more unit tests - all pass!
This commit is contained in:
parent
4c18c7c5bc
commit
2c23acfa74
@ -7,8 +7,11 @@ import com.google.common.primitives.Bytes;
|
||||
|
||||
import data.transaction.GenesisTransactionData;
|
||||
import data.transaction.TransactionData;
|
||||
import qora.account.Account;
|
||||
import qora.account.PrivateKeyAccount;
|
||||
import qora.assets.Asset;
|
||||
import qora.crypto.Crypto;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
import transform.TransformationException;
|
||||
import transform.transaction.TransactionTransformer;
|
||||
@ -88,31 +91,33 @@ public class GenesisTransaction extends Transaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
// TODO
|
||||
// this.save();
|
||||
public void process() throws DataException {
|
||||
GenesisTransactionData genesisTransactionData = (GenesisTransactionData) this.transactionData;
|
||||
|
||||
// Set recipient's balance
|
||||
// TODO
|
||||
// this.recipient.setConfirmedBalance(Asset.QORA, this.amount);
|
||||
// Save this transaction itself
|
||||
this.repository.getTransactionRepository().save(this.transactionData);
|
||||
|
||||
// Set recipient's reference
|
||||
// TODO
|
||||
// recipient.setLastReference(this.signature);
|
||||
// Update recipient's balance
|
||||
Account recipient = new Account(repository, genesisTransactionData.getRecipient());
|
||||
recipient.setConfirmedBalance(Asset.QORA, genesisTransactionData.getAmount());
|
||||
|
||||
// Set recipient's starting reference
|
||||
recipient.setLastReference(genesisTransactionData.getSignature());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orphan() {
|
||||
// TODO
|
||||
// this.delete();
|
||||
public void orphan() throws DataException {
|
||||
GenesisTransactionData genesisTransactionData = (GenesisTransactionData) this.transactionData;
|
||||
|
||||
// Reset recipient's balance
|
||||
// TODO
|
||||
// this.recipient.deleteBalance(Asset.QORA);
|
||||
// Delete this transaction
|
||||
this.repository.getTransactionRepository().delete(this.transactionData);
|
||||
|
||||
// Set recipient's reference
|
||||
// TODO
|
||||
// recipient.setLastReference(null);
|
||||
// Delete recipient's balance
|
||||
Account recipient = new Account(repository, genesisTransactionData.getRecipient());
|
||||
recipient.deleteBalance(Asset.QORA);
|
||||
|
||||
// Delete recipient's last reference
|
||||
recipient.setLastReference(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -187,8 +187,9 @@ public abstract class Transaction {
|
||||
* Load encapsulating Block from DB, if any
|
||||
*
|
||||
* @return Block, or null if transaction is not in a Block
|
||||
* @throws DataException
|
||||
*/
|
||||
public BlockData getBlock() {
|
||||
public BlockData getBlock() throws DataException {
|
||||
return this.repository.getTransactionRepository().toBlock(this.transactionData);
|
||||
}
|
||||
|
||||
|
@ -4,4 +4,6 @@ public interface RepositoryFactory {
|
||||
|
||||
public Repository getRepository() throws DataException;
|
||||
|
||||
public void close() throws DataException;
|
||||
|
||||
}
|
||||
|
@ -2,14 +2,22 @@ package repository;
|
||||
|
||||
public abstract class RepositoryManager {
|
||||
|
||||
private static RepositoryFactory repositoryFactory;
|
||||
private static RepositoryFactory repositoryFactory = null;
|
||||
|
||||
public static void setRepositoryFactory(RepositoryFactory newRepositoryFactory) {
|
||||
repositoryFactory = newRepositoryFactory;
|
||||
}
|
||||
|
||||
public static Repository getRepository() throws DataException {
|
||||
if (repositoryFactory == null)
|
||||
throw new DataException("No repository available");
|
||||
|
||||
return repositoryFactory.getRepository();
|
||||
}
|
||||
|
||||
public static void closeRepositoryFactory() throws DataException {
|
||||
repositoryFactory.close();
|
||||
repositoryFactory = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ public interface TransactionRepository {
|
||||
|
||||
public int getHeight(TransactionData transactionData);
|
||||
|
||||
public BlockData toBlock(TransactionData transactionData);
|
||||
public BlockData toBlock(TransactionData transactionData) throws DataException;
|
||||
|
||||
public void save(TransactionData transactionData) throws DataException;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package repository.hsqldb;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hsqldb.jdbc.JDBCPool;
|
||||
@ -18,11 +19,11 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
|
||||
// one-time initialization goes in here
|
||||
this.connectionUrl = connectionUrl;
|
||||
|
||||
connectionPool = new JDBCPool();
|
||||
connectionPool.setUrl(this.connectionUrl);
|
||||
this.connectionPool = new JDBCPool();
|
||||
this.connectionPool.setUrl(this.connectionUrl);
|
||||
|
||||
// Perform DB updates?
|
||||
try (final Connection connection = connectionPool.getConnection()) {
|
||||
try (final Connection connection = this.connectionPool.getConnection()) {
|
||||
HSQLDBDatabaseUpdates.updateDatabase(connection);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Repository initialization error", e);
|
||||
@ -47,4 +48,18 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void close() throws DataException {
|
||||
try {
|
||||
// Close all existing connections immediately
|
||||
this.connectionPool.close(0);
|
||||
|
||||
// Now that all connections are closed, create a dedicated connection to shut down repository
|
||||
Connection connection = DriverManager.getConnection(this.connectionUrl);
|
||||
connection.createStatement().execute("SHUTDOWN");
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Error during repository shutdown", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,8 +36,6 @@ public class HSQLDBCreateOrderTransactionRepository extends HSQLDBTransactionRep
|
||||
|
||||
@Override
|
||||
public void save(TransactionData transactionData) throws DataException {
|
||||
super.save(transactionData);
|
||||
|
||||
CreateOrderTransactionData createOrderTransactionData = (CreateOrderTransactionData) transactionData;
|
||||
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("CreateAssetOrderTransactions");
|
||||
|
@ -33,8 +33,6 @@ public class HSQLDBGenesisTransactionRepository extends HSQLDBTransactionReposit
|
||||
|
||||
@Override
|
||||
public void save(TransactionData transactionData) throws DataException {
|
||||
super.save(transactionData);
|
||||
|
||||
GenesisTransactionData genesisTransactionData = (GenesisTransactionData) transactionData;
|
||||
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("GenesisTransactions");
|
||||
|
@ -41,8 +41,6 @@ public class HSQLDBIssueAssetTransactionRepository extends HSQLDBTransactionRepo
|
||||
|
||||
@Override
|
||||
public void save(TransactionData transactionData) throws DataException {
|
||||
super.save(transactionData);
|
||||
|
||||
IssueAssetTransactionData issueAssetTransactionData = (IssueAssetTransactionData) transactionData;
|
||||
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("IssueAssetTransactions");
|
||||
|
@ -41,8 +41,6 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit
|
||||
|
||||
@Override
|
||||
public void save(TransactionData transactionData) throws DataException {
|
||||
super.save(transactionData);
|
||||
|
||||
MessageTransactionData messageTransactionData = (MessageTransactionData) transactionData;
|
||||
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("MessageTransactions");
|
||||
|
@ -34,8 +34,6 @@ public class HSQLDBPaymentTransactionRepository extends HSQLDBTransactionReposit
|
||||
|
||||
@Override
|
||||
public void save(TransactionData transactionData) throws DataException {
|
||||
super.save(transactionData);
|
||||
|
||||
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) transactionData;
|
||||
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("PaymentTransactions");
|
||||
|
@ -42,11 +42,11 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
|
||||
TransactionType type = TransactionType.valueOf(rs.getInt(1));
|
||||
byte[] reference = this.repository.getResultSetBytes(rs.getBinaryStream(2));
|
||||
byte[] creator = this.repository.getResultSetBytes(rs.getBinaryStream(3));
|
||||
byte[] creatorPublicKey = this.repository.getResultSetBytes(rs.getBinaryStream(3));
|
||||
long timestamp = rs.getTimestamp(4).getTime();
|
||||
BigDecimal fee = rs.getBigDecimal(5).setScale(8);
|
||||
|
||||
return this.fromBase(type, signature, reference, creator, timestamp, fee);
|
||||
return this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch transaction from repository", e);
|
||||
}
|
||||
@ -60,11 +60,11 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
|
||||
TransactionType type = TransactionType.valueOf(rs.getInt(1));
|
||||
byte[] signature = this.repository.getResultSetBytes(rs.getBinaryStream(2));
|
||||
byte[] creator = this.repository.getResultSetBytes(rs.getBinaryStream(3));
|
||||
byte[] creatorPublicKey = this.repository.getResultSetBytes(rs.getBinaryStream(3));
|
||||
long timestamp = rs.getTimestamp(4).getTime();
|
||||
BigDecimal fee = rs.getBigDecimal(5).setScale(8);
|
||||
|
||||
return this.fromBase(type, signature, reference, creator, timestamp, fee);
|
||||
return this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch transaction from repository", e);
|
||||
}
|
||||
@ -115,7 +115,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData toBlock(TransactionData transactionData) {
|
||||
public BlockData toBlock(TransactionData transactionData) throws DataException {
|
||||
byte[] signature = transactionData.getSignature();
|
||||
if (signature == null)
|
||||
return null;
|
||||
@ -130,7 +130,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
|
||||
return this.repository.getBlockRepository().fromSignature(blockSignature);
|
||||
} catch (SQLException | DataException e) {
|
||||
return null;
|
||||
throw new DataException("Unable to fetch transaction's block from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,6 +145,32 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
} catch (SQLException e) {
|
||||
throw new DataException(e);
|
||||
}
|
||||
|
||||
// Now call transaction-type-specific save() method
|
||||
switch (transactionData.getType()) {
|
||||
case GENESIS:
|
||||
this.genesisTransactionRepository.save(transactionData);
|
||||
break;
|
||||
|
||||
case PAYMENT:
|
||||
this.paymentTransactionRepository.save(transactionData);
|
||||
break;
|
||||
|
||||
case ISSUE_ASSET:
|
||||
this.issueAssetTransactionRepository.save(transactionData);
|
||||
break;
|
||||
|
||||
case CREATE_ASSET_ORDER:
|
||||
this.createOrderTransactionRepository.save(transactionData);
|
||||
break;
|
||||
|
||||
case MESSAGE:
|
||||
this.messageTransactionRepository.save(transactionData);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new DataException("Unsupported transaction type during save into repository");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -154,7 +180,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
try {
|
||||
this.repository.checkedExecute("DELETE FROM Transactions WHERE signature = ?", transactionData.getSignature());
|
||||
} catch (SQLException e) {
|
||||
throw new DataException(e);
|
||||
throw new DataException("Unable to delete transaction from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ public class BlockTests extends Common {
|
||||
assertNotNull(block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
// only true if blockchain is empty
|
||||
// assertTrue(block.isValid(connection));
|
||||
// assertTrue(block.isValid());
|
||||
|
||||
List<Transaction> transactions = block.getTransactions();
|
||||
assertNotNull(transactions);
|
||||
|
@ -15,13 +15,12 @@ public class Common {
|
||||
@BeforeClass
|
||||
public static void setRepository() throws DataException {
|
||||
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
||||
|
||||
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void closeRepository() throws DataException {
|
||||
// Currently a no-op?
|
||||
RepositoryManager.closeRepositoryFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
93
src/test/GenesisTests.java
Normal file
93
src/test/GenesisTests.java
Normal file
@ -0,0 +1,93 @@
|
||||
package test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import data.transaction.TransactionData;
|
||||
import qora.account.Account;
|
||||
import qora.assets.Asset;
|
||||
import qora.block.GenesisBlock;
|
||||
import qora.transaction.Transaction;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
import repository.RepositoryFactory;
|
||||
import repository.RepositoryManager;
|
||||
import repository.hsqldb.HSQLDBRepositoryFactory;
|
||||
|
||||
// Don't extend Common as we want an in-memory database
|
||||
public class GenesisTests {
|
||||
|
||||
public 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";
|
||||
|
||||
@BeforeClass
|
||||
public static void setRepository() throws DataException {
|
||||
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
||||
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void closeRepository() throws DataException {
|
||||
RepositoryManager.closeRepositoryFactory();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenesisBlockTransactions() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
assertEquals("Blockchain should be empty for this test", 0, repository.getBlockRepository().getBlockchainHeight());
|
||||
|
||||
GenesisBlock block = new GenesisBlock(repository);
|
||||
|
||||
assertNotNull(block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
// Note: only true if blockchain is empty
|
||||
assertTrue(block.isValid());
|
||||
|
||||
List<Transaction> transactions = block.getTransactions();
|
||||
assertNotNull(transactions);
|
||||
|
||||
for (Transaction transaction : transactions) {
|
||||
assertNotNull(transaction);
|
||||
|
||||
TransactionData transactionData = transaction.getTransactionData();
|
||||
|
||||
assertEquals(Transaction.TransactionType.GENESIS, transactionData.getType());
|
||||
assertTrue(transactionData.getFee().compareTo(BigDecimal.ZERO) == 0);
|
||||
assertNull(transactionData.getReference());
|
||||
assertNotNull(transactionData.getSignature());
|
||||
assertTrue(transaction.isSignatureValid());
|
||||
assertEquals(Transaction.ValidationResult.OK, transaction.isValid());
|
||||
}
|
||||
|
||||
// Actually try to process genesis block onto empty blockchain
|
||||
block.process();
|
||||
repository.saveChanges();
|
||||
|
||||
// Attempt to load first transaction directly from database
|
||||
TransactionData transactionData = repository.getTransactionRepository().fromSignature(transactions.get(0).getTransactionData().getSignature());
|
||||
assertNotNull(transactionData);
|
||||
|
||||
assertEquals(Transaction.TransactionType.GENESIS, transactionData.getType());
|
||||
assertTrue(transactionData.getFee().compareTo(BigDecimal.ZERO) == 0);
|
||||
assertNull(transactionData.getReference());
|
||||
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
assertNotNull(transaction);
|
||||
|
||||
assertTrue(transaction.isSignatureValid());
|
||||
assertEquals(Transaction.ValidationResult.OK, transaction.isValid());
|
||||
|
||||
// Check known balance
|
||||
Account testAccount = new Account(repository, "QegT2Ws5YjLQzEZ9YMzWsAZMBE8cAygHZN");
|
||||
BigDecimal testBalance = testAccount.getConfirmedBalance(Asset.QORA);
|
||||
BigDecimal expectedBalance = new BigDecimal("12606834").setScale(8);
|
||||
assertTrue(testBalance.compareTo(expectedBalance) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user