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