mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-23 04:36:50 +00:00
Interim work on TRANSFER_PRIVS transaction
Converted AccountData's initialLevel to blocksMintedAdjustment. Corresponding changes to AccountLevelTransaction so that level set in genesis block is converted to blocksMintedAdjustment, via cumulativeBlocksByLevel. Ditto changes to HSQLDBAccountRepository, HSQLDBDatabaseUpdates, [HSQLDB]TransactionRepository, etc. Changes to API call POST /admin/mintingaccounts to check passed reward-share private key maps to a reward-share with minting account that still has privilege to mint. It's possible for a TRANSFER_PRIVS transaction to transfer away minting privileges from a minting account referenced by in a reward-share. Change to RewardShareTransaction to allow users to cancel a reward-share even if minting-account component no longer has minting privs. This should allow users to clean up more after a privs transfer. Re-order processing/orphaning in Block.process()/Block.orphan() to be more consistent and also to take in account changes that might have been caused by TRANSFER_PRIVS transactions which affect who might actually receive block rewards/tx fees. Founders now gain blocksMinted & levels as part of minting blocks. (Needed to make TRANSFER_PRIVS from a founder account to work). BlockMinter now has added checks to make sure that the reward-shares it might use to mint blocks still have valid minting-accounts. i.e. that the minting-account component of reward-share hasn't had minting privs transferred away by TRANSFER_PRIVS tx. Controller now rejects online-accounts from peers that no longer have minting privs (e.g. transferred away by TRANSFER_PRIVS) Corresponding, Controller no longer SENDS online-accounts that no longer have minting privs to other peers. Added some tests - more tests needed, e.g. for multiple transfers into the same account, or a test for minting post transfer for both sender & recipient.
This commit is contained in:
218
src/test/java/org/qora/test/TransferPrivsTests.java
Normal file
218
src/test/java/org/qora/test/TransferPrivsTests.java
Normal file
@@ -0,0 +1,218 @@
|
||||
package org.qora.test;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qora.block.BlockChain;
|
||||
import org.qora.data.account.AccountData;
|
||||
import org.qora.data.transaction.BaseTransactionData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
import org.qora.data.transaction.TransferPrivsTransactionData;
|
||||
import org.qora.repository.DataException;
|
||||
import org.qora.repository.Repository;
|
||||
import org.qora.repository.RepositoryManager;
|
||||
import org.qora.test.common.BlockUtils;
|
||||
import org.qora.test.common.Common;
|
||||
import org.qora.test.common.TestAccount;
|
||||
import org.qora.test.common.TransactionUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public class TransferPrivsTests extends Common {
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws DataException {
|
||||
Common.useDefaultSettings();
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest() throws DataException {
|
||||
Common.orphanCheck();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAliceIntoDilbertTransferPrivs() throws DataException {
|
||||
final List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
final int maximumLevel = cumulativeBlocksByLevel.size() - 1;
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount alice = Common.getTestAccount(repository, "alice");
|
||||
AccountData initialAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
|
||||
|
||||
TestAccount dilbert = Common.getTestAccount(repository, "dilbert");
|
||||
AccountData initialDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
|
||||
|
||||
// Blocks needed by Alice to get Dilbert to next level post-combine
|
||||
final int expectedPostCombineLevel = initialDilbertData.getLevel() + 1;
|
||||
final int blocksNeeded = cumulativeBlocksByLevel.get(expectedPostCombineLevel) - initialDilbertData.getBlocksMinted() - initialDilbertData.getBlocksMintedAdjustment();
|
||||
|
||||
// Level we expect Alice to reach after minting above blocks
|
||||
int expectedLevel = 0;
|
||||
for (int newLevel = maximumLevel; newLevel > 0; --newLevel)
|
||||
if (blocksNeeded >= cumulativeBlocksByLevel.get(newLevel)) {
|
||||
expectedLevel = newLevel;
|
||||
break;
|
||||
}
|
||||
|
||||
// Mint enough blocks to bump recipient level when we combine accounts
|
||||
for (int bc = 0; bc < blocksNeeded; ++bc)
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
// Check minting account has gained level
|
||||
assertEquals("minter level incorrect", expectedLevel, (int) alice.getLevel());
|
||||
|
||||
// Grab pre-combine versions of Alice and Dilbert data
|
||||
AccountData preCombineAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
|
||||
AccountData preCombineDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
|
||||
assertEquals(expectedLevel, preCombineAliceData.getLevel());
|
||||
|
||||
// Combine Alice into Dilbert
|
||||
byte[] reference = alice.getLastReference();
|
||||
long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1;
|
||||
int txGroupId = 0;
|
||||
BigDecimal fee = BigDecimal.ONE.setScale(8);
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, alice.getPublicKey(), fee, null);
|
||||
TransactionData transactionData = new TransferPrivsTransactionData(baseTransactionData, dilbert.getAddress());
|
||||
|
||||
TransactionUtils.signAndMint(repository, transactionData, alice);
|
||||
|
||||
AccountData newAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
|
||||
AccountData newDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
|
||||
|
||||
checkSenderCleared(newAliceData);
|
||||
|
||||
// Confirm recipient has bumped level
|
||||
assertEquals("recipient's level incorrect", expectedPostCombineLevel, newDilbertData.getLevel());
|
||||
|
||||
// Confirm recipient has gained sender's flags
|
||||
assertEquals("recipient's flags should be changed", initialAliceData.getFlags() | initialDilbertData.getFlags(), (int) newDilbertData.getFlags());
|
||||
|
||||
// Confirm recipient has increased minted block count
|
||||
assertEquals("recipient minted block count incorrect", initialDilbertData.getBlocksMinted() + initialAliceData.getBlocksMinted() + blocksNeeded + 1, newDilbertData.getBlocksMinted());
|
||||
|
||||
// Confirm recipient has increased minted block adjustment
|
||||
assertEquals("recipient minted block adjustment incorrect", initialDilbertData.getBlocksMintedAdjustment() + initialAliceData.getBlocksMintedAdjustment(), newDilbertData.getBlocksMintedAdjustment());
|
||||
|
||||
// Orphan previous block
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
// Sender checks...
|
||||
AccountData orphanedAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
|
||||
checkAccountDataRestored("sender", preCombineAliceData, orphanedAliceData);
|
||||
|
||||
// Recipient checks...
|
||||
AccountData orphanedDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
|
||||
checkAccountDataRestored("recipient", preCombineDilbertData, orphanedDilbertData);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDilbertIntoAliceTransferPrivs() throws DataException {
|
||||
final List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
final int maximumLevel = cumulativeBlocksByLevel.size() - 1;
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount alice = Common.getTestAccount(repository, "alice");
|
||||
AccountData initialAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
|
||||
|
||||
TestAccount dilbert = Common.getTestAccount(repository, "dilbert");
|
||||
AccountData initialDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
|
||||
|
||||
// Blocks needed by Alice to get Alice to next level post-combine
|
||||
final int expectedPostCombineLevel = initialDilbertData.getLevel() + 1;
|
||||
final int blocksNeeded = cumulativeBlocksByLevel.get(expectedPostCombineLevel) - initialDilbertData.getBlocksMinted() - initialDilbertData.getBlocksMintedAdjustment();
|
||||
|
||||
// Level we expect Alice to reach after minting above blocks
|
||||
int expectedLevel = 0;
|
||||
for (int newLevel = maximumLevel; newLevel > 0; --newLevel)
|
||||
if (blocksNeeded >= cumulativeBlocksByLevel.get(newLevel)) {
|
||||
expectedLevel = newLevel;
|
||||
break;
|
||||
}
|
||||
|
||||
// Mint enough blocks to bump recipient level when we combine accounts
|
||||
for (int bc = 0; bc < blocksNeeded; ++bc)
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
// Check minting account has gained level
|
||||
assertEquals("minter level incorrect", expectedLevel, (int) alice.getLevel());
|
||||
|
||||
// Grab pre-combine versions of Alice and Dilbert data
|
||||
AccountData preCombineAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
|
||||
AccountData preCombineDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
|
||||
assertEquals(expectedLevel, preCombineAliceData.getLevel());
|
||||
|
||||
// Combine Dilbert into Alice
|
||||
byte[] reference = dilbert.getLastReference();
|
||||
long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1;
|
||||
int txGroupId = 0;
|
||||
BigDecimal fee = BigDecimal.ONE.setScale(8);
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, dilbert.getPublicKey(), fee, null);
|
||||
TransactionData transactionData = new TransferPrivsTransactionData(baseTransactionData, alice.getAddress());
|
||||
|
||||
TransactionUtils.signAndMint(repository, transactionData, dilbert);
|
||||
|
||||
AccountData newAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
|
||||
AccountData newDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
|
||||
|
||||
checkSenderCleared(newDilbertData);
|
||||
|
||||
// Confirm recipient has bumped level
|
||||
assertEquals("recipient's level incorrect", expectedPostCombineLevel, newAliceData.getLevel());
|
||||
|
||||
// Confirm recipient has gained sender's flags
|
||||
assertEquals("recipient's flags should be changed", initialAliceData.getFlags() | initialDilbertData.getFlags(), (int) newAliceData.getFlags());
|
||||
|
||||
// Confirm recipient has increased minted block count
|
||||
assertEquals("recipient minted block count incorrect", initialDilbertData.getBlocksMinted() + initialAliceData.getBlocksMinted() + blocksNeeded + 1, newAliceData.getBlocksMinted());
|
||||
|
||||
// Confirm recipient has increased minted block adjustment
|
||||
assertEquals("recipient minted block adjustment incorrect", initialDilbertData.getBlocksMintedAdjustment() + initialAliceData.getBlocksMintedAdjustment(), newAliceData.getBlocksMintedAdjustment());
|
||||
|
||||
// Orphan previous block
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
// Sender checks...
|
||||
AccountData orphanedDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
|
||||
checkAccountDataRestored("sender", preCombineDilbertData, orphanedDilbertData);
|
||||
|
||||
// Recipient checks...
|
||||
AccountData orphanedAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
|
||||
checkAccountDataRestored("recipient", preCombineAliceData, orphanedAliceData);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSenderCleared(AccountData senderAccountData) {
|
||||
// Confirm sender has zeroed flags
|
||||
assertEquals("sender's flags should be zeroed", 0, (int) senderAccountData.getFlags());
|
||||
|
||||
// Confirm sender has zeroed level
|
||||
assertEquals("sender's level should be zeroed", 0, (int) senderAccountData.getLevel());
|
||||
|
||||
// Confirm sender has zeroed minted block count
|
||||
assertEquals("sender's minted block count should be zeroed", 0, (int) senderAccountData.getBlocksMinted());
|
||||
|
||||
// Confirm sender has zeroed minted block adjustment
|
||||
assertEquals("sender's minted block adjustment should be zeroed", 0, (int) senderAccountData.getBlocksMintedAdjustment());
|
||||
}
|
||||
|
||||
private void checkAccountDataRestored(String accountName, AccountData expectedAccountData, AccountData actualAccountData) {
|
||||
// Confirm flags have been restored
|
||||
assertEquals(accountName + "'s flags weren't restored", expectedAccountData.getFlags(), actualAccountData.getFlags());
|
||||
|
||||
// Confirm minted blocks count
|
||||
assertEquals(accountName + "'s minted block count wasn't restored", expectedAccountData.getBlocksMinted(), actualAccountData.getBlocksMinted());
|
||||
|
||||
// Confirm minted block adjustment
|
||||
assertEquals(accountName + "'s minted block adjustment wasn't restored", expectedAccountData.getBlocksMintedAdjustment(), actualAccountData.getBlocksMintedAdjustment());
|
||||
|
||||
// Confirm level has been restored
|
||||
assertEquals(accountName + "'s level wasn't restored", expectedAccountData.getLevel(), actualAccountData.getLevel());
|
||||
}
|
||||
|
||||
}
|
@@ -160,10 +160,10 @@ public class Common {
|
||||
AccountBalanceData initialBalance = initialBalances.get(i);
|
||||
AccountBalanceData remainingBalance = remainingBalances.get(i);
|
||||
|
||||
assertEquals("Remaining balance's asset differs", initialBalance.getAssetId(), remainingBalance.getAssetId());
|
||||
assertEquals("Remaining balance's address differs", initialBalance.getAddress(), remainingBalance.getAddress());
|
||||
assertEquals(initialBalance.getAddress() + " remaining balance's asset differs", initialBalance.getAssetId(), remainingBalance.getAssetId());
|
||||
|
||||
assertEqualBigDecimals("Remaining balance differs", initialBalance.getBalance(), remainingBalance.getBalance());
|
||||
assertEqualBigDecimals(initialBalance.getAddress() + " remaining balance differs", initialBalance.getBalance(), remainingBalance.getBalance());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,17 @@
|
||||
package org.qora.test.common.transaction;
|
||||
|
||||
import org.qora.account.PrivateKeyAccount;
|
||||
import org.qora.data.transaction.TransferPrivsTransactionData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
import org.qora.repository.DataException;
|
||||
import org.qora.repository.Repository;
|
||||
|
||||
public class TransferPrivsTestTransaction extends TestTransaction {
|
||||
|
||||
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
|
||||
String recipient = account.getAddress();
|
||||
|
||||
return new TransferPrivsTransactionData(generateBase(account), recipient);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user