@ -3,7 +3,10 @@ package org.qora.test;
import org.junit.After ;
import org.junit.Before ;
import org.junit.Test ;
import org.qora.account.Account ;
import org.qora.account.PrivateKeyAccount ;
import org.qora.block.BlockChain ;
import org.qora.block.BlockMinter ;
import org.qora.data.account.AccountData ;
import org.qora.data.transaction.BaseTransactionData ;
import org.qora.data.transaction.TransactionData ;
@ -11,6 +14,7 @@ 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.AccountUtils ;
import org.qora.test.common.BlockUtils ;
import org.qora.test.common.Common ;
import org.qora.test.common.TestAccount ;
@ -23,9 +27,14 @@ import java.util.List;
public class TransferPrivsTests extends Common {
private static List < Integer > cumulativeBlocksByLevel ;
@Before
public void beforeTest ( ) throws DataException {
Common . useDefaultSettings ( ) ;
cumulativeBlocksByLevel = BlockChain . getInstance ( ) . getCumulativeBlocksByLevel ( ) ;
}
@After
@ -35,159 +44,229 @@ public class TransferPrivsTests extends Common {
@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 ( ) ;
assertTrue ( alice . canMint ( ) ) ;
assertTrue ( dilbert . canMint ( ) ) ;
// 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 ( ) ) ;
// Dilbert has level, Alice does not so we need Alice to mint enough blocks to bump Dilbert's level post-combine
final int expectedPostCombineLevel = dilbert . getLevel ( ) + 1 ;
PrivateKeyAccount aliceMintingAccount = Common . getTestAccount ( repository , "alice-reward-share" ) ;
mintToSurpassLevelPostCombine ( repository , aliceMintingAccount , dilbert ) ;
// 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 ) ;
combineAccounts ( repository , alice , dilbert , aliceMintingAccount ) ;
AccountData newAliceData = repository . getAccountRepository ( ) . getAccount ( alice . getAddress ( ) ) ;
AccountData newDilbertData = repository . getAccountRepository ( ) . getAccount ( dilbert . getAddress ( ) ) ;
// Grab post-combine data
AccountData postCombineAliceData = repository . getAccountRepository ( ) . getAccount ( alice . getAddress ( ) ) ;
AccountData postCombineDilbertData = repository . getAccountRepository ( ) . getAccount ( dilbert . getAddress ( ) ) ;
checkSenderCleared ( newAliceData ) ;
// Post-combine sender checks
checkSenderPostTransfer ( postCombineAliceData ) ;
assertFalse ( alice . canMint ( ) ) ;
// 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 ( ) ) ;
// Post-combine recipient checks
checkRecipientPostTransfer ( preCombineAliceData , preCombineDilbertData , postCombineDilbertData , expectedPostCombineLevel ) ;
assertTrue ( dilbert . canMint ( ) ) ;
// Orphan previous block
BlockUtils . orphanLastBlock ( repository ) ;
// Sender checks...
// Sender checks
AccountData orphanedAliceData = repository . getAccountRepository ( ) . getAccount ( alice . getAddress ( ) ) ;
checkAccountDataRestored ( "sender" , preCombineAliceData , orphanedAliceData ) ;
assertTrue ( alice . canMint ( ) ) ;
// Recipient checks...
// Recipient checks
AccountData orphanedDilbertData = repository . getAccountRepository ( ) . getAccount ( dilbert . getAddress ( ) ) ;
checkAccountDataRestored ( "recipient" , preCombineDilbertData , orphanedDilbertData ) ;
assertTrue ( dilbert . canMint ( ) ) ;
}
}
@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 ;
}
assertTrue ( dilbert . canMint ( ) ) ;
assertTrue ( alice . canMint ( ) ) ;
// 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 ( ) ) ;
// Dilbert has level, Alice does not so we need Alice to mint enough blocks to surpass Dilbert's level post-combine
final int expectedPostCombineLevel = dilbert . getLevel ( ) + 1 ;
PrivateKeyAccount mintingAccount = Common . getTestAccount ( repository , "alice-reward-share" ) ;
mintToSurpassLevelPostCombine ( repository , mintingAccount , dilbert ) ;
// 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 ) ;
combineAccounts ( repository , dilbert , alice , mintingAccount ) ;
BaseTransactionData baseTransactionData = new BaseTransactionData ( timestamp , txGroupId , reference , dilbert . getPublicKey ( ) , fee , null ) ;
TransactionData transactionData = new TransferPrivsTransactionData ( baseTransactionData , alice . getAddress ( ) ) ;
// Grab post-combine data
AccountData postCombineAliceData = repository . getAccountRepository ( ) . getAccount ( alice . getAddress ( ) ) ;
AccountData postCombineDilbertData = repository . getAccountRepository ( ) . getAccount ( dilbert . getAddress ( ) ) ;
TransactionUtils . signAndMint ( repository , transactionData , dilbert ) ;
// Post-combine sender checks
checkSenderPostTransfer ( postCombineDilbertData ) ;
assertFalse ( dilbert . canMint ( ) ) ;
AccountData newAliceData = repository . getAccountRepository ( ) . getAccount ( alice . getAddress ( ) ) ;
AccountData newDilbertData = repository . getAccountRepository ( ) . getAccount ( dilbert . getAddress ( ) ) ;
// Post-combine recipient checks
checkRecipientPostTransfer ( preCombineDilbertData , preCombineAliceData , postCombineAliceData , expectedPostCombineLevel ) ;
assertTrue ( alice . canMint ( ) ) ;
checkSenderCleared ( newDilbertData ) ;
// Orphan previous block
BlockUtils . orphanLastBlock ( repository ) ;
// Sender checks
AccountData orphanedDilbertData = repository . getAccountRepository ( ) . getAccount ( dilbert . getAddress ( ) ) ;
checkAccountDataRestored ( "sender" , preCombineDilbertData , orphanedDilbertData ) ;
assertTrue ( dilbert . canMint ( ) ) ;
// Recipient checks
AccountData orphanedAliceData = repository . getAccountRepository ( ) . getAccount ( alice . getAddress ( ) ) ;
checkAccountDataRestored ( "recipient" , preCombineAliceData , orphanedAliceData ) ;
assertTrue ( alice . canMint ( ) ) ;
}
}
@Test
public void testMultipleIntoChloeTransferPrivs ( ) throws DataException {
try ( final Repository repository = RepositoryManager . getRepository ( ) ) {
// Alice needs to mint block containing REWARD_SHARE BEFORE Alice loses minting privs
byte [ ] aliceChloeRewardSharePrivateKey = AccountUtils . rewardShare ( repository , "alice" , "chloe" , BigDecimal . ZERO ) ; // Block minted by Alice
PrivateKeyAccount aliceChloeRewardShareAccount = new PrivateKeyAccount ( repository , aliceChloeRewardSharePrivateKey ) ;
// Confirm recipient has bumped level
assertEquals ( "recipient's level incorrect" , expectedPostCombineLevel , newAliceData . getLevel ( ) ) ;
// Alice needs to mint block containing REWARD_SHARE BEFORE Alice loses minting privs
byte [ ] dilbertRewardSharePrivateKey = AccountUtils . rewardShare ( repository , "dilbert" , "dilbert" , BigDecimal . ZERO ) ; // Block minted by Alice
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount ( repository , dilbertRewardSharePrivateKey ) ;
// Confirm recipient has gained sender's flags
assertEquals ( "recipient's flags should be changed" , initialAliceData . getFlags ( ) | initialDilbertData . getFlags ( ) , ( int ) newAliceData . getFlags ( ) ) ;
TestAccount alice = Common . getTestAccount ( repository , "alice" ) ;
TestAccount chloe = Common . getTestAccount ( repository , "chloe" ) ;
TestAccount dilbert = Common . getTestAccount ( repository , "dilbert" ) ;
// Confirm recipient has increased minted block count
assertEquals ( "recipient minted block count incorrect" , initialDilbertData . getBlocksMinted ( ) + initialAliceData . getBlocksMinted ( ) + blocksNeeded + 1 , newAliceData . getBlocksMinted ( ) ) ;
assertTrue ( dilbert . canMint ( ) ) ;
assertFalse ( chloe . canMint ( ) ) ;
// Confirm recipient has increased minted block adjustment
assertEquals ( "recipient minted block adjustment incorrect" , initialDilbertData . getBlocksMintedAdjustment ( ) + initialAliceData . getBlocksMintedAdjustment ( ) , newAliceData . getBlocksMintedAdjustment ( ) ) ;
// COMBINE DILBERT INTO CHLOE
// Orphan previous block
// Alice-Chloe reward share needs to mint enough blocks to surpass Dilbert's level post-combine
final int expectedPost1stCombineLevel = dilbert . getLevel ( ) + 1 ;
mintToSurpassLevelPostCombine ( repository , aliceChloeRewardShareAccount , dilbert ) ;
// Grab pre-combine versions of Dilbert and Chloe data
AccountData pre1stCombineDilbertData = repository . getAccountRepository ( ) . getAccount ( dilbert . getAddress ( ) ) ;
AccountData pre1stCombineChloeData = repository . getAccountRepository ( ) . getAccount ( chloe . getAddress ( ) ) ;
final int pre1stCombineBlockHeight = repository . getBlockRepository ( ) . getBlockchainHeight ( ) ;
// Combine Dilbert into Chloe
combineAccounts ( repository , dilbert , chloe , dilbertRewardShareAccount ) ;
// Grab post-combine data
AccountData post1stCombineDilbertData = repository . getAccountRepository ( ) . getAccount ( dilbert . getAddress ( ) ) ;
AccountData post1stCombineChloeData = repository . getAccountRepository ( ) . getAccount ( chloe . getAddress ( ) ) ;
// Post-combine sender checks
checkSenderPostTransfer ( post1stCombineDilbertData ) ;
assertFalse ( dilbert . canMint ( ) ) ;
// Post-combine recipient checks
checkRecipientPostTransfer ( pre1stCombineDilbertData , pre1stCombineChloeData , post1stCombineChloeData , expectedPost1stCombineLevel ) ;
assertTrue ( chloe . canMint ( ) ) ;
// COMBINE ALICE INTO CHLOE
assertTrue ( alice . canMint ( ) ) ;
assertTrue ( chloe . canMint ( ) ) ;
// Alice needs to mint enough blocks to surpass Chloe's level post-combine
final int expectedPost2ndCombineLevel = chloe . getLevel ( ) + 1 ;
PrivateKeyAccount aliceMintingAccount = Common . getTestAccount ( repository , "alice-reward-share" ) ;
mintToSurpassLevelPostCombine ( repository , aliceMintingAccount , chloe ) ;
// Grab pre-combine versions of Alice and Chloe data
AccountData pre2ndCombineAliceData = repository . getAccountRepository ( ) . getAccount ( alice . getAddress ( ) ) ;
AccountData pre2ndCombineChloeData = repository . getAccountRepository ( ) . getAccount ( chloe . getAddress ( ) ) ;
// Combine Alice into Chloe
combineAccounts ( repository , alice , chloe , aliceMintingAccount ) ;
// Grab post-combine data
AccountData post2ndCombineAliceData = repository . getAccountRepository ( ) . getAccount ( alice . getAddress ( ) ) ;
AccountData post2ndCombineChloeData = repository . getAccountRepository ( ) . getAccount ( chloe . getAddress ( ) ) ;
// Post-combine sender checks
checkSenderPostTransfer ( post2ndCombineAliceData ) ;
assertFalse ( alice . canMint ( ) ) ;
// Post-combine recipient checks
checkRecipientPostTransfer ( pre2ndCombineAliceData , pre2ndCombineChloeData , post2ndCombineChloeData , expectedPost2ndCombineLevel ) ;
assertTrue ( chloe . canMint ( ) ) ;
// Orphan 2nd combine
BlockUtils . orphanLastBlock ( repository ) ;
// Sender checks...
// Sender checks
AccountData orphanedAliceData = repository . getAccountRepository ( ) . getAccount ( alice . getAddress ( ) ) ;
checkAccountDataRestored ( "sender" , pre2ndCombineAliceData , orphanedAliceData ) ;
assertTrue ( alice . canMint ( ) ) ;
// Recipient checks
AccountData orphanedChloeData = repository . getAccountRepository ( ) . getAccount ( chloe . getAddress ( ) ) ;
checkAccountDataRestored ( "recipient" , pre2ndCombineChloeData , orphanedChloeData ) ;
assertTrue ( chloe . canMint ( ) ) ;
// Orphan 1nd combine
BlockUtils . orphanToBlock ( repository , pre1stCombineBlockHeight ) ;
// Sender checks
AccountData orphanedDilbertData = repository . getAccountRepository ( ) . getAccount ( dilbert . getAddress ( ) ) ;
checkAccountDataRestored ( "sender" , preCombineDilbertData , orphanedDilbertData ) ;
checkAccountDataRestored ( "sender" , pre1stCombineDilbertData , orphanedDilbertData ) ;
assertTrue ( dilbert . canMint ( ) ) ;
// Recipient checks...
AccountData orphanedAliceData = repository . getAccountRepository ( ) . getAccount ( alice . getAddress ( ) ) ;
checkAccountDataRestored ( "recipient" , preCombineAliceData , orphanedAliceData ) ;
// Recipient checks
orphanedChloeData = repository . getAccountRepository ( ) . getAccount ( chloe . getAddress ( ) ) ;
checkAccountDataRestored ( "recipient" , pre1stCombineChloeData , orphanedChloeData ) ;
// Chloe canMint() would return true here due to Alice-Chloe reward-share minting at top of method, so undo that minting by orphaning back to block 1
BlockUtils . orphanToBlock ( repository , 1 ) ;
assertFalse ( chloe . canMint ( ) ) ;
}
}
private void checkSenderCleared ( AccountData senderAccountData ) {
/** Mint enough blocks, using <tt>mintingAccount</tt> so that minting account(s) will surpass <tt>targetAccount</tt>'s level post-combine. */
private void mintToSurpassLevelPostCombine ( Repository repository , PrivateKeyAccount mintingAccount , Account targetAccount ) throws DataException {
AccountData preMintAccountData = repository . getAccountRepository ( ) . getAccount ( targetAccount . getAddress ( ) ) ;
final int minterBlocksNeeded = cumulativeBlocksByLevel . get ( preMintAccountData . getLevel ( ) + 1 ) - preMintAccountData . getBlocksMinted ( ) - preMintAccountData . getBlocksMintedAdjustment ( ) ;
// Mint enough blocks to bump testAccount level
for ( int bc = 0 ; bc < minterBlocksNeeded ; + + bc )
BlockMinter . mintTestingBlock ( repository , mintingAccount ) ;
}
/** Combine sender's level, flags and block counts into recipient using TRANSFER_PRIVS transaction. */
private void combineAccounts ( Repository repository , PrivateKeyAccount senderAccount , Account recipientAccount , PrivateKeyAccount mintingAccount ) throws DataException {
byte [ ] reference = senderAccount . 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 , senderAccount . getPublicKey ( ) , fee , null ) ;
TransactionData transactionData = new TransferPrivsTransactionData ( baseTransactionData , recipientAccount . getAddress ( ) ) ;
TransactionUtils . signAsUnconfirmed ( repository , transactionData , senderAccount ) ;
BlockMinter . mintTestingBlock ( repository , mintingAccount ) ;
}
private void checkSenderPostTransfer ( AccountData senderAccountData ) {
// Confirm sender has zeroed flags
assertEquals ( "sender's flags should be zeroed" , 0 , ( int ) senderAccountData . getFlags ( ) ) ;
@ -201,6 +280,20 @@ public class TransferPrivsTests extends Common {
assertEquals ( "sender's minted block adjustment should be zeroed" , 0 , ( int ) senderAccountData . getBlocksMintedAdjustment ( ) ) ;
}
private void checkRecipientPostTransfer ( AccountData preCombineSenderData , AccountData preCombineRecipientData , AccountData postCombineRecipientData , int expectedPostCombineLevel ) {
// Confirm recipient has bumped level
assertEquals ( "recipient's level incorrect" , expectedPostCombineLevel , postCombineRecipientData . getLevel ( ) ) ;
// Confirm recipient has gained sender's flags
assertEquals ( "recipient's flags should be changed" , preCombineSenderData . getFlags ( ) | preCombineRecipientData . getFlags ( ) , ( int ) postCombineRecipientData . getFlags ( ) ) ;
// Confirm recipient has increased minted block count
assertEquals ( "recipient minted block count incorrect" , preCombineRecipientData . getBlocksMinted ( ) + preCombineSenderData . getBlocksMinted ( ) + 1 , postCombineRecipientData . getBlocksMinted ( ) ) ;
// Confirm recipient has increased minted block adjustment
assertEquals ( "recipient minted block adjustment incorrect" , preCombineRecipientData . getBlocksMintedAdjustment ( ) + preCombineSenderData . getBlocksMintedAdjustment ( ) , postCombineRecipientData . 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 ( ) ) ;