Work on granting forging rights

Move hard-coded forging tiers to blockchain config.
Tests for granting forging rights.

Added API call to list top block forgers.

Fixed typo with Reward[s]ByHeight class name.
This commit is contained in:
catbref
2019-04-19 09:55:04 +01:00
parent 93230e9704
commit d33ffee3ba
15 changed files with 364 additions and 59 deletions

View File

@@ -5,6 +5,8 @@ import java.util.HashMap;
import java.util.Map;
import org.qora.account.PrivateKeyAccount;
import org.qora.crypto.Crypto;
import org.qora.data.transaction.EnableForgingTransactionData;
import org.qora.data.transaction.PaymentTransactionData;
import org.qora.data.transaction.ProxyForgingTransactionData;
import org.qora.data.transaction.TransactionData;
@@ -46,6 +48,30 @@ public class AccountUtils {
return proxyPrivateKey;
}
public static TransactionData createEnableForging(Repository repository, String forger, byte[] recipientPublicKey) throws DataException {
PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, forger);
byte[] reference = forgingAccount.getLastReference();
long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1000;
return new EnableForgingTransactionData(timestamp, txGroupId, reference, forgingAccount.getPublicKey(), Crypto.toAddress(recipientPublicKey), fee);
}
public static TransactionData createEnableForging(Repository repository, String forger, String recipient) throws DataException {
PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient);
return createEnableForging(repository, forger, recipientAccount.getPublicKey());
}
public static TransactionData enableForging(Repository repository, String forger, String recipient) throws DataException {
TransactionData transactionData = createEnableForging(repository, forger, recipient);
PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, forger);
TransactionUtils.signAndForge(repository, transactionData, forgingAccount);
return transactionData;
}
public static Map<String, Map<Long, BigDecimal>> getBalances(Repository repository, long... assetIds) throws DataException {
Map<String, Map<Long, BigDecimal>> balances = new HashMap<>();

View File

@@ -0,0 +1,163 @@
package org.qora.test.forging;
import static org.junit.Assert.*;
import java.util.Random;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.qora.account.PrivateKeyAccount;
import org.qora.block.BlockChain;
import org.qora.block.BlockGenerator;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.repository.RepositoryManager;
import org.qora.test.common.AccountUtils;
import org.qora.test.common.Common;
import org.qora.test.common.TransactionUtils;
import org.qora.transaction.Transaction;
import org.qora.transaction.Transaction.ValidationResult;
public class GrantForgingTests extends Common {
@Before
public void beforeTest() throws DataException {
Common.useDefaultSettings();
}
@After
public void afterTest() throws DataException {
Common.orphanCheck();
}
@Test
public void testSimpleGrant() throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, "alice");
TransactionData transactionData = AccountUtils.createEnableForging(repository, "alice", "bob");
Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(forgingAccount);
ValidationResult result = transaction.isValidUnconfirmed();
// Alice can't grant without forging minimum number of blocks
assertEquals(ValidationResult.FORGE_MORE_BLOCKS, result);
// Forge a load of blocks
int blocksNeeded = BlockChain.getInstance().getForgingTiers().get(0).minBlocks;
for (int i = 0; i < blocksNeeded; ++i)
BlockGenerator.generateTestingBlock(repository, forgingAccount);
// Alice should be able to grant now
result = transaction.isValidUnconfirmed();
assertEquals(ValidationResult.OK, result);
TransactionUtils.signAndForge(repository, transactionData, forgingAccount);
}
}
@Test
public void testMaxGrant() throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, "alice");
TransactionData transactionData = AccountUtils.createEnableForging(repository, "alice", "bob");
Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(forgingAccount);
ValidationResult result = transaction.isValidUnconfirmed();
// Alice can't grant without forging minimum number of blocks
assertEquals(ValidationResult.FORGE_MORE_BLOCKS, result);
// Forge a load of blocks
int blocksNeeded = BlockChain.getInstance().getForgingTiers().get(0).minBlocks;
for (int i = 0; i < blocksNeeded; ++i)
BlockGenerator.generateTestingBlock(repository, forgingAccount);
// Alice should be able to grant up to 5 now
// Gift to random accounts
Random random = new Random();
for (int i = 0; i < 5; ++i) {
byte[] publicKey = new byte[32];
random.nextBytes(publicKey);
transactionData = AccountUtils.createEnableForging(repository, "alice", publicKey);
transaction = Transaction.fromData(repository, transactionData);
transaction.sign(forgingAccount);
result = transaction.isValidUnconfirmed();
assertEquals("Couldn't enable account #" + i, ValidationResult.OK, result);
TransactionUtils.signAndForge(repository, transactionData, forgingAccount);
}
// Alice's allocation used up
byte[] publicKey = new byte[32];
random.nextBytes(publicKey);
transactionData = AccountUtils.createEnableForging(repository, "alice", publicKey);
transaction = Transaction.fromData(repository, transactionData);
transaction.sign(forgingAccount);
result = transaction.isValidUnconfirmed();
assertEquals(ValidationResult.FORGING_ENABLE_LIMIT, result);
}
}
@Test
public void testFinalTier() throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
PrivateKeyAccount aliceAccount = Common.getTestAccount(repository, "alice");
TransactionData transactionData = AccountUtils.createEnableForging(repository, "alice", "bob");
Transaction transaction = Transaction.fromData(repository, transactionData);
transaction.sign(aliceAccount);
ValidationResult result = transaction.isValidUnconfirmed();
// Alice can't grant without forging minimum number of blocks
assertEquals(ValidationResult.FORGE_MORE_BLOCKS, result);
// Forge a load of blocks
int blocksNeeded = BlockChain.getInstance().getForgingTiers().get(0).minBlocks;
for (int i = 0; i < blocksNeeded; ++i)
BlockGenerator.generateTestingBlock(repository, aliceAccount);
// Alice should be able to grant now
AccountUtils.enableForging(repository, "alice", "bob");
// Bob can't grant without forging minimum number of blocks
transactionData = AccountUtils.createEnableForging(repository, "bob", "chloe");
transaction = Transaction.fromData(repository, transactionData);
transaction.sign(aliceAccount);
result = transaction.isValidUnconfirmed();
assertEquals(ValidationResult.FORGE_MORE_BLOCKS, result);
// Bob needs to forge a load of blocks
PrivateKeyAccount bobAccount = Common.getTestAccount(repository, "bob");
blocksNeeded = BlockChain.getInstance().getForgingTiers().get(1).minBlocks;
for (int i = 0; i < blocksNeeded; ++i)
BlockGenerator.generateTestingBlock(repository, bobAccount);
// Bob should be able to grant now
AccountUtils.enableForging(repository, "bob", "chloe");
// Chloe is final tier so shouldn't be able to grant
Random random = new Random();
byte[] publicKey = new byte[32];
random.nextBytes(publicKey);
transactionData = AccountUtils.createEnableForging(repository, "chloe", publicKey);
transaction = Transaction.fromData(repository, transactionData);
transaction.sign(aliceAccount);
result = transaction.isValidUnconfirmed();
assertEquals(ValidationResult.FORGING_ENABLE_LIMIT, result);
}
}
}

View File

@@ -11,7 +11,7 @@ import org.junit.Test;
import org.qora.account.PrivateKeyAccount;
import org.qora.asset.Asset;
import org.qora.block.BlockChain;
import org.qora.block.BlockChain.RewardsByHeight;
import org.qora.block.BlockChain.RewardByHeight;
import org.qora.block.BlockGenerator;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
@@ -54,11 +54,11 @@ public class RewardTests extends Common {
PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, "alice");
List<RewardsByHeight> rewards = BlockChain.getInstance().getBlockRewardsByHeight();
List<RewardByHeight> rewards = BlockChain.getInstance().getBlockRewardsByHeight();
int rewardIndex = rewards.size() - 1;
RewardsByHeight rewardInfo = rewards.get(rewardIndex);
RewardByHeight rewardInfo = rewards.get(rewardIndex);
BigDecimal expectedBalance = initialBalances.get("alice").get(Asset.QORA);
for (int height = rewardInfo.height; height > 1; --height) {

View File

@@ -30,6 +30,11 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"forgingTiers": [
{ "minBlocks": 5, "maxSubAccounts": 5 },
{ "minBlocks": 4, "maxSubAccounts": 3 },
{ "minBlocks": 0, "maxSubAccounts": 0 }
],
"featureTriggers": {
"messageHeight": 0,
"atHeight": 0,