mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-14 11:15:49 +00:00
For Balance Recorder, reward recordings only, that is the default.
This commit is contained in:
parent
c71f5fa8bf
commit
4f0aabfb36
@ -283,7 +283,7 @@ public class AssetsResource {
|
|||||||
Optional<HSQLDBBalanceRecorder> recorder = HSQLDBBalanceRecorder.getInstance();
|
Optional<HSQLDBBalanceRecorder> recorder = HSQLDBBalanceRecorder.getInstance();
|
||||||
|
|
||||||
if( recorder.isPresent()) {
|
if( recorder.isPresent()) {
|
||||||
Optional<BlockHeightRangeAddressAmounts> addressAmounts = recorder.get().getAddressAmounts(new BlockHeightRange(begin, end));
|
Optional<BlockHeightRangeAddressAmounts> addressAmounts = recorder.get().getAddressAmounts(new BlockHeightRange(begin, end, false));
|
||||||
|
|
||||||
if( addressAmounts.isPresent() ) {
|
if( addressAmounts.isPresent() ) {
|
||||||
return addressAmounts.get().getAmounts().stream()
|
return addressAmounts.get().getAmounts().stream()
|
||||||
|
@ -12,12 +12,15 @@ public class BlockHeightRange {
|
|||||||
|
|
||||||
private int end;
|
private int end;
|
||||||
|
|
||||||
|
private boolean isRewardDistribution;
|
||||||
|
|
||||||
public BlockHeightRange() {
|
public BlockHeightRange() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockHeightRange(int begin, int end) {
|
public BlockHeightRange(int begin, int end, boolean isRewardDistribution) {
|
||||||
this.begin = begin;
|
this.begin = begin;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
|
this.isRewardDistribution = isRewardDistribution;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBegin() {
|
public int getBegin() {
|
||||||
@ -28,6 +31,10 @@ public class BlockHeightRange {
|
|||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRewardDistribution() {
|
||||||
|
return isRewardDistribution;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
@ -46,6 +53,7 @@ public class BlockHeightRange {
|
|||||||
return "BlockHeightRange{" +
|
return "BlockHeightRange{" +
|
||||||
"begin=" + begin +
|
"begin=" + begin +
|
||||||
", end=" + end +
|
", end=" + end +
|
||||||
|
", isRewardDistribution=" + isRewardDistribution +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package org.qortal.repository.hsqldb;
|
|||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.qortal.api.SearchMode;
|
import org.qortal.api.SearchMode;
|
||||||
|
import org.qortal.api.resource.TransactionsResource;
|
||||||
import org.qortal.arbitrary.misc.Category;
|
import org.qortal.arbitrary.misc.Category;
|
||||||
import org.qortal.arbitrary.misc.Service;
|
import org.qortal.arbitrary.misc.Service;
|
||||||
import org.qortal.controller.Controller;
|
import org.qortal.controller.Controller;
|
||||||
@ -14,7 +15,10 @@ import org.qortal.data.arbitrary.ArbitraryResourceCache;
|
|||||||
import org.qortal.data.arbitrary.ArbitraryResourceData;
|
import org.qortal.data.arbitrary.ArbitraryResourceData;
|
||||||
import org.qortal.data.arbitrary.ArbitraryResourceMetadata;
|
import org.qortal.data.arbitrary.ArbitraryResourceMetadata;
|
||||||
import org.qortal.data.arbitrary.ArbitraryResourceStatus;
|
import org.qortal.data.arbitrary.ArbitraryResourceStatus;
|
||||||
|
import org.qortal.data.transaction.TransactionData;
|
||||||
import org.qortal.repository.DataException;
|
import org.qortal.repository.DataException;
|
||||||
|
import org.qortal.repository.Repository;
|
||||||
|
import org.qortal.repository.RepositoryManager;
|
||||||
import org.qortal.settings.Settings;
|
import org.qortal.settings.Settings;
|
||||||
import org.qortal.utils.BalanceRecorderUtils;
|
import org.qortal.utils.BalanceRecorderUtils;
|
||||||
|
|
||||||
@ -434,37 +438,11 @@ public class HSQLDBCacheUtils {
|
|||||||
// if there is a prior height
|
// if there is a prior height
|
||||||
if(priorHeight.isPresent()) {
|
if(priorHeight.isPresent()) {
|
||||||
|
|
||||||
BlockHeightRange blockHeightRange = new BlockHeightRange(priorHeight.get(), currentHeight);
|
boolean isRewardDistribution = BalanceRecorderUtils.isRewardDistributionRange(priorHeight.get(), currentHeight);
|
||||||
|
|
||||||
LOGGER.debug("building dynamics for block heights: range = " + blockHeightRange);
|
// if this range has a reward recording block or if other blocks are enabled for recording
|
||||||
|
if( isRewardDistribution || !Settings.getInstance().isRewardRecordingOnly() ) {
|
||||||
List<AccountBalanceData> currentBalances = balancesByHeight.get(currentHeight);
|
produceBalanceDynamics(currentHeight, priorHeight, isRewardDistribution, balancesByHeight, balanceDynamics, capacity);
|
||||||
|
|
||||||
List<AddressAmountData> currentDynamics
|
|
||||||
= BalanceRecorderUtils.buildBalanceDynamics(
|
|
||||||
currentBalances,
|
|
||||||
balancesByHeight.get(priorHeight.get()),
|
|
||||||
Settings.getInstance().getMinimumBalanceRecording());
|
|
||||||
|
|
||||||
LOGGER.debug("dynamics built: count = " + currentDynamics.size());
|
|
||||||
|
|
||||||
if(LOGGER.isDebugEnabled())
|
|
||||||
currentDynamics.stream()
|
|
||||||
.sorted(Comparator.comparingLong(AddressAmountData::getAmount).reversed())
|
|
||||||
.limit(Settings.getInstance().getTopBalanceLoggingLimit())
|
|
||||||
.forEach(top5Dynamic -> LOGGER.debug("Top Dynamics = " + top5Dynamic));
|
|
||||||
|
|
||||||
BlockHeightRangeAddressAmounts amounts
|
|
||||||
= new BlockHeightRangeAddressAmounts( blockHeightRange, currentDynamics );
|
|
||||||
|
|
||||||
balanceDynamics.add(amounts);
|
|
||||||
|
|
||||||
BalanceRecorderUtils.removeRecordingsBelowHeight(currentHeight - Settings.getInstance().getBalanceRecorderRollbackAllowance(), balancesByHeight);
|
|
||||||
|
|
||||||
while(balanceDynamics.size() > capacity) {
|
|
||||||
BlockHeightRangeAddressAmounts oldestDynamics = BalanceRecorderUtils.removeOldestDynamics(balanceDynamics);
|
|
||||||
|
|
||||||
LOGGER.debug("removing oldest dynamics: range " + oldestDynamics.getRange());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -482,6 +460,69 @@ public class HSQLDBCacheUtils {
|
|||||||
timer.scheduleAtFixedRate(task, 300_000, frequency * 60_000);
|
timer.scheduleAtFixedRate(task, 300_000, frequency * 60_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void produceBalanceDynamics(int currentHeight, Optional<Integer> priorHeight, boolean isRewardDistribution, ConcurrentHashMap<Integer, List<AccountBalanceData>> balancesByHeight, CopyOnWriteArrayList<BlockHeightRangeAddressAmounts> balanceDynamics, int capacity) {
|
||||||
|
BlockHeightRange blockHeightRange = new BlockHeightRange(priorHeight.get(), currentHeight, isRewardDistribution);
|
||||||
|
|
||||||
|
LOGGER.debug("building dynamics for block heights: range = " + blockHeightRange);
|
||||||
|
|
||||||
|
List<AccountBalanceData> currentBalances = balancesByHeight.get(currentHeight);
|
||||||
|
|
||||||
|
ArrayList<TransactionData> transactions = getTransactionDataForBlocks(blockHeightRange);
|
||||||
|
|
||||||
|
LOGGER.info("transactions counted for balance adjustments: count = " + transactions.size());
|
||||||
|
List<AddressAmountData> currentDynamics
|
||||||
|
= BalanceRecorderUtils.buildBalanceDynamics(
|
||||||
|
currentBalances,
|
||||||
|
balancesByHeight.get(priorHeight.get()),
|
||||||
|
Settings.getInstance().getMinimumBalanceRecording(),
|
||||||
|
transactions);
|
||||||
|
|
||||||
|
LOGGER.debug("dynamics built: count = " + currentDynamics.size());
|
||||||
|
|
||||||
|
if(LOGGER.isDebugEnabled())
|
||||||
|
currentDynamics.stream()
|
||||||
|
.sorted(Comparator.comparingLong(AddressAmountData::getAmount).reversed())
|
||||||
|
.limit(Settings.getInstance().getTopBalanceLoggingLimit())
|
||||||
|
.forEach(top5Dynamic -> LOGGER.debug("Top Dynamics = " + top5Dynamic));
|
||||||
|
|
||||||
|
BlockHeightRangeAddressAmounts amounts
|
||||||
|
= new BlockHeightRangeAddressAmounts( blockHeightRange, currentDynamics );
|
||||||
|
|
||||||
|
balanceDynamics.add(amounts);
|
||||||
|
|
||||||
|
BalanceRecorderUtils.removeRecordingsBelowHeight(currentHeight - Settings.getInstance().getBalanceRecorderRollbackAllowance(), balancesByHeight);
|
||||||
|
|
||||||
|
while(balanceDynamics.size() > capacity) {
|
||||||
|
BlockHeightRangeAddressAmounts oldestDynamics = BalanceRecorderUtils.removeOldestDynamics(balanceDynamics);
|
||||||
|
|
||||||
|
LOGGER.debug("removing oldest dynamics: range " + oldestDynamics.getRange());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ArrayList<TransactionData> getTransactionDataForBlocks(BlockHeightRange blockHeightRange) {
|
||||||
|
ArrayList<TransactionData> transactions;
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
List<byte[]> signatures
|
||||||
|
= repository.getTransactionRepository().getSignaturesMatchingCriteria(
|
||||||
|
blockHeightRange.getBegin() + 1, blockHeightRange.getEnd() - blockHeightRange.getBegin(),
|
||||||
|
null, null,null, null, null,
|
||||||
|
TransactionsResource.ConfirmationStatus.CONFIRMED,
|
||||||
|
null, null, null);
|
||||||
|
|
||||||
|
transactions = new ArrayList<>(signatures.size());
|
||||||
|
for (byte[] signature : signatures) {
|
||||||
|
transactions.add(repository.getTransactionRepository().fromSignature(signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.debug(String.format("Found %s transactions for " + blockHeightRange, transactions.size()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
transactions = new ArrayList<>(0);
|
||||||
|
LOGGER.warn("Problems getting transactions for balance recording: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return transactions;
|
||||||
|
}
|
||||||
|
|
||||||
private static int recordCurrentBalances(ConcurrentHashMap<Integer, List<AccountBalanceData>> balancesByHeight) {
|
private static int recordCurrentBalances(ConcurrentHashMap<Integer, List<AccountBalanceData>> balancesByHeight) {
|
||||||
int currentHeight;
|
int currentHeight;
|
||||||
|
|
||||||
|
@ -494,7 +494,14 @@ public class Settings {
|
|||||||
*/
|
*/
|
||||||
private int balanceRecorderRollbackAllowance = 100;
|
private int balanceRecorderRollbackAllowance = 100;
|
||||||
|
|
||||||
// Domain mapping
|
/**
|
||||||
|
* Is Reward Recording Only
|
||||||
|
*
|
||||||
|
* Set true to only retain the recordings that cover reward distributions, otherwise set false.
|
||||||
|
*/
|
||||||
|
private boolean rewardRecordingOnly = true;
|
||||||
|
|
||||||
|
// Domain mapping
|
||||||
public static class ThreadLimit {
|
public static class ThreadLimit {
|
||||||
private String messageType;
|
private String messageType;
|
||||||
private Integer limit;
|
private Integer limit;
|
||||||
@ -1311,4 +1318,8 @@ public class Settings {
|
|||||||
public int getBalanceRecorderRollbackAllowance() {
|
public int getBalanceRecorderRollbackAllowance() {
|
||||||
return balanceRecorderRollbackAllowance;
|
return balanceRecorderRollbackAllowance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRewardRecordingOnly() {
|
||||||
|
return rewardRecordingOnly;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,26 @@
|
|||||||
package org.qortal.utils;
|
package org.qortal.utils;
|
||||||
|
|
||||||
|
import org.qortal.block.Block;
|
||||||
|
import org.qortal.crypto.Crypto;
|
||||||
|
import org.qortal.data.PaymentData;
|
||||||
import org.qortal.data.account.AccountBalanceData;
|
import org.qortal.data.account.AccountBalanceData;
|
||||||
import org.qortal.data.account.AddressAmountData;
|
import org.qortal.data.account.AddressAmountData;
|
||||||
import org.qortal.data.account.BlockHeightRange;
|
import org.qortal.data.account.BlockHeightRange;
|
||||||
import org.qortal.data.account.BlockHeightRangeAddressAmounts;
|
import org.qortal.data.account.BlockHeightRangeAddressAmounts;
|
||||||
|
import org.qortal.data.transaction.ATTransactionData;
|
||||||
|
import org.qortal.data.transaction.BaseTransactionData;
|
||||||
|
import org.qortal.data.transaction.BuyNameTransactionData;
|
||||||
|
import org.qortal.data.transaction.CreateAssetOrderTransactionData;
|
||||||
|
import org.qortal.data.transaction.DeployAtTransactionData;
|
||||||
|
import org.qortal.data.transaction.MultiPaymentTransactionData;
|
||||||
|
import org.qortal.data.transaction.PaymentTransactionData;
|
||||||
|
import org.qortal.data.transaction.TransactionData;
|
||||||
|
import org.qortal.data.transaction.TransferAssetTransactionData;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
@ -67,22 +80,190 @@ public class BalanceRecorderUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<AddressAmountData> buildBalanceDynamics(final List<AccountBalanceData> balances, final List<AccountBalanceData> priorBalances, long minimum) {
|
public static List<AddressAmountData> buildBalanceDynamics(
|
||||||
|
final List<AccountBalanceData> balances,
|
||||||
|
final List<AccountBalanceData> priorBalances,
|
||||||
|
long minimum,
|
||||||
|
List<TransactionData> transactions) {
|
||||||
|
|
||||||
List<AddressAmountData> addressAmounts = new ArrayList<>(balances.size());
|
Map<String, Long> amountsByAddress = new HashMap<>(transactions.size());
|
||||||
|
|
||||||
// prior balance
|
for( TransactionData transactionData : transactions ) {
|
||||||
addressAmounts.addAll(
|
|
||||||
balances.stream()
|
mapBalanceModificationsForTransaction(amountsByAddress, transactionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AddressAmountData> addressAmounts
|
||||||
|
= balances.stream()
|
||||||
.map(balance -> buildBalanceDynamicsForAccount(priorBalances, balance))
|
.map(balance -> buildBalanceDynamicsForAccount(priorBalances, balance))
|
||||||
|
.map( data -> adjustAddressAmount(amountsByAddress.getOrDefault(data.getAddress(), 0L), data))
|
||||||
.filter(ADDRESS_AMOUNT_DATA_NOT_ZERO)
|
.filter(ADDRESS_AMOUNT_DATA_NOT_ZERO)
|
||||||
.filter( data -> data.getAmount() >= minimum)
|
.filter(data -> data.getAmount() >= minimum)
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList());
|
||||||
);
|
|
||||||
|
|
||||||
return addressAmounts;
|
return addressAmounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AddressAmountData adjustAddressAmount(long adjustment, AddressAmountData data) {
|
||||||
|
|
||||||
|
return new AddressAmountData(data.getAddress(), data.getAmount() - adjustment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void mapBalanceModificationsForTransaction(Map<String, Long> amountsByAddress, TransactionData transactionData) {
|
||||||
|
String creatorAddress;
|
||||||
|
|
||||||
|
// AT Transaction
|
||||||
|
if( transactionData instanceof ATTransactionData) {
|
||||||
|
creatorAddress = mapBalanceModificationsForAtTransaction(amountsByAddress, (ATTransactionData) transactionData);
|
||||||
|
}
|
||||||
|
// Buy Name Transaction
|
||||||
|
else if( transactionData instanceof BuyNameTransactionData) {
|
||||||
|
creatorAddress = mapBalanceModificationsForBuyNameTransaction(amountsByAddress, (BuyNameTransactionData) transactionData);
|
||||||
|
}
|
||||||
|
// Create Asset Order Transaction
|
||||||
|
else if( transactionData instanceof CreateAssetOrderTransactionData) {
|
||||||
|
//TODO I'm not sure how to handle this one. This hasn't been used at this point in the blockchain.
|
||||||
|
|
||||||
|
creatorAddress = Crypto.toAddress(transactionData.getCreatorPublicKey());
|
||||||
|
}
|
||||||
|
// Deploy AT Transaction
|
||||||
|
else if( transactionData instanceof DeployAtTransactionData ) {
|
||||||
|
creatorAddress = mapBalanceModificationsForDeployAtTransaction(amountsByAddress, (DeployAtTransactionData) transactionData);
|
||||||
|
}
|
||||||
|
// Multi Payment Transaction
|
||||||
|
else if( transactionData instanceof MultiPaymentTransactionData) {
|
||||||
|
creatorAddress = mapBalanceModificationsForMultiPaymentTransaction(amountsByAddress, (MultiPaymentTransactionData) transactionData);
|
||||||
|
}
|
||||||
|
// Payment Transaction
|
||||||
|
else if( transactionData instanceof PaymentTransactionData ) {
|
||||||
|
creatorAddress = mapBalanceModicationsForPaymentTransaction(amountsByAddress, (PaymentTransactionData) transactionData);
|
||||||
|
}
|
||||||
|
// Transfer Asset Transaction
|
||||||
|
else if( transactionData instanceof TransferAssetTransactionData) {
|
||||||
|
creatorAddress = mapBalanceModificationsForTransferAssetTransaction(amountsByAddress, (TransferAssetTransactionData) transactionData);
|
||||||
|
}
|
||||||
|
// Other Transactions
|
||||||
|
else {
|
||||||
|
creatorAddress = Crypto.toAddress(transactionData.getCreatorPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
// all transactions modify the balance for fees
|
||||||
|
mapBalanceModifications(amountsByAddress, transactionData.getFee(), creatorAddress, Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String mapBalanceModificationsForTransferAssetTransaction(Map<String, Long> amountsByAddress, TransferAssetTransactionData transferAssetData) {
|
||||||
|
String creatorAddress = Crypto.toAddress(transferAssetData.getSenderPublicKey());
|
||||||
|
|
||||||
|
if( transferAssetData.getAssetId() == 0) {
|
||||||
|
mapBalanceModifications(
|
||||||
|
amountsByAddress,
|
||||||
|
transferAssetData.getAmount(),
|
||||||
|
creatorAddress,
|
||||||
|
Optional.of(transferAssetData.getRecipient())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return creatorAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String mapBalanceModicationsForPaymentTransaction(Map<String, Long> amountsByAddress, PaymentTransactionData paymentData) {
|
||||||
|
String creatorAddress = Crypto.toAddress(paymentData.getCreatorPublicKey());
|
||||||
|
|
||||||
|
mapBalanceModifications(amountsByAddress,
|
||||||
|
paymentData.getAmount(),
|
||||||
|
creatorAddress,
|
||||||
|
Optional.of(paymentData.getRecipient())
|
||||||
|
);
|
||||||
|
return creatorAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String mapBalanceModificationsForMultiPaymentTransaction(Map<String, Long> amountsByAddress, MultiPaymentTransactionData multiPaymentData) {
|
||||||
|
String creatorAddress = Crypto.toAddress(multiPaymentData.getCreatorPublicKey());
|
||||||
|
|
||||||
|
for(PaymentData payment : multiPaymentData.getPayments() ) {
|
||||||
|
mapBalanceModificationsForTransaction(
|
||||||
|
amountsByAddress,
|
||||||
|
getPaymentTransactionData(multiPaymentData, payment)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return creatorAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String mapBalanceModificationsForDeployAtTransaction(Map<String, Long> amountsByAddress, DeployAtTransactionData transactionData) {
|
||||||
|
String creatorAddress;
|
||||||
|
DeployAtTransactionData deployAtData = transactionData;
|
||||||
|
|
||||||
|
creatorAddress = Crypto.toAddress(deployAtData.getCreatorPublicKey());
|
||||||
|
|
||||||
|
if( deployAtData.getAssetId() == 0 ) {
|
||||||
|
mapBalanceModifications(
|
||||||
|
amountsByAddress,
|
||||||
|
deployAtData.getAmount(),
|
||||||
|
creatorAddress,
|
||||||
|
Optional.of(deployAtData.getAtAddress())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return creatorAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String mapBalanceModificationsForBuyNameTransaction(Map<String, Long> amountsByAddress, BuyNameTransactionData transactionData) {
|
||||||
|
String creatorAddress;
|
||||||
|
BuyNameTransactionData buyNameData = transactionData;
|
||||||
|
|
||||||
|
creatorAddress = Crypto.toAddress(buyNameData.getCreatorPublicKey());
|
||||||
|
|
||||||
|
mapBalanceModifications(
|
||||||
|
amountsByAddress,
|
||||||
|
buyNameData.getAmount(),
|
||||||
|
creatorAddress,
|
||||||
|
Optional.of(buyNameData.getSeller())
|
||||||
|
);
|
||||||
|
return creatorAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String mapBalanceModificationsForAtTransaction(Map<String, Long> amountsByAddress, ATTransactionData transactionData) {
|
||||||
|
String creatorAddress;
|
||||||
|
ATTransactionData atData = transactionData;
|
||||||
|
creatorAddress = atData.getATAddress();
|
||||||
|
|
||||||
|
if( atData.getAssetId() != null && atData.getAssetId() == 0) {
|
||||||
|
mapBalanceModifications(
|
||||||
|
amountsByAddress,
|
||||||
|
atData.getAmount(),
|
||||||
|
creatorAddress,
|
||||||
|
Optional.of(atData.getRecipient())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return creatorAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PaymentTransactionData getPaymentTransactionData(MultiPaymentTransactionData multiPaymentData, PaymentData payment) {
|
||||||
|
return new PaymentTransactionData(
|
||||||
|
new BaseTransactionData(
|
||||||
|
multiPaymentData.getTimestamp(),
|
||||||
|
multiPaymentData.getTxGroupId(),
|
||||||
|
multiPaymentData.getReference(),
|
||||||
|
multiPaymentData.getCreatorPublicKey(),
|
||||||
|
0L,
|
||||||
|
multiPaymentData.getSignature()
|
||||||
|
),
|
||||||
|
payment.getRecipient(),
|
||||||
|
payment.getAmount()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void mapBalanceModifications(Map<String, Long> amountsByAddress, Long amount, String sender, Optional<String> recipient) {
|
||||||
|
amountsByAddress.put(
|
||||||
|
sender,
|
||||||
|
amountsByAddress.getOrDefault(sender, 0L) - amount
|
||||||
|
);
|
||||||
|
|
||||||
|
if( recipient.isPresent() )
|
||||||
|
amountsByAddress.put(
|
||||||
|
recipient.get(),
|
||||||
|
amountsByAddress.getOrDefault(recipient.get(), 0L) + amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static void removeRecordingsAboveHeight(int currentHeight, ConcurrentHashMap<Integer, List<AccountBalanceData>> balancesByHeight) {
|
public static void removeRecordingsAboveHeight(int currentHeight, ConcurrentHashMap<Integer, List<AccountBalanceData>> balancesByHeight) {
|
||||||
balancesByHeight.entrySet().stream()
|
balancesByHeight.entrySet().stream()
|
||||||
.filter(heightWithBalances -> heightWithBalances.getKey() > currentHeight)
|
.filter(heightWithBalances -> heightWithBalances.getKey() > currentHeight)
|
||||||
@ -116,4 +297,23 @@ public class BalanceRecorderUtils {
|
|||||||
.sorted(Comparator.reverseOrder()).findFirst();
|
.sorted(Comparator.reverseOrder()).findFirst();
|
||||||
return priorHeight;
|
return priorHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is Reward Distribution Range?
|
||||||
|
*
|
||||||
|
* @param start start height, exclusive
|
||||||
|
* @param end end height, inclusive
|
||||||
|
*
|
||||||
|
* @return true there is a reward distribution block within this block range
|
||||||
|
*/
|
||||||
|
public static boolean isRewardDistributionRange(int start, int end) {
|
||||||
|
|
||||||
|
// iterate through the block height until a reward distribution block or the end of the range
|
||||||
|
for( int i = start + 1; i <= end; i++) {
|
||||||
|
if( Block.isRewardDistributionBlock(i) ) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no reward distribution blocks found within range
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,27 @@ package org.qortal.test.utils;
|
|||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.qortal.asset.Asset;
|
||||||
|
import org.qortal.block.Block;
|
||||||
|
import org.qortal.crypto.Crypto;
|
||||||
|
import org.qortal.data.PaymentData;
|
||||||
import org.qortal.data.account.AccountBalanceData;
|
import org.qortal.data.account.AccountBalanceData;
|
||||||
import org.qortal.data.account.AddressAmountData;
|
import org.qortal.data.account.AddressAmountData;
|
||||||
import org.qortal.data.account.BlockHeightRange;
|
import org.qortal.data.account.BlockHeightRange;
|
||||||
import org.qortal.data.account.BlockHeightRangeAddressAmounts;
|
import org.qortal.data.account.BlockHeightRangeAddressAmounts;
|
||||||
|
import org.qortal.data.transaction.ATTransactionData;
|
||||||
|
import org.qortal.data.transaction.BaseTransactionData;
|
||||||
|
import org.qortal.data.transaction.BuyNameTransactionData;
|
||||||
|
import org.qortal.data.transaction.DeployAtTransactionData;
|
||||||
|
import org.qortal.data.transaction.MultiPaymentTransactionData;
|
||||||
|
import org.qortal.data.transaction.PaymentTransactionData;
|
||||||
|
import org.qortal.data.transaction.RegisterNameTransactionData;
|
||||||
|
import org.qortal.data.transaction.TransactionData;
|
||||||
|
import org.qortal.data.transaction.TransferAssetTransactionData;
|
||||||
import org.qortal.utils.BalanceRecorderUtils;
|
import org.qortal.utils.BalanceRecorderUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -18,6 +32,10 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
public class BalanceRecorderUtilsTests {
|
public class BalanceRecorderUtilsTests {
|
||||||
|
|
||||||
|
public static final String RECIPIENT_ADDRESS = "recipient";
|
||||||
|
public static final String AT_ADDRESS = "atAddress";
|
||||||
|
public static final String OTHER = "Other";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotZeroForZero() {
|
public void testNotZeroForZero() {
|
||||||
boolean test = BalanceRecorderUtils.ADDRESS_AMOUNT_DATA_NOT_ZERO.test( new AddressAmountData("", 0));
|
boolean test = BalanceRecorderUtils.ADDRESS_AMOUNT_DATA_NOT_ZERO.test( new AddressAmountData("", 0));
|
||||||
@ -42,8 +60,8 @@ public class BalanceRecorderUtilsTests {
|
|||||||
@Test
|
@Test
|
||||||
public void testAddressAmountComparatorReverseOrder() {
|
public void testAddressAmountComparatorReverseOrder() {
|
||||||
|
|
||||||
BlockHeightRangeAddressAmounts addressAmounts1 = new BlockHeightRangeAddressAmounts(new BlockHeightRange(2, 3), new ArrayList<>(0));
|
BlockHeightRangeAddressAmounts addressAmounts1 = new BlockHeightRangeAddressAmounts(new BlockHeightRange(2, 3, false), new ArrayList<>(0));
|
||||||
BlockHeightRangeAddressAmounts addressAmounts2 = new BlockHeightRangeAddressAmounts(new BlockHeightRange(1, 2), new ArrayList<>(0));
|
BlockHeightRangeAddressAmounts addressAmounts2 = new BlockHeightRangeAddressAmounts(new BlockHeightRange(1, 2, false), new ArrayList<>(0));
|
||||||
|
|
||||||
int compare = BalanceRecorderUtils.BLOCK_HEIGHT_RANGE_ADDRESS_AMOUNTS_COMPARATOR.compare(addressAmounts1, addressAmounts2);
|
int compare = BalanceRecorderUtils.BLOCK_HEIGHT_RANGE_ADDRESS_AMOUNTS_COMPARATOR.compare(addressAmounts1, addressAmounts2);
|
||||||
|
|
||||||
@ -53,8 +71,8 @@ public class BalanceRecorderUtilsTests {
|
|||||||
@Test
|
@Test
|
||||||
public void testAddressAmountComparatorForwardOrder() {
|
public void testAddressAmountComparatorForwardOrder() {
|
||||||
|
|
||||||
BlockHeightRangeAddressAmounts addressAmounts1 = new BlockHeightRangeAddressAmounts(new BlockHeightRange(1, 2), new ArrayList<>(0));
|
BlockHeightRangeAddressAmounts addressAmounts1 = new BlockHeightRangeAddressAmounts(new BlockHeightRange(1, 2, false), new ArrayList<>(0));
|
||||||
BlockHeightRangeAddressAmounts addressAmounts2 = new BlockHeightRangeAddressAmounts(new BlockHeightRange(2, 3), new ArrayList<>(0));
|
BlockHeightRangeAddressAmounts addressAmounts2 = new BlockHeightRangeAddressAmounts(new BlockHeightRange(2, 3, false), new ArrayList<>(0));
|
||||||
|
|
||||||
int compare = BalanceRecorderUtils.BLOCK_HEIGHT_RANGE_ADDRESS_AMOUNTS_COMPARATOR.compare(addressAmounts1, addressAmounts2);
|
int compare = BalanceRecorderUtils.BLOCK_HEIGHT_RANGE_ADDRESS_AMOUNTS_COMPARATOR.compare(addressAmounts1, addressAmounts2);
|
||||||
|
|
||||||
@ -124,7 +142,7 @@ public class BalanceRecorderUtilsTests {
|
|||||||
List<AccountBalanceData> priorBalances = new ArrayList<>(1);
|
List<AccountBalanceData> priorBalances = new ArrayList<>(1);
|
||||||
priorBalances.add(new AccountBalanceData(address, 0, 1));
|
priorBalances.add(new AccountBalanceData(address, 0, 1));
|
||||||
|
|
||||||
List<AddressAmountData> dynamics = BalanceRecorderUtils.buildBalanceDynamics(balances, priorBalances, 0);
|
List<AddressAmountData> dynamics = BalanceRecorderUtils.buildBalanceDynamics(balances, priorBalances, 0, new ArrayList<>(0));
|
||||||
|
|
||||||
Assert.assertNotNull(dynamics);
|
Assert.assertNotNull(dynamics);
|
||||||
Assert.assertEquals(1, dynamics.size());
|
Assert.assertEquals(1, dynamics.size());
|
||||||
@ -145,7 +163,7 @@ public class BalanceRecorderUtilsTests {
|
|||||||
|
|
||||||
List<AccountBalanceData> priorBalances = new ArrayList<>(0);
|
List<AccountBalanceData> priorBalances = new ArrayList<>(0);
|
||||||
|
|
||||||
List<AddressAmountData> dynamics = BalanceRecorderUtils.buildBalanceDynamics(balances, priorBalances, 0);
|
List<AddressAmountData> dynamics = BalanceRecorderUtils.buildBalanceDynamics(balances, priorBalances, 0, new ArrayList<>(0));
|
||||||
|
|
||||||
Assert.assertNotNull(dynamics);
|
Assert.assertNotNull(dynamics);
|
||||||
Assert.assertEquals(1, dynamics.size());
|
Assert.assertEquals(1, dynamics.size());
|
||||||
@ -156,6 +174,55 @@ public class BalanceRecorderUtilsTests {
|
|||||||
Assert.assertEquals(2, addressAmountData.getAmount());
|
Assert.assertEquals(2, addressAmountData.getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildBalanceDynamicOneAccountAdjustment() {
|
||||||
|
List<AccountBalanceData> balances = new ArrayList<>(1);
|
||||||
|
balances.add(new AccountBalanceData(RECIPIENT_ADDRESS, 0, 20));
|
||||||
|
|
||||||
|
List<AccountBalanceData> priorBalances = new ArrayList<>(0);
|
||||||
|
priorBalances.add(new AccountBalanceData(RECIPIENT_ADDRESS, 0, 12));
|
||||||
|
|
||||||
|
List<TransactionData> transactions = new ArrayList<>();
|
||||||
|
|
||||||
|
final long amount = 5L;
|
||||||
|
final long fee = 1L;
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
|
||||||
|
PaymentTransactionData paymentData
|
||||||
|
= new PaymentTransactionData(
|
||||||
|
new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
RECIPIENT_ADDRESS,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
|
||||||
|
transactions.add(paymentData);
|
||||||
|
|
||||||
|
List<AddressAmountData> dynamics
|
||||||
|
= BalanceRecorderUtils.buildBalanceDynamics(
|
||||||
|
balances,
|
||||||
|
priorBalances,
|
||||||
|
0,
|
||||||
|
transactions
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.assertNotNull(dynamics);
|
||||||
|
Assert.assertEquals(1, dynamics.size());
|
||||||
|
|
||||||
|
AddressAmountData addressAmountData = dynamics.get(0);
|
||||||
|
Assert.assertNotNull(addressAmountData);
|
||||||
|
Assert.assertEquals(RECIPIENT_ADDRESS, addressAmountData.getAddress());
|
||||||
|
Assert.assertEquals(3, addressAmountData.getAmount());
|
||||||
|
} catch( Exception e ) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBuildBalanceDynamicsTwoAccountsNegativeValues() {
|
public void testBuildBalanceDynamicsTwoAccountsNegativeValues() {
|
||||||
|
|
||||||
@ -170,7 +237,7 @@ public class BalanceRecorderUtilsTests {
|
|||||||
priorBalances.add(new AccountBalanceData(address2, 0, 200));
|
priorBalances.add(new AccountBalanceData(address2, 0, 200));
|
||||||
priorBalances.add(new AccountBalanceData(address1, 0, 5000));
|
priorBalances.add(new AccountBalanceData(address1, 0, 5000));
|
||||||
|
|
||||||
List<AddressAmountData> dynamics = BalanceRecorderUtils.buildBalanceDynamics(balances, priorBalances, -100L);
|
List<AddressAmountData> dynamics = BalanceRecorderUtils.buildBalanceDynamics(balances, priorBalances, -100L, new ArrayList<>(0));
|
||||||
|
|
||||||
Assert.assertNotNull(dynamics);
|
Assert.assertNotNull(dynamics);
|
||||||
Assert.assertEquals(2, dynamics.size());
|
Assert.assertEquals(2, dynamics.size());
|
||||||
@ -304,10 +371,10 @@ public class BalanceRecorderUtilsTests {
|
|||||||
|
|
||||||
CopyOnWriteArrayList<BlockHeightRangeAddressAmounts> dynamics = new CopyOnWriteArrayList<>();
|
CopyOnWriteArrayList<BlockHeightRangeAddressAmounts> dynamics = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
BlockHeightRange range1 = new BlockHeightRange(10, 20);
|
BlockHeightRange range1 = new BlockHeightRange(10, 20, false);
|
||||||
dynamics.add(new BlockHeightRangeAddressAmounts(range1, new ArrayList<>()));
|
dynamics.add(new BlockHeightRangeAddressAmounts(range1, new ArrayList<>()));
|
||||||
|
|
||||||
BlockHeightRange range2 = new BlockHeightRange(1, 4);
|
BlockHeightRange range2 = new BlockHeightRange(1, 4, false);
|
||||||
dynamics.add(new BlockHeightRangeAddressAmounts(range2, new ArrayList<>()));
|
dynamics.add(new BlockHeightRangeAddressAmounts(range2, new ArrayList<>()));
|
||||||
|
|
||||||
Assert.assertEquals(2, dynamics.size());
|
Assert.assertEquals(2, dynamics.size());
|
||||||
@ -323,13 +390,13 @@ public class BalanceRecorderUtilsTests {
|
|||||||
|
|
||||||
CopyOnWriteArrayList<BlockHeightRangeAddressAmounts> dynamics = new CopyOnWriteArrayList<>();
|
CopyOnWriteArrayList<BlockHeightRangeAddressAmounts> dynamics = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
BlockHeightRange range1 = new BlockHeightRange(1,5);
|
BlockHeightRange range1 = new BlockHeightRange(1,5, false);
|
||||||
dynamics.add(new BlockHeightRangeAddressAmounts(range1, new ArrayList<>()));
|
dynamics.add(new BlockHeightRangeAddressAmounts(range1, new ArrayList<>()));
|
||||||
|
|
||||||
BlockHeightRange range2 = new BlockHeightRange(6, 11);
|
BlockHeightRange range2 = new BlockHeightRange(6, 11, false);
|
||||||
dynamics.add((new BlockHeightRangeAddressAmounts(range2, new ArrayList<>())));
|
dynamics.add((new BlockHeightRangeAddressAmounts(range2, new ArrayList<>())));
|
||||||
|
|
||||||
BlockHeightRange range3 = new BlockHeightRange(22, 16);
|
BlockHeightRange range3 = new BlockHeightRange(22, 16, false);
|
||||||
dynamics.add(new BlockHeightRangeAddressAmounts(range3, new ArrayList<>()));
|
dynamics.add(new BlockHeightRangeAddressAmounts(range3, new ArrayList<>()));
|
||||||
|
|
||||||
Assert.assertEquals(3, dynamics.size());
|
Assert.assertEquals(3, dynamics.size());
|
||||||
@ -344,18 +411,353 @@ public class BalanceRecorderUtilsTests {
|
|||||||
public void testRemoveOldestDynamicsTwice() {
|
public void testRemoveOldestDynamicsTwice() {
|
||||||
CopyOnWriteArrayList<BlockHeightRangeAddressAmounts> dynamics = new CopyOnWriteArrayList<>();
|
CopyOnWriteArrayList<BlockHeightRangeAddressAmounts> dynamics = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
dynamics.add(new BlockHeightRangeAddressAmounts(new BlockHeightRange(1, 5), new ArrayList<>()));
|
dynamics.add(new BlockHeightRangeAddressAmounts(new BlockHeightRange(1, 5, false), new ArrayList<>()));
|
||||||
dynamics.add(new BlockHeightRangeAddressAmounts(new BlockHeightRange(5, 9), new ArrayList<>()));
|
dynamics.add(new BlockHeightRangeAddressAmounts(new BlockHeightRange(5, 9, false), new ArrayList<>()));
|
||||||
|
|
||||||
Assert.assertEquals(2, dynamics.size());
|
Assert.assertEquals(2, dynamics.size());
|
||||||
|
|
||||||
BalanceRecorderUtils.removeOldestDynamics(dynamics);
|
BalanceRecorderUtils.removeOldestDynamics(dynamics);
|
||||||
|
|
||||||
Assert.assertEquals(1, dynamics.size());
|
Assert.assertEquals(1, dynamics.size());
|
||||||
Assert.assertTrue(dynamics.get(0).getRange().equals(new BlockHeightRange(5, 9)));
|
Assert.assertTrue(dynamics.get(0).getRange().equals(new BlockHeightRange(5, 9, false)));
|
||||||
|
|
||||||
BalanceRecorderUtils.removeOldestDynamics(dynamics);
|
BalanceRecorderUtils.removeOldestDynamics(dynamics);
|
||||||
|
|
||||||
Assert.assertEquals(0, dynamics.size());
|
Assert.assertEquals(0, dynamics.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapBalanceModificationsForPaymentTransaction() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final long amount = 1L;
|
||||||
|
final long fee = 1L;
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
|
||||||
|
PaymentTransactionData paymentData
|
||||||
|
= new PaymentTransactionData(
|
||||||
|
new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
RECIPIENT_ADDRESS,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
|
||||||
|
// map balance modifications for addresses in the transaction
|
||||||
|
Map<String, Long> amountsByAddress = new HashMap<>();
|
||||||
|
BalanceRecorderUtils.mapBalanceModicationsForPaymentTransaction(amountsByAddress, paymentData);
|
||||||
|
|
||||||
|
// this will not add the fee, that is done in a different place
|
||||||
|
assertAmountsByAddress(amountsByAddress, amount, creatorPublicKey, RECIPIENT_ADDRESS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapBalanceModificationsForAssetOrderTransaction() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try{
|
||||||
|
final long amount = 1L;
|
||||||
|
final long fee = 1L;
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
|
||||||
|
TransferAssetTransactionData transferAssetData
|
||||||
|
= new TransferAssetTransactionData(
|
||||||
|
new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
RECIPIENT_ADDRESS,
|
||||||
|
amount,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
// map balance modifications for addresses in the transaction
|
||||||
|
Map<String, Long> amountsByAddress = new HashMap<>();
|
||||||
|
BalanceRecorderUtils.mapBalanceModificationsForTransferAssetTransaction(amountsByAddress, transferAssetData);
|
||||||
|
|
||||||
|
assertAmountsByAddress(amountsByAddress, amount, creatorPublicKey, RECIPIENT_ADDRESS);
|
||||||
|
} catch( Exception e) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapBalanceModificationsForATTransactionMessageType() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
final long fee = 1L;
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
Map<String, Long> amountsByAddress = new HashMap<>();
|
||||||
|
|
||||||
|
ATTransactionData atTransactionData = new ATTransactionData(new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
AT_ADDRESS,
|
||||||
|
RECIPIENT_ADDRESS,
|
||||||
|
new byte[0]);
|
||||||
|
BalanceRecorderUtils.mapBalanceModificationsForAtTransaction( amountsByAddress, atTransactionData);
|
||||||
|
|
||||||
|
// no balance changes for AT message
|
||||||
|
Assert.assertTrue(amountsByAddress.size() == 0);
|
||||||
|
} catch( Exception e) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapBalanceModificationsForATTransactionPaymentType() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try{
|
||||||
|
final long amount = 1L;
|
||||||
|
final long fee = 1L;
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
|
||||||
|
Map<String, Long> amountsByAddress = new HashMap<>();
|
||||||
|
|
||||||
|
ATTransactionData atTransactionData
|
||||||
|
= new ATTransactionData(
|
||||||
|
new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
AT_ADDRESS,
|
||||||
|
RECIPIENT_ADDRESS,
|
||||||
|
amount,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
BalanceRecorderUtils.mapBalanceModificationsForAtTransaction( amountsByAddress, atTransactionData);
|
||||||
|
|
||||||
|
assertAmountByAddress(amountsByAddress, amount, RECIPIENT_ADDRESS);
|
||||||
|
|
||||||
|
assertAmountByAddress(amountsByAddress, -amount, AT_ADDRESS);
|
||||||
|
} catch( Exception e) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapBalanceModificationsForBuyNameTransaction() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try{
|
||||||
|
final long amount = 100L;
|
||||||
|
final long fee = 1L;
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
Map<String, Long> amountsByAddress = new HashMap<>();
|
||||||
|
|
||||||
|
BuyNameTransactionData buyNameData
|
||||||
|
= new BuyNameTransactionData(
|
||||||
|
new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
"null",
|
||||||
|
amount,
|
||||||
|
RECIPIENT_ADDRESS
|
||||||
|
);
|
||||||
|
|
||||||
|
BalanceRecorderUtils.mapBalanceModificationsForBuyNameTransaction(amountsByAddress, buyNameData);
|
||||||
|
|
||||||
|
assertAmountsByAddress(amountsByAddress, amount, creatorPublicKey, RECIPIENT_ADDRESS);
|
||||||
|
} catch( Exception e) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapBalanceModificationsForMultiPaymentTransaction() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try{
|
||||||
|
final long amount = 100L;
|
||||||
|
final long fee = 1L;
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
Map<String, Long> amountsByAddress = new HashMap<>();
|
||||||
|
|
||||||
|
List<PaymentData> payments = new ArrayList<>();
|
||||||
|
|
||||||
|
payments.add(new PaymentData(RECIPIENT_ADDRESS, 0, amount));
|
||||||
|
|
||||||
|
MultiPaymentTransactionData multiPayment
|
||||||
|
= new MultiPaymentTransactionData(new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
payments);
|
||||||
|
BalanceRecorderUtils.mapBalanceModificationsForMultiPaymentTransaction(amountsByAddress,multiPayment);
|
||||||
|
assertAmountsByAddress(amountsByAddress, amount, creatorPublicKey, RECIPIENT_ADDRESS);
|
||||||
|
} catch( Exception e ) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapBalanceModificationsForMultiPaymentTransaction2PaymentsOneAddress() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try{
|
||||||
|
final long amount = 100L;
|
||||||
|
final long fee = 1L;
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
Map<String, Long> amountsByAddress = new HashMap<>();
|
||||||
|
|
||||||
|
List<PaymentData> payments = new ArrayList<>();
|
||||||
|
|
||||||
|
payments.add(new PaymentData(RECIPIENT_ADDRESS, 0, amount));
|
||||||
|
payments.add(new PaymentData(RECIPIENT_ADDRESS, 0, amount));
|
||||||
|
|
||||||
|
MultiPaymentTransactionData multiPayment
|
||||||
|
= new MultiPaymentTransactionData(new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
payments);
|
||||||
|
BalanceRecorderUtils.mapBalanceModificationsForMultiPaymentTransaction(amountsByAddress,multiPayment);
|
||||||
|
assertAmountsByAddress(amountsByAddress, 2*amount, creatorPublicKey, RECIPIENT_ADDRESS);
|
||||||
|
} catch( Exception e ) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapBalanceModificationsForMultiPaymentTransaction2PaymentsTwoAddresses() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try{
|
||||||
|
final long amount = 100L;
|
||||||
|
final long fee = 1L;
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
Map<String, Long> amountsByAddress = new HashMap<>();
|
||||||
|
|
||||||
|
List<PaymentData> payments = new ArrayList<>();
|
||||||
|
|
||||||
|
payments.add(new PaymentData(RECIPIENT_ADDRESS, 0, amount));
|
||||||
|
payments.add(new PaymentData(OTHER, 0, amount));
|
||||||
|
|
||||||
|
MultiPaymentTransactionData multiPayment
|
||||||
|
= new MultiPaymentTransactionData(new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
payments);
|
||||||
|
BalanceRecorderUtils.mapBalanceModificationsForMultiPaymentTransaction(amountsByAddress,multiPayment);
|
||||||
|
assertAmountByAddress(amountsByAddress, amount, RECIPIENT_ADDRESS);
|
||||||
|
assertAmountByAddress(amountsByAddress, amount, OTHER);
|
||||||
|
|
||||||
|
String creatorAddress = Crypto.toAddress(creatorPublicKey);
|
||||||
|
|
||||||
|
assertAmountByAddress(amountsByAddress, 2*-amount, creatorAddress);
|
||||||
|
} catch( Exception e ) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapBalanceModificationsForDeployAtTransaction() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try{
|
||||||
|
final long amount = 3L;
|
||||||
|
final long fee = 1L;
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
Map<String, Long> amountsByAddress = new HashMap<>();
|
||||||
|
|
||||||
|
DeployAtTransactionData deployAt
|
||||||
|
= new DeployAtTransactionData(
|
||||||
|
new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
AT_ADDRESS, "name", "description", "type", "tags", new byte[0], amount, Asset.QORT
|
||||||
|
);
|
||||||
|
|
||||||
|
BalanceRecorderUtils.mapBalanceModificationsForDeployAtTransaction(amountsByAddress,deployAt);
|
||||||
|
assertAmountsByAddress(amountsByAddress, amount, creatorPublicKey, AT_ADDRESS);
|
||||||
|
} catch( Exception e) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapBalanceModificationsForTransaction() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final long fee = 2;
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = TestUtils.generatePublicKey();
|
||||||
|
Map<String, Long> amountsByAddress = new HashMap<>();
|
||||||
|
|
||||||
|
BalanceRecorderUtils.mapBalanceModificationsForTransaction(
|
||||||
|
amountsByAddress,
|
||||||
|
new RegisterNameTransactionData(
|
||||||
|
new BaseTransactionData(0L, 0, null, creatorPublicKey, fee, null),
|
||||||
|
"aaa", "data", "aaa")
|
||||||
|
);
|
||||||
|
|
||||||
|
String creatorAddress = Crypto.toAddress(creatorPublicKey);
|
||||||
|
|
||||||
|
assertAmountByAddress(amountsByAddress, -fee, creatorAddress);
|
||||||
|
} catch(Exception e) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBlockHeightRangeEqualityTrue() {
|
||||||
|
|
||||||
|
BlockHeightRange range1 = new BlockHeightRange(2, 4, false);
|
||||||
|
BlockHeightRange range2 = new BlockHeightRange(2, 4, true);
|
||||||
|
|
||||||
|
Assert.assertTrue(range1.equals(range2));
|
||||||
|
Assert.assertEquals(range1, range2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBloHeightRangeEqualityFalse() {
|
||||||
|
|
||||||
|
BlockHeightRange range1 = new BlockHeightRange(2, 3, true);
|
||||||
|
BlockHeightRange range2 = new BlockHeightRange(2, 4, true);
|
||||||
|
|
||||||
|
Assert.assertFalse(range1.equals(range2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertAmountsByAddress(Map<String, Long> amountsByAddress, long amount, byte[] creatorPublicKey, String recipientAddress) {
|
||||||
|
assertAmountByAddress(amountsByAddress, amount, recipientAddress);
|
||||||
|
|
||||||
|
String creatorAddress = Crypto.toAddress(creatorPublicKey);
|
||||||
|
|
||||||
|
assertAmountByAddress(amountsByAddress, -amount, creatorAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertAmountByAddress(Map<String, Long> amountsByAddress, long amount, String address) {
|
||||||
|
Long amountForAddress = amountsByAddress.get(address);
|
||||||
|
|
||||||
|
Assert.assertTrue(amountsByAddress.containsKey(address));
|
||||||
|
Assert.assertNotNull(amountForAddress);
|
||||||
|
Assert.assertEquals(amount, amountForAddress.longValue());
|
||||||
|
}
|
||||||
}
|
}
|
48
src/test/java/org/qortal/test/utils/TestUtils.java
Normal file
48
src/test/java/org/qortal/test/utils/TestUtils.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package org.qortal.test.utils;
|
||||||
|
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.Security;
|
||||||
|
|
||||||
|
public class TestUtils {
|
||||||
|
public static byte[] generatePublicKey() throws Exception {
|
||||||
|
// Add the Bouncy Castle provider
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
|
||||||
|
// Generate a key pair
|
||||||
|
KeyPair keyPair = generateKeyPair();
|
||||||
|
|
||||||
|
// Get the public key
|
||||||
|
PublicKey publicKey = keyPair.getPublic();
|
||||||
|
|
||||||
|
// Get the public key as a byte array
|
||||||
|
byte[] publicKeyBytes = publicKey.getEncoded();
|
||||||
|
|
||||||
|
// Generate a RIPEMD160 message digest from the public key
|
||||||
|
byte[] ripeMd160Digest = generateRipeMd160Digest(publicKeyBytes);
|
||||||
|
|
||||||
|
return ripeMd160Digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KeyPair generateKeyPair() throws Exception {
|
||||||
|
// Generate a key pair using the RSA algorithm
|
||||||
|
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
||||||
|
keyGen.initialize(2048); // Key size (bits)
|
||||||
|
return keyGen.generateKeyPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] generateRipeMd160Digest(byte[] input) throws Exception {
|
||||||
|
// Create a RIPEMD160 message digest instance
|
||||||
|
MessageDigest ripeMd160 = MessageDigest.getInstance("RIPEMD160", new BouncyCastleProvider());
|
||||||
|
|
||||||
|
// Update the message digest with the input bytes
|
||||||
|
ripeMd160.update(input);
|
||||||
|
|
||||||
|
// Get the message digest bytes
|
||||||
|
return ripeMd160.digest();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user