@ -9,7 +9,6 @@ import java.sql.SQLException;
import java.sql.Timestamp ;
import java.util.ArrayList ;
import java.util.Arrays ;
import static java.util.Arrays.stream ;
import java.util.Calendar ;
import java.util.HashMap ;
import java.util.List ;
@ -20,15 +19,15 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger ;
import org.qora.api.resource.TransactionsResource.ConfirmationStatus ;
import org.qora.data.PaymentData ;
import org.qora.data.group.GroupApprovalData ;
import org.qora.data.transaction.GroupApprovalTransactionData ;
import org.qora.data.transaction.TransactionData ;
import org.qora.data.transaction.TransferAssetTransactionData ;
import org.qora.group.Group ;
import org.qora.repository.DataException ;
import org.qora.repository.TransactionRepository ;
import org.qora.repository.hsqldb.HSQLDBRepository ;
import org.qora.repository.hsqldb.HSQLDBSaver ;
import org.qora.transaction.Transaction ;
import org.qora.transaction.Transaction.ApprovalStatus ;
import org.qora.transaction.Transaction.TransactionType ;
import static org.qora.transaction.Transaction.TransactionType.* ;
@ -69,7 +68,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
try {
// params: long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature
subclassInfo . fromBaseMethod = subclassInfo . clazz . getDeclaredMethod ( "fromBase" , long . class , int . class , byte [ ] . class , byte [ ] . class , BigDecimal . class , byte [ ] . class ) ;
subclassInfo . fromBaseMethod = subclassInfo . clazz . getDeclaredMethod ( "fromBase" , long . class , int . class , byte [ ] . class , byte [ ] . class , BigDecimal . class , ApprovalStatus . class , Integer . class , byte [ ] . class ) ;
} catch ( IllegalArgumentException | SecurityException | NoSuchMethodException e ) {
LOGGER . debug ( String . format ( "HSQLDBTransactionRepository subclass's \"fromBase\" method not found for transaction type \"%s\"" , txType . name ( ) ) ) ;
}
@ -129,7 +128,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
@Override
public TransactionData fromSignature ( byte [ ] signature ) throws DataException {
try ( ResultSet resultSet = this . repository . checkedExecute ( "SELECT type, reference, creator, creation, fee, tx_group_id FROM Transactions WHERE signature = ?" ,
try ( ResultSet resultSet = this . repository . checkedExecute ( "SELECT type, reference, creator, creation, fee, tx_group_id, approval_status, height FROM Transactions WHERE signature = ?" ,
signature ) ) {
if ( resultSet = = null )
return null ;
@ -140,9 +139,14 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
long timestamp = resultSet . getTimestamp ( 4 , Calendar . getInstance ( HSQLDBRepository . UTC ) ) . getTime ( ) ;
BigDecimal fee = resultSet . getBigDecimal ( 5 ) . setScale ( 8 ) ;
int txGroupId = resultSet . getInt ( 6 ) ;
ApprovalStatus approvalStatus = ApprovalStatus . valueOf ( resultSet . getInt ( 7 ) ) ;
TransactionData transactionData = this . fromBase ( type , timestamp , txGroupId , reference , creatorPublicKey , fee , signature ) ;
return maybeIncludeBlockHeight ( transactionData ) ;
Integer height = resultSet . getInt ( 8 ) ;
if ( resultSet . wasNull ( ) )
height = null ;
TransactionData transactionData = this . fromBase ( type , timestamp , txGroupId , reference , creatorPublicKey , fee , approvalStatus , height , signature ) ;
return transactionData ;
} catch ( SQLException e ) {
throw new DataException ( "Unable to fetch transaction from repository" , e ) ;
}
@ -150,7 +154,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
@Override
public TransactionData fromReference ( byte [ ] reference ) throws DataException {
try ( ResultSet resultSet = this . repository . checkedExecute ( "SELECT type, signature, creator, creation, fee, tx_group_id FROM Transactions WHERE reference = ?" ,
try ( ResultSet resultSet = this . repository . checkedExecute ( "SELECT type, signature, creator, creation, fee, tx_group_id, approval_status, height FROM Transactions WHERE reference = ?" ,
reference ) ) {
if ( resultSet = = null )
return null ;
@ -161,27 +165,24 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
long timestamp = resultSet . getTimestamp ( 4 , Calendar . getInstance ( HSQLDBRepository . UTC ) ) . getTime ( ) ;
BigDecimal fee = resultSet . getBigDecimal ( 5 ) . setScale ( 8 ) ;
int txGroupId = resultSet . getInt ( 6 ) ;
ApprovalStatus approvalStatus = ApprovalStatus . valueOf ( resultSet . getInt ( 7 ) ) ;
Integer height = resultSet . getInt ( 8 ) ;
if ( resultSet . wasNull ( ) )
height = null ;
TransactionData transactionData = this . fromBase ( type , timestamp , txGroupId , reference , creatorPublicKey , fee , signature ) ;
return maybeIncludeBlockHeight ( transactionData ) ;
TransactionData transactionData = this . fromBase ( type , timestamp , txGroupId , reference , creatorPublicKey , fee , approvalStatus , height , signature ) ;
return transactionData ;
} catch ( SQLException e ) {
throw new DataException ( "Unable to fetch transaction from repository" , e ) ;
}
}
private TransactionData maybeIncludeBlockHeight ( TransactionData transactionData ) throws DataException {
int blockHeight = getHeightFromSignature ( transactionData . getSignature ( ) ) ;
if ( blockHeight ! = 0 )
transactionData . setBlockHeight ( blockHeight ) ;
return transactionData ;
}
@Override
public TransactionData fromHeightAndSequence ( int height , int sequence ) throws DataException {
try ( ResultSet resultSet = this . repository . checkedExecute (
"SELECT transaction_signature FROM BlockTransactions JOIN Blocks ON signature = block_signature WHERE height = ? AND sequence = ?" , height ,
sequence ) ) {
String sql = "SELECT transaction_signature FROM BlockTransactions JOIN Blocks ON signature = block_signature WHERE height = ? AND sequence = ?" ;
try ( ResultSet resultSet = this . repository . checkedExecute ( sql , height , sequence ) ) {
if ( resultSet = = null )
return null ;
@ -189,19 +190,19 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
return this . fromSignature ( signature ) ;
} catch ( SQLException e ) {
throw new DataException ( "Unable to fetch transaction from repository" , e ) ;
throw new DataException ( "Unable to fetch transaction height and sequence from repository" , e ) ;
}
}
private TransactionData fromBase ( TransactionType type , long timestamp , int txGroupId , byte [ ] reference , byte [ ] creatorPublicKey , BigDecimal fee , byte [ ] signature )
private TransactionData fromBase ( TransactionType type , long timestamp , int txGroupId , byte [ ] reference , byte [ ] creatorPublicKey , BigDecimal fee , ApprovalStatus approvalStatus , Integer blockHeight , byte [ ] signature )
throws DataException {
HSQLDBTransactionRepository txRepository = repositoryByTxType [ type . value ] ;
if ( txRepository = = null )
throw new DataException ( "Unsupported transaction type [" + type . name ( ) + "] during fetch from HSQLDB repository" ) ;
try {
// params: long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature
return ( TransactionData ) subclassInfos [ type . value ] . fromBaseMethod . invoke ( txRepository , timestamp , txGroupId , reference , creatorPublicKey , fee , signature ) ;
// params: long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, ApprovalStatus approvalStatus, Integer blockHeight, byte[] signature
return ( TransactionData ) subclassInfos [ type . value ] . fromBaseMethod . invoke ( txRepository , timestamp , txGroupId , reference , creatorPublicKey , fee , approvalStatus , blockHeight , signature ) ;
} catch ( InvocationTargetException e ) {
if ( e . getCause ( ) instanceof DataException )
throw ( DataException ) e . getCause ( ) ;
@ -264,15 +265,19 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
if ( signature = = null )
return 0 ;
String sql = "SELECT height from Transactions WHERE signature = ? LIMIT 1" ;
// Fetch height using join via block's transactions
try ( ResultSet resultSet = this . repository . checkedExecute (
"SELECT height from BlockTransactions JOIN Blocks ON Blocks.signature = BlockTransactions.block_signature WHERE transaction_signature = ? LIMIT 1" ,
signature ) ) {
try ( ResultSet resultSet = this . repository . checkedExecute ( sql , signature ) ) {
if ( resultSet = = null )
return 0 ;
return resultSet . getInt ( 1 ) ;
Integer height = resultSet . getInt ( 1 ) ;
if ( resultSet . wasNull ( ) )
return 0 ;
return height ;
} catch ( SQLException e ) {
throw new DataException ( "Unable to fetch transaction's height from repository" , e ) ;
}
@ -340,10 +345,8 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
@Override
public Map < TransactionType , Integer > getTransactionSummary ( int startHeight , int endHeight ) throws DataException {
String sql = "SELECT type, COUNT(signature) FROM Transactions "
+ "JOIN BlockTransactions ON transaction_signature = signature "
+ "JOIN Blocks ON Blocks.signature = block_signature "
+ "WHERE height BETWEEN ? AND ? "
+ "GROUP BY type" ;
+ "WHERE height BETWEEN ? AND ? "
+ "GROUP BY type" ;
Map < TransactionType , Integer > transactionCounts = new HashMap < > ( ) ;
@ -385,25 +388,6 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
// Tables, starting with Transactions
String tables = "Transactions" ;
// BlockTransactions if we want confirmed transactions
switch ( confirmationStatus ) {
case BOTH :
break ;
case CONFIRMED :
tables + = " JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature" ;
if ( hasHeightRange )
tables + = " JOIN Blocks ON Blocks.signature = BlockTransactions.block_signature" ;
break ;
case UNCONFIRMED :
tables + = " LEFT OUTER JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature" ;
whereClauses . add ( "BlockTransactions.transaction_signature IS NULL" ) ;
break ;
}
if ( hasAddress ) {
tables + = " JOIN TransactionParticipants ON TransactionParticipants.signature = Transactions.signature" ;
groupBy = " GROUP BY TransactionParticipants.signature, Transactions.creation" ;
@ -416,11 +400,27 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
}
// WHERE clauses next
// Confirmation status
switch ( confirmationStatus ) {
case BOTH :
break ;
case CONFIRMED :
whereClauses . add ( "Transactions.height IS NOT NULL" ) ;
break ;
case UNCONFIRMED :
whereClauses . add ( "Transactions.height IS NULL" ) ;
break ;
}
// Height range
if ( hasHeightRange ) {
whereClauses . add ( "Blocks.height >= " + startBlock ) ;
whereClauses . add ( "Transaction s.height >= " + startBlock ) ;
if ( blockLimit ! = null )
whereClauses . add ( "Blocks.height < " + ( startBlock + blockLimit ) ) ;
whereClauses . add ( "Transaction s.height < " + ( startBlock + blockLimit ) ) ;
}
if ( txGroupId ! = null ) {
@ -484,20 +484,6 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
String sql = "SELECT Transactions.signature FROM Transactions" ;
// BlockTransactions if we want confirmed transactions
switch ( confirmationStatus ) {
case BOTH :
break ;
case CONFIRMED :
sql + = " JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature" ;
break ;
case UNCONFIRMED :
sql + = " LEFT OUTER JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature" ;
break ;
}
for ( TransactionType type : transactionTypes )
sql + = " LEFT OUTER JOIN " + type . className + "Transactions USING (signature)" ;
@ -506,16 +492,17 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
sql + = " WHERE Transactions.type IN (" + String . join ( ", " , typeValueStrings ) + ")" ;
// BlockTransactions if we want confirmed transaction s
// Confirmation statu s
switch ( confirmationStatus ) {
case BOTH :
break ;
case CONFIRMED :
sql + = " AND Transactions.height IS NOT NULL" ;
break ;
case UNCONFIRMED :
sql + = " AND BlockTransactions.transaction_signature IS NULL" ;
sql + = " AND Transactions.height IS NULL" ;
break ;
}
@ -565,7 +552,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
throws DataException {
List < Object > bindParams = new ArrayList < > ( 3 ) ;
String sql = "SELECT creation, tx_group_id, reference, fee, signature, sender, recipient, amount, asset_name "
String sql = "SELECT creation, tx_group_id, reference, fee, signature, sender, recipient, amount, asset_name, approval_status, height "
+ "FROM TransferAssetTransactions JOIN Transactions USING (signature) " ;
if ( address ! = null )
@ -600,8 +587,12 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
String recipient = resultSet . getString ( 7 ) ;
BigDecimal amount = resultSet . getBigDecimal ( 8 ) ;
String assetName = resultSet . getString ( 9 ) ;
ApprovalStatus approvalStatus = ApprovalStatus . valueOf ( resultSet . getInt ( 10 ) ) ;
Integer height = resultSet . getInt ( 11 ) ;
if ( resultSet . wasNull ( ) )
height = null ;
assetTransfers . add ( new TransferAssetTransactionData ( timestamp , txGroupId , reference , creatorPublicKey , recipient , amount , assetId , fee , assetName , signature ) ) ;
assetTransfers . add ( new TransferAssetTransactionData ( timestamp , txGroupId , reference , creatorPublicKey , recipient , amount , assetId , fee , assetName , approvalStatus , height , signature ) ) ;
} while ( resultSet . next ( ) ) ;
return assetTransfers ;
@ -611,37 +602,31 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
}
@Override
public List < TransactionData > getPendingTransactions ( Integer txGroupId , Integer limit , Integer offset , Boolean reverse ) throws DataException {
String [ ] txTypesNeedingApproval = stream ( Transaction . TransactionType . values ( ) )
. filter ( txType - > txType . needsApproval )
. map ( txType - > String . valueOf ( txType . value ) )
. toArray ( String [ ] : : new ) ;
String txTypes = String . join ( ", " , txTypesNeedingApproval ) ;
/ *
* We only want transactions matching certain types needing approval ,
* with txGroupId not set to NO_GROUP and where auto - approval won ' t
* happen due to the transaction creator being an admin of that group .
* /
String sql = "SELECT signature FROM UnconfirmedTransactions "
+ "NATURAL JOIN Transactions "
+ "LEFT OUTER JOIN Accounts ON Accounts.public_key = Transactions.creator "
+ "LEFT OUTER JOIN GroupAdmins ON GroupAdmins.admin = Accounts.account AND GroupAdmins.group_id = Transactions.tx_group_id "
+ "WHERE Transactions.tx_group_id != ? AND GroupAdmins.admin IS NULL "
+ "AND Transactions.type IN (" + txTypes + ") "
+ "ORDER BY creation" ;
public List < TransactionData > getApprovalPendingTransactions ( Integer txGroupId , Integer limit , Integer offset , Boolean reverse ) throws DataException {
String sql = "SELECT signature FROM Transactions WHERE Transactions.approval_status = ? " ;
List < Object > bindParams = new ArrayList < > ( ) ;
bindParams . add ( ApprovalStatus . PENDING . value ) ;
if ( txGroupId ! = null ) {
sql + = "AND Transactions.tx_group_id = ? " ;
bindParams . add ( txGroupId ) ;
}
sql + = "ORDER BY creation" ;
if ( reverse ! = null & & reverse )
sql + = " DESC" ;
sql + = ", signature" ;
if ( reverse ! = null & & reverse )
sql + = " DESC" ;
sql + = HSQLDBRepository . limitOffsetSql ( limit , offset ) ;
List < TransactionData > transactions = new ArrayList < TransactionData > ( ) ;
// Find transactions with no corresponding row in BlockTransactions
try ( ResultSet resultSet = this . repository . checkedExecute ( sql , Group . NO_GROUP ) ) {
try ( ResultSet resultSet = this . repository . checkedExecute ( sql , bindParams . toArray ( ) ) ) {
if ( resultSet = = null )
return transactions ;
@ -652,85 +637,136 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
if ( transactionData = = null )
// Something inconsistent with the repository
throw new DataException ( "Unable to fetch unconfirmed transaction from repository?" ) ;
throw new DataException ( "Unable to fetch approval-pending transaction from repository?" ) ;
transactions . add ( transactionData ) ;
} while ( resultSet . next ( ) ) ;
return transactions ;
} catch ( SQLException | DataException e ) {
throw new DataException ( "Unable to fetch unconfirmed transactions from repository" , e ) ;
throw new DataException ( "Unable to fetch approval-pending transactions from repository" , e ) ;
}
}
@Override
public int countTransactionApprovals ( int txGroupId , byte [ ] signature ) throws DataException {
// Fetch total number of approvals for signature
// NOT simply number of GROUP_APPROVAL transactions as some may be rejecting transaction, or changed opinions
// Also make sure that GROUP_APPROVAL transaction's admin is still an admin of group
public List < TransactionData > getApprovalPendingTransactions ( int blockHeight ) throws DataException {
String sql = "SELECT signature FROM Transactions "
+ "JOIN Groups on Groups.group_id = Transactions.tx_group_id "
+ "WHERE Transactions.approval_status = ? "
+ "AND Transactions.height >= ? - Groups.min_block_delay" ;
List < TransactionData > transactions = new ArrayList < TransactionData > ( ) ;
// Sub-query SQL to find latest GroupApprovalTransaction relating to passed signature
String latestApprovalSql = "SELECT admin AS creator, MAX(creation) AS creation FROM GroupApprovalTransactions NATURAL JOIN Transactions WHERE pending_signature = ? GROUP BY admin" ;
try ( ResultSet resultSet = this . repository . checkedExecute ( sql , ApprovalStatus . PENDING , blockHeight ) ) {
if ( resultSet = = null )
return transactions ;
String sql = "SELECT COUNT(*) FROM "
+ "(" + latestApprovalSql + ") "
+ "NATURAL JOIN Transactions "
+ "NATURAL JOIN GroupApprovalTransactions "
+ "LEFT OUTER JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature "
+ "LEFT OUTER JOIN Accounts ON Accounts.public_key = GroupApprovalTransactions.admin "
+ "LEFT OUTER JOIN GroupAdmins ON GroupAdmins.admin = Accounts.account "
+ "WHERE approval = TRUE AND GroupAdmins.group_id = ?" ;
do {
byte [ ] signature = resultSet . getBytes ( 1 ) ;
try ( ResultSet resultSet = this . repository . checkedExecute ( sql , signature , txGroupId ) ) {
return resultSet . getInt ( 1 ) ;
TransactionData transactionData = this . fromSignature ( signature ) ;
if ( transactionData = = null )
// Something inconsistent with the repository
throw new DataException ( "Unable to fetch approval-expiring transaction from repository?" ) ;
transactions . add ( transactionData ) ;
} while ( resultSet . next ( ) ) ;
return transactions ;
} catch ( SQLException | DataException e ) {
throw new DataException ( "Unable to fetch approval-expiring transactions from repository" , e ) ;
}
}
@Override
public List < TransactionData > getApprovalExpiringTransactions ( int blockHeight ) throws DataException {
String sql = "SELECT signature FROM Transactions "
+ "JOIN Groups on Groups.group_id = Transactions.tx_group_id "
+ "WHERE Transactions.approval_status = ? "
+ "AND Transactions.height < ? - Groups.max_block_delay" ;
List < TransactionData > transactions = new ArrayList < TransactionData > ( ) ;
try ( ResultSet resultSet = this . repository . checkedExecute ( sql , ApprovalStatus . PENDING , blockHeight ) ) {
if ( resultSet = = null )
return transactions ;
do {
byte [ ] signature = resultSet . getBytes ( 1 ) ;
TransactionData transactionData = this . fromSignature ( signature ) ;
if ( transactionData = = null )
// Something inconsistent with the repository
throw new DataException ( "Unable to fetch approval-expiring transaction from repository?" ) ;
transactions . add ( transactionData ) ;
} while ( resultSet . next ( ) ) ;
return transactions ;
} catch ( SQLException | DataException e ) {
throw new DataException ( "Unable to fetch approval-expiring transactions from repository" , e ) ;
}
}
@Override
public GroupApprovalTransactionData getLatestApproval ( byte [ ] pendingSignature , byte [ ] adminPublicKey ) throws DataException {
String sql = "SELECT signature FROM GroupApprovalTransactions "
+ "NATURAL JOIN Transactions "
+ "WHERE pending_signature = ? AND admin = ? AND height IS NOT NULL "
+ "ORDER BY creation DESC, signature DESC LIMIT 1" ;
try ( ResultSet resultSet = this . repository . checkedExecute ( sql , pendingSignature , adminPublicKey ) ) {
if ( resultSet = = null )
return null ;
byte [ ] signature = resultSet . getBytes ( 1 ) ;
return ( GroupApprovalTransactionData ) this . fromSignature ( signature ) ;
} catch ( SQLException e ) {
throw new DataException ( "Unable to count transaction group-admin approvals from repository" , e ) ;
throw new DataException ( "Unable to fetch latest transaction group-admin approval from repository" , e ) ;
}
}
@Override
public List < GroupApprovalTransactionData > getLatestApprovals ( byte [ ] pendingSignature , byte [ ] adminPublicKey ) throws DataException {
// Fetch latest approvals for signature
public GroupApprovalData getApprovalData ( byte [ ] pendingSignature ) throws DataException {
// Fetch latest approval data for pending transaction's signature
// NOT simply number of GROUP_APPROVAL transactions as some may be rejecting transaction, or changed opinions
// Also make sure that GROUP_APPROVAL transaction's admin is still an admin of group
Object [ ] bindArgs ;
// Sub-query SQL to find latest GroupApprovalTransaction relating to passed signature
String latestApprovalSql = "SELECT admin AS creator, MAX(creation) AS creation FROM GroupApprovalTransactions NATURAL JOIN Transactions WHERE pending_signature = ? " ;
if ( adminPublicKey ! = null )
latestApprovalSql + = "AND admin = ? " ;
latestApprovalSql + = "GROUP BY admin" ;
// Sub-query SQL to find latest GroupApprovalTransaction relating to passed pending signature
String latestApprovalSql = "SELECT pending_signature, admin, approval, creation, signature FROM GroupApprovalTransactions "
+ "NATURAL JOIN Transactions WHERE pending_signature = ? AND height IS NOT NULL" ;
String sql = "SELECT signature FROM "
+ "(" + latestApprovalSql + ") "
+ "NATURAL JOIN Transactions "
+ "NATURAL JOIN GroupApprovalTransactions "
+ "LEFT OUTER JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature "
+ "LEFT OUTER JOIN Accounts ON Accounts.public_key = GroupApprovalTransactions.admin "
+ "LEFT OUTER JOIN GroupAdmins ON GroupAdmins.admin = Accounts.account "
+ "WHERE approval = TRUE AND GroupAdmins.group_id = Transactions.tx_group_id" ;
String sql = "SELECT GAT.admin, GAT.approval FROM "
+ "(" + latestApprovalSql + ") AS GAT "
+ "LEFT OUTER JOIN (" + latestApprovalSql + ") AS NewerGAT "
+ "ON NewerGAT.admin = GAT.admin AND (NewerGAT.creation > GAT.creation OR (NewerGAT.creation = GAT.creation AND NewerGat.signature > GAT.signature)) "
+ "JOIN Transactions AS PendingTransactions ON PendingTransactions.signature = GAT.pending_ signature "
+ "LEFT OUTER JOIN Accounts ON Accounts.public_key = GAT.admin "
+ "LEFT OUTER JOIN GroupAdmins ON GroupAdmins.admin = Accounts.account AND GroupAdmins.group_id = PendingTransactions.tx_group_id "
+ "WHERE NewerGAT.admin IS NULL " ;
if ( adminPublicKey ! = null )
bindArgs = new Object [ ] { pendingSignature , adminPublicKey } ;
else
bindArgs = new Object [ ] { pendingSignature } ;
GroupApprovalData groupApprovalData = new GroupApprovalData ( ) ;
List < GroupApprovalTransactionData > approvals = new ArrayList < > ( ) ;
try ( ResultSet resultSet = this . repository . checkedExecute ( sql , bindArgs ) ) {
try ( ResultSet resultSet = this . repository . checkedExecute ( sql , pendingSignature , pendingSignature ) ) {
if ( resultSet = = null )
return approvals ;
return groupApprovalData ;
do {
byte [ ] signature = resultSet . getBytes ( 1 ) ;
byte [ ] adminPublicKey = resultSet . getBytes ( 1 ) ;
boolean approval = resultSet . getBoolean ( 2 ) ;
approvals . add ( ( GroupApprovalTransactionData ) this . fromSignature ( signature ) ) ;
if ( approval )
groupApprovalData . approvingAdmins . add ( adminPublicKey ) ;
else
groupApprovalData . rejectingAdmins . add ( adminPublicKey ) ;
} while ( resultSet . next ( ) ) ;
return approvals ;
return groupApprovalData ;
} catch ( SQLException e ) {
throw new DataException ( "Unable to fetch latest transaction group-admin approvals from repository" , e ) ;
throw new DataException ( "Unable to fetch latest transaction group-approval decision s from repository" , e ) ;
}
}
@ -745,12 +781,16 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
@Override
public List < TransactionData > getUnconfirmedTransactions ( Integer limit , Integer offset , Boolean reverse ) throws DataException {
String sql = "SELECT signature FROM UnconfirmedTransactions ORDER BY creation" ;
String sql = "SELECT signature FROM UnconfirmedTransactions " ;
sql + = "ORDER BY creation" ;
if ( reverse ! = null & & reverse )
sql + = " DESC" ;
sql + = ", signature" ;
if ( reverse ! = null & & reverse )
sql + = " DESC" ;
sql + = HSQLDBRepository . limitOffsetSql ( limit , offset ) ;
List < TransactionData > transactions = new ArrayList < TransactionData > ( ) ;
@ -787,10 +827,25 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
}
}
@Override
public void updateHeight ( byte [ ] signature , Integer height ) throws DataException {
HSQLDBSaver saver = new HSQLDBSaver ( "Transactions" ) ;
saver . bind ( "signature" , signature ) . bind ( "height" , height ) ;
try {
saver . execute ( repository ) ;
} catch ( SQLException e ) {
throw new DataException ( "Unable to update transaction's height in repository" , e ) ;
}
}
@Override
public void unconfirmTransaction ( TransactionData transactionData ) throws DataException {
HSQLDBSaver saver = new HSQLDBSaver ( "UnconfirmedTransactions" ) ;
saver . bind ( "signature" , transactionData . getSignature ( ) ) . bind ( "creation" , new Timestamp ( transactionData . getTimestamp ( ) ) ) ;
try {
saver . execute ( repository ) ;
} catch ( SQLException e ) {
@ -801,9 +856,13 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
@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 ) . bind ( "tx_group_id" , transactionData . getTxGroupId ( ) ) ;
// Do not include "height" as that is modified a different way
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 ) . bind ( "tx_group_id" , transactionData . getTxGroupId ( ) ) ;
try {
saver . execute ( this . repository ) ;
} catch ( SQLException e ) {