Implemented REGISTER_NAME transaction fee increase from 0.001 to 5 QORT (average value based on community vote).

This commit is contained in:
CalDescent 2022-02-13 13:45:48 +00:00
parent ea42a5617f
commit 0712259057
15 changed files with 132 additions and 0 deletions

View File

@ -72,6 +72,11 @@ public class BlockChain {
transactionV5Timestamp;
}
// Custom transaction fees
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
private long nameRegistrationUnitFee;
private long nameRegistrationUnitFeeTimestamp;
/** Map of which blockchain features are enabled when (height/timestamp) */
@XmlJavaTypeAdapter(StringLongMapXmlAdapter.class)
private Map<String, Long> featureTriggers;
@ -301,6 +306,16 @@ public class BlockChain {
return this.maxBlockSize;
}
// Custom transaction fees
public long getNameRegistrationUnitFee() {
return this.nameRegistrationUnitFee;
}
public long getNameRegistrationUnitFeeTimestamp() {
// FUTURE: we could use a separate structure to indicate fee adjustments for different transaction types
return this.nameRegistrationUnitFeeTimestamp;
}
/** Returns true if approval-needing transaction types require a txGroupId other than NO_GROUP. */
public boolean getRequireGroupForApproval() {
return this.requireGroupForApproval;

View File

@ -37,6 +37,15 @@ public class RegisterNameTransaction extends Transaction {
return Collections.emptyList();
}
@Override
public long getUnitFee(Long timestamp) {
// Use a higher unit fee after the fee increase timestamp
if (timestamp > BlockChain.getInstance().getNameRegistrationUnitFeeTimestamp()) {
return BlockChain.getInstance().getNameRegistrationUnitFee();
}
return BlockChain.getInstance().getUnitFee();
}
// Navigation
public Account getRegistrant() {

View File

@ -4,6 +4,8 @@
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
"unitFee": "0.001",
"nameRegistrationUnitFee": "5",
"nameRegistrationUnitFeeTimestamp": 9999999999999,
"useBrokenMD160ForAddresses": false,
"requireGroupForApproval": false,
"defaultGroupId": 0,

View File

@ -20,7 +20,9 @@ import org.qortal.test.common.BlockUtils;
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.utils.Amounts;
import org.qortal.utils.NTP;
public class BuySellTests extends Common {
@ -62,6 +64,7 @@ public class BuySellTests extends Common {
public void testRegisterName() throws DataException {
// Register-name
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "{}");
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
String name = transactionData.getName();

View File

@ -11,7 +11,9 @@ import org.qortal.repository.RepositoryManager;
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.NTP;
import java.util.List;
@ -33,6 +35,7 @@ public class IntegrityTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -56,6 +59,7 @@ public class IntegrityTests extends Common {
String data = "\uD83E\uDD73";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -82,6 +86,7 @@ public class IntegrityTests extends Common {
String name = "initial_name";
String data = "initial_data";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Update the name, but keep the new name blank
@ -116,6 +121,7 @@ public class IntegrityTests extends Common {
String name = "initial_name";
String data = "initial_data";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Update the name, but keep the new name blank
@ -143,6 +149,7 @@ public class IntegrityTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -172,6 +179,7 @@ public class IntegrityTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -210,6 +218,7 @@ public class IntegrityTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -235,6 +244,7 @@ public class IntegrityTests extends Common {
// Attempt to register the new name
transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), newName, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(alice);
@ -254,6 +264,7 @@ public class IntegrityTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -268,6 +279,7 @@ public class IntegrityTests extends Common {
// Attempt to register the name again
String duplicateName = "TEST-nÁme";
transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), duplicateName, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(alice);
@ -287,6 +299,7 @@ public class IntegrityTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -320,6 +333,7 @@ public class IntegrityTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -329,6 +343,7 @@ public class IntegrityTests extends Common {
String secondName = "new-missing-name";
String secondNameData = "{\"data2\":true}";
transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), secondName, secondNameData);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the second name exists and the data is correct
@ -362,6 +377,7 @@ public class IntegrityTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -393,6 +409,7 @@ public class IntegrityTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct

View File

@ -3,20 +3,26 @@ package org.qortal.test.naming;
import static org.junit.Assert.*;
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.Before;
import org.junit.Test;
import org.qortal.account.PrivateKeyAccount;
import org.qortal.block.BlockChain;
import org.qortal.controller.BlockMinter;
import org.qortal.data.transaction.*;
import org.qortal.naming.Name;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.settings.Settings;
import org.qortal.test.common.*;
import org.qortal.test.common.transaction.TestTransaction;
import org.qortal.transaction.RegisterNameTransaction;
import org.qortal.transaction.Transaction;
import org.qortal.transaction.Transaction.ValidationResult;
import org.qortal.utils.NTP;
public class MiscTests extends Common {
@ -34,6 +40,7 @@ public class MiscTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
List<String> recentNames = repository.getNameRepository().getRecentNames(0L);
@ -53,11 +60,13 @@ public class MiscTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// duplicate
String duplicateName = "TEST-nÁme";
transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), duplicateName, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(alice);
@ -76,12 +85,14 @@ public class MiscTests extends Common {
String data = "{}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// duplicate (this time registered by Bob)
PrivateKeyAccount bob = Common.getTestAccount(repository, "bob");
String duplicateName = "TEST-nÁme";
transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(bob), duplicateName, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(alice);
@ -100,12 +111,14 @@ public class MiscTests extends Common {
String data = "{\"age\":30}";
TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Register another name that we will later attempt to rename to first name (above)
String otherName = "new-name";
String otherData = "";
transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), otherName, otherData);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// we shouldn't be able to update name to existing name
@ -129,6 +142,7 @@ public class MiscTests extends Common {
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(alice);
@ -147,6 +161,7 @@ public class MiscTests extends Common {
String data = "{\"age\":30}";
TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// we shouldn't be able to update name to an address
@ -175,6 +190,7 @@ public class MiscTests extends Common {
// Register the name
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -201,6 +217,7 @@ public class MiscTests extends Common {
// Register the name
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -252,6 +269,7 @@ public class MiscTests extends Common {
// Register the name
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Ensure the name exists and the data is correct
@ -283,6 +301,7 @@ public class MiscTests extends Common {
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
// Ensure the name doesn't exist
assertNull(repository.getNameRepository().fromName(name));
@ -304,4 +323,54 @@ public class MiscTests extends Common {
}
}
// test name registration fee increase
@Test
public void testRegisterNameFeeIncrease() throws DataException, IllegalAccessException {
try (final Repository repository = RepositoryManager.getRepository()) {
// Set nameRegistrationUnitFeeTimestamp to a time far in the future
long futureTimestamp = 9999999999999L; // 20 Nov 2286
FieldUtils.writeField(BlockChain.getInstance(), "nameRegistrationUnitFeeTimestamp", futureTimestamp, true);
assertEquals(futureTimestamp, BlockChain.getInstance().getNameRegistrationUnitFeeTimestamp());
// Validate unit fees pre and post timestamp
assertEquals(10000000, BlockChain.getInstance().getUnitFee()); // 0.1 QORT
assertEquals(500000000, BlockChain.getInstance().getNameRegistrationUnitFee()); // 5 QORT
// Register-name
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
String name = "test-name";
String data = "{\"age\":30}";
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
assertEquals(10000000L, transactionData.getFee().longValue());
TransactionUtils.signAndMint(repository, transactionData, alice);
// Set nameRegistrationUnitFeeTimestamp to a time in the past
Long now = NTP.getTime();
FieldUtils.writeField(BlockChain.getInstance(), "nameRegistrationUnitFeeTimestamp", now - 1000L, true);
assertEquals(now - 1000L, BlockChain.getInstance().getNameRegistrationUnitFeeTimestamp());
// Register a different name
// First try with the default unit fee
String name2 = "test-name-2";
transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name2, data);
assertEquals(10000000L, transactionData.getFee().longValue());
Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(alice);
ValidationResult result = transaction.importAsUnconfirmed();
assertTrue("Transaction should be invalid", ValidationResult.INSUFFICIENT_FEE == result);
// Now try using correct fee (this is specified by the UI, via the /transaction/unitfee API endpoint)
transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name2, data);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
assertEquals(500000000L, transactionData.getFee().longValue());
transaction = Transaction.fromData(repository, transactionData);
transaction.sign(alice);
result = transaction.importAsUnconfirmed();
assertTrue("Transaction should be valid", ValidationResult.OK == result);
}
}
}

View File

@ -16,6 +16,8 @@ import org.qortal.test.common.BlockUtils;
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.utils.NTP;
public class UpdateTests extends Common {
@ -34,6 +36,7 @@ public class UpdateTests extends Common {
String initialData = "{\"age\":30}";
TransactionData initialTransactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData);
initialTransactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, initialTransactionData, alice);
// Check name, reduced name, and data exist
@ -100,6 +103,7 @@ public class UpdateTests extends Common {
String constantReducedName = "initia1-name";
TransactionData initialTransactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData);
initialTransactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, initialTransactionData, alice);
// Check initial name exists
@ -147,6 +151,7 @@ public class UpdateTests extends Common {
String initialData = "{\"age\":30}";
TransactionData initialTransactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData);
initialTransactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, initialTransactionData, alice);
// Check initial name exists
@ -225,6 +230,7 @@ public class UpdateTests extends Common {
String initialData = "{\"age\":30}";
TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Check initial name exists
@ -282,6 +288,7 @@ public class UpdateTests extends Common {
String initialData = "{\"age\":30}";
TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Check initial name exists
@ -323,6 +330,7 @@ public class UpdateTests extends Common {
String initialData = "{\"age\":30}";
TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Check initial name exists
@ -385,6 +393,7 @@ public class UpdateTests extends Common {
String initialData = "{\"age\":30}";
TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData);
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(NTP.getTime()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Check initial name exists

View File

@ -5,6 +5,7 @@
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
"unitFee": "0.1",
"nameRegistrationUnitFee": "5",
"requireGroupForApproval": false,
"minAccountLevelToRewardShare": 5,
"maxRewardSharesPerMintingAccount": 20,

View File

@ -5,6 +5,7 @@
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
"unitFee": "0.1",
"nameRegistrationUnitFee": "5",
"requireGroupForApproval": false,
"minAccountLevelToRewardShare": 5,
"maxRewardSharesPerMintingAccount": 20,

View File

@ -5,6 +5,7 @@
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
"unitFee": "0.1",
"nameRegistrationUnitFee": "5",
"requireGroupForApproval": false,
"minAccountLevelToRewardShare": 5,
"maxRewardSharesPerMintingAccount": 20,

View File

@ -5,6 +5,7 @@
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
"unitFee": "0.1",
"nameRegistrationUnitFee": "5",
"requireGroupForApproval": false,
"minAccountLevelToRewardShare": 5,
"maxRewardSharesPerMintingAccount": 20,

View File

@ -5,6 +5,7 @@
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
"unitFee": "0.1",
"nameRegistrationUnitFee": "5",
"requireGroupForApproval": false,
"minAccountLevelToRewardShare": 5,
"maxRewardSharesPerMintingAccount": 20,

View File

@ -5,6 +5,7 @@
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
"unitFee": "0.1",
"nameRegistrationUnitFee": "5",
"requireGroupForApproval": false,
"minAccountLevelToRewardShare": 5,
"maxRewardSharesPerMintingAccount": 20,

View File

@ -5,6 +5,7 @@
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
"unitFee": "0.1",
"nameRegistrationUnitFee": "5",
"requireGroupForApproval": false,
"minAccountLevelToRewardShare": 5,
"maxRewardSharesPerMintingAccount": 20,

View File

@ -5,6 +5,7 @@
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
"unitFee": "0.1",
"nameRegistrationUnitFee": "5",
"requireGroupForApproval": false,
"minAccountLevelToRewardShare": 5,
"maxRewardSharesPerMintingAccount": 20,