Repository work

Rolled BlockTransactionRepository into BlockRepository.

Added AccountRepository for general account info and account balances.

BlockTransformer now takes Block as param instead of BlockData as it needs Block's transactions.
This commit is contained in:
catbref 2018-06-12 12:15:38 +01:00
parent d45c33fe90
commit 37d9bcbb53
16 changed files with 289 additions and 149 deletions

View File

@ -0,0 +1,38 @@
package data.account;
import java.math.BigDecimal;
public class AccountBalanceData {
// Properties
protected String address;
protected long assetId;
protected BigDecimal balance;
// Constructors
public AccountBalanceData(String address, long assetId, BigDecimal balance) {
this.address = address;
this.assetId = assetId;
this.balance = balance;
}
// Getters/Setters
public String getAddress() {
return this.address;
}
public long getAssetId() {
return this.assetId;
}
public BigDecimal getBalance() {
return this.balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
}

View File

@ -8,11 +8,13 @@ public class AccountData {
// Constructors
protected AccountData() {
public AccountData(String address, byte[] reference) {
this.address = address;
this.reference = reference;
}
public AccountData(String address) {
this.address = address;
this(address, null);
}
// Getters/Setters

View File

@ -1,35 +1,31 @@
package qora.account;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import database.DB;
import repository.hsqldb.HSQLDBSaver;
import data.account.AccountBalanceData;
import data.account.AccountData;
import repository.DataException;
import repository.Repository;
public class Account {
public static final int ADDRESS_LENGTH = 25;
protected String address;
protected Repository repository;
protected AccountData accountData;
protected Account() {
}
public Account(String address) {
this.address = address;
public Account(Repository repository, String address) throws DataException {
this.repository = repository;
this.accountData = this.repository.getAccountRepository().getAccount(address);
if (this.accountData == null)
this.accountData = new AccountData(address);
}
public String getAddress() {
return this.address;
}
@Override
public boolean equals(Object b) {
if (!(b instanceof Account))
return false;
return this.getAddress().equals(((Account) b).getAddress());
return this.accountData.getAddress();
}
// Balance manipulations - assetId is 0 for QORA
@ -39,22 +35,21 @@ public class Account {
return null;
}
public BigDecimal getConfirmedBalance(long assetId) throws SQLException {
ResultSet resultSet = DB.checkedExecute("SELECT balance FROM AccountBalances WHERE account = ? and asset_id = ?", this.getAddress(), assetId);
if (resultSet == null)
public BigDecimal getConfirmedBalance(long assetId) throws DataException {
AccountBalanceData accountBalanceData = this.repository.getAccountRepository().getBalance(this.accountData.getAddress(), assetId);
if (accountBalanceData == null)
return BigDecimal.ZERO.setScale(8);
return resultSet.getBigDecimal(1);
return accountBalanceData.getBalance();
}
public void setConfirmedBalance(long assetId, BigDecimal balance) throws SQLException {
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountBalances");
saveHelper.bind("account", this.getAddress()).bind("asset_id", assetId).bind("balance", balance);
saveHelper.execute();
public void setConfirmedBalance(long assetId, BigDecimal balance) throws DataException {
AccountBalanceData accountBalanceData = new AccountBalanceData(this.accountData.getAddress(), assetId, balance);
this.repository.getAccountRepository().save(accountBalanceData);
}
public void deleteBalance(long assetId) throws SQLException {
DB.checkedExecute("DELETE FROM AccountBalances WHERE account = ? and asset_id = ?", this.getAddress(), assetId);
public void deleteBalance(long assetId) throws DataException {
this.repository.getAccountRepository().delete(this.accountData.getAddress(), assetId);
}
// Reference manipulations
@ -63,14 +58,14 @@ public class Account {
* Fetch last reference for account.
*
* @return byte[] reference, or null if no reference or account not found.
* @throws SQLException
* @throws DataException
*/
public byte[] getLastReference() throws SQLException {
ResultSet resultSet = DB.checkedExecute("SELECT reference FROM Accounts WHERE account = ?", this.getAddress());
if (resultSet == null)
public byte[] getLastReference() throws DataException {
AccountData accountData = this.repository.getAccountRepository().getAccount(this.accountData.getAddress());
if (accountData == null)
return null;
return DB.getResultSetBytes(resultSet.getBinaryStream(1));
return accountData.getReference();
}
/**
@ -78,12 +73,10 @@ public class Account {
*
* @param reference
* -- null allowed
* @throws SQLException
* @throws DataException
*/
public void setLastReference(byte[] reference) throws SQLException {
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
saveHelper.bind("account", this.getAddress()).bind("reference", reference);
saveHelper.execute();
public void setLastReference(byte[] reference) throws DataException {
this.repository.getAccountRepository().save(accountData);
}
}

View File

@ -231,7 +231,7 @@ public class Block {
// Check there is space in block
try {
if (BlockTransformer.getDataLength(this.blockData) + TransactionTransformer.getDataLength(transactionData) > MAX_BLOCK_BYTES)
if (BlockTransformer.getDataLength(this) + TransactionTransformer.getDataLength(transactionData) > MAX_BLOCK_BYTES)
return false;
} catch (TransformationException e) {
return false;
@ -436,7 +436,7 @@ public class Block {
// Link transaction to this block
BlockTransactionData blockTransactionData = new BlockTransactionData(this.getSignature(), sequence, transaction.getTransactionData().getSignature());
this.repository.getBlockTransactionRepository().save(blockTransactionData);
this.repository.getBlockRepository().save(blockTransactionData);
}
}

View File

@ -20,7 +20,7 @@ import qora.assets.Order;
import repository.hsqldb.HSQLDBSaver;
import transform.TransformationException;
public class CreateOrderTransaction extends TransactionHandler {
public class CreateOrderTransaction extends Transaction {
// Properties
private Order order;

View File

@ -0,0 +1,22 @@
package repository;
import data.account.AccountBalanceData;
import data.account.AccountData;
public interface AccountRepository {
// General account
public AccountData getAccount(String address) throws DataException;
public void save(AccountData accountData) throws DataException;
// Account balances
public AccountBalanceData getBalance(String address, long assetId) throws DataException;
public void save(AccountBalanceData accountBalanceData) throws DataException;
public void delete(String address, long assetId) throws DataException;
}

View File

@ -3,6 +3,7 @@ package repository;
import java.util.List;
import data.block.BlockData;
import data.block.BlockTransactionData;
import data.transaction.TransactionData;
public interface BlockRepository {
@ -17,4 +18,6 @@ public interface BlockRepository {
public void save(BlockData blockData) throws DataException;
public void save(BlockTransactionData blockTransactionData) throws DataException;
}

View File

@ -1,9 +0,0 @@
package repository;
import data.block.BlockTransactionData;
public interface BlockTransactionRepository {
public void save(BlockTransactionData blockTransactionData) throws DataException;
}

View File

@ -4,19 +4,19 @@ public class DataException extends Exception {
private static final long serialVersionUID = -3963965667288257605L;
public DataException() {}
public DataException() {
}
public DataException(String message)
{
super(message);
}
public DataException(String message, Throwable cause) {
super(message, cause);
}
public DataException(String message) {
super(message);
}
public DataException(Throwable cause) {
super(cause);
}
public DataException(String message, Throwable cause) {
super(message, cause);
}
public DataException(Throwable cause) {
super(cause);
}
}

View File

@ -2,9 +2,9 @@ package repository;
public interface Repository {
public BlockRepository getBlockRepository();
public AccountRepository getAccountRepository();
public BlockTransactionRepository getBlockTransactionRepository();
public BlockRepository getBlockRepository();
public TransactionRepository getTransactionRepository();

View File

@ -0,0 +1,77 @@
package repository.hsqldb;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import data.account.AccountBalanceData;
import data.account.AccountData;
import repository.AccountRepository;
import repository.DataException;
public class HSQLDBAccountRepository implements AccountRepository {
protected HSQLDBRepository repository;
public HSQLDBAccountRepository(HSQLDBRepository repository) {
this.repository = repository;
}
public AccountData getAccount(String address) throws DataException {
try {
ResultSet resultSet = this.repository.checkedExecute("SELECT reference FROM Accounts WHERE account = ?", address);
if (resultSet == null)
return null;
return new AccountData(address, this.repository.getResultSetBytes(resultSet.getBinaryStream(1)));
} catch (SQLException e) {
throw new DataException("Unable to fetch account info from repository", e);
}
}
public void save(AccountData accountData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
saveHelper.bind("account", accountData.getAddress()).bind("reference", accountData.getReference());
try {
saveHelper.execute(this.repository.connection);
} catch (SQLException e) {
throw new DataException("Unable to save account info into repository", e);
}
}
public AccountBalanceData getBalance(String address, long assetId) throws DataException {
try {
ResultSet resultSet = this.repository.checkedExecute("SELECT balance FROM AccountBalances WHERE account = ? and asset_id = ?", address, assetId);
if (resultSet == null)
return null;
BigDecimal balance = resultSet.getBigDecimal(1).setScale(8);
return new AccountBalanceData(address, assetId, balance);
} catch (SQLException e) {
throw new DataException("Unable to fetch account balance from repository", e);
}
}
public void save(AccountBalanceData accountBalanceData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountBalances");
saveHelper.bind("account", accountBalanceData.getAddress()).bind("asset_id", accountBalanceData.getAssetId()).bind("balance",
accountBalanceData.getBalance());
try {
saveHelper.execute(this.repository.connection);
} catch (SQLException e) {
throw new DataException("Unable to save account balance into repository", e);
}
}
public void delete(String address, long assetId) throws DataException {
try {
this.repository.checkedExecute("DELETE FROM AccountBalances WHERE account = ? and asset_id = ?", address, assetId);
} catch (SQLException e) {
throw new DataException("Unable to delete account balance from repository", e);
}
}
}

View File

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.List;
import data.block.BlockData;
import data.block.BlockTransactionData;
import data.transaction.TransactionData;
import repository.BlockRepository;
import repository.DataException;
@ -115,4 +116,16 @@ public class HSQLDBBlockRepository implements BlockRepository {
}
}
public void save(BlockTransactionData blockTransactionData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("BlockTransactions");
saveHelper.bind("block_signature", blockTransactionData.getBlockSignature()).bind("sequence", blockTransactionData.getSequence())
.bind("transaction_signature", blockTransactionData.getTransactionSignature());
try {
saveHelper.execute(this.repository.connection);
} catch (SQLException e) {
throw new DataException("Unable to save BlockTransaction into repository", e);
}
}
}

View File

@ -1,29 +0,0 @@
package repository.hsqldb;
import java.sql.SQLException;
import data.block.BlockTransactionData;
import repository.BlockTransactionRepository;
import repository.DataException;
public class HSQLDBBlockTransactionRepository implements BlockTransactionRepository {
protected HSQLDBRepository repository;
public HSQLDBBlockTransactionRepository(HSQLDBRepository repository) {
this.repository = repository;
}
public void save(BlockTransactionData blockTransactionData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("BlockTransactions");
saveHelper.bind("block_signature", blockTransactionData.getBlockSignature()).bind("sequence", blockTransactionData.getSequence())
.bind("transaction_signature", blockTransactionData.getTransactionSignature());
try {
saveHelper.execute(this.repository.connection);
} catch (SQLException e) {
throw new DataException("Unable to save BlockTransaction into repository", e);
}
}
}

View File

@ -8,8 +8,8 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import repository.AccountRepository;
import repository.BlockRepository;
import repository.BlockTransactionRepository;
import repository.DataException;
import repository.Repository;
import repository.TransactionRepository;
@ -24,13 +24,13 @@ public class HSQLDBRepository implements Repository {
}
@Override
public BlockRepository getBlockRepository() {
return new HSQLDBBlockRepository(this);
public AccountRepository getAccountRepository() {
return new HSQLDBAccountRepository(this);
}
@Override
public BlockTransactionRepository getBlockTransactionRepository() {
return new HSQLDBBlockTransactionRepository(this);
public BlockRepository getBlockRepository() {
return new HSQLDBBlockRepository(this);
}
@Override

View File

@ -1,14 +1,22 @@
package transform;
@SuppressWarnings("serial")
public class TransformationException extends Exception {
private static final long serialVersionUID = 1132363278761469714L;
public TransformationException() {
}
public TransformationException(String message) {
super(message);
}
public TransformationException(Exception e) {
super(e);
public TransformationException(String message, Throwable cause) {
super(message, cause);
}
public TransformationException(Throwable cause) {
super(cause);
}
}

View File

@ -19,13 +19,17 @@ import com.google.common.primitives.Longs;
import data.block.BlockData;
import data.transaction.TransactionData;
import qora.account.PublicKeyAccount;
import qora.assets.Order;
import qora.assets.Trade;
import qora.block.Block;
import qora.transaction.CreateOrderTransaction;
import qora.transaction.Transaction;
import repository.DataException;
import transform.TransformationException;
import transform.Transformer;
import transform.transaction.TransactionTransformer;
import utils.Base58;
import utils.Pair;
import utils.Serialization;
public class BlockTransformer extends Transformer {
@ -48,7 +52,14 @@ public class BlockTransformer extends Transformer {
protected static final int AT_FEES_LENGTH = LONG_LENGTH;
protected static final int AT_LENGTH = AT_FEES_LENGTH + AT_BYTES_LENGTH;
public static BlockData fromBytes(byte[] bytes) throws TransformationException {
/**
* Extract block data and transaction data from serialized bytes.
*
* @param bytes
* @return BlockData and a List of transactions.
* @throws TransformationException
*/
public static Pair<BlockData, List<TransactionData>> fromBytes(byte[] bytes) throws TransformationException {
if (bytes == null)
return null;
@ -91,8 +102,9 @@ public class BlockTransformer extends Transformer {
int transactionCount = byteBuffer.getInt();
// Parse transactions now, compared to deferred parsing in Gen1, so we can throw ParseException if need be
// Parse transactions now, compared to deferred parsing in Gen1, so we can throw ParseException if need be.
List<TransactionData> transactions = new ArrayList<TransactionData>();
BigDecimal totalFees = BigDecimal.ZERO.setScale(8);
for (int t = 0; t < transactionCount; ++t) {
if (byteBuffer.remaining() < TRANSACTION_SIZE_LENGTH)
throw new TransformationException("Byte data too short for Block Transaction length");
@ -106,41 +118,50 @@ public class BlockTransformer extends Transformer {
byte[] transactionBytes = new byte[transactionLength];
byteBuffer.get(transactionBytes);
TransactionData transaction = TransactionTransformer.fromBytes(transactionBytes);
transactions.add(transaction);
TransactionData transactionData = TransactionTransformer.fromBytes(transactionBytes);
transactions.add(transactionData);
totalFees.add(transactionData.getFee());
}
if (byteBuffer.hasRemaining())
throw new TransformationException("Excess byte data found after parsing Block");
// XXX Can't return a simple BlockData object because it doesn't support holding the transactions
// return new BlockData(version, reference, timestamp, generatingBalance, generatorPublicKey, generatorSignature, transactionsSignature, atBytes, atFees, transactions);
return null;
// XXX we don't know height!
int height = 0;
BlockData blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance, generatorPublicKey, generatorSignature,
atBytes, atFees);
return new Pair<BlockData, List<TransactionData>>(blockData, transactions);
}
public static int getDataLength(BlockData blockData) throws TransformationException {
// TODO
public static int getDataLength(Block block) throws TransformationException {
BlockData blockData = block.getBlockData();
int blockLength = BASE_LENGTH;
if (blockData.getVersion() >= 2 && blockData.getAtBytes() != null)
blockLength += AT_FEES_LENGTH + AT_BYTES_LENGTH + blockData.getAtBytes().length;
/*
* XXX Where do the transactions come from? A param? Do we pass a Block instead of BlockData?
// Short cut for no transactions
if (block.getTransactions() == null || block.getTransactions().isEmpty())
return blockLength;
try {
// Short cut for no transactions
List<Transaction> transactions = block.getTransactions();
if (transactions == null || transactions.isEmpty())
return blockLength;
for (TransactionData transaction : this.transactions)
blockLength += TRANSACTION_SIZE_LENGTH + transaction.getDataLength();
*/
for (Transaction transaction : transactions)
blockLength += TRANSACTION_SIZE_LENGTH + TransactionTransformer.getDataLength(transaction.getTransactionData());
} catch (DataException e) {
throw new TransformationException("Unable to determine serialized block length", e);
}
return blockLength;
}
public static byte[] toBytes(BlockData blockData) throws TransformationException {
public static byte[] toBytes(Block block) throws TransformationException {
BlockData blockData = block.getBlockData();
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream(getDataLength(blockData));
ByteArrayOutputStream bytes = new ByteArrayOutputStream(getDataLength(block));
bytes.write(Ints.toByteArray(blockData.getVersion()));
bytes.write(Longs.toByteArray(blockData.getTimestamp()));
@ -168,22 +189,22 @@ public class BlockTransformer extends Transformer {
// Transactions
bytes.write(Ints.toByteArray(blockData.getTransactionCount()));
/*
* XXX Where do the transactions come from? A param? Do we pass a Block instead of BlockData?
for (TransactionData transaction : blockData.getTransactions()) {
bytes.write(Ints.toByteArray(transaction.getDataLength()));
bytes.write(transaction.toBytes());
for (Transaction transaction : block.getTransactions()) {
TransactionData transactionData = transaction.getTransactionData();
bytes.write(Ints.toByteArray(TransactionTransformer.getDataLength(transactionData)));
bytes.write(TransactionTransformer.toBytes(transactionData));
}
*/
return bytes.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (IOException | DataException e) {
throw new TransformationException("Unable to serialize block", e);
}
}
@SuppressWarnings("unchecked")
public static JSONObject toJSON(BlockData blockData) throws TransformationException {
public static JSONObject toJSON(Block block) throws TransformationException {
BlockData blockData = block.getBlockData();
JSONObject json = new JSONObject();
json.put("version", blockData.getVersion());
@ -205,31 +226,31 @@ public class BlockTransformer extends Transformer {
JSONArray transactionsJson = new JSONArray();
boolean tradesHappened = false;
/*
* XXX Where do the transactions come from? A param? Do we pass a Block instead of BlockData?
for (TransactionData transaction : blockData.getTransactions()) {
transactionsJson.add(transaction.toJSON());
try {
for (Transaction transaction : block.getTransactions()) {
transactionsJson.add(TransactionTransformer.toJSON(transaction.getTransactionData()));
// If this is an asset CreateOrderTransaction then check to see if any trades happened
if (transaction.getType() == Transaction.TransactionType.CREATE_ASSET_ORDER) {
CreateOrderTransaction orderTransaction = (CreateOrderTransaction) transaction;
Order order = orderTransaction.getOrder();
List<Trade> trades = order.getTrades();
// If this is an asset CreateOrderTransaction then check to see if any trades happened
if (transaction.getTransactionData().getType() == Transaction.TransactionType.CREATE_ASSET_ORDER) {
CreateOrderTransaction orderTransaction = (CreateOrderTransaction) transaction;
Order order = orderTransaction.getOrder();
List<Trade> trades = order.getTrades();
// Filter out trades with timestamps that don't match order transaction's timestamp
trades.removeIf((Trade trade) -> trade.getTimestamp() != order.getTimestamp());
// Filter out trades with timestamps that don't match order transaction's timestamp
trades.removeIf((Trade trade) -> trade.getTimestamp() != order.getTimestamp());
// Any trades left?
if (!trades.isEmpty()) {
tradesHappened = true;
// No need to check any further
break;
// Any trades left?
if (!trades.isEmpty()) {
tradesHappened = true;
// No need to check any further
break;
}
}
}
} catch (DataException e) {
throw new TransformationException("Unable to transform block into JSON", e);
}
json.put("transactions", transactionsJson);
*/
// Add asset trade activity flag
json.put("assetTrades", tradesHappened);
@ -262,7 +283,8 @@ public class BlockTransformer extends Transformer {
}
public static byte[] getBytesForTransactionsSignature(Block block) throws TransformationException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream(GENERATOR_SIGNATURE_LENGTH + block.getBlockData().getTransactionCount() * TransactionTransformer.SIGNATURE_LENGTH);
ByteArrayOutputStream bytes = new ByteArrayOutputStream(
GENERATOR_SIGNATURE_LENGTH + block.getBlockData().getTransactionCount() * TransactionTransformer.SIGNATURE_LENGTH);
try {
bytes.write(block.getBlockData().getGeneratorSignature());
@ -279,5 +301,5 @@ public class BlockTransformer extends Transformer {
throw new TransformationException(e);
}
}
}