diff --git a/src/main/java/org/qortal/data/account/AccountData.java b/src/main/java/org/qortal/data/account/AccountData.java index 4d662f04..868d1bc1 100644 --- a/src/main/java/org/qortal/data/account/AccountData.java +++ b/src/main/java/org/qortal/data/account/AccountData.java @@ -18,6 +18,7 @@ public class AccountData { protected int level; protected int blocksMinted; protected int blocksMintedAdjustment; + protected int blocksMintedPenalty; // Constructors @@ -25,7 +26,7 @@ public class AccountData { protected AccountData() { } - public AccountData(String address, byte[] reference, byte[] publicKey, int defaultGroupId, int flags, int level, int blocksMinted, int blocksMintedAdjustment) { + public AccountData(String address, byte[] reference, byte[] publicKey, int defaultGroupId, int flags, int level, int blocksMinted, int blocksMintedAdjustment, int blocksMintedPenalty) { this.address = address; this.reference = reference; this.publicKey = publicKey; @@ -34,10 +35,11 @@ public class AccountData { this.level = level; this.blocksMinted = blocksMinted; this.blocksMintedAdjustment = blocksMintedAdjustment; + this.blocksMintedPenalty = blocksMintedPenalty; } public AccountData(String address) { - this(address, null, null, Group.NO_GROUP, 0, 0, 0, 0); + this(address, null, null, Group.NO_GROUP, 0, 0, 0, 0, 0); } // Getters/Setters @@ -102,6 +104,14 @@ public class AccountData { this.blocksMintedAdjustment = blocksMintedAdjustment; } + public int getBlocksMintedPenalty() { + return this.blocksMintedPenalty; + } + + public void setBlocksMintedPenalty(int blocksMintedPenalty) { + this.blocksMintedPenalty = blocksMintedPenalty; + } + // Comparison @Override diff --git a/src/main/java/org/qortal/network/message/AccountMessage.java b/src/main/java/org/qortal/network/message/AccountMessage.java index d22ef879..453862b0 100644 --- a/src/main/java/org/qortal/network/message/AccountMessage.java +++ b/src/main/java/org/qortal/network/message/AccountMessage.java @@ -41,6 +41,8 @@ public class AccountMessage extends Message { bytes.write(Ints.toByteArray(accountData.getBlocksMintedAdjustment())); + bytes.write(Ints.toByteArray(accountData.getBlocksMintedPenalty())); + } catch (IOException e) { throw new AssertionError("IOException shouldn't occur with ByteArrayOutputStream"); } @@ -80,7 +82,9 @@ public class AccountMessage extends Message { int blocksMintedAdjustment = byteBuffer.getInt(); - AccountData accountData = new AccountData(address, reference, publicKey, defaultGroupId, flags, level, blocksMinted, blocksMintedAdjustment); + int blocksMintedPenalty = byteBuffer.getInt(); + + AccountData accountData = new AccountData(address, reference, publicKey, defaultGroupId, flags, level, blocksMinted, blocksMintedAdjustment, blocksMintedPenalty); return new AccountMessage(id, accountData); } diff --git a/src/main/java/org/qortal/repository/AccountRepository.java b/src/main/java/org/qortal/repository/AccountRepository.java index 281f34f1..1175337c 100644 --- a/src/main/java/org/qortal/repository/AccountRepository.java +++ b/src/main/java/org/qortal/repository/AccountRepository.java @@ -1,13 +1,9 @@ package org.qortal.repository; import java.util.List; +import java.util.Set; -import org.qortal.data.account.AccountBalanceData; -import org.qortal.data.account.AccountData; -import org.qortal.data.account.EligibleQoraHolderData; -import org.qortal.data.account.MintingAccountData; -import org.qortal.data.account.QortFromQoraData; -import org.qortal.data.account.RewardShareData; +import org.qortal.data.account.*; public interface AccountRepository { @@ -19,6 +15,9 @@ public interface AccountRepository { /** Returns accounts with any bit set in given mask. */ public List getFlaggedAccounts(int mask) throws DataException; + /** Returns accounts with a blockedMintedPenalty */ + public List getPenaltyAccounts() throws DataException; + /** Returns account's last reference or null if not set or account not found. */ public byte[] getLastReference(String address) throws DataException; @@ -100,6 +99,18 @@ public interface AccountRepository { */ public void modifyMintedBlockCounts(List addresses, int delta) throws DataException; + /** Returns account's block minted penalty count or null if account not found. */ + public Integer getBlocksMintedPenaltyCount(String address) throws DataException; + + /** + * Sets blocks minted penalties for given list of accounts. + * This replaces the existing values rather than modifying them by a delta. + * + * @param accountPenalties + * @throws DataException + */ + public void updateBlocksMintedPenalties(Set accountPenalties) throws DataException; + /** Delete account from repository. */ public void delete(String address) throws DataException; diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java index 9fdb0a3f..cb188502 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java @@ -6,15 +6,11 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import org.qortal.asset.Asset; -import org.qortal.data.account.AccountBalanceData; -import org.qortal.data.account.AccountData; -import org.qortal.data.account.EligibleQoraHolderData; -import org.qortal.data.account.MintingAccountData; -import org.qortal.data.account.QortFromQoraData; -import org.qortal.data.account.RewardShareData; +import org.qortal.data.account.*; import org.qortal.repository.AccountRepository; import org.qortal.repository.DataException; @@ -30,7 +26,7 @@ public class HSQLDBAccountRepository implements AccountRepository { @Override public AccountData getAccount(String address) throws DataException { - String sql = "SELECT reference, public_key, default_group_id, flags, level, blocks_minted, blocks_minted_adjustment FROM Accounts WHERE account = ?"; + String sql = "SELECT reference, public_key, default_group_id, flags, level, blocks_minted, blocks_minted_adjustment, blocks_minted_penalty FROM Accounts WHERE account = ?"; try (ResultSet resultSet = this.repository.checkedExecute(sql, address)) { if (resultSet == null) @@ -43,8 +39,9 @@ public class HSQLDBAccountRepository implements AccountRepository { int level = resultSet.getInt(5); int blocksMinted = resultSet.getInt(6); int blocksMintedAdjustment = resultSet.getInt(7); + int blocksMintedPenalty = resultSet.getInt(8); - return new AccountData(address, reference, publicKey, defaultGroupId, flags, level, blocksMinted, blocksMintedAdjustment); + return new AccountData(address, reference, publicKey, defaultGroupId, flags, level, blocksMinted, blocksMintedAdjustment, blocksMintedPenalty); } catch (SQLException e) { throw new DataException("Unable to fetch account info from repository", e); } @@ -52,7 +49,7 @@ public class HSQLDBAccountRepository implements AccountRepository { @Override public List getFlaggedAccounts(int mask) throws DataException { - String sql = "SELECT reference, public_key, default_group_id, flags, level, blocks_minted, blocks_minted_adjustment, account FROM Accounts WHERE BITAND(flags, ?) != 0"; + String sql = "SELECT reference, public_key, default_group_id, flags, level, blocks_minted, blocks_minted_adjustment, blocks_minted_penalty, account FROM Accounts WHERE BITAND(flags, ?) != 0"; List accounts = new ArrayList<>(); @@ -68,9 +65,10 @@ public class HSQLDBAccountRepository implements AccountRepository { int level = resultSet.getInt(5); int blocksMinted = resultSet.getInt(6); int blocksMintedAdjustment = resultSet.getInt(7); - String address = resultSet.getString(8); + int blocksMintedPenalty = resultSet.getInt(8); + String address = resultSet.getString(9); - accounts.add(new AccountData(address, reference, publicKey, defaultGroupId, flags, level, blocksMinted, blocksMintedAdjustment)); + accounts.add(new AccountData(address, reference, publicKey, defaultGroupId, flags, level, blocksMinted, blocksMintedAdjustment, blocksMintedPenalty)); } while (resultSet.next()); return accounts; @@ -79,6 +77,36 @@ public class HSQLDBAccountRepository implements AccountRepository { } } + @Override + public List getPenaltyAccounts() throws DataException { + String sql = "SELECT reference, public_key, default_group_id, flags, level, blocks_minted, blocks_minted_adjustment, blocks_minted_penalty, account FROM Accounts WHERE blocks_minted_penalty != 0"; + + List accounts = new ArrayList<>(); + + try (ResultSet resultSet = this.repository.checkedExecute(sql)) { + if (resultSet == null) + return accounts; + + do { + byte[] reference = resultSet.getBytes(1); + byte[] publicKey = resultSet.getBytes(2); + int defaultGroupId = resultSet.getInt(3); + int flags = resultSet.getInt(4); + int level = resultSet.getInt(5); + int blocksMinted = resultSet.getInt(6); + int blocksMintedAdjustment = resultSet.getInt(7); + int blocksMintedPenalty = resultSet.getInt(8); + String address = resultSet.getString(9); + + accounts.add(new AccountData(address, reference, publicKey, defaultGroupId, flags, level, blocksMinted, blocksMintedAdjustment, blocksMintedPenalty)); + } while (resultSet.next()); + + return accounts; + } catch (SQLException e) { + throw new DataException("Unable to fetch penalty accounts from repository", e); + } + } + @Override public byte[] getLastReference(String address) throws DataException { String sql = "SELECT reference FROM Accounts WHERE account = ?"; @@ -298,6 +326,39 @@ public class HSQLDBAccountRepository implements AccountRepository { } } + @Override + public Integer getBlocksMintedPenaltyCount(String address) throws DataException { + String sql = "SELECT blocks_minted_penalty FROM Accounts WHERE account = ?"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, address)) { + if (resultSet == null) + return null; + + return resultSet.getInt(1); + } catch (SQLException e) { + throw new DataException("Unable to fetch account's block minted penalty count from repository", e); + } + } + public void updateBlocksMintedPenalties(Set accountPenalties) throws DataException { + // Nothing to do? + if (accountPenalties == null || accountPenalties.isEmpty()) + return; + + // Map balance changes into SQL bind params, filtering out no-op changes + List updateBlocksMintedPenaltyParams = accountPenalties.stream() + .map(accountPenalty -> new Object[] { accountPenalty.getAddress(), accountPenalty.getBlocksMintedPenalty(), accountPenalty.getBlocksMintedPenalty() }) + .collect(Collectors.toList()); + + // Perform actual balance changes + String sql = "INSERT INTO Accounts (account, blocks_minted_penalty) VALUES (?, ?) " + + "ON DUPLICATE KEY UPDATE blocks_minted_penalty = blocks_minted_penalty + ?"; + try { + this.repository.executeCheckedBatchUpdate(sql, updateBlocksMintedPenaltyParams); + } catch (SQLException e) { + throw new DataException("Unable to set blocks minted penalties in repository", e); + } + } + @Override public void delete(String address) throws DataException { // NOTE: Account balances are deleted automatically by the database thanks to "ON DELETE CASCADE" in AccountBalances' FOREIGN KEY