diff --git a/src/main/java/org/qora/block/Block.java b/src/main/java/org/qora/block/Block.java index 1d0a6b3e..7194dc69 100644 --- a/src/main/java/org/qora/block/Block.java +++ b/src/main/java/org/qora/block/Block.java @@ -31,6 +31,7 @@ import org.qora.repository.ATRepository; import org.qora.repository.BlockRepository; import org.qora.repository.DataException; import org.qora.repository.Repository; +import org.qora.repository.TransactionRepository; import org.qora.transaction.AtTransaction; import org.qora.transaction.Transaction; import org.qora.transaction.Transaction.ApprovalStatus; @@ -1064,21 +1065,7 @@ public class Block { processBlockRewards(); // Process transactions (we'll link them to this block after saving the block itself) - // AT-generated transactions are already prepended to our transactions at this point. - List transactions = this.getTransactions(); - for (Transaction transaction : transactions) { - // AT_TRANSACTIONs are created locally and need saving into repository before processing - if (transaction.getTransactionData().getType() == TransactionType.AT) - this.repository.getTransactionRepository().save(transaction.getTransactionData()); - - // Only process transactions that don't require group-approval. - // Group-approval transactions are dealt with later. - if (transaction.getTransactionData().getApprovalStatus() == ApprovalStatus.NOT_REQUIRED) - transaction.process(); - - // Regardless of group-approval, update relevant info for creator (e.g. lastReference) - transaction.processReferencesAndFees(); - } + processTransactions(); // Group-approval transactions processGroupApprovalTransactions(); @@ -1087,86 +1074,19 @@ public class Block { rewardTransactionFees(); // Process AT fees and save AT states into repository - ATRepository atRepository = this.repository.getATRepository(); - for (ATStateData atState : this.getATStates()) { - Account atAccount = new Account(this.repository, atState.getATAddress()); - - // Subtract AT-generated fees from AT accounts - atAccount.setConfirmedBalance(Asset.QORA, atAccount.getConfirmedBalance(Asset.QORA).subtract(atState.getFees())); - - atRepository.save(atState); - } + processAtFeesAndStates(); // Link block into blockchain by fetching signature of highest block and setting that as our reference BlockData latestBlockData = this.repository.getBlockRepository().fromHeight(blockchainHeight); if (latestBlockData != null) this.blockData.setReference(latestBlockData.getSignature()); + // Save block this.repository.getBlockRepository().save(this.blockData); // Link transactions to this block, thus removing them from unconfirmed transactions list. // Also update "transaction participants" in repository for "transactions involving X" support in API - for (int sequence = 0; sequence < transactions.size(); ++sequence) { - Transaction transaction = transactions.get(sequence); - - // Link transaction to this block - BlockTransactionData blockTransactionData = new BlockTransactionData(this.getSignature(), sequence, - transaction.getTransactionData().getSignature()); - this.repository.getBlockRepository().save(blockTransactionData); - - // Update transaction's height in repository - this.repository.getTransactionRepository().updateBlockHeight(transaction.getTransactionData().getSignature(), this.blockData.getHeight()); - // Update local transactionData's height too - transaction.getTransactionData().setBlockHeight(this.blockData.getHeight()); - - // No longer unconfirmed - this.repository.getTransactionRepository().confirmTransaction(transaction.getTransactionData().getSignature()); - - List participants = transaction.getInvolvedAccounts(); - List participantAddresses = participants.stream().map(account -> account.getAddress()).collect(Collectors.toList()); - this.repository.getTransactionRepository().saveParticipants(transaction.getTransactionData(), participantAddresses); - } - } - - protected void processGroupApprovalTransactions() throws DataException { - // Search for pending transactions that have now expired - List approvalExpiringTransactions = this.repository.getTransactionRepository().getApprovalExpiringTransactions(this.blockData.getHeight()); - for (TransactionData transactionData : approvalExpiringTransactions) { - transactionData.setApprovalStatus(ApprovalStatus.EXPIRED); - this.repository.getTransactionRepository().save(transactionData); - } - - // Search for pending transactions within min/max block delay range - List approvalPendingTransactions = this.repository.getTransactionRepository().getApprovalPendingTransactions(this.blockData.getHeight()); - for (TransactionData transactionData : approvalPendingTransactions) { - Transaction transaction = Transaction.fromData(this.repository, transactionData); - - // something like: - Boolean isApproved = transaction.getApprovalDecision(); - - if (isApproved == null) - continue; // approve/reject threshold not yet met - - if (!isApproved) { - // REJECT - transactionData.setApprovalStatus(ApprovalStatus.REJECTED); - this.repository.getTransactionRepository().save(transactionData); - continue; - } - - // Approved, but check transaction can still be processed - if (transaction.isProcessable() != Transaction.ValidationResult.OK) { - transactionData.setApprovalStatus(ApprovalStatus.INVALID); - this.repository.getTransactionRepository().save(transactionData); - continue; - } - - // APPROVED, in which case do transaction.process(); - transactionData.setApprovalStatus(ApprovalStatus.APPROVED); - this.repository.getTransactionRepository().save(transactionData); - - transaction.process(); - } + linkTransactionsToBlock(); } protected void processBlockRewards() throws DataException { @@ -1194,6 +1114,76 @@ public class Block { this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).add(reward)); } + protected void processTransactions() throws DataException { + // Process transactions (we'll link them to this block after saving the block itself) + // AT-generated transactions are already prepended to our transactions at this point. + List transactions = this.getTransactions(); + + for (Transaction transaction : transactions) { + TransactionData transactionData = transaction.getTransactionData(); + + // AT_TRANSACTIONs are created locally and need saving into repository before processing + if (transactionData.getType() == TransactionType.AT) + this.repository.getTransactionRepository().save(transactionData); + + // Only process transactions that don't require group-approval. + // Group-approval transactions are dealt with later. + if (transactionData.getApprovalStatus() == ApprovalStatus.NOT_REQUIRED) + transaction.process(); + + // Regardless of group-approval, update relevant info for creator (e.g. lastReference) + transaction.processReferencesAndFees(); + } + } + + protected void processGroupApprovalTransactions() throws DataException { + TransactionRepository transactionRepository = this.repository.getTransactionRepository(); + + // Search for pending transactions that have now expired + List approvalExpiringTransactions = transactionRepository.getApprovalExpiringTransactions(this.blockData.getHeight()); + + for (TransactionData transactionData : approvalExpiringTransactions) { + transactionData.setApprovalStatus(ApprovalStatus.EXPIRED); + transactionRepository.save(transactionData); + } + + // Search for pending transactions within min/max block delay range + List approvalPendingTransactions = transactionRepository.getApprovalPendingTransactions(this.blockData.getHeight()); + + for (TransactionData transactionData : approvalPendingTransactions) { + Transaction transaction = Transaction.fromData(this.repository, transactionData); + + // something like: + Boolean isApproved = transaction.getApprovalDecision(); + + if (isApproved == null) + continue; // approve/reject threshold not yet met + + // Update approval height for transaction in repository + transactionRepository.updateApprovalHeight(transactionData.getSignature(), this.blockData.getHeight()); + + if (!isApproved) { + // REJECT + transactionData.setApprovalStatus(ApprovalStatus.REJECTED); + transactionRepository.save(transactionData); + continue; + } + + // Approved, but check transaction can still be processed + if (transaction.isProcessable() != Transaction.ValidationResult.OK) { + transactionData.setApprovalStatus(ApprovalStatus.INVALID); + transactionRepository.save(transactionData); + continue; + } + + // APPROVED, in which case do transaction.process(); + transactionData.setApprovalStatus(ApprovalStatus.APPROVED); + transactionRepository.save(transactionData); + + transaction.process(); + } + } + protected void rewardTransactionFees() throws DataException { BigDecimal blockFees = this.blockData.getTotalFees(); @@ -1219,32 +1209,57 @@ public class Block { this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).add(blockFees)); } + protected void processAtFeesAndStates() throws DataException { + ATRepository atRepository = this.repository.getATRepository(); + + for (ATStateData atState : this.getATStates()) { + Account atAccount = new Account(this.repository, atState.getATAddress()); + + // Subtract AT-generated fees from AT accounts + atAccount.setConfirmedBalance(Asset.QORA, atAccount.getConfirmedBalance(Asset.QORA).subtract(atState.getFees())); + + atRepository.save(atState); + } + } + + protected void linkTransactionsToBlock() throws DataException { + TransactionRepository transactionRepository = this.repository.getTransactionRepository(); + + for (int sequence = 0; sequence < transactions.size(); ++sequence) { + Transaction transaction = transactions.get(sequence); + TransactionData transactionData = transaction.getTransactionData(); + + // Link transaction to this block + BlockTransactionData blockTransactionData = new BlockTransactionData(this.getSignature(), sequence, + transactionData.getSignature()); + this.repository.getBlockRepository().save(blockTransactionData); + + // Update transaction's height in repository + transactionRepository.updateBlockHeight(transactionData.getSignature(), this.blockData.getHeight()); + + // Update local transactionData's height too + transaction.getTransactionData().setBlockHeight(this.blockData.getHeight()); + + // No longer unconfirmed + transactionRepository.confirmTransaction(transactionData.getSignature()); + + List participants = transaction.getInvolvedAccounts(); + List participantAddresses = participants.stream().map(account -> account.getAddress()).collect(Collectors.toList()); + transactionRepository.saveParticipants(transactionData, participantAddresses); + } + } + /** * Removes block from blockchain undoing transactions and adding them to unconfirmed pile. * * @throws DataException */ public void orphan() throws DataException { - // Orphan transactions in reverse order, and unlink them from this block - // AT-generated transactions are already added to our transactions so no special handling is needed here. - List transactions = this.getTransactions(); - for (int sequence = transactions.size() - 1; sequence >= 0; --sequence) { - Transaction transaction = transactions.get(sequence); - transaction.orphan(); + // Orphan, and unlink, transactions from this block + orphanTransactionsFromBlock(); - // Unlink transaction from this block - BlockTransactionData blockTransactionData = new BlockTransactionData(this.getSignature(), sequence, - transaction.getTransactionData().getSignature()); - this.repository.getBlockRepository().delete(blockTransactionData); - - // Add to unconfirmed pile, or delete if AT_TRANSACTION - if (transaction.getTransactionData().getType() == TransactionType.AT) - this.repository.getTransactionRepository().delete(transaction.getTransactionData()); - else - this.repository.getTransactionRepository().unconfirmTransaction(transaction.getTransactionData()); - - this.repository.getTransactionRepository().deleteParticipants(transaction.getTransactionData()); - } + // Undo any group-approval decisions that happen at this block + orphanGroupApprovalTransactions(); // Block rewards removed after transactions undone orphanBlockRewards(); @@ -1253,18 +1268,70 @@ public class Block { deductTransactionFees(); // Return AT fees and delete AT states from repository - ATRepository atRepository = this.repository.getATRepository(); - for (ATStateData atState : this.getATStates()) { - Account atAccount = new Account(this.repository, atState.getATAddress()); - - // Return AT-generated fees to AT accounts - atAccount.setConfirmedBalance(Asset.QORA, atAccount.getConfirmedBalance(Asset.QORA).add(atState.getFees())); - } - // Delete ATStateData for this height - atRepository.deleteATStates(this.blockData.getHeight()); + orphanAtFeesAndStates(); // Delete block from blockchain this.repository.getBlockRepository().delete(this.blockData); + this.blockData.setHeight(null); + } + + protected void orphanTransactionsFromBlock() throws DataException { + TransactionRepository transactionRepository = this.repository.getTransactionRepository(); + + // AT-generated transactions are already added to our transactions so no special handling is needed here. + List transactions = this.getTransactions(); + + for (int sequence = transactions.size() - 1; sequence >= 0; --sequence) { + Transaction transaction = transactions.get(sequence); + TransactionData transactionData = transaction.getTransactionData(); + + // Orphan transaction + // Only orphan transactions that didn't require group-approval. + // Group-approval transactions are dealt with later. + if (transactionData.getApprovalStatus() == ApprovalStatus.NOT_REQUIRED) + transaction.orphan(); + + // Regardless of group-approval, update relevant info for creator (e.g. lastReference) + transaction.orphanReferencesAndFees(); + + // Unlink transaction from this block + BlockTransactionData blockTransactionData = new BlockTransactionData(this.getSignature(), sequence, + transactionData.getSignature()); + this.repository.getBlockRepository().delete(blockTransactionData); + + // Add to unconfirmed pile and remove height, or delete if AT_TRANSACTION + if (transaction.getTransactionData().getType() == TransactionType.AT) { + transactionRepository.delete(transactionData); + } else { + // Add to unconfirmed pile + transactionRepository.unconfirmTransaction(transactionData); + + // Unset height + transactionRepository.updateBlockHeight(transactionData.getSignature(), null); + } + + transactionRepository.deleteParticipants(transactionData); + } + } + + protected void orphanGroupApprovalTransactions() throws DataException { + TransactionRepository transactionRepository = this.repository.getTransactionRepository(); + + // Find all transactions where decision happened at this block height + List transactions = transactionRepository.getApprovalTransactionDecidedAtHeight(this.blockData.getHeight()); + + for (TransactionData transactionData : transactions) { + // Orphan/un-process transaction + Transaction transaction = Transaction.fromData(repository, transactionData); + transaction.orphan(); + + // Revert back to PENDING + transactionData.setApprovalStatus(ApprovalStatus.PENDING); + transactionRepository.save(transactionData); + + // Undo approval decision height + transactionRepository.updateApprovalHeight(transactionData.getSignature(), null); + } } protected void orphanBlockRewards() throws DataException { @@ -1317,6 +1384,19 @@ public class Block { this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).subtract(blockFees)); } + protected void orphanAtFeesAndStates() throws DataException { + ATRepository atRepository = this.repository.getATRepository(); + for (ATStateData atState : this.getATStates()) { + Account atAccount = new Account(this.repository, atState.getATAddress()); + + // Return AT-generated fees to AT accounts + atAccount.setConfirmedBalance(Asset.QORA, atAccount.getConfirmedBalance(Asset.QORA).add(atState.getFees())); + } + + // Delete ATStateData for this height + atRepository.deleteATStates(this.blockData.getHeight()); + } + protected BigDecimal getRewardAtHeight(int ourHeight) { List rewardsByHeight = BlockChain.getInstance().getBlockRewardsByHeight(); diff --git a/src/main/java/org/qora/payment/Payment.java b/src/main/java/org/qora/payment/Payment.java index ea0fd348..ee40dfa2 100644 --- a/src/main/java/org/qora/payment/Payment.java +++ b/src/main/java/org/qora/payment/Payment.java @@ -33,6 +33,9 @@ public class Payment { // Processing + + // isValid + /** Are payments valid? */ public ValidationResult isValid(byte[] senderPublicKey, List payments, BigDecimal fee, boolean isZeroAmountValid) throws DataException { AssetRepository assetRepository = this.repository.getAssetRepository(); @@ -115,6 +118,8 @@ public class Payment { return isValid(senderPublicKey, paymentData, fee, false); } + // isProcessable + /** Are multiple payments processable? */ public ValidationResult isProcessable(byte[] senderPublicKey, List payments, BigDecimal fee, boolean isZeroAmountValid) throws DataException { // Essentially the same as isValid... @@ -136,6 +141,8 @@ public class Payment { return isProcessable(senderPublicKey, paymentData, fee, false); } + // process + /** Multiple payment processing */ public void process(byte[] senderPublicKey, List payments, BigDecimal fee, byte[] signature) throws DataException { @@ -162,6 +169,8 @@ public class Payment { process(senderPublicKey, Collections.singletonList(paymentData), fee, signature); } + // processReferenceAndFees + /** Multiple payment reference processing */ public void processReferencesAndFees(byte[] senderPublicKey, List payments, BigDecimal fee, byte[] signature, boolean alwaysInitializeRecipientReference) throws DataException { @@ -173,7 +182,7 @@ public class Payment { // Update sender's reference sender.setLastReference(signature); - // Process all payments + // Process all recipients for (PaymentData paymentData : payments) { Account recipient = new Account(this.repository, paymentData.getRecipient()); @@ -191,16 +200,11 @@ public class Payment { processReferencesAndFees(senderPublicKey, Collections.singletonList(payment), fee, signature, alwaysInitializeRecipientReference); } - public void orphan(byte[] senderPublicKey, List payments, BigDecimal fee, byte[] signature, byte[] reference, - boolean alwaysUninitializeRecipientReference) throws DataException { + // orphan + + public void orphan(byte[] senderPublicKey, List payments, BigDecimal fee, byte[] signature, byte[] reference) throws DataException { Account sender = new PublicKeyAccount(this.repository, senderPublicKey); - // Update sender's balance due to fee - sender.setConfirmedBalance(Asset.QORA, sender.getConfirmedBalance(Asset.QORA).add(fee)); - - // Update sender's reference - sender.setLastReference(reference); - // Orphan all payments for (PaymentData paymentData : payments) { Account recipient = new Account(this.repository, paymentData.getRecipient()); @@ -212,6 +216,29 @@ public class Payment { // Update recipient's balance recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).subtract(amount)); + } + } + + public void orphan(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature, byte[] reference) throws DataException { + orphan(senderPublicKey, Collections.singletonList(paymentData), fee, signature, reference); + } + + // orphanReferencesAndFees + + public void orphanReferencesAndFees(byte[] senderPublicKey, List payments, BigDecimal fee, byte[] signature, byte[] reference, + boolean alwaysUninitializeRecipientReference) throws DataException { + Account sender = new PublicKeyAccount(this.repository, senderPublicKey); + + // Update sender's balance due to fee + sender.setConfirmedBalance(Asset.QORA, sender.getConfirmedBalance(Asset.QORA).add(fee)); + + // Update sender's reference + sender.setLastReference(reference); + + // Orphan all recipients + for (PaymentData paymentData : payments) { + Account recipient = new Account(this.repository, paymentData.getRecipient()); + long assetId = paymentData.getAssetId(); /* * For QORA amounts only: If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own @@ -222,9 +249,9 @@ public class Payment { } } - public void orphan(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature, byte[] reference, + public void orphanReferencesAndFees(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature, byte[] reference, boolean alwaysUninitializeRecipientReference) throws DataException { - orphan(senderPublicKey, Collections.singletonList(paymentData), fee, signature, reference, alwaysUninitializeRecipientReference); + orphanReferencesAndFees(senderPublicKey, Collections.singletonList(paymentData), fee, signature, reference, alwaysUninitializeRecipientReference); } } diff --git a/src/main/java/org/qora/repository/TransactionRepository.java b/src/main/java/org/qora/repository/TransactionRepository.java index b8ab4b8f..d2830c8a 100644 --- a/src/main/java/org/qora/repository/TransactionRepository.java +++ b/src/main/java/org/qora/repository/TransactionRepository.java @@ -113,6 +113,9 @@ public interface TransactionRepository { */ public List getApprovalExpiringTransactions(int blockHeight) throws DataException; + /** Returns list of transactions that had group-approval decided at passed block height. */ + public List getApprovalTransactionDecidedAtHeight(int approvalHeight) throws DataException; + /** Returns latest approval decision by given admin for given pending transaction signature. */ public GroupApprovalTransactionData getLatestApproval(byte[] pendingSignature, byte[] adminPublicKey) throws DataException; diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBATRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBATRepository.java index f62c2fe3..57278eb4 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBATRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBATRepository.java @@ -25,9 +25,9 @@ public class HSQLDBATRepository implements ATRepository { @Override public ATData fromATAddress(String atAddress) throws DataException { - try (ResultSet resultSet = this.repository.checkedExecute( - "SELECT creator, creation, version, asset_id, code_bytes, is_sleeping, sleep_until_height, is_finished, had_fatal_error, is_frozen, frozen_balance FROM ATs WHERE AT_address = ?", - atAddress)) { + final String sql = "SELECT creator, creation, version, asset_id, code_bytes, is_sleeping, sleep_until_height, is_finished, had_fatal_error, is_frozen, frozen_balance FROM ATs WHERE AT_address = ?"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, atAddress)) { if (resultSet == null) return null; @@ -39,7 +39,7 @@ public class HSQLDBATRepository implements ATRepository { boolean isSleeping = resultSet.getBoolean(6); Integer sleepUntilHeight = resultSet.getInt(7); - if (resultSet.wasNull()) + if (sleepUntilHeight == 0 && resultSet.wasNull()) sleepUntilHeight = null; boolean isFinished = resultSet.getBoolean(8); @@ -47,8 +47,6 @@ public class HSQLDBATRepository implements ATRepository { boolean isFrozen = resultSet.getBoolean(10); BigDecimal frozenBalance = resultSet.getBigDecimal(11); - if (resultSet.wasNull()) - frozenBalance = null; return new ATData(atAddress, creatorPublicKey, creation, version, assetId, codeBytes, isSleeping, sleepUntilHeight, isFinished, hadFatalError, isFrozen, frozenBalance); @@ -68,10 +66,11 @@ public class HSQLDBATRepository implements ATRepository { @Override public List getAllExecutableATs() throws DataException { + final String sql = "SELECT AT_address, creator, creation, version, asset_id, code_bytes, is_sleeping, sleep_until_height, had_fatal_error, is_frozen, frozen_balance FROM ATs WHERE is_finished = false ORDER BY creation ASC"; + List executableATs = new ArrayList(); - try (ResultSet resultSet = this.repository.checkedExecute( - "SELECT AT_address, creator, creation, version, asset_id, code_bytes, is_sleeping, sleep_until_height, had_fatal_error, is_frozen, frozen_balance FROM ATs WHERE is_finished = false ORDER BY creation ASC")) { + try (ResultSet resultSet = this.repository.checkedExecute(sql)) { if (resultSet == null) return executableATs; @@ -87,15 +86,13 @@ public class HSQLDBATRepository implements ATRepository { boolean isSleeping = resultSet.getBoolean(7); Integer sleepUntilHeight = resultSet.getInt(8); - if (resultSet.wasNull()) + if (sleepUntilHeight == 0 && resultSet.wasNull()) sleepUntilHeight = null; boolean hadFatalError = resultSet.getBoolean(9); boolean isFrozen = resultSet.getBoolean(10); BigDecimal frozenBalance = resultSet.getBigDecimal(11); - if (resultSet.wasNull()) - frozenBalance = null; ATData atData = new ATData(atAddress, creatorPublicKey, creation, version, assetId, codeBytes, isSleeping, sleepUntilHeight, isFinished, hadFatalError, isFrozen, frozenBalance); @@ -111,9 +108,9 @@ public class HSQLDBATRepository implements ATRepository { @Override public Integer getATCreationBlockHeight(String atAddress) throws DataException { - try (ResultSet resultSet = this.repository.checkedExecute( - "SELECT height from DeployATTransactions JOIN BlockTransactions ON transaction_signature = signature JOIN Blocks ON Blocks.signature = block_signature WHERE AT_address = ?", - atAddress)) { + final String sql = "SELECT height from DeployATTransactions JOIN BlockTransactions ON transaction_signature = signature JOIN Blocks ON Blocks.signature = block_signature WHERE AT_address = ?"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, atAddress)) { if (resultSet == null) return null; diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBGroupRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBGroupRepository.java index 1841ea49..9f7d1dbf 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBGroupRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBGroupRepository.java @@ -29,8 +29,9 @@ public class HSQLDBGroupRepository implements GroupRepository { @Override public GroupData fromGroupId(int groupId) throws DataException { - try (ResultSet resultSet = this.repository - .checkedExecute("SELECT group_name, owner, description, created, updated, reference, is_open, approval_threshold, min_block_delay, max_block_delay, creation_group_id FROM Groups WHERE group_id = ?", groupId)) { + final String sql = "SELECT group_name, owner, description, created, updated, reference, is_open, approval_threshold, min_block_delay, max_block_delay, creation_group_id FROM Groups WHERE group_id = ?"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, groupId)) { if (resultSet == null) return null; @@ -41,7 +42,7 @@ public class HSQLDBGroupRepository implements GroupRepository { // Special handling for possibly-NULL "updated" column Timestamp updatedTimestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)); - Long updated = resultSet.wasNull() ? null : updatedTimestamp.getTime(); + Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime(); byte[] reference = resultSet.getBytes(6); boolean isOpen = resultSet.getBoolean(7); @@ -73,7 +74,7 @@ public class HSQLDBGroupRepository implements GroupRepository { // Special handling for possibly-NULL "updated" column Timestamp updatedTimestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)); - Long updated = resultSet.wasNull() ? null : updatedTimestamp.getTime(); + Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime(); byte[] reference = resultSet.getBytes(6); boolean isOpen = resultSet.getBoolean(7); @@ -131,7 +132,7 @@ public class HSQLDBGroupRepository implements GroupRepository { // Special handling for possibly-NULL "updated" column Timestamp updatedTimestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)); - Long updated = resultSet.wasNull() ? null : updatedTimestamp.getTime(); + Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime(); byte[] reference = resultSet.getBytes(7); boolean isOpen = resultSet.getBoolean(8); diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBNetworkRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBNetworkRepository.java index 4a75541e..f3d10ecf 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBNetworkRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBNetworkRepository.java @@ -38,7 +38,7 @@ public class HSQLDBNetworkRepository implements NetworkRepository { Long lastAttempted = HSQLDBRepository.getZonedTimestampMilli(resultSet, 3); Integer lastHeight = resultSet.getInt(4); - if (resultSet.wasNull()) + if (lastHeight == 0 && resultSet.wasNull()) lastHeight = null; byte[] lastBlockSignature = resultSet.getBytes(5); 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 5f6d418b..39ce7f4a 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java @@ -736,6 +736,34 @@ public class HSQLDBTransactionRepository implements TransactionRepository { } } + @Override + public List getApprovalTransactionDecidedAtHeight(int approvalHeight) throws DataException { + final String sql = "SELECT signature from Transactions WHERE approval_height = ?"; + + List transactions = new ArrayList<>(); + + try (ResultSet resultSet = this.repository.checkedExecute(sql, approvalHeight)) { + 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-decided transaction from repository?"); + + transactions.add(transactionData); + } while (resultSet.next()); + + return transactions; + } catch (SQLException | DataException e) { + throw new DataException("Unable to fetch approval-decided transactions from repository", e); + } + } + @Override public GroupApprovalTransactionData getLatestApproval(byte[] pendingSignature, byte[] adminPublicKey) throws DataException { String sql = "SELECT signature FROM GroupApprovalTransactions " diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java index 1e1256eb..f2a67a95 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java @@ -20,7 +20,7 @@ public class HSQLDBTransferAssetTransactionRepository extends HSQLDBTransactionR TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException { String sql = "SELECT recipient, asset_id, amount, asset_name FROM TransferAssetTransactions JOIN Assets USING (asset_id) WHERE signature = ?"; - try (ResultSet resultSet = this.repository.checkedExecute(sql)) { + try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) { if (resultSet == null) return null; diff --git a/src/main/java/org/qora/transaction/AccountFlagsTransaction.java b/src/main/java/org/qora/transaction/AccountFlagsTransaction.java index 34eb233a..9ecbfe3e 100644 --- a/src/main/java/org/qora/transaction/AccountFlagsTransaction.java +++ b/src/main/java/org/qora/transaction/AccountFlagsTransaction.java @@ -120,14 +120,6 @@ public class AccountFlagsTransaction extends Transaction { // Remove previous flags from transaction itself accountFlagsTransactionData.setPreviousFlags(null); this.repository.getTransactionRepository().save(accountFlagsTransactionData); - - Account creator = getCreator(); - - // Update creator's balance - creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(accountFlagsTransactionData.getFee())); - - // Update creator's reference - creator.setLastReference(accountFlagsTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/AddGroupAdminTransaction.java b/src/main/java/org/qora/transaction/AddGroupAdminTransaction.java index 1c5bddf4..568a35b6 100644 --- a/src/main/java/org/qora/transaction/AddGroupAdminTransaction.java +++ b/src/main/java/org/qora/transaction/AddGroupAdminTransaction.java @@ -113,8 +113,6 @@ public class AddGroupAdminTransaction extends Transaction { // Update Group adminship Group group = new Group(this.repository, addGroupAdminTransactionData.getGroupId()); group.promoteToAdmin(addGroupAdminTransactionData); - - // We would save updated transaction at this point, but it hasn't been modified } @Override @@ -122,15 +120,6 @@ public class AddGroupAdminTransaction extends Transaction { // Revert group adminship Group group = new Group(this.repository, addGroupAdminTransactionData.getGroupId()); group.unpromoteToAdmin(addGroupAdminTransactionData); - - // We would save updated transaction at this point, but it hasn't been modified - - // Update owner's balance - Account owner = getOwner(); - owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).add(addGroupAdminTransactionData.getFee())); - - // Update owner's reference - owner.setLastReference(addGroupAdminTransactionData.getReference()); } } \ No newline at end of file diff --git a/src/main/java/org/qora/transaction/ArbitraryTransaction.java b/src/main/java/org/qora/transaction/ArbitraryTransaction.java index 8fda1acc..1a60d2d5 100644 --- a/src/main/java/org/qora/transaction/ArbitraryTransaction.java +++ b/src/main/java/org/qora/transaction/ArbitraryTransaction.java @@ -116,8 +116,6 @@ public class ArbitraryTransaction extends Transaction { @Override public void process() throws DataException { - // We would save updated transaction at this point, but it hasn't been modified - // Wrap and delegate payment processing to Payment class. new Payment(this.repository).process(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(), arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature()); @@ -132,11 +130,16 @@ public class ArbitraryTransaction extends Transaction { @Override public void orphan() throws DataException { - // Wrap and delegate payment processing to Payment class. Always revert recipients' last references regardless of asset. + // Wrap and delegate payment processing to Payment class. new Payment(this.repository).orphan(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(), - arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature(), arbitraryTransactionData.getReference(), true); + arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature(), arbitraryTransactionData.getReference()); + } - // We would save transaction in orphaned form at this point, but it hasn't been modified + @Override + public void orphanReferencesAndFees() throws DataException { + // Wrap and delegate reference and fee processing to Payment class. Always revert recipients' last references regardless of asset. + new Payment(this.repository).orphanReferencesAndFees(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(), + arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature(), arbitraryTransactionData.getReference(), true); } // Data access diff --git a/src/main/java/org/qora/transaction/AtTransaction.java b/src/main/java/org/qora/transaction/AtTransaction.java index fdf872f2..25f79054 100644 --- a/src/main/java/org/qora/transaction/AtTransaction.java +++ b/src/main/java/org/qora/transaction/AtTransaction.java @@ -161,8 +161,6 @@ public class AtTransaction extends Transaction { @Override public void process() throws DataException { - // We would save updated transaction at this point, but it hasn't been modified - if (this.atTransactionData.getAmount() != null) { Account sender = getATAccount(); Account recipient = getRecipient(); @@ -206,6 +204,17 @@ public class AtTransaction extends Transaction { // Update recipient's balance recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).subtract(amount)); + } + + // As AT_TRANSACTIONs are really part of a block, the caller (Block) will probably delete this transaction after orphaning + } + + @Override + public void orphanReferencesAndFees() throws DataException { + if (this.atTransactionData.getAmount() != null) { + Account recipient = getRecipient(); + + long assetId = this.atTransactionData.getAssetId(); /* * For QORA amounts only: If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own @@ -214,10 +223,6 @@ public class AtTransaction extends Transaction { if (assetId == Asset.QORA && Arrays.equals(recipient.getLastReference(), this.atTransactionData.getSignature())) recipient.setLastReference(null); } - - // We would save updated transaction at this point, but it hasn't been modified - - // As AT_TRANSACTIONs are really part of a block, the caller (Block) will probably delete this transaction after orphaning } } diff --git a/src/main/java/org/qora/transaction/BuyNameTransaction.java b/src/main/java/org/qora/transaction/BuyNameTransaction.java index 69b5693b..07a87f05 100644 --- a/src/main/java/org/qora/transaction/BuyNameTransaction.java +++ b/src/main/java/org/qora/transaction/BuyNameTransaction.java @@ -134,13 +134,6 @@ public class BuyNameTransaction extends Transaction { // Save this transaction, with removed "name reference" this.repository.getTransactionRepository().save(buyNameTransactionData); - - // Update buyer's balance - Account buyer = getBuyer(); - buyer.setConfirmedBalance(Asset.QORA, buyer.getConfirmedBalance(Asset.QORA).add(buyNameTransactionData.getFee())); - - // Update buyer's reference - buyer.setLastReference(buyNameTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/CancelAssetOrderTransaction.java b/src/main/java/org/qora/transaction/CancelAssetOrderTransaction.java index 91954f5e..84433757 100644 --- a/src/main/java/org/qora/transaction/CancelAssetOrderTransaction.java +++ b/src/main/java/org/qora/transaction/CancelAssetOrderTransaction.java @@ -94,8 +94,6 @@ public class CancelAssetOrderTransaction extends Transaction { @Override public void process() throws DataException { - // We would save updated transaction at this point, but it hasn't been modified - // Mark Order as completed so no more trades can happen OrderData orderData = this.repository.getAssetRepository().fromOrderId(cancelOrderTransactionData.getOrderId()); Order order = new Order(this.repository, orderData); @@ -104,16 +102,6 @@ public class CancelAssetOrderTransaction extends Transaction { @Override public void orphan() throws DataException { - Account creator = getCreator(); - - // We would save transaction in orphaned form at this point, but it hasn't been modified - - // Update creator's balance regarding fee - creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(cancelOrderTransactionData.getFee())); - - // Update creator's last reference - creator.setLastReference(cancelOrderTransactionData.getReference()); - // Unmark Order as completed so trades can happen again OrderData orderData = this.repository.getAssetRepository().fromOrderId(cancelOrderTransactionData.getOrderId()); Order order = new Order(this.repository, orderData); diff --git a/src/main/java/org/qora/transaction/CancelGroupBanTransaction.java b/src/main/java/org/qora/transaction/CancelGroupBanTransaction.java index a8d0470c..164f7f78 100644 --- a/src/main/java/org/qora/transaction/CancelGroupBanTransaction.java +++ b/src/main/java/org/qora/transaction/CancelGroupBanTransaction.java @@ -124,13 +124,6 @@ public class CancelGroupBanTransaction extends Transaction { // Save this transaction with removed member/admin references this.repository.getTransactionRepository().save(groupUnbanTransactionData); - - // Update admin's balance - Account admin = getAdmin(); - admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).add(groupUnbanTransactionData.getFee())); - - // Update admin's reference - admin.setLastReference(groupUnbanTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/CancelGroupInviteTransaction.java b/src/main/java/org/qora/transaction/CancelGroupInviteTransaction.java index 7c7d728c..94ab0a55 100644 --- a/src/main/java/org/qora/transaction/CancelGroupInviteTransaction.java +++ b/src/main/java/org/qora/transaction/CancelGroupInviteTransaction.java @@ -124,13 +124,6 @@ public class CancelGroupInviteTransaction extends Transaction { // Save this transaction with removed member/admin references this.repository.getTransactionRepository().save(cancelGroupInviteTransactionData); - - // Update admin's balance - Account admin = getAdmin(); - admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).add(cancelGroupInviteTransactionData.getFee())); - - // Update admin's reference - admin.setLastReference(cancelGroupInviteTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/CancelSellNameTransaction.java b/src/main/java/org/qora/transaction/CancelSellNameTransaction.java index 7bbfd7ab..88756295 100644 --- a/src/main/java/org/qora/transaction/CancelSellNameTransaction.java +++ b/src/main/java/org/qora/transaction/CancelSellNameTransaction.java @@ -121,13 +121,6 @@ public class CancelSellNameTransaction extends Transaction { // Save this transaction, with removed "name reference" this.repository.getTransactionRepository().save(cancelSellNameTransactionData); - - // Update owner's balance - Account owner = getOwner(); - owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).add(cancelSellNameTransactionData.getFee())); - - // Update owner's reference - owner.setLastReference(cancelSellNameTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/CreateAssetOrderTransaction.java b/src/main/java/org/qora/transaction/CreateAssetOrderTransaction.java index 8f561cb2..a61018dd 100644 --- a/src/main/java/org/qora/transaction/CreateAssetOrderTransaction.java +++ b/src/main/java/org/qora/transaction/CreateAssetOrderTransaction.java @@ -178,16 +178,6 @@ public class CreateAssetOrderTransaction extends Transaction { @Override public void process() throws DataException { - Account creator = getCreator(); - - // Update creator's balance due to fee - creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(createOrderTransactionData.getFee())); - - // Update creator's last reference - creator.setLastReference(createOrderTransactionData.getSignature()); - - // We would save updated transaction at this point, but it hasn't been modified - // Order Id is transaction's signature byte[] orderId = createOrderTransactionData.getSignature(); @@ -201,22 +191,12 @@ public class CreateAssetOrderTransaction extends Transaction { @Override public void orphan() throws DataException { - Account creator = getCreator(); - - // Update creator's balance due to fee - creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(createOrderTransactionData.getFee())); - - // Update creator's last reference - creator.setLastReference(createOrderTransactionData.getReference()); - // Order Id is transaction's signature byte[] orderId = createOrderTransactionData.getSignature(); // Orphan the order itself OrderData orderData = this.repository.getAssetRepository().fromOrderId(orderId); new Order(this.repository, orderData).orphan(); - - // We would save updated transaction at this point, but it hasn't been modified } } diff --git a/src/main/java/org/qora/transaction/CreateGroupTransaction.java b/src/main/java/org/qora/transaction/CreateGroupTransaction.java index b0584506..f1701798 100644 --- a/src/main/java/org/qora/transaction/CreateGroupTransaction.java +++ b/src/main/java/org/qora/transaction/CreateGroupTransaction.java @@ -122,7 +122,7 @@ public class CreateGroupTransaction extends Transaction { // Note newly assigned group ID in our transaction record createGroupTransactionData.setGroupId(group.getGroupData().getGroupId()); - // Save this transaction + // Save this transaction with newly assigned group ID this.repository.getTransactionRepository().save(createGroupTransactionData); } @@ -137,13 +137,6 @@ public class CreateGroupTransaction extends Transaction { // Save this transaction with removed group ID this.repository.getTransactionRepository().save(createGroupTransactionData); - - // Update creator's balance - Account creator = getCreator(); - creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(createGroupTransactionData.getFee())); - - // Update creator's reference - creator.setLastReference(createGroupTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/CreatePollTransaction.java b/src/main/java/org/qora/transaction/CreatePollTransaction.java index b2718441..c3e96072 100644 --- a/src/main/java/org/qora/transaction/CreatePollTransaction.java +++ b/src/main/java/org/qora/transaction/CreatePollTransaction.java @@ -153,8 +153,6 @@ public class CreatePollTransaction extends Transaction { // Publish poll to allow voting Poll poll = new Poll(this.repository, createPollTransactionData); poll.publish(); - - // We would save updated transaction at this point, but it hasn't been modified } @Override @@ -162,15 +160,6 @@ public class CreatePollTransaction extends Transaction { // Unpublish poll Poll poll = new Poll(this.repository, createPollTransactionData.getPollName()); poll.unpublish(); - - // We would save updated transaction at this point, but it hasn't been modified - - // Update creator's balance - Account creator = getCreator(); - creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(createPollTransactionData.getFee())); - - // Update creator's reference - creator.setLastReference(createPollTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/DeployAtTransaction.java b/src/main/java/org/qora/transaction/DeployAtTransaction.java index 331a4b7e..767d07d2 100644 --- a/src/main/java/org/qora/transaction/DeployAtTransaction.java +++ b/src/main/java/org/qora/transaction/DeployAtTransaction.java @@ -238,8 +238,6 @@ public class DeployAtTransaction extends Transaction { AT at = new AT(this.repository, this.deployATTransactionData); at.deploy(); - // We would save updated transaction at this point, but it hasn't been modified - long assetId = deployATTransactionData.getAssetId(); // Update creator's balance regarding initial payment to AT @@ -260,17 +258,11 @@ public class DeployAtTransaction extends Transaction { AT at = new AT(this.repository, this.deployATTransactionData); at.undeploy(); - // We would save updated transaction at this point, but it hasn't been modified - long assetId = deployATTransactionData.getAssetId(); - // Update creator's balance + // Update creator's balance regarding initial payment to AT Account creator = getCreator(); creator.setConfirmedBalance(assetId, creator.getConfirmedBalance(assetId).add(deployATTransactionData.getAmount())); - creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(deployATTransactionData.getFee())); - - // Update creator's reference - creator.setLastReference(deployATTransactionData.getReference()); // Delete AT's account (and hence its balance) this.repository.getAccountRepository().delete(this.deployATTransactionData.getAtAddress()); diff --git a/src/main/java/org/qora/transaction/EnableForgingTransaction.java b/src/main/java/org/qora/transaction/EnableForgingTransaction.java index d9190608..dbed9c92 100644 --- a/src/main/java/org/qora/transaction/EnableForgingTransaction.java +++ b/src/main/java/org/qora/transaction/EnableForgingTransaction.java @@ -146,8 +146,6 @@ public class EnableForgingTransaction extends Transaction { target.setFlags(targetFlags); target.setForgingEnabler(creator.getAddress()); - - // We would save updated transaction at this point, but it hasn't been modified } @Override @@ -169,14 +167,6 @@ public class EnableForgingTransaction extends Transaction { target.setFlags(targetFlags); target.setForgingEnabler(null); - - // We would save updated transaction at this point, but it hasn't been modified - - // Update creator's balance - creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(enableForgingTransactionData.getFee())); - - // Update creator's reference - creator.setLastReference(enableForgingTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/GenesisTransaction.java b/src/main/java/org/qora/transaction/GenesisTransaction.java index f6a755f3..00b6e05c 100644 --- a/src/main/java/org/qora/transaction/GenesisTransaction.java +++ b/src/main/java/org/qora/transaction/GenesisTransaction.java @@ -136,8 +136,6 @@ public class GenesisTransaction extends Transaction { @Override public void process() throws DataException { - // We would save updated transaction at this point, but it hasn't been modified - Account recipient = new Account(repository, genesisTransactionData.getRecipient()); // Update recipient's balance @@ -156,10 +154,13 @@ public class GenesisTransaction extends Transaction { @Override public void orphan() throws DataException { - // We would save updated transaction at this point, but it hasn't been modified - // Delete recipient's account (and balance) this.repository.getAccountRepository().delete(genesisTransactionData.getRecipient()); } + @Override + public void orphanReferencesAndFees() throws DataException { + // Recipient's last reference removed thanks to delete() called by orphan() above. + } + } diff --git a/src/main/java/org/qora/transaction/GroupApprovalTransaction.java b/src/main/java/org/qora/transaction/GroupApprovalTransaction.java index 848fd0cc..e3220d53 100644 --- a/src/main/java/org/qora/transaction/GroupApprovalTransaction.java +++ b/src/main/java/org/qora/transaction/GroupApprovalTransaction.java @@ -106,13 +106,6 @@ public class GroupApprovalTransaction extends Transaction { // Save this transaction with removed prior reference groupApprovalTransactionData.setPriorReference(null); this.repository.getTransactionRepository().save(groupApprovalTransactionData); - - // Update admin's balance - Account admin = getAdmin(); - admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).add(groupApprovalTransactionData.getFee())); - - // Update admin's reference - admin.setLastReference(groupApprovalTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/GroupBanTransaction.java b/src/main/java/org/qora/transaction/GroupBanTransaction.java index 01e07619..8516e4a5 100644 --- a/src/main/java/org/qora/transaction/GroupBanTransaction.java +++ b/src/main/java/org/qora/transaction/GroupBanTransaction.java @@ -125,13 +125,6 @@ public class GroupBanTransaction extends Transaction { // Save this transaction with removed member/admin references this.repository.getTransactionRepository().save(groupBanTransactionData); - - // Update admin's balance - Account admin = getAdmin(); - admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).add(groupBanTransactionData.getFee())); - - // Update admin's reference - admin.setLastReference(groupBanTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/GroupInviteTransaction.java b/src/main/java/org/qora/transaction/GroupInviteTransaction.java index a77a2c15..03d6a647 100644 --- a/src/main/java/org/qora/transaction/GroupInviteTransaction.java +++ b/src/main/java/org/qora/transaction/GroupInviteTransaction.java @@ -131,13 +131,6 @@ public class GroupInviteTransaction extends Transaction { // Save this transaction with removed member/admin references this.repository.getTransactionRepository().save(groupInviteTransactionData); - - // Update admin's balance - Account admin = getAdmin(); - admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).add(groupInviteTransactionData.getFee())); - - // Update admin's reference - admin.setLastReference(groupInviteTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/GroupKickTransaction.java b/src/main/java/org/qora/transaction/GroupKickTransaction.java index 96b36bf7..7f10d8e8 100644 --- a/src/main/java/org/qora/transaction/GroupKickTransaction.java +++ b/src/main/java/org/qora/transaction/GroupKickTransaction.java @@ -131,13 +131,6 @@ public class GroupKickTransaction extends Transaction { // Save this transaction with removed member/admin references this.repository.getTransactionRepository().save(groupKickTransactionData); - - // Update admin's balance - Account admin = getAdmin(); - admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).add(groupKickTransactionData.getFee())); - - // Update admin's reference - admin.setLastReference(groupKickTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/IssueAssetTransaction.java b/src/main/java/org/qora/transaction/IssueAssetTransaction.java index eb3ed87c..7de6947f 100644 --- a/src/main/java/org/qora/transaction/IssueAssetTransaction.java +++ b/src/main/java/org/qora/transaction/IssueAssetTransaction.java @@ -142,15 +142,15 @@ public class IssueAssetTransaction extends Transaction { Asset asset = new Asset(this.repository, issueAssetTransactionData); asset.issue(); + // Add asset to owner + Account owner = getOwner(); + owner.setConfirmedBalance(asset.getAssetData().getAssetId(), BigDecimal.valueOf(issueAssetTransactionData.getQuantity()).setScale(8)); + // Note newly assigned asset ID in our transaction record issueAssetTransactionData.setAssetId(asset.getAssetData().getAssetId()); - // Save this transaction, now with corresponding assetId + // Save this transaction with newly assigned assetId this.repository.getTransactionRepository().save(issueAssetTransactionData); - - // Add asset to owner - Account owner = getOwner(); - owner.setConfirmedBalance(issueAssetTransactionData.getAssetId(), BigDecimal.valueOf(issueAssetTransactionData.getQuantity()).setScale(8)); } @Override @@ -168,13 +168,6 @@ public class IssueAssetTransaction extends Transaction { // Save this transaction, with removed assetId this.repository.getTransactionRepository().save(issueAssetTransactionData); - - // Update issuer's balance - Account issuer = getIssuer(); - issuer.setConfirmedBalance(Asset.QORA, issuer.getConfirmedBalance(Asset.QORA).add(issueAssetTransactionData.getFee())); - - // Update issuer's reference - issuer.setLastReference(issueAssetTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/JoinGroupTransaction.java b/src/main/java/org/qora/transaction/JoinGroupTransaction.java index 240d7672..de883a95 100644 --- a/src/main/java/org/qora/transaction/JoinGroupTransaction.java +++ b/src/main/java/org/qora/transaction/JoinGroupTransaction.java @@ -111,13 +111,6 @@ public class JoinGroupTransaction extends Transaction { // Save this transaction with removed references this.repository.getTransactionRepository().save(joinGroupTransactionData); - - // Update joiner's balance - Account joiner = getJoiner(); - joiner.setConfirmedBalance(Asset.QORA, joiner.getConfirmedBalance(Asset.QORA).add(joinGroupTransactionData.getFee())); - - // Update joiner's reference - joiner.setLastReference(joinGroupTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/LeaveGroupTransaction.java b/src/main/java/org/qora/transaction/LeaveGroupTransaction.java index fbc1dad4..5f7a9292 100644 --- a/src/main/java/org/qora/transaction/LeaveGroupTransaction.java +++ b/src/main/java/org/qora/transaction/LeaveGroupTransaction.java @@ -110,13 +110,6 @@ public class LeaveGroupTransaction extends Transaction { // Save this transaction with removed member/admin references this.repository.getTransactionRepository().save(leaveGroupTransactionData); - - // Update leaver's balance - Account leaver = getLeaver(); - leaver.setConfirmedBalance(Asset.QORA, leaver.getConfirmedBalance(Asset.QORA).add(leaveGroupTransactionData.getFee())); - - // Update leaver's reference - leaver.setLastReference(leaveGroupTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/MessageTransaction.java b/src/main/java/org/qora/transaction/MessageTransaction.java index 24dab77e..7dc0b982 100644 --- a/src/main/java/org/qora/transaction/MessageTransaction.java +++ b/src/main/java/org/qora/transaction/MessageTransaction.java @@ -120,8 +120,6 @@ public class MessageTransaction extends Transaction { @Override public void process() throws DataException { - // We would save updated transaction at this point, but it hasn't been modified - // Wrap and delegate payment processing to Payment class. new Payment(this.repository).process(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(), messageTransactionData.getSignature()); @@ -136,11 +134,16 @@ public class MessageTransaction extends Transaction { @Override public void orphan() throws DataException { - // Wrap and delegate payment processing to Payment class. Only revert recipient's last reference if transferring QORA. + // Wrap and delegate payment processing to Payment class. new Payment(this.repository).orphan(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(), - messageTransactionData.getSignature(), messageTransactionData.getReference(), false); + messageTransactionData.getSignature(), messageTransactionData.getReference()); + } - // We would save updated transaction at this point, but it hasn't been modified + @Override + public void orphanReferencesAndFees() throws DataException { + // Wrap and delegate references processing to Payment class. Only revert recipient's last reference if transferring QORA. + new Payment(this.repository).orphanReferencesAndFees(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(), + messageTransactionData.getSignature(), messageTransactionData.getReference(), false); } } diff --git a/src/main/java/org/qora/transaction/MultiPaymentTransaction.java b/src/main/java/org/qora/transaction/MultiPaymentTransaction.java index 68b5ad0e..702c4fbf 100644 --- a/src/main/java/org/qora/transaction/MultiPaymentTransaction.java +++ b/src/main/java/org/qora/transaction/MultiPaymentTransaction.java @@ -119,8 +119,6 @@ public class MultiPaymentTransaction extends Transaction { @Override public void process() throws DataException { - // We would save updated transaction at this point, but it hasn't been modified - // Wrap and delegate payment processing to Payment class. new Payment(this.repository).process(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(), multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature()); @@ -128,8 +126,6 @@ public class MultiPaymentTransaction extends Transaction { @Override public void processReferencesAndFees() throws DataException { - // We would save updated transaction at this point, but it hasn't been modified - // Wrap and delegate reference processing to Payment class. Always update recipients' last references regardless of asset. new Payment(this.repository).processReferencesAndFees(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(), multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), true); @@ -139,9 +135,14 @@ public class MultiPaymentTransaction extends Transaction { public void orphan() throws DataException { // Wrap and delegate payment processing to Payment class. Always revert recipients' last references regardless of asset. new Payment(this.repository).orphan(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(), - multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), multiPaymentTransactionData.getReference(), true); + multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), multiPaymentTransactionData.getReference()); + } - // We would save updated transaction at this point, but it hasn't been modified + @Override + public void orphanReferencesAndFees() throws DataException { + // Wrap and delegate reference processing to Payment class. Always revert recipients' last references regardless of asset. + new Payment(this.repository).orphanReferencesAndFees(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(), + multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), multiPaymentTransactionData.getReference(), true); } } diff --git a/src/main/java/org/qora/transaction/PaymentTransaction.java b/src/main/java/org/qora/transaction/PaymentTransaction.java index fffdc0fa..a0c37b64 100644 --- a/src/main/java/org/qora/transaction/PaymentTransaction.java +++ b/src/main/java/org/qora/transaction/PaymentTransaction.java @@ -88,8 +88,6 @@ public class PaymentTransaction extends Transaction { @Override public void process() throws DataException { - // We would save updated transaction at this point, but it hasn't been modified - // Wrap and delegate payment processing to Payment class. new Payment(this.repository).process(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(), paymentTransactionData.getSignature()); @@ -106,9 +104,14 @@ public class PaymentTransaction extends Transaction { public void orphan() throws DataException { // Wrap and delegate payment processing to Payment class. Only revert recipient's last reference if transferring QORA. new Payment(this.repository).orphan(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(), - paymentTransactionData.getSignature(), paymentTransactionData.getReference(), false); + paymentTransactionData.getSignature(), paymentTransactionData.getReference()); + } - // We would save updated transaction at this point, but it hasn't been modified + @Override + public void orphanReferencesAndFees() throws DataException { + // Wrap and delegate payment processing to Payment class. Only revert recipient's last reference if transferring QORA. + new Payment(this.repository).orphanReferencesAndFees(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(), + paymentTransactionData.getSignature(), paymentTransactionData.getReference(), false); } } diff --git a/src/main/java/org/qora/transaction/ProxyForgingTransaction.java b/src/main/java/org/qora/transaction/ProxyForgingTransaction.java index 5d41f6c3..89949b47 100644 --- a/src/main/java/org/qora/transaction/ProxyForgingTransaction.java +++ b/src/main/java/org/qora/transaction/ProxyForgingTransaction.java @@ -161,12 +161,11 @@ public class ProxyForgingTransaction extends Transaction { // Save this transaction, with removed previous share info proxyForgingTransactionData.setPreviousShare(null); this.repository.getTransactionRepository().save(proxyForgingTransactionData); + } - // Update forger's balance - forger.setConfirmedBalance(Asset.QORA, forger.getConfirmedBalance(Asset.QORA).add(proxyForgingTransactionData.getFee())); - - // Update forger's reference - forger.setLastReference(proxyForgingTransactionData.getReference()); + @Override + public void orphanReferencesAndFees() throws DataException { + super.orphanReferencesAndFees(); // If recipient didn't have a last-reference prior to this transaction then remove it Account recipient = new Account(this.repository, proxyForgingTransactionData.getRecipient()); diff --git a/src/main/java/org/qora/transaction/RegisterNameTransaction.java b/src/main/java/org/qora/transaction/RegisterNameTransaction.java index 93bd8c42..ca4c3748 100644 --- a/src/main/java/org/qora/transaction/RegisterNameTransaction.java +++ b/src/main/java/org/qora/transaction/RegisterNameTransaction.java @@ -126,8 +126,6 @@ public class RegisterNameTransaction extends Transaction { // Register Name Name name = new Name(this.repository, registerNameTransactionData); name.register(); - - // We would save updated transaction at this point, but it hasn't been modified } @Override @@ -135,15 +133,6 @@ public class RegisterNameTransaction extends Transaction { // Unregister name Name name = new Name(this.repository, registerNameTransactionData.getName()); name.unregister(); - - // We would save updated transaction at this point, but it hasn't been modified - - // Update registrant's balance - Account registrant = getRegistrant(); - registrant.setConfirmedBalance(Asset.QORA, registrant.getConfirmedBalance(Asset.QORA).add(registerNameTransactionData.getFee())); - - // Update registrant's reference - registrant.setLastReference(registerNameTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/RemoveGroupAdminTransaction.java b/src/main/java/org/qora/transaction/RemoveGroupAdminTransaction.java index 5efa78e0..971806f8 100644 --- a/src/main/java/org/qora/transaction/RemoveGroupAdminTransaction.java +++ b/src/main/java/org/qora/transaction/RemoveGroupAdminTransaction.java @@ -124,13 +124,6 @@ public class RemoveGroupAdminTransaction extends Transaction { // Save this transaction with removed group references this.repository.getTransactionRepository().save(removeGroupAdminTransactionData); - - // Update owner's balance - Account owner = getOwner(); - owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).add(removeGroupAdminTransactionData.getFee())); - - // Update owner's reference - owner.setLastReference(removeGroupAdminTransactionData.getReference()); } } \ No newline at end of file diff --git a/src/main/java/org/qora/transaction/SellNameTransaction.java b/src/main/java/org/qora/transaction/SellNameTransaction.java index ec54294f..af97ccd2 100644 --- a/src/main/java/org/qora/transaction/SellNameTransaction.java +++ b/src/main/java/org/qora/transaction/SellNameTransaction.java @@ -116,8 +116,6 @@ public class SellNameTransaction extends Transaction { // Sell Name Name name = new Name(this.repository, sellNameTransactionData.getName()); name.sell(sellNameTransactionData); - - // We would save updated transaction at this point, but it hasn't been modified } @Override @@ -125,15 +123,6 @@ public class SellNameTransaction extends Transaction { // Revert name Name name = new Name(this.repository, sellNameTransactionData.getName()); name.unsell(sellNameTransactionData); - - // We would save updated transaction at this point, but it hasn't been modified - - // Update owner's balance - Account owner = getOwner(); - owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).add(sellNameTransactionData.getFee())); - - // Update owner's reference - owner.setLastReference(sellNameTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/SetGroupTransaction.java b/src/main/java/org/qora/transaction/SetGroupTransaction.java index d0bdca81..70d7845a 100644 --- a/src/main/java/org/qora/transaction/SetGroupTransaction.java +++ b/src/main/java/org/qora/transaction/SetGroupTransaction.java @@ -111,12 +111,6 @@ public class SetGroupTransaction extends Transaction { // Save this transaction with removed previous defaultGroupId value setGroupTransactionData.setPreviousDefaultGroupId(null); this.repository.getTransactionRepository().save(setGroupTransactionData); - - // Update creator's balance - creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(setGroupTransactionData.getFee())); - - // Update admin's reference - creator.setLastReference(setGroupTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/Transaction.java b/src/main/java/org/qora/transaction/Transaction.java index 409dbca5..8e3f6b93 100644 --- a/src/main/java/org/qora/transaction/Transaction.java +++ b/src/main/java/org/qora/transaction/Transaction.java @@ -909,6 +909,22 @@ public abstract class Transaction { */ public abstract void orphan() throws DataException; + /** + * Update last references, subtract transaction fees, etc. + * + * @throws DataException + */ + public void orphanReferencesAndFees() throws DataException { + Account creator = getCreator(); + + // Update transaction creator's balance + creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(transactionData.getFee())); + + // Update transaction creator's reference + creator.setLastReference(transactionData.getReference()); + } + + // Comparison /** Returns comparator that sorts ATTransactions first, then by timestamp, then by signature */ diff --git a/src/main/java/org/qora/transaction/TransferAssetTransaction.java b/src/main/java/org/qora/transaction/TransferAssetTransaction.java index d0434755..c352b36c 100644 --- a/src/main/java/org/qora/transaction/TransferAssetTransaction.java +++ b/src/main/java/org/qora/transaction/TransferAssetTransaction.java @@ -100,8 +100,6 @@ public class TransferAssetTransaction extends Transaction { @Override public void process() throws DataException { - // We would save updated transaction at this point, but it hasn't been modified - // Wrap asset transfer as a payment and delegate processing to Payment class. Only update recipient's last reference if transferring QORA. new Payment(this.repository).process(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(), transferAssetTransactionData.getSignature()); @@ -118,9 +116,14 @@ public class TransferAssetTransaction extends Transaction { public void orphan() throws DataException { // Wrap asset transfer as a payment and delegate processing to Payment class. Only revert recipient's last reference if transferring QORA. new Payment(this.repository).orphan(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(), - transferAssetTransactionData.getSignature(), transferAssetTransactionData.getReference(), false); + transferAssetTransactionData.getSignature(), transferAssetTransactionData.getReference()); + } - // We would save updated transaction at this point, but it hasn't been modified + @Override + public void orphanReferencesAndFees() throws DataException { + // Wrap asset transfer as a payment and delegate processing to Payment class. Only revert recipient's last reference if transferring QORA. + new Payment(this.repository).orphanReferencesAndFees(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(), + transferAssetTransactionData.getSignature(), transferAssetTransactionData.getReference(), false); } } diff --git a/src/main/java/org/qora/transaction/UpdateAssetTransaction.java b/src/main/java/org/qora/transaction/UpdateAssetTransaction.java index 1a679432..f08c70ab 100644 --- a/src/main/java/org/qora/transaction/UpdateAssetTransaction.java +++ b/src/main/java/org/qora/transaction/UpdateAssetTransaction.java @@ -146,14 +146,6 @@ public class UpdateAssetTransaction extends Transaction { // Save this transaction, with removed "name reference" to previous transaction that updated name this.repository.getTransactionRepository().save(updateAssetTransactionData); - - // Update owner's balance - Account owner = getOwner(); - owner.setConfirmedBalance(Asset.QORA, - owner.getConfirmedBalance(Asset.QORA).add(updateAssetTransactionData.getFee())); - - // Update owner's reference - owner.setLastReference(updateAssetTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/UpdateGroupTransaction.java b/src/main/java/org/qora/transaction/UpdateGroupTransaction.java index 0d076d72..ede37bcc 100644 --- a/src/main/java/org/qora/transaction/UpdateGroupTransaction.java +++ b/src/main/java/org/qora/transaction/UpdateGroupTransaction.java @@ -148,13 +148,6 @@ public class UpdateGroupTransaction extends Transaction { // Save this transaction, now with removed "group reference" this.repository.getTransactionRepository().save(updateGroupTransactionData); - - // Update owner's balance - Account owner = getOwner(); - owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).add(updateGroupTransactionData.getFee())); - - // Update owner's reference - owner.setLastReference(updateGroupTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/UpdateNameTransaction.java b/src/main/java/org/qora/transaction/UpdateNameTransaction.java index 94da0bc3..eba2c0f1 100644 --- a/src/main/java/org/qora/transaction/UpdateNameTransaction.java +++ b/src/main/java/org/qora/transaction/UpdateNameTransaction.java @@ -151,13 +151,6 @@ public class UpdateNameTransaction extends Transaction { // Save this transaction, now with removed "name reference" this.repository.getTransactionRepository().save(updateNameTransactionData); - - // Update owner's balance - Account owner = getOwner(); - owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).add(updateNameTransactionData.getFee())); - - // Update owner's reference - owner.setLastReference(updateNameTransactionData.getReference()); } } diff --git a/src/main/java/org/qora/transaction/VoteOnPollTransaction.java b/src/main/java/org/qora/transaction/VoteOnPollTransaction.java index 7d856c43..67553b00 100644 --- a/src/main/java/org/qora/transaction/VoteOnPollTransaction.java +++ b/src/main/java/org/qora/transaction/VoteOnPollTransaction.java @@ -144,12 +144,7 @@ public class VoteOnPollTransaction extends Transaction { @Override public void orphan() throws DataException { - // Update voter's balance Account voter = getVoter(); - voter.setConfirmedBalance(Asset.QORA, voter.getConfirmedBalance(Asset.QORA).add(voteOnPollTransactionData.getFee())); - - // Update voter's reference - voter.setLastReference(voteOnPollTransactionData.getReference()); // Does this transaction have previous vote info? VotingRepository votingRepository = this.repository.getVotingRepository();