qortal/src/repository/hsqldb/transaction/HSQLDBTransactionRepository.java
2018-07-02 18:09:36 +01:00

299 lines
12 KiB
Java

package repository.hsqldb.transaction;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import data.PaymentData;
import data.block.BlockData;
import data.transaction.TransactionData;
import qora.transaction.Transaction.TransactionType;
import repository.DataException;
import repository.TransactionRepository;
import repository.hsqldb.HSQLDBRepository;
import repository.hsqldb.HSQLDBSaver;
public class HSQLDBTransactionRepository implements TransactionRepository {
protected HSQLDBRepository repository;
private HSQLDBGenesisTransactionRepository genesisTransactionRepository;
private HSQLDBPaymentTransactionRepository paymentTransactionRepository;
private HSQLDBRegisterNameTransactionRepository registerNameTransactionRepository;
private HSQLDBUpdateNameTransactionRepository updateNameTransactionRepository;
private HSQLDBSellNameTransactionRepository sellNameTransactionRepository;
private HSQLDBCreatePollTransactionRepository createPollTransactionRepository;
private HSQLDBVoteOnPollTransactionRepository voteOnPollTransactionRepository;
private HSQLDBIssueAssetTransactionRepository issueAssetTransactionRepository;
private HSQLDBTransferAssetTransactionRepository transferAssetTransactionRepository;
private HSQLDBCreateOrderTransactionRepository createOrderTransactionRepository;
private HSQLDBCancelOrderTransactionRepository cancelOrderTransactionRepository;
private HSQLDBMultiPaymentTransactionRepository multiPaymentTransactionRepository;
private HSQLDBMessageTransactionRepository messageTransactionRepository;
public HSQLDBTransactionRepository(HSQLDBRepository repository) {
this.repository = repository;
this.genesisTransactionRepository = new HSQLDBGenesisTransactionRepository(repository);
this.paymentTransactionRepository = new HSQLDBPaymentTransactionRepository(repository);
this.registerNameTransactionRepository = new HSQLDBRegisterNameTransactionRepository(repository);
this.updateNameTransactionRepository = new HSQLDBUpdateNameTransactionRepository(repository);
this.sellNameTransactionRepository = new HSQLDBSellNameTransactionRepository(repository);
this.createPollTransactionRepository = new HSQLDBCreatePollTransactionRepository(repository);
this.voteOnPollTransactionRepository = new HSQLDBVoteOnPollTransactionRepository(repository);
this.issueAssetTransactionRepository = new HSQLDBIssueAssetTransactionRepository(repository);
this.transferAssetTransactionRepository = new HSQLDBTransferAssetTransactionRepository(repository);
this.createOrderTransactionRepository = new HSQLDBCreateOrderTransactionRepository(repository);
this.cancelOrderTransactionRepository = new HSQLDBCancelOrderTransactionRepository(repository);
this.multiPaymentTransactionRepository = new HSQLDBMultiPaymentTransactionRepository(repository);
this.messageTransactionRepository = new HSQLDBMessageTransactionRepository(repository);
}
protected HSQLDBTransactionRepository() {
}
public TransactionData fromSignature(byte[] signature) throws DataException {
try {
ResultSet rs = this.repository.checkedExecute("SELECT type, reference, creator, creation, fee FROM Transactions WHERE signature = ?", signature);
if (rs == null)
return null;
TransactionType type = TransactionType.valueOf(rs.getInt(1));
byte[] reference = rs.getBytes(2);
byte[] creatorPublicKey = rs.getBytes(3);
long timestamp = rs.getTimestamp(4).getTime();
BigDecimal fee = rs.getBigDecimal(5).setScale(8);
return this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
} catch (SQLException e) {
throw new DataException("Unable to fetch transaction from repository", e);
}
}
public TransactionData fromReference(byte[] reference) throws DataException {
try {
ResultSet rs = this.repository.checkedExecute("SELECT type, signature, creator, creation, fee FROM Transactions WHERE reference = ?", reference);
if (rs == null)
return null;
TransactionType type = TransactionType.valueOf(rs.getInt(1));
byte[] signature = rs.getBytes(2);
byte[] creatorPublicKey = rs.getBytes(3);
long timestamp = rs.getTimestamp(4).getTime();
BigDecimal fee = rs.getBigDecimal(5).setScale(8);
return this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
} catch (SQLException e) {
throw new DataException("Unable to fetch transaction from repository", e);
}
}
private TransactionData fromBase(TransactionType type, byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee)
throws DataException {
switch (type) {
case GENESIS:
return this.genesisTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case PAYMENT:
return this.paymentTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case REGISTER_NAME:
return this.registerNameTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case UPDATE_NAME:
return this.updateNameTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case SELL_NAME:
return this.sellNameTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case CREATE_POLL:
return this.createPollTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case VOTE_ON_POLL:
return this.voteOnPollTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case ISSUE_ASSET:
return this.issueAssetTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case TRANSFER_ASSET:
return this.transferAssetTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case CREATE_ASSET_ORDER:
return this.createOrderTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case CANCEL_ASSET_ORDER:
return this.cancelOrderTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case MULTIPAYMENT:
return this.multiPaymentTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
case MESSAGE:
return this.messageTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
default:
throw new DataException("Unsupported transaction type [" + type.value + "] during fetch from HSQLDB repository");
}
}
protected List<PaymentData> getPaymentsFromSignature(byte[] signature) throws DataException {
try {
ResultSet rs = this.repository.checkedExecute("SELECT recipient, amount, asset_id FROM SharedTransactionPayments WHERE signature = ?", signature);
if (rs == null)
return null;
List<PaymentData> payments = new ArrayList<PaymentData>();
// NOTE: do-while because checkedExecute() above has already called rs.next() for us
do {
String recipient = rs.getString(1);
BigDecimal amount = rs.getBigDecimal(2);
long assetId = rs.getLong(3);
payments.add(new PaymentData(recipient, assetId, amount));
} while (rs.next());
return payments;
} catch (SQLException e) {
throw new DataException("Unable to fetch payments from repository", e);
}
}
protected void savePayments(byte[] signature, List<PaymentData> payments) throws DataException {
for (PaymentData paymentData : payments) {
HSQLDBSaver saver = new HSQLDBSaver("SharedTransactionPayments");
saver.bind("signature", signature).bind("recipient", paymentData.getRecipient()).bind("amount", paymentData.getAmount()).bind("asset_id",
paymentData.getAssetId());
try {
saver.execute(this.repository);
} catch (SQLException e) {
throw new DataException("Unable to save payment into repository", e);
}
}
}
@Override
public int getHeightFromSignature(byte[] signature) throws DataException {
if (signature == null)
return 0;
// in one go?
try {
ResultSet rs = this.repository.checkedExecute(
"SELECT height from BlockTransactions JOIN Blocks ON Blocks.signature = BlockTransactions.block_signature WHERE transaction_signature = ? LIMIT 1",
signature);
if (rs == null)
return 0;
return rs.getInt(1);
} catch (SQLException e) {
throw new DataException("Unable to fetch transaction's height from repository", e);
}
}
@Override
public BlockData getBlockDataFromSignature(byte[] signature) throws DataException {
if (signature == null)
return null;
// Fetch block signature (if any)
try {
ResultSet rs = this.repository.checkedExecute("SELECT block_signature from BlockTransactions WHERE transaction_signature = ? LIMIT 1", signature);
if (rs == null)
return null;
byte[] blockSignature = rs.getBytes(1);
return this.repository.getBlockRepository().fromSignature(blockSignature);
} catch (SQLException | DataException e) {
throw new DataException("Unable to fetch transaction's block from repository", e);
}
}
@Override
public void save(TransactionData transactionData) throws DataException {
HSQLDBSaver saver = new HSQLDBSaver("Transactions");
saver.bind("signature", transactionData.getSignature()).bind("reference", transactionData.getReference()).bind("type", transactionData.getType().value)
.bind("creator", transactionData.getCreatorPublicKey()).bind("creation", new Timestamp(transactionData.getTimestamp()))
.bind("fee", transactionData.getFee()).bind("milestone_block", null);
try {
saver.execute(this.repository);
} catch (SQLException e) {
throw new DataException("Unable to save transaction into repository", 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 REGISTER_NAME:
this.registerNameTransactionRepository.save(transactionData);
break;
case UPDATE_NAME:
this.updateNameTransactionRepository.save(transactionData);
break;
case SELL_NAME:
this.sellNameTransactionRepository.save(transactionData);
break;
case CREATE_POLL:
this.createPollTransactionRepository.save(transactionData);
break;
case VOTE_ON_POLL:
this.voteOnPollTransactionRepository.save(transactionData);
break;
case ISSUE_ASSET:
this.issueAssetTransactionRepository.save(transactionData);
break;
case TRANSFER_ASSET:
this.transferAssetTransactionRepository.save(transactionData);
break;
case CREATE_ASSET_ORDER:
this.createOrderTransactionRepository.save(transactionData);
break;
case CANCEL_ASSET_ORDER:
this.cancelOrderTransactionRepository.save(transactionData);
break;
case MULTIPAYMENT:
this.multiPaymentTransactionRepository.save(transactionData);
break;
case MESSAGE:
this.messageTransactionRepository.save(transactionData);
break;
default:
throw new DataException("Unsupported transaction type [" + transactionData.getType().value + "] during save into HSQLDB repository");
}
}
@Override
public void delete(TransactionData transactionData) throws DataException {
// NOTE: The corresponding row in sub-table is deleted automatically by the database thanks to "ON DELETE CASCADE" in the sub-table's FOREIGN KEY
// definition.
try {
this.repository.delete("Transactions", "signature = ?", transactionData.getSignature());
} catch (SQLException e) {
throw new DataException("Unable to delete transaction from repository", e);
}
}
}