diff --git a/src/main/java/org/qortal/block/BlockChain.java b/src/main/java/org/qortal/block/BlockChain.java index bce09aed..d7e405ed 100644 --- a/src/main/java/org/qortal/block/BlockChain.java +++ b/src/main/java/org/qortal/block/BlockChain.java @@ -92,7 +92,8 @@ public class BlockChain { adminsReplaceFoundersHeight, nullGroupMembershipHeight, ignoreLevelForRewardShareHeight, - adminQueryFixHeight + adminQueryFixHeight, + multipleNamesPerAccountHeight } // Custom transaction fees @@ -112,7 +113,8 @@ public class BlockChain { /** Whether to use legacy, broken RIPEMD160 implementation when converting public keys to addresses. */ private boolean useBrokenMD160ForAddresses = false; - /** Whether only one registered name is allowed per account. */ + /** This should get ignored and overwritten in the oneNamePerAccount(int blockchainHeight) method, + * because it is based on block height, not based on the genesis block.*/ private boolean oneNamePerAccount = false; /** Checkpoints */ @@ -474,8 +476,9 @@ public class BlockChain { return this.useBrokenMD160ForAddresses; } - public boolean oneNamePerAccount() { - return this.oneNamePerAccount; + public boolean oneNamePerAccount(int blockchainHeight) { + // this is not set on a simple blockchain setting, it is based on a feature trigger height + return blockchainHeight < this.getMultipleNamesPerAccountHeight(); } public List getCheckpoints() { @@ -688,6 +691,10 @@ public class BlockChain { return this.featureTriggers.get(FeatureTrigger.adminQueryFixHeight.name()).intValue(); } + public int getMultipleNamesPerAccountHeight() { + return this.featureTriggers.get(FeatureTrigger.multipleNamesPerAccountHeight.name()).intValue(); + } + // More complex getters for aspects that change by height or timestamp public long getRewardAtHeight(int ourHeight) { diff --git a/src/main/java/org/qortal/transaction/BuyNameTransaction.java b/src/main/java/org/qortal/transaction/BuyNameTransaction.java index 72c15f69..b7ca1d93 100644 --- a/src/main/java/org/qortal/transaction/BuyNameTransaction.java +++ b/src/main/java/org/qortal/transaction/BuyNameTransaction.java @@ -79,7 +79,7 @@ public class BuyNameTransaction extends Transaction { return ValidationResult.BUYER_ALREADY_OWNER; // If accounts are only allowed one registered name then check for this - if (BlockChain.getInstance().oneNamePerAccount() + if (BlockChain.getInstance().oneNamePerAccount(this.repository.getBlockRepository().getBlockchainHeight()) && !this.repository.getNameRepository().getNamesByOwner(buyer.getAddress()).isEmpty()) return ValidationResult.MULTIPLE_NAMES_FORBIDDEN; diff --git a/src/main/java/org/qortal/transaction/RegisterNameTransaction.java b/src/main/java/org/qortal/transaction/RegisterNameTransaction.java index a89e60c0..c4520fbf 100644 --- a/src/main/java/org/qortal/transaction/RegisterNameTransaction.java +++ b/src/main/java/org/qortal/transaction/RegisterNameTransaction.java @@ -94,7 +94,7 @@ public class RegisterNameTransaction extends Transaction { return ValidationResult.NAME_ALREADY_REGISTERED; // If accounts are only allowed one registered name then check for this - if (BlockChain.getInstance().oneNamePerAccount() + if (BlockChain.getInstance().oneNamePerAccount(this.repository.getBlockRepository().getBlockchainHeight()) && !this.repository.getNameRepository().getNamesByOwner(getRegistrant().getAddress()).isEmpty()) return ValidationResult.MULTIPLE_NAMES_FORBIDDEN; diff --git a/src/main/resources/blockchain.json b/src/main/resources/blockchain.json index 3264b670..14ba924b 100644 --- a/src/main/resources/blockchain.json +++ b/src/main/resources/blockchain.json @@ -119,7 +119,8 @@ "adminsReplaceFoundersHeight": 2012800, "nullGroupMembershipHeight": 2012800, "ignoreLevelForRewardShareHeight": 2012800, - "adminQueryFixHeight": 2012800 + "adminQueryFixHeight": 2012800, + "multipleNamesPerAccountHeight": 9999999 }, "checkpoints": [ { "height": 1136300, "signature": "3BbwawEF2uN8Ni5ofpJXkukoU8ctAPxYoFB7whq9pKfBnjfZcpfEJT4R95NvBDoTP8WDyWvsUvbfHbcr9qSZuYpSKZjUQTvdFf6eqznHGEwhZApWfvXu6zjGCxYCp65F4jsVYYJjkzbjmkCg5WAwN5voudngA23kMK6PpTNygapCzXt" } diff --git a/src/test/java/org/qortal/test/naming/BuySellTests.java b/src/test/java/org/qortal/test/naming/BuySellTests.java index a1c644fc..ddf9d913 100644 --- a/src/test/java/org/qortal/test/naming/BuySellTests.java +++ b/src/test/java/org/qortal/test/naming/BuySellTests.java @@ -4,6 +4,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.qortal.account.PrivateKeyAccount; +import org.qortal.block.BlockChain; import org.qortal.data.naming.NameData; import org.qortal.data.transaction.BuyNameTransactionData; import org.qortal.data.transaction.CancelSellNameTransactionData; @@ -17,6 +18,7 @@ import org.qortal.test.common.Common; import org.qortal.test.common.TransactionUtils; import org.qortal.test.common.transaction.TestTransaction; import org.qortal.transaction.RegisterNameTransaction; +import org.qortal.transaction.Transaction; import org.qortal.utils.Amounts; import java.util.Random; @@ -84,6 +86,46 @@ public class BuySellTests extends Common { assertTrue(repository.getNameRepository().nameExists(name)); } + @Test + public void testRegisterNameMultiple() throws DataException { + // register name 1 + RegisterNameTransactionData transactionData1 = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "{}"); + transactionData1.setFee(new RegisterNameTransaction(null, null).getUnitFee(transactionData1.getTimestamp())); + TransactionUtils.signAndMint(repository, transactionData1, alice); + + String name1 = transactionData1.getName(); + + // check name does exist + assertTrue(repository.getNameRepository().nameExists(name1)); + + // register another name, second registered name should fail before the feature trigger + final String name2 = "another name"; + RegisterNameTransactionData transactionData2 = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name2, "{}"); + Transaction.ValidationResult resultBeforeFeatureTrigger = TransactionUtils.signAndImport(repository, transactionData2, alice); + + // check that that multiple names is forbidden + assertTrue(Transaction.ValidationResult.MULTIPLE_NAMES_FORBIDDEN.equals(resultBeforeFeatureTrigger)); + + // mint passed the feature trigger block + BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight()); + + // register again, now that we are passed the feature trigger + RegisterNameTransactionData transactionData3 = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name2, "{}"); + Transaction.ValidationResult resultAfterFeatureTrigger = TransactionUtils.signAndImport(repository, transactionData3, alice); + + // check that multiple names is ok + assertTrue(Transaction.ValidationResult.OK.equals(resultAfterFeatureTrigger)); + + // mint block, confirm transaction + BlockUtils.mintBlock(repository); + + // check name does exist + assertTrue(repository.getNameRepository().nameExists(name2)); + + // check that there are 2 names for one account + assertEquals(2, repository.getNameRepository().getNamesByOwner(alice.getAddress(), 0, 0, false).size() ); + } + @Test public void testSellName() throws DataException { // Register-name diff --git a/src/test/resources/test-chain-v2.json b/src/test/resources/test-chain-v2.json index 5395116f..e410aae4 100644 --- a/src/test/resources/test-chain-v2.json +++ b/src/test/resources/test-chain-v2.json @@ -114,7 +114,8 @@ "adminsReplaceFoundersHeight": 9999999999999, "ignoreLevelForRewardShareHeight": 9999999999999, "nullGroupMembershipHeight": 20, - "adminQueryFixHeight": 9999999999999 + "adminQueryFixHeight": 9999999999999, + "multipleNamesPerAccountHeight": 10 }, "genesisInfo": { "version": 4,