diff --git a/src/main/java/org/qora/block/BlockChain.java b/src/main/java/org/qora/block/BlockChain.java index 912b7bc5..d868f736 100644 --- a/src/main/java/org/qora/block/BlockChain.java +++ b/src/main/java/org/qora/block/BlockChain.java @@ -107,6 +107,8 @@ public class BlockChain { } List forgingTiers; + private int maxProxyRelationships; + // Constructors, etc. private BlockChain() { @@ -259,6 +261,10 @@ public class BlockChain { return this.forgingTiers; } + public int getMaxProxyRelationships() { + return this.maxProxyRelationships; + } + // Convenience methods for specific blockchain feature triggers public long getMessageReleaseHeight() { diff --git a/src/main/java/org/qora/repository/AccountRepository.java b/src/main/java/org/qora/repository/AccountRepository.java index cd65d979..913e8e1f 100644 --- a/src/main/java/org/qora/repository/AccountRepository.java +++ b/src/main/java/org/qora/repository/AccountRepository.java @@ -91,6 +91,10 @@ public interface AccountRepository { public boolean isProxyPublicKey(byte[] publicKey) throws DataException; + public int countProxyAccounts(byte[] forgerPublicKey) throws DataException; + + public List getProxyAccounts() throws DataException; + public List findProxyAccounts(List recipients, List forgers, List involvedAddresses, Integer limit, Integer offset, Boolean reverse) throws DataException; public void save(ProxyForgerData proxyForgerData) throws DataException; diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java index dd3a7f22..927141b4 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java @@ -361,6 +361,42 @@ public class HSQLDBAccountRepository implements AccountRepository { } } + @Override + public int countProxyAccounts(byte[] forgerPublicKey) throws DataException { + String sql = "SELECT COUNT(*) FROM ProxyForgers WHERE forger = ?"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, forgerPublicKey)) { + return resultSet.getInt(1); + } catch (SQLException e) { + throw new DataException("Unable to count proxy forging relationships in repository", e); + } + } + + @Override + public List getProxyAccounts() throws DataException { + String sql = "SELECT forger, recipient, share, proxy_public_key FROM ProxyForgers"; + + List proxyAccounts = new ArrayList<>(); + + try (ResultSet resultSet = this.repository.checkedExecute(sql)) { + if (resultSet == null) + return proxyAccounts; + + do { + byte[] forgerPublicKey = resultSet.getBytes(1); + String recipient = resultSet.getString(2); + BigDecimal share = resultSet.getBigDecimal(3); + byte[] proxyPublicKey = resultSet.getBytes(4); + + proxyAccounts.add(new ProxyForgerData(forgerPublicKey, recipient, proxyPublicKey, share)); + } while (resultSet.next()); + + return proxyAccounts; + } catch (SQLException e) { + throw new DataException("Unable to fetch proxy forge accounts from repository", e); + } + } + @Override public List findProxyAccounts(List recipients, List forgers, List involvedAddresses, Integer limit, Integer offset, Boolean reverse) throws DataException { diff --git a/src/main/java/org/qora/transaction/ProxyForgingTransaction.java b/src/main/java/org/qora/transaction/ProxyForgingTransaction.java index 89949b47..4550e2fd 100644 --- a/src/main/java/org/qora/transaction/ProxyForgingTransaction.java +++ b/src/main/java/org/qora/transaction/ProxyForgingTransaction.java @@ -9,6 +9,7 @@ import org.qora.account.Account; import org.qora.account.Forging; import org.qora.account.PublicKeyAccount; import org.qora.asset.Asset; +import org.qora.block.BlockChain; import org.qora.crypto.Crypto; import org.qora.data.account.ProxyForgerData; import org.qora.data.transaction.ProxyForgingTransactionData; @@ -98,9 +99,15 @@ public class ProxyForgingTransaction extends Transaction { // If proxy public key aleady exists in repository, then it must be for the same forger-recipient combo ProxyForgerData proxyForgerData = this.repository.getAccountRepository().getProxyForgeData(this.proxyForgingTransactionData.getProxyPublicKey()); - if (proxyForgerData != null) + if (proxyForgerData != null) { if (!proxyForgerData.getRecipient().equals(recipient.getAddress()) || !Arrays.equals(proxyForgerData.getForgerPublicKey(), creator.getPublicKey())) return ValidationResult.INVALID_PUBLIC_KEY; + } else { + // This is a new relationship - check that the generator hasn't reach maximum number of relationships + int relationshipCount = this.repository.getAccountRepository().countProxyAccounts(creator.getPublicKey()); + if (relationshipCount >= BlockChain.getInstance().getMaxProxyRelationships()) + return ValidationResult.MAXIMUM_PROXY_RELATIONSHIPS; + } // Check fee is positive if (proxyForgingTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) diff --git a/src/main/java/org/qora/transaction/Transaction.java b/src/main/java/org/qora/transaction/Transaction.java index 5962ebd9..abaa3862 100644 --- a/src/main/java/org/qora/transaction/Transaction.java +++ b/src/main/java/org/qora/transaction/Transaction.java @@ -232,6 +232,7 @@ public abstract class Transaction { AT_ALREADY_EXISTS(81), GROUP_APPROVAL_NOT_REQUIRED(82), GROUP_APPROVAL_DECIDED(83), + MAXIMUM_PROXY_RELATIONSHIPS(84), NOT_YET_RELEASED(1000); public final int value; diff --git a/src/main/resources/blockchain.json b/src/main/resources/blockchain.json index 44309206..0ab227dc 100644 --- a/src/main/resources/blockchain.json +++ b/src/main/resources/blockchain.json @@ -10,6 +10,7 @@ "requireGroupForApproval": false, "defaultGroupId": 0, "oneNamePerAccount": false, + "maxProxyRelationships": 4, "genesisInfo": { "version": 1, "timestamp": "1400247274336",