diff --git a/src/main/java/org/qora/block/Block.java b/src/main/java/org/qora/block/Block.java index a641975a..c3653131 100644 --- a/src/main/java/org/qora/block/Block.java +++ b/src/main/java/org/qora/block/Block.java @@ -153,14 +153,23 @@ public class Block { this.isRecipientAlsoMinter = this.mintingAccountData.getAddress().equals(this.recipientAccountData.getAddress()); } + /** + * Returns share bin for expanded account. + *

+ * This is a method, not a final variable, because account's level can change between construction and call, + * e.g. during Block.process() where account levels are bumped right before Block.distributeBlockReward(). + * + * @return share "bin" (index into BlockShareByLevel blockchain config, so 0+), or -1 if no bin found + */ int getShareBin() { if (this.isMinterFounder) return -1; final List sharesByLevel = BlockChain.getInstance().getBlockSharesByLevel(); + final int accountLevel = this.mintingAccountData.getLevel(); for (int s = 0; s < sharesByLevel.size(); ++s) - if (sharesByLevel.get(s).levels.contains(this.mintingAccountData.getLevel())) + if (sharesByLevel.get(s).levels.contains(accountLevel)) return s; return -1; @@ -1603,8 +1612,8 @@ public class Block { BigDecimal binAmount = sharesByLevel.get(binIndex).share.multiply(totalAmount).setScale(8, RoundingMode.DOWN); LOGGER.trace(() -> String.format("Bin %d share of %s: %s", binIndex, totalAmount.toPlainString(), binAmount.toPlainString())); - // Spread across all accounts in bin - List binnedAccounts = expandedAccounts.stream().filter(accountInfo -> !accountInfo.isMinterFounder && accountInfo.getShareBin() == binIndex).collect(Collectors.toList()); + // Spread across all accounts in bin. getShareBin() returns -1 for minter accounts that are also founders, so they are effectively filtered out. + List binnedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.getShareBin() == binIndex).collect(Collectors.toList()); if (binnedAccounts.isEmpty()) continue; @@ -1731,8 +1740,20 @@ public class Block { perFounderAmount.toPlainString())); for (int a = 0; a < founderAccounts.size(); ++a) { - Account founderAccount = new Account(this.repository, founderAccounts.get(a).getAddress()); - founderAccount.setConfirmedBalance(Asset.QORT, founderAccount.getConfirmedBalance(Asset.QORT).add(perFounderAmount)); + // If founder is minter in any online reward-shares then founder's amount is spread across these, otherwise founder gets whole amount. + List founderExpandedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.isMinterFounder).collect(Collectors.toList()); + + if (founderExpandedAccounts.isEmpty()) { + // Simple case: no founder-as-minter reward-shares online so founder gets whole amount. + Account founderAccount = new Account(this.repository, founderAccounts.get(a).getAddress()); + founderAccount.setConfirmedBalance(Asset.QORT, founderAccount.getConfirmedBalance(Asset.QORT).add(perFounderAmount)); + } else { + // Distribute over reward-shares + BigDecimal perFounderRewardShareAmount = perFounderAmount.divide(BigDecimal.valueOf(founderExpandedAccounts.size()), RoundingMode.DOWN); + + for (int fea = 0; fea < founderExpandedAccounts.size(); ++fea) + founderExpandedAccounts.get(fea).distribute(perFounderRewardShareAmount); + } } } diff --git a/src/main/java/org/qora/crypto/MemoryPoW.java b/src/main/java/org/qora/crypto/MemoryPoW.java index 62b81e22..1948d71c 100644 --- a/src/main/java/org/qora/crypto/MemoryPoW.java +++ b/src/main/java/org/qora/crypto/MemoryPoW.java @@ -4,7 +4,7 @@ import com.google.common.primitives.Bytes; public class MemoryPoW { - private static final int WORK_BUFFER_LENGTH = 4 * 1024 * 1024; + public static final int WORK_BUFFER_LENGTH = 4 * 1024 * 1024; private static final int WORK_BUFFER_LENGTH_MASK = WORK_BUFFER_LENGTH - 1; private static final int HASH_LENGTH = 32; diff --git a/src/main/java/org/qora/data/transaction/RewardShareTransactionData.java b/src/main/java/org/qora/data/transaction/RewardShareTransactionData.java index 58478611..ab79111b 100644 --- a/src/main/java/org/qora/data/transaction/RewardShareTransactionData.java +++ b/src/main/java/org/qora/data/transaction/RewardShareTransactionData.java @@ -27,6 +27,7 @@ public class RewardShareTransactionData extends TransactionData { @Schema(example = "reward_share_public_key") private byte[] rewardSharePublicKey; + @Schema(description = "Percentage of block rewards that minter shares to recipient, or negative value to cancel existing reward-share") private BigDecimal sharePercent; // No need to ever expose this via API diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java index 8e50e9dc..f9df6876 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java @@ -474,10 +474,11 @@ public class HSQLDBAccountRepository implements AccountRepository { public void save(AccountBalanceData accountBalanceData) throws DataException { // If balance is zero and there are no prior historic balance, then simply delete balances for this assetId (typically during orphaning) if (accountBalanceData.getBalance().signum() == 0) { + String existsSql = "account = ? AND asset_id = ? AND height < (SELECT height - 1 FROM NextBlockHeight)"; // height prior to current block. no matches (obviously) prior to genesis block + boolean hasPriorBalances; try { - hasPriorBalances = this.repository.exists("HistoricAccountBalances", "account = ? AND asset_id = ? AND height < (SELECT IFNULL(MAX(height), 1) FROM Blocks)", - accountBalanceData.getAddress(), accountBalanceData.getAssetId()); + hasPriorBalances = this.repository.exists("HistoricAccountBalances", existsSql, accountBalanceData.getAddress(), accountBalanceData.getAssetId()); } catch (SQLException e) { throw new DataException("Unable to check for historic account balances in repository", e); } diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java index b70bab59..a7b15809 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -876,6 +876,30 @@ public class HSQLDBDatabaseUpdates { + "ON DUPLICATE KEY UPDATE balance = new_row.balance"); break; + case 62: + // Rework sub-queries that need to know next block height as currently they fail for genesis block and/or are still too slow + // Table to hold next block height. + stmt.execute("CREATE TABLE NextBlockHeight (height INT NOT NULL)"); + // Initial value - should work for empty DB or populated DB. + stmt.execute("INSERT INTO NextBlockHeight VALUES (SELECT IFNULL(MAX(height), 0) + 1 FROM Blocks)"); + // We use triggers on Blocks to update a simple "next block height" table + String blockUpdateSql = "UPDATE NextBlockHeight SET height = (SELECT height + 1 FROM Blocks ORDER BY height DESC LIMIT 1)"; + stmt.execute("CREATE TRIGGER Next_block_height_insert_trigger AFTER INSERT ON Blocks " + blockUpdateSql); + stmt.execute("CREATE TRIGGER Next_block_height_update_trigger AFTER UPDATE ON Blocks " + blockUpdateSql); + stmt.execute("CREATE TRIGGER Next_block_height_delete_trigger AFTER DELETE ON Blocks " + blockUpdateSql); + // Now update previously slow/broken sub-queries + stmt.execute("DROP TRIGGER Historic_account_balance_insert_trigger"); + stmt.execute("DROP TRIGGER Historic_account_balance_update_trigger"); + stmt.execute("CREATE TRIGGER Historic_account_balance_insert_trigger AFTER INSERT ON AccountBalances REFERENCING NEW ROW AS new_row FOR EACH ROW " + + "INSERT INTO HistoricAccountBalances VALUES " + + "(new_row.account, new_row.asset_id, (SELECT height from NextBlockHeight), new_row.balance) " + + "ON DUPLICATE KEY UPDATE balance = new_row.balance"); + stmt.execute("CREATE TRIGGER Historic_account_balance_update_trigger AFTER UPDATE ON AccountBalances REFERENCING NEW ROW AS new_row FOR EACH ROW " + + "INSERT INTO HistoricAccountBalances VALUES " + + "(new_row.account, new_row.asset_id, (SELECT height from NextBlockHeight), new_row.balance) " + + "ON DUPLICATE KEY UPDATE balance = new_row.balance"); + break; + default: // nothing to do return false; diff --git a/src/main/java/org/qora/transaction/RewardShareTransaction.java b/src/main/java/org/qora/transaction/RewardShareTransaction.java index 800acc06..6d9121bb 100644 --- a/src/main/java/org/qora/transaction/RewardShareTransaction.java +++ b/src/main/java/org/qora/transaction/RewardShareTransaction.java @@ -112,9 +112,8 @@ public class RewardShareTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { - // Check reward share given to recipient - if (this.rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) < 0 - || this.rewardShareTransactionData.getSharePercent().compareTo(MAX_SHARE) > 0) + // Check reward share given to recipient. Negative is potentially OK to end a current reward-share. Zero also fine. + if (this.rewardShareTransactionData.getSharePercent().compareTo(MAX_SHARE) > 0) return ValidationResult.INVALID_REWARD_SHARE_PERCENT; PublicKeyAccount creator = getCreator(); @@ -144,13 +143,13 @@ public class RewardShareTransaction extends Transaction { if (existingRewardShareData != null && !this.doesRewardShareMatch(existingRewardShareData)) return ValidationResult.INVALID_PUBLIC_KEY; - final boolean isSharePercentZero = this.rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) == 0; + final boolean isSharePercentNegative = this.rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) < 0; if (existingRewardShareData == null) { // This is a new reward-share - // No point starting a new reward-share with 0% share (i.e. delete reward-share) - if (isSharePercentZero) + // No point starting a new reward-share with negative share (i.e. delete reward-share) + if (isSharePercentNegative) return ValidationResult.INVALID_REWARD_SHARE_PERCENT; // Check the minting account hasn't reach maximum number of reward-shares @@ -161,7 +160,7 @@ public class RewardShareTransaction extends Transaction { // This transaction intends to modify/terminate an existing reward-share // Modifying an existing self-share is pointless and forbidden (due to 0 fee). Deleting self-share is OK though. - if (isRecipientAlsoMinter && !isSharePercentZero) + if (isRecipientAlsoMinter && !isSharePercentNegative) return ValidationResult.INVALID_REWARD_SHARE_PERCENT; } @@ -188,8 +187,10 @@ public class RewardShareTransaction extends Transaction { // Save this transaction, with previous share info this.repository.getTransactionRepository().save(rewardShareTransactionData); - // 0% share is actually a request to delete existing reward-share - if (rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) == 0) { + final boolean isSharePercentNegative = this.rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) < 0; + + // Negative share is actually a request to delete existing reward-share + if (isSharePercentNegative) { this.repository.getAccountRepository().delete(mintingAccount.getPublicKey(), rewardShareTransactionData.getRecipient()); } else { // Save reward-share info diff --git a/src/test/java/org/qora/test/AccountBalanceTests.java b/src/test/java/org/qora/test/AccountBalanceTests.java index eff6d836..389707ba 100644 --- a/src/test/java/org/qora/test/AccountBalanceTests.java +++ b/src/test/java/org/qora/test/AccountBalanceTests.java @@ -96,6 +96,11 @@ public class AccountBalanceTests extends Common { BigDecimal initialBalance = testNewerBalance(repository, alice); + // Fetch all historic balances + List historicBalances = repository.getAccountRepository().getHistoricBalances(alice.getAddress(), Asset.QORT); + for (AccountBalanceData historicBalance : historicBalances) + System.out.println(String.format("Balance at height %d: %s", historicBalance.getHeight(), historicBalance.getBalance().toPlainString())); + // Fetch balance at height 1, even though newer balance exists AccountBalanceData accountBalanceData = repository.getAccountRepository().getBalance(alice.getAddress(), Asset.QORT, 1); BigDecimal genesisBalance = accountBalanceData.getBalance(); @@ -115,6 +120,7 @@ public class AccountBalanceTests extends Common { try (final Repository repository = RepositoryManager.getRepository()) { PublicKeyAccount recipientAccount = new PublicKeyAccount(repository, publicKey); + System.out.println(String.format("Test recipient: %s", recipientAccount.getAddress())); // Mint a few blocks for (int i = 0; i < 10; ++i) @@ -124,6 +130,12 @@ public class AccountBalanceTests extends Common { BigDecimal balance = recipientAccount.getConfirmedBalance(Asset.QORT); assertEqualBigDecimals("recipient's balance should be zero", BigDecimal.ZERO, balance); + // Confirm recipient has no historic balances + List historicBalances = repository.getAccountRepository().getHistoricBalances(recipientAccount.getAddress(), Asset.QORT); + for (AccountBalanceData historicBalance : historicBalances) + System.err.println(String.format("Block %d: %s", historicBalance.getHeight(), historicBalance.getBalance().toPlainString())); + assertTrue("recipient should not have historic balances yet", historicBalances.isEmpty()); + // Send 1 QORT to recipient TestAccount sendingAccount = Common.getTestAccount(repository, "alice"); pay(repository, sendingAccount, recipientAccount, BigDecimal.ONE); @@ -145,7 +157,7 @@ public class AccountBalanceTests extends Common { balance = recipientAccount.getConfirmedBalance(Asset.QORT); assertEqualBigDecimals("recipient's balance incorrect", totalAmount, balance); - List historicBalances = repository.getAccountRepository().getHistoricBalances(recipientAccount.getAddress(), Asset.QORT); + historicBalances = repository.getAccountRepository().getHistoricBalances(recipientAccount.getAddress(), Asset.QORT); for (AccountBalanceData historicBalance : historicBalances) System.out.println(String.format("Block %d: %s", historicBalance.getHeight(), historicBalance.getBalance().toPlainString())); @@ -172,6 +184,12 @@ public class AccountBalanceTests extends Common { // Re-check balance from (now) invalid height accountBalanceData = repository.getAccountRepository().getBalance(recipientAccount.getAddress(), Asset.QORT, height - 2); assertNull("recipient's invalid-height balance data should be null", accountBalanceData); + + // Confirm recipient has no historic balances + historicBalances = repository.getAccountRepository().getHistoricBalances(recipientAccount.getAddress(), Asset.QORT); + for (AccountBalanceData historicBalance : historicBalances) + System.err.println(String.format("Block %d: %s", historicBalance.getHeight(), historicBalance.getBalance().toPlainString())); + assertTrue("recipient should have no remaining historic balances", historicBalances.isEmpty()); } } @@ -191,7 +209,7 @@ public class AccountBalanceTests extends Common { @Test public void testRepositorySpeed() throws DataException, SQLException { Random random = new Random(); - final long MAX_QUERY_TIME = 100L; // ms + final long MAX_QUERY_TIME = 80L; // ms try (final Repository repository = RepositoryManager.getRepository()) { System.out.println("Creating random accounts..."); diff --git a/src/test/java/org/qora/test/BTCTests.java b/src/test/java/org/qora/test/BTCTests.java deleted file mode 100644 index 87848c3b..00000000 --- a/src/test/java/org/qora/test/BTCTests.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.qora.test; - -import org.bitcoinj.script.Script; -import org.bitcoinj.script.ScriptBuilder; -import org.junit.Test; -import org.qora.crosschain.BTC; - -import com.google.common.hash.HashCode; - -public class BTCTests { - - @Test - public void testWatchAddress() throws Exception { - // String testAddress = "mrTDPdM15cFWJC4g223BXX5snicfVJBx6M"; - String testAddress = "1GRENT17xMQe2ukPhwAeZU1TaUUon1Qc65"; - - long testStartTime = 1539000000L; - - BTC btc = BTC.getInstance(); - - // Disabled for now, pending further work - // btc.watch(testAddress, testStartTime); - - // Disabled for now, pending further work - // Thread.sleep(5000); - - // Disabled for now, pending further work - // btc.watch(testAddress, testStartTime); - - btc.shutdown(); - } - - @Test - public void testWatchScript() throws Exception { - long testStartTime = 1539000000L; - - BTC btc = BTC.getInstance(); - - byte[] redeemScriptHash = HashCode.fromString("3dbcc35e69ebc449f616fa3eb3723dfad9cbb5b3").asBytes(); - Script redeemScript = ScriptBuilder.createP2SHOutputScript(redeemScriptHash); - redeemScript.setCreationTimeSeconds(testStartTime); - - // Disabled for now, pending further work - // btc.watch(redeemScript); - - // Disabled for now, pending further work - Thread.sleep(5000); - - // Disabled for now, pending further work - // btc.watch(redeemScript); - - btc.shutdown(); - } - - @Test - public void updateCheckpoints() throws Exception { - BTC btc = BTC.getInstance(); - - btc.updateCheckpoints(); - } - -} diff --git a/src/test/java/org/qora/test/EPCTests.java b/src/test/java/org/qora/test/EPCTests.java index fa3c6704..00a075a7 100644 --- a/src/test/java/org/qora/test/EPCTests.java +++ b/src/test/java/org/qora/test/EPCTests.java @@ -50,6 +50,9 @@ public class EPCTests { } private void testEPC(ExecuteProduceConsume testEPC) throws InterruptedException { + final int runTime = 60; // seconds + System.out.println(String.format("Testing EPC for %s seconds:", runTime)); + final long start = System.currentTimeMillis(); testEPC.start(); @@ -67,7 +70,7 @@ public class EPCTests { }, 1L, 1L, TimeUnit.SECONDS); // Let it run for a minute - Thread.sleep(60_000L); + Thread.sleep(runTime * 1000L); statusExecutor.shutdownNow(); final long before = System.currentTimeMillis(); diff --git a/src/test/java/org/qora/test/MemoryPoWTests.java b/src/test/java/org/qora/test/MemoryPoWTests.java index 0fbd984e..be2a52c9 100644 --- a/src/test/java/org/qora/test/MemoryPoWTests.java +++ b/src/test/java/org/qora/test/MemoryPoWTests.java @@ -24,7 +24,7 @@ public class MemoryPoWTests { Integer nonce = MemoryPoW.compute(data, start, range, difficulty); long finishTime = System.currentTimeMillis(); - System.out.println(String.format("Memory-hard PoW took %dms", finishTime - startTime)); + System.out.println(String.format("Memory-hard PoW (buffer size: %dKB, range: %d, leading zeros: %d) took %dms", MemoryPoW.WORK_BUFFER_LENGTH / 1024, range, difficulty, finishTime - startTime)); assertNotNull(nonce); diff --git a/src/test/java/org/qora/test/RepositoryTests.java b/src/test/java/org/qora/test/RepositoryTests.java index f700b337..90ffa2eb 100644 --- a/src/test/java/org/qora/test/RepositoryTests.java +++ b/src/test/java/org/qora/test/RepositoryTests.java @@ -7,11 +7,14 @@ import org.qora.asset.Asset; import org.qora.repository.DataException; import org.qora.repository.Repository; import org.qora.repository.RepositoryManager; +import org.qora.repository.hsqldb.HSQLDBRepository; +import org.qora.test.common.BlockUtils; import org.qora.test.common.Common; import static org.junit.Assert.*; import java.math.BigDecimal; +import java.sql.SQLException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -90,4 +93,52 @@ public class RepositoryTests extends Common { } } + /** Check that the sub-query used to fetch highest block height is optimized by HSQLDB. */ + @Test + public void testBlockHeightSpeed() throws DataException, SQLException { + try (final Repository repository = RepositoryManager.getRepository()) { + // Mint some blocks + System.out.println("Minting test blocks - should take approx. 30 seconds..."); + for (int i = 0; i < 30000; ++i) + BlockUtils.mintBlock(repository); + + final HSQLDBRepository hsqldb = (HSQLDBRepository) repository; + + // Too slow: + testSql(hsqldb, "SELECT IFNULL(MAX(height), 0) + 1 FROM Blocks", false); + + // Fast but if there are no rows, then no result is returned, which causes some triggers to fail: + testSql(hsqldb, "SELECT IFNULL(height, 0) + 1 FROM (SELECT height FROM Blocks ORDER BY height DESC LIMIT 1)", true); + + // Too slow: + testSql(hsqldb, "SELECT COUNT(*) + 1 FROM Blocks", false); + + // 2-stage, using cached value: + hsqldb.prepareStatement("DROP TABLE IF EXISTS TestNextBlockHeight").execute(); + hsqldb.prepareStatement("CREATE TABLE TestNextBlockHeight (height INT NOT NULL)").execute(); + hsqldb.prepareStatement("INSERT INTO TestNextBlockHeight VALUES (SELECT IFNULL(MAX(height), 0) + 1 FROM Blocks)").execute(); + + // 1: Check fetching cached next block height is fast: + testSql(hsqldb, "SELECT height from TestNextBlockHeight", true); + + // 2: Check updating NextBlockHeight (typically called via trigger) is fast: + testSql(hsqldb, "UPDATE TestNextBlockHeight SET height = (SELECT height FROM Blocks ORDER BY height DESC LIMIT 1)", true); + } + } + + private void testSql(HSQLDBRepository hsqldb, String sql, boolean isFast) throws DataException, SQLException { + // Execute query to prime caches + hsqldb.prepareStatement(sql).execute(); + + // Execute again for a slightly more accurate timing + final long start = System.currentTimeMillis(); + hsqldb.prepareStatement(sql).execute(); + + final long executionTime = System.currentTimeMillis() - start; + System.out.println(String.format("%s: [%d ms] SQL: %s", (isFast ? "fast": "slow"), executionTime, sql)); + + final long threshold = 3; // ms + assertTrue( isFast ? executionTime < threshold : executionTime > threshold); + } + } diff --git a/src/test/java/org/qora/test/SerializationTests.java b/src/test/java/org/qora/test/SerializationTests.java index e5345417..096ddbac 100644 --- a/src/test/java/org/qora/test/SerializationTests.java +++ b/src/test/java/org/qora/test/SerializationTests.java @@ -117,16 +117,16 @@ public class SerializationTests extends Common { public void benchmarkBitSetCompression() { Random random = new Random(); - System.out.println(String.format("Known Online UncompressedBitSet UncompressedIntList Compressed")); + System.out.println(String.format("Known Online UncompressedBitSet UncompressedIntList Compressed")); - for (int run = 0; run < 1000; ++run) { + for (int run = 0; run < 100; ++run) { final int numberOfKnownAccounts = random.nextInt(1 << 17) + 1; - // 5% to 25% - final int numberOfAccountsToEncode = random.nextInt((numberOfKnownAccounts / 20) + numberOfKnownAccounts / 5); + // 3% to 23% + final int numberOfAccountsToEncode = random.nextInt((numberOfKnownAccounts / 20) + numberOfKnownAccounts / 3); // Enough uncompressed bytes to fit one bit per known account - final int uncompressedBitSetSize = ((numberOfKnownAccounts - 1) >> 3) + 1; + final int uncompressedBitSetSize = ((numberOfKnownAccounts - 1) >> 3) + 1; // the >> 3 is to scale size from 8 bits to 1 byte // Size of a simple list of ints final int uncompressedIntListSize = numberOfAccountsToEncode * 4; @@ -138,7 +138,7 @@ public class SerializationTests extends Common { int compressedSize = compressedSet.toByteBuffer().remaining(); - System.out.println(String.format("%d %d %d %d %d", numberOfKnownAccounts, numberOfAccountsToEncode, uncompressedBitSetSize, uncompressedIntListSize, compressedSize)); + System.out.println(String.format("%6d %6d %18d %19d %10d", numberOfKnownAccounts, numberOfAccountsToEncode, uncompressedBitSetSize, uncompressedIntListSize, compressedSize)); } } diff --git a/src/test/java/org/qora/test/minting/RewardShareTests.java b/src/test/java/org/qora/test/minting/RewardShareTests.java index e14c0cb6..42ef8ba0 100644 --- a/src/test/java/org/qora/test/minting/RewardShareTests.java +++ b/src/test/java/org/qora/test/minting/RewardShareTests.java @@ -23,6 +23,8 @@ import org.qora.utils.Base58; public class RewardShareTests extends Common { + private static final BigDecimal CANCEL_SHARE_PERCENT = BigDecimal.ONE.negate(); + @Before public void beforeTest() throws DataException { Common.useDefaultSettings(); @@ -60,7 +62,7 @@ public class RewardShareTests extends Common { assertEqualBigDecimals("Incorrect share percentage", sharePercent, rewardShareData.getSharePercent()); // Delete reward-share - byte[] newRewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "bob", BigDecimal.ZERO); + byte[] newRewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "bob", CANCEL_SHARE_PERCENT); PrivateKeyAccount newRewardShareAccount = new PrivateKeyAccount(repository, newRewardSharePrivateKey); // Confirm reward-share keys match @@ -112,10 +114,10 @@ public class RewardShareTests extends Common { } @Test - public void testZeroInitialShareInvalid() throws DataException { + public void testNegativeInitialShareInvalid() throws DataException { try (final Repository repository = RepositoryManager.getRepository()) { - // Create invalid REWARD_SHARE transaction with initial 0% reward share - TransactionData transactionData = AccountUtils.createRewardShare(repository, "alice", "bob", BigDecimal.ZERO); + // Create invalid REWARD_SHARE transaction with initial negative reward share + TransactionData transactionData = AccountUtils.createRewardShare(repository, "alice", "bob", CANCEL_SHARE_PERCENT); // Confirm transaction is invalid Transaction transaction = Transaction.fromData(repository, transactionData); @@ -162,8 +164,8 @@ public class RewardShareTests extends Common { validationResult = newTransaction.isValidUnconfirmed(); assertNotSame("Subsequent zero-fee self-share should be invalid", ValidationResult.OK, validationResult); - // Subsequent terminating (0% share) self-reward-share should be OK - newTransactionData = AccountUtils.createRewardShare(repository, testAccountName, testAccountName, BigDecimal.ZERO); + // Subsequent terminating (negative share) self-reward-share should be OK + newTransactionData = AccountUtils.createRewardShare(repository, testAccountName, testAccountName, CANCEL_SHARE_PERCENT); newTransaction = Transaction.fromData(repository, newTransactionData); // Confirm terminating reward-share is valid