diff --git a/src/main/java/org/qortal/api/RewardSharePercentTypeAdapter.java b/src/main/java/org/qortal/api/RewardSharePercentTypeAdapter.java new file mode 100644 index 00000000..1ad2034c --- /dev/null +++ b/src/main/java/org/qortal/api/RewardSharePercentTypeAdapter.java @@ -0,0 +1,25 @@ +package org.qortal.api; + +import java.math.BigDecimal; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +public class RewardSharePercentTypeAdapter extends XmlAdapter { + + @Override + public Integer unmarshal(String input) throws Exception { + if (input == null) + return null; + + return new BigDecimal(input).setScale(2).unscaledValue().intValue(); + } + + @Override + public String marshal(Integer output) throws Exception { + if (output == null) + return null; + + return String.format("%d.%02d", output / 100, Math.abs(output % 100)); + } + +} diff --git a/src/main/java/org/qortal/asset/Asset.java b/src/main/java/org/qortal/asset/Asset.java index ebfbf4d4..15135356 100644 --- a/src/main/java/org/qortal/asset/Asset.java +++ b/src/main/java/org/qortal/asset/Asset.java @@ -6,6 +6,7 @@ import org.qortal.data.transaction.TransactionData; import org.qortal.data.transaction.UpdateAssetTransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class Asset { @@ -25,8 +26,7 @@ public class Asset { public static final int MAX_DESCRIPTION_SIZE = 4000; public static final int MAX_DATA_SIZE = 400000; - public static final long MULTIPLIER = 100000000L; - public static final long MAX_QUANTITY = 10_000_000_000L * MULTIPLIER; // but also to 8 decimal places + public static final long MAX_QUANTITY = 10_000_000_000L * Amounts.MULTIPLIER; // but also to 8 decimal places // Properties private Repository repository; diff --git a/src/main/java/org/qortal/asset/Order.java b/src/main/java/org/qortal/asset/Order.java index acb965e5..20473931 100644 --- a/src/main/java/org/qortal/asset/Order.java +++ b/src/main/java/org/qortal/asset/Order.java @@ -2,6 +2,7 @@ package org.qortal.asset; import static org.qortal.utils.Amounts.prettyAmount; +import java.math.BigInteger; import java.util.Arrays; import java.util.List; @@ -29,6 +30,9 @@ public class Order { // Used quite a bit private final long haveAssetId; private final long wantAssetId; + private final boolean isAmountInWantAsset; + private final BigInteger orderAmount; + private final BigInteger orderPrice; /** Cache of price-pair units e.g. QORT/GOLD, but use getPricePair() instead! */ private String cachedPricePair; @@ -46,6 +50,10 @@ public class Order { this.haveAssetId = this.orderData.getHaveAssetId(); this.wantAssetId = this.orderData.getWantAssetId(); + this.isAmountInWantAsset = haveAssetId < wantAssetId; + + this.orderAmount = BigInteger.valueOf(this.orderData.getAmount()); + this.orderPrice = BigInteger.valueOf(this.orderData.getPrice()); } // Getters/Setters @@ -84,29 +92,29 @@ public class Order { */ public static long calculateAmountGranularity(boolean isAmountAssetDivisible, boolean isReturnAssetDivisible, long price) { // Calculate the minimum increment for matched-amount using greatest-common-divisor - long returnAmount = Asset.MULTIPLIER; // 1 unit * multiplier - long matchedAmount = price; + BigInteger returnAmount = Amounts.MULTIPLIER_BI; // 1 unit * multiplier + BigInteger matchedAmount = BigInteger.valueOf(price); - long gcd = Amounts.greatestCommonDivisor(returnAmount, matchedAmount); - returnAmount /= gcd; - matchedAmount /= gcd; + BigInteger gcd = returnAmount.gcd(matchedAmount); + returnAmount = returnAmount.divide(gcd); + matchedAmount = matchedAmount.divide(gcd); // Calculate GCD in combination with divisibility if (isAmountAssetDivisible) - returnAmount *= Asset.MULTIPLIER; + returnAmount = returnAmount.multiply(Amounts.MULTIPLIER_BI); if (isReturnAssetDivisible) - matchedAmount *= Asset.MULTIPLIER; + matchedAmount = matchedAmount.multiply(Amounts.MULTIPLIER_BI); - gcd = Amounts.greatestCommonDivisor(returnAmount, matchedAmount); + gcd = returnAmount.gcd(matchedAmount); // Calculate the granularity at which we have to buy - long granularity = returnAmount / gcd; + BigInteger granularity = returnAmount.multiply(Amounts.MULTIPLIER_BI).divide(gcd); if (isAmountAssetDivisible) - granularity /= Asset.MULTIPLIER; + granularity = granularity.divide(Amounts.MULTIPLIER_BI); // Return - return granularity; + return granularity.longValue(); } /** @@ -142,24 +150,24 @@ public class Order { /** Returns amount of have-asset to remove from order's creator's balance on placing this order. */ private long calcHaveAssetCommittment() { - long committedCost = this.orderData.getAmount(); + // Simple case: amount is in have asset + if (!this.isAmountInWantAsset) + return this.orderData.getAmount(); - // If "amount" is in want-asset then we need to convert - if (haveAssetId < wantAssetId) - committedCost *= this.orderData.getPrice() + 1; // +1 to round up + return Amounts.roundUpScaledMultiply(this.orderAmount, this.orderPrice); + } - return committedCost; + private long calcHaveAssetRefund(long amount) { + // Simple case: amount is in have asset + if (!this.isAmountInWantAsset) + return amount; + + return Amounts.roundUpScaledMultiply(BigInteger.valueOf(amount), this.orderPrice); } /** Returns amount of remaining have-asset to refund to order's creator's balance on cancelling this order. */ private long calcHaveAssetRefund() { - long refund = getAmountLeft(); - - // If "amount" is in want-asset then we need to convert - if (haveAssetId < wantAssetId) - refund *= this.orderData.getPrice() + 1; // +1 to round up - - return refund; + return calcHaveAssetRefund(getAmountLeft()); } // Navigation @@ -235,7 +243,7 @@ public class Order { prettyAmount(Order.getAmountLeft(orderData)), amountAssetData.getName())); - long maxReturnAmount = Order.getAmountLeft(orderData) * (orderData.getPrice() + 1); // +1 to round up + long maxReturnAmount = Amounts.roundUpScaledMultiply(Order.getAmountLeft(orderData), orderData.getPrice()); String pricePair = getPricePair(); LOGGER.trace(() -> String.format("%s price: %s %s (%s %s tradable)", ourTheir, @@ -344,17 +352,17 @@ public class Order { // Trade can go ahead! // Calculate the total cost to us, in return-asset, based on their price - long returnAmountTraded = matchedAmount * theirOrderData.getPrice(); + long returnAmountTraded = Amounts.roundDownScaledMultiply(matchedAmount, theirOrderData.getPrice()); LOGGER.trace(() -> String.format("returnAmountTraded: %s %s", prettyAmount(returnAmountTraded), returnAssetData.getName())); // Safety check checkDivisibility(returnAssetData, returnAmountTraded, this.orderData); - long tradedWantAmount = (haveAssetId > wantAssetId) ? returnAmountTraded : matchedAmount; - long tradedHaveAmount = (haveAssetId > wantAssetId) ? matchedAmount : returnAmountTraded; + long tradedWantAmount = this.isAmountInWantAsset ? matchedAmount : returnAmountTraded; + long tradedHaveAmount = this.isAmountInWantAsset ? returnAmountTraded : matchedAmount; // We also need to know how much have-asset to refund based on price improvement (only one direction applies) - long haveAssetRefund = haveAssetId < wantAssetId ? Math.abs(ourPrice -theirPrice) * matchedAmount : 0; + long haveAssetRefund = this.isAmountInWantAsset ? Amounts.roundDownScaledMultiply(matchedAmount, Math.abs(ourPrice - theirPrice)) : 0; LOGGER.trace(() -> String.format("We traded %s %s (have-asset) for %s %s (want-asset), saving %s %s (have-asset)", prettyAmount(tradedHaveAmount), haveAssetData.getName(), @@ -364,6 +372,7 @@ public class Order { // Construct trade TradeData tradeData = new TradeData(this.orderData.getOrderId(), theirOrderData.getOrderId(), tradedWantAmount, tradedHaveAmount, haveAssetRefund, this.orderData.getTimestamp()); + // Process trade, updating corresponding orders in repository Trade trade = new Trade(this.repository, tradeData); trade.process(); @@ -386,7 +395,7 @@ public class Order { * @throws DataException if divisibility check fails */ private void checkDivisibility(AssetData assetData, long amount, OrderData orderData) throws DataException { - if (assetData.getIsDivisible() || amount % Asset.MULTIPLIER == 0) + if (assetData.getIsDivisible() || amount % Amounts.MULTIPLIER == 0) // Asset is divisible or amount has no fractional part return; diff --git a/src/main/java/org/qortal/at/QortalATAPI.java b/src/main/java/org/qortal/at/QortalATAPI.java index ae00a936..91ba06f3 100644 --- a/src/main/java/org/qortal/at/QortalATAPI.java +++ b/src/main/java/org/qortal/at/QortalATAPI.java @@ -27,13 +27,14 @@ import org.qortal.group.Group; import org.qortal.repository.DataException; import org.qortal.repository.Repository; import org.qortal.transaction.AtTransaction; +import org.qortal.utils.Amounts; import com.google.common.primitives.Bytes; public class QortalATAPI extends API { // Useful constants - private static final long FEE_PER_STEP = 1 * Asset.MULTIPLIER; // 1 QORT per "step" + private static final long FEE_PER_STEP = 1 * Amounts.MULTIPLIER; // 1 QORT per "step" private static final int MAX_STEPS_PER_ROUND = 500; private static final int STEPS_PER_FUNCTION_CALL = 10; private static final int MINUTES_PER_BLOCK = 10; diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index abee08d8..0ad91e18 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -196,7 +196,7 @@ public class Block { this.repository.getAccountRepository().modifyAssetBalance(this.mintingAccount.getAddress(), Asset.QORT, accountAmount); } else { // minter & recipient different - extra work needed - long recipientAmount = (accountAmount * this.sharePercent) / 100L / 100L; // because scaled by 2dp and 'percent' means "per 1e2" + long recipientAmount = (accountAmount * this.sharePercent) / 100L / 100L; // because scaled by 2dp and 'percent' means "per 100" long minterAmount = accountAmount - recipientAmount; LOGGER.trace(() -> String.format("Minter account %s share: %s", this.mintingAccount.getAddress(), Amounts.prettyAmount(minterAmount))); @@ -1257,6 +1257,8 @@ public class Block { // Link transactions to this block, thus removing them from unconfirmed transactions list. // Also update "transaction participants" in repository for "transactions involving X" support in API linkTransactionsToBlock(); + + postBlockTidy(); } protected void increaseAccountLevels() throws DataException { @@ -1450,7 +1452,7 @@ public class Block { public void orphan() throws DataException { LOGGER.trace(() -> String.format("Orphaning block %d", this.blockData.getHeight())); - this.repository.setDebug(false); + this.repository.setDebug(true); try { // Return AT fees and delete AT states from repository orphanAtFeesAndStates(); @@ -1478,6 +1480,8 @@ public class Block { // Delete block from blockchain this.repository.getBlockRepository().delete(this.blockData); this.blockData.setHeight(null); + + postBlockTidy(); } finally { this.repository.setDebug(false); } @@ -1796,4 +1800,9 @@ public class Block { } } + /** Opportunity to tidy repository, etc. after block process/orphan. */ + private void postBlockTidy() throws DataException { + this.repository.getAccountRepository().tidy(); + } + } diff --git a/src/main/java/org/qortal/block/BlockChain.java b/src/main/java/org/qortal/block/BlockChain.java index a2672252..4ac0e2d6 100644 --- a/src/main/java/org/qortal/block/BlockChain.java +++ b/src/main/java/org/qortal/block/BlockChain.java @@ -103,10 +103,11 @@ public class BlockChain { BigDecimal qoraHoldersShare; /** Unscaled (* 1e8) share of block reward/fees to legacy QORA coin holders */ private long qoraHoldersUnscaledShare; // calculated after unmarshal + /** How many legacy QORA per 1 QORT of block reward. */ BigDecimal qoraPerQortReward; - /** How many legacy QORA per 1 QORT of block reward. */ - private long unscaledQoraPerQortReward; + /** How many legacy QORA per 1 QORT of block reward. Unscaled (* 1e8). */ + private long unscaledQoraPerQortReward; // calculated after unmarshal /** * Number of minted blocks required to reach next level from previous. @@ -454,6 +455,9 @@ public class BlockChain { // Calculate unscaled long version of Qora-holders block reward share this.qoraHoldersUnscaledShare = this.qoraHoldersShare.setScale(8).unscaledValue().longValue(); + // Calculate unscaled long version of Qora-per-Qort block reward + this.unscaledQoraPerQortReward = this.qoraPerQortReward.setScale(8).unscaledValue().longValue(); + // Convert collections to unmodifiable form this.rewardsByHeight = Collections.unmodifiableList(this.rewardsByHeight); this.sharesByLevel = Collections.unmodifiableList(this.sharesByLevel); diff --git a/src/main/java/org/qortal/data/account/AccountBalanceData.java b/src/main/java/org/qortal/data/account/AccountBalanceData.java index 68bf1aab..94c03ab4 100644 --- a/src/main/java/org/qortal/data/account/AccountBalanceData.java +++ b/src/main/java/org/qortal/data/account/AccountBalanceData.java @@ -5,6 +5,8 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import org.qortal.utils.Amounts; + // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) public class AccountBalanceData { @@ -70,4 +72,8 @@ public class AccountBalanceData { return this.assetName; } + public String toString() { + return String.format("%s has %s %s [assetId %d]", this.address, Amounts.prettyAmount(this.balance), (assetName != null ? assetName : ""), assetId); + } + } diff --git a/src/main/java/org/qortal/data/account/RewardShareData.java b/src/main/java/org/qortal/data/account/RewardShareData.java index ffb46f8f..c68e4257 100644 --- a/src/main/java/org/qortal/data/account/RewardShareData.java +++ b/src/main/java/org/qortal/data/account/RewardShareData.java @@ -1,11 +1,10 @@ package org.qortal.data.account; -import java.math.BigDecimal; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlTransient; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import io.swagger.v3.oas.annotations.media.Schema; @@ -24,9 +23,7 @@ public class RewardShareData { private String recipient; private byte[] rewardSharePublicKey; - // JAXB to use separate getter - @XmlTransient - @Schema(hidden = true) + @XmlJavaTypeAdapter(value = org.qortal.api.RewardSharePercentTypeAdapter.class) private int sharePercent; // Constructors @@ -74,9 +71,4 @@ public class RewardShareData { return this.minter; } - @XmlElement(name = "sharePercent") - public BigDecimal getSharePercentJaxb() { - return BigDecimal.valueOf(this.sharePercent, 2); - } - } diff --git a/src/main/java/org/qortal/data/transaction/ATTransactionData.java b/src/main/java/org/qortal/data/transaction/ATTransactionData.java index 21ce25ca..92469eaf 100644 --- a/src/main/java/org/qortal/data/transaction/ATTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/ATTransactionData.java @@ -1,11 +1,10 @@ package org.qortal.data.transaction; -import java.math.BigDecimal; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlTransient; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.qortal.account.NullAccount; import org.qortal.transaction.Transaction.TransactionType; @@ -23,9 +22,8 @@ public class ATTransactionData extends TransactionData { private String recipient; - @XmlTransient - @Schema(hidden = true) // Not always present + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) private Long amount; // Not always present @@ -78,14 +76,4 @@ public class ATTransactionData extends TransactionData { return this.message; } - // Some JAXB/API-related getters - - @Schema(name = "amount") - public BigDecimal getAmountJaxb() { - if (this.amount == null) - return null; - - return BigDecimal.valueOf(this.amount, 8); - } - } diff --git a/src/main/java/org/qortal/data/transaction/IssueAssetTransactionData.java b/src/main/java/org/qortal/data/transaction/IssueAssetTransactionData.java index da23e5f1..071bade7 100644 --- a/src/main/java/org/qortal/data/transaction/IssueAssetTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/IssueAssetTransactionData.java @@ -3,6 +3,7 @@ package org.qortal.data.transaction; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue; import org.qortal.account.NullAccount; @@ -20,23 +21,33 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode; public class IssueAssetTransactionData extends TransactionData { // Properties + // assetId can be null but assigned during save() or during load from repository @Schema(accessMode = AccessMode.READ_ONLY) private Long assetId = null; + @Schema(description = "asset issuer's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP") private byte[] issuerPublicKey; + @Schema(description = "asset owner's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v") private String owner; + @Schema(description = "asset name", example = "GOLD") private String assetName; + @Schema(description = "asset description", example = "Gold asset - 1 unit represents one 1kg of gold") private String description; + @Schema(description = "total supply of asset in existence (integer)", example = "1000") + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) private long quantity; + @Schema(description = "whether asset quantities can be fractional", example = "true") private boolean isDivisible; + @Schema(description = "non-human-readable asset-related data, typically JSON", example = "{\"logo\": \"data:image/jpeg;base64,/9j/4AAQSkZJRgA==\"}") private String data; + @Schema(description = "whether non-owner holders of asset are barred from using asset", example = "false") private boolean isUnspendable; diff --git a/src/main/java/org/qortal/data/transaction/RewardShareTransactionData.java b/src/main/java/org/qortal/data/transaction/RewardShareTransactionData.java index 0950dfda..5513d605 100644 --- a/src/main/java/org/qortal/data/transaction/RewardShareTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/RewardShareTransactionData.java @@ -1,12 +1,10 @@ package org.qortal.data.transaction; -import java.math.BigDecimal; - import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlTransient; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue; import org.qortal.transaction.Transaction.TransactionType; @@ -28,9 +26,8 @@ public class RewardShareTransactionData extends TransactionData { @Schema(example = "reward_share_public_key") private byte[] rewardSharePublicKey; - // JAXB will use special getter below - @XmlTransient - @Schema(hidden = true) + @Schema(description = "Percentage of block rewards that minter shares to recipient, or negative value to cancel existing reward-share") + @XmlJavaTypeAdapter(value = org.qortal.api.RewardSharePercentTypeAdapter.class) private int sharePercent; // No need to ever expose this via API @@ -93,12 +90,4 @@ public class RewardShareTransactionData extends TransactionData { this.previousSharePercent = previousSharePercent; } - // Special JAXB getters - - @Schema(name = "sharePercent", description = "Percentage of block rewards that minter shares to recipient, or negative value to cancel existing reward-share") - @XmlElement(name = "sharePercent") - public BigDecimal getSharePercentJaxb() { - return BigDecimal.valueOf(this.sharePercent, 2); - } - } diff --git a/src/main/java/org/qortal/data/transaction/TransactionData.java b/src/main/java/org/qortal/data/transaction/TransactionData.java index e4151a99..cfb6872e 100644 --- a/src/main/java/org/qortal/data/transaction/TransactionData.java +++ b/src/main/java/org/qortal/data/transaction/TransactionData.java @@ -1,6 +1,5 @@ package org.qortal.data.transaction; -import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; @@ -9,6 +8,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlTransient; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode; import org.qortal.crypto.Crypto; @@ -51,18 +51,24 @@ public abstract class TransactionData { // Properties shared with all transaction types @Schema(accessMode = AccessMode.READ_ONLY, hidden = true) protected TransactionType type; + @XmlTransient // represented in transaction-specific properties @Schema(hidden = true) protected byte[] creatorPublicKey; + @Schema(description = "timestamp when transaction created, in milliseconds since unix epoch", example = "__unix_epoch_time_milliseconds__") protected long timestamp; + @Schema(description = "sender's last transaction ID", example = "real_transaction_reference_in_base58") protected byte[] reference; - @XmlTransient - @Schema(hidden = true) + + @Schema(description = "fee for processing transaction", example = "0.0001") + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) protected Long fee; // can be null if fee not calculated yet + @Schema(accessMode = AccessMode.READ_ONLY, description = "signature for transaction's raw bytes, using sender's private key", example = "real_transaction_signature_in_base58") protected byte[] signature; + @Schema(description = "groupID for this transaction") protected int txGroupId; @@ -184,14 +190,6 @@ public abstract class TransactionData { // JAXB special - @Schema(name = "fee", description = "fee for processing transaction", example = "0.0001") - protected BigDecimal getFeeJaxb() { - if (this.fee == null) - return null; - - return BigDecimal.valueOf(this.fee, 8); - } - @XmlElement(name = "creatorAddress") protected String getCreatorAddress() { return Crypto.toAddress(this.creatorPublicKey); diff --git a/src/main/java/org/qortal/payment/Payment.java b/src/main/java/org/qortal/payment/Payment.java index afdc4270..5ff01567 100644 --- a/src/main/java/org/qortal/payment/Payment.java +++ b/src/main/java/org/qortal/payment/Payment.java @@ -18,6 +18,7 @@ import org.qortal.repository.AssetRepository; import org.qortal.repository.DataException; import org.qortal.repository.Repository; import org.qortal.transaction.Transaction.ValidationResult; +import org.qortal.utils.Amounts; public class Payment { @@ -93,7 +94,7 @@ public class Payment { return ValidationResult.ASSET_DOES_NOT_MATCH_AT; // Check asset amount is integer if asset is not divisible - if (!assetData.getIsDivisible() && paymentData.getAmount() % Asset.MULTIPLIER != 0) + if (!assetData.getIsDivisible() && paymentData.getAmount() % Amounts.MULTIPLIER != 0) return ValidationResult.INVALID_AMOUNT; // Set or add amount into amounts-by-asset map @@ -149,7 +150,7 @@ public class Payment { // process /** Multiple payment processing */ - public void process(byte[] senderPublicKey, List payments, byte[] signature) throws DataException { + public void process(byte[] senderPublicKey, List payments) throws DataException { Account sender = new PublicKeyAccount(this.repository, senderPublicKey); // Process all payments @@ -168,8 +169,8 @@ public class Payment { } /** Single payment processing */ - public void process(byte[] senderPublicKey, PaymentData paymentData, byte[] signature) throws DataException { - process(senderPublicKey, Collections.singletonList(paymentData), signature); + public void process(byte[] senderPublicKey, PaymentData paymentData) throws DataException { + process(senderPublicKey, Collections.singletonList(paymentData)); } // processReferenceAndFees @@ -205,7 +206,7 @@ public class Payment { // orphan - public void orphan(byte[] senderPublicKey, List payments, byte[] signature, byte[] reference) throws DataException { + public void orphan(byte[] senderPublicKey, List payments) throws DataException { Account sender = new PublicKeyAccount(this.repository, senderPublicKey); // Orphan all payments @@ -222,8 +223,8 @@ public class Payment { } } - public void orphan(byte[] senderPublicKey, PaymentData paymentData, byte[] signature, byte[] reference) throws DataException { - orphan(senderPublicKey, Collections.singletonList(paymentData), signature, reference); + public void orphan(byte[] senderPublicKey, PaymentData paymentData) throws DataException { + orphan(senderPublicKey, Collections.singletonList(paymentData)); } // orphanReferencesAndFees @@ -247,10 +248,8 @@ public class Payment { * For QORT amounts only: If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own * (which would have changed their last reference) thus this is their first reference so remove it. */ - if ((alwaysUninitializeRecipientReference || assetId == Asset.QORT) && Arrays.equals(recipient.getLastReference(), signature)) { + if ((alwaysUninitializeRecipientReference || assetId == Asset.QORT) && Arrays.equals(recipient.getLastReference(), signature)) recipient.setLastReference(null); - this.repository.getAccountRepository().delete(recipient.getAddress(), assetId); - } } } diff --git a/src/main/java/org/qortal/repository/AccountRepository.java b/src/main/java/org/qortal/repository/AccountRepository.java index 4372c0b5..c88102a4 100644 --- a/src/main/java/org/qortal/repository/AccountRepository.java +++ b/src/main/java/org/qortal/repository/AccountRepository.java @@ -91,6 +91,9 @@ public interface AccountRepository { /** Delete account from repository. */ public void delete(String address) throws DataException; + /** Generic opportunistic tidy. */ + public void tidy() throws DataException; + // Account balances public AccountBalanceData getBalance(String address, long assetId) throws DataException; diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java index f6ab9b41..4ac90721 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java @@ -308,6 +308,15 @@ public class HSQLDBAccountRepository implements AccountRepository { } } + @Override + public void tidy() throws DataException { + try { + this.repository.delete("AccountBalances", "balance = 0"); + } catch (SQLException e) { + throw new DataException("Unable to tidy zero account balances from repository", e); + } + } + // Account balances @Override diff --git a/src/main/java/org/qortal/transaction/ArbitraryTransaction.java b/src/main/java/org/qortal/transaction/ArbitraryTransaction.java index b40a6557..04ecc09f 100644 --- a/src/main/java/org/qortal/transaction/ArbitraryTransaction.java +++ b/src/main/java/org/qortal/transaction/ArbitraryTransaction.java @@ -63,8 +63,7 @@ public class ArbitraryTransaction extends Transaction { @Override public void process() throws DataException { // Wrap and delegate payment processing to Payment class. - new Payment(this.repository).process(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(), - arbitraryTransactionData.getSignature()); + new Payment(this.repository).process(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments()); } @Override @@ -77,8 +76,7 @@ public class ArbitraryTransaction extends Transaction { @Override public void orphan() throws DataException { // Wrap and delegate payment processing to Payment class. - new Payment(this.repository).orphan(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(), - arbitraryTransactionData.getSignature(), arbitraryTransactionData.getReference()); + new Payment(this.repository).orphan(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments()); } @Override diff --git a/src/main/java/org/qortal/transaction/AtTransaction.java b/src/main/java/org/qortal/transaction/AtTransaction.java index 6d5c804d..f463a0c4 100644 --- a/src/main/java/org/qortal/transaction/AtTransaction.java +++ b/src/main/java/org/qortal/transaction/AtTransaction.java @@ -13,6 +13,7 @@ import org.qortal.repository.DataException; import org.qortal.repository.Repository; import org.qortal.transform.TransformationException; import org.qortal.transform.transaction.AtTransactionTransformer; +import org.qortal.utils.Amounts; import com.google.common.primitives.Bytes; @@ -113,7 +114,7 @@ public class AtTransaction extends Transaction { return ValidationResult.ASSET_DOES_NOT_EXIST; // Check asset amount is integer if asset is not divisible - if (!assetData.getIsDivisible() && amount % Asset.MULTIPLIER != 0) + if (!assetData.getIsDivisible() && amount % Amounts.MULTIPLIER != 0) return ValidationResult.INVALID_AMOUNT; Account sender = getATAccount(); diff --git a/src/main/java/org/qortal/transaction/CreateAssetOrderTransaction.java b/src/main/java/org/qortal/transaction/CreateAssetOrderTransaction.java index 7d73c6ef..3635c797 100644 --- a/src/main/java/org/qortal/transaction/CreateAssetOrderTransaction.java +++ b/src/main/java/org/qortal/transaction/CreateAssetOrderTransaction.java @@ -1,5 +1,6 @@ package org.qortal.transaction; +import java.math.BigInteger; import java.util.Collections; import java.util.List; @@ -13,6 +14,7 @@ import org.qortal.data.transaction.TransactionData; import org.qortal.repository.AssetRepository; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class CreateAssetOrderTransaction extends Transaction { @@ -79,9 +81,6 @@ public class CreateAssetOrderTransaction extends Transaction { Account creator = getCreator(); - long committedCost; - long maxOtherAmount; - /* * "amount" might be either have-asset or want-asset, whichever has the highest assetID. * @@ -93,34 +92,39 @@ public class CreateAssetOrderTransaction extends Transaction { * stake 123 GOLD, return 49200 QORT */ boolean isAmountWantAsset = haveAssetId < wantAssetId; + BigInteger amount = BigInteger.valueOf(this.createOrderTransactionData.getAmount()); + BigInteger price = BigInteger.valueOf(this.createOrderTransactionData.getPrice()); + + BigInteger committedCost; + BigInteger maxOtherAmount; if (isAmountWantAsset) { // have/commit 49200 QORT, want/return 123 GOLD - committedCost = this.createOrderTransactionData.getAmount() * this.createOrderTransactionData.getPrice(); - maxOtherAmount = this.createOrderTransactionData.getAmount(); + committedCost = amount.multiply(price).divide(Amounts.MULTIPLIER_BI); + maxOtherAmount = amount; } else { // have/commit 123 GOLD, want/return 49200 QORT - committedCost = this.createOrderTransactionData.getAmount(); - maxOtherAmount = this.createOrderTransactionData.getAmount() * this.createOrderTransactionData.getPrice(); + committedCost = amount; + maxOtherAmount = amount.multiply(price).divide(Amounts.MULTIPLIER_BI); } // Check amount is integer if amount's asset is not divisible - if (!haveAssetData.getIsDivisible() && committedCost % Asset.MULTIPLIER != 0) + if (!haveAssetData.getIsDivisible() && committedCost.mod(Amounts.MULTIPLIER_BI).signum() != 0) return ValidationResult.INVALID_AMOUNT; // Check total return from fulfilled order would be integer if return's asset is not divisible - if (!wantAssetData.getIsDivisible() && maxOtherAmount % Asset.MULTIPLIER != 0) + if (!wantAssetData.getIsDivisible() && maxOtherAmount.mod(Amounts.MULTIPLIER_BI).signum() != 0) return ValidationResult.INVALID_RETURN; // Check order creator has enough asset balance AFTER removing fee, in case asset is QORT // If asset is QORT then we need to check amount + fee in one go if (haveAssetId == Asset.QORT) { // Check creator has enough funds for amount + fee in QORT - if (creator.getConfirmedBalance(Asset.QORT) < committedCost + this.createOrderTransactionData.getFee()) + if (creator.getConfirmedBalance(Asset.QORT) < committedCost.longValue() + this.createOrderTransactionData.getFee()) return ValidationResult.NO_BALANCE; } else { // Check creator has enough funds for amount in whatever asset - if (creator.getConfirmedBalance(haveAssetId) < committedCost) + if (creator.getConfirmedBalance(haveAssetId) < committedCost.longValue()) return ValidationResult.NO_BALANCE; // Check creator has enough funds for fee in QORT diff --git a/src/main/java/org/qortal/transaction/DeployAtTransaction.java b/src/main/java/org/qortal/transaction/DeployAtTransaction.java index d799ff81..0a055d3f 100644 --- a/src/main/java/org/qortal/transaction/DeployAtTransaction.java +++ b/src/main/java/org/qortal/transaction/DeployAtTransaction.java @@ -17,6 +17,7 @@ import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; import org.qortal.transform.Transformer; +import org.qortal.utils.Amounts; import com.google.common.base.Utf8; @@ -130,7 +131,7 @@ public class DeployAtTransaction extends Transaction { return ValidationResult.ASSET_NOT_SPENDABLE; // Check asset amount is integer if asset is not divisible - if (!assetData.getIsDivisible() && this.deployATTransactionData.getAmount() % Asset.MULTIPLIER != 0) + if (!assetData.getIsDivisible() && this.deployATTransactionData.getAmount() % Amounts.MULTIPLIER != 0) return ValidationResult.INVALID_AMOUNT; Account creator = this.getCreator(); diff --git a/src/main/java/org/qortal/transaction/IssueAssetTransaction.java b/src/main/java/org/qortal/transaction/IssueAssetTransaction.java index ff088912..3c27c7a7 100644 --- a/src/main/java/org/qortal/transaction/IssueAssetTransaction.java +++ b/src/main/java/org/qortal/transaction/IssueAssetTransaction.java @@ -10,6 +10,7 @@ import org.qortal.data.transaction.IssueAssetTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; import com.google.common.base.Utf8; @@ -77,7 +78,7 @@ public class IssueAssetTransaction extends Transaction { return ValidationResult.INVALID_QUANTITY; // Check quantity versus indivisibility - if (!this.issueAssetTransactionData.getIsDivisible() && this.issueAssetTransactionData.getQuantity() % Asset.MULTIPLIER != 0) + if (!this.issueAssetTransactionData.getIsDivisible() && this.issueAssetTransactionData.getQuantity() % Amounts.MULTIPLIER != 0) return ValidationResult.INVALID_QUANTITY; Account issuer = getIssuer(); diff --git a/src/main/java/org/qortal/transaction/MessageTransaction.java b/src/main/java/org/qortal/transaction/MessageTransaction.java index 3390ab3a..463f47a2 100644 --- a/src/main/java/org/qortal/transaction/MessageTransaction.java +++ b/src/main/java/org/qortal/transaction/MessageTransaction.java @@ -77,7 +77,7 @@ public class MessageTransaction extends Transaction { @Override public void process() throws DataException { // Wrap and delegate payment processing to Payment class. - new Payment(this.repository).process(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getSignature()); + new Payment(this.repository).process(this.messageTransactionData.getSenderPublicKey(), getPaymentData()); } @Override @@ -90,7 +90,7 @@ public class MessageTransaction extends Transaction { @Override public void orphan() throws DataException { // Wrap and delegate payment processing to Payment class. - new Payment(this.repository).orphan(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getSignature(), this.messageTransactionData.getReference()); + new Payment(this.repository).orphan(this.messageTransactionData.getSenderPublicKey(), getPaymentData()); } @Override diff --git a/src/main/java/org/qortal/transaction/MultiPaymentTransaction.java b/src/main/java/org/qortal/transaction/MultiPaymentTransaction.java index bdc73575..4c3f75dc 100644 --- a/src/main/java/org/qortal/transaction/MultiPaymentTransaction.java +++ b/src/main/java/org/qortal/transaction/MultiPaymentTransaction.java @@ -70,7 +70,7 @@ public class MultiPaymentTransaction extends Transaction { @Override public void process() throws DataException { // Wrap and delegate payment processing to Payment class. - new Payment(this.repository).process(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments(), this.multiPaymentTransactionData.getSignature()); + new Payment(this.repository).process(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments()); } @Override @@ -83,8 +83,7 @@ public class MultiPaymentTransaction extends Transaction { @Override public void orphan() throws DataException { // Wrap and delegate payment processing to Payment class. Always revert recipients' last references regardless of asset. - new Payment(this.repository).orphan(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments(), - this.multiPaymentTransactionData.getSignature(), this.multiPaymentTransactionData.getReference()); + new Payment(this.repository).orphan(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments()); } @Override diff --git a/src/main/java/org/qortal/transaction/PaymentTransaction.java b/src/main/java/org/qortal/transaction/PaymentTransaction.java index 9372f281..f6caaef5 100644 --- a/src/main/java/org/qortal/transaction/PaymentTransaction.java +++ b/src/main/java/org/qortal/transaction/PaymentTransaction.java @@ -64,7 +64,7 @@ public class PaymentTransaction extends Transaction { @Override public void process() throws DataException { // Wrap and delegate payment processing to Payment class. - new Payment(this.repository).process(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), this.paymentTransactionData.getSignature()); + new Payment(this.repository).process(this.paymentTransactionData.getSenderPublicKey(), getPaymentData()); } @Override @@ -77,8 +77,7 @@ public class PaymentTransaction extends Transaction { @Override public void orphan() throws DataException { // Wrap and delegate payment processing to Payment class. Only revert recipient's last reference if transferring QORT. - new Payment(this.repository).orphan(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), - this.paymentTransactionData.getSignature(), this.paymentTransactionData.getReference()); + new Payment(this.repository).orphan(this.paymentTransactionData.getSenderPublicKey(), getPaymentData()); } @Override diff --git a/src/main/java/org/qortal/transaction/TransferAssetTransaction.java b/src/main/java/org/qortal/transaction/TransferAssetTransaction.java index 991a1ff0..a2855a35 100644 --- a/src/main/java/org/qortal/transaction/TransferAssetTransaction.java +++ b/src/main/java/org/qortal/transaction/TransferAssetTransaction.java @@ -64,7 +64,7 @@ public class TransferAssetTransaction extends Transaction { @Override public void process() throws DataException { // Wrap asset transfer as a payment and delegate processing to Payment class. - new Payment(this.repository).process(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), this.transferAssetTransactionData.getSignature()); + new Payment(this.repository).process(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData()); } @Override @@ -77,8 +77,7 @@ public class TransferAssetTransaction extends Transaction { @Override public void orphan() throws DataException { // Wrap asset transfer as a payment and delegate processing to Payment class. - new Payment(this.repository).orphan(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), - this.transferAssetTransactionData.getSignature(), this.transferAssetTransactionData.getReference()); + new Payment(this.repository).orphan(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData()); } @Override diff --git a/src/main/java/org/qortal/transaction/TransferPrivsTransaction.java b/src/main/java/org/qortal/transaction/TransferPrivsTransaction.java index 92c9f533..3fedfcb4 100644 --- a/src/main/java/org/qortal/transaction/TransferPrivsTransaction.java +++ b/src/main/java/org/qortal/transaction/TransferPrivsTransaction.java @@ -191,23 +191,28 @@ public class TransferPrivsTransaction extends Transaction { break; } - // Restore recipient block minted count/adjustment - recipientData.setBlocksMinted(recipientData.getBlocksMinted() - this.transferPrivsTransactionData.getPreviousSenderBlocksMinted()); - accountRepository.setMintedBlockCount(recipientData); - recipientData.setBlocksMintedAdjustment(recipientData.getBlocksMintedAdjustment() - this.transferPrivsTransactionData.getPreviousSenderBlocksMintedAdjustment()); - accountRepository.setBlocksMintedAdjustment(recipientData); + if (previousRecipientFlags != null) { + // Restore recipient block minted count/adjustment + recipientData.setBlocksMinted(recipientData.getBlocksMinted() - this.transferPrivsTransactionData.getPreviousSenderBlocksMinted()); + accountRepository.setMintedBlockCount(recipientData); + recipientData.setBlocksMintedAdjustment(recipientData.getBlocksMintedAdjustment() - this.transferPrivsTransactionData.getPreviousSenderBlocksMintedAdjustment()); + accountRepository.setBlocksMintedAdjustment(recipientData); - // Recalculate recipient's level - effectiveBlocksMinted = recipientData.getBlocksMinted() + recipientData.getBlocksMintedAdjustment(); - for (int newLevel = maximumLevel; newLevel > 0; --newLevel) - if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) { - // Account level - recipientData.setLevel(newLevel); - accountRepository.setLevel(recipientData); - LOGGER.trace(() -> String.format("TRANSFER_PRIVS recipient %s reset to level %d", recipientData.getAddress(), recipientData.getLevel())); + // Recalculate recipient's level + effectiveBlocksMinted = recipientData.getBlocksMinted() + recipientData.getBlocksMintedAdjustment(); + for (int newLevel = maximumLevel; newLevel > 0; --newLevel) + if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) { + // Account level + recipientData.setLevel(newLevel); + accountRepository.setLevel(recipientData); + LOGGER.trace(() -> String.format("TRANSFER_PRIVS recipient %s reset to level %d", recipientData.getAddress(), recipientData.getLevel())); - break; - } + break; + } + } else { + // Recipient didn't exist before now + accountRepository.delete(recipient.getAddress()); + } // Clear values in transaction data this.transferPrivsTransactionData.setPreviousSenderBlocksMinted(null); diff --git a/src/main/java/org/qortal/utils/Amounts.java b/src/main/java/org/qortal/utils/Amounts.java index 39e42e24..1be28c47 100644 --- a/src/main/java/org/qortal/utils/Amounts.java +++ b/src/main/java/org/qortal/utils/Amounts.java @@ -1,9 +1,16 @@ package org.qortal.utils; import java.math.BigDecimal; +import java.math.BigInteger; public abstract class Amounts { + public static final long MULTIPLIER = 100000000L; + + // For calculations that might overflow longs + public static final BigInteger MULTIPLIER_BI = BigInteger.valueOf(MULTIPLIER); + public static final BigInteger ROUNDING = MULTIPLIER_BI.subtract(BigInteger.ONE); + public static String prettyAmount(long amount) { StringBuilder stringBuilder = new StringBuilder(20); @@ -13,7 +20,7 @@ public abstract class Amounts { int dpLength = stringBuilder.length(); - stringBuilder.append(amount % 100000000L); + stringBuilder.append(Math.abs(amount % 100000000L)); int paddingRequired = 8 - (stringBuilder.length() - dpLength); if (paddingRequired > 0) @@ -41,4 +48,24 @@ public abstract class Amounts { return Math.abs(a); } + public static long roundUpScaledMultiply(BigInteger amount, BigInteger price) { + return amount.multiply(price).add(ROUNDING).divide(MULTIPLIER_BI).longValue(); + } + + public static long roundUpScaledMultiply(long amount, long price) { + return roundUpScaledMultiply(BigInteger.valueOf(amount), BigInteger.valueOf(price)); + } + + public static long roundDownScaledMultiply(BigInteger amount, BigInteger price) { + return amount.multiply(price).divide(MULTIPLIER_BI).longValue(); + } + + public static long roundDownScaledMultiply(long amount, long price) { + return roundDownScaledMultiply(BigInteger.valueOf(amount), BigInteger.valueOf(price)); + } + + public static long scaledDivide(long dividend, long divisor) { + return BigInteger.valueOf(dividend).multiply(Amounts.MULTIPLIER_BI).divide(BigInteger.valueOf(divisor)).longValue(); + } + } diff --git a/src/test/java/org/qortal/test/AccountRefCacheTests.java b/src/test/java/org/qortal/test/AccountRefCacheTests.java index 184a8537..22355a90 100644 --- a/src/test/java/org/qortal/test/AccountRefCacheTests.java +++ b/src/test/java/org/qortal/test/AccountRefCacheTests.java @@ -246,7 +246,7 @@ public class AccountRefCacheTests extends Common { // Test Block support @Test public void testBlockSupport() throws DataException { - final long amount = 12345670000L; + final long amount = 123_45670000L; try (final Repository repository = RepositoryManager.getRepository()) { TestAccount alice = Common.getTestAccount(repository, "alice"); diff --git a/src/test/java/org/qortal/test/TransferPrivsTests.java b/src/test/java/org/qortal/test/TransferPrivsTests.java index 0329da72..5b483882 100644 --- a/src/test/java/org/qortal/test/TransferPrivsTests.java +++ b/src/test/java/org/qortal/test/TransferPrivsTests.java @@ -21,6 +21,7 @@ import org.qortal.test.common.Common; import org.qortal.test.common.TestAccount; import org.qortal.test.common.TransactionUtils; import org.qortal.transform.Transformer; +import org.qortal.utils.Amounts; import static org.junit.Assert.*; @@ -280,7 +281,7 @@ public class TransferPrivsTests extends Common { byte[] reference = senderAccount.getLastReference(); long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1; int txGroupId = 0; - long fee = 1L; + long fee = 1L * Amounts.MULTIPLIER; BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, senderAccount.getPublicKey(), fee, null); TransactionData transactionData = new TransferPrivsTransactionData(baseTransactionData, recipientAccount.getAddress()); diff --git a/src/test/java/org/qortal/test/apps/DecodeOnlineAccounts.java b/src/test/java/org/qortal/test/apps/DecodeOnlineAccounts.java index edfe717e..1781f719 100644 --- a/src/test/java/org/qortal/test/apps/DecodeOnlineAccounts.java +++ b/src/test/java/org/qortal/test/apps/DecodeOnlineAccounts.java @@ -1,5 +1,6 @@ package org.qortal.test.apps; +import java.math.BigDecimal; import java.security.Security; import org.bitcoinj.core.Base58; @@ -88,7 +89,7 @@ public class DecodeOnlineAccounts { System.out.println(String.format("Reward-share public key: %s, minter: %s, recipient: %s, share: %s", Base58.encode(rewardShareData.getRewardSharePublicKey()), rewardShareData.getMintingAccount(), rewardShareData.getRecipient(), - rewardShareData.getSharePercent().toPlainString())); + BigDecimal.valueOf(rewardShareData.getSharePercent(), 2).toPlainString())); } } catch (DataException e) { e.printStackTrace(); diff --git a/src/test/java/org/qortal/test/assets/CancellingTests.java b/src/test/java/org/qortal/test/assets/CancellingTests.java index 6329e50e..0d998cb5 100644 --- a/src/test/java/org/qortal/test/assets/CancellingTests.java +++ b/src/test/java/org/qortal/test/assets/CancellingTests.java @@ -2,8 +2,6 @@ package org.qortal.test.assets; import static org.junit.Assert.*; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.Map; import org.junit.After; @@ -17,9 +15,16 @@ import org.qortal.test.common.AssetUtils; import org.qortal.test.common.Common; import org.qortal.transaction.Transaction; import org.qortal.transaction.Transaction.ValidationResult; +import org.qortal.utils.Amounts; public class CancellingTests extends Common { + /* + * Commitments are always rounded up. + * Returns (amounts traded) are always rounded down. + * Thus expected post-cancel refunds should be rounded up too. + */ + @Before public void beforeTest() throws DataException { Common.useDefaultSettings(); @@ -32,11 +37,11 @@ public class CancellingTests extends Common { @Test public void testSimpleCancel() throws DataException { - BigDecimal amount = new BigDecimal("1234.87654321").setScale(8); - BigDecimal price = new BigDecimal("1.35615263").setScale(8); + long amount = 1234_87654321L; + long price = 1_35615263L; try (Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, AssetUtils.otherAssetId, amount, price); AssetUtils.cancelOrder(repository, "alice", aliceOrderId); @@ -45,7 +50,7 @@ public class CancellingTests extends Common { AssetUtils.cancelOrder(repository, "bob", bobOrderId); // Check asset balances match pre-ordering values - BigDecimal expectedBalance; + long expectedBalance; expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId); AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); @@ -57,11 +62,11 @@ public class CancellingTests extends Common { @Test public void testRepeatCancel() throws DataException { - BigDecimal amount = new BigDecimal("1234.87654321").setScale(8); - BigDecimal price = new BigDecimal("1.35615263").setScale(8); + long amount = 1234_87654321L; + long price = 1_35615263L; try (Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, AssetUtils.otherAssetId, amount, price); AssetUtils.cancelOrder(repository, "alice", aliceOrderId); @@ -70,7 +75,7 @@ public class CancellingTests extends Common { assertCannotCancelClosedOrder(repository, "alice", aliceOrderId); // Check asset balances match pre-ordering values - BigDecimal expectedBalance; + long expectedBalance; expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId); AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); @@ -82,125 +87,141 @@ public class CancellingTests extends Common { @Test public void testPartialTargetMatchCancel() throws DataException { - BigDecimal aliceAmount = new BigDecimal("1234").setScale(8); // OTHER - BigDecimal alicePrice = new BigDecimal("1.5").setScale(8); // TEST/OTHER + // TEST has a lower assetId than OTHER - BigDecimal bobAmount = new BigDecimal("500").setScale(8); // OTHER - BigDecimal bobPrice = new BigDecimal("1.2").setScale(8); // TEST/OTHER + // Alice has TEST, wants OTHER + long aliceAmount = 1234_00000000L; // OTHER is 'want' asset + long alicePrice = 1_50000000L; // TEST/OTHER - BigDecimal aliceCommitment = aliceAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // TEST - BigDecimal bobCommitment = bobAmount; // OTHER + // Bob has OTHER, wants TEST + long bobAmount = 500_00000000L; // OTHER is 'have' asset + long bobPrice = 1_20000000L; // TEST/OTHER - BigDecimal matchedAmount = aliceAmount.min(bobAmount); // 500 OTHER + long aliceCommitment = Amounts.roundUpScaledMultiply(aliceAmount, alicePrice); // TEST + long bobCommitment = bobAmount; // OTHER - BigDecimal aliceReturn = matchedAmount; // OTHER - BigDecimal bobReturn = matchedAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // TEST + long matchedAmount = Math.min(aliceAmount, bobAmount); // 500 OTHER - BigDecimal aliceRefund = aliceAmount.subtract(matchedAmount).multiply(alicePrice).setScale(8, RoundingMode.DOWN); // TEST - BigDecimal bobRefund = BigDecimal.ZERO; // because Bob's order is fully matched + long aliceReturn = matchedAmount; // OTHER + long bobReturn = Amounts.roundDownScaledMultiply(matchedAmount, alicePrice); // TEST - BigDecimal bobSaving = BigDecimal.ZERO; // not in this direction + long aliceRefund = Amounts.roundUpScaledMultiply(aliceAmount - matchedAmount, alicePrice); // TEST + long bobRefund = 0L; // because Bob's order is fully matched + + long bobSaving = 0L; // not in this direction try (Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); + // Place 'target' order byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice); + // Place 'initiating' order: the order that initiates a trade byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.testAssetId, bobAmount, bobPrice); AssetUtils.cancelOrder(repository, "alice", aliceOrderId); - assertCannotCancelClosedOrder(repository, "bob", bobOrderId); + assertCannotCancelClosedOrder(repository, "bob", bobOrderId); // because full matched // Check asset balances - BigDecimal expectedBalance; + long expectedBalance; // Alice - expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId).subtract(aliceCommitment).add(aliceRefund); + expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId) - aliceCommitment + aliceRefund; AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); - expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(aliceReturn); + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId) + aliceReturn; AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); // Bob - expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(bobCommitment).add(bobSaving).add(bobRefund); + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId) - bobCommitment + bobSaving + bobRefund; AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); - expectedBalance = initialBalances.get("bob").get(AssetUtils.testAssetId).add(bobReturn); + expectedBalance = initialBalances.get("bob").get(AssetUtils.testAssetId) + bobReturn; AccountUtils.assertBalance(repository, "bob", AssetUtils.testAssetId, expectedBalance); } } @Test public void testPartialInitiatorMatchCancel() throws DataException { - BigDecimal aliceAmount = new BigDecimal("500").setScale(8); // OTHER - BigDecimal alicePrice = new BigDecimal("1.5").setScale(8); // TEST/OTHER + // TEST has a lower assetId than OTHER - BigDecimal bobAmount = new BigDecimal("1234").setScale(8); // OTHER - BigDecimal bobPrice = new BigDecimal("1.2").setScale(8); // TEST/OTHER + // Alice has TEST, wants OTHER + long aliceAmount = 500_00000000L; // OTHER is 'want' asset + long alicePrice = 1_50000000L; // TEST/OTHER - BigDecimal aliceCommitment = aliceAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // TEST - BigDecimal bobCommitment = bobAmount; // OTHER + // Bob has OTHER, wants TEST + long bobAmount = 1234_00000000L; // OTHER is 'have' asset + long bobPrice = 1_20000000L; // TEST/OTHER - BigDecimal matchedAmount = aliceAmount.min(bobAmount); // 500 OTHER + long aliceCommitment = Amounts.roundUpScaledMultiply(aliceAmount, alicePrice); // TEST + long bobCommitment = bobAmount; // OTHER - BigDecimal aliceReturn = matchedAmount; // OTHER - BigDecimal bobReturn = matchedAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // TEST + long matchedAmount = Math.min(aliceAmount, bobAmount); // 500 OTHER - BigDecimal aliceRefund = BigDecimal.ZERO; // because Alice's order is fully matched - BigDecimal bobRefund = bobAmount.subtract(matchedAmount); // OTHER + long aliceReturn = matchedAmount; // OTHER + long bobReturn = Amounts.roundDownScaledMultiply(matchedAmount, alicePrice); // TEST - BigDecimal bobSaving = BigDecimal.ZERO; // not in this direction + long aliceRefund = 0L; // because Alice's order is fully matched + long bobRefund = bobAmount - matchedAmount; // OTHER + + long bobSaving = 0L; // not in this direction try (Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); + // Place 'target' order byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice); + // Place 'initiating' order: the order that initiates a trade byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.testAssetId, bobAmount, bobPrice); - assertCannotCancelClosedOrder(repository, "alice", aliceOrderId); + assertCannotCancelClosedOrder(repository, "alice", aliceOrderId); // because fully matched AssetUtils.cancelOrder(repository, "bob", bobOrderId); // Check asset balances - BigDecimal expectedBalance; + long expectedBalance; // Alice - expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId).subtract(aliceCommitment).add(aliceRefund); + expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId) - aliceCommitment + aliceRefund; AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); - expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(aliceReturn); + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId) + aliceReturn; AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); // Bob - expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(bobCommitment).add(bobSaving).add(bobRefund); + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId) - bobCommitment + bobSaving + bobRefund; AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); - expectedBalance = initialBalances.get("bob").get(AssetUtils.testAssetId).add(bobReturn); + expectedBalance = initialBalances.get("bob").get(AssetUtils.testAssetId) + bobReturn; AccountUtils.assertBalance(repository, "bob", AssetUtils.testAssetId, expectedBalance); } } @Test public void testPartialTargetMatchCancelInverted() throws DataException { - BigDecimal aliceAmount = new BigDecimal("1234").setScale(8); // GOLD - BigDecimal alicePrice = new BigDecimal("1.2").setScale(8); // OTHER/GOLD + // GOLD has a higher assetId than OTHER, hence "inverted" viz-a-viz have/want assetIds - BigDecimal bobAmount = new BigDecimal("500").setScale(8); // GOLD - BigDecimal bobPrice = new BigDecimal("1.5").setScale(8); // OTHER/GOLD + // Alice has GOLD, wants OTHER + long aliceAmount = 1234_00000000L; // GOLD is 'have' asset + long alicePrice = 1_20000000L; // OTHER/GOLD - BigDecimal aliceCommitment = aliceAmount; // GOLD - BigDecimal bobCommitment = bobAmount.multiply(bobPrice).setScale(8, RoundingMode.DOWN); // OTHER + // Bob has OTHER, wants GOLD + long bobAmount = 500_00000000L; // GOLD is 'want' asset + long bobPrice = 1_50000000L; // OTHER/GOLD - BigDecimal matchedAmount = aliceAmount.min(bobAmount); // 500 GOLD + long aliceCommitment = aliceAmount; // GOLD + long bobCommitment = Amounts.roundUpScaledMultiply(bobAmount, bobPrice); // OTHER - BigDecimal aliceReturn = matchedAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // OTHER - BigDecimal bobReturn = matchedAmount; // GOLD + long matchedAmount = Math.min(aliceAmount, bobAmount); // 500 GOLD - BigDecimal aliceRefund = aliceAmount.subtract(matchedAmount); // GOLD - BigDecimal bobRefund = BigDecimal.ZERO; // because Bob's order is fully matched + long aliceReturn = Amounts.roundDownScaledMultiply(matchedAmount, alicePrice); // OTHER + long bobReturn = matchedAmount; // GOLD - BigDecimal bobSaving = new BigDecimal("150").setScale(8); // (1.5 - 1.2) * 500 = 150 OTHER + long aliceRefund = aliceAmount - matchedAmount; // GOLD + long bobRefund = 0L; // because Bob's order is fully matched + + long bobSaving = 150_00000000L; // (1.5 - 1.2) * 500 = 150 OTHER try (Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.goldAssetId, AssetUtils.otherAssetId); + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.goldAssetId, AssetUtils.otherAssetId); byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice); byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.goldAssetId, bobAmount, bobPrice); @@ -209,47 +230,51 @@ public class CancellingTests extends Common { assertCannotCancelClosedOrder(repository, "bob", bobOrderId); // Check asset balances - BigDecimal expectedBalance; + long expectedBalance; // Alice - expectedBalance = initialBalances.get("alice").get(AssetUtils.goldAssetId).subtract(aliceCommitment).add(aliceRefund); + expectedBalance = initialBalances.get("alice").get(AssetUtils.goldAssetId) - aliceCommitment + aliceRefund; AccountUtils.assertBalance(repository, "alice", AssetUtils.goldAssetId, expectedBalance); - expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(aliceReturn); + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId) + aliceReturn; AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); // Bob - expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(bobCommitment).add(bobSaving).add(bobRefund); + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId) - bobCommitment + bobSaving + bobRefund; AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); - expectedBalance = initialBalances.get("bob").get(AssetUtils.goldAssetId).add(bobReturn); + expectedBalance = initialBalances.get("bob").get(AssetUtils.goldAssetId) + bobReturn; AccountUtils.assertBalance(repository, "bob", AssetUtils.goldAssetId, expectedBalance); } } @Test public void testPartialInitiatorMatchCancelInverted() throws DataException { - BigDecimal aliceAmount = new BigDecimal("500").setScale(8); // GOLD - BigDecimal alicePrice = new BigDecimal("1.2").setScale(8); // OTHER/GOLD + // GOLD has a higher assetId than OTHER, hence "inverted" viz-a-viz have/want assetIds - BigDecimal bobAmount = new BigDecimal("1234").setScale(8); // GOLD - BigDecimal bobPrice = new BigDecimal("1.5").setScale(8); // OTHER/GOLD + // Alice has GOLD, wants OTHER + long aliceAmount = 500_00000000L; // GOLD is 'have' asset + long alicePrice = 1_20000000L; // OTHER/GOLD - BigDecimal aliceCommitment = aliceAmount; // GOLD - BigDecimal bobCommitment = bobAmount.multiply(bobPrice).setScale(8, RoundingMode.DOWN); // OTHER + // Bob has OTHER, wants GOLD + long bobAmount = 1234_00000000L; // GOLD is 'want' asset + long bobPrice = 1_50000000L; // OTHER/GOLD - BigDecimal matchedAmount = aliceAmount.min(bobAmount); // 500 GOLD + long aliceCommitment = aliceAmount; // GOLD + long bobCommitment = Amounts.roundUpScaledMultiply(bobAmount, bobPrice); // OTHER - BigDecimal aliceReturn = matchedAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // OTHER - BigDecimal bobReturn = matchedAmount; // GOLD + long matchedAmount = Math.min(aliceAmount, bobAmount); // 500 GOLD - BigDecimal aliceRefund = BigDecimal.ZERO; // because Alice's order is fully matched - BigDecimal bobRefund = bobAmount.subtract(matchedAmount).multiply(bobPrice).setScale(8, RoundingMode.DOWN); // OTHER + long aliceReturn = Amounts.roundDownScaledMultiply(matchedAmount, alicePrice); // OTHER + long bobReturn = matchedAmount; // GOLD - BigDecimal bobSaving = new BigDecimal("150").setScale(8); // (1.5 - 1.2) * 500 = 150 OTHER + long aliceRefund = 0L; // because Alice's order is fully matched + long bobRefund = Amounts.roundUpScaledMultiply(bobAmount - matchedAmount, bobPrice); // OTHER + + long bobSaving = 150_00000000L; // (1.5 - 1.2) * 500 = 150 OTHER try (Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.goldAssetId, AssetUtils.otherAssetId); + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.goldAssetId, AssetUtils.otherAssetId); byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice); byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.goldAssetId, bobAmount, bobPrice); @@ -258,20 +283,20 @@ public class CancellingTests extends Common { AssetUtils.cancelOrder(repository, "bob", bobOrderId); // Check asset balances - BigDecimal expectedBalance; + long expectedBalance; // Alice - expectedBalance = initialBalances.get("alice").get(AssetUtils.goldAssetId).subtract(aliceCommitment).add(aliceRefund); + expectedBalance = initialBalances.get("alice").get(AssetUtils.goldAssetId) - aliceCommitment + aliceRefund; AccountUtils.assertBalance(repository, "alice", AssetUtils.goldAssetId, expectedBalance); - expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(aliceReturn); + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId) + aliceReturn; AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); // Bob - expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(bobCommitment).add(bobSaving).add(bobRefund); + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId) - bobCommitment + bobSaving + bobRefund; AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); - expectedBalance = initialBalances.get("bob").get(AssetUtils.goldAssetId).add(bobReturn); + expectedBalance = initialBalances.get("bob").get(AssetUtils.goldAssetId) + bobReturn; AccountUtils.assertBalance(repository, "bob", AssetUtils.goldAssetId, expectedBalance); } } diff --git a/src/test/java/org/qortal/test/assets/GranularityTests.java b/src/test/java/org/qortal/test/assets/GranularityTests.java index 7a6e976b..9288f3a3 100644 --- a/src/test/java/org/qortal/test/assets/GranularityTests.java +++ b/src/test/java/org/qortal/test/assets/GranularityTests.java @@ -1,7 +1,8 @@ package org.qortal.test.assets; +import static org.junit.Assert.assertEquals; + import java.math.BigDecimal; -import java.math.RoundingMode; import org.junit.After; import org.junit.Before; @@ -9,7 +10,9 @@ import org.junit.Test; import org.qortal.asset.Order; import org.qortal.repository.DataException; import org.qortal.test.common.Common; +import org.qortal.utils.Amounts; +/** Check granularity adjustment values. */ public class GranularityTests extends Common { @Before @@ -22,18 +25,18 @@ public class GranularityTests extends Common { Common.orphanCheck(); } - /** - * Check granularity adjustment values. - */ @Test - public void testGranularities() { + public void testDivisibleGranularities() { // Price 1/12 is rounded down to 0.08333333. // To keep [divisible] amount * 0.08333333 to nearest 0.00000001 then amounts need to be multiples of 1.00000000. testGranularity(true, true, "1", "12", "1"); // Any amount * 12 will be valid for divisible asset so granularity is 0.00000001 testGranularity(true, true, "12", "1", "0.00000001"); + } + @Test + public void testIndivisibleGranularities() { // Price 1/10 is 0.10000000. // To keep amount * 0.1 to nearest 1 then amounts need to be multiples of 10. testGranularity(false, false, "1", "10", "10"); @@ -41,7 +44,10 @@ public class GranularityTests extends Common { // Price is 50307/123 which is 409 // Any [indivisible] amount * 409 will be valid for divisible asset to granularity is 1 testGranularity(false, false, "50307", "123", "1"); + } + @Test + public void testMixedDivisibilityGranularities() { // Price 1/800 is 0.00125000 // Amounts are indivisible so must be integer. // Return-amounts are divisible and can be fractional. @@ -75,12 +81,19 @@ public class GranularityTests extends Common { testGranularity(true, false, "800", "1", "0.00125000"); } - private void testGranularity(boolean isAmountAssetDivisible, boolean isReturnAssetDivisible, String dividend, String divisor, String expectedGranularity) { - BigDecimal bdPrice = new BigDecimal(dividend).setScale(8).divide(new BigDecimal(divisor).setScale(8), RoundingMode.DOWN); - long price = bdPrice.unscaledValue().longValue(); + private void testGranularity(boolean isAmountAssetDivisible, boolean isReturnAssetDivisible, String dividendStr, String divisorStr, String expectedGranularityStr) { + long dividend = toUnscaledLong(dividendStr); + long divisor = toUnscaledLong(divisorStr); + long expectedGranularity = toUnscaledLong(expectedGranularityStr); - BigDecimal granularity = BigDecimal.valueOf(Order.calculateAmountGranularity(isAmountAssetDivisible, isReturnAssetDivisible, price), 8); - assertEqualBigDecimals("Granularity incorrect", new BigDecimal(expectedGranularity), granularity); + long price = Amounts.scaledDivide(dividend, divisor); + + long granularity = Order.calculateAmountGranularity(isAmountAssetDivisible, isReturnAssetDivisible, price); + assertEquals("Granularity incorrect", expectedGranularity, granularity); + } + + private long toUnscaledLong(String value) { + return new BigDecimal(value).setScale(8).unscaledValue().longValue(); } } diff --git a/src/test/java/org/qortal/test/assets/MiscTests.java b/src/test/java/org/qortal/test/assets/MiscTests.java new file mode 100644 index 00000000..6a1be14f --- /dev/null +++ b/src/test/java/org/qortal/test/assets/MiscTests.java @@ -0,0 +1,50 @@ +package org.qortal.test.assets; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.qortal.repository.DataException; +import org.qortal.test.common.Common; +import org.qortal.utils.Amounts; + +public class MiscTests extends Common { + + @Before + public void beforeTest() throws DataException { + Common.useDefaultSettings(); + } + + @After + public void afterTest() throws DataException { + Common.orphanCheck(); + } + + @Test + public void testCalcCommitmentWithRoundUp() throws DataException { + long amount = 1234_87654321L; + long price = 1_35615263L; + + // 1234.87654321 * 1.35615263 = 1674.6810717995501423 + // rounded up to 8dp gives: 1674.68107180 + long expectedCommitment = 1674_68107180L; + + long actualCommitment = Amounts.roundUpScaledMultiply(amount, price); + assertEquals(expectedCommitment, actualCommitment); + } + + @Test + public void testCalcCommitmentWithoutRoundUp() throws DataException { + long amount = 1234_87650000L; + long price = 1_35610000L; + + // 1234.87650000 * 1.35610000 = 1674.6160216500000000 + // rounded up to 8dp gives: 1674.61602165 + long expectedCommitment = 1674_61602165L; + + long actualCommitment = Amounts.roundUpScaledMultiply(amount, price); + assertEquals(expectedCommitment, actualCommitment); + } + +} diff --git a/src/test/java/org/qortal/test/assets/TradingTests.java b/src/test/java/org/qortal/test/assets/TradingTests.java index d307e41d..2df34748 100644 --- a/src/test/java/org/qortal/test/assets/TradingTests.java +++ b/src/test/java/org/qortal/test/assets/TradingTests.java @@ -10,9 +10,10 @@ import org.qortal.repository.RepositoryManager; import org.qortal.test.common.AccountUtils; import org.qortal.test.common.AssetUtils; import org.qortal.test.common.Common; +import org.qortal.utils.Amounts; + +import static org.junit.Assert.assertEquals; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.Map; public class TradingTests extends Common { @@ -29,60 +30,68 @@ public class TradingTests extends Common { @Test public void testSimple() throws DataException { - final BigDecimal goldAmount = BigDecimal.valueOf(24L).setScale(8); - final BigDecimal price = BigDecimal.valueOf(2L).setScale(8); - final BigDecimal otherAmount = BigDecimal.valueOf(48L).setScale(8); - + // GOLD has a higher assetId than OTHER // amounts are in GOLD // prices are in OTHER/GOLD + long goldAmount = 24L * Amounts.MULTIPLIER; + long price = 2L * Amounts.MULTIPLIER; - final BigDecimal aliceAmount = goldAmount; - final BigDecimal alicePrice = price; + long otherAmount = 48L * Amounts.MULTIPLIER; - final BigDecimal bobAmount = goldAmount; - final BigDecimal bobPrice = price; + long aliceAmount = goldAmount; + long alicePrice = price; - final BigDecimal aliceCommitment = goldAmount; - final BigDecimal bobCommitment = otherAmount; + long bobAmount = goldAmount; + long bobPrice = price; - final BigDecimal aliceReturn = otherAmount; - final BigDecimal bobReturn = goldAmount; + // Alice has GOLD, wants OTHER so her commitment is in GOLD + long aliceCommitment = goldAmount; + // Bob has OTHER, wants GOLD so his commitment is in OTHER + long bobCommitment = otherAmount; + + long aliceReturn = otherAmount; + long bobReturn = goldAmount; // alice (target) order: have 'goldAmount' GOLD, want OTHER @ 'price' OTHER/GOLD (commits goldAmount GOLD) // bob (initiating) order: have OTHER, want 'goldAmount' GOLD @ 'price' OTHER/GOLD (commits goldAmount*price = otherAmount OTHER) // Alice should be -goldAmount, +otherAmount // Bob should be -otherAmount, +goldAmount - AssetUtils.genericTradeTest(AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + long bobSaving = 0L; // no price improvement + + AssetUtils.genericTradeTest(AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } @Test public void testSimpleInverted() throws DataException { - final BigDecimal testAmount = BigDecimal.valueOf(48L).setScale(8); - final BigDecimal price = BigDecimal.valueOf(2L).setScale(8); - final BigDecimal otherAmount = BigDecimal.valueOf(24L).setScale(8); - + // TEST has a lower assetId than OTHER, so this is 'inverted' viz-a-viz have/want assetIds, compared with testSimple() above // amounts are in OTHER // prices are in TEST/OTHER + long testAmount = 48L * Amounts.MULTIPLIER; + long price = 2L * Amounts.MULTIPLIER; - final BigDecimal aliceAmount = otherAmount; - final BigDecimal alicePrice = price; + long otherAmount = 24L * Amounts.MULTIPLIER; - final BigDecimal bobAmount = otherAmount; - final BigDecimal bobPrice = price; + long aliceAmount = otherAmount; + long alicePrice = price; - final BigDecimal aliceCommitment = testAmount; - final BigDecimal bobCommitment = otherAmount; + long bobAmount = otherAmount; + long bobPrice = price; - final BigDecimal aliceReturn = otherAmount; - final BigDecimal bobReturn = testAmount; + long aliceCommitment = testAmount; + long bobCommitment = otherAmount; + + long aliceReturn = otherAmount; + long bobReturn = testAmount; // alice (target) order: have TEST, want 'otherAmount' OTHER @ 'price' TEST/OTHER (commits otherAmount*price = testAmount TEST) // bob (initiating) order: have 'otherAmount' OTHER, want TEST @ 'price' TEST/OTHER (commits otherAmount OTHER) // Alice should be -testAmount, +otherAmount // Bob should be -otherAmount, +testAmount - AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + long bobSaving = 0L; // no price improvement + + AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } /** @@ -90,32 +99,34 @@ public class TradingTests extends Common { */ @Test public void testMixedDivisibility() throws DataException { - // Issue indivisible asset + // Issue indivisible asset, which will have higher assetId than anything else, so amounts will be in INDIV long indivAssetId; try (Repository repository = RepositoryManager.getRepository()) { - indivAssetId = AssetUtils.issueAsset(repository, "alice", "INDIV", 100000000L, false); + indivAssetId = AssetUtils.issueAsset(repository, "alice", "INDIV", 1000000_00000000L, false); } - final BigDecimal indivAmount = BigDecimal.valueOf(2L).setScale(8); - final BigDecimal otherAmount = BigDecimal.valueOf(24L).setScale(8); - final BigDecimal price = BigDecimal.valueOf(12L).setScale(8); - // amounts are in INDIV // prices are in OTHER/INDIV + long indivAmount = 2L * Amounts.MULTIPLIER; + long price = 12L * Amounts.MULTIPLIER; - final BigDecimal aliceAmount = indivAmount; - final BigDecimal alicePrice = price; + long otherAmount = 24L * Amounts.MULTIPLIER; - final BigDecimal bobAmount = indivAmount; - final BigDecimal bobPrice = price; + long aliceAmount = indivAmount; + long alicePrice = price; - final BigDecimal aliceCommitment = indivAmount; - final BigDecimal bobCommitment = otherAmount; + long bobAmount = indivAmount; + long bobPrice = price; - final BigDecimal aliceReturn = otherAmount; - final BigDecimal bobReturn = indivAmount; + long aliceCommitment = indivAmount; + long bobCommitment = otherAmount; - AssetUtils.genericTradeTest(indivAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + long aliceReturn = otherAmount; + long bobReturn = indivAmount; + + long bobSaving = 0L; // no price improvement + + AssetUtils.genericTradeTest(indivAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } /** @@ -123,32 +134,34 @@ public class TradingTests extends Common { */ @Test public void testMixedDivisibilityInverted() throws DataException { - // Issue indivisible asset + // Issue indivisible asset, which will have higher assetId than anything else, so amounts will be in INDIV long indivAssetId; try (Repository repository = RepositoryManager.getRepository()) { - indivAssetId = AssetUtils.issueAsset(repository, "bob", "INDIV", 100000000L, false); + indivAssetId = AssetUtils.issueAsset(repository, "bob", "INDIV", 1000000_00000000L, false); } - final BigDecimal indivAmount = BigDecimal.valueOf(2L).setScale(8); - final BigDecimal testAmount = BigDecimal.valueOf(24L).setScale(8); - final BigDecimal price = BigDecimal.valueOf(12L).setScale(8); - // amounts are in INDIV // prices are in TEST/INDIV + long indivAmount = 2L * Amounts.MULTIPLIER; + long price = 12L * Amounts.MULTIPLIER; - final BigDecimal aliceAmount = indivAmount; - final BigDecimal alicePrice = price; + long testAmount = 24L * Amounts.MULTIPLIER; - final BigDecimal bobAmount = indivAmount; - final BigDecimal bobPrice = price; + long aliceAmount = indivAmount; + long alicePrice = price; - final BigDecimal aliceCommitment = testAmount; - final BigDecimal bobCommitment = indivAmount; + long bobAmount = indivAmount; + long bobPrice = price; - final BigDecimal aliceReturn = indivAmount; - final BigDecimal bobReturn = testAmount; + long aliceCommitment = testAmount; + long bobCommitment = indivAmount; - AssetUtils.genericTradeTest(AssetUtils.testAssetId, indivAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + long aliceReturn = indivAmount; + long bobReturn = testAmount; + + long bobSaving = 0L; // no price improvement + + AssetUtils.genericTradeTest(AssetUtils.testAssetId, indivAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } /** @@ -168,32 +181,35 @@ public class TradingTests extends Common { long richesAssetId; try (Repository repository = RepositoryManager.getRepository()) { // Issue indivisible asset - ragsAssetId = AssetUtils.issueAsset(repository, "alice", "rags", 1000000L, false); + ragsAssetId = AssetUtils.issueAsset(repository, "alice", "rags", 1000000_00000000L, false); // Issue another indivisible asset - richesAssetId = AssetUtils.issueAsset(repository, "bob", "riches", 1000000L, false); + richesAssetId = AssetUtils.issueAsset(repository, "bob", "riches", 1000000_00000000L, false); } // "amount" will be in riches, "price" will be in rags/riches - final BigDecimal ragsAmount = BigDecimal.valueOf(50307L).setScale(8); - final BigDecimal richesAmount = BigDecimal.valueOf(123L).setScale(8); + long ragsAmount = 50307_00000000L; + long richesAmount = 123_00000000L; - final BigDecimal price = ragsAmount.divide(richesAmount, RoundingMode.DOWN); - final BigDecimal two = BigDecimal.valueOf(2L); + long price = Amounts.scaledDivide(ragsAmount, richesAmount); - final BigDecimal aliceAmount = richesAmount.multiply(two).setScale(8); - final BigDecimal alicePrice = price; - final BigDecimal aliceCommitment = aliceAmount.multiply(alicePrice).setScale(8); // rags + long aliceAmount = richesAmount * 2; + long alicePrice = price; + long aliceCommitment = Amounts.roundUpScaledMultiply(aliceAmount, alicePrice); // rags - final BigDecimal bobAmount = richesAmount; - final BigDecimal bobPrice = price; - final BigDecimal bobCommitment = bobAmount; // riches + long bobAmount = richesAmount; + long bobPrice = price; + long bobCommitment = bobAmount; // riches - final BigDecimal aliceReturn = bobAmount; // riches - final BigDecimal bobReturn = bobAmount.multiply(alicePrice).setScale(8); // rags + long matchedAmount = Math.min(aliceAmount, bobAmount); - AssetUtils.genericTradeTest(ragsAssetId, richesAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + long aliceReturn = bobAmount; // riches + long bobReturn = Amounts.roundDownScaledMultiply(matchedAmount, alicePrice); // rags + + long bobSaving = 0L; // no price improvement + + AssetUtils.genericTradeTest(ragsAssetId, richesAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } /** @@ -239,28 +255,32 @@ public class TradingTests extends Common { long richesAssetId; try (Repository repository = RepositoryManager.getRepository()) { // Issue indivisible asset - ragsAssetId = AssetUtils.issueAsset(repository, "alice", "rags", 1000000L, false); + ragsAssetId = AssetUtils.issueAsset(repository, "alice", "rags", 1000000_00000000L, false); // Issue another indivisible asset - richesAssetId = AssetUtils.issueAsset(repository, "bob", "riches", 1000000L, false); + richesAssetId = AssetUtils.issueAsset(repository, "bob", "riches", 1000000_00000000L, false); } // "amount" will be in riches, "price" will be in rags/riches // Buying 3 riches @ 1 rags/riches max, so expecting to pay 3 rags max - final BigDecimal aliceAmount = new BigDecimal("3").setScale(8); - final BigDecimal alicePrice = new BigDecimal("1").setScale(8); - final BigDecimal aliceCommitment = aliceAmount.multiply(alicePrice).setScale(8); // rags + long aliceAmount = 3_00000000L; + long alicePrice = 1_00000000L; + long aliceCommitment = Amounts.roundUpScaledMultiply(aliceAmount, alicePrice); // rags // Selling 8 riches @ 0.25 rags/riches min, so expecting 2 rags min - final BigDecimal bobAmount = new BigDecimal("8").setScale(8); - final BigDecimal bobPrice = new BigDecimal("0.25").setScale(8); - final BigDecimal bobCommitment = bobAmount; // riches + long bobAmount = 8_00000000L; + long bobPrice = 25000000L; + long bobCommitment = bobAmount; // riches - final BigDecimal aliceReturn = aliceAmount; // riches - final BigDecimal bobReturn = aliceAmount.multiply(alicePrice).setScale(8); + long matchedAmount = Math.min(aliceAmount, bobAmount); - AssetUtils.genericTradeTest(ragsAssetId, richesAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + long aliceReturn = aliceAmount; // riches + long bobReturn = Amounts.roundDownScaledMultiply(matchedAmount, alicePrice); + + long bobSaving = 0L; // no price improvement + + AssetUtils.genericTradeTest(ragsAssetId, richesAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } /** @@ -278,19 +298,25 @@ public class TradingTests extends Common { */ @Test public void testNonExactFraction() throws DataException { - final BigDecimal aliceAmount = new BigDecimal("24.00000000").setScale(8); // OTHER - final BigDecimal alicePrice = new BigDecimal("0.08333333").setScale(8); // TEST/OTHER - final BigDecimal aliceCommitment = new BigDecimal("1.99999992").setScale(8); // 24 * 0.08333333 = 1.99999992 TEST + // TEST has a lower assetId than OTHER + // amounts are in OTHER + // prices are in TEST/OTHER - final BigDecimal bobAmount = new BigDecimal("24.00000000").setScale(8); // OTHER - final BigDecimal bobPrice = new BigDecimal("0.08333333").setScale(8); // TEST/OTHER - final BigDecimal bobCommitment = new BigDecimal("24.00000000").setScale(8); // OTHER + long aliceAmount = 24_00000000L; // OTHER + long alicePrice = 8333333L; // TEST/OTHER + long aliceCommitment = 1_99999992L; // 24 * 0.08333333 = 1.99999992 TEST + + long bobAmount = 24_00000000L; // OTHER + long bobPrice = 8333333L; // TEST/OTHER + long bobCommitment = 24_00000000L; // OTHER // Expected traded amounts - final BigDecimal aliceReturn = new BigDecimal("24.00000000").setScale(8); // OTHER - final BigDecimal bobReturn = new BigDecimal("1.99999992").setScale(8); // TEST + long aliceReturn = 24_00000000L; // OTHER + long bobReturn = 1_99999992L; // TEST - AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + long bobSaving = 0L; // no price improvement + + AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } /** @@ -298,21 +324,27 @@ public class TradingTests extends Common { */ @Test public void testSimplePriceImprovement() throws DataException { + // TEST has a lower assetId than OTHER + // amounts are in OTHER + // prices are in TEST/OTHER + // Alice is buying OTHER - final BigDecimal aliceAmount = new BigDecimal("100").setScale(8); // OTHER - final BigDecimal alicePrice = new BigDecimal("0.3").setScale(8); // TEST/OTHER - final BigDecimal aliceCommitment = new BigDecimal("30").setScale(8); // 100 * 0.3 = 30 TEST + long aliceAmount = 100_00000000L; // OTHER + long alicePrice = 30000000L; // TEST/OTHER + long aliceCommitment = 30_00000000L; // 100 * 0.3 = 30 TEST // Bob is selling OTHER - final BigDecimal bobAmount = new BigDecimal("100").setScale(8); // OTHER - final BigDecimal bobPrice = new BigDecimal("0.2").setScale(8); // TEST/OTHER - final BigDecimal bobCommitment = new BigDecimal("100").setScale(8); // OTHER + long bobAmount = 100_00000000L; // OTHER + long bobPrice = 20000000L; // TEST/OTHER + long bobCommitment = 100_00000000L; // OTHER // Expected traded amounts - final BigDecimal aliceReturn = new BigDecimal("100").setScale(8); // OTHER - final BigDecimal bobReturn = new BigDecimal("30").setScale(8); // TEST + long aliceReturn = 100_00000000L; // OTHER + long bobReturn = 30_00000000L; // 100 * 0.3 = 30 TEST (Alice's price) - AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + long bobSaving = 0L; // No price improvement for Bob + + AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } /** @@ -320,20 +352,25 @@ public class TradingTests extends Common { */ @Test public void testSimplePriceImprovementInverted() throws DataException { - // Alice is seller GOLD - final BigDecimal aliceAmount = new BigDecimal("100").setScale(8); // GOLD - final BigDecimal alicePrice = new BigDecimal("2").setScale(8); // OTHER/GOLD - final BigDecimal aliceCommitment = new BigDecimal("100").setScale(8); // GOLD + // GOLD has a higher assetId than OTHER + // amounts are in GOLD + // prices are in OTHER/GOLD + + // Alice is selling GOLD + long aliceAmount = 100_00000000L; // GOLD + long alicePrice = 2_00000000L; // OTHER/GOLD + long aliceCommitment = 100_00000000L; // GOLD // Bob is buying GOLD - final BigDecimal bobAmount = new BigDecimal("50").setScale(8); // GOLD - final BigDecimal bobPrice = new BigDecimal("3").setScale(8); // OTHER/GOLD - final BigDecimal bobCommitment = new BigDecimal("150").setScale(8); // 50 * 3 = 150 OTHER + long bobAmount = 50_00000000L; // GOLD + long bobPrice = 3_00000000L; // OTHER/GOLD + long bobCommitment = 150_00000000L; // 50 * 3 = 150 OTHER // Expected traded amounts - final BigDecimal aliceReturn = new BigDecimal("100").setScale(8); // 50 * 2 = 100 OTHER - final BigDecimal bobReturn = new BigDecimal("50").setScale(8); // 50 GOLD - final BigDecimal bobSaving = new BigDecimal("50").setScale(8); // 50 * (3 - 2) = 50 OTHER + long aliceReturn = 100_00000000L; // 50 * 2 = 100 OTHER + long bobReturn = 50_00000000L; // 50 GOLD + + long bobSaving = 50_00000000L; // 50 * (3 - 2) = 50 OTHER AssetUtils.genericTradeTest(AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } @@ -343,24 +380,25 @@ public class TradingTests extends Common { */ @Test public void testPriceImprovement() throws DataException { + // GOLD has a higher assetId than OTHER // Amounts are in GOLD // Prices are in OTHER/GOLD - final BigDecimal initialGoldAssetAmount = new BigDecimal("24.00000000").setScale(8); + long initialGoldAssetAmount = 24_00000000L; - final BigDecimal basePrice = new BigDecimal("1.00000000").setScale(8); - final BigDecimal betterPrice = new BigDecimal("2.10000000").setScale(8); - final BigDecimal bestPrice = new BigDecimal("2.40000000").setScale(8); + long basePrice = 1_00000000L; + long betterPrice = 2_10000000L; + long bestPrice = 2_40000000L; - final BigDecimal minimalPrice = new BigDecimal("1.5000000").setScale(8); - final BigDecimal matchingGoldAssetAmount = new BigDecimal("12.00000000").setScale(8); + long midwayPrice = 1_5000000L; + long matchingGoldAssetAmount = 12_00000000L; try (Repository repository = RepositoryManager.getRepository()) { // Give some OTHER to Chloe and Dilbert - AssetUtils.transferAsset(repository, "bob", "chloe", AssetUtils.otherAssetId, BigDecimal.valueOf(1000L).setScale(8)); - AssetUtils.transferAsset(repository, "bob", "dilbert", AssetUtils.otherAssetId, BigDecimal.valueOf(1000L).setScale(8)); + AssetUtils.transferAsset(repository, "bob", "chloe", AssetUtils.otherAssetId, 1000_00000000L); + AssetUtils.transferAsset(repository, "bob", "dilbert", AssetUtils.otherAssetId, 1000_00000000L); - Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.otherAssetId, AssetUtils.goldAssetId); + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.otherAssetId, AssetUtils.goldAssetId); // Create 'better' initial order: buying GOLD @ betterPrice byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.goldAssetId, initialGoldAssetAmount, betterPrice); @@ -371,29 +409,32 @@ public class TradingTests extends Common { // Create 'base' initial order: buying GOLD @ basePrice (shouldn't even match) byte[] dilbertOrderId = AssetUtils.createOrder(repository, "dilbert", AssetUtils.otherAssetId, AssetUtils.goldAssetId, initialGoldAssetAmount, basePrice); - // Create matching order: selling GOLD @ minimalPrice which would match at least one buy order - byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.goldAssetId, AssetUtils.otherAssetId, matchingGoldAssetAmount, minimalPrice); + // Create matching order: selling GOLD @ midwayPrice which would match at least one buy order + byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.goldAssetId, AssetUtils.otherAssetId, matchingGoldAssetAmount, midwayPrice); // Check balances to check expected outcome - BigDecimal expectedBalance; + long expectedBalance; // We're expecting Alice's order to match with Chloe's order (as Bob's and Dilberts's orders have worse prices) - BigDecimal matchedOtherAmount = matchingGoldAssetAmount.multiply(bestPrice).setScale(8, RoundingMode.DOWN); - BigDecimal tradedGoldAssetAmount = matchingGoldAssetAmount; + long matchedOtherAmount = Amounts.roundDownScaledMultiply(matchingGoldAssetAmount, bestPrice); + long tradedGoldAssetAmount = matchingGoldAssetAmount; + + // XXX surely either "market maker" (i.e. target order) should receive benefit, or Alice should receive a partial refund? // NO refund due to price improvement - Alice receives more OTHER back than she was expecting - BigDecimal aliceSaving = BigDecimal.ZERO; + long aliceSaving = 0L; // Alice GOLD - BigDecimal aliceCommitment = matchingGoldAssetAmount; - expectedBalance = initialBalances.get("alice").get(AssetUtils.goldAssetId).subtract(aliceCommitment).add(aliceSaving); + long aliceCommitment = matchingGoldAssetAmount; + expectedBalance = initialBalances.get("alice").get(AssetUtils.goldAssetId) - aliceCommitment + aliceSaving; AccountUtils.assertBalance(repository, "alice", AssetUtils.goldAssetId, expectedBalance); // Alice OTHER - expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(matchedOtherAmount); + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId) + matchedOtherAmount; AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); // Bob OTHER - expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(initialGoldAssetAmount.multiply(betterPrice).setScale(8, RoundingMode.DOWN)); + long bobCommitment = Amounts.roundDownScaledMultiply(initialGoldAssetAmount, betterPrice); + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId) - bobCommitment; AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); // Bob GOLD @@ -401,15 +442,17 @@ public class TradingTests extends Common { AccountUtils.assertBalance(repository, "bob", AssetUtils.goldAssetId, expectedBalance); // Chloe OTHER - expectedBalance = initialBalances.get("chloe").get(AssetUtils.otherAssetId).subtract(initialGoldAssetAmount.multiply(bestPrice).setScale(8, RoundingMode.DOWN)); + long chloeCommitment = Amounts.roundDownScaledMultiply(initialGoldAssetAmount, bestPrice); + expectedBalance = initialBalances.get("chloe").get(AssetUtils.otherAssetId) - chloeCommitment; AccountUtils.assertBalance(repository, "chloe", AssetUtils.otherAssetId, expectedBalance); // Chloe GOLD - expectedBalance = initialBalances.get("chloe").get(AssetUtils.goldAssetId).add(tradedGoldAssetAmount); + expectedBalance = initialBalances.get("chloe").get(AssetUtils.goldAssetId) + tradedGoldAssetAmount; // Alice traded with Chloe AccountUtils.assertBalance(repository, "chloe", AssetUtils.goldAssetId, expectedBalance); // Dilbert OTHER - expectedBalance = initialBalances.get("dilbert").get(AssetUtils.otherAssetId).subtract(initialGoldAssetAmount.multiply(basePrice).setScale(8, RoundingMode.DOWN)); + long dilbertCommitment = Amounts.roundDownScaledMultiply(initialGoldAssetAmount, basePrice); + expectedBalance = initialBalances.get("dilbert").get(AssetUtils.otherAssetId) - dilbertCommitment; AccountUtils.assertBalance(repository, "dilbert", AssetUtils.otherAssetId, expectedBalance); // Dilbert GOLD @@ -423,16 +466,16 @@ public class TradingTests extends Common { OrderData dilbertOrderData = repository.getAssetRepository().fromOrderId(dilbertOrderId); // Alice's fulfilled - Common.assertEqualBigDecimals("Alice's order's fulfilled amount incorrect", tradedGoldAssetAmount, aliceOrderData.getFulfilled()); + assertEquals("Alice's order's fulfilled amount incorrect", tradedGoldAssetAmount, aliceOrderData.getFulfilled()); // Bob's fulfilled should be zero - Common.assertEqualBigDecimals("Bob's order should be totally unfulfilled", BigDecimal.ZERO, bobOrderData.getFulfilled()); + assertEquals("Bob's order should be totally unfulfilled", 0L, bobOrderData.getFulfilled()); // Chloe's fulfilled - Common.assertEqualBigDecimals("Chloe's order's fulfilled amount incorrect", tradedGoldAssetAmount, chloeOrderData.getFulfilled()); + assertEquals("Chloe's order's fulfilled amount incorrect", tradedGoldAssetAmount, chloeOrderData.getFulfilled()); // Dilbert's fulfilled should be zero - Common.assertEqualBigDecimals("Dilbert's order should be totally unfulfilled", BigDecimal.ZERO, dilbertOrderData.getFulfilled()); + assertEquals("Dilbert's order should be totally unfulfilled", 0L, dilbertOrderData.getFulfilled()); } } @@ -444,21 +487,21 @@ public class TradingTests extends Common { // Amounts are in OTHER // Prices are in TEST/OTHER - final BigDecimal initialOtherAmount = new BigDecimal("24.00000000").setScale(8); + long initialOtherAmount = 24_00000000L; - final BigDecimal basePrice = new BigDecimal("3.00000000").setScale(8); - final BigDecimal betterPrice = new BigDecimal("2.10000000").setScale(8); - final BigDecimal bestPrice = new BigDecimal("1.40000000").setScale(8); + long basePrice = 3_00000000L; + long betterPrice = 2_10000000L; + long bestPrice = 1_40000000L; - final BigDecimal maximalPrice = new BigDecimal("2.5000000").setScale(8); - final BigDecimal aliceOtherAmount = new BigDecimal("12.00000000").setScale(8); + long midwayPrice = 2_50000000L; + long aliceOtherAmount = 12_00000000L; try (Repository repository = RepositoryManager.getRepository()) { // Give some OTHER to Chloe and Dilbert - AssetUtils.transferAsset(repository, "bob", "chloe", AssetUtils.otherAssetId, BigDecimal.valueOf(1000L).setScale(8)); - AssetUtils.transferAsset(repository, "bob", "dilbert", AssetUtils.otherAssetId, BigDecimal.valueOf(1000L).setScale(8)); + AssetUtils.transferAsset(repository, "bob", "chloe", AssetUtils.otherAssetId, 1000_00000000L); + AssetUtils.transferAsset(repository, "bob", "dilbert", AssetUtils.otherAssetId, 1000_00000000L); - Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); // Create 'better' initial order: selling OTHER @ betterPrice byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.testAssetId, initialOtherAmount, betterPrice); @@ -469,29 +512,29 @@ public class TradingTests extends Common { // Create 'base' initial order: selling OTHER @ basePrice (shouldn't even match) byte[] dilbertOrderId = AssetUtils.createOrder(repository, "dilbert", AssetUtils.otherAssetId, AssetUtils.testAssetId, initialOtherAmount, basePrice); - // Create matching order: buying OTHER @ maximalPrice which would match at least one sell order - byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceOtherAmount, maximalPrice); + // Create matching order: buying OTHER @ midwayPrice which would match at least one sell order + byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceOtherAmount, midwayPrice); // Check balances to check expected outcome - BigDecimal expectedBalance; + long expectedBalance; // We're expecting Alice's order to match with Chloe's order (as Bob's and Dilberts's orders have worse prices) - BigDecimal matchedOtherAmount = aliceOtherAmount; - BigDecimal tradedTestAmount = aliceOtherAmount.multiply(bestPrice).setScale(8, RoundingMode.DOWN); + long matchedOtherAmount = aliceOtherAmount; + long tradedTestAmount = Amounts.roundDownScaledMultiply(aliceOtherAmount, bestPrice); // Due to price improvement, Alice should get back some of her TEST - BigDecimal aliceSaving = maximalPrice.subtract(bestPrice).abs().multiply(matchedOtherAmount).setScale(8, RoundingMode.DOWN); + long aliceSaving = Amounts.roundUpScaledMultiply(matchedOtherAmount, Math.abs(midwayPrice - bestPrice)); // Alice TEST - BigDecimal aliceCommitment = aliceOtherAmount.multiply(maximalPrice).setScale(8, RoundingMode.DOWN); - expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId).subtract(aliceCommitment).add(aliceSaving); + long aliceCommitment = Amounts.roundUpScaledMultiply(aliceOtherAmount, midwayPrice); + expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId) - aliceCommitment + aliceSaving; AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); // Alice OTHER - expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(matchedOtherAmount); + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId) + matchedOtherAmount; // traded with Chloe AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); // Bob OTHER - expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(initialOtherAmount); + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId) - initialOtherAmount; AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); // Bob TEST @@ -499,15 +542,15 @@ public class TradingTests extends Common { AccountUtils.assertBalance(repository, "bob", AssetUtils.testAssetId, expectedBalance); // Chloe OTHER - expectedBalance = initialBalances.get("chloe").get(AssetUtils.otherAssetId).subtract(initialOtherAmount); + expectedBalance = initialBalances.get("chloe").get(AssetUtils.otherAssetId) - initialOtherAmount; AccountUtils.assertBalance(repository, "chloe", AssetUtils.otherAssetId, expectedBalance); // Chloe TEST - expectedBalance = initialBalances.get("chloe").get(AssetUtils.testAssetId).add(tradedTestAmount); + expectedBalance = initialBalances.get("chloe").get(AssetUtils.testAssetId) + tradedTestAmount; // traded with Alice AccountUtils.assertBalance(repository, "chloe", AssetUtils.testAssetId, expectedBalance); // Dilbert OTHER - expectedBalance = initialBalances.get("dilbert").get(AssetUtils.otherAssetId).subtract(initialOtherAmount); + expectedBalance = initialBalances.get("dilbert").get(AssetUtils.otherAssetId) - initialOtherAmount; AccountUtils.assertBalance(repository, "dilbert", AssetUtils.otherAssetId, expectedBalance); // Dilbert TEST @@ -521,16 +564,16 @@ public class TradingTests extends Common { OrderData dilbertOrderData = repository.getAssetRepository().fromOrderId(dilbertOrderId); // Alice's fulfilled - Common.assertEqualBigDecimals("Alice's order's fulfilled amount incorrect", matchedOtherAmount, aliceOrderData.getFulfilled()); + assertEquals("Alice's order's fulfilled amount incorrect", matchedOtherAmount, aliceOrderData.getFulfilled()); // Bob's fulfilled should be zero - Common.assertEqualBigDecimals("Bob's order should be totally unfulfilled", BigDecimal.ZERO, bobOrderData.getFulfilled()); + assertEquals("Bob's order should be totally unfulfilled", 0L, bobOrderData.getFulfilled()); // Chloe's fulfilled - Common.assertEqualBigDecimals("Chloe's order's fulfilled amount incorrect", matchedOtherAmount, chloeOrderData.getFulfilled()); + assertEquals("Chloe's order's fulfilled amount incorrect", matchedOtherAmount, chloeOrderData.getFulfilled()); // Dilbert's fulfilled should be zero - Common.assertEqualBigDecimals("Dilbert's order should be totally unfulfilled", BigDecimal.ZERO, dilbertOrderData.getFulfilled()); + assertEquals("Dilbert's order should be totally unfulfilled", 0L, dilbertOrderData.getFulfilled()); } } @@ -545,21 +588,22 @@ public class TradingTests extends Common { // prices are in OTHER/GOLD // Selling 10 GOLD @ 2 OTHER/GOLD min so wants 20 OTHER minimum - final BigDecimal aliceAmount = new BigDecimal("10").setScale(8); - final BigDecimal alicePrice = new BigDecimal("2").setScale(8); + long aliceAmount = 10_00000000L; + long alicePrice = 2_00000000L; // Buying 10 GOLD @ 1 OTHER/GOLD max, paying 10 OTHER maximum - final BigDecimal bobAmount = new BigDecimal("10").setScale(8); - final BigDecimal bobPrice = new BigDecimal("1").setScale(8); + long bobAmount = 10_00000000L; + long bobPrice = 1_00000000L; - final BigDecimal aliceCommitment = new BigDecimal("10").setScale(8); // 10 GOLD - final BigDecimal bobCommitment = new BigDecimal("10").setScale(8); // 10 GOLD * 1 OTHER/GOLD = 10 OTHER + long aliceCommitment = 10_00000000L; // 10 GOLD + long bobCommitment = 10_00000000L; // 10 GOLD * 1 OTHER/GOLD = 10 OTHER // Orders should not match! - final BigDecimal aliceReturn = BigDecimal.ZERO; - final BigDecimal bobReturn = BigDecimal.ZERO; + long aliceReturn = 0L; + long bobReturn = 0L; + long bobSaving = 0L; - AssetUtils.genericTradeTest(AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } /** @@ -573,21 +617,22 @@ public class TradingTests extends Common { // prices are in TEST/OTHER // Buying 10 OTHER @ 1 TEST/OTHER max, paying 10 TEST maximum - final BigDecimal aliceAmount = new BigDecimal("10").setScale(8); - final BigDecimal alicePrice = new BigDecimal("1").setScale(8); + long aliceAmount = 10_00000000L; + long alicePrice = 1_00000000L; // Selling 10 OTHER @ 2 TEST/OTHER min, so wants 20 TEST minimum - final BigDecimal bobAmount = new BigDecimal("10").setScale(8); // OTHER - final BigDecimal bobPrice = new BigDecimal("2").setScale(8); + long bobAmount = 10_00000000L; // OTHER + long bobPrice = 2_00000000L; - final BigDecimal aliceCommitment = new BigDecimal("10").setScale(8); // 10 OTHER * 1 TEST/OTHER = 10 TEST - final BigDecimal bobCommitment = new BigDecimal("10").setScale(8); // 10 OTHER + long aliceCommitment = 10_00000000L; // 10 OTHER * 1 TEST/OTHER = 10 TEST + long bobCommitment = 10_00000000L; // 10 OTHER // Orders should not match! - final BigDecimal aliceReturn = BigDecimal.ZERO; - final BigDecimal bobReturn = BigDecimal.ZERO; + long aliceReturn = 0L; + long bobReturn = 0L; + long bobSaving = 0L; - AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } -} \ No newline at end of file +} diff --git a/src/test/java/org/qortal/test/common/AccountUtils.java b/src/test/java/org/qortal/test/common/AccountUtils.java index 4bd45b4c..59722ae1 100644 --- a/src/test/java/org/qortal/test/common/AccountUtils.java +++ b/src/test/java/org/qortal/test/common/AccountUtils.java @@ -13,11 +13,12 @@ import org.qortal.data.transaction.TransactionData; import org.qortal.group.Group; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class AccountUtils { public static final int txGroupId = Group.NO_GROUP; - public static final long fee = 1L; + public static final long fee = 1L * Amounts.MULTIPLIER; public static void pay(Repository repository, String sender, String recipient, long amount) throws DataException { PrivateKeyAccount sendingAccount = Common.getTestAccount(repository, sender); diff --git a/src/test/java/org/qortal/test/common/AssetUtils.java b/src/test/java/org/qortal/test/common/AssetUtils.java index 251651d6..92918169 100644 --- a/src/test/java/org/qortal/test/common/AssetUtils.java +++ b/src/test/java/org/qortal/test/common/AssetUtils.java @@ -19,6 +19,7 @@ import org.qortal.repository.DataException; import org.qortal.repository.Repository; import org.qortal.repository.RepositoryManager; import org.qortal.transaction.Transaction; +import org.qortal.utils.Amounts; import java.util.Map; import java.util.Random; @@ -26,7 +27,7 @@ import java.util.Random; public class AssetUtils { public static final int txGroupId = Group.NO_GROUP; - public static final long fee = 1L; + public static final long fee = 1L * Amounts.MULTIPLIER; // QORT: 0, LEGACY_QORA: 1, QORT_FROM_QORA: 2 public static final long testAssetId = 3L; // Owned by Alice diff --git a/src/test/java/org/qortal/test/common/GroupUtils.java b/src/test/java/org/qortal/test/common/GroupUtils.java index b7240096..a026b605 100644 --- a/src/test/java/org/qortal/test/common/GroupUtils.java +++ b/src/test/java/org/qortal/test/common/GroupUtils.java @@ -11,11 +11,12 @@ import org.qortal.group.Group.ApprovalThreshold; import org.qortal.repository.DataException; import org.qortal.repository.Repository; import org.qortal.transaction.Transaction.ApprovalStatus; +import org.qortal.utils.Amounts; public class GroupUtils { public static final int txGroupId = Group.NO_GROUP; - public static final long fee = 1L; + public static final long fee = 1L * Amounts.MULTIPLIER; public static int createGroup(Repository repository, String creatorAccountName, String groupName, boolean isOpen, ApprovalThreshold approvalThreshold, int minimumBlockDelay, int maximumBlockDelay) throws DataException { diff --git a/src/test/java/org/qortal/test/common/transaction/ArbitraryTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/ArbitraryTestTransaction.java index 6be80575..0b48748d 100644 --- a/src/test/java/org/qortal/test/common/transaction/ArbitraryTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/ArbitraryTestTransaction.java @@ -1,6 +1,5 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -12,6 +11,7 @@ import org.qortal.data.transaction.TransactionData; import org.qortal.data.transaction.ArbitraryTransactionData.DataType; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class ArbitraryTestTransaction extends TestTransaction { @@ -26,7 +26,7 @@ public class ArbitraryTestTransaction extends TestTransaction { String recipient = account.getAddress(); final long assetId = Asset.QORT; - BigDecimal amount = BigDecimal.valueOf(123L); + long amount = 123L * Amounts.MULTIPLIER; List payments = new ArrayList<>(); payments.add(new PaymentData(recipient, assetId, amount)); diff --git a/src/test/java/org/qortal/test/common/transaction/AtTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/AtTestTransaction.java index c0daf668..74216f2f 100644 --- a/src/test/java/org/qortal/test/common/transaction/AtTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/AtTestTransaction.java @@ -1,7 +1,5 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; - import org.qortal.account.PrivateKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; @@ -9,6 +7,7 @@ import org.qortal.data.transaction.ATTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class AtTestTransaction extends TestTransaction { @@ -17,7 +16,7 @@ public class AtTestTransaction extends TestTransaction { random.nextBytes(signature); String atAddress = Crypto.toATAddress(signature); String recipient = account.getAddress(); - BigDecimal amount = BigDecimal.valueOf(123); + long amount = 123L * Amounts.MULTIPLIER; final long assetId = Asset.QORT; byte[] message = new byte[32]; random.nextBytes(message); diff --git a/src/test/java/org/qortal/test/common/transaction/BuyNameTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/BuyNameTestTransaction.java index bf69fc86..9588b605 100644 --- a/src/test/java/org/qortal/test/common/transaction/BuyNameTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/BuyNameTestTransaction.java @@ -1,12 +1,11 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; - import org.qortal.account.PrivateKeyAccount; import org.qortal.data.transaction.BuyNameTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class BuyNameTestTransaction extends TestTransaction { @@ -15,7 +14,7 @@ public class BuyNameTestTransaction extends TestTransaction { if (!wantValid) name += " " + random.nextInt(1_000_000); - BigDecimal amount = BigDecimal.valueOf(123); + long amount = 123L * Amounts.MULTIPLIER; String seller = account.getAddress(); return new BuyNameTransactionData(generateBase(account), name, amount, seller); diff --git a/src/test/java/org/qortal/test/common/transaction/CreateAssetOrderTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/CreateAssetOrderTestTransaction.java index f15079e4..382567fe 100644 --- a/src/test/java/org/qortal/test/common/transaction/CreateAssetOrderTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/CreateAssetOrderTestTransaction.java @@ -1,21 +1,20 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; - import org.qortal.account.PrivateKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.transaction.CreateAssetOrderTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class CreateAssetOrderTestTransaction extends TestTransaction { public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException { final long haveAssetId = Asset.QORT; final long wantAssetId = 1; - BigDecimal amount = BigDecimal.valueOf(123); - BigDecimal price = BigDecimal.valueOf(123); + long amount = 123L * Amounts.MULTIPLIER; + long price = 123L * Amounts.MULTIPLIER; return new CreateAssetOrderTransactionData(generateBase(account), haveAssetId, wantAssetId, amount, price); } diff --git a/src/test/java/org/qortal/test/common/transaction/DeployAtTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/DeployAtTestTransaction.java index df4356b8..3e523178 100644 --- a/src/test/java/org/qortal/test/common/transaction/DeployAtTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/DeployAtTestTransaction.java @@ -1,6 +1,5 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; import java.util.Random; import org.qortal.account.PrivateKeyAccount; @@ -9,6 +8,7 @@ import org.qortal.data.transaction.DeployAtTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class DeployAtTestTransaction extends TestTransaction { @@ -21,8 +21,8 @@ public class DeployAtTestTransaction extends TestTransaction { String tags = "random AT tags"; byte[] creationBytes = new byte[1024]; random.nextBytes(creationBytes); - BigDecimal amount = BigDecimal.valueOf(123); - final long assetId = Asset.QORT; + long amount = 123L * Amounts.MULTIPLIER; + long assetId = Asset.QORT; return new DeployAtTransactionData(generateBase(account), name, description, atType, tags, creationBytes, amount, assetId); } diff --git a/src/test/java/org/qortal/test/common/transaction/GenesisTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/GenesisTestTransaction.java index 8c0ed0a2..f5c619a5 100644 --- a/src/test/java/org/qortal/test/common/transaction/GenesisTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/GenesisTestTransaction.java @@ -1,18 +1,17 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; - import org.qortal.account.PrivateKeyAccount; import org.qortal.data.transaction.GenesisTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class GenesisTestTransaction extends TestTransaction { public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException { String recipient = account.getAddress(); - BigDecimal amount = BigDecimal.valueOf(123); + long amount = 123L * Amounts.MULTIPLIER; return new GenesisTransactionData(generateBase(account), recipient, amount); } diff --git a/src/test/java/org/qortal/test/common/transaction/MessageTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/MessageTestTransaction.java index e7a2a080..7725bf3c 100644 --- a/src/test/java/org/qortal/test/common/transaction/MessageTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/MessageTestTransaction.java @@ -1,13 +1,12 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; - import org.qortal.account.PrivateKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.transaction.MessageTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class MessageTestTransaction extends TestTransaction { @@ -15,7 +14,7 @@ public class MessageTestTransaction extends TestTransaction { final int version = 3; String recipient = account.getAddress(); final long assetId = Asset.QORT; - BigDecimal amount = BigDecimal.valueOf(123L); + long amount = 123L * Amounts.MULTIPLIER; byte[] data = "message contents".getBytes(); final boolean isText = true; final boolean isEncrypted = false; diff --git a/src/test/java/org/qortal/test/common/transaction/MultiPaymentTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/MultiPaymentTestTransaction.java index 68344f5f..a25924c2 100644 --- a/src/test/java/org/qortal/test/common/transaction/MultiPaymentTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/MultiPaymentTestTransaction.java @@ -1,6 +1,5 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -11,13 +10,14 @@ import org.qortal.data.transaction.MultiPaymentTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class MultiPaymentTestTransaction extends TestTransaction { public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException { String recipient = account.getAddress(); final long assetId = Asset.QORT; - BigDecimal amount = BigDecimal.valueOf(123L); + long amount = 123L * Amounts.MULTIPLIER; List payments = new ArrayList<>(); payments.add(new PaymentData(recipient, assetId, amount)); diff --git a/src/test/java/org/qortal/test/common/transaction/PaymentTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/PaymentTestTransaction.java index a8d73731..f58e2071 100644 --- a/src/test/java/org/qortal/test/common/transaction/PaymentTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/PaymentTestTransaction.java @@ -1,18 +1,17 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; - import org.qortal.account.PrivateKeyAccount; import org.qortal.data.transaction.PaymentTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class PaymentTestTransaction extends TestTransaction { public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException { String recipient = account.getAddress(); - BigDecimal amount = BigDecimal.valueOf(123L); + long amount = 123L * Amounts.MULTIPLIER; return new PaymentTransactionData(generateBase(account), recipient, amount); } diff --git a/src/test/java/org/qortal/test/common/transaction/RewardShareTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/RewardShareTestTransaction.java index 2adaf08d..ceb3c37a 100644 --- a/src/test/java/org/qortal/test/common/transaction/RewardShareTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/RewardShareTestTransaction.java @@ -1,7 +1,5 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; - import org.qortal.account.PrivateKeyAccount; import org.qortal.data.transaction.RewardShareTransactionData; import org.qortal.data.transaction.TransactionData; @@ -13,7 +11,7 @@ public class RewardShareTestTransaction extends TestTransaction { public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException { String recipient = account.getAddress(); byte[] rewardSharePublicKey = account.getRewardSharePrivateKey(account.getPublicKey()); - BigDecimal sharePercent = BigDecimal.valueOf(50); + int sharePercent = 50_00; return new RewardShareTransactionData(generateBase(account), recipient, rewardSharePublicKey, sharePercent); } diff --git a/src/test/java/org/qortal/test/common/transaction/SellNameTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/SellNameTestTransaction.java index 310f108a..9fa99b4d 100644 --- a/src/test/java/org/qortal/test/common/transaction/SellNameTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/SellNameTestTransaction.java @@ -1,12 +1,11 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; - import org.qortal.account.PrivateKeyAccount; import org.qortal.data.transaction.SellNameTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class SellNameTestTransaction extends TestTransaction { @@ -15,7 +14,7 @@ public class SellNameTestTransaction extends TestTransaction { if (!wantValid) name += " " + random.nextInt(1_000_000); - BigDecimal amount = BigDecimal.valueOf(123); + long amount = 123L * Amounts.MULTIPLIER; return new SellNameTransactionData(generateBase(account), name, amount); } diff --git a/src/test/java/org/qortal/test/common/transaction/TestTransaction.java b/src/test/java/org/qortal/test/common/transaction/TestTransaction.java index 00aa92de..f1553a2c 100644 --- a/src/test/java/org/qortal/test/common/transaction/TestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/TestTransaction.java @@ -13,7 +13,7 @@ public abstract class TestTransaction { protected static final Random random = new Random(); public static BaseTransactionData generateBase(PrivateKeyAccount account) throws DataException { - return new BaseTransactionData(System.currentTimeMillis(), Group.NO_GROUP, account.getLastReference(), account.getPublicKey(), BlockChain.getInstance().getUnitFee(), null); + return new BaseTransactionData(System.currentTimeMillis(), Group.NO_GROUP, account.getLastReference(), account.getPublicKey(), BlockChain.getInstance().getUnscaledUnitFee(), null); } } diff --git a/src/test/java/org/qortal/test/common/transaction/TransferAssetTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/TransferAssetTestTransaction.java index 10d29c99..7d5e64d6 100644 --- a/src/test/java/org/qortal/test/common/transaction/TransferAssetTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/TransferAssetTestTransaction.java @@ -1,20 +1,19 @@ package org.qortal.test.common.transaction; -import java.math.BigDecimal; - import org.qortal.account.PrivateKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.transaction.TransactionData; import org.qortal.data.transaction.TransferAssetTransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; public class TransferAssetTestTransaction extends TestTransaction { public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException { String recipient = account.getAddress(); final long assetId = Asset.QORT; - BigDecimal amount = BigDecimal.valueOf(123); + long amount = 123L * Amounts.MULTIPLIER; return new TransferAssetTransactionData(generateBase(account), recipient, amount, assetId); } diff --git a/src/test/java/org/qortal/test/group/GroupApprovalTests.java b/src/test/java/org/qortal/test/group/GroupApprovalTests.java index e5c34ab0..34d34c22 100644 --- a/src/test/java/org/qortal/test/group/GroupApprovalTests.java +++ b/src/test/java/org/qortal/test/group/GroupApprovalTests.java @@ -21,16 +21,16 @@ import org.qortal.test.common.TransactionUtils; import org.qortal.transaction.Transaction; import org.qortal.transaction.Transaction.ApprovalStatus; import org.qortal.transaction.Transaction.ValidationResult; +import org.qortal.utils.Amounts; import static org.junit.Assert.*; -import java.math.BigDecimal; import java.util.Arrays; public class GroupApprovalTests extends Common { - private static final BigDecimal amount = BigDecimal.valueOf(5000L).setScale(8); - private static final BigDecimal fee = BigDecimal.ONE.setScale(8); + private static final long amount = 5000L * Amounts.MULTIPLIER; + private static final long fee = 1L * Amounts.MULTIPLIER; private static final int minBlockDelay = 5; private static final int maxBlockDelay = 10; @@ -89,10 +89,10 @@ public class GroupApprovalTests extends Common { PrivateKeyAccount bobAccount = Common.getTestAccount(repository, "bob"); byte[] bobOriginalReference = bobAccount.getLastReference(); - BigDecimal aliceOriginalBalance = aliceAccount.getConfirmedBalance(Asset.QORT); - BigDecimal bobOriginalBalance = bobAccount.getConfirmedBalance(Asset.QORT); + long aliceOriginalBalance = aliceAccount.getConfirmedBalance(Asset.QORT); + long bobOriginalBalance = bobAccount.getConfirmedBalance(Asset.QORT); - BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); + Long blockReward = BlockUtils.getNextBlockReward(repository); Transaction bobAssetTransaction = buildIssueAssetTransaction(repository, "bob", groupId); TransactionUtils.signAndMint(repository, bobAssetTransaction.getTransactionData(), bobAccount); @@ -105,12 +105,12 @@ public class GroupApprovalTests extends Common { assertFalse("reference should have changed", Arrays.equals(bobOriginalReference, bobPostAssetReference)); // Bob's balance should have the fee removed, even though the transaction itself hasn't been approved yet - BigDecimal bobPostAssetBalance = bobAccount.getConfirmedBalance(Asset.QORT); - Common.assertEqualBigDecimals("approval-pending transaction creator's balance incorrect", bobOriginalBalance.subtract(fee), bobPostAssetBalance); + long bobPostAssetBalance = bobAccount.getConfirmedBalance(Asset.QORT); + assertEquals("approval-pending transaction creator's balance incorrect", bobOriginalBalance - fee, bobPostAssetBalance); // Transaction fee should have ended up in forging account - BigDecimal alicePostAssetBalance = aliceAccount.getConfirmedBalance(Asset.QORT); - Common.assertEqualBigDecimals("block minter's balance incorrect", aliceOriginalBalance.add(blockReward).add(fee), alicePostAssetBalance); + long alicePostAssetBalance = aliceAccount.getConfirmedBalance(Asset.QORT); + assertEquals("block minter's balance incorrect", aliceOriginalBalance + blockReward + fee, alicePostAssetBalance); // Have Bob do a non-approval transaction to change his last-reference Transaction bobPaymentTransaction = buildPaymentTransaction(repository, "bob", "chloe", amount, Group.NO_GROUP); @@ -166,8 +166,8 @@ public class GroupApprovalTests extends Common { assertTrue("reference should be pre-payment", Arrays.equals(bobOriginalReference, bobReference)); // Also check Bob's balance is back to original value - BigDecimal bobBalance = bobAccount.getConfirmedBalance(Asset.QORT); - Common.assertEqualBigDecimals("reverted balance doesn't match original", bobOriginalBalance, bobBalance); + long bobBalance = bobAccount.getConfirmedBalance(Asset.QORT); + assertEquals("reverted balance doesn't match original", bobOriginalBalance, bobBalance); } } @@ -414,7 +414,7 @@ public class GroupApprovalTests extends Common { } } - private Transaction buildPaymentTransaction(Repository repository, String sender, String recipient, BigDecimal amount, int txGroupId) throws DataException { + private Transaction buildPaymentTransaction(Repository repository, String sender, String recipient, long amount, int txGroupId) throws DataException { PrivateKeyAccount sendingAccount = Common.getTestAccount(repository, sender); PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient); diff --git a/src/test/java/org/qortal/test/minting/RewardTests.java b/src/test/java/org/qortal/test/minting/RewardTests.java index ff025816..20bcb982 100644 --- a/src/test/java/org/qortal/test/minting/RewardTests.java +++ b/src/test/java/org/qortal/test/minting/RewardTests.java @@ -2,8 +2,6 @@ package org.qortal.test.minting; import static org.junit.Assert.*; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -25,6 +23,7 @@ import org.qortal.test.common.AccountUtils; import org.qortal.test.common.BlockUtils; import org.qortal.test.common.Common; import org.qortal.test.common.TestAccount; +import org.qortal.utils.Amounts; public class RewardTests extends Common { @@ -94,7 +93,7 @@ public class RewardTests extends Common { // We're expecting reward * 12.8% to Bob, the rest to Alice - long bobShare = (blockReward * share) / 100L; + long bobShare = (blockReward * share) / 100L / 100L; AccountUtils.assertBalance(repository, "bob", Asset.QORT, initialBalances.get("bob").get(Asset.QORT) + bobShare); long aliceShare = blockReward - bobShare; @@ -142,7 +141,7 @@ public class RewardTests extends Common { */ // Expected reward - long qoraHoldersReward = (blockReward * qoraHoldersShare) / Asset.MULTIPLIER; + long qoraHoldersReward = (blockReward * qoraHoldersShare) / Amounts.MULTIPLIER; assertTrue("QORA-holders share of block reward should be less than total block reward", qoraHoldersReward < blockReward); long ourQoraHeld = initialBalances.get("chloe").get(Asset.LEGACY_QORA); @@ -162,19 +161,19 @@ public class RewardTests extends Common { public void testMaxLegacyQoraReward() throws DataException { Common.useSettings("test-settings-v2-qora-holder.json"); - BigDecimal qoraPerQort = BlockChain.getInstance().getQoraPerQortReward(); + long qoraPerQort = BlockChain.getInstance().getUnscaledQoraPerQortReward(); try (final Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA); + Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA); // Mint lots of blocks for (int i = 0; i < 100; ++i) BlockUtils.mintBlock(repository); // Expected balances to be limited by Chloe's legacy QORA amount - BigDecimal expectedBalance = initialBalances.get("chloe").get(Asset.LEGACY_QORA).divide(qoraPerQort); - AccountUtils.assertBalance(repository, "chloe", Asset.QORT, initialBalances.get("chloe").get(Asset.QORT).add(expectedBalance)); - AccountUtils.assertBalance(repository, "chloe", Asset.QORT_FROM_QORA, initialBalances.get("chloe").get(Asset.QORT_FROM_QORA).add(expectedBalance)); + long expectedBalance = initialBalances.get("chloe").get(Asset.LEGACY_QORA) / qoraPerQort; + AccountUtils.assertBalance(repository, "chloe", Asset.QORT, initialBalances.get("chloe").get(Asset.QORT) + expectedBalance); + AccountUtils.assertBalance(repository, "chloe", Asset.QORT_FROM_QORA, initialBalances.get("chloe").get(Asset.QORT_FROM_QORA) + expectedBalance); } } @@ -189,7 +188,7 @@ public class RewardTests extends Common { assertEquals(0, (int) chloe.getLevel()); // Alice needs to mint block containing REWARD_SHARE BEFORE Alice loses minting privs - byte[] aliceChloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "chloe", BigDecimal.ZERO); // Block minted by Alice + byte[] aliceChloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "chloe", 0); // Block minted by Alice PrivateKeyAccount aliceChloeRewardShareAccount = new PrivateKeyAccount(repository, aliceChloeRewardSharePrivateKey); final int minterBlocksNeeded = cumulativeBlocksByLevel.get(1); @@ -211,10 +210,8 @@ public class RewardTests extends Common { public void testFounderRewards() throws DataException { Common.useSettings("test-settings-v2-founder-rewards.json"); - BigDecimal perHundred = BigDecimal.valueOf(100L); - try (final Repository repository = RepositoryManager.getRepository()) { - BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); + Long blockReward = BlockUtils.getNextBlockReward(repository); List mintingAndOnlineAccounts = new ArrayList<>(); @@ -227,14 +224,15 @@ public class RewardTests extends Common { // Chloe self-share and reward-share with Dilbert both online PrivateKeyAccount chloeSelfShare = Common.getTestAccount(repository, "chloe-reward-share"); mintingAndOnlineAccounts.add(chloeSelfShare); + PrivateKeyAccount chloeDilbertRewardShare = new PrivateKeyAccount(repository, Base58.decode("HuiyqLipUN1V9p1HZfLhyEwmEA6BTaT2qEfjgkwPViV4")); mintingAndOnlineAccounts.add(chloeDilbertRewardShare); BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0])); // 3 founders (online or not) so blockReward divided by 3 - BigDecimal founderCount = BigDecimal.valueOf(3L); - BigDecimal perFounderReward = blockReward.divide(founderCount, RoundingMode.DOWN); + int founderCount = 3; + long perFounderReward = blockReward / founderCount; // Alice simple self-share so her reward is perFounderReward AccountUtils.assertBalance(repository, "alice", Asset.QORT, perFounderReward); @@ -243,16 +241,19 @@ public class RewardTests extends Common { AccountUtils.assertBalance(repository, "bob", Asset.QORT, perFounderReward); // Chloe has two reward-shares, so her reward is divided by 2 - BigDecimal chloeSharesCount = BigDecimal.valueOf(2L); - BigDecimal chloePerShareReward = perFounderReward.divide(chloeSharesCount, RoundingMode.DOWN); + int chloeSharesCount = 2; + long chloePerShareReward = perFounderReward / chloeSharesCount; + // Her self-share gets chloePerShareReward - BigDecimal chloeExpectedBalance = chloePerShareReward; + long chloeExpectedBalance = chloePerShareReward; + // Her reward-share with Dilbert: 25% goes to Dilbert - BigDecimal dilbertSharePercent = BigDecimal.valueOf(25L); - BigDecimal dilbertExpectedBalance = chloePerShareReward.multiply(dilbertSharePercent).divide(perHundred, RoundingMode.DOWN); + int dilbertSharePercent = 25; + long dilbertExpectedBalance = (chloePerShareReward * dilbertSharePercent) / 100L; + // The remaining 75% goes to Chloe - BigDecimal rewardShareRemaining = chloePerShareReward.subtract(dilbertExpectedBalance); - chloeExpectedBalance = chloeExpectedBalance.add(rewardShareRemaining); + long rewardShareRemaining = chloePerShareReward - dilbertExpectedBalance; + chloeExpectedBalance += rewardShareRemaining; AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeExpectedBalance); } } diff --git a/src/test/java/org/qortal/test/naming/BuySellTests.java b/src/test/java/org/qortal/test/naming/BuySellTests.java index 5c077996..0d323c5b 100644 --- a/src/test/java/org/qortal/test/naming/BuySellTests.java +++ b/src/test/java/org/qortal/test/naming/BuySellTests.java @@ -2,7 +2,6 @@ package org.qortal.test.naming; import static org.junit.Assert.*; -import java.math.BigDecimal; import java.util.Random; import org.junit.After; @@ -21,6 +20,7 @@ import org.qortal.test.common.BlockUtils; import org.qortal.test.common.Common; import org.qortal.test.common.TransactionUtils; import org.qortal.test.common.transaction.TestTransaction; +import org.qortal.utils.Amounts; public class BuySellTests extends Common { @@ -31,7 +31,7 @@ public class BuySellTests extends Common { private PrivateKeyAccount bob; private String name; - private BigDecimal price; + private Long price; @Before public void beforeTest() throws DataException { @@ -42,7 +42,7 @@ public class BuySellTests extends Common { bob = Common.getTestAccount(repository, "bob"); name = "test name" + " " + random.nextInt(1_000_000); - price = new BigDecimal(random.nextInt(1000)).setScale(8); + price = random.nextInt(1000) * Amounts.MULTIPLIER; } @After @@ -96,7 +96,7 @@ public class BuySellTests extends Common { // Check name is for sale nameData = repository.getNameRepository().fromName(name); assertTrue(nameData.getIsForSale()); - assertEqualBigDecimals("price incorrect", price, nameData.getSalePrice()); + assertEquals("price incorrect", price, nameData.getSalePrice()); // Orphan sell-name BlockUtils.orphanLastBlock(repository); @@ -112,7 +112,7 @@ public class BuySellTests extends Common { // Check name is for sale nameData = repository.getNameRepository().fromName(name); assertTrue(nameData.getIsForSale()); - assertEqualBigDecimals("price incorrect", price, nameData.getSalePrice()); + assertEquals("price incorrect", price, nameData.getSalePrice()); // Orphan sell-name and register-name BlockUtils.orphanBlocks(repository, 2); @@ -134,7 +134,7 @@ public class BuySellTests extends Common { // Check name is for sale nameData = repository.getNameRepository().fromName(name); assertTrue(nameData.getIsForSale()); - assertEqualBigDecimals("price incorrect", price, nameData.getSalePrice()); + assertEquals("price incorrect", price, nameData.getSalePrice()); } @Test @@ -159,7 +159,7 @@ public class BuySellTests extends Common { // Check name is for sale nameData = repository.getNameRepository().fromName(name); assertTrue(nameData.getIsForSale()); - assertEqualBigDecimals("price incorrect", price, nameData.getSalePrice()); + assertEquals("price incorrect", price, nameData.getSalePrice()); } @Test @@ -186,7 +186,7 @@ public class BuySellTests extends Common { // Check name is for sale (not sold) nameData = repository.getNameRepository().fromName(name); assertTrue(nameData.getIsForSale()); - assertEqualBigDecimals("price incorrect", price, nameData.getSalePrice()); + assertEquals("price incorrect", price, nameData.getSalePrice()); // Re-process buy-name BlockUtils.mintBlock(repository); @@ -225,7 +225,7 @@ public class BuySellTests extends Common { testBuyName(); // Sell-name - BigDecimal newPrice = new BigDecimal(random.nextInt(1000)).setScale(8); + Long newPrice = random.nextInt(1000) * Amounts.MULTIPLIER; SellNameTransactionData transactionData = new SellNameTransactionData(TestTransaction.generateBase(bob), name, newPrice); TransactionUtils.signAndMint(repository, transactionData, bob); @@ -234,7 +234,7 @@ public class BuySellTests extends Common { // Check name is for sale nameData = repository.getNameRepository().fromName(name); assertTrue(nameData.getIsForSale()); - assertEqualBigDecimals("price incorrect", newPrice, nameData.getSalePrice()); + assertEquals("price incorrect", newPrice, nameData.getSalePrice()); // Orphan sell-name BlockUtils.orphanLastBlock(repository); @@ -250,7 +250,7 @@ public class BuySellTests extends Common { // Check name is for sale nameData = repository.getNameRepository().fromName(name); assertTrue(nameData.getIsForSale()); - assertEqualBigDecimals("price incorrect", newPrice, nameData.getSalePrice()); + assertEquals("price incorrect", newPrice, nameData.getSalePrice()); // Orphan sell-name and buy-name BlockUtils.orphanBlocks(repository, 2); @@ -259,7 +259,7 @@ public class BuySellTests extends Common { nameData = repository.getNameRepository().fromName(name); assertTrue(nameData.getIsForSale()); // Note: original sale price - assertEqualBigDecimals("price incorrect", price, nameData.getSalePrice()); + assertEquals("price incorrect", price, nameData.getSalePrice()); assertEquals(alice.getAddress(), nameData.getOwner()); // Re-process buy-name and sell-name @@ -274,7 +274,7 @@ public class BuySellTests extends Common { // Check name is for sale nameData = repository.getNameRepository().fromName(name); assertTrue(nameData.getIsForSale()); - assertEqualBigDecimals("price incorrect", newPrice, nameData.getSalePrice()); + assertEquals("price incorrect", newPrice, nameData.getSalePrice()); assertEquals(bob.getAddress(), nameData.getOwner()); } diff --git a/src/test/resources/test-chain-v2.json b/src/test/resources/test-chain-v2.json index 6c735ab6..7696f225 100644 --- a/src/test/resources/test-chain-v2.json +++ b/src/test/resources/test-chain-v2.json @@ -55,12 +55,12 @@ { "type": "CREATE_GROUP", "creatorPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT100", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 }, - { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, - { "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, - { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": "1000000", "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": "1000000", "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": "1000000", "isDivisible": true, "fee": 0 }, { "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 }, - { "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": 100 }, + { "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": "100" }, { "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 5 } ]