From 3e3c0affb0dbc9c6c97c0dacb17fd6917ed7a58c Mon Sep 17 00:00:00 2001 From: catbref Date: Tue, 7 Jan 2020 14:26:54 +0000 Subject: [PATCH] Change auto-update to required approved tx created by non-admin. Previously it was possible broadcast an auto-update tx that was created by a 'dev' group admin. Now the auto-update tx must be created by a 'dev' group non-admin/owner and hence require group approval before it takes effect. To this end, a new TransactionRepository.getLatestAutoUpdateTransaction() method has been added to simplify finding the latest matching, approved auto-update transaction. Corresponding changes also made to AutoUpdate.run() --- .../java/org/qora/controller/AutoUpdate.java | 10 ++--- .../repository/TransactionRepository.java | 38 ++++++++++++++++ .../HSQLDBTransactionRepository.java | 45 +++++++++++++++++++ 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/qora/controller/AutoUpdate.java b/src/main/java/org/qora/controller/AutoUpdate.java index 92b337d9..77236d6d 100644 --- a/src/main/java/org/qora/controller/AutoUpdate.java +++ b/src/main/java/org/qora/controller/AutoUpdate.java @@ -19,7 +19,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.qora.ApplyUpdate; import org.qora.api.ApiRequest; -import org.qora.api.resource.TransactionsResource.ConfirmationStatus; import org.qora.data.transaction.ArbitraryTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.gui.SysTray; @@ -45,7 +44,6 @@ public class AutoUpdate extends Thread { private static final int DEV_GROUP_ID = 1; private static final int UPDATE_SERVICE = 1; - private static final List ARBITRARY_TX_TYPE = Arrays.asList(TransactionType.ARBITRARY); private static final int GIT_COMMIT_HASH_LENGTH = 20; // SHA-1 private static final int EXPECTED_DATA_LENGTH = Transformer.TIMESTAMP_LENGTH + GIT_COMMIT_HASH_LENGTH + Transformer.SHA256_LENGTH; @@ -81,7 +79,7 @@ public class AutoUpdate extends Thread { return; } - // Try to clean up any leftover downloads (but if we are/have attempted update then don't delete new JAR) + // Try to clean up any leftover downloads (but if have attempted update then don't delete new JAR) if (!attemptedUpdate) try { Path newJar = Paths.get(NEW_JAR_FILENAME); @@ -92,11 +90,11 @@ public class AutoUpdate extends Thread { // Look for "update" tx which is arbitrary tx in dev-group with service 1 and timestamp later than buildTimestamp try (final Repository repository = RepositoryManager.getRepository()) { - List signatures = repository.getTransactionRepository().getSignaturesMatchingCriteria(null, null, DEV_GROUP_ID, ARBITRARY_TX_TYPE, UPDATE_SERVICE, null, ConfirmationStatus.CONFIRMED, 1, null, true); - if (signatures == null || signatures.isEmpty()) + byte[] signature = repository.getTransactionRepository().getLatestAutoUpdateTransaction(TransactionType.ARBITRARY, DEV_GROUP_ID, UPDATE_SERVICE); + if (signature == null) continue; - TransactionData transactionData = repository.getTransactionRepository().fromSignature(signatures.get(0)); + TransactionData transactionData = repository.getTransactionRepository().fromSignature(signature); if (!(transactionData instanceof ArbitraryTransactionData)) continue; diff --git a/src/main/java/org/qora/repository/TransactionRepository.java b/src/main/java/org/qora/repository/TransactionRepository.java index 960069b3..676a058d 100644 --- a/src/main/java/org/qora/repository/TransactionRepository.java +++ b/src/main/java/org/qora/repository/TransactionRepository.java @@ -47,10 +47,48 @@ public interface TransactionRepository { */ public Map getTransactionSummary(int startHeight, int endHeight) throws DataException; + /** + * Returns signatures for transactions that match search criteria. + *

+ * If blockLimit is specified, and startBlock is null, + * then startBlock is assumed to be 1 or max-block-height, + * depending on reverse being false or true + * respectively. + * + * @param startBlock height of first block to check + * @param blockLimit number of blocks (from startBlock) to check + * @param txGroupId + * @param txTypes + * @param service arbitrary transaction service ID + * @param address + * @param confirmationStatus + * @param limit + * @param offset + * @param reverse + * @return + * @throws DataException + */ public List getSignaturesMatchingCriteria(Integer startBlock, Integer blockLimit, Integer txGroupId, List txTypes, Integer service, String address, ConfirmationStatus confirmationStatus, Integer limit, Integer offset, Boolean reverse) throws DataException; + /** + * Returns signature for latest auto-update transaction. + *

+ * Transaction must be CONFIRMED and APPROVED + * and also not created by group admin/owner. + *

+ * We can check the latter by testing for transaction's approvalHeight + * being greater than blockHeight. + * + * @param txType + * @param txGroupId + * @param service + * @return + * @throws DataException + */ + public byte[] getLatestAutoUpdateTransaction(TransactionType txType, int txGroupId, Integer service) throws DataException; + /** * Returns list of transactions relating to specific asset ID. * diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java index f1f921d6..683823d4 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java @@ -512,6 +512,51 @@ public class HSQLDBTransactionRepository implements TransactionRepository { } } + @Override + public byte[] getLatestAutoUpdateTransaction(TransactionType txType, int txGroupId, Integer service) throws DataException { + StringBuilder sql = new StringBuilder(1024); + sql.append("SELECT Transactions.signature FROM Transactions"); + + if (service != null) { + // This is for ARBITRARY transactions + sql.append(" LEFT OUTER JOIN ArbitraryTransactions ON ArbitraryTransactions.signature = Transactions.signature"); + } + + sql.append(" WHERE type = "); + // Enum int value safe to use literally + sql.append(txType.value); + + sql.append(" AND tx_group_id = "); + // int value safe to use literally + sql.append(txGroupId); + + if (service != null) { + // This is for ARBITRARY transactions + sql.append(" AND service = "); + // int value safe to use literally + sql.append(service); + } + + // "approvalHeight > blockHeight" filters out 'auto-approved' transactions, i.e. those by group admins/owner + sql.append(" AND block_height IS NOT NULL AND approval_height IS NOT NULL AND approval_height > block_height"); + + // we want approved, not rejected! + sql.append(" AND approval_status = "); + // Enum int value safe to use literally + sql.append(ApprovalStatus.APPROVED.value); + + sql.append(" ORDER BY creation DESC LIMIT 1"); + + try (ResultSet resultSet = this.repository.checkedExecute(sql.toString())) { + if (resultSet == null) + return null; + + return resultSet.getBytes(1); + } catch (SQLException e) { + throw new DataException("Unable to fetch latest auto-update transaction signature from repository", e); + } + } + @Override public List getAssetTransactions(long assetId, ConfirmationStatus confirmationStatus, Integer limit, Integer offset, Boolean reverse) throws DataException {