From 9eaf31707a368067eb0c7b3b26a28c7600adfbc9 Mon Sep 17 00:00:00 2001 From: catbref Date: Wed, 29 Apr 2020 17:30:25 +0100 Subject: [PATCH] Massive conversion from BigDecimal to long. Now possible thanks to removing Qora v1 support. Maximum asset quantities now unified to 10_000_000_000, to 8 decimal places, removing prior 10 billion billion indivisible maximum. All values can now fit into a 64bit long. (Except maybe when processing asset trades). Added a general-use JAXB AmountTypeAdapter for converting amounts to/from String/long. Asset trading engine split into more methods for easier readability. Switched to using FIXED founder block reward distribution code, ready for launch. In HSQLDBDatabaseUpdates, QortalAmount changed from DECIMAL(27, 0) to BIGINT RewardSharePercent added to replace DECIMAL(5,2) with INT Ripped out unused Transaction.isInvolved and Transaction.getAmount in all subclasses. Changed Transaction.getRecipientAccounts() : List to Transaction.getRecipientAddresses() : List as only addresses are ever used. Corrected returned values for above getRecipientAddresses() for some transaction subclasses. Added some account caching to some transactions to reduce repeated loads during validation and then processing. Transaction transformers: Changed serialization of asset amounts from using 12 bytes to now standard 8 byte long. Updated transaction 'layouts' to reflect new sizes. RewardShareTransactionTransformer still uses 8byte long to represent reward share percent. Updated some unit tests - more work needed! --- src/main/java/org/qortal/account/Account.java | 24 +- .../org/qortal/api/AmountTypeAdapter.java | 27 +++ .../org/qortal/api/model/AggregatedOrder.java | 9 +- .../api/resource/AddressesResource.java | 3 +- src/main/java/org/qortal/asset/Asset.java | 3 +- src/main/java/org/qortal/asset/Order.java | 207 +++++++++--------- src/main/java/org/qortal/asset/Trade.java | 36 ++- src/main/java/org/qortal/at/AT.java | 5 +- .../java/org/qortal/at/BlockchainAPI.java | 11 +- src/main/java/org/qortal/at/QortalATAPI.java | 25 +-- src/main/java/org/qortal/block/Block.java | 192 ++++++++-------- .../java/org/qortal/block/BlockChain.java | 46 +++- .../java/org/qortal/block/GenesisBlock.java | 7 +- .../java/org/qortal/data/PaymentData.java | 13 +- .../data/account/AccountBalanceData.java | 16 +- .../qortal/data/account/QortFromQoraData.java | 17 +- .../qortal/data/account/RewardShareData.java | 18 +- .../java/org/qortal/data/asset/AssetData.java | 2 +- .../java/org/qortal/data/asset/OrderData.java | 37 ++-- .../qortal/data/asset/RecentTradeData.java | 10 +- .../java/org/qortal/data/asset/TradeData.java | 31 ++- src/main/java/org/qortal/data/at/ATData.java | 20 +- .../java/org/qortal/data/at/ATStateData.java | 12 +- .../java/org/qortal/data/block/BlockData.java | 28 ++- .../java/org/qortal/data/naming/NameData.java | 20 +- .../data/transaction/ATTransactionData.java | 26 ++- .../data/transaction/BaseTransactionData.java | 6 +- .../transaction/BuyNameTransactionData.java | 19 +- .../CreateAssetOrderTransactionData.java | 21 +- .../transaction/DeployAtTransactionData.java | 12 +- .../transaction/GenesisTransactionData.java | 21 +- .../transaction/MessageTransactionData.java | 10 +- .../transaction/PaymentTransactionData.java | 16 +- .../RewardShareTransactionData.java | 27 ++- .../transaction/SellNameTransactionData.java | 16 +- .../data/transaction/TransactionData.java | 17 +- .../TransferAssetTransactionData.java | 15 +- src/main/java/org/qortal/naming/Name.java | 8 +- src/main/java/org/qortal/payment/Payment.java | 71 +++--- .../qortal/repository/AccountRepository.java | 3 +- .../qortal/repository/AssetRepository.java | 3 +- .../repository/hsqldb/HSQLDBATRepository.java | 15 +- .../hsqldb/HSQLDBAccountRepository.java | 31 ++- .../hsqldb/HSQLDBAssetRepository.java | 83 ++++--- .../hsqldb/HSQLDBBlockRepository.java | 5 +- .../hsqldb/HSQLDBDatabaseUpdates.java | 9 +- .../hsqldb/HSQLDBNameRepository.java | 59 ++--- .../repository/hsqldb/HSQLDBRepository.java | 6 +- .../HSQLDBAtTransactionRepository.java | 5 +- .../HSQLDBBuyNameTransactionRepository.java | 3 +- ...CreateAssetOrderTransactionRepository.java | 5 +- .../HSQLDBDeployAtTransactionRepository.java | 3 +- .../HSQLDBGenesisTransactionRepository.java | 3 +- .../HSQLDBMessageTransactionRepository.java | 3 +- .../HSQLDBPaymentTransactionRepository.java | 3 +- ...SQLDBRewardShareTransactionRepository.java | 8 +- .../HSQLDBSellNameTransactionRepository.java | 3 +- .../HSQLDBTransactionRepository.java | 34 +-- ...LDBTransferAssetTransactionRepository.java | 3 +- .../transaction/AccountFlagsTransaction.java | 74 ++----- .../transaction/AccountLevelTransaction.java | 59 +---- .../transaction/AddGroupAdminTransaction.java | 76 +++---- .../transaction/ArbitraryTransaction.java | 60 +---- .../org/qortal/transaction/AtTransaction.java | 131 +++++------ .../transaction/BuyNameTransaction.java | 73 ++---- .../CancelAssetOrderTransaction.java | 57 +---- .../CancelGroupBanTransaction.java | 73 +++--- .../CancelGroupInviteTransaction.java | 71 ++---- .../CancelSellNameTransaction.java | 49 +---- .../CreateAssetOrderTransaction.java | 73 ++---- .../transaction/CreateGroupTransaction.java | 71 ++---- .../transaction/CreatePollTransaction.java | 66 ++---- .../transaction/DeployAtTransaction.java | 92 +++----- .../transaction/GenesisTransaction.java | 51 +---- .../transaction/GroupApprovalTransaction.java | 47 +--- .../transaction/GroupBanTransaction.java | 72 ++---- .../transaction/GroupInviteTransaction.java | 67 ++---- .../transaction/GroupKickTransaction.java | 52 +---- .../transaction/IssueAssetTransaction.java | 101 ++++----- .../transaction/JoinGroupTransaction.java | 50 +---- .../transaction/LeaveGroupTransaction.java | 55 ++--- .../transaction/MessageTransaction.java | 80 ++----- .../transaction/MultiPaymentTransaction.java | 78 ++----- .../transaction/PaymentTransaction.java | 64 ++---- .../transaction/RegisterNameTransaction.java | 60 ++--- .../RemoveGroupAdminTransaction.java | 69 ++---- .../transaction/RewardShareTransaction.java | 101 ++++----- .../transaction/SellNameTransaction.java | 61 ++---- .../transaction/SetGroupTransaction.java | 48 +--- .../org/qortal/transaction/Transaction.java | 87 ++++---- .../transaction/TransferAssetTransaction.java | 76 ++----- .../transaction/TransferPrivsTransaction.java | 45 +--- .../transaction/UpdateAssetTransaction.java | 67 ++---- .../transaction/UpdateGroupTransaction.java | 73 ++---- .../transaction/UpdateNameTransaction.java | 72 ++---- .../transaction/VoteOnPollTransaction.java | 78 +++---- .../qortal/transform/PaymentTransformer.java | 28 +-- .../org/qortal/transform/Transformer.java | 4 +- .../transform/block/BlockTransformer.java | 25 ++- .../AccountFlagsTransactionTransformer.java | 6 +- .../AccountLevelTransactionTransformer.java | 7 +- .../AddGroupAdminTransactionTransformer.java | 6 +- .../ArbitraryTransactionTransformer.java | 8 +- .../transaction/AtTransactionTransformer.java | 9 +- .../BuyNameTransactionTransformer.java | 11 +- ...ancelAssetOrderTransactionTransformer.java | 7 +- .../CancelGroupBanTransactionTransformer.java | 6 +- ...ncelGroupInviteTransactionTransformer.java | 6 +- .../CancelSellNameTransactionTransformer.java | 6 +- ...reateAssetOrderTransactionTransformer.java | 19 +- .../CreateGroupTransactionTransformer.java | 6 +- .../CreatePollTransactionTransformer.java | 6 +- .../DeployAtTransactionTransformer.java | 15 +- .../GenesisTransactionTransformer.java | 9 +- .../GroupApprovalTransactionTransformer.java | 7 +- .../GroupBanTransactionTransformer.java | 6 +- .../GroupInviteTransactionTransformer.java | 6 +- .../GroupKickTransactionTransformer.java | 6 +- .../IssueAssetTransactionTransformer.java | 9 +- .../JoinGroupTransactionTransformer.java | 6 +- .../LeaveGroupTransactionTransformer.java | 6 +- .../MessageTransactionTransformer.java | 11 +- .../MultiPaymentTransactionTransformer.java | 6 +- .../PaymentTransactionTransformer.java | 12 +- .../RegisterNameTransactionTransformer.java | 6 +- ...emoveGroupAdminTransactionTransformer.java | 6 +- .../RewardShareTransactionTransformer.java | 16 +- .../SellNameTransactionTransformer.java | 11 +- .../SetGroupTransactionTransformer.java | 6 +- .../transaction/TransactionTransformer.java | 5 +- .../TransferAssetTransactionTransformer.java | 13 +- .../TransferPrivsTransactionTransformer.java | 7 +- .../UpdateAssetTransactionTransformer.java | 6 +- .../UpdateGroupTransactionTransformer.java | 6 +- .../UpdateNameTransactionTransformer.java | 6 +- .../VoteOnPollTransactionTransformer.java | 6 +- src/main/java/org/qortal/utils/Amounts.java | 44 ++++ .../org/qortal/test/AccountBalanceTests.java | 17 +- .../org/qortal/test/AccountRefCacheTests.java | 3 +- src/test/java/org/qortal/test/BlockTests.java | 5 +- .../java/org/qortal/test/RepositoryTests.java | 5 +- .../org/qortal/test/TransferPrivsTests.java | 7 +- .../qortal/test/assets/GranularityTests.java | 5 +- .../org/qortal/test/common/AccountUtils.java | 27 +-- .../org/qortal/test/common/AssetUtils.java | 34 +-- .../org/qortal/test/common/BlockUtils.java | 4 +- .../java/org/qortal/test/common/Common.java | 4 +- .../org/qortal/test/common/GroupUtils.java | 4 +- .../qortal/test/minting/RewardShareTests.java | 24 +- .../org/qortal/test/minting/RewardTests.java | 56 ++--- 150 files changed, 1795 insertions(+), 2767 deletions(-) create mode 100644 src/main/java/org/qortal/api/AmountTypeAdapter.java create mode 100644 src/main/java/org/qortal/utils/Amounts.java diff --git a/src/main/java/org/qortal/account/Account.java b/src/main/java/org/qortal/account/Account.java index 62190133..df832e8a 100644 --- a/src/main/java/org/qortal/account/Account.java +++ b/src/main/java/org/qortal/account/Account.java @@ -1,6 +1,6 @@ package org.qortal.account; -import java.math.BigDecimal; +import static org.qortal.utils.Amounts.prettyAmount; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -54,32 +54,24 @@ public class Account { // Balance manipulations - assetId is 0 for QORT - public BigDecimal getBalance(long assetId) throws DataException { + public long getConfirmedBalance(long assetId) throws DataException { AccountBalanceData accountBalanceData = this.repository.getAccountRepository().getBalance(this.address, assetId); if (accountBalanceData == null) - return BigDecimal.ZERO.setScale(8); + return 0; return accountBalanceData.getBalance(); } - public BigDecimal getConfirmedBalance(long assetId) throws DataException { - AccountBalanceData accountBalanceData = this.repository.getAccountRepository().getBalance(this.address, assetId); - if (accountBalanceData == null) - return BigDecimal.ZERO.setScale(8); - - return accountBalanceData.getBalance(); - } - - public void setConfirmedBalance(long assetId, BigDecimal balance) throws DataException { + public void setConfirmedBalance(long assetId, long balance) throws DataException { // Safety feature! - if (balance.compareTo(BigDecimal.ZERO) < 0) { - String message = String.format("Refusing to set negative balance %s [assetId %d] for %s", balance.toPlainString(), assetId, this.address); + if (balance < 0) { + String message = String.format("Refusing to set negative balance %s [assetId %d] for %s", prettyAmount(balance), assetId, this.address); LOGGER.error(message); throw new DataException(message); } // Delete account balance record instead of setting balance to zero - if (balance.signum() == 0) { + if (balance == 0) { this.repository.getAccountRepository().delete(this.address, assetId); return; } @@ -90,7 +82,7 @@ public class Account { AccountBalanceData accountBalanceData = new AccountBalanceData(this.address, assetId, balance); this.repository.getAccountRepository().save(accountBalanceData); - LOGGER.trace(() -> String.format("%s balance now %s [assetId %s]", this.address, balance.toPlainString(), assetId)); + LOGGER.trace(() -> String.format("%s balance now %s [assetId %s]", this.address, prettyAmount(balance), assetId)); } public void deleteBalance(long assetId) throws DataException { diff --git a/src/main/java/org/qortal/api/AmountTypeAdapter.java b/src/main/java/org/qortal/api/AmountTypeAdapter.java new file mode 100644 index 00000000..5cbf81ee --- /dev/null +++ b/src/main/java/org/qortal/api/AmountTypeAdapter.java @@ -0,0 +1,27 @@ +package org.qortal.api; + +import java.math.BigDecimal; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +import org.qortal.utils.Amounts; + +public class AmountTypeAdapter extends XmlAdapter { + + @Override + public Long unmarshal(String input) throws Exception { + if (input == null) + return null; + + return new BigDecimal(input).setScale(8).unscaledValue().longValue(); + } + + @Override + public String marshal(Long output) throws Exception { + if (output == null) + return null; + + return Amounts.prettyAmount(output); + } + +} diff --git a/src/main/java/org/qortal/api/model/AggregatedOrder.java b/src/main/java/org/qortal/api/model/AggregatedOrder.java index e0e9e30a..ff8ebfe2 100644 --- a/src/main/java/org/qortal/api/model/AggregatedOrder.java +++ b/src/main/java/org/qortal/api/model/AggregatedOrder.java @@ -1,11 +1,10 @@ package org.qortal.api.model; -import java.math.BigDecimal; - import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.qortal.data.asset.OrderData; @@ -29,12 +28,14 @@ public class AggregatedOrder { } @XmlElement(name = "price") - public BigDecimal getPrice() { + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + public long getPrice() { return this.orderData.getPrice(); } @XmlElement(name = "unfulfilled") - public BigDecimal getUnfulfilled() { + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + public long getUnfulfilled() { return this.orderData.getAmount(); } diff --git a/src/main/java/org/qortal/api/resource/AddressesResource.java b/src/main/java/org/qortal/api/resource/AddressesResource.java index 674ae2a1..bda95bea 100644 --- a/src/main/java/org/qortal/api/resource/AddressesResource.java +++ b/src/main/java/org/qortal/api/resource/AddressesResource.java @@ -46,6 +46,7 @@ import org.qortal.transaction.Transaction.ValidationResult; import org.qortal.transform.TransformationException; import org.qortal.transform.Transformer; import org.qortal.transform.transaction.RewardShareTransactionTransformer; +import org.qortal.utils.Amounts; import org.qortal.utils.Base58; @Path("/addresses") @@ -195,7 +196,7 @@ public class AddressesResource { else if (!repository.getAssetRepository().assetExists(assetId)) throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID); - return account.getBalance(assetId); + return Amounts.toBigDecimal(account.getConfirmedBalance(assetId)); } catch (ApiException e) { throw e; } catch (DataException e) { diff --git a/src/main/java/org/qortal/asset/Asset.java b/src/main/java/org/qortal/asset/Asset.java index e26dd029..ebfbf4d4 100644 --- a/src/main/java/org/qortal/asset/Asset.java +++ b/src/main/java/org/qortal/asset/Asset.java @@ -25,7 +25,8 @@ public class Asset { public static final int MAX_DESCRIPTION_SIZE = 4000; public static final int MAX_DATA_SIZE = 400000; - public static final long MAX_QUANTITY = 10_000_000_000L; // but also to 8 decimal places + public static final long MULTIPLIER = 100000000L; + public static final long MAX_QUANTITY = 10_000_000_000L * 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 d2ea1d7b..acb965e5 100644 --- a/src/main/java/org/qortal/asset/Order.java +++ b/src/main/java/org/qortal/asset/Order.java @@ -1,8 +1,7 @@ package org.qortal.asset; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.RoundingMode; +import static org.qortal.utils.Amounts.prettyAmount; + import java.util.Arrays; import java.util.List; @@ -14,9 +13,9 @@ import org.qortal.account.PublicKeyAccount; import org.qortal.data.asset.AssetData; import org.qortal.data.asset.OrderData; import org.qortal.data.asset.TradeData; -import org.qortal.repository.AssetRepository; import org.qortal.repository.DataException; import org.qortal.repository.Repository; +import org.qortal.utils.Amounts; import org.qortal.utils.Base58; public class Order { @@ -57,16 +56,16 @@ public class Order { // More information - public static BigDecimal getAmountLeft(OrderData orderData) { - return orderData.getAmount().subtract(orderData.getFulfilled()); + public static long getAmountLeft(OrderData orderData) { + return orderData.getAmount() - orderData.getFulfilled(); } - public BigDecimal getAmountLeft() { + public long getAmountLeft() { return Order.getAmountLeft(this.orderData); } public static boolean isFulfilled(OrderData orderData) { - return orderData.getFulfilled().compareTo(orderData.getAmount()) == 0; + return orderData.getFulfilled() == orderData.getAmount(); } public boolean isFulfilled() { @@ -83,31 +82,28 @@ public class Order { *

* @return granularity of matched-amount */ - public static BigDecimal calculateAmountGranularity(boolean isAmountAssetDivisible, boolean isReturnAssetDivisible, BigDecimal price) { - // Multiplier to scale BigDecimal fractional amounts into integer domain - BigInteger multiplier = BigInteger.valueOf(1_0000_0000L); - + public static long calculateAmountGranularity(boolean isAmountAssetDivisible, boolean isReturnAssetDivisible, long price) { // Calculate the minimum increment for matched-amount using greatest-common-divisor - BigInteger returnAmount = multiplier; // 1 unit (* multiplier) - BigInteger matchedAmount = price.movePointRight(8).toBigInteger(); + long returnAmount = Asset.MULTIPLIER; // 1 unit * multiplier + long matchedAmount = price; - BigInteger gcd = returnAmount.gcd(matchedAmount); - returnAmount = returnAmount.divide(gcd); - matchedAmount = matchedAmount.divide(gcd); + long gcd = Amounts.greatestCommonDivisor(returnAmount, matchedAmount); + returnAmount /= gcd; + matchedAmount /= gcd; // Calculate GCD in combination with divisibility if (isAmountAssetDivisible) - returnAmount = returnAmount.multiply(multiplier); + returnAmount *= Asset.MULTIPLIER; if (isReturnAssetDivisible) - matchedAmount = matchedAmount.multiply(multiplier); + matchedAmount *= Asset.MULTIPLIER; - gcd = returnAmount.gcd(matchedAmount); + gcd = Amounts.greatestCommonDivisor(returnAmount, matchedAmount); // Calculate the granularity at which we have to buy - BigDecimal granularity = new BigDecimal(returnAmount.divide(gcd)); + long granularity = returnAmount / gcd; if (isAmountAssetDivisible) - granularity = granularity.movePointLeft(8); + granularity /= Asset.MULTIPLIER; // Return return granularity; @@ -145,23 +141,23 @@ public class Order { } /** Returns amount of have-asset to remove from order's creator's balance on placing this order. */ - private BigDecimal calcHaveAssetCommittment() { - BigDecimal committedCost = this.orderData.getAmount(); + private long calcHaveAssetCommittment() { + long committedCost = this.orderData.getAmount(); // If "amount" is in want-asset then we need to convert if (haveAssetId < wantAssetId) - committedCost = committedCost.multiply(this.orderData.getPrice()).setScale(8, RoundingMode.HALF_UP); + committedCost *= this.orderData.getPrice() + 1; // +1 to round up return committedCost; } /** Returns amount of remaining have-asset to refund to order's creator's balance on cancelling this order. */ - private BigDecimal calcHaveAssetRefund() { - BigDecimal refund = getAmountLeft(); + private long calcHaveAssetRefund() { + long refund = getAmountLeft(); // If "amount" is in want-asset then we need to convert if (haveAssetId < wantAssetId) - refund = refund.multiply(this.orderData.getPrice()).setScale(8, RoundingMode.HALF_UP); + refund *= this.orderData.getPrice() + 1; // +1 to round up return refund; } @@ -229,37 +225,30 @@ public class Order { final AssetData amountAssetData = this.repository.getAssetRepository().fromAssetId(amountAssetId); final AssetData returnAssetData = this.repository.getAssetRepository().fromAssetId(returnAssetId); - LOGGER.debug(String.format("%s %s", orderPrefix, Base58.encode(orderData.getOrderId()))); + LOGGER.debug(() -> String.format("%s %s", orderPrefix, Base58.encode(orderData.getOrderId()))); - LOGGER.trace(String.format("%s have %s, want %s.", weThey, haveAssetData.getName(), wantAssetData.getName())); + LOGGER.trace(() -> String.format("%s have %s, want %s.", weThey, haveAssetData.getName(), wantAssetData.getName())); - LOGGER.trace(String.format("%s amount: %s (ordered) - %s (fulfilled) = %s %s left", ourTheir, - orderData.getAmount().stripTrailingZeros().toPlainString(), - orderData.getFulfilled().stripTrailingZeros().toPlainString(), - Order.getAmountLeft(orderData).stripTrailingZeros().toPlainString(), + LOGGER.trace(() -> String.format("%s amount: %s (ordered) - %s (fulfilled) = %s %s left", ourTheir, + prettyAmount(orderData.getAmount()), + prettyAmount(orderData.getFulfilled()), + prettyAmount(Order.getAmountLeft(orderData)), amountAssetData.getName())); - BigDecimal maxReturnAmount = Order.getAmountLeft(orderData).multiply(orderData.getPrice()).setScale(8, RoundingMode.HALF_UP); + long maxReturnAmount = Order.getAmountLeft(orderData) * (orderData.getPrice() + 1); // +1 to round up + String pricePair = getPricePair(); - LOGGER.trace(String.format("%s price: %s %s (%s %s tradable)", ourTheir, - orderData.getPrice().toPlainString(), getPricePair(), - maxReturnAmount.stripTrailingZeros().toPlainString(), returnAssetData.getName())); + LOGGER.trace(() -> String.format("%s price: %s %s (%s %s tradable)", ourTheir, + prettyAmount(orderData.getPrice()), + pricePair, + prettyAmount(maxReturnAmount), + returnAssetData.getName())); } public void process() throws DataException { - AssetRepository assetRepository = this.repository.getAssetRepository(); - - AssetData haveAssetData = getHaveAsset(); - AssetData wantAssetData = getWantAsset(); - - /** The asset while working out amount that matches. */ - AssetData matchingAssetData = getAmountAsset(); - /** The return asset traded if trade completes. */ - AssetData returnAssetData = getReturnAsset(); - // Subtract have-asset from creator Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey()); - creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId).subtract(this.calcHaveAssetCommittment())); + creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId) - this.calcHaveAssetCommittment()); // Save this order into repository so it's available for matching, possibly by itself this.repository.getAssetRepository().save(this.orderData); @@ -268,12 +257,24 @@ public class Order { // Fetch corresponding open orders that might potentially match, hence reversed want/have assetIDs. // Returned orders are sorted with lowest "price" first. - List orders = assetRepository.getOpenOrdersForTrading(wantAssetId, haveAssetId, this.orderData.getPrice()); - LOGGER.trace("Open orders fetched from repository: " + orders.size()); + List orders = this.repository.getAssetRepository().getOpenOrdersForTrading(wantAssetId, haveAssetId, this.orderData.getPrice()); + LOGGER.trace(() -> String.format("Open orders fetched from repository: %d", orders.size())); if (orders.isEmpty()) return; + matchOrders(orders); + } + + private void matchOrders(List orders) throws DataException { + AssetData haveAssetData = getHaveAsset(); + AssetData wantAssetData = getWantAsset(); + + /** The asset while working out amount that matches. */ + AssetData matchingAssetData = getAmountAsset(); + /** The return asset traded if trade completes. */ + AssetData returnAssetData = getReturnAsset(); + // Attempt to match orders /* @@ -295,86 +296,70 @@ public class Order { * If their order only had 36 GOLD left, only 36 * 486.00074844 = 17496.02694384 QORT would be traded. */ - BigDecimal ourPrice = this.orderData.getPrice(); + long ourPrice = this.orderData.getPrice(); + String pricePair = getPricePair(); for (OrderData theirOrderData : orders) { logOrder("Considering order", false, theirOrderData); // Determine their order price - BigDecimal theirPrice; - - // Pricing units are the same way round for both orders, so no conversion needed. - theirPrice = theirOrderData.getPrice(); - LOGGER.trace(String.format("Their price: %s %s", theirPrice.toPlainString(), getPricePair())); + long theirPrice = theirOrderData.getPrice(); + LOGGER.trace(() -> String.format("Their price: %s %s", prettyAmount(theirPrice), pricePair)); // If their price is worse than what we're willing to accept then we're done as prices only get worse as we iterate through list of orders - if (haveAssetId < wantAssetId && theirPrice.compareTo(ourPrice) > 0) - break; - if (haveAssetId > wantAssetId && theirPrice.compareTo(ourPrice) < 0) + if ((haveAssetId < wantAssetId && theirPrice > ourPrice) || (haveAssetId > wantAssetId && theirPrice < ourPrice)) break; // Calculate how much we could buy at their price, "amount" is expressed in terms of asset with highest assetID. - BigDecimal ourMaxAmount = this.getAmountLeft(); - LOGGER.trace("ourMaxAmount (max we could trade at their price): " + ourMaxAmount.stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName()); + long ourMaxAmount = this.getAmountLeft(); + LOGGER.trace(() -> String.format("ourMaxAmount (max we could trade at their price): %s %s", prettyAmount(ourMaxAmount), matchingAssetData.getName())); // How much is remaining available in their order. - BigDecimal theirAmountLeft = Order.getAmountLeft(theirOrderData); - LOGGER.trace("theirAmountLeft (max amount remaining in their order): " + theirAmountLeft.stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName()); + long theirAmountLeft = Order.getAmountLeft(theirOrderData); + LOGGER.trace(() -> String.format("theirAmountLeft (max amount remaining in their order): %s %s", prettyAmount(theirAmountLeft), matchingAssetData.getName())); // So matchable want-asset amount is the minimum of above two values - BigDecimal matchedAmount = ourMaxAmount.min(theirAmountLeft); - LOGGER.trace("matchedAmount: " + matchedAmount.stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName()); + long interimMatchedAmount = Math.min(ourMaxAmount, theirAmountLeft); + LOGGER.trace(() -> String.format("matchedAmount: %s %s", prettyAmount(interimMatchedAmount), matchingAssetData.getName())); // If we can't buy anything then try another order - if (matchedAmount.compareTo(BigDecimal.ZERO) <= 0) + if (interimMatchedAmount <= 0) continue; // Calculate amount granularity, based on price and both assets' divisibility, so that return-amount traded is a valid value (integer or to 8 d.p.) - BigDecimal granularity = calculateAmountGranularity(matchingAssetData.getIsDivisible(), returnAssetData.getIsDivisible(), theirOrderData.getPrice()); - LOGGER.trace("granularity (amount granularity): " + granularity.stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName()); + long granularity = calculateAmountGranularity(matchingAssetData.getIsDivisible(), returnAssetData.getIsDivisible(), theirOrderData.getPrice()); + LOGGER.trace(() -> String.format("granularity (amount granularity): %s %s", prettyAmount(granularity), matchingAssetData.getName())); // Reduce matched amount (if need be) to fit granularity - matchedAmount = matchedAmount.subtract(matchedAmount.remainder(granularity)); - LOGGER.trace("matchedAmount adjusted for granularity: " + matchedAmount.stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName()); + long matchedAmount = interimMatchedAmount - interimMatchedAmount % granularity; + LOGGER.trace(() -> String.format("matchedAmount adjusted for granularity: %s %s", prettyAmount(matchedAmount), matchingAssetData.getName())); // If we can't buy anything then try another order - if (matchedAmount.compareTo(BigDecimal.ZERO) <= 0) + if (matchedAmount <= 0) continue; // Safety check - if (!matchingAssetData.getIsDivisible() && matchedAmount.stripTrailingZeros().scale() > 0) { - Account participant = new PublicKeyAccount(this.repository, theirOrderData.getCreatorPublicKey()); - - String message = String.format("Refusing to trade fractional %s [indivisible assetID %d] for %s", - matchedAmount.toPlainString(), matchingAssetData.getAssetId(), participant.getAddress()); - LOGGER.error(message); - throw new DataException(message); - } + checkDivisibility(matchingAssetData, matchedAmount, theirOrderData); // Trade can go ahead! // Calculate the total cost to us, in return-asset, based on their price - BigDecimal returnAmountTraded = matchedAmount.multiply(theirOrderData.getPrice()).setScale(8, RoundingMode.DOWN); - LOGGER.trace("returnAmountTraded: " + returnAmountTraded.stripTrailingZeros().toPlainString() + " " + returnAssetData.getName()); + long returnAmountTraded = matchedAmount * theirOrderData.getPrice(); + LOGGER.trace(() -> String.format("returnAmountTraded: %s %s", prettyAmount(returnAmountTraded), returnAssetData.getName())); // Safety check - if (!returnAssetData.getIsDivisible() && returnAmountTraded.stripTrailingZeros().scale() > 0) { - String message = String.format("Refusing to trade fractional %s [indivisible assetID %d] for %s", - returnAmountTraded.toPlainString(), returnAssetData.getAssetId(), creator.getAddress()); - LOGGER.error(message); - throw new DataException(message); - } + checkDivisibility(returnAssetData, returnAmountTraded, this.orderData); - BigDecimal tradedWantAmount = (haveAssetId > wantAssetId) ? returnAmountTraded : matchedAmount; - BigDecimal tradedHaveAmount = (haveAssetId > wantAssetId) ? matchedAmount : returnAmountTraded; + long tradedWantAmount = (haveAssetId > wantAssetId) ? returnAmountTraded : matchedAmount; + long tradedHaveAmount = (haveAssetId > wantAssetId) ? matchedAmount : returnAmountTraded; // We also need to know how much have-asset to refund based on price improvement (only one direction applies) - BigDecimal haveAssetRefund = haveAssetId < wantAssetId ? ourPrice.subtract(theirPrice).abs().multiply(matchedAmount).setScale(8, RoundingMode.DOWN) : BigDecimal.ZERO; + long haveAssetRefund = haveAssetId < wantAssetId ? Math.abs(ourPrice -theirPrice) * matchedAmount : 0; - LOGGER.trace(String.format("We traded %s %s (have-asset) for %s %s (want-asset), saving %s %s (have-asset)", - tradedHaveAmount.toPlainString(), haveAssetData.getName(), - tradedWantAmount.toPlainString(), wantAssetData.getName(), - haveAssetRefund.toPlainString(), haveAssetData.getName())); + LOGGER.trace(() -> String.format("We traded %s %s (have-asset) for %s %s (want-asset), saving %s %s (have-asset)", + prettyAmount(tradedHaveAmount), haveAssetData.getName(), + prettyAmount(tradedWantAmount), wantAssetData.getName(), + prettyAmount(haveAssetRefund), haveAssetData.getName())); // Construct trade TradeData tradeData = new TradeData(this.orderData.getOrderId(), theirOrderData.getOrderId(), @@ -384,17 +369,33 @@ public class Order { trade.process(); // Update our order in terms of fulfilment, etc. but do not save into repository as that's handled by Trade above - BigDecimal amountFulfilled = matchedAmount; - this.orderData.setFulfilled(this.orderData.getFulfilled().add(amountFulfilled)); - LOGGER.trace("Updated our order's fulfilled amount to: " + this.orderData.getFulfilled().stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName()); - LOGGER.trace("Our order's amount remaining: " + this.getAmountLeft().stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName()); + long amountFulfilled = matchedAmount; + this.orderData.setFulfilled(this.orderData.getFulfilled() + amountFulfilled); + LOGGER.trace(() -> String.format("Updated our order's fulfilled amount to: %s %s", prettyAmount(this.orderData.getFulfilled()), matchingAssetData.getName())); + LOGGER.trace(() -> String.format("Our order's amount remaining: %s %s", prettyAmount(this.getAmountLeft()), matchingAssetData.getName())); // Continue on to process other open orders if we still have amount left to match - if (this.getAmountLeft().compareTo(BigDecimal.ZERO) <= 0) + if (this.getAmountLeft() <= 0) break; } } + /** + * Check amount has no fractional part if asset is indivisible. + * + * @throws DataException if divisibility check fails + */ + private void checkDivisibility(AssetData assetData, long amount, OrderData orderData) throws DataException { + if (assetData.getIsDivisible() || amount % Asset.MULTIPLIER == 0) + // Asset is divisible or amount has no fractional part + return; + + String message = String.format("Refusing to trade fractional %s [indivisible assetID %d] for order %s", + prettyAmount(amount), assetData.getAssetId(), Base58.encode(orderData.getOrderId())); + LOGGER.error(message); + throw new DataException(message); + } + public void orphan() throws DataException { // Orphan trades that occurred as a result of this order for (TradeData tradeData : getTrades()) @@ -408,7 +409,7 @@ public class Order { // Return asset to creator Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey()); - creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId).add(this.calcHaveAssetCommittment())); + creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId) + this.calcHaveAssetCommittment()); } // This is called by CancelOrderTransaction so that an Order can no longer trade @@ -418,14 +419,14 @@ public class Order { // Update creator's balance with unfulfilled amount Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey()); - creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId).add(calcHaveAssetRefund())); + creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId) + calcHaveAssetRefund()); } // Opposite of cancel() above for use during orphaning public void reopen() throws DataException { // Update creator's balance with unfulfilled amount Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey()); - creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId).subtract(calcHaveAssetRefund())); + creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId) - calcHaveAssetRefund()); this.orderData.setIsClosed(false); this.repository.getAssetRepository().save(this.orderData); diff --git a/src/main/java/org/qortal/asset/Trade.java b/src/main/java/org/qortal/asset/Trade.java index 18943b78..425c8f8a 100644 --- a/src/main/java/org/qortal/asset/Trade.java +++ b/src/main/java/org/qortal/asset/Trade.java @@ -1,7 +1,5 @@ package org.qortal.asset; -import java.math.BigDecimal; - import org.qortal.account.Account; import org.qortal.account.PublicKeyAccount; import org.qortal.data.asset.OrderData; @@ -20,7 +18,7 @@ public class Trade { private OrderData initiatingOrder; private OrderData targetOrder; - private BigDecimal fulfilled; + private long fulfilled; // Constructors @@ -42,7 +40,7 @@ public class Trade { // "amount" and "fulfilled" are the same asset for both orders // which is the matchedAmount in asset with highest assetID - this.fulfilled = (initiatingOrder.getHaveAssetId() < initiatingOrder.getWantAssetId()) ? this.tradeData.getTargetAmount() : this.tradeData.getInitiatorAmount(); + this.fulfilled = initiatingOrder.getHaveAssetId() < initiatingOrder.getWantAssetId() ? this.tradeData.getTargetAmount() : this.tradeData.getInitiatorAmount(); } public void process() throws DataException { @@ -55,13 +53,13 @@ public class Trade { commonPrep(); // Update corresponding Orders on both sides of trade - initiatingOrder.setFulfilled(initiatingOrder.getFulfilled().add(fulfilled)); + initiatingOrder.setFulfilled(initiatingOrder.getFulfilled() + fulfilled); initiatingOrder.setIsFulfilled(Order.isFulfilled(initiatingOrder)); // Set isClosed to true if isFulfilled now true initiatingOrder.setIsClosed(initiatingOrder.getIsFulfilled()); assetRepository.save(initiatingOrder); - targetOrder.setFulfilled(targetOrder.getFulfilled().add(fulfilled)); + targetOrder.setFulfilled(targetOrder.getFulfilled() + fulfilled); targetOrder.setIsFulfilled(Order.isFulfilled(targetOrder)); // Set isClosed to true if isFulfilled now true targetOrder.setIsClosed(targetOrder.getIsFulfilled()); @@ -69,33 +67,31 @@ public class Trade { // Actually transfer asset balances Account initiatingCreator = new PublicKeyAccount(this.repository, initiatingOrder.getCreatorPublicKey()); - initiatingCreator.setConfirmedBalance(initiatingOrder.getWantAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getWantAssetId()).add(tradeData.getTargetAmount())); + initiatingCreator.setConfirmedBalance(initiatingOrder.getWantAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getWantAssetId()) + tradeData.getTargetAmount()); Account targetCreator = new PublicKeyAccount(this.repository, targetOrder.getCreatorPublicKey()); - targetCreator.setConfirmedBalance(targetOrder.getWantAssetId(), targetCreator.getConfirmedBalance(targetOrder.getWantAssetId()).add(tradeData.getInitiatorAmount())); + targetCreator.setConfirmedBalance(targetOrder.getWantAssetId(), targetCreator.getConfirmedBalance(targetOrder.getWantAssetId()) + tradeData.getInitiatorAmount()); // Possible partial saving to refund to initiator - BigDecimal initiatorSaving = this.tradeData.getInitiatorSaving(); - if (initiatorSaving.compareTo(BigDecimal.ZERO) > 0) - initiatingCreator.setConfirmedBalance(initiatingOrder.getHaveAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getHaveAssetId()).add(initiatorSaving)); + long initiatorSaving = this.tradeData.getInitiatorSaving(); + if (initiatorSaving > 0) + initiatingCreator.setConfirmedBalance(initiatingOrder.getHaveAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getHaveAssetId()) + initiatorSaving); } public void orphan() throws DataException { - AssetRepository assetRepository = this.repository.getAssetRepository(); - // Note: targetAmount is amount traded FROM target order // Note: initiatorAmount is amount traded FROM initiating order commonPrep(); // Revert corresponding Orders on both sides of trade - initiatingOrder.setFulfilled(initiatingOrder.getFulfilled().subtract(fulfilled)); + initiatingOrder.setFulfilled(initiatingOrder.getFulfilled() - fulfilled); initiatingOrder.setIsFulfilled(Order.isFulfilled(initiatingOrder)); // Set isClosed to false if isFulfilled now false initiatingOrder.setIsClosed(initiatingOrder.getIsFulfilled()); assetRepository.save(initiatingOrder); - targetOrder.setFulfilled(targetOrder.getFulfilled().subtract(fulfilled)); + targetOrder.setFulfilled(targetOrder.getFulfilled() - fulfilled); targetOrder.setIsFulfilled(Order.isFulfilled(targetOrder)); // Set isClosed to false if isFulfilled now false targetOrder.setIsClosed(targetOrder.getIsFulfilled()); @@ -103,15 +99,15 @@ public class Trade { // Reverse asset transfers Account initiatingCreator = new PublicKeyAccount(this.repository, initiatingOrder.getCreatorPublicKey()); - initiatingCreator.setConfirmedBalance(initiatingOrder.getWantAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getWantAssetId()).subtract(tradeData.getTargetAmount())); + initiatingCreator.setConfirmedBalance(initiatingOrder.getWantAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getWantAssetId()) - tradeData.getTargetAmount()); Account targetCreator = new PublicKeyAccount(this.repository, targetOrder.getCreatorPublicKey()); - targetCreator.setConfirmedBalance(targetOrder.getWantAssetId(), targetCreator.getConfirmedBalance(targetOrder.getWantAssetId()).subtract(tradeData.getInitiatorAmount())); + targetCreator.setConfirmedBalance(targetOrder.getWantAssetId(), targetCreator.getConfirmedBalance(targetOrder.getWantAssetId()) - tradeData.getInitiatorAmount()); // Possible partial saving to claw back from initiator - BigDecimal initiatorSaving = this.tradeData.getInitiatorSaving(); - if (initiatorSaving.compareTo(BigDecimal.ZERO) > 0) - initiatingCreator.setConfirmedBalance(initiatingOrder.getHaveAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getHaveAssetId()).subtract(initiatorSaving)); + long initiatorSaving = this.tradeData.getInitiatorSaving(); + if (initiatorSaving > 0) + initiatingCreator.setConfirmedBalance(initiatingOrder.getHaveAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getHaveAssetId()) - initiatorSaving); // Remove trade from repository assetRepository.delete(tradeData); diff --git a/src/main/java/org/qortal/at/AT.java b/src/main/java/org/qortal/at/AT.java index b02307a4..52da2335 100644 --- a/src/main/java/org/qortal/at/AT.java +++ b/src/main/java/org/qortal/at/AT.java @@ -1,6 +1,5 @@ package org.qortal.at; -import java.math.BigDecimal; import java.util.List; import org.ciyam.at.MachineState; @@ -51,7 +50,7 @@ public class AT { byte[] stateData = machineState.toBytes(); byte[] stateHash = Crypto.digest(stateData); - this.atStateData = new ATStateData(atAddress, height, creation, stateData, stateHash, BigDecimal.ZERO.setScale(8)); + this.atStateData = new ATStateData(atAddress, height, creation, stateData, stateHash, 0L); } // Getters / setters @@ -97,7 +96,7 @@ public class AT { long creation = this.atData.getCreation(); byte[] stateData = state.toBytes(); byte[] stateHash = Crypto.digest(stateData); - BigDecimal atFees = api.calcFinalFees(state); + long atFees = api.calcFinalFees(state); this.atStateData = new ATStateData(atAddress, height, creation, stateData, stateHash, atFees); diff --git a/src/main/java/org/qortal/at/BlockchainAPI.java b/src/main/java/org/qortal/at/BlockchainAPI.java index 2e91a0f6..e21d786d 100644 --- a/src/main/java/org/qortal/at/BlockchainAPI.java +++ b/src/main/java/org/qortal/at/BlockchainAPI.java @@ -3,7 +3,6 @@ package org.qortal.at; import static java.util.Arrays.stream; import static java.util.stream.Collectors.toMap; -import java.math.BigDecimal; import java.util.List; import java.util.Map; @@ -85,16 +84,16 @@ public enum BlockchainAPI { switch (transactionData.getType()) { case PAYMENT: - return ((PaymentTransactionData) transactionData).getAmount().unscaledValue().longValue(); + return ((PaymentTransactionData) transactionData).getAmount(); case AT: - BigDecimal amount = ((ATTransactionData) transactionData).getAmount(); + Long amount = ((ATTransactionData) transactionData).getAmount(); - if (amount != null) - return amount.unscaledValue().longValue(); - else + if (amount == null) return 0xffffffffffffffffL; + return amount; + default: return 0xffffffffffffffffL; } diff --git a/src/main/java/org/qortal/at/QortalATAPI.java b/src/main/java/org/qortal/at/QortalATAPI.java index ff6765c3..ae00a936 100644 --- a/src/main/java/org/qortal/at/QortalATAPI.java +++ b/src/main/java/org/qortal/at/QortalATAPI.java @@ -1,6 +1,5 @@ package org.qortal.at; -import java.math.BigDecimal; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -34,7 +33,7 @@ import com.google.common.primitives.Bytes; public class QortalATAPI extends API { // Useful constants - private static final BigDecimal FEE_PER_STEP = BigDecimal.valueOf(1.0).setScale(8); // 1 QORT per "step" + private static final long FEE_PER_STEP = 1 * Asset.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; @@ -62,8 +61,8 @@ public class QortalATAPI extends API { return this.transactions; } - public BigDecimal calcFinalFees(MachineState state) { - return FEE_PER_STEP.multiply(BigDecimal.valueOf(state.getSteps())); + public long calcFinalFees(MachineState state) { + return state.getSteps() * FEE_PER_STEP; } // Inherited methods from CIYAM AT API @@ -83,7 +82,7 @@ public class QortalATAPI extends API { @Override public long getFeePerStep() { - return FEE_PER_STEP.unscaledValue().longValue(); + return FEE_PER_STEP; } @Override @@ -254,23 +253,22 @@ public class QortalATAPI extends API { try { Account atAccount = this.getATAccount(); - return atAccount.getConfirmedBalance(Asset.QORT).unscaledValue().longValue(); + return atAccount.getConfirmedBalance(Asset.QORT); } catch (DataException e) { throw new RuntimeException("AT API unable to fetch AT's current balance?", e); } } @Override - public void payAmountToB(long unscaledAmount, MachineState state) { + public void payAmountToB(long amount, MachineState state) { byte[] publicKey = state.getB(); PublicKeyAccount recipient = new PublicKeyAccount(this.repository, publicKey); long timestamp = this.getNextTransactionTimestamp(); byte[] reference = this.getLastReference(); - BigDecimal amount = BigDecimal.valueOf(unscaledAmount, 8); - BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, BigDecimal.ZERO, null); + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, 0L, null); ATTransactionData atTransactionData = new ATTransactionData(baseTransactionData, this.atData.getATAddress(), recipient.getAddress(), amount, this.atData.getAssetId(), new byte[0]); AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData); @@ -289,9 +287,9 @@ public class QortalATAPI extends API { long timestamp = this.getNextTransactionTimestamp(); byte[] reference = this.getLastReference(); - BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, BigDecimal.ZERO, null); + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, 0L, null); ATTransactionData atTransactionData = new ATTransactionData(baseTransactionData, this.atData.getATAddress(), - recipient.getAddress(), BigDecimal.ZERO, this.atData.getAssetId(), message); + recipient.getAddress(), 0L, this.atData.getAssetId(), message); AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData); // Add to our transactions @@ -314,11 +312,10 @@ public class QortalATAPI extends API { Account creator = this.getCreator(); long timestamp = this.getNextTransactionTimestamp(); byte[] reference = this.getLastReference(); - BigDecimal amount = BigDecimal.valueOf(finalBalance, 8); - BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, BigDecimal.ZERO, null); + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, 0L, null); ATTransactionData atTransactionData = new ATTransactionData(baseTransactionData, this.atData.getATAddress(), - creator.getAddress(), amount, this.atData.getAssetId(), new byte[0]); + creator.getAddress(), finalBalance, this.atData.getAssetId(), new byte[0]); AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData); // Add to our transactions diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index f5d06e1a..abee08d8 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -50,6 +50,7 @@ import org.qortal.transform.TransformationException; import org.qortal.transform.Transformer; import org.qortal.transform.block.BlockTransformer; import org.qortal.transform.transaction.TransactionTransformer; +import org.qortal.utils.Amounts; import org.qortal.utils.Base58; import org.qortal.utils.NTP; import org.roaringbitmap.IntIterator; @@ -123,15 +124,14 @@ public class Block { /** Locally-generated AT states */ protected List ourAtStates; /** Locally-generated AT fees */ - protected BigDecimal ourAtFees; // Generated locally + protected long ourAtFees; // Generated locally /** Lazy-instantiated expanded info on block's online accounts. */ static class ExpandedAccount { - private static final BigDecimal oneHundred = BigDecimal.valueOf(100L); - private final Repository repository; private final RewardShareData rewardShareData; + private final int sharePercent; private final boolean isRecipientAlsoMinter; private final Account mintingAccount; @@ -145,6 +145,7 @@ public class Block { ExpandedAccount(Repository repository, int accountIndex) throws DataException { this.repository = repository; this.rewardShareData = repository.getAccountRepository().getRewardShareByIndex(accountIndex); + this.sharePercent = this.rewardShareData.getSharePercent(); this.mintingAccount = new Account(repository, this.rewardShareData.getMinter()); this.mintingAccountData = repository.getAccountRepository().getAccount(this.mintingAccount.getAddress()); @@ -187,26 +188,23 @@ public class Block { return -1; } - void distribute(BigDecimal accountAmount) throws DataException { + void distribute(long accountAmount) throws DataException { if (this.isRecipientAlsoMinter) { // minter & recipient the same - simpler case - LOGGER.trace(() -> String.format("Minter/recipient account %s share: %s", this.mintingAccount.getAddress(), accountAmount.toPlainString())); - if (accountAmount.signum() != 0) - // this.mintingAccount.setConfirmedBalance(Asset.QORT, this.mintingAccount.getConfirmedBalance(Asset.QORT).add(accountAmount)); + LOGGER.trace(() -> String.format("Minter/recipient account %s share: %s", this.mintingAccount.getAddress(), Amounts.prettyAmount(accountAmount))); + if (accountAmount != 0) this.repository.getAccountRepository().modifyAssetBalance(this.mintingAccount.getAddress(), Asset.QORT, accountAmount); } else { // minter & recipient different - extra work needed - BigDecimal recipientAmount = accountAmount.multiply(this.rewardShareData.getSharePercent()).divide(oneHundred, RoundingMode.DOWN); - BigDecimal minterAmount = accountAmount.subtract(recipientAmount); + long recipientAmount = (accountAmount * this.sharePercent) / 100L / 100L; // because scaled by 2dp and 'percent' means "per 1e2" + long minterAmount = accountAmount - recipientAmount; - LOGGER.trace(() -> String.format("Minter account %s share: %s", this.mintingAccount.getAddress(), minterAmount.toPlainString())); - if (minterAmount.signum() != 0) - // this.mintingAccount.setConfirmedBalance(Asset.QORT, this.mintingAccount.getConfirmedBalance(Asset.QORT).add(minterAmount)); + LOGGER.trace(() -> String.format("Minter account %s share: %s", this.mintingAccount.getAddress(), Amounts.prettyAmount(minterAmount))); + if (minterAmount != 0) this.repository.getAccountRepository().modifyAssetBalance(this.mintingAccount.getAddress(), Asset.QORT, minterAmount); - LOGGER.trace(() -> String.format("Recipient account %s share: %s", this.recipientAccount.getAddress(), recipientAmount.toPlainString())); - if (recipientAmount.signum() != 0) - // this.recipientAccount.setConfirmedBalance(Asset.QORT, this.recipientAccount.getConfirmedBalance(Asset.QORT).add(recipientAmount)); + LOGGER.trace(() -> String.format("Recipient account %s share: %s", this.recipientAccount.getAddress(), Amounts.prettyAmount(recipientAmount))); + if (recipientAmount != 0) this.repository.getAccountRepository().modifyAssetBalance(this.recipientAccount.getAddress(), Asset.QORT, recipientAmount); } } @@ -256,17 +254,17 @@ public class Block { this.transactions = new ArrayList<>(); - BigDecimal totalFees = BigDecimal.ZERO.setScale(8); + long totalFees = 0; // We have to sum fees too for (TransactionData transactionData : transactions) { this.transactions.add(Transaction.fromData(repository, transactionData)); - totalFees = totalFees.add(transactionData.getFee()); + totalFees += transactionData.getFee(); } this.atStates = atStates; for (ATStateData atState : atStates) - totalFees = totalFees.add(atState.getFees()); + totalFees += atState.getFees(); this.blockData.setTotalFees(totalFees); } @@ -361,8 +359,8 @@ public class Block { int height = parentBlockData.getHeight() + 1; int atCount = 0; - BigDecimal atFees = BigDecimal.ZERO.setScale(8); - BigDecimal totalFees = atFees; + long atFees = 0; + long totalFees = 0; // This instance used for AT processing BlockData preAtBlockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, @@ -427,12 +425,12 @@ public class Block { newBlock.transactions = this.transactions; int transactionCount = this.blockData.getTransactionCount(); - BigDecimal totalFees = this.blockData.getTotalFees(); + long totalFees = this.blockData.getTotalFees(); byte[] transactionsSignature = null; // We'll calculate this later Integer height = this.blockData.getHeight(); int atCount = newBlock.ourAtStates.size(); - BigDecimal atFees = newBlock.ourAtFees; + long atFees = newBlock.ourAtFees; byte[] encodedOnlineAccounts = this.blockData.getEncodedOnlineAccounts(); int onlineAccountsCount = this.blockData.getOnlineAccountsCount(); @@ -648,7 +646,7 @@ public class Block { this.blockData.setTransactionCount(this.blockData.getTransactionCount() + 1); // Update totalFees - this.blockData.setTotalFees(this.blockData.getTotalFees().add(transactionData.getFee())); + this.blockData.setTotalFees(this.blockData.getTotalFees() + transactionData.getFee()); // We've added a transaction, so recalculate transactions signature calcTransactionsSignature(); @@ -691,7 +689,7 @@ public class Block { this.blockData.setTransactionCount(this.blockData.getTransactionCount() - 1); // Update totalFees - this.blockData.setTotalFees(this.blockData.getTotalFees().subtract(transactionData.getFee())); + this.blockData.setTotalFees(this.blockData.getTotalFees() - transactionData.getFee()); // We've removed a transaction, so recalculate transactions signature calcTransactionsSignature(); @@ -1118,7 +1116,7 @@ public class Block { if (this.ourAtStates.size() != this.blockData.getATCount()) return ValidationResult.AT_STATES_MISMATCH; - if (this.ourAtFees.compareTo(this.blockData.getATFees()) != 0) + if (this.ourAtFees != this.blockData.getATFees()) return ValidationResult.AT_STATES_MISMATCH; // Note: this.atStates fully loaded thanks to this.getATStates() call above @@ -1132,7 +1130,7 @@ public class Block { if (!Arrays.equals(ourAtState.getStateHash(), theirAtState.getStateHash())) return ValidationResult.AT_STATES_MISMATCH; - if (ourAtState.getFees().compareTo(theirAtState.getFees()) != 0) + if (ourAtState.getFees() != theirAtState.getFees()) return ValidationResult.AT_STATES_MISMATCH; } @@ -1167,7 +1165,7 @@ public class Block { List allAtTransactions = new ArrayList<>(); this.ourAtStates = new ArrayList<>(); - this.ourAtFees = BigDecimal.ZERO.setScale(8); + this.ourAtFees = 0; // Find all executable ATs, ordered by earliest creation date first List executableATs = this.repository.getATRepository().getAllExecutableATs(); @@ -1182,7 +1180,7 @@ public class Block { ATStateData atStateData = at.getATStateData(); this.ourAtStates.add(atStateData); - this.ourAtFees = this.ourAtFees.add(atStateData.getFees()); + this.ourAtFees += atStateData.getFees(); } // Prepend our entire AT-Transactions/states to block's transactions @@ -1313,7 +1311,7 @@ public class Block { } protected void processBlockRewards() throws DataException { - BigDecimal reward = BlockChain.getInstance().getRewardAtHeight(this.blockData.getHeight()); + Long reward = BlockChain.getInstance().getRewardAtHeight(this.blockData.getHeight()); // No reward for our height? if (reward == null) @@ -1396,10 +1394,10 @@ public class Block { } protected void rewardTransactionFees() throws DataException { - BigDecimal blockFees = this.blockData.getTotalFees(); + long blockFees = this.blockData.getTotalFees(); // No transaction fees? - if (blockFees.compareTo(BigDecimal.ZERO) <= 0) + if (blockFees <= 0) return; distributeBlockReward(blockFees); @@ -1412,7 +1410,7 @@ public class Block { Account atAccount = new Account(this.repository, atState.getATAddress()); // Subtract AT-generated fees from AT accounts - atAccount.setConfirmedBalance(Asset.QORT, atAccount.getConfirmedBalance(Asset.QORT).subtract(atState.getFees())); + atAccount.setConfirmedBalance(Asset.QORT, atAccount.getConfirmedBalance(Asset.QORT) - atState.getFees()); atRepository.save(atState); } @@ -1439,8 +1437,7 @@ public class Block { // No longer unconfirmed transactionRepository.confirmTransaction(transactionData.getSignature()); - List participants = transaction.getInvolvedAccounts(); - List participantAddresses = participants.stream().map(Account::getAddress).collect(Collectors.toList()); + List participantAddresses = transaction.getInvolvedAddresses(); transactionRepository.saveParticipants(transactionData, participantAddresses); } } @@ -1547,23 +1544,23 @@ public class Block { } protected void orphanBlockRewards() throws DataException { - BigDecimal reward = BlockChain.getInstance().getRewardAtHeight(this.blockData.getHeight()); + Long reward = BlockChain.getInstance().getRewardAtHeight(this.blockData.getHeight()); // No reward for our height? if (reward == null) return; - distributeBlockReward(reward.negate()); + distributeBlockReward(0 - reward); } protected void deductTransactionFees() throws DataException { - BigDecimal blockFees = this.blockData.getTotalFees(); + long blockFees = this.blockData.getTotalFees(); // No transaction fees? - if (blockFees.compareTo(BigDecimal.ZERO) <= 0) + if (blockFees <= 0) return; - distributeBlockReward(blockFees.negate()); + distributeBlockReward(0 - blockFees); } protected void orphanAtFeesAndStates() throws DataException { @@ -1572,7 +1569,7 @@ public class Block { Account atAccount = new Account(this.repository, atState.getATAddress()); // Return AT-generated fees to AT accounts - atAccount.setConfirmedBalance(Asset.QORT, atAccount.getConfirmedBalance(Asset.QORT).add(atState.getFees())); + atAccount.setConfirmedBalance(Asset.QORT, atAccount.getConfirmedBalance(Asset.QORT) + atState.getFees()); } // Delete ATStateData for this height @@ -1630,175 +1627,168 @@ public class Block { } } - protected void distributeBlockReward(BigDecimal totalAmount) throws DataException { - LOGGER.trace(() -> String.format("Distributing: %s", totalAmount.toPlainString())); + protected void distributeBlockReward(long totalAmount) throws DataException { + LOGGER.trace(() -> String.format("Distributing: %s", Amounts.prettyAmount(totalAmount))); // Distribute according to account level - BigDecimal sharedByLevelAmount = distributeBlockRewardByLevel(totalAmount); - LOGGER.trace(() -> String.format("Shared %s of %s based on account levels", sharedByLevelAmount.toPlainString(), totalAmount.toPlainString())); + long sharedByLevelAmount = distributeBlockRewardByLevel(totalAmount); + LOGGER.trace(() -> String.format("Shared %s of %s based on account levels", Amounts.prettyAmount(sharedByLevelAmount), Amounts.prettyAmount(totalAmount))); // Distribute amongst legacy QORA holders - BigDecimal sharedByQoraHoldersAmount = distributeBlockRewardToQoraHolders(totalAmount); - LOGGER.trace(() -> String.format("Shared %s of %s to legacy QORA holders", sharedByQoraHoldersAmount.toPlainString(), totalAmount.toPlainString())); + long sharedByQoraHoldersAmount = distributeBlockRewardToQoraHolders(totalAmount); + LOGGER.trace(() -> String.format("Shared %s of %s to legacy QORA holders", Amounts.prettyAmount(sharedByQoraHoldersAmount), Amounts.prettyAmount(totalAmount))); // Spread remainder across founder accounts - BigDecimal foundersAmount = totalAmount.subtract(sharedByLevelAmount).subtract(sharedByQoraHoldersAmount); + long foundersAmount = totalAmount - sharedByLevelAmount - sharedByQoraHoldersAmount; distributeBlockRewardToFounders(foundersAmount); } - private BigDecimal distributeBlockRewardByLevel(BigDecimal totalAmount) throws DataException { + private long distributeBlockRewardByLevel(long totalAmount) throws DataException { List expandedAccounts = this.getExpandedAccounts(); List sharesByLevel = BlockChain.getInstance().getBlockSharesByLevel(); // Distribute amount across bins - BigDecimal sharedAmount = BigDecimal.ZERO; + long sharedAmount = 0; for (int s = 0; s < sharesByLevel.size(); ++s) { final int binIndex = s; - BigDecimal binAmount = sharesByLevel.get(binIndex).share.multiply(totalAmount).setScale(8, RoundingMode.DOWN); - LOGGER.trace(() -> String.format("Bin %d share of %s: %s", binIndex, totalAmount.toPlainString(), binAmount.toPlainString())); + long binAmount = (totalAmount * sharesByLevel.get(binIndex).unscaledShare) / 100000000L; + LOGGER.trace(() -> String.format("Bin %d share of %s: %s", binIndex, Amounts.prettyAmount(totalAmount), Amounts.prettyAmount(binAmount))); // Spread across all accounts in bin. getShareBin() returns -1 for minter accounts that are also founders, so they are effectively filtered out. List binnedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.getShareBin() == binIndex).collect(Collectors.toList()); if (binnedAccounts.isEmpty()) continue; - BigDecimal binSize = BigDecimal.valueOf(binnedAccounts.size()); - BigDecimal accountAmount = binAmount.divide(binSize, RoundingMode.DOWN); + long perAccountAmount = binAmount / binnedAccounts.size(); for (int a = 0; a < binnedAccounts.size(); ++a) { ExpandedAccount expandedAccount = binnedAccounts.get(a); - expandedAccount.distribute(accountAmount); - sharedAmount = sharedAmount.add(accountAmount); + expandedAccount.distribute(perAccountAmount); + sharedAmount += perAccountAmount; } } return sharedAmount; } - private BigDecimal distributeBlockRewardToQoraHolders(BigDecimal totalAmount) throws DataException { - BigDecimal qoraHoldersAmount = BlockChain.getInstance().getQoraHoldersShare().multiply(totalAmount).setScale(8, RoundingMode.DOWN); - LOGGER.trace(() -> String.format("Legacy QORA holders share of %s: %s", totalAmount.toPlainString(), qoraHoldersAmount.toPlainString())); + private long distributeBlockRewardToQoraHolders(long totalAmount) throws DataException { + long qoraHoldersAmount = (BlockChain.getInstance().getQoraHoldersUnscaledShare() * totalAmount) / 100000000L; + LOGGER.trace(() -> String.format("Legacy QORA holders share of %s: %s", Amounts.prettyAmount(totalAmount), Amounts.prettyAmount(qoraHoldersAmount))); - final boolean isProcessingNotOrphaning = totalAmount.signum() >= 0; + final boolean isProcessingNotOrphaning = totalAmount >= 0; - BigDecimal qoraPerQortReward = BlockChain.getInstance().getQoraPerQortReward(); + long qoraPerQortReward = BlockChain.getInstance().getUnscaledQoraPerQortReward(); List qoraHolders = this.repository.getAccountRepository().getEligibleLegacyQoraHolders(isProcessingNotOrphaning ? null : this.blockData.getHeight()); - BigDecimal totalQoraHeld = BigDecimal.ZERO; + long totalQoraHeld = 0; for (int i = 0; i < qoraHolders.size(); ++i) - totalQoraHeld = totalQoraHeld.add(qoraHolders.get(i).getBalance()); + totalQoraHeld += qoraHolders.get(i).getBalance(); - BigDecimal finalTotalQoraHeld = totalQoraHeld; - LOGGER.trace(() -> String.format("Total legacy QORA held: %s", finalTotalQoraHeld.toPlainString())); + long finalTotalQoraHeld = totalQoraHeld; + LOGGER.trace(() -> String.format("Total legacy QORA held: %s", Amounts.prettyAmount(finalTotalQoraHeld))); - BigDecimal sharedAmount = BigDecimal.ZERO; - if (totalQoraHeld.signum() <= 0) + long sharedAmount = 0; + if (totalQoraHeld <= 0) return sharedAmount; for (int h = 0; h < qoraHolders.size(); ++h) { AccountBalanceData qoraHolder = qoraHolders.get(h); - BigDecimal holderReward = qoraHoldersAmount.multiply(qoraHolder.getBalance()).divide(totalQoraHeld, RoundingMode.DOWN).setScale(8, RoundingMode.DOWN); - BigDecimal finalHolderReward = holderReward; + long holderReward = (qoraHoldersAmount * qoraHolder.getBalance()) / totalQoraHeld; + long finalHolderReward = holderReward; LOGGER.trace(() -> String.format("QORA holder %s has %s / %s QORA so share: %s", - qoraHolder.getAddress(), qoraHolder.getBalance().toPlainString(), finalTotalQoraHeld, finalHolderReward.toPlainString())); + qoraHolder.getAddress(), Amounts.prettyAmount(qoraHolder.getBalance()), finalTotalQoraHeld, Amounts.prettyAmount(finalHolderReward))); // Too small to register this time? - if (holderReward.signum() == 0) + if (holderReward == 0) continue; Account qoraHolderAccount = new Account(repository, qoraHolder.getAddress()); - BigDecimal newQortFromQoraBalance = qoraHolderAccount.getConfirmedBalance(Asset.QORT_FROM_QORA).add(holderReward); + long newQortFromQoraBalance = qoraHolderAccount.getConfirmedBalance(Asset.QORT_FROM_QORA) + holderReward; // If processing, make sure we don't overpay if (isProcessingNotOrphaning) { - BigDecimal maxQortFromQora = qoraHolder.getBalance().divide(qoraPerQortReward, RoundingMode.DOWN); + long maxQortFromQora = qoraHolder.getBalance() / qoraPerQortReward; - if (newQortFromQoraBalance.compareTo(maxQortFromQora) >= 0) { + if (newQortFromQoraBalance >= maxQortFromQora) { // Reduce final QORT-from-QORA payment to match max - BigDecimal adjustment = newQortFromQoraBalance.subtract(maxQortFromQora); + long adjustment = newQortFromQoraBalance - maxQortFromQora; - holderReward = holderReward.subtract(adjustment); - newQortFromQoraBalance = newQortFromQoraBalance.subtract(adjustment); + holderReward -= adjustment; + newQortFromQoraBalance -= adjustment; // This is also the QORA holder's final QORT-from-QORA block QortFromQoraData qortFromQoraData = new QortFromQoraData(qoraHolder.getAddress(), holderReward, this.blockData.getHeight()); this.repository.getAccountRepository().save(qortFromQoraData); - BigDecimal finalAdjustedHolderReward = holderReward; + long finalAdjustedHolderReward = holderReward; LOGGER.trace(() -> String.format("QORA holder %s final share %s at height %d", - qoraHolder.getAddress(), finalAdjustedHolderReward.toPlainString(), this.blockData.getHeight())); + qoraHolder.getAddress(), Amounts.prettyAmount(finalAdjustedHolderReward), this.blockData.getHeight())); } } else { // Orphaning QortFromQoraData qortFromQoraData = this.repository.getAccountRepository().getQortFromQoraInfo(qoraHolder.getAddress()); if (qortFromQoraData != null) { // Final QORT-from-QORA amount from repository was stored during processing, and hence positive. - // So we use add() here as qortFromQora is negative during orphaning. - // More efficient than holderReward.subtract(final-qort-from-qora.negate()) - BigDecimal adjustment = holderReward.add(qortFromQoraData.getFinalQortFromQora()); + // So we use + here as qortFromQora is negative during orphaning. + // More efficient than "holderReward - (0 - final-qort-from-qora)" + long adjustment = holderReward + qortFromQoraData.getFinalQortFromQora(); - holderReward = holderReward.subtract(adjustment); - newQortFromQoraBalance = newQortFromQoraBalance.subtract(adjustment); + holderReward -= adjustment; + newQortFromQoraBalance -= adjustment; this.repository.getAccountRepository().deleteQortFromQoraInfo(qoraHolder.getAddress()); - BigDecimal finalAdjustedHolderReward = holderReward; + long finalAdjustedHolderReward = holderReward; LOGGER.trace(() -> String.format("QORA holder %s final share %s was at height %d", - qoraHolder.getAddress(), finalAdjustedHolderReward.toPlainString(), this.blockData.getHeight())); + qoraHolder.getAddress(), Amounts.prettyAmount(finalAdjustedHolderReward), this.blockData.getHeight())); } } - // qoraHolderAccount.setConfirmedBalance(Asset.QORT, qoraHolderAccount.getConfirmedBalance(Asset.QORT).add(holderReward)); this.repository.getAccountRepository().modifyAssetBalance(qoraHolder.getAddress(), Asset.QORT, holderReward); - if (newQortFromQoraBalance.signum() > 0) + if (newQortFromQoraBalance > 0) qoraHolderAccount.setConfirmedBalance(Asset.QORT_FROM_QORA, newQortFromQoraBalance); else // Remove QORT_FROM_QORA balance as it's zero qoraHolderAccount.deleteBalance(Asset.QORT_FROM_QORA); - sharedAmount = sharedAmount.add(holderReward); + sharedAmount += holderReward; } return sharedAmount; } - private void distributeBlockRewardToFounders(BigDecimal foundersAmount) throws DataException { + private void distributeBlockRewardToFounders(long foundersAmount) throws DataException { // Remaining reward portion is spread across all founders, online or not List founderAccounts = this.repository.getAccountRepository().getFlaggedAccounts(Account.FOUNDER_FLAG); - BigDecimal foundersCount = BigDecimal.valueOf(founderAccounts.size()); - BigDecimal perFounderAmount = foundersAmount.divide(foundersCount, RoundingMode.DOWN); + + long foundersCount = founderAccounts.size(); + long perFounderAmount = foundersAmount / foundersCount; LOGGER.trace(() -> String.format("Sharing remaining %s to %d founder%s, %s each", - foundersAmount.toPlainString(), founderAccounts.size(), (founderAccounts.size() != 1 ? "s" : ""), - perFounderAmount.toPlainString())); + Amounts.prettyAmount(foundersAmount), + founderAccounts.size(), (founderAccounts.size() != 1 ? "s" : ""), + Amounts.prettyAmount(perFounderAmount))); List expandedAccounts = this.getExpandedAccounts(); for (int a = 0; a < founderAccounts.size(); ++a) { Account founderAccount = new Account(this.repository, founderAccounts.get(a).getAddress()); // If founder is minter in any online reward-shares then founder's amount is spread across these, otherwise founder gets whole amount. - - /* Fixed version: List founderExpandedAccounts = expandedAccounts.stream().filter( accountInfo -> accountInfo.isMinterFounder && accountInfo.mintingAccountData.getAddress().equals(founderAccount.getAddress()) ).collect(Collectors.toList()); - */ - - // Broken version: - List founderExpandedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.isMinterFounder).collect(Collectors.toList()); if (founderExpandedAccounts.isEmpty()) { // Simple case: no founder-as-minter reward-shares online so founder gets whole amount. - // founderAccount.setConfirmedBalance(Asset.QORT, founderAccount.getConfirmedBalance(Asset.QORT).add(perFounderAmount)); this.repository.getAccountRepository().modifyAssetBalance(founderAccount.getAddress(), Asset.QORT, perFounderAmount); } else { // Distribute over reward-shares - BigDecimal perFounderRewardShareAmount = perFounderAmount.divide(BigDecimal.valueOf(founderExpandedAccounts.size()), RoundingMode.DOWN); + long perFounderRewardShareAmount = perFounderAmount / founderExpandedAccounts.size(); for (int fea = 0; fea < founderExpandedAccounts.size(); ++fea) founderExpandedAccounts.get(fea).distribute(perFounderRewardShareAmount); diff --git a/src/main/java/org/qortal/block/BlockChain.java b/src/main/java/org/qortal/block/BlockChain.java index 90fc98ef..a2672252 100644 --- a/src/main/java/org/qortal/block/BlockChain.java +++ b/src/main/java/org/qortal/block/BlockChain.java @@ -4,7 +4,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.InputStream; import java.math.BigDecimal; -import java.math.MathContext; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; @@ -57,8 +56,9 @@ public class BlockChain { private long transactionExpiryPeriod; private BigDecimal unitFee; - private BigDecimal maxBytesPerUnitFee; - private BigDecimal minFeePerByte; + private long unscaledUnitFee; // calculated after unmarshal + + private int maxBytesPerUnitFee; /** Maximum acceptable timestamp disagreement offset in milliseconds. */ private long blockTimestampMargin; @@ -87,6 +87,7 @@ public class BlockChain { public static class RewardByHeight { public int height; public BigDecimal reward; + public long unscaledReward; // reward * 1e8, calculated after unmarshal } List rewardsByHeight; @@ -94,13 +95,18 @@ public class BlockChain { public static class ShareByLevel { public List levels; public BigDecimal share; + public long unscaledShare; // share * 1e8, calculated after unmarshal } List sharesByLevel; /** Share of block reward/fees to legacy QORA coin holders */ 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; /** * Number of minted blocks required to reach next level from previous. @@ -265,12 +271,12 @@ public class BlockChain { return this.unitFee; } - public BigDecimal getMaxBytesPerUnitFee() { - return this.maxBytesPerUnitFee; + public long getUnscaledUnitFee() { + return this.unscaledUnitFee; } - public BigDecimal getMinFeePerByte() { - return this.minFeePerByte; + public int getMaxBytesPerUnitFee() { + return this.maxBytesPerUnitFee; } public long getTransactionExpiryPeriod() { @@ -318,10 +324,18 @@ public class BlockChain { return this.qoraHoldersShare; } + public long getQoraHoldersUnscaledShare() { + return this.qoraHoldersUnscaledShare; + } + public BigDecimal getQoraPerQortReward() { return this.qoraPerQortReward; } + public long getUnscaledQoraPerQortReward() { + return this.unscaledQoraPerQortReward; + } + public int getMinAccountLevelToMint() { return this.minAccountLevelToMint; } @@ -350,11 +364,11 @@ public class BlockChain { // More complex getters for aspects that change by height or timestamp - public BigDecimal getRewardAtHeight(int ourHeight) { + public Long getRewardAtHeight(int ourHeight) { // Scan through for reward at our height for (int i = rewardsByHeight.size() - 1; i >= 0; --i) if (rewardsByHeight.get(i).height <= ourHeight) - return rewardsByHeight.get(i).reward; + return rewardsByHeight.get(i).unscaledReward; return null; } @@ -416,9 +430,8 @@ public class BlockChain { /** Minor normalization, cached value generation, etc. */ private void fixUp() { - this.maxBytesPerUnitFee = this.maxBytesPerUnitFee.setScale(8); this.unitFee = this.unitFee.setScale(8); - this.minFeePerByte = this.unitFee.divide(this.maxBytesPerUnitFee, MathContext.DECIMAL32); + this.unscaledUnitFee = this.unitFee.unscaledValue().longValue(); // Pre-calculate cumulative blocks required for each level int cumulativeBlocks = 0; @@ -430,6 +443,17 @@ public class BlockChain { cumulativeBlocks += this.blocksNeededByLevel.get(level); } + // Calculate unscaled long versions of block rewards by height + for (RewardByHeight rewardByHeight : this.rewardsByHeight) + rewardByHeight.unscaledReward = rewardByHeight.reward.setScale(8).unscaledValue().longValue(); + + // Calculate unscaled long versions of block reward shares-by-level + for (ShareByLevel shareByLevel : this.sharesByLevel) + shareByLevel.unscaledShare = shareByLevel.share.setScale(8).unscaledValue().longValue(); + + // Calculate unscaled long version of Qora-holders block reward share + this.qoraHoldersUnscaledShare = this.qoraHoldersShare.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/block/GenesisBlock.java b/src/main/java/org/qortal/block/GenesisBlock.java index 610528d2..966cf884 100644 --- a/src/main/java/org/qortal/block/GenesisBlock.java +++ b/src/main/java/org/qortal/block/GenesisBlock.java @@ -2,7 +2,6 @@ package org.qortal.block; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -86,7 +85,7 @@ public class GenesisBlock extends Block { // Add default values to transactions transactionsData.stream().forEach(transactionData -> { if (transactionData.getFee() == null) - transactionData.setFee(BigDecimal.ZERO.setScale(8)); + transactionData.setFee(0L); if (transactionData.getCreatorPublicKey() == null) transactionData.setCreatorPublicKey(NullAccount.PUBLIC_KEY); @@ -97,14 +96,14 @@ public class GenesisBlock extends Block { byte[] reference = GENESIS_BLOCK_REFERENCE; int transactionCount = transactionsData.size(); - BigDecimal totalFees = BigDecimal.ZERO.setScale(8); + long totalFees = 0; byte[] minterPublicKey = NullAccount.PUBLIC_KEY; byte[] bytesForSignature = getBytesForMinterSignature(info.timestamp, reference, minterPublicKey); byte[] minterSignature = calcGenesisMinterSignature(bytesForSignature); byte[] transactionsSignature = calcGenesisTransactionsSignature(); int height = 1; int atCount = 0; - BigDecimal atFees = BigDecimal.ZERO.setScale(8); + long atFees = 0; genesisBlockData = new BlockData(info.version, reference, transactionCount, totalFees, transactionsSignature, height, info.timestamp, minterPublicKey, minterSignature, atCount, atFees); diff --git a/src/main/java/org/qortal/data/PaymentData.java b/src/main/java/org/qortal/data/PaymentData.java index 8dbcc58f..c2b58b10 100644 --- a/src/main/java/org/qortal/data/PaymentData.java +++ b/src/main/java/org/qortal/data/PaymentData.java @@ -1,18 +1,21 @@ package org.qortal.data; -import java.math.BigDecimal; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) public class PaymentData { // Properties + private String recipient; + private long assetId; - private BigDecimal amount; + + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long amount; // Constructors @@ -20,7 +23,7 @@ public class PaymentData { protected PaymentData() { } - public PaymentData(String recipient, long assetId, BigDecimal amount) { + public PaymentData(String recipient, long assetId, long amount) { this.recipient = recipient; this.assetId = assetId; this.amount = amount; @@ -36,7 +39,7 @@ public class PaymentData { return this.assetId; } - public BigDecimal getAmount() { + public long getAmount() { return this.amount; } diff --git a/src/main/java/org/qortal/data/account/AccountBalanceData.java b/src/main/java/org/qortal/data/account/AccountBalanceData.java index be4f69d9..68bf1aab 100644 --- a/src/main/java/org/qortal/data/account/AccountBalanceData.java +++ b/src/main/java/org/qortal/data/account/AccountBalanceData.java @@ -1,9 +1,9 @@ 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.adapters.XmlJavaTypeAdapter; // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) @@ -12,7 +12,9 @@ public class AccountBalanceData { // Properties private String address; private long assetId; - private BigDecimal balance; + + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long balance; // Not always present: private Integer height; @@ -24,19 +26,19 @@ public class AccountBalanceData { protected AccountBalanceData() { } - public AccountBalanceData(String address, long assetId, BigDecimal balance) { + public AccountBalanceData(String address, long assetId, long balance) { this.address = address; this.assetId = assetId; this.balance = balance; } - public AccountBalanceData(String address, long assetId, BigDecimal balance, int height) { + public AccountBalanceData(String address, long assetId, long balance, int height) { this(address, assetId, balance); this.height = height; } - public AccountBalanceData(String address, long assetId, BigDecimal balance, String assetName) { + public AccountBalanceData(String address, long assetId, long balance, String assetName) { this(address, assetId, balance); this.assetName = assetName; @@ -52,11 +54,11 @@ public class AccountBalanceData { return this.assetId; } - public BigDecimal getBalance() { + public long getBalance() { return this.balance; } - public void setBalance(BigDecimal balance) { + public void setBalance(long balance) { this.balance = balance; } diff --git a/src/main/java/org/qortal/data/account/QortFromQoraData.java b/src/main/java/org/qortal/data/account/QortFromQoraData.java index 97219d1e..fbff4d07 100644 --- a/src/main/java/org/qortal/data/account/QortFromQoraData.java +++ b/src/main/java/org/qortal/data/account/QortFromQoraData.java @@ -1,18 +1,23 @@ 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.adapters.XmlJavaTypeAdapter; // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) public class QortFromQoraData { // Properties + private String address; - // Not always present: - private BigDecimal finalQortFromQora; + + // Not always present + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private Long finalQortFromQora; + + // Not always present private Integer finalBlockHeight; // Constructors @@ -21,7 +26,7 @@ public class QortFromQoraData { protected QortFromQoraData() { } - public QortFromQoraData(String address, BigDecimal finalQortFromQora, Integer finalBlockHeight) { + public QortFromQoraData(String address, Long finalQortFromQora, Integer finalBlockHeight) { this.address = address; this.finalQortFromQora = finalQortFromQora; this.finalBlockHeight = finalBlockHeight; @@ -33,11 +38,11 @@ public class QortFromQoraData { return this.address; } - public BigDecimal getFinalQortFromQora() { + public Long getFinalQortFromQora() { return this.finalQortFromQora; } - public void setFinalQortFromQora(BigDecimal finalQortFromQora) { + public void setFinalQortFromQora(Long finalQortFromQora) { this.finalQortFromQora = finalQortFromQora; } diff --git a/src/main/java/org/qortal/data/account/RewardShareData.java b/src/main/java/org/qortal/data/account/RewardShareData.java index 2ffaf8d6..ffb46f8f 100644 --- a/src/main/java/org/qortal/data/account/RewardShareData.java +++ b/src/main/java/org/qortal/data/account/RewardShareData.java @@ -23,7 +23,11 @@ public class RewardShareData { private String recipient; private byte[] rewardSharePublicKey; - private BigDecimal sharePercent; + + // JAXB to use separate getter + @XmlTransient + @Schema(hidden = true) + private int sharePercent; // Constructors @@ -32,7 +36,7 @@ public class RewardShareData { } // Used when fetching from repository - public RewardShareData(byte[] minterPublicKey, String minter, String recipient, byte[] rewardSharePublicKey, BigDecimal sharePercent) { + public RewardShareData(byte[] minterPublicKey, String minter, String recipient, byte[] rewardSharePublicKey, int sharePercent) { this.minterPublicKey = minterPublicKey; this.minter = minter; this.recipient = recipient; @@ -58,13 +62,21 @@ public class RewardShareData { return this.rewardSharePublicKey; } - public BigDecimal getSharePercent() { + /** Returns share percent scaled by 100. i.e. 12.34% is represented by 1234 */ + public int getSharePercent() { return this.sharePercent; } + // Some JAXB/API-related getters + @XmlElement(name = "mintingAccount") public String getMintingAccount() { return this.minter; } + @XmlElement(name = "sharePercent") + public BigDecimal getSharePercentJaxb() { + return BigDecimal.valueOf(this.sharePercent, 2); + } + } diff --git a/src/main/java/org/qortal/data/asset/AssetData.java b/src/main/java/org/qortal/data/asset/AssetData.java index b094686d..b98919e4 100644 --- a/src/main/java/org/qortal/data/asset/AssetData.java +++ b/src/main/java/org/qortal/data/asset/AssetData.java @@ -27,7 +27,7 @@ public class AssetData { // Constructors - // necessary for JAX-RS serialization + // necessary for JAXB serialization protected AssetData() { } diff --git a/src/main/java/org/qortal/data/asset/OrderData.java b/src/main/java/org/qortal/data/asset/OrderData.java index 9314c220..eb4e5c63 100644 --- a/src/main/java/org/qortal/data/asset/OrderData.java +++ b/src/main/java/org/qortal/data/asset/OrderData.java @@ -1,10 +1,9 @@ package org.qortal.data.asset; -import java.math.BigDecimal; - import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.qortal.crypto.Crypto; @@ -26,13 +25,16 @@ public class OrderData implements Comparable { private long wantAssetId; @Schema(description = "amount of highest-assetID asset to trade") - private BigDecimal amount; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long amount; @Schema(description = "price in lowest-assetID asset / highest-assetID asset") - private BigDecimal price; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long price; @Schema(description = "how much of \"amount\" has traded") - private BigDecimal fulfilled; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long fulfilled; private long timestamp; @@ -73,26 +75,23 @@ public class OrderData implements Comparable { if (this.creator == null && this.creatorPublicKey != null) this.creator = Crypto.toAddress(this.creatorPublicKey); + this.amountAssetId = Math.max(this.haveAssetId, this.wantAssetId); + // If we don't have the extra asset name fields then we can't fill in the others if (this.haveAssetName == null) return; - // TODO: fill in for 'old' pricing scheme - - // 'new' pricing scheme if (this.haveAssetId < this.wantAssetId) { - this.amountAssetId = this.wantAssetId; this.amountAssetName = this.wantAssetName; this.pricePair = this.haveAssetName + "/" + this.wantAssetName; } else { - this.amountAssetId = this.haveAssetId; this.amountAssetName = this.haveAssetName; this.pricePair = this.wantAssetName + "/" + this.haveAssetName; } } /** Constructs OrderData using data from repository, including optional API fields. */ - public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal fulfilled, BigDecimal price, long timestamp, + public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, long amount, long fulfilled, long price, long timestamp, boolean isClosed, boolean isFulfilled, String haveAssetName, String wantAssetName) { this.orderId = orderId; this.creatorPublicKey = creatorPublicKey; @@ -110,13 +109,13 @@ public class OrderData implements Comparable { } /** Constructs OrderData using data from repository, excluding optional API fields. */ - public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal fulfilled, BigDecimal price, long timestamp, boolean isClosed, boolean isFulfilled) { + public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, long amount, long fulfilled, long price, long timestamp, boolean isClosed, boolean isFulfilled) { this(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, fulfilled, price, timestamp, isClosed, isFulfilled, null, null); } /** Constructs OrderData using data typically received from network. */ - public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, long timestamp) { - this(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, BigDecimal.ZERO.setScale(8), price, timestamp, false, false); + public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, long amount, long price, long timestamp) { + this(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, 0 /*fulfilled*/, price, timestamp, false, false); } // Getters/setters @@ -137,19 +136,19 @@ public class OrderData implements Comparable { return this.wantAssetId; } - public BigDecimal getAmount() { + public long getAmount() { return this.amount; } - public BigDecimal getFulfilled() { + public long getFulfilled() { return this.fulfilled; } - public void setFulfilled(BigDecimal fulfilled) { + public void setFulfilled(long fulfilled) { this.fulfilled = fulfilled; } - public BigDecimal getPrice() { + public long getPrice() { return this.price; } @@ -198,7 +197,7 @@ public class OrderData implements Comparable { @Override public int compareTo(OrderData orderData) { // Compare using prices - return this.price.compareTo(orderData.getPrice()); + return Long.compare(this.price, orderData.getPrice()); } } diff --git a/src/main/java/org/qortal/data/asset/RecentTradeData.java b/src/main/java/org/qortal/data/asset/RecentTradeData.java index 6b126a53..56a83180 100644 --- a/src/main/java/org/qortal/data/asset/RecentTradeData.java +++ b/src/main/java/org/qortal/data/asset/RecentTradeData.java @@ -20,9 +20,7 @@ public class RecentTradeData { private BigDecimal amount; - @Schema( - description = "when trade happened" - ) + @Schema(description = "when trade happened") private long timestamp; // Constructors @@ -31,11 +29,11 @@ public class RecentTradeData { protected RecentTradeData() { } - public RecentTradeData(long assetId, long otherAssetId, BigDecimal otherAmount, BigDecimal amount, long timestamp) { + public RecentTradeData(long assetId, long otherAssetId, long otherAmount, long amount, long timestamp) { this.assetId = assetId; this.otherAssetId = otherAssetId; - this.otherAmount = otherAmount; - this.amount = amount; + this.otherAmount = BigDecimal.valueOf(otherAmount, 8); + this.amount = BigDecimal.valueOf(amount, 8); this.timestamp = timestamp; } diff --git a/src/main/java/org/qortal/data/asset/TradeData.java b/src/main/java/org/qortal/data/asset/TradeData.java index a5b1b460..af827f38 100644 --- a/src/main/java/org/qortal/data/asset/TradeData.java +++ b/src/main/java/org/qortal/data/asset/TradeData.java @@ -1,12 +1,11 @@ package org.qortal.data.asset; -import java.math.BigDecimal; - import javax.xml.bind.Marshaller; 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; import io.swagger.v3.oas.annotations.media.Schema.AccessMode; @@ -24,17 +23,17 @@ public class TradeData { @XmlElement(name = "targetOrderId") private byte[] target; - @Schema(name = "targetAmount", description = "amount traded from target") - @XmlElement(name = "targetAmount") - private BigDecimal targetAmount; + @Schema(description = "amount traded from target") + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long targetAmount; - @Schema(name = "initiatorAmount", description = "amount traded from initiator") - @XmlElement(name = "initiatorAmount") - private BigDecimal initiatorAmount; + @Schema(description = "amount traded from initiator") + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long initiatorAmount; - @Schema(name = "initiatorSaving", description = "amount refunded to initiator due to price improvement") - @XmlElement(name = "initiatorSaving") - private BigDecimal initiatorSaving; + @Schema(description = "amount refunded to initiator due to price improvement") + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long initiatorSaving; @Schema(description = "when trade happened") private long timestamp; @@ -95,7 +94,7 @@ public class TradeData { } } - public TradeData(byte[] initiator, byte[] target, BigDecimal targetAmount, BigDecimal initiatorAmount, BigDecimal initiatorSaving, long timestamp, + public TradeData(byte[] initiator, byte[] target, long targetAmount, long initiatorAmount, long initiatorSaving, long timestamp, Long haveAssetId, String haveAssetName, Long wantAssetId, String wantAssetName) { this.initiator = initiator; this.target = target; @@ -110,7 +109,7 @@ public class TradeData { this.wantAssetName = wantAssetName; } - public TradeData(byte[] initiator, byte[] target, BigDecimal targetAmount, BigDecimal initiatorAmount, BigDecimal initiatorSaving, long timestamp) { + public TradeData(byte[] initiator, byte[] target, long targetAmount, long initiatorAmount, long initiatorSaving, long timestamp) { this(initiator, target, targetAmount, initiatorAmount, initiatorSaving, timestamp, null, null, null, null); } @@ -124,15 +123,15 @@ public class TradeData { return this.target; } - public BigDecimal getTargetAmount() { + public long getTargetAmount() { return this.targetAmount; } - public BigDecimal getInitiatorAmount() { + public long getInitiatorAmount() { return this.initiatorAmount; } - public BigDecimal getInitiatorSaving() { + public long getInitiatorSaving() { return this.initiatorSaving; } diff --git a/src/main/java/org/qortal/data/at/ATData.java b/src/main/java/org/qortal/data/at/ATData.java index 0ab73bdc..755c6caf 100644 --- a/src/main/java/org/qortal/data/at/ATData.java +++ b/src/main/java/org/qortal/data/at/ATData.java @@ -1,7 +1,5 @@ package org.qortal.data.at; -import java.math.BigDecimal; - public class ATData { // Properties @@ -16,12 +14,12 @@ public class ATData { private boolean isFinished; private boolean hadFatalError; private boolean isFrozen; - private BigDecimal frozenBalance; + private Long frozenBalance; // Constructors public ATData(String ATAddress, byte[] creatorPublicKey, long creation, int version, long assetId, byte[] codeBytes, boolean isSleeping, - Integer sleepUntilHeight, boolean isFinished, boolean hadFatalError, boolean isFrozen, BigDecimal frozenBalance) { + Integer sleepUntilHeight, boolean isFinished, boolean hadFatalError, boolean isFrozen, Long frozenBalance) { this.ATAddress = ATAddress; this.creatorPublicKey = creatorPublicKey; this.creation = creation; @@ -36,16 +34,6 @@ public class ATData { this.frozenBalance = frozenBalance; } - public ATData(String ATAddress, byte[] creatorPublicKey, long creation, int version, long assetId, byte[] codeBytes, boolean isSleeping, - Integer sleepUntilHeight, boolean isFinished, boolean hadFatalError, boolean isFrozen, Long frozenBalance) { - this(ATAddress, creatorPublicKey, creation, version, assetId, codeBytes, isSleeping, sleepUntilHeight, isFinished, hadFatalError, isFrozen, - (BigDecimal) null); - - // Convert Long frozenBalance to BigDecimal - if (frozenBalance != null) - this.frozenBalance = BigDecimal.valueOf(frozenBalance, 8); - } - // Getters / setters public String getATAddress() { @@ -112,11 +100,11 @@ public class ATData { this.isFrozen = isFrozen; } - public BigDecimal getFrozenBalance() { + public Long getFrozenBalance() { return this.frozenBalance; } - public void setFrozenBalance(BigDecimal frozenBalance) { + public void setFrozenBalance(Long frozenBalance) { this.frozenBalance = frozenBalance; } diff --git a/src/main/java/org/qortal/data/at/ATStateData.java b/src/main/java/org/qortal/data/at/ATStateData.java index aa8cf4cd..a1131b4b 100644 --- a/src/main/java/org/qortal/data/at/ATStateData.java +++ b/src/main/java/org/qortal/data/at/ATStateData.java @@ -1,7 +1,5 @@ package org.qortal.data.at; -import java.math.BigDecimal; - public class ATStateData { // Properties @@ -10,12 +8,12 @@ public class ATStateData { private Long creation; private byte[] stateData; private byte[] stateHash; - private BigDecimal fees; + private Long fees; // Constructors /** Create new ATStateData */ - public ATStateData(String ATAddress, Integer height, Long creation, byte[] stateData, byte[] stateHash, BigDecimal fees) { + public ATStateData(String ATAddress, Integer height, Long creation, byte[] stateData, byte[] stateHash, Long fees) { this.ATAddress = ATAddress; this.height = height; this.creation = creation; @@ -25,7 +23,7 @@ public class ATStateData { } /** For recreating per-block ATStateData from repository where not all info is needed */ - public ATStateData(String ATAddress, int height, byte[] stateHash, BigDecimal fees) { + public ATStateData(String ATAddress, int height, byte[] stateHash, Long fees) { this(ATAddress, height, null, null, stateHash, fees); } @@ -35,7 +33,7 @@ public class ATStateData { } /** For creating ATStateData from serialized bytes when we don't have all the info */ - public ATStateData(String ATAddress, byte[] stateHash, BigDecimal fees) { + public ATStateData(String ATAddress, byte[] stateHash, Long fees) { this(ATAddress, null, null, null, stateHash, fees); } @@ -66,7 +64,7 @@ public class ATStateData { return this.stateHash; } - public BigDecimal getFees() { + public Long getFees() { return this.fees; } diff --git a/src/main/java/org/qortal/data/block/BlockData.java b/src/main/java/org/qortal/data/block/BlockData.java index ac216f4f..63f40a47 100644 --- a/src/main/java/org/qortal/data/block/BlockData.java +++ b/src/main/java/org/qortal/data/block/BlockData.java @@ -3,11 +3,11 @@ package org.qortal.data.block; import com.google.common.primitives.Bytes; import java.io.Serializable; -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.adapters.XmlJavaTypeAdapter; import org.qortal.crypto.Crypto; @@ -23,14 +23,20 @@ public class BlockData implements Serializable { private int version; private byte[] reference; private int transactionCount; - private BigDecimal totalFees; + + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long totalFees; + private byte[] transactionsSignature; private Integer height; private long timestamp; private byte[] minterPublicKey; private byte[] minterSignature; private int atCount; - private BigDecimal atFees; + + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long atFees; + private byte[] encodedOnlineAccounts; private int onlineAccountsCount; private Long onlineAccountsTimestamp; @@ -42,8 +48,8 @@ public class BlockData implements Serializable { protected BlockData() { } - public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp, - byte[] minterPublicKey, byte[] minterSignature, int atCount, BigDecimal atFees, + public BlockData(int version, byte[] reference, int transactionCount, long totalFees, byte[] transactionsSignature, Integer height, long timestamp, + byte[] minterPublicKey, byte[] minterSignature, int atCount, long atFees, byte[] encodedOnlineAccounts, int onlineAccountsCount, Long onlineAccountsTimestamp, byte[] onlineAccountsSignatures) { this.version = version; this.reference = reference; @@ -67,8 +73,8 @@ public class BlockData implements Serializable { this.signature = null; } - public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp, - byte[] minterPublicKey, byte[] minterSignature, int atCount, BigDecimal atFees) { + public BlockData(int version, byte[] reference, int transactionCount, long totalFees, byte[] transactionsSignature, Integer height, long timestamp, + byte[] minterPublicKey, byte[] minterSignature, int atCount, long atFees) { this(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, minterPublicKey, minterSignature, atCount, atFees, null, 0, null, null); } @@ -103,11 +109,11 @@ public class BlockData implements Serializable { this.transactionCount = transactionCount; } - public BigDecimal getTotalFees() { + public long getTotalFees() { return this.totalFees; } - public void setTotalFees(BigDecimal totalFees) { + public void setTotalFees(long totalFees) { this.totalFees = totalFees; } @@ -151,11 +157,11 @@ public class BlockData implements Serializable { this.atCount = atCount; } - public BigDecimal getATFees() { + public long getATFees() { return this.atFees; } - public void setATFees(BigDecimal atFees) { + public void setATFees(long atFees) { this.atFees = atFees; } diff --git a/src/main/java/org/qortal/data/naming/NameData.java b/src/main/java/org/qortal/data/naming/NameData.java index 90fa7cca..dffe830e 100644 --- a/src/main/java/org/qortal/data/naming/NameData.java +++ b/src/main/java/org/qortal/data/naming/NameData.java @@ -1,10 +1,9 @@ package org.qortal.data.naming; -import java.math.BigDecimal; - 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 io.swagger.v3.oas.annotations.media.Schema; @@ -20,17 +19,14 @@ public class NameData { private Long updated; // No need to expose this via API @XmlTransient - @Schema( - hidden = true - ) + @Schema(hidden = true) private byte[] reference; private boolean isForSale; - private BigDecimal salePrice; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private Long salePrice; // For internal use @XmlTransient - @Schema( - hidden = true - ) + @Schema(hidden = true) private int creationGroupId; // Constructors @@ -39,7 +35,7 @@ public class NameData { protected NameData() { } - public NameData(String owner, String name, String data, long registered, Long updated, byte[] reference, boolean isForSale, BigDecimal salePrice, + public NameData(String owner, String name, String data, long registered, Long updated, byte[] reference, boolean isForSale, Long salePrice, int creationGroupId) { this.owner = owner; this.name = name; @@ -106,11 +102,11 @@ public class NameData { this.isForSale = isForSale; } - public BigDecimal getSalePrice() { + public Long getSalePrice() { return this.salePrice; } - public void setSalePrice(BigDecimal salePrice) { + public void setSalePrice(Long salePrice) { this.salePrice = salePrice; } diff --git a/src/main/java/org/qortal/data/transaction/ATTransactionData.java b/src/main/java/org/qortal/data/transaction/ATTransactionData.java index 10429ba0..21ce25ca 100644 --- a/src/main/java/org/qortal/data/transaction/ATTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/ATTransactionData.java @@ -5,6 +5,7 @@ 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 org.qortal.account.NullAccount; import org.qortal.transaction.Transaction.TransactionType; @@ -17,10 +18,19 @@ import io.swagger.v3.oas.annotations.media.Schema; public class ATTransactionData extends TransactionData { // Properties + private String atAddress; + private String recipient; - private BigDecimal amount; + + @XmlTransient + @Schema(hidden = true) + // Not always present + private Long amount; + + // Not always present private Long assetId; + private byte[] message; // Constructors @@ -35,7 +45,7 @@ public class ATTransactionData extends TransactionData { } /** From repository */ - public ATTransactionData(BaseTransactionData baseTransactionData, String atAddress, String recipient, BigDecimal amount, Long assetId, byte[] message) { + public ATTransactionData(BaseTransactionData baseTransactionData, String atAddress, String recipient, Long amount, Long assetId, byte[] message) { super(TransactionType.AT, baseTransactionData); this.creatorPublicKey = NullAccount.PUBLIC_KEY; @@ -56,7 +66,7 @@ public class ATTransactionData extends TransactionData { return this.recipient; } - public BigDecimal getAmount() { + public Long getAmount() { return this.amount; } @@ -68,4 +78,14 @@ 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/BaseTransactionData.java b/src/main/java/org/qortal/data/transaction/BaseTransactionData.java index da56797e..12e0eee2 100644 --- a/src/main/java/org/qortal/data/transaction/BaseTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/BaseTransactionData.java @@ -1,13 +1,11 @@ package org.qortal.data.transaction; -import java.math.BigDecimal; - import org.qortal.transaction.Transaction.ApprovalStatus; public class BaseTransactionData extends TransactionData { /** Constructor for use by repository. */ - public BaseTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, + public BaseTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, Long fee, ApprovalStatus approvalStatus, Integer blockHeight, Integer approvalHeight, byte[] signature) { this.timestamp = timestamp; this.txGroupId = txGroupId; @@ -21,7 +19,7 @@ public class BaseTransactionData extends TransactionData { } /** Constructor for use by transaction transformer. */ - public BaseTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) { + public BaseTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, Long fee, byte[] signature) { this(timestamp, txGroupId, reference, creatorPublicKey, fee, null, null, null, signature); } diff --git a/src/main/java/org/qortal/data/transaction/BuyNameTransactionData.java b/src/main/java/org/qortal/data/transaction/BuyNameTransactionData.java index f6545118..014e1f54 100644 --- a/src/main/java/org/qortal/data/transaction/BuyNameTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/BuyNameTransactionData.java @@ -1,7 +1,5 @@ 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; @@ -18,16 +16,17 @@ import io.swagger.v3.oas.annotations.media.Schema; public class BuyNameTransactionData extends TransactionData { // Properties + @Schema(description = "buyer's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP") private byte[] buyerPublicKey; + @Schema(description = "which name to buy", example = "my-name") private String name; + @Schema(description = "selling price", example = "123.456") - @XmlJavaTypeAdapter( - type = BigDecimal.class, - value = org.qortal.api.BigDecimalTypeAdapter.class - ) - private BigDecimal amount; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long amount; + @Schema(description = "seller's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v") private String seller; @@ -49,7 +48,7 @@ public class BuyNameTransactionData extends TransactionData { /** From repository */ public BuyNameTransactionData(BaseTransactionData baseTransactionData, - String name, BigDecimal amount, String seller, byte[] nameReference) { + String name, long amount, String seller, byte[] nameReference) { super(TransactionType.BUY_NAME, baseTransactionData); this.buyerPublicKey = baseTransactionData.creatorPublicKey; @@ -60,7 +59,7 @@ public class BuyNameTransactionData extends TransactionData { } /** From network/API */ - public BuyNameTransactionData(BaseTransactionData baseTransactionData, String name, BigDecimal amount, String seller) { + public BuyNameTransactionData(BaseTransactionData baseTransactionData, String name, long amount, String seller) { this(baseTransactionData, name, amount, seller, null); } @@ -74,7 +73,7 @@ public class BuyNameTransactionData extends TransactionData { return this.name; } - public BigDecimal getAmount() { + public long getAmount() { return this.amount; } diff --git a/src/main/java/org/qortal/data/transaction/CreateAssetOrderTransactionData.java b/src/main/java/org/qortal/data/transaction/CreateAssetOrderTransactionData.java index fede447c..6b048541 100644 --- a/src/main/java/org/qortal/data/transaction/CreateAssetOrderTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/CreateAssetOrderTransactionData.java @@ -1,11 +1,10 @@ package org.qortal.data.transaction; -import java.math.BigDecimal; - import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.qortal.transaction.Transaction.TransactionType; @@ -18,14 +17,20 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode; public class CreateAssetOrderTransactionData extends TransactionData { // Properties + @Schema(description = "ID of asset on offer to give by order creator", example = "1") private long haveAssetId; + @Schema(description = "ID of asset wanted to receive by order creator", example = "0") private long wantAssetId; + @Schema(description = "amount of highest-assetID asset to trade") - private BigDecimal amount; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long amount; + @Schema(description = "price in lowest-assetID asset / highest-assetID asset") - private BigDecimal price; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long price; // Used by API - not always present @@ -70,7 +75,7 @@ public class CreateAssetOrderTransactionData extends TransactionData { /** Constructs using data from repository, including optional asset names. */ public CreateAssetOrderTransactionData(BaseTransactionData baseTransactionData, - long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, String haveAssetName, String wantAssetName) { + long haveAssetId, long wantAssetId, long amount, long price, String haveAssetName, String wantAssetName) { super(TransactionType.CREATE_ASSET_ORDER, baseTransactionData); this.haveAssetId = haveAssetId; @@ -83,7 +88,7 @@ public class CreateAssetOrderTransactionData extends TransactionData { } /** Constructor excluding optional asset names. */ - public CreateAssetOrderTransactionData(BaseTransactionData baseTransactionData, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price) { + public CreateAssetOrderTransactionData(BaseTransactionData baseTransactionData, long haveAssetId, long wantAssetId, long amount, long price) { this(baseTransactionData, haveAssetId, wantAssetId, amount, price, null, null); } @@ -97,11 +102,11 @@ public class CreateAssetOrderTransactionData extends TransactionData { return this.wantAssetId; } - public BigDecimal getAmount() { + public long getAmount() { return this.amount; } - public BigDecimal getPrice() { + public long getPrice() { return this.price; } diff --git a/src/main/java/org/qortal/data/transaction/DeployAtTransactionData.java b/src/main/java/org/qortal/data/transaction/DeployAtTransactionData.java index 75557a30..7a2ebdab 100644 --- a/src/main/java/org/qortal/data/transaction/DeployAtTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/DeployAtTransactionData.java @@ -1,9 +1,8 @@ package org.qortal.data.transaction; -import java.math.BigDecimal; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.qortal.transaction.Transaction.TransactionType; @@ -20,7 +19,8 @@ public class DeployAtTransactionData extends TransactionData { private String aTType; private String tags; private byte[] creationBytes; - private BigDecimal amount; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long amount; private long assetId; private String aTAddress; @@ -33,7 +33,7 @@ public class DeployAtTransactionData extends TransactionData { /** From repository */ public DeployAtTransactionData(BaseTransactionData baseTransactionData, - String aTAddress, String name, String description, String aTType, String tags, byte[] creationBytes, BigDecimal amount, long assetId) { + String aTAddress, String name, String description, String aTType, String tags, byte[] creationBytes, long amount, long assetId) { super(TransactionType.DEPLOY_AT, baseTransactionData); this.aTAddress = aTAddress; @@ -48,7 +48,7 @@ public class DeployAtTransactionData extends TransactionData { /** From network/API */ public DeployAtTransactionData(BaseTransactionData baseTransactionData, - String name, String description, String aTType, String tags, byte[] creationBytes, BigDecimal amount, long assetId) { + String name, String description, String aTType, String tags, byte[] creationBytes, long amount, long assetId) { this(baseTransactionData, null, name, description, aTType, tags, creationBytes, amount, assetId); } @@ -74,7 +74,7 @@ public class DeployAtTransactionData extends TransactionData { return this.creationBytes; } - public BigDecimal getAmount() { + public long getAmount() { return this.amount; } diff --git a/src/main/java/org/qortal/data/transaction/GenesisTransactionData.java b/src/main/java/org/qortal/data/transaction/GenesisTransactionData.java index c92ed605..e425f136 100644 --- a/src/main/java/org/qortal/data/transaction/GenesisTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/GenesisTransactionData.java @@ -1,9 +1,8 @@ package org.qortal.data.transaction; -import java.math.BigDecimal; - 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.asset.Asset; @@ -13,18 +12,18 @@ import io.swagger.v3.oas.annotations.media.Schema; // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) -@Schema( - allOf = { - TransactionData.class - } -) +@Schema(allOf = {TransactionData.class}) //JAXB: use this subclass if XmlDiscriminatorNode matches XmlDiscriminatorValue below: @XmlDiscriminatorValue("GENESIS") public class GenesisTransactionData extends TransactionData { // Properties + private String recipient; - private BigDecimal amount; + + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long amount; + private long assetId; // Constructors @@ -35,7 +34,7 @@ public class GenesisTransactionData extends TransactionData { } /** From repository */ - public GenesisTransactionData(BaseTransactionData baseTransactionData, String recipient, BigDecimal amount, long assetId) { + public GenesisTransactionData(BaseTransactionData baseTransactionData, String recipient, long amount, long assetId) { super(TransactionType.GENESIS, baseTransactionData); this.recipient = recipient; @@ -44,7 +43,7 @@ public class GenesisTransactionData extends TransactionData { } /** From repository (where asset locked to QORT) */ - public GenesisTransactionData(BaseTransactionData baseTransactionData, String recipient, BigDecimal amount) { + public GenesisTransactionData(BaseTransactionData baseTransactionData, String recipient, long amount) { this(baseTransactionData, recipient, amount, Asset.QORT); } @@ -54,7 +53,7 @@ public class GenesisTransactionData extends TransactionData { return this.recipient; } - public BigDecimal getAmount() { + public long getAmount() { return this.amount; } diff --git a/src/main/java/org/qortal/data/transaction/MessageTransactionData.java b/src/main/java/org/qortal/data/transaction/MessageTransactionData.java index a9849fc7..0c9b29f3 100644 --- a/src/main/java/org/qortal/data/transaction/MessageTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/MessageTransactionData.java @@ -1,10 +1,9 @@ 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.adapters.XmlJavaTypeAdapter; import org.qortal.asset.Asset; import org.qortal.transaction.Transaction.TransactionType; @@ -21,7 +20,8 @@ public class MessageTransactionData extends TransactionData { private int version; private String recipient; private Long assetId; - private BigDecimal amount; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long amount; private byte[] data; private boolean isText; private boolean isEncrypted; @@ -38,7 +38,7 @@ public class MessageTransactionData extends TransactionData { } public MessageTransactionData(BaseTransactionData baseTransactionData, - int version, String recipient, Long assetId, BigDecimal amount, byte[] data, boolean isText, boolean isEncrypted) { + int version, String recipient, Long assetId, long amount, byte[] data, boolean isText, boolean isEncrypted) { super(TransactionType.MESSAGE, baseTransactionData); this.senderPublicKey = baseTransactionData.creatorPublicKey; @@ -74,7 +74,7 @@ public class MessageTransactionData extends TransactionData { return this.assetId; } - public BigDecimal getAmount() { + public long getAmount() { return this.amount; } diff --git a/src/main/java/org/qortal/data/transaction/PaymentTransactionData.java b/src/main/java/org/qortal/data/transaction/PaymentTransactionData.java index 2cbe1fb5..94d8ab45 100644 --- a/src/main/java/org/qortal/data/transaction/PaymentTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/PaymentTransactionData.java @@ -1,7 +1,5 @@ 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; @@ -17,16 +15,16 @@ import io.swagger.v3.oas.annotations.media.Schema; public class PaymentTransactionData extends TransactionData { // Properties + @Schema(description = "sender's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP") private byte[] senderPublicKey; + @Schema(description = "recipient's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v") private String recipient; + @Schema(description = "amount to send", example = "123.456") - @XmlJavaTypeAdapter( - type = BigDecimal.class, - value = org.qortal.api.BigDecimalTypeAdapter.class - ) - private BigDecimal amount; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long amount; // Constructors @@ -40,7 +38,7 @@ public class PaymentTransactionData extends TransactionData { } /** From repository */ - public PaymentTransactionData(BaseTransactionData baseTransactionData, String recipient, BigDecimal amount) { + public PaymentTransactionData(BaseTransactionData baseTransactionData, String recipient, long amount) { super(TransactionType.PAYMENT, baseTransactionData); this.senderPublicKey = baseTransactionData.creatorPublicKey; @@ -58,7 +56,7 @@ public class PaymentTransactionData extends TransactionData { return this.recipient; } - public BigDecimal getAmount() { + public long getAmount() { return this.amount; } diff --git a/src/main/java/org/qortal/data/transaction/RewardShareTransactionData.java b/src/main/java/org/qortal/data/transaction/RewardShareTransactionData.java index 224957f6..0950dfda 100644 --- a/src/main/java/org/qortal/data/transaction/RewardShareTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/RewardShareTransactionData.java @@ -5,6 +5,7 @@ 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 org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue; @@ -27,13 +28,15 @@ public class RewardShareTransactionData extends TransactionData { @Schema(example = "reward_share_public_key") private byte[] rewardSharePublicKey; - @Schema(description = "Percentage of block rewards that minter shares to recipient, or negative value to cancel existing reward-share") - private BigDecimal sharePercent; + // JAXB will use special getter below + @XmlTransient + @Schema(hidden = true) + private int sharePercent; // No need to ever expose this via API @XmlTransient @Schema(hidden = true) - private BigDecimal previousSharePercent; + private Integer previousSharePercent; // Constructors @@ -48,7 +51,7 @@ public class RewardShareTransactionData extends TransactionData { /** From repository */ public RewardShareTransactionData(BaseTransactionData baseTransactionData, - String recipient, byte[] rewardSharePublicKey, BigDecimal sharePercent, BigDecimal previousSharePercent) { + String recipient, byte[] rewardSharePublicKey, int sharePercent, Integer previousSharePercent) { super(TransactionType.REWARD_SHARE, baseTransactionData); this.minterPublicKey = baseTransactionData.creatorPublicKey; @@ -60,7 +63,7 @@ public class RewardShareTransactionData extends TransactionData { /** From network/API */ public RewardShareTransactionData(BaseTransactionData baseTransactionData, - String recipient, byte[] rewardSharePublicKey, BigDecimal sharePercent) { + String recipient, byte[] rewardSharePublicKey, int sharePercent) { this(baseTransactionData, recipient, rewardSharePublicKey, sharePercent, null); } @@ -78,16 +81,24 @@ public class RewardShareTransactionData extends TransactionData { return this.rewardSharePublicKey; } - public BigDecimal getSharePercent() { + public int getSharePercent() { return this.sharePercent; } - public BigDecimal getPreviousSharePercent() { + public Integer getPreviousSharePercent() { return this.previousSharePercent; } - public void setPreviousSharePercent(BigDecimal previousSharePercent) { + public void setPreviousSharePercent(Integer previousSharePercent) { 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/SellNameTransactionData.java b/src/main/java/org/qortal/data/transaction/SellNameTransactionData.java index aad7fbd8..eb74ecde 100644 --- a/src/main/java/org/qortal/data/transaction/SellNameTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/SellNameTransactionData.java @@ -1,7 +1,5 @@ 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; @@ -17,16 +15,16 @@ import io.swagger.v3.oas.annotations.media.Schema; public class SellNameTransactionData extends TransactionData { // Properties + @Schema(description = "owner's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP") private byte[] ownerPublicKey; + @Schema(description = "which name to sell", example = "my-name") private String name; + @Schema(description = "selling price", example = "123.456") - @XmlJavaTypeAdapter( - type = BigDecimal.class, - value = org.qortal.api.BigDecimalTypeAdapter.class - ) - private BigDecimal amount; + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long amount; // Constructors @@ -39,7 +37,7 @@ public class SellNameTransactionData extends TransactionData { this.creatorPublicKey = this.ownerPublicKey; } - public SellNameTransactionData(BaseTransactionData baseTransactionData, String name, BigDecimal amount) { + public SellNameTransactionData(BaseTransactionData baseTransactionData, String name, long amount) { super(TransactionType.SELL_NAME, baseTransactionData); this.ownerPublicKey = baseTransactionData.creatorPublicKey; @@ -57,7 +55,7 @@ public class SellNameTransactionData extends TransactionData { return this.name; } - public BigDecimal getAmount() { + public long getAmount() { return this.amount; } diff --git a/src/main/java/org/qortal/data/transaction/TransactionData.java b/src/main/java/org/qortal/data/transaction/TransactionData.java index 4275a1bd..e4151a99 100644 --- a/src/main/java/org/qortal/data/transaction/TransactionData.java +++ b/src/main/java/org/qortal/data/transaction/TransactionData.java @@ -58,8 +58,9 @@ public abstract class TransactionData { protected long timestamp; @Schema(description = "sender's last transaction ID", example = "real_transaction_reference_in_base58") protected byte[] reference; - @Schema(description = "fee for processing transaction", example = "1.0") - protected BigDecimal fee; + @XmlTransient + @Schema(hidden = true) + 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") @@ -138,11 +139,11 @@ public abstract class TransactionData { this.creatorPublicKey = creatorPublicKey; } - public BigDecimal getFee() { + public Long getFee() { return this.fee; } - public void setFee(BigDecimal fee) { + public void setFee(Long fee) { this.fee = fee; } @@ -183,6 +184,14 @@ 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/data/transaction/TransferAssetTransactionData.java b/src/main/java/org/qortal/data/transaction/TransferAssetTransactionData.java index b1392ca3..e1036aeb 100644 --- a/src/main/java/org/qortal/data/transaction/TransferAssetTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/TransferAssetTransactionData.java @@ -1,10 +1,9 @@ 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.adapters.XmlJavaTypeAdapter; import org.qortal.transaction.Transaction.TransactionType; @@ -17,11 +16,15 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode; public class TransferAssetTransactionData extends TransactionData { // Properties + @Schema(example = "sender_public_key") private byte[] senderPublicKey; private String recipient; - private BigDecimal amount; + + @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) + private long amount; + private long assetId; // Used by API - not always present @@ -40,7 +43,7 @@ public class TransferAssetTransactionData extends TransactionData { } /** Constructs using data from repository, including optional assetName. */ - public TransferAssetTransactionData(BaseTransactionData baseTransactionData, String recipient, BigDecimal amount, long assetId, String assetName) { + public TransferAssetTransactionData(BaseTransactionData baseTransactionData, String recipient, long amount, long assetId, String assetName) { super(TransactionType.TRANSFER_ASSET, baseTransactionData); this.senderPublicKey = baseTransactionData.creatorPublicKey; @@ -51,7 +54,7 @@ public class TransferAssetTransactionData extends TransactionData { } /** Constructor excluding optional assetName. */ - public TransferAssetTransactionData(BaseTransactionData baseTransactionData, String recipient, BigDecimal amount, long assetId) { + public TransferAssetTransactionData(BaseTransactionData baseTransactionData, String recipient, long amount, long assetId) { this(baseTransactionData, recipient, amount, assetId, null); } @@ -65,7 +68,7 @@ public class TransferAssetTransactionData extends TransactionData { return this.recipient; } - public BigDecimal getAmount() { + public long getAmount() { return this.amount; } diff --git a/src/main/java/org/qortal/naming/Name.java b/src/main/java/org/qortal/naming/Name.java index f16c51fd..e8d99769 100644 --- a/src/main/java/org/qortal/naming/Name.java +++ b/src/main/java/org/qortal/naming/Name.java @@ -158,13 +158,13 @@ public class Name { // Update seller's balance Account seller = new Account(this.repository, this.nameData.getOwner()); - seller.setConfirmedBalance(Asset.QORT, seller.getConfirmedBalance(Asset.QORT).add(buyNameTransactionData.getAmount())); + seller.setConfirmedBalance(Asset.QORT, seller.getConfirmedBalance(Asset.QORT) + buyNameTransactionData.getAmount()); // Set new owner Account buyer = new PublicKeyAccount(this.repository, buyNameTransactionData.getBuyerPublicKey()); this.nameData.setOwner(buyer.getAddress()); // Update buyer's balance - buyer.setConfirmedBalance(Asset.QORT, buyer.getConfirmedBalance(Asset.QORT).subtract(buyNameTransactionData.getAmount())); + buyer.setConfirmedBalance(Asset.QORT, buyer.getConfirmedBalance(Asset.QORT) - buyNameTransactionData.getAmount()); // Update reference in transaction data buyNameTransactionData.setNameReference(this.nameData.getReference()); @@ -189,14 +189,14 @@ public class Name { // Revert buyer's balance Account buyer = new PublicKeyAccount(this.repository, buyNameTransactionData.getBuyerPublicKey()); - buyer.setConfirmedBalance(Asset.QORT, buyer.getConfirmedBalance(Asset.QORT).add(buyNameTransactionData.getAmount())); + buyer.setConfirmedBalance(Asset.QORT, buyer.getConfirmedBalance(Asset.QORT) + buyNameTransactionData.getAmount()); // Previous Name's owner and/or data taken from referenced transaction this.revert(); // Revert seller's balance Account seller = new Account(this.repository, this.nameData.getOwner()); - seller.setConfirmedBalance(Asset.QORT, seller.getConfirmedBalance(Asset.QORT).subtract(buyNameTransactionData.getAmount())); + seller.setConfirmedBalance(Asset.QORT, seller.getConfirmedBalance(Asset.QORT) - buyNameTransactionData.getAmount()); // Save reverted name data this.repository.getNameRepository().save(this.nameData); diff --git a/src/main/java/org/qortal/payment/Payment.java b/src/main/java/org/qortal/payment/Payment.java index 9a99bc01..afdc4270 100644 --- a/src/main/java/org/qortal/payment/Payment.java +++ b/src/main/java/org/qortal/payment/Payment.java @@ -1,6 +1,5 @@ package org.qortal.payment; -import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -37,15 +36,15 @@ public class Payment { // isValid /** Are payments valid? */ - public ValidationResult isValid(byte[] senderPublicKey, List payments, BigDecimal fee, boolean isZeroAmountValid) throws DataException { + public ValidationResult isValid(byte[] senderPublicKey, List payments, long fee, boolean isZeroAmountValid) throws DataException { AssetRepository assetRepository = this.repository.getAssetRepository(); // Check fee is positive - if (fee.compareTo(BigDecimal.ZERO) <= 0) + if (fee <= 0) return ValidationResult.NEGATIVE_FEE; // Total up payment amounts by assetId - Map amountsByAssetId = new HashMap<>(); + Map amountsByAssetId = new HashMap<>(); // Add transaction fee to start with amountsByAssetId.put(Asset.QORT, fee); @@ -55,11 +54,11 @@ public class Payment { // Check payments, and calculate amount total by assetId for (PaymentData paymentData : payments) { // Check amount is zero or positive - if (paymentData.getAmount().compareTo(BigDecimal.ZERO) < 0) + if (paymentData.getAmount() < 0) return ValidationResult.NEGATIVE_AMOUNT; // Optional zero-amount check - if (!isZeroAmountValid && paymentData.getAmount().compareTo(BigDecimal.ZERO) <= 0) + if (!isZeroAmountValid && paymentData.getAmount() <= 0) return ValidationResult.NEGATIVE_AMOUNT; // Check recipient address is valid @@ -94,64 +93,63 @@ 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().stripTrailingZeros().scale() > 0) + if (!assetData.getIsDivisible() && paymentData.getAmount() % Asset.MULTIPLIER != 0) return ValidationResult.INVALID_AMOUNT; // Set or add amount into amounts-by-asset map - amountsByAssetId.compute(paymentData.getAssetId(), (assetId, amount) -> amount == null ? paymentData.getAmount() : amount.add(paymentData.getAmount())); + amountsByAssetId.compute(paymentData.getAssetId(), (assetId, amount) -> amount == null ? paymentData.getAmount() : amount + paymentData.getAmount()); } // Check sender has enough of each asset - for (Entry pair : amountsByAssetId.entrySet()) - if (sender.getConfirmedBalance(pair.getKey()).compareTo(pair.getValue()) < 0) + for (Entry pair : amountsByAssetId.entrySet()) + if (sender.getConfirmedBalance(pair.getKey()) < pair.getValue()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; } /** Are payments valid? */ - public ValidationResult isValid(byte[] senderPublicKey, List payments, BigDecimal fee) throws DataException { + public ValidationResult isValid(byte[] senderPublicKey, List payments, long fee) throws DataException { return isValid(senderPublicKey, payments, fee, false); } /** Is single payment valid? */ - public ValidationResult isValid(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, boolean isZeroAmountValid) throws DataException { + public ValidationResult isValid(byte[] senderPublicKey, PaymentData paymentData, long fee, boolean isZeroAmountValid) throws DataException { return isValid(senderPublicKey, Collections.singletonList(paymentData), fee, isZeroAmountValid); } /** Is single payment valid? */ - public ValidationResult isValid(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee) throws DataException { + public ValidationResult isValid(byte[] senderPublicKey, PaymentData paymentData, long fee) throws DataException { return isValid(senderPublicKey, paymentData, fee, false); } // isProcessable /** Are multiple payments processable? */ - public ValidationResult isProcessable(byte[] senderPublicKey, List payments, BigDecimal fee, boolean isZeroAmountValid) throws DataException { + public ValidationResult isProcessable(byte[] senderPublicKey, List payments, long fee, boolean isZeroAmountValid) throws DataException { // Essentially the same as isValid... return isValid(senderPublicKey, payments, fee, isZeroAmountValid); } /** Are multiple payments processable? */ - public ValidationResult isProcessable(byte[] senderPublicKey, List payments, BigDecimal fee) throws DataException { + public ValidationResult isProcessable(byte[] senderPublicKey, List payments, long fee) throws DataException { return isProcessable(senderPublicKey, payments, fee, false); } /** Is single payment processable? */ - public ValidationResult isProcessable(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, boolean isZeroAmountValid) throws DataException { + public ValidationResult isProcessable(byte[] senderPublicKey, PaymentData paymentData, long fee, boolean isZeroAmountValid) throws DataException { return isProcessable(senderPublicKey, Collections.singletonList(paymentData), fee, isZeroAmountValid); } /** Is single payment processable? */ - public ValidationResult isProcessable(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee) throws DataException { + public ValidationResult isProcessable(byte[] senderPublicKey, PaymentData paymentData, long fee) throws DataException { return isProcessable(senderPublicKey, paymentData, fee, false); } // process /** Multiple payment processing */ - public void process(byte[] senderPublicKey, List payments, BigDecimal fee, byte[] signature) - throws DataException { + public void process(byte[] senderPublicKey, List payments, byte[] signature) throws DataException { Account sender = new PublicKeyAccount(this.repository, senderPublicKey); // Process all payments @@ -159,31 +157,30 @@ public class Payment { Account recipient = new Account(this.repository, paymentData.getRecipient()); long assetId = paymentData.getAssetId(); - BigDecimal amount = paymentData.getAmount(); + long amount = paymentData.getAmount(); // Update sender's balance due to amount - sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId).subtract(amount)); + sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId) - amount); // Update recipient's balance - recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).add(amount)); + recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId) + amount); } } /** Single payment processing */ - public void process(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature) - throws DataException { - process(senderPublicKey, Collections.singletonList(paymentData), fee, signature); + public void process(byte[] senderPublicKey, PaymentData paymentData, byte[] signature) throws DataException { + process(senderPublicKey, Collections.singletonList(paymentData), signature); } // processReferenceAndFees /** Multiple payment reference processing */ - public void processReferencesAndFees(byte[] senderPublicKey, List payments, BigDecimal fee, byte[] signature, boolean alwaysInitializeRecipientReference) + public void processReferencesAndFees(byte[] senderPublicKey, List payments, long fee, byte[] signature, boolean alwaysInitializeRecipientReference) throws DataException { Account sender = new PublicKeyAccount(this.repository, senderPublicKey); // Update sender's balance due to fee - sender.setConfirmedBalance(Asset.QORT, sender.getConfirmedBalance(Asset.QORT).subtract(fee)); + sender.setConfirmedBalance(Asset.QORT, sender.getConfirmedBalance(Asset.QORT) - fee); // Update sender's reference sender.setLastReference(signature); @@ -201,42 +198,42 @@ public class Payment { } /** Multiple payment reference processing */ - public void processReferencesAndFees(byte[] senderPublicKey, PaymentData payment, BigDecimal fee, byte[] signature, boolean alwaysInitializeRecipientReference) + public void processReferencesAndFees(byte[] senderPublicKey, PaymentData payment, long fee, byte[] signature, boolean alwaysInitializeRecipientReference) throws DataException { processReferencesAndFees(senderPublicKey, Collections.singletonList(payment), fee, signature, alwaysInitializeRecipientReference); } // orphan - public void orphan(byte[] senderPublicKey, List payments, BigDecimal fee, byte[] signature, byte[] reference) throws DataException { + public void orphan(byte[] senderPublicKey, List payments, byte[] signature, byte[] reference) throws DataException { Account sender = new PublicKeyAccount(this.repository, senderPublicKey); // Orphan all payments for (PaymentData paymentData : payments) { Account recipient = new Account(this.repository, paymentData.getRecipient()); long assetId = paymentData.getAssetId(); - BigDecimal amount = paymentData.getAmount(); + long amount = paymentData.getAmount(); // Update sender's balance due to amount - sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId).add(amount)); + sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId) + amount); // Update recipient's balance - recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).subtract(amount)); + recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId) - amount); } } - public void orphan(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature, byte[] reference) throws DataException { - orphan(senderPublicKey, Collections.singletonList(paymentData), fee, signature, reference); + public void orphan(byte[] senderPublicKey, PaymentData paymentData, byte[] signature, byte[] reference) throws DataException { + orphan(senderPublicKey, Collections.singletonList(paymentData), signature, reference); } // orphanReferencesAndFees - public void orphanReferencesAndFees(byte[] senderPublicKey, List payments, BigDecimal fee, byte[] signature, byte[] reference, + public void orphanReferencesAndFees(byte[] senderPublicKey, List payments, long fee, byte[] signature, byte[] reference, boolean alwaysUninitializeRecipientReference) throws DataException { Account sender = new PublicKeyAccount(this.repository, senderPublicKey); // Update sender's balance due to fee - sender.setConfirmedBalance(Asset.QORT, sender.getConfirmedBalance(Asset.QORT).add(fee)); + sender.setConfirmedBalance(Asset.QORT, sender.getConfirmedBalance(Asset.QORT) + fee); // Update sender's reference sender.setLastReference(reference); @@ -257,7 +254,7 @@ public class Payment { } } - public void orphanReferencesAndFees(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature, byte[] reference, + public void orphanReferencesAndFees(byte[] senderPublicKey, PaymentData paymentData, long fee, byte[] signature, byte[] reference, boolean alwaysUninitializeRecipientReference) throws DataException { orphanReferencesAndFees(senderPublicKey, Collections.singletonList(paymentData), fee, signature, reference, alwaysUninitializeRecipientReference); } diff --git a/src/main/java/org/qortal/repository/AccountRepository.java b/src/main/java/org/qortal/repository/AccountRepository.java index 15aba42a..4372c0b5 100644 --- a/src/main/java/org/qortal/repository/AccountRepository.java +++ b/src/main/java/org/qortal/repository/AccountRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository; -import java.math.BigDecimal; import java.util.List; import org.qortal.data.account.AccountBalanceData; @@ -106,7 +105,7 @@ public interface AccountRepository { public List getAssetBalances(List addresses, List assetIds, BalanceOrdering balanceOrdering, Boolean excludeZero, Integer limit, Integer offset, Boolean reverse) throws DataException; - public void modifyAssetBalance(String address, long assetId, BigDecimal deltaBalance) throws DataException; + public void modifyAssetBalance(String address, long assetId, long deltaBalance) throws DataException; public void save(AccountBalanceData accountBalanceData) throws DataException; diff --git a/src/main/java/org/qortal/repository/AssetRepository.java b/src/main/java/org/qortal/repository/AssetRepository.java index e9e2527b..3aa70451 100644 --- a/src/main/java/org/qortal/repository/AssetRepository.java +++ b/src/main/java/org/qortal/repository/AssetRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository; -import java.math.BigDecimal; import java.util.List; import org.qortal.data.asset.AssetData; @@ -45,7 +44,7 @@ public interface AssetRepository { return getOpenOrders(haveAssetId, wantAssetId, null, null, null); } - public List getOpenOrdersForTrading(long haveAssetId, long wantAssetId, BigDecimal minimumPrice) throws DataException; + public List getOpenOrdersForTrading(long haveAssetId, long wantAssetId, Long minimumPrice) throws DataException; public List getAggregatedOpenOrders(long haveAssetId, long wantAssetId, Integer limit, Integer offset, Boolean reverse) throws DataException; diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java index 59955bb3..7b399422 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; @@ -46,7 +45,9 @@ public class HSQLDBATRepository implements ATRepository { boolean hadFatalError = resultSet.getBoolean(9); boolean isFrozen = resultSet.getBoolean(10); - BigDecimal frozenBalance = resultSet.getBigDecimal(11); + Long frozenBalance = resultSet.getLong(11); + if (frozenBalance == 0 && resultSet.wasNull()) + frozenBalance = null; return new ATData(atAddress, creatorPublicKey, creation, version, assetId, codeBytes, isSleeping, sleepUntilHeight, isFinished, hadFatalError, isFrozen, frozenBalance); @@ -92,7 +93,9 @@ public class HSQLDBATRepository implements ATRepository { boolean hadFatalError = resultSet.getBoolean(9); boolean isFrozen = resultSet.getBoolean(10); - BigDecimal frozenBalance = resultSet.getBigDecimal(11); + Long frozenBalance = resultSet.getLong(11); + if (frozenBalance == 0 && resultSet.wasNull()) + frozenBalance = null; ATData atData = new ATData(atAddress, creatorPublicKey, creation, version, assetId, codeBytes, isSleeping, sleepUntilHeight, isFinished, hadFatalError, isFrozen, frozenBalance); @@ -159,7 +162,7 @@ public class HSQLDBATRepository implements ATRepository { long creation = resultSet.getTimestamp(1, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); byte[] stateData = resultSet.getBytes(2); // Actually BLOB byte[] stateHash = resultSet.getBytes(3); - BigDecimal fees = resultSet.getBigDecimal(4); + long fees = resultSet.getLong(4); return new ATStateData(atAddress, height, creation, stateData, stateHash, fees); } catch (SQLException e) { @@ -178,7 +181,7 @@ public class HSQLDBATRepository implements ATRepository { long creation = resultSet.getTimestamp(2, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); byte[] stateData = resultSet.getBytes(3); // Actually BLOB byte[] stateHash = resultSet.getBytes(4); - BigDecimal fees = resultSet.getBigDecimal(5); + long fees = resultSet.getLong(5); return new ATStateData(atAddress, height, creation, stateData, stateHash, fees); } catch (SQLException e) { @@ -199,7 +202,7 @@ public class HSQLDBATRepository implements ATRepository { do { String atAddress = resultSet.getString(1); byte[] stateHash = resultSet.getBytes(2); - BigDecimal fees = resultSet.getBigDecimal(3); + long fees = resultSet.getLong(3); ATStateData atStateData = new ATStateData(atAddress, height, stateHash, fees); atStates.add(atStateData); diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java index c38fa438..f6ab9b41 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -319,7 +318,7 @@ public class HSQLDBAccountRepository implements AccountRepository { if (resultSet == null) return null; - BigDecimal balance = resultSet.getBigDecimal(1).setScale(8); + long balance = resultSet.getLong(1); return new AccountBalanceData(address, assetId, balance); } catch (SQLException e) { @@ -342,7 +341,7 @@ public class HSQLDBAccountRepository implements AccountRepository { do { String address = resultSet.getString(1); - BigDecimal balance = resultSet.getBigDecimal(2).setScale(8); + long balance = resultSet.getLong(2); accountBalances.add(new AccountBalanceData(address, assetId, balance)); } while (resultSet.next()); @@ -445,7 +444,7 @@ public class HSQLDBAccountRepository implements AccountRepository { do { String address = resultSet.getString(1); long assetId = resultSet.getLong(2); - BigDecimal balance = resultSet.getBigDecimal(3).setScale(8); + long balance = resultSet.getLong(3); String assetName = resultSet.getString(4); accountBalances.add(new AccountBalanceData(address, assetId, balance, assetName)); @@ -458,13 +457,13 @@ public class HSQLDBAccountRepository implements AccountRepository { } @Override - public void modifyAssetBalance(String address, long assetId, BigDecimal deltaBalance) throws DataException { + public void modifyAssetBalance(String address, long assetId, long deltaBalance) throws DataException { // If deltaBalance is zero then do nothing - if (deltaBalance.signum() == 0) + if (deltaBalance == 0) return; // If deltaBalance is negative then we assume AccountBalances & parent Accounts rows exist - if (deltaBalance.signum() < 0) { + if (deltaBalance < 0) { // Perform actual balance change String sql = "UPDATE AccountBalances set balance = balance + ? WHERE account = ? AND asset_id = ?"; try { @@ -496,8 +495,8 @@ public class HSQLDBAccountRepository implements AccountRepository { public void save(AccountBalanceData accountBalanceData) throws DataException { HSQLDBSaver saveHelper = new HSQLDBSaver("AccountBalances"); - saveHelper.bind("account", accountBalanceData.getAddress()).bind("asset_id", accountBalanceData.getAssetId()).bind("balance", - accountBalanceData.getBalance()); + saveHelper.bind("account", accountBalanceData.getAddress()).bind("asset_id", accountBalanceData.getAssetId()) + .bind("balance", accountBalanceData.getBalance()); try { saveHelper.execute(this.repository); @@ -527,7 +526,7 @@ public class HSQLDBAccountRepository implements AccountRepository { String minter = resultSet.getString(1); byte[] rewardSharePublicKey = resultSet.getBytes(2); - BigDecimal sharePercent = resultSet.getBigDecimal(3); + int sharePercent = resultSet.getInt(3); return new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent); } catch (SQLException e) { @@ -546,7 +545,7 @@ public class HSQLDBAccountRepository implements AccountRepository { byte[] minterPublicKey = resultSet.getBytes(1); String minter = resultSet.getString(2); String recipient = resultSet.getString(3); - BigDecimal sharePercent = resultSet.getBigDecimal(4); + int sharePercent = resultSet.getInt(4); return new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent); } catch (SQLException e) { @@ -588,7 +587,7 @@ public class HSQLDBAccountRepository implements AccountRepository { byte[] minterPublicKey = resultSet.getBytes(1); String minter = resultSet.getString(2); String recipient = resultSet.getString(3); - BigDecimal sharePercent = resultSet.getBigDecimal(4); + int sharePercent = resultSet.getInt(4); byte[] rewardSharePublicKey = resultSet.getBytes(5); rewardShares.add(new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent)); @@ -676,7 +675,7 @@ public class HSQLDBAccountRepository implements AccountRepository { byte[] minterPublicKey = resultSet.getBytes(1); String minter = resultSet.getString(2); String recipient = resultSet.getString(3); - BigDecimal sharePercent = resultSet.getBigDecimal(4); + int sharePercent = resultSet.getInt(4); byte[] rewardSharePublicKey = resultSet.getBytes(5); rewardShares.add(new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent)); @@ -715,7 +714,7 @@ public class HSQLDBAccountRepository implements AccountRepository { byte[] minterPublicKey = resultSet.getBytes(1); String minter = resultSet.getString(2); String recipient = resultSet.getString(3); - BigDecimal sharePercent = resultSet.getBigDecimal(4); + int sharePercent = resultSet.getInt(4); byte[] rewardSharePublicKey = resultSet.getBytes(5); return new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent); @@ -820,7 +819,7 @@ public class HSQLDBAccountRepository implements AccountRepository { do { String address = resultSet.getString(1); - BigDecimal balance = resultSet.getBigDecimal(2).setScale(8); + long balance = resultSet.getLong(2); accountBalances.add(new AccountBalanceData(address, Asset.LEGACY_QORA, balance)); } while (resultSet.next()); @@ -839,7 +838,7 @@ public class HSQLDBAccountRepository implements AccountRepository { if (resultSet == null) return null; - BigDecimal finalQortFromQora = resultSet.getBigDecimal(1); + long finalQortFromQora = resultSet.getLong(1); Integer finalBlockHeight = resultSet.getInt(2); if (finalBlockHeight == 0 && resultSet.wasNull()) finalBlockHeight = null; diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBAssetRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBAssetRepository.java index 718b1e67..30af058b 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBAssetRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBAssetRepository.java @@ -1,11 +1,11 @@ package org.qortal.repository.hsqldb; -import java.math.BigDecimal; +import static org.qortal.repository.hsqldb.HSQLDBRepository.getZonedTimestampMilli; +import static org.qortal.repository.hsqldb.HSQLDBRepository.toOffsetDateTime; + import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Timestamp; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collections; import java.util.List; @@ -171,8 +171,7 @@ public class HSQLDBAssetRepository implements AssetRepository { if (assetData.getAssetId() == null) { // Fetch new assetId - try (ResultSet resultSet = this.repository - .checkedExecute("SELECT asset_id FROM Assets WHERE reference = ?", assetData.getReference())) { + try (ResultSet resultSet = this.repository.checkedExecute("SELECT asset_id FROM Assets WHERE reference = ?", assetData.getReference())) { if (resultSet == null) throw new DataException("Unable to fetch new asset ID from repository"); @@ -213,10 +212,10 @@ public class HSQLDBAssetRepository implements AssetRepository { byte[] creatorPublicKey = resultSet.getBytes(1); long haveAssetId = resultSet.getLong(2); long wantAssetId = resultSet.getLong(3); - BigDecimal amount = resultSet.getBigDecimal(4); - BigDecimal fulfilled = resultSet.getBigDecimal(5); - BigDecimal price = resultSet.getBigDecimal(6); - long timestamp = resultSet.getTimestamp(7, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long amount = resultSet.getLong(4); + long fulfilled = resultSet.getLong(5); + long price = resultSet.getLong(6); + long timestamp = getZonedTimestampMilli(resultSet, 7); boolean isClosed = resultSet.getBoolean(8); boolean isFulfilled = resultSet.getBoolean(9); String haveAssetName = resultSet.getString(10); @@ -263,10 +262,10 @@ public class HSQLDBAssetRepository implements AssetRepository { do { byte[] creatorPublicKey = resultSet.getBytes(1); byte[] orderId = resultSet.getBytes(2); - BigDecimal amount = resultSet.getBigDecimal(3); - BigDecimal fulfilled = resultSet.getBigDecimal(4); - BigDecimal price = resultSet.getBigDecimal(5); - long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long amount = resultSet.getLong(3); + long fulfilled = resultSet.getLong(4); + long price = resultSet.getLong(5); + long timestamp = getZonedTimestampMilli(resultSet, 6); boolean isClosed = false; boolean isFulfilled = false; @@ -282,7 +281,7 @@ public class HSQLDBAssetRepository implements AssetRepository { } @Override - public List getOpenOrdersForTrading(long haveAssetId, long wantAssetId, BigDecimal minimumPrice) throws DataException { + public List getOpenOrdersForTrading(long haveAssetId, long wantAssetId, Long minimumPrice) throws DataException { List bindParams = new ArrayList<>(3); StringBuilder sql = new StringBuilder(512); @@ -317,10 +316,10 @@ public class HSQLDBAssetRepository implements AssetRepository { do { byte[] creatorPublicKey = resultSet.getBytes(1); byte[] orderId = resultSet.getBytes(2); - BigDecimal amount = resultSet.getBigDecimal(3); - BigDecimal fulfilled = resultSet.getBigDecimal(4); - BigDecimal price = resultSet.getBigDecimal(5); - long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long amount = resultSet.getLong(3); + long fulfilled = resultSet.getLong(4); + long price = resultSet.getLong(5); + long timestamp = getZonedTimestampMilli(resultSet, 6); boolean isClosed = false; boolean isFulfilled = false; @@ -366,11 +365,11 @@ public class HSQLDBAssetRepository implements AssetRepository { return orders; do { - BigDecimal price = resultSet.getBigDecimal(1); - BigDecimal totalUnfulfilled = resultSet.getBigDecimal(2); + long price = resultSet.getLong(1); + long totalUnfulfilled = resultSet.getLong(2); long timestamp = resultSet.getTimestamp(3).getTime(); - OrderData order = new OrderData(null, null, haveAssetId, wantAssetId, totalUnfulfilled, BigDecimal.ZERO, + OrderData order = new OrderData(null, null, haveAssetId, wantAssetId, totalUnfulfilled, 0L, price, timestamp, false, false, haveAssetData.getName(), wantAssetData.getName()); orders.add(order); } while (resultSet.next()); @@ -417,10 +416,10 @@ public class HSQLDBAssetRepository implements AssetRepository { byte[] orderId = resultSet.getBytes(1); long haveAssetId = resultSet.getLong(2); long wantAssetId = resultSet.getLong(3); - BigDecimal amount = resultSet.getBigDecimal(4); - BigDecimal fulfilled = resultSet.getBigDecimal(5); - BigDecimal price = resultSet.getBigDecimal(6); - long timestamp = resultSet.getTimestamp(7, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long amount = resultSet.getLong(4); + long fulfilled = resultSet.getLong(5); + long price = resultSet.getLong(6); + long timestamp = getZonedTimestampMilli(resultSet, 7); boolean isClosed = resultSet.getBoolean(8); boolean isFulfilled = resultSet.getBoolean(9); String haveAssetName = resultSet.getString(10); @@ -478,10 +477,10 @@ public class HSQLDBAssetRepository implements AssetRepository { do { byte[] orderId = resultSet.getBytes(1); - BigDecimal amount = resultSet.getBigDecimal(2); - BigDecimal fulfilled = resultSet.getBigDecimal(3); - BigDecimal price = resultSet.getBigDecimal(4); - long timestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long amount = resultSet.getLong(2); + long fulfilled = resultSet.getLong(3); + long price = resultSet.getLong(4); + long timestamp = getZonedTimestampMilli(resultSet, 5); boolean isClosed = resultSet.getBoolean(6); boolean isFulfilled = resultSet.getBoolean(7); @@ -503,7 +502,7 @@ public class HSQLDBAssetRepository implements AssetRepository { saveHelper.bind("asset_order_id", orderData.getOrderId()).bind("creator", orderData.getCreatorPublicKey()) .bind("have_asset_id", orderData.getHaveAssetId()).bind("want_asset_id", orderData.getWantAssetId()) .bind("amount", orderData.getAmount()).bind("fulfilled", orderData.getFulfilled()) - .bind("price", orderData.getPrice()).bind("ordered", new Timestamp(orderData.getTimestamp())) + .bind("price", orderData.getPrice()).bind("ordered", toOffsetDateTime(orderData.getTimestamp())) .bind("is_closed", orderData.getIsClosed()).bind("is_fulfilled", orderData.getIsFulfilled()); try { @@ -556,10 +555,10 @@ public class HSQLDBAssetRepository implements AssetRepository { do { byte[] initiatingOrderId = resultSet.getBytes(1); byte[] targetOrderId = resultSet.getBytes(2); - BigDecimal targetAmount = resultSet.getBigDecimal(3); - BigDecimal initiatorAmount = resultSet.getBigDecimal(4); - BigDecimal initiatorSaving = resultSet.getBigDecimal(5); - long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long targetAmount = resultSet.getLong(3); + long initiatorAmount = resultSet.getLong(4); + long initiatorSaving = resultSet.getLong(5); + long timestamp = getZonedTimestampMilli(resultSet, 6); TradeData trade = new TradeData(initiatingOrderId, targetOrderId, targetAmount, initiatorAmount, initiatorSaving, timestamp, haveAssetId, haveAssetData.getName(), wantAssetId, wantAssetData.getName()); @@ -648,9 +647,9 @@ public class HSQLDBAssetRepository implements AssetRepository { do { long haveAssetId = resultSet.getLong(1); long wantAssetId = resultSet.getLong(2); - BigDecimal otherAmount = resultSet.getBigDecimal(3); - BigDecimal amount = resultSet.getBigDecimal(4); - long timestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long otherAmount = resultSet.getLong(3); + long amount = resultSet.getLong(4); + long timestamp = getZonedTimestampMilli(resultSet, 5); RecentTradeData recentTrade = new RecentTradeData(haveAssetId, wantAssetId, otherAmount, amount, timestamp); @@ -689,10 +688,10 @@ public class HSQLDBAssetRepository implements AssetRepository { do { byte[] initiatingOrderId = resultSet.getBytes(1); byte[] targetOrderId = resultSet.getBytes(2); - BigDecimal targetAmount = resultSet.getBigDecimal(3); - BigDecimal initiatorAmount = resultSet.getBigDecimal(4); - BigDecimal initiatorSaving = resultSet.getBigDecimal(5); - long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long targetAmount = resultSet.getLong(3); + long initiatorAmount = resultSet.getLong(4); + long initiatorSaving = resultSet.getLong(5); + long timestamp = getZonedTimestampMilli(resultSet, 6); long haveAssetId = resultSet.getLong(7); String haveAssetName = resultSet.getString(8); @@ -716,7 +715,7 @@ public class HSQLDBAssetRepository implements AssetRepository { saveHelper.bind("initiating_order_id", tradeData.getInitiator()).bind("target_order_id", tradeData.getTarget()) .bind("target_amount", tradeData.getTargetAmount()).bind("initiator_amount", tradeData.getInitiatorAmount()) - .bind("initiator_saving", tradeData.getInitiatorSaving()).bind("traded", new Timestamp(tradeData.getTimestamp())); + .bind("initiator_saving", tradeData.getInitiatorSaving()).bind("traded", toOffsetDateTime(tradeData.getTimestamp())); try { saveHelper.execute(this.repository); diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockRepository.java index a99af8be..5984edaa 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockRepository.java @@ -3,7 +3,6 @@ package org.qortal.repository.hsqldb; import static org.qortal.repository.hsqldb.HSQLDBRepository.getZonedTimestampMilli; import static org.qortal.repository.hsqldb.HSQLDBRepository.toOffsetDateTime; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -39,14 +38,14 @@ public class HSQLDBBlockRepository implements BlockRepository { int version = resultSet.getInt(1); byte[] reference = resultSet.getBytes(2); int transactionCount = resultSet.getInt(3); - BigDecimal totalFees = resultSet.getBigDecimal(4); + long totalFees = resultSet.getLong(4); byte[] transactionsSignature = resultSet.getBytes(5); int height = resultSet.getInt(6); long timestamp = getZonedTimestampMilli(resultSet, 7); byte[] minterPublicKey = resultSet.getBytes(8); byte[] minterSignature = resultSet.getBytes(9); int atCount = resultSet.getInt(10); - BigDecimal atFees = resultSet.getBigDecimal(11); + long atFees = resultSet.getLong(11); byte[] encodedOnlineAccounts = resultSet.getBytes(12); int onlineAccountsCount = resultSet.getInt(13); Long onlineAccountsTimestamp = getZonedTimestampMilli(resultSet, 14); diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java index 359145c9..012b5ce4 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -99,7 +99,7 @@ public class HSQLDBDatabaseUpdates { stmt.execute("CREATE TYPE Signature AS VARBINARY(64)"); stmt.execute("CREATE TYPE QortalAddress AS VARCHAR(36)"); stmt.execute("CREATE TYPE QortalPublicKey AS VARBINARY(32)"); - stmt.execute("CREATE TYPE QortalAmount AS DECIMAL(27, 8)"); + stmt.execute("CREATE TYPE QortalAmount AS BIGINT"); stmt.execute("CREATE TYPE GenericDescription AS VARCHAR(4000)"); stmt.execute("CREATE TYPE RegisteredName AS VARCHAR(128) COLLATE SQL_TEXT_NO_PAD"); stmt.execute("CREATE TYPE NameData AS VARCHAR(4000)"); @@ -122,6 +122,7 @@ public class HSQLDBDatabaseUpdates { stmt.execute("CREATE TYPE GroupID AS INTEGER"); stmt.execute("CREATE TYPE GroupName AS VARCHAR(400) COLLATE SQL_TEXT_UCC_NO_PAD"); stmt.execute("CREATE TYPE GroupReason AS VARCHAR(128) COLLATE SQL_TEXT_UCC_NO_PAD"); + stmt.execute("CREATE TYPE RewardSharePercent AS INT"); break; case 1: @@ -713,10 +714,10 @@ public class HSQLDBDatabaseUpdates { case 46: // Proxy forging // Transaction emitted by forger announcing they are forging on behalf of recipient - stmt.execute("CREATE TABLE ProxyForgingTransactions (signature Signature, forger QortalPublicKey NOT NULL, recipient QortalAddress NOT NULL, proxy_public_key QortalPublicKey NOT NULL, share DECIMAL(5,2) NOT NULL, " - + "previous_share DECIMAL(5,2), PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)"); + stmt.execute("CREATE TABLE ProxyForgingTransactions (signature Signature, forger QortalPublicKey NOT NULL, recipient QortalAddress NOT NULL, proxy_public_key QortalPublicKey NOT NULL, share RewardSharePercent NOT NULL, " + + "previous_share RewardSharePercent, PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)"); // Table of current shares - stmt.execute("CREATE TABLE ProxyForgers (forger QortalPublicKey NOT NULL, recipient QortalAddress NOT NULL, proxy_public_key QortalPublicKey NOT NULL, share DECIMAL(5,2) NOT NULL, " + stmt.execute("CREATE TABLE ProxyForgers (forger QortalPublicKey NOT NULL, recipient QortalAddress NOT NULL, proxy_public_key QortalPublicKey NOT NULL, share RewardSharePercent NOT NULL, " + "PRIMARY KEY (forger, recipient))"); // Proxy-forged blocks will contain proxy public key, which will be used to look up block reward sharing, so create index for those lookups stmt.execute("CREATE INDEX ProxyForgersProxyPublicKeyIndex ON ProxyForgers (proxy_public_key)"); diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBNameRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBNameRepository.java index a4b1ae04..a5b66115 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBNameRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBNameRepository.java @@ -1,6 +1,8 @@ package org.qortal.repository.hsqldb; -import java.math.BigDecimal; +import static org.qortal.repository.hsqldb.HSQLDBRepository.getZonedTimestampMilli; +import static org.qortal.repository.hsqldb.HSQLDBRepository.toOffsetDateTime; + import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; @@ -38,7 +40,11 @@ public class HSQLDBNameRepository implements NameRepository { byte[] reference = resultSet.getBytes(5); boolean isForSale = resultSet.getBoolean(6); - BigDecimal salePrice = resultSet.getBigDecimal(7); + + Long salePrice = resultSet.getLong(7); + if (salePrice == 0 && resultSet.wasNull()) + salePrice = null; + int creationGroupId = resultSet.getInt(8); return new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId); @@ -75,15 +81,15 @@ public class HSQLDBNameRepository implements NameRepository { String name = resultSet.getString(1); String data = resultSet.getString(2); String owner = resultSet.getString(3); - long registered = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); - - // Special handling for possibly-NULL "updated" column - Timestamp updatedTimestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)); - Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime(); - + long registered = getZonedTimestampMilli(resultSet, 4); + Long updated = getZonedTimestampMilli(resultSet, 5); // can be null byte[] reference = resultSet.getBytes(6); boolean isForSale = resultSet.getBoolean(7); - BigDecimal salePrice = resultSet.getBigDecimal(8); + + Long salePrice = resultSet.getLong(8); + if (salePrice == 0 && resultSet.wasNull()) + salePrice = null; + int creationGroupId = resultSet.getInt(9); names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId)); @@ -114,15 +120,15 @@ public class HSQLDBNameRepository implements NameRepository { String name = resultSet.getString(1); String data = resultSet.getString(2); String owner = resultSet.getString(3); - long registered = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); - - // Special handling for possibly-NULL "updated" column - Timestamp updatedTimestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)); - Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime(); - + long registered = getZonedTimestampMilli(resultSet, 4); + Long updated = getZonedTimestampMilli(resultSet, 5); // can be null byte[] reference = resultSet.getBytes(6); boolean isForSale = true; - BigDecimal salePrice = resultSet.getBigDecimal(7); + + Long salePrice = resultSet.getLong(7); + if (salePrice == 0 && resultSet.wasNull()) + salePrice = null; + int creationGroupId = resultSet.getInt(8); names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId)); @@ -152,15 +158,15 @@ public class HSQLDBNameRepository implements NameRepository { do { String name = resultSet.getString(1); String data = resultSet.getString(2); - long registered = resultSet.getTimestamp(3, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); - - // Special handling for possibly-NULL "updated" column - Timestamp updatedTimestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)); - Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime(); - + long registered = getZonedTimestampMilli(resultSet, 3); + Long updated = getZonedTimestampMilli(resultSet, 4); // can be null byte[] reference = resultSet.getBytes(5); boolean isForSale = resultSet.getBoolean(6); - BigDecimal salePrice = resultSet.getBigDecimal(7); + + Long salePrice = resultSet.getLong(7); + if (salePrice == 0 && resultSet.wasNull()) + salePrice = null; + int creationGroupId = resultSet.getInt(8); names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId)); @@ -200,12 +206,9 @@ public class HSQLDBNameRepository implements NameRepository { public void save(NameData nameData) throws DataException { HSQLDBSaver saveHelper = new HSQLDBSaver("Names"); - // Special handling for "updated" timestamp - Long updated = nameData.getUpdated(); - Timestamp updatedTimestamp = updated == null ? null : new Timestamp(updated); - saveHelper.bind("owner", nameData.getOwner()).bind("name", nameData.getName()).bind("data", nameData.getData()) - .bind("registered", new Timestamp(nameData.getRegistered())).bind("updated", updatedTimestamp).bind("reference", nameData.getReference()) + .bind("registered", toOffsetDateTime(nameData.getRegistered())).bind("updated", toOffsetDateTime(nameData.getUpdated())) + .bind("reference", nameData.getReference()) .bind("is_for_sale", nameData.getIsForSale()).bind("sale_price", nameData.getSalePrice()) .bind("creation_group_id", nameData.getCreationGroupId()); diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java index 98de2f8b..cc015661 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java @@ -763,7 +763,7 @@ public class HSQLDBRepository implements Repository { } /** Converts milliseconds from epoch to OffsetDateTime needed for TIMESTAMP WITH TIME ZONE columns. */ - /* package */ static OffsetDateTime toOffsetDateTime(Long timestamp) { + public static OffsetDateTime toOffsetDateTime(Long timestamp) { if (timestamp == null) return null; @@ -771,12 +771,12 @@ public class HSQLDBRepository implements Repository { } /** Converts OffsetDateTime from TIMESTAMP WITH TIME ZONE column to milliseconds from epoch. */ - /* package */ static long fromOffsetDateTime(OffsetDateTime offsetDateTime) { + public static long fromOffsetDateTime(OffsetDateTime offsetDateTime) { return offsetDateTime.toInstant().toEpochMilli(); } /** Returns TIMESTAMP WITH TIME ZONE column value as milliseconds from epoch, or null. */ - /* package */ static Long getZonedTimestampMilli(ResultSet resultSet, int columnIndex) throws SQLException { + public static Long getZonedTimestampMilli(ResultSet resultSet, int columnIndex) throws SQLException { OffsetDateTime offsetDateTime = resultSet.getObject(columnIndex, OffsetDateTime.class); if (offsetDateTime == null) return null; diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBAtTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBAtTransactionRepository.java index a14fee4b..a816ec60 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBAtTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBAtTransactionRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb.transaction; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; @@ -27,7 +26,9 @@ public class HSQLDBAtTransactionRepository extends HSQLDBTransactionRepository { String atAddress = resultSet.getString(1); String recipient = resultSet.getString(2); - BigDecimal amount = resultSet.getBigDecimal(3); + Long amount = resultSet.getLong(3); + if (amount == 0 && resultSet.wasNull()) + amount = null; Long assetId = resultSet.getLong(4); if (assetId == 0 && resultSet.wasNull()) diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java index 7e62b5b2..8ae4ffa2 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb.transaction; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; @@ -25,7 +24,7 @@ public class HSQLDBBuyNameTransactionRepository extends HSQLDBTransactionReposit return null; String name = resultSet.getString(1); - BigDecimal amount = resultSet.getBigDecimal(2); + long amount = resultSet.getLong(2); String seller = resultSet.getString(3); byte[] nameReference = resultSet.getBytes(4); diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java index 4ae9ad12..7cea87bb 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb.transaction; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; @@ -30,9 +29,9 @@ public class HSQLDBCreateAssetOrderTransactionRepository extends HSQLDBTransacti return null; long haveAssetId = resultSet.getLong(1); - BigDecimal amount = resultSet.getBigDecimal(2); + long amount = resultSet.getLong(2); long wantAssetId = resultSet.getLong(3); - BigDecimal price = resultSet.getBigDecimal(4); + long price = resultSet.getLong(4); String haveAssetName = resultSet.getString(5); String wantAssetName = resultSet.getString(6); diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBDeployAtTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBDeployAtTransactionRepository.java index a22cdc5e..6c3b60c2 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBDeployAtTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBDeployAtTransactionRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb.transaction; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; @@ -29,7 +28,7 @@ public class HSQLDBDeployAtTransactionRepository extends HSQLDBTransactionReposi String atType = resultSet.getString(3); String tags = resultSet.getString(4); byte[] creationBytes = resultSet.getBytes(5); - BigDecimal amount = resultSet.getBigDecimal(6).setScale(8); + long amount = resultSet.getLong(6); long assetId = resultSet.getLong(7); // Special null-checking for AT address diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java index 0407ba91..d0a65df0 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb.transaction; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; @@ -25,7 +24,7 @@ public class HSQLDBGenesisTransactionRepository extends HSQLDBTransactionReposit return null; String recipient = resultSet.getString(1); - BigDecimal amount = resultSet.getBigDecimal(2).setScale(8); + long amount = resultSet.getLong(2); long assetId = resultSet.getLong(3); return new GenesisTransactionData(baseTransactionData, recipient, amount, assetId); diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java index 2e4b0b9d..1ef2a707 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb.transaction; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; @@ -28,7 +27,7 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit String recipient = resultSet.getString(2); boolean isText = resultSet.getBoolean(3); boolean isEncrypted = resultSet.getBoolean(4); - BigDecimal amount = resultSet.getBigDecimal(5); + long amount = resultSet.getLong(5); // Special null-checking for asset ID Long assetId = resultSet.getLong(6); diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java index 7acc3cdd..a4942e3f 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb.transaction; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; @@ -25,7 +24,7 @@ public class HSQLDBPaymentTransactionRepository extends HSQLDBTransactionReposit return null; String recipient = resultSet.getString(1); - BigDecimal amount = resultSet.getBigDecimal(2); + long amount = resultSet.getLong(2); return new PaymentTransactionData(baseTransactionData, recipient, amount); } catch (SQLException e) { diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBRewardShareTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBRewardShareTransactionRepository.java index 164a6f68..81a567ea 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBRewardShareTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBRewardShareTransactionRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb.transaction; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; @@ -26,8 +25,11 @@ public class HSQLDBRewardShareTransactionRepository extends HSQLDBTransactionRep String recipient = resultSet.getString(1); byte[] rewardSharePublicKey = resultSet.getBytes(2); - BigDecimal sharePercent = resultSet.getBigDecimal(3); - BigDecimal previousSharePercent = resultSet.getBigDecimal(4); + int sharePercent = resultSet.getInt(3); + + Integer previousSharePercent = resultSet.getInt(4); + if (previousSharePercent == 0 && resultSet.wasNull()) + previousSharePercent = null; return new RewardShareTransactionData(baseTransactionData, recipient, rewardSharePublicKey, sharePercent, previousSharePercent); } catch (SQLException e) { diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java index 33202358..f5fa3681 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb.transaction; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; @@ -25,7 +24,7 @@ public class HSQLDBSellNameTransactionRepository extends HSQLDBTransactionReposi return null; String name = resultSet.getString(1); - BigDecimal amount = resultSet.getBigDecimal(2); + long amount = resultSet.getLong(2); return new SellNameTransactionData(baseTransactionData, name, amount); } catch (SQLException e) { diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBTransactionRepository.java index d2b58a45..a4ca30d1 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBTransactionRepository.java @@ -1,16 +1,16 @@ package org.qortal.repository.hsqldb.transaction; +import static org.qortal.repository.hsqldb.HSQLDBRepository.getZonedTimestampMilli; +import static org.qortal.repository.hsqldb.HSQLDBRepository.toOffsetDateTime; + import static org.qortal.transaction.Transaction.TransactionType.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Timestamp; import java.util.ArrayList; -import java.util.Calendar; import java.util.EnumMap; import java.util.List; import java.util.Map; @@ -135,8 +135,12 @@ public class HSQLDBTransactionRepository implements TransactionRepository { byte[] reference = resultSet.getBytes(2); byte[] creatorPublicKey = resultSet.getBytes(3); - long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); - BigDecimal fee = resultSet.getBigDecimal(5).setScale(8); + long timestamp = getZonedTimestampMilli(resultSet, 4); + + Long fee = resultSet.getLong(5); + if (fee == 0 && resultSet.wasNull()) + fee = null; + int txGroupId = resultSet.getInt(6); Integer blockHeight = resultSet.getInt(7); @@ -168,8 +172,12 @@ public class HSQLDBTransactionRepository implements TransactionRepository { byte[] signature = resultSet.getBytes(2); byte[] creatorPublicKey = resultSet.getBytes(3); - long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); - BigDecimal fee = resultSet.getBigDecimal(5).setScale(8); + long timestamp = getZonedTimestampMilli(resultSet, 4); + + Long fee = resultSet.getLong(5); + if (fee == 0 && resultSet.wasNull()) + fee = null; + int txGroupId = resultSet.getInt(6); Integer blockHeight = resultSet.getInt(7); @@ -244,7 +252,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository { // NOTE: do-while because checkedExecute() above has already called rs.next() for us do { String recipient = resultSet.getString(1); - BigDecimal amount = resultSet.getBigDecimal(2); + long amount = resultSet.getLong(2); long assetId = resultSet.getLong(3); payments.add(new PaymentData(recipient, assetId, amount)); @@ -673,10 +681,10 @@ public class HSQLDBTransactionRepository implements TransactionRepository { return assetTransfers; do { - long timestamp = resultSet.getTimestamp(1, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long timestamp = getZonedTimestampMilli(resultSet, 1); int txGroupId = resultSet.getInt(2); byte[] reference = resultSet.getBytes(3); - BigDecimal fee = resultSet.getBigDecimal(4).setScale(8); + long fee = resultSet.getLong(4); byte[] signature = resultSet.getBytes(5); byte[] creatorPublicKey = resultSet.getBytes(6); @@ -693,7 +701,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, creatorPublicKey, fee, approvalStatus, blockHeight, approvalHeight, signature); String recipient = resultSet.getString(10); - BigDecimal amount = resultSet.getBigDecimal(11); + long amount = resultSet.getLong(11); String assetName = resultSet.getString(12); assetTransfers.add(new TransferAssetTransactionData(baseTransactionData, recipient, amount, assetId, assetName)); @@ -1027,7 +1035,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository { public void unconfirmTransaction(TransactionData transactionData) throws DataException { HSQLDBSaver saver = new HSQLDBSaver("UnconfirmedTransactions"); - saver.bind("signature", transactionData.getSignature()).bind("creation", new Timestamp(transactionData.getTimestamp())); + saver.bind("signature", transactionData.getSignature()).bind("creation", toOffsetDateTime(transactionData.getTimestamp())); try { saver.execute(repository); @@ -1044,7 +1052,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository { saver.bind("signature", transactionData.getSignature()).bind("reference", transactionData.getReference()) .bind("type", transactionData.getType().value) - .bind("creator", transactionData.getCreatorPublicKey()).bind("creation", new Timestamp(transactionData.getTimestamp())) + .bind("creator", transactionData.getCreatorPublicKey()).bind("creation", toOffsetDateTime(transactionData.getTimestamp())) .bind("fee", transactionData.getFee()).bind("milestone_block", null).bind("tx_group_id", transactionData.getTxGroupId()) .bind("approval_status", transactionData.getApprovalStatus().value); diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java index 45fc0ddb..5c0a23d0 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java @@ -1,6 +1,5 @@ package org.qortal.repository.hsqldb.transaction; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; @@ -27,7 +26,7 @@ public class HSQLDBTransferAssetTransactionRepository extends HSQLDBTransactionR String recipient = resultSet.getString(1); long assetId = resultSet.getLong(2); - BigDecimal amount = resultSet.getBigDecimal(3); + long amount = resultSet.getLong(3); String assetName = resultSet.getString(4); return new TransferAssetTransactionData(baseTransactionData, recipient, amount, assetId, assetName); diff --git a/src/main/java/org/qortal/transaction/AccountFlagsTransaction.java b/src/main/java/org/qortal/transaction/AccountFlagsTransaction.java index bc7511d5..355340b6 100644 --- a/src/main/java/org/qortal/transaction/AccountFlagsTransaction.java +++ b/src/main/java/org/qortal/transaction/AccountFlagsTransaction.java @@ -1,12 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.NullAccount; -import org.qortal.asset.Asset; import org.qortal.data.transaction.AccountFlagsTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.DataException; @@ -15,7 +12,9 @@ import org.qortal.repository.Repository; public class AccountFlagsTransaction extends Transaction { // Properties + private AccountFlagsTransactionData accountFlagsTransactionData; + private Account targetAccount = null; // Constructors @@ -28,78 +27,46 @@ public class AccountFlagsTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getCreator().getAddress())) - return true; - - if (address.equals(this.getTarget().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getCreator().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.accountFlagsTransactionData.getTarget()); } // Navigation public Account getTarget() { - return new Account(this.repository, this.accountFlagsTransactionData.getTarget()); + if (this.targetAccount == null) + this.targetAccount = new Account(this.repository, this.accountFlagsTransactionData.getTarget()); + + return this.targetAccount; } // Processing @Override public ValidationResult isValid() throws DataException { - Account creator = getCreator(); - - // Only null account can modify flags - if (!creator.getAddress().equals(NullAccount.ADDRESS)) - return ValidationResult.NO_FLAG_PERMISSION; - - // Check fee is zero or positive - if (accountFlagsTransactionData.getFee().compareTo(BigDecimal.ZERO) < 0) - return ValidationResult.NEGATIVE_FEE; - - // Check creator has enough funds - if (creator.getConfirmedBalance(Asset.QORT).compareTo(accountFlagsTransactionData.getFee()) < 0) - return ValidationResult.NO_BALANCE; - - return ValidationResult.OK; + // Invalid outside of genesis block + return ValidationResult.NO_FLAG_PERMISSION; } @Override public void process() throws DataException { - Account target = getTarget(); + Account target = this.getTarget(); Integer previousFlags = target.getFlags(); - accountFlagsTransactionData.setPreviousFlags(previousFlags); + this.accountFlagsTransactionData.setPreviousFlags(previousFlags); // Save this transaction with target account's previous flags value - this.repository.getTransactionRepository().save(accountFlagsTransactionData); + this.repository.getTransactionRepository().save(this.accountFlagsTransactionData); // If account doesn't have entry in database yet (e.g. genesis block) then flags are zero if (previousFlags == null) previousFlags = 0; // Set account's new flags - int newFlags = previousFlags & accountFlagsTransactionData.getAndMask() - | accountFlagsTransactionData.getOrMask() ^ accountFlagsTransactionData.getXorMask(); + int newFlags = previousFlags + & this.accountFlagsTransactionData.getAndMask() + | this.accountFlagsTransactionData.getOrMask() + ^ this.accountFlagsTransactionData.getXorMask(); target.setFlags(newFlags); } @@ -107,15 +74,14 @@ public class AccountFlagsTransaction extends Transaction { @Override public void processReferencesAndFees() throws DataException { // Set account's reference - getTarget().setLastReference(this.accountFlagsTransactionData.getSignature()); + this.getTarget().setLastReference(this.accountFlagsTransactionData.getSignature()); } @Override public void orphan() throws DataException { // Revert Account target = getTarget(); - - Integer previousFlags = accountFlagsTransactionData.getPreviousFlags(); + Integer previousFlags = this.accountFlagsTransactionData.getPreviousFlags(); // If previousFlags are null then account didn't exist before this transaction if (previousFlags == null) @@ -124,7 +90,7 @@ public class AccountFlagsTransaction extends Transaction { target.setFlags(previousFlags); // Remove previous flags from transaction itself - accountFlagsTransactionData.setPreviousFlags(null); + this.accountFlagsTransactionData.setPreviousFlags(null); this.repository.getTransactionRepository().save(accountFlagsTransactionData); } diff --git a/src/main/java/org/qortal/transaction/AccountLevelTransaction.java b/src/main/java/org/qortal/transaction/AccountLevelTransaction.java index 14d46f29..da986344 100644 --- a/src/main/java/org/qortal/transaction/AccountLevelTransaction.java +++ b/src/main/java/org/qortal/transaction/AccountLevelTransaction.java @@ -1,12 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.NullAccount; -import org.qortal.asset.Asset; import org.qortal.block.BlockChain; import org.qortal.data.transaction.AccountLevelTransactionData; import org.qortal.data.transaction.TransactionData; @@ -16,7 +13,9 @@ import org.qortal.repository.Repository; public class AccountLevelTransaction extends Transaction { // Properties + private AccountLevelTransactionData accountLevelTransactionData; + private Account targetAccount = null; // Constructors @@ -29,59 +28,25 @@ public class AccountLevelTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getCreator().getAddress())) - return true; - - if (address.equals(this.getTarget().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getCreator().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.accountLevelTransactionData.getTarget()); } // Navigation public Account getTarget() { - return new Account(this.repository, this.accountLevelTransactionData.getTarget()); + if (this.targetAccount == null) + this.targetAccount = new Account(this.repository, this.accountLevelTransactionData.getTarget()); + + return this.targetAccount; } // Processing @Override public ValidationResult isValid() throws DataException { - Account creator = getCreator(); - - // Only genesis account can modify level - if (!creator.getAddress().equals(new NullAccount(repository).getAddress())) - return ValidationResult.NO_FLAG_PERMISSION; - - // Check fee is zero or positive - if (accountLevelTransactionData.getFee().compareTo(BigDecimal.ZERO) < 0) - return ValidationResult.NEGATIVE_FEE; - - // Check creator has enough funds - if (creator.getConfirmedBalance(Asset.QORT).compareTo(accountLevelTransactionData.getFee()) < 0) - return ValidationResult.NO_BALANCE; - - return ValidationResult.OK; + // Invalid outside of genesis block + return ValidationResult.NO_FLAG_PERMISSION; } @Override @@ -89,13 +54,13 @@ public class AccountLevelTransaction extends Transaction { Account target = getTarget(); // Save this transaction - this.repository.getTransactionRepository().save(accountLevelTransactionData); + this.repository.getTransactionRepository().save(this.accountLevelTransactionData); // Set account's initial level target.setLevel(this.accountLevelTransactionData.getLevel()); // Set account's blocks minted adjustment - final List cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel(); + List cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel(); int blocksMintedAdjustment = cumulativeBlocksByLevel.get(this.accountLevelTransactionData.getLevel()); target.setBlocksMintedAdjustment(blocksMintedAdjustment); } diff --git a/src/main/java/org/qortal/transaction/AddGroupAdminTransaction.java b/src/main/java/org/qortal/transaction/AddGroupAdminTransaction.java index fcfe897a..d62bd451 100644 --- a/src/main/java/org/qortal/transaction/AddGroupAdminTransaction.java +++ b/src/main/java/org/qortal/transaction/AddGroupAdminTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.data.transaction.AddGroupAdminTransactionData; @@ -17,7 +15,9 @@ import org.qortal.repository.Repository; public class AddGroupAdminTransaction extends Transaction { // Properties + private AddGroupAdminTransactionData addGroupAdminTransactionData; + Account memberAccount = null; // Constructors @@ -30,79 +30,55 @@ public class AddGroupAdminTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getOwner().getAddress())) - return true; - - if (address.equals(this.getMember().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getOwner().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.addGroupAdminTransactionData.getMember()); } // Navigation - public Account getOwner() throws DataException { - return new PublicKeyAccount(this.repository, this.addGroupAdminTransactionData.getOwnerPublicKey()); + public Account getOwner() { + return this.getCreator(); } - public Account getMember() throws DataException { - return new Account(this.repository, this.addGroupAdminTransactionData.getMember()); + public Account getMember() { + if (this.memberAccount == null) + this.memberAccount = new Account(this.repository, this.addGroupAdminTransactionData.getMember()); + + return this.memberAccount; } // Processing @Override public ValidationResult isValid() throws DataException { + int groupId = this.addGroupAdminTransactionData.getGroupId(); + String memberAddress = this.addGroupAdminTransactionData.getMember(); + // Check member address is valid - if (!Crypto.isValidAddress(addGroupAdminTransactionData.getMember())) + if (!Crypto.isValidAddress(memberAddress)) return ValidationResult.INVALID_ADDRESS; // Check group exists - if (!this.repository.getGroupRepository().groupExists(addGroupAdminTransactionData.getGroupId())) + if (!this.repository.getGroupRepository().groupExists(groupId)) return ValidationResult.GROUP_DOES_NOT_EXIST; - // Check fee is positive - if (addGroupAdminTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - Account owner = getOwner(); - String groupOwner = this.repository.getGroupRepository().getOwner(addGroupAdminTransactionData.getGroupId()); + String groupOwner = this.repository.getGroupRepository().getOwner(groupId); // Check transaction's public key matches group's current owner if (!owner.getAddress().equals(groupOwner)) return ValidationResult.INVALID_GROUP_OWNER; - Account member = getMember(); - - // Check address is a member - if (!this.repository.getGroupRepository().memberExists(addGroupAdminTransactionData.getGroupId(), member.getAddress())) + // Check address is a group member + if (!this.repository.getGroupRepository().memberExists(groupId, memberAddress)) return ValidationResult.NOT_GROUP_MEMBER; - // Check member is not already an admin - if (this.repository.getGroupRepository().adminExists(addGroupAdminTransactionData.getGroupId(), member.getAddress())) + // Check group member is not already an admin + if (this.repository.getGroupRepository().adminExists(groupId, memberAddress)) return ValidationResult.ALREADY_GROUP_ADMIN; // Check group owner has enough funds - if (owner.getConfirmedBalance(Asset.QORT).compareTo(addGroupAdminTransactionData.getFee()) < 0) + if (owner.getConfirmedBalance(Asset.QORT) < this.addGroupAdminTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -111,15 +87,15 @@ public class AddGroupAdminTransaction extends Transaction { @Override public void process() throws DataException { // Update Group adminship - Group group = new Group(this.repository, addGroupAdminTransactionData.getGroupId()); - group.promoteToAdmin(addGroupAdminTransactionData); + Group group = new Group(this.repository, this.addGroupAdminTransactionData.getGroupId()); + group.promoteToAdmin(this.addGroupAdminTransactionData); } @Override public void orphan() throws DataException { // Revert group adminship - Group group = new Group(this.repository, addGroupAdminTransactionData.getGroupId()); - group.unpromoteToAdmin(addGroupAdminTransactionData); + Group group = new Group(this.repository, this.addGroupAdminTransactionData.getGroupId()); + group.unpromoteToAdmin(this.addGroupAdminTransactionData); } } \ No newline at end of file diff --git a/src/main/java/org/qortal/transaction/ArbitraryTransaction.java b/src/main/java/org/qortal/transaction/ArbitraryTransaction.java index 1b8f02b4..b40a6557 100644 --- a/src/main/java/org/qortal/transaction/ArbitraryTransaction.java +++ b/src/main/java/org/qortal/transaction/ArbitraryTransaction.java @@ -1,12 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; -import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; -import org.qortal.asset.Asset; import org.qortal.data.PaymentData; import org.qortal.data.transaction.ArbitraryTransactionData; import org.qortal.data.transaction.TransactionData; @@ -33,57 +30,14 @@ public class ArbitraryTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - List recipients = new ArrayList<>(); - - if (arbitraryTransactionData.getVersion() != 1) - for (PaymentData paymentData : arbitraryTransactionData.getPayments()) - recipients.add(new Account(this.repository, paymentData.getRecipient())); - - return recipients; - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getSender().getAddress())) - return true; - - if (arbitraryTransactionData.getVersion() != 1) - for (PaymentData paymentData : arbitraryTransactionData.getPayments()) - if (address.equals(paymentData.getRecipient())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - String senderAddress = this.getSender().getAddress(); - - if (address.equals(senderAddress)) - amount = amount.subtract(this.transactionData.getFee()); - - if (arbitraryTransactionData.getVersion() != 1) - for (PaymentData paymentData : arbitraryTransactionData.getPayments()) - // We're only interested in QORT - if (paymentData.getAssetId() == Asset.QORT) { - if (address.equals(paymentData.getRecipient())) - amount = amount.add(paymentData.getAmount()); - else if (address.equals(senderAddress)) - amount = amount.subtract(paymentData.getAmount()); - } - - return amount; + public List getRecipientAddresses() throws DataException { + return this.arbitraryTransactionData.getPayments().stream().map(PaymentData::getRecipient).collect(Collectors.toList()); } // Navigation - public Account getSender() throws DataException { - return new PublicKeyAccount(this.repository, this.arbitraryTransactionData.getSenderPublicKey()); + public Account getSender() { + return this.getCreator(); } // Processing @@ -110,7 +64,7 @@ public class ArbitraryTransaction extends Transaction { public void process() throws DataException { // Wrap and delegate payment processing to Payment class. new Payment(this.repository).process(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(), - arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature()); + arbitraryTransactionData.getSignature()); } @Override @@ -124,7 +78,7 @@ public class ArbitraryTransaction extends Transaction { public void orphan() throws DataException { // Wrap and delegate payment processing to Payment class. new Payment(this.repository).orphan(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(), - arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature(), arbitraryTransactionData.getReference()); + arbitraryTransactionData.getSignature(), arbitraryTransactionData.getReference()); } @Override diff --git a/src/main/java/org/qortal/transaction/AtTransaction.java b/src/main/java/org/qortal/transaction/AtTransaction.java index 15ae12e3..6d5c804d 100644 --- a/src/main/java/org/qortal/transaction/AtTransaction.java +++ b/src/main/java/org/qortal/transaction/AtTransaction.java @@ -1,9 +1,6 @@ package org.qortal.transaction; -import java.math.BigDecimal; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.qortal.account.Account; @@ -22,7 +19,10 @@ import com.google.common.primitives.Bytes; public class AtTransaction extends Transaction { // Properties + private ATTransactionData atTransactionData; + private Account atAccount = null; + private Account recipientAccount = null; // Other useful constants public static final int MAX_DATA_SIZE = 256; @@ -50,97 +50,62 @@ public class AtTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(new Account(this.repository, this.atTransactionData.getRecipient())); - } - - /** For AT-Transactions, the use the AT address instead of transaction creator (which is genesis account) */ - @Override - public List getInvolvedAccounts() throws DataException { - List participants = new ArrayList<>(getRecipientAccounts()); - participants.add(getATAccount()); - return participants; - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.atTransactionData.getATAddress())) - return true; - - if (address.equals(this.atTransactionData.getRecipient())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - String atAddress = this.atTransactionData.getATAddress(); - - if (address.equals(atAddress)) { - amount = amount.subtract(this.atTransactionData.getFee()); - - if (this.atTransactionData.getAmount() != null && this.atTransactionData.getAssetId() == Asset.QORT) - amount = amount.subtract(this.atTransactionData.getAmount()); - } - - if (address.equals(this.atTransactionData.getRecipient()) && this.atTransactionData.getAmount() != null - && this.atTransactionData.getAssetId() == Asset.QORT) - amount = amount.add(this.atTransactionData.getAmount()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Arrays.asList(this.atTransactionData.getATAddress(), this.atTransactionData.getRecipient()); } // Navigation - public Account getATAccount() throws DataException { - return new Account(this.repository, this.atTransactionData.getATAddress()); + public Account getATAccount() { + if (this.atAccount == null) + this.atAccount = new Account(this.repository, this.atTransactionData.getATAddress()); + + return this.atAccount; } - public Account getRecipient() throws DataException { - return new Account(this.repository, this.atTransactionData.getRecipient()); + public Account getRecipient() { + if (this.recipientAccount == null) + this.recipientAccount = new Account(this.repository, this.atTransactionData.getRecipient()); + + return this.recipientAccount; } // Processing @Override public boolean hasValidReference() throws DataException { - // Check reference is correct + // Check reference is correct, using AT account, not transaction creator which is null account Account atAccount = getATAccount(); return Arrays.equals(atAccount.getLastReference(), atTransactionData.getReference()); } @Override public ValidationResult isValid() throws DataException { - if (this.atTransactionData.getMessage().length > MAX_DATA_SIZE) - return ValidationResult.INVALID_DATA_LENGTH; - - BigDecimal amount = this.atTransactionData.getAmount(); - byte[] message = this.atTransactionData.getMessage(); - - // We can only have either message or amount - boolean amountIsZero = amount.compareTo(BigDecimal.ZERO.setScale(8)) == 0; - boolean messageIsEmpty = message.length == 0; - - if ((messageIsEmpty && amountIsZero) || (!messageIsEmpty && !amountIsZero)) - return ValidationResult.INVALID_AT_TRANSACTION; - - // If we have no payment then we're done - if (amountIsZero) - return ValidationResult.OK; - - // Check amount is zero or positive - if (amount.compareTo(BigDecimal.ZERO) < 0) - return ValidationResult.NEGATIVE_AMOUNT; - // Check recipient address is valid if (!Crypto.isValidAddress(this.atTransactionData.getRecipient())) return ValidationResult.INVALID_ADDRESS; + Long amount = this.atTransactionData.getAmount(); + byte[] message = this.atTransactionData.getMessage(); + + // We can only have either message or amount + boolean amountIsNull = amount == null; + boolean messageIsEmpty = message == null || message.length == 0; + + if ((messageIsEmpty && amountIsNull) || (!messageIsEmpty && !amountIsNull)) + return ValidationResult.INVALID_AT_TRANSACTION; + + if (!messageIsEmpty && message.length > MAX_DATA_SIZE) + return ValidationResult.INVALID_DATA_LENGTH; + + // If we have no payment then we're done + if (amountIsNull) + return ValidationResult.OK; + + // Check amount is zero or positive + if (amount < 0) + return ValidationResult.NEGATIVE_AMOUNT; + long assetId = this.atTransactionData.getAssetId(); AssetData assetData = this.repository.getAssetRepository().fromAssetId(assetId); // Check asset even exists @@ -148,12 +113,12 @@ 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.stripTrailingZeros().scale() > 0) + if (!assetData.getIsDivisible() && amount % Asset.MULTIPLIER != 0) return ValidationResult.INVALID_AMOUNT; Account sender = getATAccount(); // Check sender has enough of asset - if (sender.getConfirmedBalance(assetId).compareTo(amount) < 0) + if (sender.getConfirmedBalance(assetId) < amount) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -161,18 +126,19 @@ public class AtTransaction extends Transaction { @Override public void process() throws DataException { - if (this.atTransactionData.getAmount() != null) { + Long amount = this.atTransactionData.getAmount(); + + if (amount != null) { Account sender = getATAccount(); Account recipient = getRecipient(); long assetId = this.atTransactionData.getAssetId(); - BigDecimal amount = this.atTransactionData.getAmount(); // Update sender's balance due to amount - sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId).subtract(amount)); + sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId) - amount); // Update recipient's balance - recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).add(amount)); + recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId) + amount); } } @@ -192,18 +158,19 @@ public class AtTransaction extends Transaction { @Override public void orphan() throws DataException { - if (this.atTransactionData.getAmount() != null) { + Long amount = this.atTransactionData.getAmount(); + + if (amount != null) { Account sender = getATAccount(); Account recipient = getRecipient(); long assetId = this.atTransactionData.getAssetId(); - BigDecimal amount = this.atTransactionData.getAmount(); // Update sender's balance due to amount - sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId).add(amount)); + sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId) + amount); // Update recipient's balance - recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).subtract(amount)); + recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId) - amount); } // As AT_TRANSACTIONs are really part of a block, the caller (Block) will probably delete this transaction after orphaning diff --git a/src/main/java/org/qortal/transaction/BuyNameTransaction.java b/src/main/java/org/qortal/transaction/BuyNameTransaction.java index 2e37468a..63b6447e 100644 --- a/src/main/java/org/qortal/transaction/BuyNameTransaction.java +++ b/src/main/java/org/qortal/transaction/BuyNameTransaction.java @@ -1,12 +1,11 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; +import org.qortal.crypto.Crypto; import org.qortal.data.naming.NameData; import org.qortal.data.transaction.BuyNameTransactionData; import org.qortal.data.transaction.TransactionData; @@ -19,6 +18,7 @@ import com.google.common.base.Utf8; public class BuyNameTransaction extends Transaction { // Properties + private BuyNameTransactionData buyNameTransactionData; // Constructors @@ -32,57 +32,36 @@ public class BuyNameTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(new Account(this.repository, this.buyNameTransactionData.getSeller())); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getBuyer().getAddress())) - return true; - - if (address.equals(this.buyNameTransactionData.getSeller())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getBuyer().getAddress())) - amount = amount.subtract(this.transactionData.getFee()).subtract(this.buyNameTransactionData.getAmount()); - - if (address.equals(this.buyNameTransactionData.getSeller())) - amount = amount.add(this.buyNameTransactionData.getAmount()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.buyNameTransactionData.getSeller()); } // Navigation - public Account getBuyer() throws DataException { - return new PublicKeyAccount(this.repository, this.buyNameTransactionData.getBuyerPublicKey()); + public Account getBuyer() { + return this.getCreator(); } // Processing @Override public ValidationResult isValid() throws DataException { + String name = this.buyNameTransactionData.getName(); + + // Check seller address is valid + if (!Crypto.isValidAddress(this.buyNameTransactionData.getSeller())) + return ValidationResult.INVALID_ADDRESS; + // Check name size bounds - int nameLength = Utf8.encodedLength(buyNameTransactionData.getName()); + int nameLength = Utf8.encodedLength(name); if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check name is lowercase - if (!buyNameTransactionData.getName().equals(buyNameTransactionData.getName().toLowerCase())) + if (!name.equals(name.toLowerCase())) return ValidationResult.NAME_NOT_LOWER_CASE; - NameData nameData = this.repository.getNameRepository().fromName(buyNameTransactionData.getName()); + NameData nameData = this.repository.getNameRepository().fromName(name); // Check name exists if (nameData == null) @@ -98,19 +77,15 @@ public class BuyNameTransaction extends Transaction { return ValidationResult.BUYER_ALREADY_OWNER; // Check expected seller currently owns name - if (!buyNameTransactionData.getSeller().equals(nameData.getOwner())) + if (!this.buyNameTransactionData.getSeller().equals(nameData.getOwner())) return ValidationResult.INVALID_SELLER; // Check amounts agree - if (buyNameTransactionData.getAmount().compareTo(nameData.getSalePrice()) != 0) + if (this.buyNameTransactionData.getAmount() != nameData.getSalePrice()) return ValidationResult.INVALID_AMOUNT; - // Check fee is positive - if (buyNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check issuer has enough funds - if (buyer.getConfirmedBalance(Asset.QORT).compareTo(buyNameTransactionData.getFee()) < 0) + if (buyer.getConfirmedBalance(Asset.QORT) < this.buyNameTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -119,21 +94,21 @@ public class BuyNameTransaction extends Transaction { @Override public void process() throws DataException { // Update Name - Name name = new Name(this.repository, buyNameTransactionData.getName()); - name.buy(buyNameTransactionData); + Name name = new Name(this.repository, this.buyNameTransactionData.getName()); + name.buy(this.buyNameTransactionData); // Save transaction with updated "name reference" pointing to previous transaction that updated name - this.repository.getTransactionRepository().save(buyNameTransactionData); + this.repository.getTransactionRepository().save(this.buyNameTransactionData); } @Override public void orphan() throws DataException { // Revert name - Name name = new Name(this.repository, buyNameTransactionData.getName()); - name.unbuy(buyNameTransactionData); + Name name = new Name(this.repository, this.buyNameTransactionData.getName()); + name.unbuy(this.buyNameTransactionData); // Save this transaction, with removed "name reference" - this.repository.getTransactionRepository().save(buyNameTransactionData); + this.repository.getTransactionRepository().save(this.buyNameTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/CancelAssetOrderTransaction.java b/src/main/java/org/qortal/transaction/CancelAssetOrderTransaction.java index 5d41b87d..b8b70dde 100644 --- a/src/main/java/org/qortal/transaction/CancelAssetOrderTransaction.java +++ b/src/main/java/org/qortal/transaction/CancelAssetOrderTransaction.java @@ -1,14 +1,12 @@ package org.qortal.transaction; -import java.math.BigDecimal; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.asset.Order; -import org.qortal.crypto.Crypto; import org.qortal.data.asset.OrderData; import org.qortal.data.transaction.CancelAssetOrderTransactionData; import org.qortal.data.transaction.TransactionData; @@ -32,30 +30,8 @@ public class CancelAssetOrderTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() { - return new ArrayList<>(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - return account.getAddress().equals(this.getCreator().getAddress()); - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (account.getAddress().equals(this.getCreator().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; - } - - // Navigation - - @Override - public PublicKeyAccount getCreator() throws DataException { - return new PublicKeyAccount(this.repository, cancelOrderTransactionData.getCreatorPublicKey()); + public List getRecipientAddresses() { + return Collections.emptyList(); } // Processing @@ -64,12 +40,8 @@ public class CancelAssetOrderTransaction extends Transaction { public ValidationResult isValid() throws DataException { AssetRepository assetRepository = this.repository.getAssetRepository(); - // Check fee is positive - if (cancelOrderTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check order even exists - OrderData orderData = assetRepository.fromOrderId(cancelOrderTransactionData.getOrderId()); + OrderData orderData = assetRepository.fromOrderId(this.cancelOrderTransactionData.getOrderId()); if (orderData == null) return ValidationResult.ORDER_DOES_NOT_EXIST; @@ -77,19 +49,14 @@ public class CancelAssetOrderTransaction extends Transaction { if (orderData.getIsClosed()) return ValidationResult.ORDER_ALREADY_CLOSED; - Account creator = getCreator(); - - // Check creator's public key results in valid address - if (!Crypto.isValidAddress(creator.getAddress())) - return ValidationResult.INVALID_ADDRESS; - - // Check creator's public key matches order's creator's public key - Account orderCreator = new PublicKeyAccount(this.repository, orderData.getCreatorPublicKey()); - if (!orderCreator.getAddress().equals(creator.getAddress())) + // Check transaction creator matches order creator + if (!Arrays.equals(this.transactionData.getCreatorPublicKey(), orderData.getCreatorPublicKey())) return ValidationResult.INVALID_ORDER_CREATOR; + Account creator = getCreator(); + // Check creator has enough QORT for fee - if (creator.getConfirmedBalance(Asset.QORT).compareTo(cancelOrderTransactionData.getFee()) < 0) + if (creator.getConfirmedBalance(Asset.QORT) < this.cancelOrderTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -98,7 +65,7 @@ public class CancelAssetOrderTransaction extends Transaction { @Override public void process() throws DataException { // Mark Order as completed so no more trades can happen - OrderData orderData = this.repository.getAssetRepository().fromOrderId(cancelOrderTransactionData.getOrderId()); + OrderData orderData = this.repository.getAssetRepository().fromOrderId(this.cancelOrderTransactionData.getOrderId()); Order order = new Order(this.repository, orderData); order.cancel(); } @@ -106,7 +73,7 @@ public class CancelAssetOrderTransaction extends Transaction { @Override public void orphan() throws DataException { // Unmark Order as completed so trades can happen again - OrderData orderData = this.repository.getAssetRepository().fromOrderId(cancelOrderTransactionData.getOrderId()); + OrderData orderData = this.repository.getAssetRepository().fromOrderId(this.cancelOrderTransactionData.getOrderId()); Order order = new Order(this.repository, orderData); order.reopen(); } diff --git a/src/main/java/org/qortal/transaction/CancelGroupBanTransaction.java b/src/main/java/org/qortal/transaction/CancelGroupBanTransaction.java index 212e84be..e01be7be 100644 --- a/src/main/java/org/qortal/transaction/CancelGroupBanTransaction.java +++ b/src/main/java/org/qortal/transaction/CancelGroupBanTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.data.group.GroupData; @@ -18,7 +16,9 @@ import org.qortal.repository.Repository; public class CancelGroupBanTransaction extends Transaction { // Properties + private CancelGroupBanTransactionData groupUnbanTransactionData; + private Account memberAccount = null; // Constructors @@ -31,53 +31,34 @@ public class CancelGroupBanTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getAdmin().getAddress())) - return true; - - if (address.equals(this.getMember().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getAdmin().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.groupUnbanTransactionData.getMember()); } // Navigation - public Account getAdmin() throws DataException { - return new PublicKeyAccount(this.repository, this.groupUnbanTransactionData.getAdminPublicKey()); + public Account getAdmin() { + return this.getCreator(); } - public Account getMember() throws DataException { - return new Account(this.repository, this.groupUnbanTransactionData.getMember()); + public Account getMember() { + if (this.memberAccount == null) + this.memberAccount = new Account(this.repository, this.groupUnbanTransactionData.getMember()); + + return this.memberAccount; } // Processing @Override public ValidationResult isValid() throws DataException { + int groupId = this.groupUnbanTransactionData.getGroupId(); + // Check member address is valid - if (!Crypto.isValidAddress(groupUnbanTransactionData.getMember())) + if (!Crypto.isValidAddress(this.groupUnbanTransactionData.getMember())) return ValidationResult.INVALID_ADDRESS; - GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupUnbanTransactionData.getGroupId()); + GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupId); // Check group exists if (groupData == null) @@ -86,21 +67,17 @@ public class CancelGroupBanTransaction extends Transaction { Account admin = getAdmin(); // Can't unban if not an admin - if (!this.repository.getGroupRepository().adminExists(groupUnbanTransactionData.getGroupId(), admin.getAddress())) + if (!this.repository.getGroupRepository().adminExists(groupId, admin.getAddress())) return ValidationResult.NOT_GROUP_ADMIN; Account member = getMember(); // Check ban actually exists - if (!this.repository.getGroupRepository().banExists(groupUnbanTransactionData.getGroupId(), member.getAddress())) + if (!this.repository.getGroupRepository().banExists(groupId, member.getAddress())) return ValidationResult.BAN_UNKNOWN; - // Check fee is positive - if (groupUnbanTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - - // Check creator has enough funds - if (admin.getConfirmedBalance(Asset.QORT).compareTo(groupUnbanTransactionData.getFee()) < 0) + // Check admin has enough funds + if (admin.getConfirmedBalance(Asset.QORT) < this.groupUnbanTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -109,21 +86,21 @@ public class CancelGroupBanTransaction extends Transaction { @Override public void process() throws DataException { // Update Group Membership - Group group = new Group(this.repository, groupUnbanTransactionData.getGroupId()); - group.cancelBan(groupUnbanTransactionData); + Group group = new Group(this.repository, this.groupUnbanTransactionData.getGroupId()); + group.cancelBan(this.groupUnbanTransactionData); // Save this transaction with updated member/admin references to transactions that can help restore state - this.repository.getTransactionRepository().save(groupUnbanTransactionData); + this.repository.getTransactionRepository().save(this.groupUnbanTransactionData); } @Override public void orphan() throws DataException { // Revert group membership - Group group = new Group(this.repository, groupUnbanTransactionData.getGroupId()); - group.uncancelBan(groupUnbanTransactionData); + Group group = new Group(this.repository, this.groupUnbanTransactionData.getGroupId()); + group.uncancelBan(this.groupUnbanTransactionData); // Save this transaction with removed member/admin references - this.repository.getTransactionRepository().save(groupUnbanTransactionData); + this.repository.getTransactionRepository().save(this.groupUnbanTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/CancelGroupInviteTransaction.java b/src/main/java/org/qortal/transaction/CancelGroupInviteTransaction.java index 8b47f67f..ea228215 100644 --- a/src/main/java/org/qortal/transaction/CancelGroupInviteTransaction.java +++ b/src/main/java/org/qortal/transaction/CancelGroupInviteTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.data.group.GroupData; @@ -18,7 +16,9 @@ import org.qortal.repository.Repository; public class CancelGroupInviteTransaction extends Transaction { // Properties + private CancelGroupInviteTransactionData cancelGroupInviteTransactionData; + private Account inviteeAccount = null; // Constructors @@ -31,53 +31,34 @@ public class CancelGroupInviteTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getAdmin().getAddress())) - return true; - - if (address.equals(this.getInvitee().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getAdmin().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.cancelGroupInviteTransactionData.getInvitee()); } // Navigation - public Account getAdmin() throws DataException { - return new PublicKeyAccount(this.repository, this.cancelGroupInviteTransactionData.getAdminPublicKey()); + public Account getAdmin() { + return this.getCreator(); } - public Account getInvitee() throws DataException { - return new Account(this.repository, this.cancelGroupInviteTransactionData.getInvitee()); + public Account getInvitee() { + if (this.inviteeAccount == null) + this.inviteeAccount = new Account(this.repository, this.cancelGroupInviteTransactionData.getInvitee()); + + return this.inviteeAccount; } // Processing @Override public ValidationResult isValid() throws DataException { + int groupId = this.cancelGroupInviteTransactionData.getGroupId(); + // Check invitee address is valid - if (!Crypto.isValidAddress(cancelGroupInviteTransactionData.getInvitee())) + if (!Crypto.isValidAddress(this.cancelGroupInviteTransactionData.getInvitee())) return ValidationResult.INVALID_ADDRESS; - GroupData groupData = this.repository.getGroupRepository().fromGroupId(cancelGroupInviteTransactionData.getGroupId()); + GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupId); // Check group exists if (groupData == null) @@ -86,21 +67,17 @@ public class CancelGroupInviteTransaction extends Transaction { Account admin = getAdmin(); // Check admin is actually an admin - if (!this.repository.getGroupRepository().adminExists(cancelGroupInviteTransactionData.getGroupId(), admin.getAddress())) + if (!this.repository.getGroupRepository().adminExists(groupId, admin.getAddress())) return ValidationResult.NOT_GROUP_ADMIN; Account invitee = getInvitee(); // Check invite exists - if (!this.repository.getGroupRepository().inviteExists(cancelGroupInviteTransactionData.getGroupId(), invitee.getAddress())) + if (!this.repository.getGroupRepository().inviteExists(groupId, invitee.getAddress())) return ValidationResult.INVITE_UNKNOWN; - // Check fee is positive - if (cancelGroupInviteTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check creator has enough funds - if (admin.getConfirmedBalance(Asset.QORT).compareTo(cancelGroupInviteTransactionData.getFee()) < 0) + if (admin.getConfirmedBalance(Asset.QORT) < this.cancelGroupInviteTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -109,21 +86,21 @@ public class CancelGroupInviteTransaction extends Transaction { @Override public void process() throws DataException { // Update Group Membership - Group group = new Group(this.repository, cancelGroupInviteTransactionData.getGroupId()); - group.cancelInvite(cancelGroupInviteTransactionData); + Group group = new Group(this.repository, this.cancelGroupInviteTransactionData.getGroupId()); + group.cancelInvite(this.cancelGroupInviteTransactionData); // Save this transaction with updated member/admin references to transactions that can help restore state - this.repository.getTransactionRepository().save(cancelGroupInviteTransactionData); + this.repository.getTransactionRepository().save(this.cancelGroupInviteTransactionData); } @Override public void orphan() throws DataException { // Revert group membership - Group group = new Group(this.repository, cancelGroupInviteTransactionData.getGroupId()); - group.uncancelInvite(cancelGroupInviteTransactionData); + Group group = new Group(this.repository, this.cancelGroupInviteTransactionData.getGroupId()); + group.uncancelInvite(this.cancelGroupInviteTransactionData); // Save this transaction with removed member/admin references - this.repository.getTransactionRepository().save(cancelGroupInviteTransactionData); + this.repository.getTransactionRepository().save(this.cancelGroupInviteTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java b/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java index c86db359..18852c56 100644 --- a/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java +++ b/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.naming.NameData; import org.qortal.data.transaction.CancelSellNameTransactionData; @@ -32,51 +30,32 @@ public class CancelSellNameTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() { - return new ArrayList<>(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getOwner().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getOwner().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.emptyList(); } // Navigation - public Account getOwner() throws DataException { - return new PublicKeyAccount(this.repository, this.cancelSellNameTransactionData.getOwnerPublicKey()); + public Account getOwner() { + return this.getCreator(); } // Processing @Override public ValidationResult isValid() throws DataException { + String name = this.cancelSellNameTransactionData.getName(); + // Check name size bounds - int nameLength = Utf8.encodedLength(cancelSellNameTransactionData.getName()); + int nameLength = Utf8.encodedLength(name); if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check name is lowercase - if (!cancelSellNameTransactionData.getName().equals(cancelSellNameTransactionData.getName().toLowerCase())) + if (!name.equals(name.toLowerCase())) return ValidationResult.NAME_NOT_LOWER_CASE; - NameData nameData = this.repository.getNameRepository().fromName(cancelSellNameTransactionData.getName()); + NameData nameData = this.repository.getNameRepository().fromName(name); // Check name exists if (nameData == null) @@ -86,17 +65,13 @@ public class CancelSellNameTransaction extends Transaction { if (!nameData.getIsForSale()) return ValidationResult.NAME_NOT_FOR_SALE; - // Check transaction's public key matches name's current owner + // Check transaction creator matches name's current owner Account owner = getOwner(); if (!owner.getAddress().equals(nameData.getOwner())) return ValidationResult.INVALID_NAME_OWNER; - // Check fee is positive - if (cancelSellNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check issuer has enough funds - if (owner.getConfirmedBalance(Asset.QORT).compareTo(cancelSellNameTransactionData.getFee()) < 0) + if (owner.getConfirmedBalance(Asset.QORT) < cancelSellNameTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; diff --git a/src/main/java/org/qortal/transaction/CreateAssetOrderTransaction.java b/src/main/java/org/qortal/transaction/CreateAssetOrderTransaction.java index 21dbd230..7d73c6ef 100644 --- a/src/main/java/org/qortal/transaction/CreateAssetOrderTransaction.java +++ b/src/main/java/org/qortal/transaction/CreateAssetOrderTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.asset.Order; import org.qortal.data.asset.AssetData; @@ -32,32 +30,12 @@ public class CreateAssetOrderTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() { - return new ArrayList<>(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - return account.getAddress().equals(this.getCreator().getAddress()); - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (account.getAddress().equals(this.getCreator().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.emptyList(); } // Navigation - @Override - public PublicKeyAccount getCreator() throws DataException { - return new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey()); - } - public Order getOrder() throws DataException { // orderId is the transaction signature OrderData orderData = this.repository.getAssetRepository().fromOrderId(this.createOrderTransactionData.getSignature()); @@ -68,25 +46,21 @@ public class CreateAssetOrderTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { - long haveAssetId = createOrderTransactionData.getHaveAssetId(); - long wantAssetId = createOrderTransactionData.getWantAssetId(); + long haveAssetId = this.createOrderTransactionData.getHaveAssetId(); + long wantAssetId = this.createOrderTransactionData.getWantAssetId(); // Check have/want assets are not the same if (haveAssetId == wantAssetId) return ValidationResult.HAVE_EQUALS_WANT; // Check amount is positive - if (createOrderTransactionData.getAmount().compareTo(BigDecimal.ZERO) <= 0) + if (this.createOrderTransactionData.getAmount() <= 0) return ValidationResult.NEGATIVE_AMOUNT; // Check price is positive - if (createOrderTransactionData.getPrice().compareTo(BigDecimal.ZERO) <= 0) + if (this.createOrderTransactionData.getPrice() <= 0) return ValidationResult.NEGATIVE_PRICE; - // Check fee is positive - if (createOrderTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - AssetRepository assetRepository = this.repository.getAssetRepository(); // Check "have" asset exists @@ -105,8 +79,8 @@ public class CreateAssetOrderTransaction extends Transaction { Account creator = getCreator(); - BigDecimal committedCost; - BigDecimal maxOtherAmount; + long committedCost; + long maxOtherAmount; /* * "amount" might be either have-asset or want-asset, whichever has the highest assetID. @@ -122,35 +96,35 @@ public class CreateAssetOrderTransaction extends Transaction { if (isAmountWantAsset) { // have/commit 49200 QORT, want/return 123 GOLD - committedCost = createOrderTransactionData.getAmount().multiply(createOrderTransactionData.getPrice()); - maxOtherAmount = createOrderTransactionData.getAmount(); + committedCost = this.createOrderTransactionData.getAmount() * this.createOrderTransactionData.getPrice(); + maxOtherAmount = this.createOrderTransactionData.getAmount(); } else { // have/commit 123 GOLD, want/return 49200 QORT - committedCost = createOrderTransactionData.getAmount(); - maxOtherAmount = createOrderTransactionData.getAmount().multiply(createOrderTransactionData.getPrice()); + committedCost = this.createOrderTransactionData.getAmount(); + maxOtherAmount = this.createOrderTransactionData.getAmount() * this.createOrderTransactionData.getPrice(); } // Check amount is integer if amount's asset is not divisible - if (!haveAssetData.getIsDivisible() && committedCost.stripTrailingZeros().scale() > 0) + if (!haveAssetData.getIsDivisible() && committedCost % Asset.MULTIPLIER != 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.stripTrailingZeros().scale() > 0) + if (!wantAssetData.getIsDivisible() && maxOtherAmount % Asset.MULTIPLIER != 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).compareTo(committedCost.add(createOrderTransactionData.getFee())) < 0) + if (creator.getConfirmedBalance(Asset.QORT) < committedCost + this.createOrderTransactionData.getFee()) return ValidationResult.NO_BALANCE; } else { // Check creator has enough funds for amount in whatever asset - if (creator.getConfirmedBalance(haveAssetId).compareTo(committedCost) < 0) + if (creator.getConfirmedBalance(haveAssetId) < committedCost) return ValidationResult.NO_BALANCE; // Check creator has enough funds for fee in QORT - if (creator.getConfirmedBalance(Asset.QORT).compareTo(createOrderTransactionData.getFee()) < 0) + if (creator.getConfirmedBalance(Asset.QORT) < this.createOrderTransactionData.getFee()) return ValidationResult.NO_BALANCE; } @@ -160,12 +134,13 @@ public class CreateAssetOrderTransaction extends Transaction { @Override public void process() throws DataException { // Order Id is transaction's signature - byte[] orderId = createOrderTransactionData.getSignature(); + byte[] orderId = this.createOrderTransactionData.getSignature(); // Process the order itself - OrderData orderData = new OrderData(orderId, createOrderTransactionData.getCreatorPublicKey(), createOrderTransactionData.getHaveAssetId(), - createOrderTransactionData.getWantAssetId(), createOrderTransactionData.getAmount(), createOrderTransactionData.getPrice(), - createOrderTransactionData.getTimestamp()); + OrderData orderData = new OrderData(orderId, this.createOrderTransactionData.getCreatorPublicKey(), + this.createOrderTransactionData.getHaveAssetId(), this.createOrderTransactionData.getWantAssetId(), + this.createOrderTransactionData.getAmount(), this.createOrderTransactionData.getPrice(), + this.createOrderTransactionData.getTimestamp()); new Order(this.repository, orderData).process(); } @@ -173,7 +148,7 @@ public class CreateAssetOrderTransaction extends Transaction { @Override public void orphan() throws DataException { // Order Id is transaction's signature - byte[] orderId = createOrderTransactionData.getSignature(); + byte[] orderId = this.createOrderTransactionData.getSignature(); // Orphan the order itself OrderData orderData = this.repository.getAssetRepository().fromOrderId(orderId); diff --git a/src/main/java/org/qortal/transaction/CreateGroupTransaction.java b/src/main/java/org/qortal/transaction/CreateGroupTransaction.java index 0d4cbc12..0240bd90 100644 --- a/src/main/java/org/qortal/transaction/CreateGroupTransaction.java +++ b/src/main/java/org/qortal/transaction/CreateGroupTransaction.java @@ -1,6 +1,5 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; @@ -31,38 +30,14 @@ public class CreateGroupTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(getOwner()); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getCreator().getAddress())) - return true; - - if (address.equals(this.getOwner().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getCreator().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.createGroupTransactionData.getOwner()); } // Navigation - public Account getOwner() throws DataException { - return new Account(this.repository, this.createGroupTransactionData.getOwner()); + public Account getOwner() { + return this.getCreator(); } // Processing @@ -70,45 +45,41 @@ public class CreateGroupTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { // Check owner address is valid - if (!Crypto.isValidAddress(createGroupTransactionData.getOwner())) + if (!Crypto.isValidAddress(this.createGroupTransactionData.getOwner())) return ValidationResult.INVALID_ADDRESS; // Check approval threshold is valid - if (createGroupTransactionData.getApprovalThreshold() == null) + if (this.createGroupTransactionData.getApprovalThreshold() == null) return ValidationResult.INVALID_GROUP_APPROVAL_THRESHOLD; // Check min/max block delay values - if (createGroupTransactionData.getMinimumBlockDelay() < 0) + if (this.createGroupTransactionData.getMinimumBlockDelay() < 0) return ValidationResult.INVALID_GROUP_BLOCK_DELAY; - if (createGroupTransactionData.getMaximumBlockDelay() < 1) + if (this.createGroupTransactionData.getMaximumBlockDelay() < 1) return ValidationResult.INVALID_GROUP_BLOCK_DELAY; - if (createGroupTransactionData.getMaximumBlockDelay() < createGroupTransactionData.getMinimumBlockDelay()) + if (this.createGroupTransactionData.getMaximumBlockDelay() < this.createGroupTransactionData.getMinimumBlockDelay()) return ValidationResult.INVALID_GROUP_BLOCK_DELAY; // Check group name size bounds - int groupNameLength = Utf8.encodedLength(createGroupTransactionData.getGroupName()); + int groupNameLength = Utf8.encodedLength(this.createGroupTransactionData.getGroupName()); if (groupNameLength < 1 || groupNameLength > Group.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check description size bounds - int descriptionLength = Utf8.encodedLength(createGroupTransactionData.getDescription()); + int descriptionLength = Utf8.encodedLength(this.createGroupTransactionData.getDescription()); if (descriptionLength < 1 || descriptionLength > Group.MAX_DESCRIPTION_SIZE) return ValidationResult.INVALID_DESCRIPTION_LENGTH; // Check group name is lowercase - if (!createGroupTransactionData.getGroupName().equals(createGroupTransactionData.getGroupName().toLowerCase())) + if (!this.createGroupTransactionData.getGroupName().equals(this.createGroupTransactionData.getGroupName().toLowerCase())) return ValidationResult.NAME_NOT_LOWER_CASE; - // Check fee is positive - if (createGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - Account creator = getCreator(); // Check creator has enough funds - if (creator.getConfirmedBalance(Asset.QORT).compareTo(createGroupTransactionData.getFee()) < 0) + if (creator.getConfirmedBalance(Asset.QORT) < this.createGroupTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -117,7 +88,7 @@ public class CreateGroupTransaction extends Transaction { @Override public ValidationResult isProcessable() throws DataException { // Check the group name isn't already taken - if (this.repository.getGroupRepository().groupExists(createGroupTransactionData.getGroupName())) + if (this.repository.getGroupRepository().groupExists(this.createGroupTransactionData.getGroupName())) return ValidationResult.GROUP_ALREADY_EXISTS; return ValidationResult.OK; @@ -126,27 +97,27 @@ public class CreateGroupTransaction extends Transaction { @Override public void process() throws DataException { // Create Group - Group group = new Group(this.repository, createGroupTransactionData); - group.create(createGroupTransactionData); + Group group = new Group(this.repository, this.createGroupTransactionData); + group.create(this.createGroupTransactionData); // Note newly assigned group ID in our transaction record - createGroupTransactionData.setGroupId(group.getGroupData().getGroupId()); + this.createGroupTransactionData.setGroupId(group.getGroupData().getGroupId()); // Save this transaction with newly assigned group ID - this.repository.getTransactionRepository().save(createGroupTransactionData); + this.repository.getTransactionRepository().save(this.createGroupTransactionData); } @Override public void orphan() throws DataException { // Uncreate group - Group group = new Group(this.repository, createGroupTransactionData.getGroupId()); + Group group = new Group(this.repository, this.createGroupTransactionData.getGroupId()); group.uncreate(); // Remove assigned group ID from transaction record - createGroupTransactionData.setGroupId(null); + this.createGroupTransactionData.setGroupId(null); // Save this transaction with removed group ID - this.repository.getTransactionRepository().save(createGroupTransactionData); + this.repository.getTransactionRepository().save(this.createGroupTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/CreatePollTransaction.java b/src/main/java/org/qortal/transaction/CreatePollTransaction.java index 67c45232..07fddce9 100644 --- a/src/main/java/org/qortal/transaction/CreatePollTransaction.java +++ b/src/main/java/org/qortal/transaction/CreatePollTransaction.java @@ -1,12 +1,10 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.data.transaction.CreatePollTransactionData; @@ -34,42 +32,13 @@ public class CreatePollTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(getOwner()); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getCreator().getAddress())) - return true; - - if (address.equals(this.getOwner().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getCreator().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.createPollTransactionData.getOwner()); } // Navigation - @Override - public PublicKeyAccount getCreator() throws DataException { - return new PublicKeyAccount(this.repository, this.createPollTransactionData.getCreatorPublicKey()); - } - - public Account getOwner() throws DataException { + public Account getOwner() { return new Account(this.repository, this.createPollTransactionData.getOwner()); } @@ -78,33 +47,31 @@ public class CreatePollTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { // Check owner address is valid - if (!Crypto.isValidAddress(createPollTransactionData.getOwner())) + if (!Crypto.isValidAddress(this.createPollTransactionData.getOwner())) return ValidationResult.INVALID_ADDRESS; // Check name size bounds - int pollNameLength = Utf8.encodedLength(createPollTransactionData.getPollName()); + int pollNameLength = Utf8.encodedLength(this.createPollTransactionData.getPollName()); if (pollNameLength < 1 || pollNameLength > Poll.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check description size bounds - int pollDescriptionLength = Utf8.encodedLength(createPollTransactionData.getDescription()); + int pollDescriptionLength = Utf8.encodedLength(this.createPollTransactionData.getDescription()); if (pollDescriptionLength < 1 || pollDescriptionLength > Poll.MAX_DESCRIPTION_SIZE) return ValidationResult.INVALID_DESCRIPTION_LENGTH; // Check poll name is lowercase - if (!createPollTransactionData.getPollName().equals(createPollTransactionData.getPollName().toLowerCase())) + if (!this.createPollTransactionData.getPollName().equals(this.createPollTransactionData.getPollName().toLowerCase())) return ValidationResult.NAME_NOT_LOWER_CASE; - // In gen1 we tested for presence of existing votes but how could there be any if poll doesn't exist? - // Check number of options - List pollOptions = createPollTransactionData.getPollOptions(); + List pollOptions = this.createPollTransactionData.getPollOptions(); int pollOptionsCount = pollOptions.size(); if (pollOptionsCount < 1 || pollOptionsCount > Poll.MAX_OPTIONS) return ValidationResult.INVALID_OPTIONS_COUNT; // Check each option - List optionNames = new ArrayList(); + List optionNames = new ArrayList<>(); for (PollOptionData pollOptionData : pollOptions) { // Check option length int optionNameLength = Utf8.encodedLength(pollOptionData.getOptionName()); @@ -119,15 +86,10 @@ public class CreatePollTransaction extends Transaction { optionNames.add(pollOptionData.getOptionName()); } - // Check fee is positive - if (createPollTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - - // Check reference is correct Account creator = getCreator(); - // Check issuer has enough funds - if (creator.getConfirmedBalance(Asset.QORT).compareTo(createPollTransactionData.getFee()) < 0) + // Check creator has enough funds + if (creator.getConfirmedBalance(Asset.QORT) < this.createPollTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -136,7 +98,7 @@ public class CreatePollTransaction extends Transaction { @Override public ValidationResult isProcessable() throws DataException { // Check the poll name isn't already taken - if (this.repository.getVotingRepository().pollExists(createPollTransactionData.getPollName())) + if (this.repository.getVotingRepository().pollExists(this.createPollTransactionData.getPollName())) return ValidationResult.POLL_ALREADY_EXISTS; return ValidationResult.OK; @@ -145,14 +107,14 @@ public class CreatePollTransaction extends Transaction { @Override public void process() throws DataException { // Publish poll to allow voting - Poll poll = new Poll(this.repository, createPollTransactionData); + Poll poll = new Poll(this.repository, this.createPollTransactionData); poll.publish(); } @Override public void orphan() throws DataException { // Unpublish poll - Poll poll = new Poll(this.repository, createPollTransactionData.getPollName()); + Poll poll = new Poll(this.repository, this.createPollTransactionData.getPollName()); poll.unpublish(); } diff --git a/src/main/java/org/qortal/transaction/DeployAtTransaction.java b/src/main/java/org/qortal/transaction/DeployAtTransaction.java index 715e49cb..d799ff81 100644 --- a/src/main/java/org/qortal/transaction/DeployAtTransaction.java +++ b/src/main/java/org/qortal/transaction/DeployAtTransaction.java @@ -1,10 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.ciyam.at.MachineState; @@ -44,35 +43,8 @@ public class DeployAtTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return new ArrayList<>(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getCreator().getAddress())) - return true; - - if (address.equals(this.getATAccount().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getCreator().getAddress())) - amount = amount.subtract(this.deployATTransactionData.getAmount()).subtract(this.transactionData.getFee()); - - if (address.equals(this.getATAccount().getAddress())) - amount = amount.add(this.deployATTransactionData.getAmount()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.deployATTransactionData.getAtAddress()); } /** Returns AT version from the header bytes */ @@ -124,30 +96,30 @@ public class DeployAtTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { // Check name size bounds - int nameLength = Utf8.encodedLength(deployATTransactionData.getName()); + int nameLength = Utf8.encodedLength(this.deployATTransactionData.getName()); if (nameLength < 1 || nameLength > MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check description size bounds - int descriptionlength = Utf8.encodedLength(deployATTransactionData.getDescription()); + int descriptionlength = Utf8.encodedLength(this.deployATTransactionData.getDescription()); if (descriptionlength < 1 || descriptionlength > MAX_DESCRIPTION_SIZE) return ValidationResult.INVALID_DESCRIPTION_LENGTH; // Check AT-type size bounds - int ATTypeLength = Utf8.encodedLength(deployATTransactionData.getAtType()); - if (ATTypeLength < 1 || ATTypeLength > MAX_AT_TYPE_SIZE) + int atTypeLength = Utf8.encodedLength(this.deployATTransactionData.getAtType()); + if (atTypeLength < 1 || atTypeLength > MAX_AT_TYPE_SIZE) return ValidationResult.INVALID_AT_TYPE_LENGTH; // Check tags size bounds - int tagsLength = Utf8.encodedLength(deployATTransactionData.getTags()); + int tagsLength = Utf8.encodedLength(this.deployATTransactionData.getTags()); if (tagsLength < 1 || tagsLength > MAX_TAGS_SIZE) return ValidationResult.INVALID_TAGS_LENGTH; // Check amount is positive - if (deployATTransactionData.getAmount().compareTo(BigDecimal.ZERO) <= 0) + if (this.deployATTransactionData.getAmount() <= 0) return ValidationResult.NEGATIVE_AMOUNT; - long assetId = deployATTransactionData.getAssetId(); + long assetId = this.deployATTransactionData.getAssetId(); AssetData assetData = this.repository.getAssetRepository().fromAssetId(assetId); // Check asset even exists if (assetData == null) @@ -158,27 +130,23 @@ public class DeployAtTransaction extends Transaction { return ValidationResult.ASSET_NOT_SPENDABLE; // Check asset amount is integer if asset is not divisible - if (!assetData.getIsDivisible() && deployATTransactionData.getAmount().stripTrailingZeros().scale() > 0) + if (!assetData.getIsDivisible() && this.deployATTransactionData.getAmount() % Asset.MULTIPLIER != 0) return ValidationResult.INVALID_AMOUNT; - // Check fee is positive - if (deployATTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - - Account creator = getCreator(); + Account creator = this.getCreator(); // Check creator has enough funds if (assetId == Asset.QORT) { // Simple case: amount and fee both in QORT - BigDecimal minimumBalance = deployATTransactionData.getFee().add(deployATTransactionData.getAmount()); + long minimumBalance = this.deployATTransactionData.getFee() + this.deployATTransactionData.getAmount(); - if (creator.getConfirmedBalance(Asset.QORT).compareTo(minimumBalance) < 0) + if (creator.getConfirmedBalance(Asset.QORT) < minimumBalance) return ValidationResult.NO_BALANCE; } else { - if (creator.getConfirmedBalance(Asset.QORT).compareTo(deployATTransactionData.getFee()) < 0) + if (creator.getConfirmedBalance(Asset.QORT) < this.deployATTransactionData.getFee()) return ValidationResult.NO_BALANCE; - if (creator.getConfirmedBalance(assetId).compareTo(deployATTransactionData.getAmount()) < 0) + if (creator.getConfirmedBalance(assetId) < this.deployATTransactionData.getAmount()) return ValidationResult.NO_BALANCE; } @@ -186,7 +154,7 @@ public class DeployAtTransaction extends Transaction { if (this.getVersion() >= 2) { // Do actual validation try { - new MachineState(deployATTransactionData.getCreationBytes()); + new MachineState(this.deployATTransactionData.getCreationBytes()); } catch (IllegalArgumentException e) { // Not valid return ValidationResult.INVALID_CREATION_BYTES; @@ -201,25 +169,25 @@ public class DeployAtTransaction extends Transaction { @Override public ValidationResult isProcessable() throws DataException { Account creator = getCreator(); - long assetId = deployATTransactionData.getAssetId(); + long assetId = this.deployATTransactionData.getAssetId(); // Check creator has enough funds if (assetId == Asset.QORT) { // Simple case: amount and fee both in QORT - BigDecimal minimumBalance = deployATTransactionData.getFee().add(deployATTransactionData.getAmount()); + long minimumBalance = this.deployATTransactionData.getFee() + this.deployATTransactionData.getAmount(); - if (creator.getConfirmedBalance(Asset.QORT).compareTo(minimumBalance) < 0) + if (creator.getConfirmedBalance(Asset.QORT) < minimumBalance) return ValidationResult.NO_BALANCE; } else { - if (creator.getConfirmedBalance(Asset.QORT).compareTo(deployATTransactionData.getFee()) < 0) + if (creator.getConfirmedBalance(Asset.QORT) < this.deployATTransactionData.getFee()) return ValidationResult.NO_BALANCE; - if (creator.getConfirmedBalance(assetId).compareTo(deployATTransactionData.getAmount()) < 0) + if (creator.getConfirmedBalance(assetId) < this.deployATTransactionData.getAmount()) return ValidationResult.NO_BALANCE; } // Check AT doesn't already exist - if (this.repository.getATRepository().exists(deployATTransactionData.getAtAddress())) + if (this.repository.getATRepository().exists(this.deployATTransactionData.getAtAddress())) return ValidationResult.AT_ALREADY_EXISTS; return ValidationResult.OK; @@ -227,24 +195,24 @@ public class DeployAtTransaction extends Transaction { @Override public void process() throws DataException { - ensureATAddress(); + this.ensureATAddress(); // Deploy AT, saving into repository AT at = new AT(this.repository, this.deployATTransactionData); at.deploy(); - long assetId = deployATTransactionData.getAssetId(); + long assetId = this.deployATTransactionData.getAssetId(); // Update creator's balance regarding initial payment to AT Account creator = getCreator(); - creator.setConfirmedBalance(assetId, creator.getConfirmedBalance(assetId).subtract(deployATTransactionData.getAmount())); + creator.setConfirmedBalance(assetId, creator.getConfirmedBalance(assetId) - this.deployATTransactionData.getAmount()); // Update AT's reference, which also creates AT account Account atAccount = this.getATAccount(); - atAccount.setLastReference(deployATTransactionData.getSignature()); + atAccount.setLastReference(this.deployATTransactionData.getSignature()); // Update AT's balance - atAccount.setConfirmedBalance(assetId, deployATTransactionData.getAmount()); + atAccount.setConfirmedBalance(assetId, this.deployATTransactionData.getAmount()); } @Override @@ -253,11 +221,11 @@ public class DeployAtTransaction extends Transaction { AT at = new AT(this.repository, this.deployATTransactionData); at.undeploy(); - long assetId = deployATTransactionData.getAssetId(); + long assetId = this.deployATTransactionData.getAssetId(); // Update creator's balance regarding initial payment to AT Account creator = getCreator(); - creator.setConfirmedBalance(assetId, creator.getConfirmedBalance(assetId).add(deployATTransactionData.getAmount())); + creator.setConfirmedBalance(assetId, creator.getConfirmedBalance(assetId) + this.deployATTransactionData.getAmount()); // Delete AT's account (and hence its balance) this.repository.getAccountRepository().delete(this.deployATTransactionData.getAtAddress()); diff --git a/src/main/java/org/qortal/transaction/GenesisTransaction.java b/src/main/java/org/qortal/transaction/GenesisTransaction.java index 42ad2f4c..067ff183 100644 --- a/src/main/java/org/qortal/transaction/GenesisTransaction.java +++ b/src/main/java/org/qortal/transaction/GenesisTransaction.java @@ -1,6 +1,5 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -36,40 +35,8 @@ public class GenesisTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(new Account(this.repository, genesisTransactionData.getRecipient())); - } - - /** For Genesis Transactions, do not include transaction creator (which is genesis account) */ - @Override - public List getInvolvedAccounts() throws DataException { - return getRecipientAccounts(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getCreator().getAddress())) - return true; - - if (address.equals(genesisTransactionData.getRecipient())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - // NOTE: genesis transactions have no fee, so no need to test against creator as sender - - if (address.equals(genesisTransactionData.getRecipient())) - amount = amount.add(genesisTransactionData.getAmount()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.genesisTransactionData.getRecipient()); } // Processing @@ -123,11 +90,11 @@ public class GenesisTransaction extends Transaction { @Override public ValidationResult isValid() { // Check amount is zero or positive - if (genesisTransactionData.getAmount().compareTo(BigDecimal.ZERO) < 0) + if (this.genesisTransactionData.getAmount() < 0) return ValidationResult.NEGATIVE_AMOUNT; // Check recipient address is valid - if (!Crypto.isValidAddress(genesisTransactionData.getRecipient())) + if (!Crypto.isValidAddress(this.genesisTransactionData.getRecipient())) return ValidationResult.INVALID_ADDRESS; return ValidationResult.OK; @@ -135,26 +102,26 @@ public class GenesisTransaction extends Transaction { @Override public void process() throws DataException { - Account recipient = new Account(repository, genesisTransactionData.getRecipient()); + Account recipient = new Account(repository, this.genesisTransactionData.getRecipient()); // Update recipient's balance - recipient.setConfirmedBalance(genesisTransactionData.getAssetId(), genesisTransactionData.getAmount()); + recipient.setConfirmedBalance(this.genesisTransactionData.getAssetId(), this.genesisTransactionData.getAmount()); } @Override public void processReferencesAndFees() throws DataException { // Do not attempt to update non-existent genesis account's reference! - Account recipient = new Account(repository, genesisTransactionData.getRecipient()); + Account recipient = new Account(repository, this.genesisTransactionData.getRecipient()); // Set recipient's starting reference (also creates account) - recipient.setLastReference(genesisTransactionData.getSignature()); + recipient.setLastReference(this.genesisTransactionData.getSignature()); } @Override public void orphan() throws DataException { // Delete recipient's account (and balance) - this.repository.getAccountRepository().delete(genesisTransactionData.getRecipient()); + this.repository.getAccountRepository().delete(this.genesisTransactionData.getRecipient()); } @Override diff --git a/src/main/java/org/qortal/transaction/GroupApprovalTransaction.java b/src/main/java/org/qortal/transaction/GroupApprovalTransaction.java index 59bd0c10..d5cf66f7 100644 --- a/src/main/java/org/qortal/transaction/GroupApprovalTransaction.java +++ b/src/main/java/org/qortal/transaction/GroupApprovalTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.transaction.GroupApprovalTransactionData; import org.qortal.data.transaction.TransactionData; @@ -28,35 +26,14 @@ public class GroupApprovalTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { + public List getRecipientAddresses() throws DataException { return Collections.emptyList(); } - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getAdmin().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getAdmin().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; - } - // Navigation - public Account getAdmin() throws DataException { - return new PublicKeyAccount(this.repository, this.groupApprovalTransactionData.getAdminPublicKey()); + public Account getAdmin() { + return this.getCreator(); } // Processing @@ -64,7 +41,7 @@ public class GroupApprovalTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { // Grab pending transaction's data - TransactionData pendingTransactionData = this.repository.getTransactionRepository().fromSignature(groupApprovalTransactionData.getPendingSignature()); + TransactionData pendingTransactionData = this.repository.getTransactionRepository().fromSignature(this.groupApprovalTransactionData.getPendingSignature()); if (pendingTransactionData == null) return ValidationResult.TRANSACTION_UNKNOWN; @@ -82,12 +59,8 @@ public class GroupApprovalTransaction extends Transaction { if (!this.repository.getGroupRepository().adminExists(pendingTransactionData.getTxGroupId(), admin.getAddress())) return ValidationResult.NOT_GROUP_ADMIN; - // Check fee is positive - if (groupApprovalTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check creator has enough funds - if (admin.getConfirmedBalance(Asset.QORT).compareTo(groupApprovalTransactionData.getFee()) < 0) + if (admin.getConfirmedBalance(Asset.QORT) < this.groupApprovalTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -96,20 +69,20 @@ public class GroupApprovalTransaction extends Transaction { @Override public void process() throws DataException { // Find previous approval decision (if any) by this admin for pending transaction - GroupApprovalTransactionData previousApproval = this.repository.getTransactionRepository().getLatestApproval(groupApprovalTransactionData.getPendingSignature(), groupApprovalTransactionData.getAdminPublicKey()); + GroupApprovalTransactionData previousApproval = this.repository.getTransactionRepository().getLatestApproval(this.groupApprovalTransactionData.getPendingSignature(), this.groupApprovalTransactionData.getAdminPublicKey()); if (previousApproval != null) - groupApprovalTransactionData.setPriorReference(previousApproval.getSignature()); + this.groupApprovalTransactionData.setPriorReference(previousApproval.getSignature()); // Save this transaction with updated prior reference to transaction that can help restore state - this.repository.getTransactionRepository().save(groupApprovalTransactionData); + this.repository.getTransactionRepository().save(this.groupApprovalTransactionData); } @Override public void orphan() throws DataException { // Save this transaction with removed prior reference - groupApprovalTransactionData.setPriorReference(null); - this.repository.getTransactionRepository().save(groupApprovalTransactionData); + this.groupApprovalTransactionData.setPriorReference(null); + this.repository.getTransactionRepository().save(this.groupApprovalTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/GroupBanTransaction.java b/src/main/java/org/qortal/transaction/GroupBanTransaction.java index 574a9e77..78e9c3d0 100644 --- a/src/main/java/org/qortal/transaction/GroupBanTransaction.java +++ b/src/main/java/org/qortal/transaction/GroupBanTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.data.group.GroupData; @@ -18,7 +16,9 @@ import org.qortal.repository.Repository; public class GroupBanTransaction extends Transaction { // Properties + private GroupBanTransactionData groupBanTransactionData; + private Account offenderAccount = null; // Constructors @@ -31,53 +31,34 @@ public class GroupBanTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getAdmin().getAddress())) - return true; - - if (address.equals(this.getOffender().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getAdmin().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.groupBanTransactionData.getOffender()); } // Navigation - public Account getAdmin() throws DataException { - return new PublicKeyAccount(this.repository, this.groupBanTransactionData.getAdminPublicKey()); + public Account getAdmin() { + return this.getCreator(); } - public Account getOffender() throws DataException { - return new Account(this.repository, this.groupBanTransactionData.getOffender()); + public Account getOffender() { + if (this.offenderAccount == null) + this.offenderAccount = new Account(this.repository, this.groupBanTransactionData.getOffender()); + + return this.offenderAccount; } // Processing @Override public ValidationResult isValid() throws DataException { + int groupId = this.groupBanTransactionData.getGroupId(); + // Check offender address is valid - if (!Crypto.isValidAddress(groupBanTransactionData.getOffender())) + if (!Crypto.isValidAddress(this.groupBanTransactionData.getOffender())) return ValidationResult.INVALID_ADDRESS; - GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupBanTransactionData.getGroupId()); + GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupId); // Check group exists if (groupData == null) @@ -86,22 +67,17 @@ public class GroupBanTransaction extends Transaction { Account admin = getAdmin(); // Can't ban if not an admin - if (!this.repository.getGroupRepository().adminExists(groupBanTransactionData.getGroupId(), admin.getAddress())) + if (!this.repository.getGroupRepository().adminExists(groupId, admin.getAddress())) return ValidationResult.NOT_GROUP_ADMIN; Account offender = getOffender(); // Can't ban another admin unless the group owner - if (!admin.getAddress().equals(groupData.getOwner()) - && this.repository.getGroupRepository().adminExists(groupBanTransactionData.getGroupId(), offender.getAddress())) + if (!admin.getAddress().equals(groupData.getOwner()) && this.repository.getGroupRepository().adminExists(groupId, offender.getAddress())) return ValidationResult.INVALID_GROUP_OWNER; - // Check fee is positive - if (groupBanTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check admin has enough funds - if (admin.getConfirmedBalance(Asset.QORT).compareTo(groupBanTransactionData.getFee()) < 0) + if (admin.getConfirmedBalance(Asset.QORT) < this.groupBanTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -110,21 +86,21 @@ public class GroupBanTransaction extends Transaction { @Override public void process() throws DataException { // Update Group Membership - Group group = new Group(this.repository, groupBanTransactionData.getGroupId()); - group.ban(groupBanTransactionData); + Group group = new Group(this.repository, this.groupBanTransactionData.getGroupId()); + group.ban(this.groupBanTransactionData); // Save this transaction with updated member/admin references to transactions that can help restore state - this.repository.getTransactionRepository().save(groupBanTransactionData); + this.repository.getTransactionRepository().save(this.groupBanTransactionData); } @Override public void orphan() throws DataException { // Revert group membership - Group group = new Group(this.repository, groupBanTransactionData.getGroupId()); - group.unban(groupBanTransactionData); + Group group = new Group(this.repository, this.groupBanTransactionData.getGroupId()); + group.unban(this.groupBanTransactionData); // Save this transaction with removed member/admin references - this.repository.getTransactionRepository().save(groupBanTransactionData); + this.repository.getTransactionRepository().save(this.groupBanTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/GroupInviteTransaction.java b/src/main/java/org/qortal/transaction/GroupInviteTransaction.java index 7599c637..a66f7584 100644 --- a/src/main/java/org/qortal/transaction/GroupInviteTransaction.java +++ b/src/main/java/org/qortal/transaction/GroupInviteTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.data.transaction.GroupInviteTransactionData; @@ -17,7 +15,9 @@ import org.qortal.repository.Repository; public class GroupInviteTransaction extends Transaction { // Properties + private GroupInviteTransactionData groupInviteTransactionData; + private Account inviteeAccount = null; // Constructors @@ -30,56 +30,35 @@ public class GroupInviteTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getAdmin().getAddress())) - return true; - - if (address.equals(this.getInvitee().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getAdmin().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.groupInviteTransactionData.getInvitee()); } // Navigation - public Account getAdmin() throws DataException { - return new PublicKeyAccount(this.repository, this.groupInviteTransactionData.getAdminPublicKey()); + public Account getAdmin() { + return this.getCreator(); } - public Account getInvitee() throws DataException { - return new Account(this.repository, this.groupInviteTransactionData.getInvitee()); + public Account getInvitee() { + if (this.inviteeAccount == null) + this.inviteeAccount = new Account(this.repository, this.groupInviteTransactionData.getInvitee()); + + return this.inviteeAccount; } // Processing @Override public ValidationResult isValid() throws DataException { - int groupId = groupInviteTransactionData.getGroupId(); + int groupId = this.groupInviteTransactionData.getGroupId(); // Check time to live zero (infinite) or positive - if (groupInviteTransactionData.getTimeToLive() < 0) + if (this.groupInviteTransactionData.getTimeToLive() < 0) return ValidationResult.INVALID_LIFETIME; // Check member address is valid - if (!Crypto.isValidAddress(groupInviteTransactionData.getInvitee())) + if (!Crypto.isValidAddress(this.groupInviteTransactionData.getInvitee())) return ValidationResult.INVALID_ADDRESS; // Check group exists @@ -102,12 +81,8 @@ public class GroupInviteTransaction extends Transaction { if (this.repository.getGroupRepository().banExists(groupId, invitee.getAddress())) return ValidationResult.BANNED_FROM_GROUP; - // Check fee is positive - if (groupInviteTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check creator has enough funds - if (admin.getConfirmedBalance(Asset.QORT).compareTo(groupInviteTransactionData.getFee()) < 0) + if (admin.getConfirmedBalance(Asset.QORT) < this.groupInviteTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -116,21 +91,21 @@ public class GroupInviteTransaction extends Transaction { @Override public void process() throws DataException { // Update Group Membership - Group group = new Group(this.repository, groupInviteTransactionData.getGroupId()); - group.invite(groupInviteTransactionData); + Group group = new Group(this.repository, this.groupInviteTransactionData.getGroupId()); + group.invite(this.groupInviteTransactionData); // Save this transaction with updated member/admin references to transactions that can help restore state - this.repository.getTransactionRepository().save(groupInviteTransactionData); + this.repository.getTransactionRepository().save(this.groupInviteTransactionData); } @Override public void orphan() throws DataException { // Revert group membership - Group group = new Group(this.repository, groupInviteTransactionData.getGroupId()); - group.uninvite(groupInviteTransactionData); + Group group = new Group(this.repository, this.groupInviteTransactionData.getGroupId()); + group.uninvite(this.groupInviteTransactionData); // Save this transaction with removed member/admin references - this.repository.getTransactionRepository().save(groupInviteTransactionData); + this.repository.getTransactionRepository().save(this.groupInviteTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/GroupKickTransaction.java b/src/main/java/org/qortal/transaction/GroupKickTransaction.java index b26f173e..86c9aaef 100644 --- a/src/main/java/org/qortal/transaction/GroupKickTransaction.java +++ b/src/main/java/org/qortal/transaction/GroupKickTransaction.java @@ -1,6 +1,5 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; @@ -32,32 +31,8 @@ public class GroupKickTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getAdmin().getAddress())) - return true; - - if (address.equals(this.getMember().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getAdmin().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.groupKickTransactionData.getMember()); } // Navigation @@ -74,12 +49,13 @@ public class GroupKickTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { + int groupId = this.groupKickTransactionData.getGroupId(); + // Check member address is valid - if (!Crypto.isValidAddress(groupKickTransactionData.getMember())) + if (!Crypto.isValidAddress(this.groupKickTransactionData.getMember())) return ValidationResult.INVALID_ADDRESS; GroupRepository groupRepository = this.repository.getGroupRepository(); - int groupId = groupKickTransactionData.getGroupId(); GroupData groupData = groupRepository.fromGroupId(groupId); // Check group exists @@ -102,12 +78,8 @@ public class GroupKickTransaction extends Transaction { if (!admin.getAddress().equals(groupData.getOwner()) && groupRepository.adminExists(groupId, member.getAddress())) return ValidationResult.INVALID_GROUP_OWNER; - // Check fee is positive - if (groupKickTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check creator has enough funds - if (admin.getConfirmedBalance(Asset.QORT).compareTo(groupKickTransactionData.getFee()) < 0) + if (admin.getConfirmedBalance(Asset.QORT) < this.groupKickTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -116,21 +88,21 @@ public class GroupKickTransaction extends Transaction { @Override public void process() throws DataException { // Update Group Membership - Group group = new Group(this.repository, groupKickTransactionData.getGroupId()); - group.kick(groupKickTransactionData); + Group group = new Group(this.repository, this.groupKickTransactionData.getGroupId()); + group.kick(this.groupKickTransactionData); // Save this transaction with updated member/admin references to transactions that can help restore state - this.repository.getTransactionRepository().save(groupKickTransactionData); + this.repository.getTransactionRepository().save(this.groupKickTransactionData); } @Override public void orphan() throws DataException { // Revert group membership - Group group = new Group(this.repository, groupKickTransactionData.getGroupId()); - group.unkick(groupKickTransactionData); + Group group = new Group(this.repository, this.groupKickTransactionData.getGroupId()); + group.unkick(this.groupKickTransactionData); // Save this transaction with removed member/admin references - this.repository.getTransactionRepository().save(groupKickTransactionData); + this.repository.getTransactionRepository().save(this.groupKickTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/IssueAssetTransaction.java b/src/main/java/org/qortal/transaction/IssueAssetTransaction.java index db66ff0b..ff088912 100644 --- a/src/main/java/org/qortal/transaction/IssueAssetTransaction.java +++ b/src/main/java/org/qortal/transaction/IssueAssetTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.data.transaction.IssueAssetTransactionData; @@ -18,7 +16,9 @@ import com.google.common.base.Utf8; public class IssueAssetTransaction extends Transaction { // Properties + private IssueAssetTransactionData issueAssetTransactionData; + private Account ownerAccount = null; // Constructors @@ -31,82 +31,59 @@ public class IssueAssetTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(getOwner()); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getIssuer().getAddress())) - return true; - - if (address.equals(this.getOwner().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getIssuer().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - // NOTE: we're only interested in QORT amounts, and genesis account issued QORT so no need to check owner - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.issueAssetTransactionData.getOwner()); } // Navigation - public Account getIssuer() throws DataException { - return new PublicKeyAccount(this.repository, this.issueAssetTransactionData.getIssuerPublicKey()); + public Account getIssuer() { + return this.getCreator(); } - public Account getOwner() throws DataException { - return new Account(this.repository, this.issueAssetTransactionData.getOwner()); + public Account getOwner() { + if (this.ownerAccount == null) + this.ownerAccount = new Account(this.repository, this.issueAssetTransactionData.getOwner()); + + return this.ownerAccount; } // Processing @Override public ValidationResult isValid() throws DataException { + // Check owner address is valid + if (!Crypto.isValidAddress(this.issueAssetTransactionData.getOwner())) + return ValidationResult.INVALID_ADDRESS; + + // Check name size bounds + int assetNameLength = Utf8.encodedLength(this.issueAssetTransactionData.getAssetName()); + if (assetNameLength < 1 || assetNameLength > Asset.MAX_NAME_SIZE) + return ValidationResult.INVALID_NAME_LENGTH; + + // Check description size bounds + int assetDescriptionlength = Utf8.encodedLength(this.issueAssetTransactionData.getDescription()); + if (assetDescriptionlength < 1 || assetDescriptionlength > Asset.MAX_DESCRIPTION_SIZE) + return ValidationResult.INVALID_DESCRIPTION_LENGTH; + // Check data field String data = this.issueAssetTransactionData.getData(); int dataLength = Utf8.encodedLength(data); if (data == null || dataLength < 1 || dataLength > Asset.MAX_DATA_SIZE) return ValidationResult.INVALID_DATA_LENGTH; - // Check owner address is valid - if (!Crypto.isValidAddress(issueAssetTransactionData.getOwner())) - return ValidationResult.INVALID_ADDRESS; - - // Check name size bounds - int assetNameLength = Utf8.encodedLength(issueAssetTransactionData.getAssetName()); - if (assetNameLength < 1 || assetNameLength > Asset.MAX_NAME_SIZE) - return ValidationResult.INVALID_NAME_LENGTH; - - // Check description size bounds - int assetDescriptionlength = Utf8.encodedLength(issueAssetTransactionData.getDescription()); - if (assetDescriptionlength < 1 || assetDescriptionlength > Asset.MAX_DESCRIPTION_SIZE) - return ValidationResult.INVALID_DESCRIPTION_LENGTH; - // Check quantity - if (issueAssetTransactionData.getQuantity() < 1 || issueAssetTransactionData.getQuantity() > Asset.MAX_QUANTITY) + if (this.issueAssetTransactionData.getQuantity() < 1 || this.issueAssetTransactionData.getQuantity() > Asset.MAX_QUANTITY) return ValidationResult.INVALID_QUANTITY; - // Check fee is positive - if (issueAssetTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; + // Check quantity versus indivisibility + if (!this.issueAssetTransactionData.getIsDivisible() && this.issueAssetTransactionData.getQuantity() % Asset.MULTIPLIER != 0) + return ValidationResult.INVALID_QUANTITY; Account issuer = getIssuer(); // Check issuer has enough funds - if (issuer.getConfirmedBalance(Asset.QORT).compareTo(issueAssetTransactionData.getFee()) < 0) + if (issuer.getConfirmedBalance(Asset.QORT) < this.issueAssetTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -115,7 +92,7 @@ public class IssueAssetTransaction extends Transaction { @Override public ValidationResult isProcessable() throws DataException { // Check the asset name isn't already taken. - if (this.repository.getAssetRepository().assetExists(issueAssetTransactionData.getAssetName())) + if (this.repository.getAssetRepository().assetExists(this.issueAssetTransactionData.getAssetName())) return ValidationResult.ASSET_ALREADY_EXISTS; return ValidationResult.OK; @@ -124,35 +101,35 @@ public class IssueAssetTransaction extends Transaction { @Override public void process() throws DataException { // Issue asset - Asset asset = new Asset(this.repository, issueAssetTransactionData); + Asset asset = new Asset(this.repository, this.issueAssetTransactionData); asset.issue(); // Add asset to owner Account owner = getOwner(); - owner.setConfirmedBalance(asset.getAssetData().getAssetId(), BigDecimal.valueOf(issueAssetTransactionData.getQuantity()).setScale(8)); + owner.setConfirmedBalance(asset.getAssetData().getAssetId(), this.issueAssetTransactionData.getQuantity()); // Note newly assigned asset ID in our transaction record - issueAssetTransactionData.setAssetId(asset.getAssetData().getAssetId()); + this.issueAssetTransactionData.setAssetId(asset.getAssetData().getAssetId()); // Save this transaction with newly assigned assetId - this.repository.getTransactionRepository().save(issueAssetTransactionData); + this.repository.getTransactionRepository().save(this.issueAssetTransactionData); } @Override public void orphan() throws DataException { // Remove asset from owner Account owner = getOwner(); - owner.deleteBalance(issueAssetTransactionData.getAssetId()); + owner.deleteBalance(this.issueAssetTransactionData.getAssetId()); // Deissue asset - Asset asset = new Asset(this.repository, issueAssetTransactionData.getAssetId()); + Asset asset = new Asset(this.repository, this.issueAssetTransactionData.getAssetId()); asset.deissue(); // Remove assigned asset ID from transaction info - issueAssetTransactionData.setAssetId(null); + this.issueAssetTransactionData.setAssetId(null); // Save this transaction, with removed assetId - this.repository.getTransactionRepository().save(issueAssetTransactionData); + this.repository.getTransactionRepository().save(this.issueAssetTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/JoinGroupTransaction.java b/src/main/java/org/qortal/transaction/JoinGroupTransaction.java index 96573201..ed69ed4e 100644 --- a/src/main/java/org/qortal/transaction/JoinGroupTransaction.java +++ b/src/main/java/org/qortal/transaction/JoinGroupTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.transaction.JoinGroupTransactionData; import org.qortal.data.transaction.TransactionData; @@ -29,42 +27,21 @@ public class JoinGroupTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { + public List getRecipientAddresses() throws DataException { return Collections.emptyList(); } - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getJoiner().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getJoiner().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; - } - // Navigation - public Account getJoiner() throws DataException { - return new PublicKeyAccount(this.repository, this.joinGroupTransactionData.getJoinerPublicKey()); + public Account getJoiner() { + return this.getCreator(); } // Processing @Override public ValidationResult isValid() throws DataException { - int groupId = joinGroupTransactionData.getGroupId(); + int groupId = this.joinGroupTransactionData.getGroupId(); // Check group exists if (!this.repository.getGroupRepository().groupExists(groupId)) @@ -83,11 +60,8 @@ public class JoinGroupTransaction extends Transaction { if (this.repository.getGroupRepository().joinRequestExists(groupId, joiner.getAddress())) return ValidationResult.JOIN_REQUEST_EXISTS; - // Check fee is positive - if (joinGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check creator has enough funds - if (joiner.getConfirmedBalance(Asset.QORT).compareTo(joinGroupTransactionData.getFee()) < 0) + // Check joiner has enough funds + if (joiner.getConfirmedBalance(Asset.QORT) < this.joinGroupTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -96,21 +70,21 @@ public class JoinGroupTransaction extends Transaction { @Override public void process() throws DataException { // Update Group Membership - Group group = new Group(this.repository, joinGroupTransactionData.getGroupId()); - group.join(joinGroupTransactionData); + Group group = new Group(this.repository, this.joinGroupTransactionData.getGroupId()); + group.join(this.joinGroupTransactionData); // Save this transaction with cached references to transactions that can help restore state - this.repository.getTransactionRepository().save(joinGroupTransactionData); + this.repository.getTransactionRepository().save(this.joinGroupTransactionData); } @Override public void orphan() throws DataException { // Revert group membership - Group group = new Group(this.repository, joinGroupTransactionData.getGroupId()); - group.unjoin(joinGroupTransactionData); + Group group = new Group(this.repository, this.joinGroupTransactionData.getGroupId()); + group.unjoin(this.joinGroupTransactionData); // Save this transaction with removed references - this.repository.getTransactionRepository().save(joinGroupTransactionData); + this.repository.getTransactionRepository().save(this.joinGroupTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/LeaveGroupTransaction.java b/src/main/java/org/qortal/transaction/LeaveGroupTransaction.java index 6b361009..ad31e565 100644 --- a/src/main/java/org/qortal/transaction/LeaveGroupTransaction.java +++ b/src/main/java/org/qortal/transaction/LeaveGroupTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.group.GroupData; import org.qortal.data.transaction.LeaveGroupTransactionData; @@ -30,42 +28,23 @@ public class LeaveGroupTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { + public List getRecipientAddresses() throws DataException { return Collections.emptyList(); } - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getLeaver().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getLeaver().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; - } - // Navigation - public Account getLeaver() throws DataException { - return new PublicKeyAccount(this.repository, this.leaveGroupTransactionData.getLeaverPublicKey()); + public Account getLeaver() { + return this.getCreator(); } // Processing @Override public ValidationResult isValid() throws DataException { - GroupData groupData = this.repository.getGroupRepository().fromGroupId(leaveGroupTransactionData.getGroupId()); + int groupId = this.leaveGroupTransactionData.getGroupId(); + + GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupId); // Check group exists if (groupData == null) @@ -78,15 +57,11 @@ public class LeaveGroupTransaction extends Transaction { return ValidationResult.GROUP_OWNER_CANNOT_LEAVE; // Check leaver is actually a member of group - if (!this.repository.getGroupRepository().memberExists(leaveGroupTransactionData.getGroupId(), leaver.getAddress())) + if (!this.repository.getGroupRepository().memberExists(groupId, leaver.getAddress())) return ValidationResult.NOT_GROUP_MEMBER; - // Check fee is positive - if (leaveGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - - // Check creator has enough funds - if (leaver.getConfirmedBalance(Asset.QORT).compareTo(leaveGroupTransactionData.getFee()) < 0) + // Check leaver has enough funds + if (leaver.getConfirmedBalance(Asset.QORT) < this.leaveGroupTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -95,21 +70,21 @@ public class LeaveGroupTransaction extends Transaction { @Override public void process() throws DataException { // Update Group Membership - Group group = new Group(this.repository, leaveGroupTransactionData.getGroupId()); - group.leave(leaveGroupTransactionData); + Group group = new Group(this.repository, this.leaveGroupTransactionData.getGroupId()); + group.leave(this.leaveGroupTransactionData); // Save this transaction with updated member/admin references to transactions that can help restore state - this.repository.getTransactionRepository().save(leaveGroupTransactionData); + this.repository.getTransactionRepository().save(this.leaveGroupTransactionData); } @Override public void orphan() throws DataException { // Revert group membership - Group group = new Group(this.repository, leaveGroupTransactionData.getGroupId()); - group.unleave(leaveGroupTransactionData); + Group group = new Group(this.repository, this.leaveGroupTransactionData.getGroupId()); + group.unleave(this.leaveGroupTransactionData); // Save this transaction with removed member/admin references - this.repository.getTransactionRepository().save(leaveGroupTransactionData); + this.repository.getTransactionRepository().save(this.leaveGroupTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/MessageTransaction.java b/src/main/java/org/qortal/transaction/MessageTransaction.java index f01fe75e..3390ab3a 100644 --- a/src/main/java/org/qortal/transaction/MessageTransaction.java +++ b/src/main/java/org/qortal/transaction/MessageTransaction.java @@ -1,12 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; -import org.qortal.asset.Asset; import org.qortal.data.PaymentData; import org.qortal.data.transaction.MessageTransactionData; import org.qortal.data.transaction.TransactionData; @@ -17,10 +14,13 @@ import org.qortal.repository.Repository; public class MessageTransaction extends Transaction { // Properties + private MessageTransactionData messageTransactionData; + private PaymentData paymentData = null; // Other useful constants public static final int MAX_DATA_SIZE = 4000; + private static final boolean isZeroAmountValid = true; // Constructors @@ -33,109 +33,71 @@ public class MessageTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(new Account(this.repository, messageTransactionData.getRecipient())); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getSender().getAddress())) - return true; - - if (address.equals(messageTransactionData.getRecipient())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - String senderAddress = this.getSender().getAddress(); - - if (address.equals(senderAddress)) - amount = amount.subtract(this.transactionData.getFee()); - - // We're only interested in QORT - if (messageTransactionData.getAssetId() == Asset.QORT) { - if (address.equals(messageTransactionData.getRecipient())) - amount = amount.add(messageTransactionData.getAmount()); - else if (address.equals(senderAddress)) - amount = amount.subtract(messageTransactionData.getAmount()); - } - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.messageTransactionData.getRecipient()); } // Navigation - public Account getSender() throws DataException { - return new PublicKeyAccount(this.repository, this.messageTransactionData.getSenderPublicKey()); + public Account getSender() { + return this.getCreator(); } - public Account getRecipient() throws DataException { + public Account getRecipient() { return new Account(this.repository, this.messageTransactionData.getRecipient()); } // Processing private PaymentData getPaymentData() { - return new PaymentData(messageTransactionData.getRecipient(), messageTransactionData.getAssetId(), messageTransactionData.getAmount()); + if (this.paymentData == null) + this.paymentData = new PaymentData(this.messageTransactionData.getRecipient(), this.messageTransactionData.getAssetId(), this.messageTransactionData.getAmount()); + + return this.paymentData; } @Override public ValidationResult isValid() throws DataException { // Check data length - if (messageTransactionData.getData().length < 1 || messageTransactionData.getData().length > MAX_DATA_SIZE) + if (this.messageTransactionData.getData().length < 1 || this.messageTransactionData.getData().length > MAX_DATA_SIZE) return ValidationResult.INVALID_DATA_LENGTH; - // Zero-amount payments (i.e. message-only) only valid for versions later than 1 - boolean isZeroAmountValid = messageTransactionData.getVersion() > 1; - // Wrap and delegate final payment checks to Payment class - return new Payment(this.repository).isValid(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(), + return new Payment(this.repository).isValid(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(), isZeroAmountValid); } @Override public ValidationResult isProcessable() throws DataException { - // Zero-amount payments (i.e. message-only) only valid for versions later than 1 - boolean isZeroAmountValid = messageTransactionData.getVersion() > 1; - // Wrap and delegate final processable checks to Payment class - return new Payment(this.repository).isProcessable(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(), + return new Payment(this.repository).isProcessable(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(), isZeroAmountValid); } @Override public void process() throws DataException { // Wrap and delegate payment processing to Payment class. - new Payment(this.repository).process(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(), - messageTransactionData.getSignature()); + new Payment(this.repository).process(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getSignature()); } @Override public void processReferencesAndFees() throws DataException { // Wrap and delegate references processing to Payment class. Only update recipient's last reference if transferring QORT. - new Payment(this.repository).processReferencesAndFees(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(), - messageTransactionData.getSignature(), false); + new Payment(this.repository).processReferencesAndFees(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(), + this.messageTransactionData.getSignature(), false); } @Override public void orphan() throws DataException { // Wrap and delegate payment processing to Payment class. - new Payment(this.repository).orphan(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(), - messageTransactionData.getSignature(), messageTransactionData.getReference()); + new Payment(this.repository).orphan(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getSignature(), this.messageTransactionData.getReference()); } @Override public void orphanReferencesAndFees() throws DataException { // Wrap and delegate references processing to Payment class. Only revert recipient's last reference if transferring QORT. - new Payment(this.repository).orphanReferencesAndFees(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(), - messageTransactionData.getSignature(), messageTransactionData.getReference(), false); + new Payment(this.repository).orphanReferencesAndFees(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(), + this.messageTransactionData.getSignature(), this.messageTransactionData.getReference(), false); } } diff --git a/src/main/java/org/qortal/transaction/MultiPaymentTransaction.java b/src/main/java/org/qortal/transaction/MultiPaymentTransaction.java index f3abcac3..bdc73575 100644 --- a/src/main/java/org/qortal/transaction/MultiPaymentTransaction.java +++ b/src/main/java/org/qortal/transaction/MultiPaymentTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; -import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.PaymentData; import org.qortal.data.transaction.MultiPaymentTransactionData; @@ -33,109 +31,67 @@ public class MultiPaymentTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - List recipients = new ArrayList<>(); - - for (PaymentData paymentData : multiPaymentTransactionData.getPayments()) - recipients.add(new Account(this.repository, paymentData.getRecipient())); - - return recipients; - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getSender().getAddress())) - return true; - - for (PaymentData paymentData : multiPaymentTransactionData.getPayments()) - if (address.equals(paymentData.getRecipient())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - String senderAddress = this.getSender().getAddress(); - - if (address.equals(senderAddress)) - amount = amount.subtract(this.transactionData.getFee()); - - // We're only interested in QORT - for (PaymentData paymentData : multiPaymentTransactionData.getPayments()) - if (paymentData.getAssetId() == Asset.QORT) { - if (address.equals(paymentData.getRecipient())) - amount = amount.add(paymentData.getAmount()); - else if (address.equals(senderAddress)) - amount = amount.subtract(paymentData.getAmount()); - } - - return amount; + public List getRecipientAddresses() throws DataException { + return this.multiPaymentTransactionData.getPayments().stream().map(PaymentData::getRecipient).collect(Collectors.toList()); } // Navigation - public Account getSender() throws DataException { - return new PublicKeyAccount(this.repository, this.multiPaymentTransactionData.getSenderPublicKey()); + public Account getSender() { + return this.getCreator(); } // Processing @Override public ValidationResult isValid() throws DataException { - List payments = multiPaymentTransactionData.getPayments(); + List payments = this.multiPaymentTransactionData.getPayments(); // Check number of payments if (payments.isEmpty() || payments.size() > MAX_PAYMENTS_COUNT) return ValidationResult.INVALID_PAYMENTS_COUNT; - // Check reference is correct Account sender = getSender(); // Check sender has enough funds for fee - if (sender.getConfirmedBalance(Asset.QORT).compareTo(multiPaymentTransactionData.getFee()) < 0) + if (sender.getConfirmedBalance(Asset.QORT) > this.multiPaymentTransactionData.getFee()) return ValidationResult.NO_BALANCE; - return new Payment(this.repository).isValid(multiPaymentTransactionData.getSenderPublicKey(), payments, multiPaymentTransactionData.getFee()); + return new Payment(this.repository).isValid(this.multiPaymentTransactionData.getSenderPublicKey(), payments, this.multiPaymentTransactionData.getFee()); } @Override public ValidationResult isProcessable() throws DataException { - List payments = multiPaymentTransactionData.getPayments(); + List payments = this.multiPaymentTransactionData.getPayments(); - return new Payment(this.repository).isProcessable(multiPaymentTransactionData.getSenderPublicKey(), payments, multiPaymentTransactionData.getFee()); + return new Payment(this.repository).isProcessable(this.multiPaymentTransactionData.getSenderPublicKey(), payments, this.multiPaymentTransactionData.getFee()); } @Override public void process() throws DataException { // Wrap and delegate payment processing to Payment class. - new Payment(this.repository).process(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(), - multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature()); + new Payment(this.repository).process(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments(), this.multiPaymentTransactionData.getSignature()); } @Override public void processReferencesAndFees() throws DataException { // Wrap and delegate reference processing to Payment class. Always update recipients' last references regardless of asset. - new Payment(this.repository).processReferencesAndFees(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(), - multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), true); + new Payment(this.repository).processReferencesAndFees(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments(), + this.multiPaymentTransactionData.getFee(), this.multiPaymentTransactionData.getSignature(), true); } @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(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(), - multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), multiPaymentTransactionData.getReference()); + new Payment(this.repository).orphan(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments(), + this.multiPaymentTransactionData.getSignature(), this.multiPaymentTransactionData.getReference()); } @Override public void orphanReferencesAndFees() throws DataException { // Wrap and delegate reference processing to Payment class. Always revert recipients' last references regardless of asset. - new Payment(this.repository).orphanReferencesAndFees(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(), - multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), multiPaymentTransactionData.getReference(), true); + new Payment(this.repository).orphanReferencesAndFees(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments(), + this.multiPaymentTransactionData.getFee(), this.multiPaymentTransactionData.getSignature(), this.multiPaymentTransactionData.getReference(), true); } } diff --git a/src/main/java/org/qortal/transaction/PaymentTransaction.java b/src/main/java/org/qortal/transaction/PaymentTransaction.java index 1dbbe337..9372f281 100644 --- a/src/main/java/org/qortal/transaction/PaymentTransaction.java +++ b/src/main/java/org/qortal/transaction/PaymentTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.PaymentData; import org.qortal.data.transaction.PaymentTransactionData; @@ -17,7 +15,9 @@ import org.qortal.repository.Repository; public class PaymentTransaction extends Transaction { // Properties + private PaymentTransactionData paymentTransactionData; + private PaymentData paymentData = null; // Constructors @@ -30,88 +30,62 @@ public class PaymentTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(new Account(this.repository, paymentTransactionData.getRecipient())); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getSender().getAddress())) - return true; - - if (address.equals(paymentTransactionData.getRecipient())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - String senderAddress = this.getSender().getAddress(); - - if (address.equals(senderAddress)) - amount = amount.subtract(this.transactionData.getFee()).subtract(paymentTransactionData.getAmount()); - - if (address.equals(paymentTransactionData.getRecipient())) - amount = amount.add(paymentTransactionData.getAmount()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.paymentTransactionData.getRecipient()); } // Navigation - public Account getSender() throws DataException { - return new PublicKeyAccount(this.repository, this.paymentTransactionData.getSenderPublicKey()); + public Account getSender() { + return this.getCreator(); } // Processing private PaymentData getPaymentData() { - return new PaymentData(paymentTransactionData.getRecipient(), Asset.QORT, paymentTransactionData.getAmount()); + if (this.paymentData == null) + this.paymentData = new PaymentData(this.paymentTransactionData.getRecipient(), Asset.QORT, this.paymentTransactionData.getAmount()); + + return this.paymentData; } @Override public ValidationResult isValid() throws DataException { // Wrap and delegate final payment checks to Payment class - return new Payment(this.repository).isValid(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee()); + return new Payment(this.repository).isValid(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), this.paymentTransactionData.getFee()); } @Override public ValidationResult isProcessable() throws DataException { // Wrap and delegate final processable checks to Payment class - return new Payment(this.repository).isProcessable(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee()); + return new Payment(this.repository).isProcessable(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), this.paymentTransactionData.getFee()); } @Override public void process() throws DataException { // Wrap and delegate payment processing to Payment class. - new Payment(this.repository).process(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(), - paymentTransactionData.getSignature()); + new Payment(this.repository).process(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), this.paymentTransactionData.getSignature()); } @Override public void processReferencesAndFees() throws DataException { // Wrap and delegate references processing to Payment class. Only update recipient's last reference if transferring QORT. - new Payment(this.repository).processReferencesAndFees(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(), - paymentTransactionData.getSignature(), false); + new Payment(this.repository).processReferencesAndFees(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), this.paymentTransactionData.getFee(), + this.paymentTransactionData.getSignature(), false); } @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(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(), - paymentTransactionData.getSignature(), paymentTransactionData.getReference()); + new Payment(this.repository).orphan(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), + this.paymentTransactionData.getSignature(), this.paymentTransactionData.getReference()); } @Override public void orphanReferencesAndFees() throws DataException { // Wrap and delegate payment processing to Payment class. Only revert recipient's last reference if transferring QORT. - new Payment(this.repository).orphanReferencesAndFees(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(), - paymentTransactionData.getSignature(), paymentTransactionData.getReference(), false); + new Payment(this.repository).orphanReferencesAndFees(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), this.paymentTransactionData.getFee(), + this.paymentTransactionData.getSignature(), this.paymentTransactionData.getReference(), false); } } diff --git a/src/main/java/org/qortal/transaction/RegisterNameTransaction.java b/src/main/java/org/qortal/transaction/RegisterNameTransaction.java index 3e358e0f..053413eb 100644 --- a/src/main/java/org/qortal/transaction/RegisterNameTransaction.java +++ b/src/main/java/org/qortal/transaction/RegisterNameTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.block.BlockChain; import org.qortal.crypto.Crypto; @@ -33,42 +31,14 @@ public class RegisterNameTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(getOwner()); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getRegistrant().getAddress())) - return true; - - if (address.equals(this.getOwner().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getRegistrant().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.registerNameTransactionData.getOwner()); } // Navigation - public Account getRegistrant() throws DataException { - return new PublicKeyAccount(this.repository, this.registerNameTransactionData.getRegistrantPublicKey()); - } - - public Account getOwner() throws DataException { - return new Account(this.repository, this.registerNameTransactionData.getOwner()); + public Account getRegistrant() { + return this.getCreator(); } // Processing @@ -78,29 +48,25 @@ public class RegisterNameTransaction extends Transaction { Account registrant = getRegistrant(); // Check owner address is valid - if (!Crypto.isValidAddress(registerNameTransactionData.getOwner())) + if (!Crypto.isValidAddress(this.registerNameTransactionData.getOwner())) return ValidationResult.INVALID_ADDRESS; // Check name size bounds - int nameLength = Utf8.encodedLength(registerNameTransactionData.getName()); + int nameLength = Utf8.encodedLength(this.registerNameTransactionData.getName()); if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check data size bounds - int dataLength = Utf8.encodedLength(registerNameTransactionData.getData()); + int dataLength = Utf8.encodedLength(this.registerNameTransactionData.getData()); if (dataLength < 1 || dataLength > Name.MAX_DATA_SIZE) return ValidationResult.INVALID_DATA_LENGTH; // Check name is lowercase - if (!registerNameTransactionData.getName().equals(registerNameTransactionData.getName().toLowerCase())) + if (!this.registerNameTransactionData.getName().equals(this.registerNameTransactionData.getName().toLowerCase())) return ValidationResult.NAME_NOT_LOWER_CASE; - // Check fee is positive - if (registerNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - - // Check issuer has enough funds - if (registrant.getConfirmedBalance(Asset.QORT).compareTo(registerNameTransactionData.getFee()) < 0) + // Check registrant has enough funds + if (registrant.getConfirmedBalance(Asset.QORT) < this.registerNameTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -109,7 +75,7 @@ public class RegisterNameTransaction extends Transaction { @Override public ValidationResult isProcessable() throws DataException { // Check the name isn't already taken - if (this.repository.getNameRepository().nameExists(registerNameTransactionData.getName())) + if (this.repository.getNameRepository().nameExists(this.registerNameTransactionData.getName())) return ValidationResult.NAME_ALREADY_REGISTERED; Account registrant = getRegistrant(); @@ -124,14 +90,14 @@ public class RegisterNameTransaction extends Transaction { @Override public void process() throws DataException { // Register Name - Name name = new Name(this.repository, registerNameTransactionData); + Name name = new Name(this.repository, this.registerNameTransactionData); name.register(); } @Override public void orphan() throws DataException { // Unregister name - Name name = new Name(this.repository, registerNameTransactionData.getName()); + Name name = new Name(this.repository, this.registerNameTransactionData.getName()); name.unregister(); } diff --git a/src/main/java/org/qortal/transaction/RemoveGroupAdminTransaction.java b/src/main/java/org/qortal/transaction/RemoveGroupAdminTransaction.java index 6d50f61a..b8cb5b29 100644 --- a/src/main/java/org/qortal/transaction/RemoveGroupAdminTransaction.java +++ b/src/main/java/org/qortal/transaction/RemoveGroupAdminTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.data.group.GroupData; @@ -18,7 +16,9 @@ import org.qortal.repository.Repository; public class RemoveGroupAdminTransaction extends Transaction { // Properties + private RemoveGroupAdminTransactionData removeGroupAdminTransactionData; + private Account adminAccount = null; // Constructors @@ -31,53 +31,34 @@ public class RemoveGroupAdminTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getOwner().getAddress())) - return true; - - if (address.equals(this.getAdmin().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getOwner().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.removeGroupAdminTransactionData.getAdmin()); } // Navigation - public Account getOwner() throws DataException { - return new PublicKeyAccount(this.repository, this.removeGroupAdminTransactionData.getOwnerPublicKey()); + public Account getOwner() { + return this.getCreator(); } - public Account getAdmin() throws DataException { - return new Account(this.repository, this.removeGroupAdminTransactionData.getAdmin()); + public Account getAdmin() { + if (this.adminAccount == null) + this.adminAccount = new Account(this.repository, this.removeGroupAdminTransactionData.getAdmin()); + + return this.adminAccount; } // Processing @Override public ValidationResult isValid() throws DataException { + int groupId = this.removeGroupAdminTransactionData.getGroupId(); + // Check admin address is valid - if (!Crypto.isValidAddress(removeGroupAdminTransactionData.getAdmin())) + if (!Crypto.isValidAddress(this.removeGroupAdminTransactionData.getAdmin())) return ValidationResult.INVALID_ADDRESS; - GroupData groupData = this.repository.getGroupRepository().fromGroupId(removeGroupAdminTransactionData.getGroupId()); + GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupId); // Check group exists if (groupData == null) @@ -92,15 +73,11 @@ public class RemoveGroupAdminTransaction extends Transaction { Account admin = getAdmin(); // Check member is an admin - if (!this.repository.getGroupRepository().adminExists(removeGroupAdminTransactionData.getGroupId(), admin.getAddress())) + if (!this.repository.getGroupRepository().adminExists(groupId, admin.getAddress())) return ValidationResult.NOT_GROUP_ADMIN; - // Check fee is positive - if (removeGroupAdminTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check creator has enough funds - if (owner.getConfirmedBalance(Asset.QORT).compareTo(removeGroupAdminTransactionData.getFee()) < 0) + if (owner.getConfirmedBalance(Asset.QORT) < this.removeGroupAdminTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -109,21 +86,21 @@ public class RemoveGroupAdminTransaction extends Transaction { @Override public void process() throws DataException { // Update Group adminship - Group group = new Group(this.repository, removeGroupAdminTransactionData.getGroupId()); - group.demoteFromAdmin(removeGroupAdminTransactionData); + Group group = new Group(this.repository, this.removeGroupAdminTransactionData.getGroupId()); + group.demoteFromAdmin(this.removeGroupAdminTransactionData); // Save this transaction with cached references to transactions that can help restore state - this.repository.getTransactionRepository().save(removeGroupAdminTransactionData); + this.repository.getTransactionRepository().save(this.removeGroupAdminTransactionData); } @Override public void orphan() throws DataException { // Revert group adminship - Group group = new Group(this.repository, removeGroupAdminTransactionData.getGroupId()); - group.undemoteFromAdmin(removeGroupAdminTransactionData); + Group group = new Group(this.repository, this.removeGroupAdminTransactionData.getGroupId()); + group.undemoteFromAdmin(this.removeGroupAdminTransactionData); // Save this transaction with removed group references - this.repository.getTransactionRepository().save(removeGroupAdminTransactionData); + this.repository.getTransactionRepository().save(this.removeGroupAdminTransactionData); } } \ No newline at end of file diff --git a/src/main/java/org/qortal/transaction/RewardShareTransaction.java b/src/main/java/org/qortal/transaction/RewardShareTransaction.java index 8a478299..c22c5300 100644 --- a/src/main/java/org/qortal/transaction/RewardShareTransaction.java +++ b/src/main/java/org/qortal/transaction/RewardShareTransaction.java @@ -1,6 +1,5 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -19,8 +18,13 @@ import org.qortal.transform.Transformer; public class RewardShareTransaction extends Transaction { + public static final int MAX_SHARE = 100 * 100; // unscaled + // Properties + private RewardShareTransactionData rewardShareTransactionData; + private boolean haveCheckedForExistingRewardShare = false; + private RewardShareData existingRewardShareData = null; // Constructors @@ -33,42 +37,23 @@ public class RewardShareTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getMintingAccount().getAddress())) - return true; - - if (address.equals(this.getRecipient().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getMintingAccount().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.rewardShareTransactionData.getRecipient()); } private RewardShareData getExistingRewardShare() throws DataException { - // Look up any existing reward-share (using transaction's reward-share public key) - RewardShareData existingRewardShareData = this.repository.getAccountRepository().getRewardShare(this.rewardShareTransactionData.getRewardSharePublicKey()); - if (existingRewardShareData == null) - // No luck, try looking up existing reward-share using minting & recipient account info - existingRewardShareData = this.repository.getAccountRepository().getRewardShare(this.rewardShareTransactionData.getMinterPublicKey(), this.rewardShareTransactionData.getRecipient()); + if (this.haveCheckedForExistingRewardShare == false) { + this.haveCheckedForExistingRewardShare = true; - return existingRewardShareData; + // Look up any existing reward-share (using transaction's reward-share public key) + this.existingRewardShareData = this.repository.getAccountRepository().getRewardShare(this.rewardShareTransactionData.getRewardSharePublicKey()); + + if (this.existingRewardShareData == null) + // No luck, try looking up existing reward-share using minting & recipient account info + this.existingRewardShareData = this.repository.getAccountRepository().getRewardShare(this.rewardShareTransactionData.getMinterPublicKey(), this.rewardShareTransactionData.getRecipient()); + } + + return this.existingRewardShareData; } private boolean doesRewardShareMatch(RewardShareData rewardShareData) { @@ -80,7 +65,7 @@ public class RewardShareTransaction extends Transaction { // Navigation public PublicKeyAccount getMintingAccount() { - return new PublicKeyAccount(this.repository, this.rewardShareTransactionData.getMinterPublicKey()); + return this.getCreator(); } public Account getRecipient() { @@ -89,12 +74,11 @@ public class RewardShareTransaction extends Transaction { // Processing - private static final BigDecimal MAX_SHARE = BigDecimal.valueOf(100).setScale(2); - @Override public ValidationResult isFeeValid() throws DataException { // Look up any existing reward-share (using transaction's reward-share public key) RewardShareData existingRewardShareData = this.getExistingRewardShare(); + // If we have an existing reward-share then minter/recipient/reward-share-public-key should all match. // This is to prevent malicious actors using multiple (fake) reward-share public keys for the same minter/recipient combo, // or reusing the same reward-share public key for a different minter/recipient pair. @@ -102,10 +86,10 @@ public class RewardShareTransaction extends Transaction { return ValidationResult.INVALID_PUBLIC_KEY; final boolean isRecipientAlsoMinter = getCreator().getAddress().equals(this.rewardShareTransactionData.getRecipient()); - final boolean isCancellingSharePercent = this.rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) < 0; + final boolean isCancellingSharePercent = this.rewardShareTransactionData.getSharePercent() < 0; // Fee can be zero if self-share, and not cancelling - if (isRecipientAlsoMinter && !isCancellingSharePercent && this.transactionData.getFee().compareTo(BigDecimal.ZERO) >= 0) + if (isRecipientAlsoMinter && !isCancellingSharePercent && this.transactionData.getFee() >= 0) return ValidationResult.OK; return super.isFeeValid(); @@ -114,7 +98,7 @@ public class RewardShareTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { // Check reward share given to recipient. Negative is potentially OK to end a current reward-share. Zero also fine. - if (this.rewardShareTransactionData.getSharePercent().compareTo(MAX_SHARE) > 0) + if (this.rewardShareTransactionData.getSharePercent() > MAX_SHARE) return ValidationResult.INVALID_REWARD_SHARE_PERCENT; // Check reward-share public key is correct length @@ -127,7 +111,7 @@ public class RewardShareTransaction extends Transaction { PublicKeyAccount creator = getCreator(); Account recipient = getRecipient(); - final boolean isCancellingSharePercent = this.rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) < 0; + final boolean isCancellingSharePercent = this.rewardShareTransactionData.getSharePercent() < 0; // Creator themselves needs to be allowed to mint (unless cancelling) if (!isCancellingSharePercent && !creator.canMint()) @@ -140,6 +124,7 @@ public class RewardShareTransaction extends Transaction { // Look up any existing reward-share (using transaction's reward-share public key) RewardShareData existingRewardShareData = this.getExistingRewardShare(); + // If we have an existing reward-share then minter/recipient/reward-share-public-key should all match. // This is to prevent malicious actors using multiple (fake) reward-share public keys for the same minter/recipient combo, // or reusing the same reward-share public key for a different minter/recipient pair. @@ -168,7 +153,7 @@ public class RewardShareTransaction extends Transaction { // Fee checking needed if not setting up new self-share if (!(isRecipientAlsoMinter && existingRewardShareData == null)) // Check creator has enough funds - if (creator.getConfirmedBalance(Asset.QORT).compareTo(rewardShareTransactionData.getFee()) < 0) + if (creator.getConfirmedBalance(Asset.QORT) < this.rewardShareTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -180,24 +165,24 @@ public class RewardShareTransaction extends Transaction { // Grab any previous share info for orphaning purposes RewardShareData rewardShareData = this.repository.getAccountRepository().getRewardShare(mintingAccount.getPublicKey(), - rewardShareTransactionData.getRecipient()); + this.rewardShareTransactionData.getRecipient()); if (rewardShareData != null) - rewardShareTransactionData.setPreviousSharePercent(rewardShareData.getSharePercent()); + this.rewardShareTransactionData.setPreviousSharePercent(rewardShareData.getSharePercent()); // Save this transaction, with previous share info - this.repository.getTransactionRepository().save(rewardShareTransactionData); + this.repository.getTransactionRepository().save(this.rewardShareTransactionData); - final boolean isSharePercentNegative = this.rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) < 0; + final boolean isSharePercentNegative = this.rewardShareTransactionData.getSharePercent() < 0; // Negative share is actually a request to delete existing reward-share if (isSharePercentNegative) { - this.repository.getAccountRepository().delete(mintingAccount.getPublicKey(), rewardShareTransactionData.getRecipient()); + this.repository.getAccountRepository().delete(mintingAccount.getPublicKey(), this.rewardShareTransactionData.getRecipient()); } else { // Save reward-share info rewardShareData = new RewardShareData(mintingAccount.getPublicKey(), mintingAccount.getAddress(), - rewardShareTransactionData.getRecipient(), rewardShareTransactionData.getRewardSharePublicKey(), - rewardShareTransactionData.getSharePercent()); + this.rewardShareTransactionData.getRecipient(), this.rewardShareTransactionData.getRewardSharePublicKey(), + this.rewardShareTransactionData.getSharePercent()); this.repository.getAccountRepository().save(rewardShareData); } } @@ -207,9 +192,9 @@ public class RewardShareTransaction extends Transaction { super.processReferencesAndFees(); // If reward-share recipient has no last-reference then use this transaction's signature as last-reference so they can spend their block rewards - Account recipient = new Account(this.repository, rewardShareTransactionData.getRecipient()); + Account recipient = new Account(this.repository, this.rewardShareTransactionData.getRecipient()); if (recipient.getLastReference() == null) - recipient.setLastReference(rewardShareTransactionData.getSignature()); + recipient.setLastReference(this.rewardShareTransactionData.getSignature()); } @Override @@ -217,21 +202,21 @@ public class RewardShareTransaction extends Transaction { // Revert PublicKeyAccount mintingAccount = getMintingAccount(); - if (rewardShareTransactionData.getPreviousSharePercent() != null) { + if (this.rewardShareTransactionData.getPreviousSharePercent() != null) { // Revert previous sharing arrangement RewardShareData rewardShareData = new RewardShareData(mintingAccount.getPublicKey(), mintingAccount.getAddress(), - rewardShareTransactionData.getRecipient(), rewardShareTransactionData.getRewardSharePublicKey(), - rewardShareTransactionData.getPreviousSharePercent()); + this.rewardShareTransactionData.getRecipient(), this.rewardShareTransactionData.getRewardSharePublicKey(), + this.rewardShareTransactionData.getPreviousSharePercent()); this.repository.getAccountRepository().save(rewardShareData); } else { // No previous arrangement so simply delete - this.repository.getAccountRepository().delete(mintingAccount.getPublicKey(), rewardShareTransactionData.getRecipient()); + this.repository.getAccountRepository().delete(mintingAccount.getPublicKey(), this.rewardShareTransactionData.getRecipient()); } // Save this transaction, with removed previous share info - rewardShareTransactionData.setPreviousSharePercent(null); - this.repository.getTransactionRepository().save(rewardShareTransactionData); + this.rewardShareTransactionData.setPreviousSharePercent(null); + this.repository.getTransactionRepository().save(this.rewardShareTransactionData); } @Override @@ -239,8 +224,8 @@ public class RewardShareTransaction extends Transaction { super.orphanReferencesAndFees(); // If recipient didn't have a last-reference prior to this transaction then remove it - Account recipient = new Account(this.repository, rewardShareTransactionData.getRecipient()); - if (Arrays.equals(recipient.getLastReference(), rewardShareTransactionData.getSignature())) + Account recipient = new Account(this.repository, this.rewardShareTransactionData.getRecipient()); + if (Arrays.equals(recipient.getLastReference(), this.rewardShareTransactionData.getSignature())) recipient.setLastReference(null); } diff --git a/src/main/java/org/qortal/transaction/SellNameTransaction.java b/src/main/java/org/qortal/transaction/SellNameTransaction.java index b9e95e10..8694e66d 100644 --- a/src/main/java/org/qortal/transaction/SellNameTransaction.java +++ b/src/main/java/org/qortal/transaction/SellNameTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.naming.NameData; import org.qortal.data.transaction.SellNameTransactionData; @@ -19,7 +17,7 @@ import com.google.common.base.Utf8; public class SellNameTransaction extends Transaction { /** Maximum amount/price for selling a name. Chosen so value, including 8 decimal places, encodes into 8 bytes or fewer. */ - private static final BigDecimal MAX_AMOUNT = BigDecimal.valueOf(10_000_000_000L); + private static final long MAX_AMOUNT = Asset.MAX_QUANTITY; // Properties private SellNameTransactionData sellNameTransactionData; @@ -35,51 +33,32 @@ public class SellNameTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() { - return new ArrayList<>(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getOwner().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getOwner().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.emptyList(); } // Navigation - public Account getOwner() throws DataException { - return new PublicKeyAccount(this.repository, this.sellNameTransactionData.getOwnerPublicKey()); + public Account getOwner() { + return this.getCreator(); } // Processing @Override public ValidationResult isValid() throws DataException { + String name = this.sellNameTransactionData.getName(); + // Check name size bounds - int nameLength = Utf8.encodedLength(sellNameTransactionData.getName()); + int nameLength = Utf8.encodedLength(name); if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check name is lowercase - if (!sellNameTransactionData.getName().equals(sellNameTransactionData.getName().toLowerCase())) + if (!name.equals(name.toLowerCase())) return ValidationResult.NAME_NOT_LOWER_CASE; - NameData nameData = this.repository.getNameRepository().fromName(sellNameTransactionData.getName()); + NameData nameData = this.repository.getNameRepository().fromName(name); // Check name exists if (nameData == null) @@ -95,19 +74,15 @@ public class SellNameTransaction extends Transaction { return ValidationResult.INVALID_NAME_OWNER; // Check amount is positive - if (sellNameTransactionData.getAmount().compareTo(BigDecimal.ZERO) <= 0) + if (this.sellNameTransactionData.getAmount() <= 0) return ValidationResult.NEGATIVE_AMOUNT; // Check amount within bounds - if (sellNameTransactionData.getAmount().compareTo(MAX_AMOUNT) >= 0) + if (this.sellNameTransactionData.getAmount() >= MAX_AMOUNT) return ValidationResult.INVALID_AMOUNT; - // Check fee is positive - if (sellNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check issuer has enough funds - if (owner.getConfirmedBalance(Asset.QORT).compareTo(sellNameTransactionData.getFee()) < 0) + if (owner.getConfirmedBalance(Asset.QORT) < this.sellNameTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -116,15 +91,15 @@ public class SellNameTransaction extends Transaction { @Override public void process() throws DataException { // Sell Name - Name name = new Name(this.repository, sellNameTransactionData.getName()); - name.sell(sellNameTransactionData); + Name name = new Name(this.repository, this.sellNameTransactionData.getName()); + name.sell(this.sellNameTransactionData); } @Override public void orphan() throws DataException { // Revert name - Name name = new Name(this.repository, sellNameTransactionData.getName()); - name.unsell(sellNameTransactionData); + Name name = new Name(this.repository, this.sellNameTransactionData.getName()); + name.unsell(this.sellNameTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/SetGroupTransaction.java b/src/main/java/org/qortal/transaction/SetGroupTransaction.java index e2f3f0b4..084044a7 100644 --- a/src/main/java/org/qortal/transaction/SetGroupTransaction.java +++ b/src/main/java/org/qortal/transaction/SetGroupTransaction.java @@ -1,6 +1,5 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; @@ -28,53 +27,30 @@ public class SetGroupTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { + public List getRecipientAddresses() throws DataException { return Collections.emptyList(); } - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getCreator().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getCreator().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; - } - // Navigation // Processing @Override public ValidationResult isValid() throws DataException { + int defaultGroupId = this.setGroupTransactionData.getDefaultGroupId(); + // Check group exists - if (!this.repository.getGroupRepository().groupExists(setGroupTransactionData.getDefaultGroupId())) + if (!this.repository.getGroupRepository().groupExists(defaultGroupId)) return ValidationResult.GROUP_DOES_NOT_EXIST; Account creator = getCreator(); // Must be member of group - if (!this.repository.getGroupRepository().memberExists(setGroupTransactionData.getDefaultGroupId(), creator.getAddress())) + if (!this.repository.getGroupRepository().memberExists(defaultGroupId, creator.getAddress())) return ValidationResult.NOT_GROUP_MEMBER; - // Check fee is positive - if (setGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check creator has enough funds - if (creator.getConfirmedBalance(Asset.QORT).compareTo(setGroupTransactionData.getFee()) < 0) + if (creator.getConfirmedBalance(Asset.QORT) < this.setGroupTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -88,13 +64,13 @@ public class SetGroupTransaction extends Transaction { if (previousDefaultGroupId == null) previousDefaultGroupId = Group.NO_GROUP; - setGroupTransactionData.setPreviousDefaultGroupId(previousDefaultGroupId); + this.setGroupTransactionData.setPreviousDefaultGroupId(previousDefaultGroupId); // Save this transaction with account's previous defaultGroupId value - this.repository.getTransactionRepository().save(setGroupTransactionData); + this.repository.getTransactionRepository().save(this.setGroupTransactionData); // Set account's new default groupID - creator.setDefaultGroupId(setGroupTransactionData.getDefaultGroupId()); + creator.setDefaultGroupId(this.setGroupTransactionData.getDefaultGroupId()); } @Override @@ -102,15 +78,15 @@ public class SetGroupTransaction extends Transaction { // Revert Account creator = getCreator(); - Integer previousDefaultGroupId = setGroupTransactionData.getPreviousDefaultGroupId(); + Integer previousDefaultGroupId = this.setGroupTransactionData.getPreviousDefaultGroupId(); if (previousDefaultGroupId == null) previousDefaultGroupId = Group.NO_GROUP; creator.setDefaultGroupId(previousDefaultGroupId); // Save this transaction with removed previous defaultGroupId value - setGroupTransactionData.setPreviousDefaultGroupId(null); - this.repository.getTransactionRepository().save(setGroupTransactionData); + this.setGroupTransactionData.setPreviousDefaultGroupId(null); + this.repository.getTransactionRepository().save(this.setGroupTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/Transaction.java b/src/main/java/org/qortal/transaction/Transaction.java index 8317a5ea..d93ba732 100644 --- a/src/main/java/org/qortal/transaction/Transaction.java +++ b/src/main/java/org/qortal/transaction/Transaction.java @@ -1,9 +1,6 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.math.BigInteger; -import java.math.MathContext; -import java.math.RoundingMode; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -261,8 +258,11 @@ public abstract class Transaction { private static final Logger LOGGER = LogManager.getLogger(Transaction.class); // Properties + protected Repository repository; protected TransactionData transactionData; + /** Cached creator account. Use getCreator() to access. */ + private PublicKeyAccount creator = null; // Constructors @@ -320,12 +320,12 @@ public abstract class Transaction { /** Returns whether transaction's fee is at least minimum unit fee as specified in blockchain config. */ public boolean hasMinimumFee() { - return this.transactionData.getFee().compareTo(BlockChain.getInstance().getUnitFee()) >= 0; + return this.transactionData.getFee() >= BlockChain.getInstance().getUnscaledUnitFee(); } - public BigDecimal feePerByte() { + public long feePerByte() { try { - return this.transactionData.getFee().divide(new BigDecimal(TransactionTransformer.getDataLength(this.transactionData)), MathContext.DECIMAL32); + return this.transactionData.getFee() / TransactionTransformer.getDataLength(this.transactionData); } catch (TransformationException e) { throw new IllegalStateException("Unable to get transaction byte length?"); } @@ -333,10 +333,13 @@ public abstract class Transaction { /** Returns whether transaction's fee is at least amount needed to cover byte-length of transaction. */ public boolean hasMinimumFeePerByte() { - return this.feePerByte().compareTo(BlockChain.getInstance().getMinFeePerByte()) >= 0; + long unitFee = BlockChain.getInstance().getUnscaledUnitFee(); + int maxBytePerUnitFee = BlockChain.getInstance().getMaxBytesPerUnitFee(); + + return this.feePerByte() >= maxBytePerUnitFee / unitFee; } - public BigDecimal calcRecommendedFee() { + public long calcRecommendedFee() { int dataLength; try { dataLength = TransactionTransformer.getDataLength(this.transactionData); @@ -344,12 +347,11 @@ public abstract class Transaction { throw new IllegalStateException("Unable to get transaction byte length?"); } - BigDecimal maxBytePerUnitFee = BlockChain.getInstance().getMaxBytesPerUnitFee(); + int maxBytePerUnitFee = BlockChain.getInstance().getMaxBytesPerUnitFee(); - BigDecimal unitFeeCount = BigDecimal.valueOf(dataLength).divide(maxBytePerUnitFee, RoundingMode.UP); + int unitFeeCount = ((dataLength - 1) / maxBytePerUnitFee) + 1; - BigDecimal recommendedFee = BlockChain.getInstance().getUnitFee().multiply(unitFeeCount).setScale(8); - return recommendedFee; + return BlockChain.getInstance().getUnscaledUnitFee() * unitFeeCount; } /** @@ -399,45 +401,32 @@ public abstract class Transaction { * @return list of recipients accounts, or empty list if none * @throws DataException */ - public abstract List getRecipientAccounts() throws DataException; - - /** - * Returns a list of involved accounts for this transaction. - *

- * "Involved" means sender or recipient. - * - * @return list of involved accounts, or empty list if none - * @throws DataException - */ - public List getInvolvedAccounts() throws DataException { - // Typically this is all the recipients plus the transaction creator/sender - List participants = new ArrayList<>(getRecipientAccounts()); - participants.add(getCreator()); - return participants; + public List getRecipientAccounts() throws DataException { + throw new DataException("Placeholder for new AT code"); } /** - * Returns whether passed account is an involved party in this transaction. - *

- * Account could be sender, or any one of the potential recipients. + * Returns a list of recipient addresses for this transaction. * - * @param account - * @return true if account is involved, false otherwise + * @return list of recipients addresses, or empty list if none * @throws DataException */ - public abstract boolean isInvolved(Account account) throws DataException; + public abstract List getRecipientAddresses() throws DataException; /** - * Returns amount of QORT lost/gained by passed account due to this transaction. + * Returns a list of involved addresses for this transaction. *

- * Amounts "lost", e.g. sent by sender and fees, are returned as negative values.
- * Amounts "gained", e.g. QORT sent to recipient, are returned as positive values. + * "Involved" means sender or recipient. * - * @param account - * @return Amount of QORT lost/gained by account, or BigDecimal.ZERO otherwise + * @return list of involved addresses, or empty list if none * @throws DataException */ - public abstract BigDecimal getAmount(Account account) throws DataException; + public List getInvolvedAddresses() throws DataException { + // Typically this is all the recipients plus the transaction creator/sender + List participants = new ArrayList<>(getRecipientAddresses()); + participants.add(0, this.getCreator().getAddress()); + return participants; + } // Navigation @@ -447,11 +436,11 @@ public abstract class Transaction { * @return creator * @throws DataException */ - protected PublicKeyAccount getCreator() throws DataException { - if (this.transactionData.getCreatorPublicKey() == null) - return null; + protected PublicKeyAccount getCreator() { + if (this.creator == null) + this.creator = new PublicKeyAccount(this.repository, this.transactionData.getCreatorPublicKey()); - return new PublicKeyAccount(this.repository, this.transactionData.getCreatorPublicKey()); + return this.creator; } /** @@ -460,7 +449,7 @@ public abstract class Transaction { * @return Parent's TransactionData, or null if no parent found (which should not happen) * @throws DataException */ - public TransactionData getParent() throws DataException { + protected TransactionData getParent() throws DataException { byte[] reference = this.transactionData.getReference(); if (reference == null) return null; @@ -474,7 +463,7 @@ public abstract class Transaction { * @return Child's TransactionData, or null if no child found * @throws DataException */ - public TransactionData getChild() throws DataException { + protected TransactionData getChild() throws DataException { byte[] signature = this.transactionData.getSignature(); if (signature == null) return null; @@ -925,9 +914,9 @@ public abstract class Transaction { Account creator = getCreator(); // Update transaction creator's balance - creator.setConfirmedBalance(Asset.QORT, creator.getConfirmedBalance(Asset.QORT).subtract(transactionData.getFee())); + creator.setConfirmedBalance(Asset.QORT, creator.getConfirmedBalance(Asset.QORT) - transactionData.getFee()); - // Update transaction creator's reference + // Update transaction creator's reference (and possibly public key) creator.setLastReference(transactionData.getSignature()); } @@ -949,9 +938,9 @@ public abstract class Transaction { Account creator = getCreator(); // Update transaction creator's balance - creator.setConfirmedBalance(Asset.QORT, creator.getConfirmedBalance(Asset.QORT).add(transactionData.getFee())); + creator.setConfirmedBalance(Asset.QORT, creator.getConfirmedBalance(Asset.QORT) + transactionData.getFee()); - // Update transaction creator's reference + // Update transaction creator's reference (and possibly public key) creator.setLastReference(transactionData.getReference()); } diff --git a/src/main/java/org/qortal/transaction/TransferAssetTransaction.java b/src/main/java/org/qortal/transaction/TransferAssetTransaction.java index c87be6a6..991a1ff0 100644 --- a/src/main/java/org/qortal/transaction/TransferAssetTransaction.java +++ b/src/main/java/org/qortal/transaction/TransferAssetTransaction.java @@ -1,12 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; -import org.qortal.asset.Asset; import org.qortal.data.PaymentData; import org.qortal.data.transaction.TransactionData; import org.qortal.data.transaction.TransferAssetTransactionData; @@ -17,7 +14,9 @@ import org.qortal.repository.Repository; public class TransferAssetTransaction extends Transaction { // Properties + private TransferAssetTransactionData transferAssetTransactionData; + private PaymentData paymentData = null; // Constructors @@ -30,94 +29,63 @@ public class TransferAssetTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(new Account(this.repository, transferAssetTransactionData.getRecipient())); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getSender().getAddress())) - return true; - - if (address.equals(transferAssetTransactionData.getRecipient())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - String senderAddress = this.getSender().getAddress(); - - if (address.equals(senderAddress)) - amount = amount.subtract(this.transactionData.getFee()); - - // We're only interested in OQRT amounts - if (transferAssetTransactionData.getAssetId() == Asset.QORT) { - if (address.equals(transferAssetTransactionData.getRecipient())) - amount = amount.add(transferAssetTransactionData.getAmount()); - else if (address.equals(senderAddress)) - amount = amount.subtract(transferAssetTransactionData.getAmount()); - } - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.transferAssetTransactionData.getRecipient()); } // Navigation - public Account getSender() throws DataException { - return new PublicKeyAccount(this.repository, this.transferAssetTransactionData.getSenderPublicKey()); + public Account getSender() { + return this.getCreator(); } // Processing private PaymentData getPaymentData() { - return new PaymentData(transferAssetTransactionData.getRecipient(), transferAssetTransactionData.getAssetId(), - transferAssetTransactionData.getAmount()); + if (this.paymentData == null) + this.paymentData = new PaymentData(this.transferAssetTransactionData.getRecipient(), this.transferAssetTransactionData.getAssetId(), + this.transferAssetTransactionData.getAmount()); + + return this.paymentData; } @Override public ValidationResult isValid() throws DataException { // Wrap asset transfer as a payment and delegate final payment checks to Payment class - return new Payment(this.repository).isValid(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee()); + return new Payment(this.repository).isValid(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), this.transferAssetTransactionData.getFee()); } @Override public ValidationResult isProcessable() throws DataException { // Wrap asset transfer as a payment and delegate final processable checks to Payment class - return new Payment(this.repository).isProcessable(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee()); + return new Payment(this.repository).isProcessable(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), this.transferAssetTransactionData.getFee()); } @Override public void process() throws DataException { - // Wrap asset transfer as a payment and delegate processing to Payment class. Only update recipient's last reference if transferring QORT. - new Payment(this.repository).process(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(), - transferAssetTransactionData.getSignature()); + // Wrap asset transfer as a payment and delegate processing to Payment class. + new Payment(this.repository).process(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), this.transferAssetTransactionData.getSignature()); } @Override public void processReferencesAndFees() throws DataException { // Wrap asset transfer as a payment and delegate processing to Payment class. Only update recipient's last reference if transferring QORT. - new Payment(this.repository).processReferencesAndFees(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(), - transferAssetTransactionData.getSignature(), false); + new Payment(this.repository).processReferencesAndFees(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), this.transferAssetTransactionData.getFee(), + this.transferAssetTransactionData.getSignature(), false); } @Override public void orphan() throws DataException { - // Wrap asset transfer as a payment and delegate processing to Payment class. Only revert recipient's last reference if transferring QORT. - new Payment(this.repository).orphan(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(), - transferAssetTransactionData.getSignature(), transferAssetTransactionData.getReference()); + // 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()); } @Override public void orphanReferencesAndFees() throws DataException { // Wrap asset transfer as a payment and delegate processing to Payment class. Only revert recipient's last reference if transferring QORT. - new Payment(this.repository).orphanReferencesAndFees(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(), - transferAssetTransactionData.getSignature(), transferAssetTransactionData.getReference(), false); + new Payment(this.repository).orphanReferencesAndFees(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), this.transferAssetTransactionData.getFee(), + this.transferAssetTransactionData.getSignature(), this.transferAssetTransactionData.getReference(), false); } } diff --git a/src/main/java/org/qortal/transaction/TransferPrivsTransaction.java b/src/main/java/org/qortal/transaction/TransferPrivsTransaction.java index 9716cfa5..92c9f533 100644 --- a/src/main/java/org/qortal/transaction/TransferPrivsTransaction.java +++ b/src/main/java/org/qortal/transaction/TransferPrivsTransaction.java @@ -1,6 +1,5 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -8,7 +7,6 @@ import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.block.BlockChain; import org.qortal.crypto.Crypto; import org.qortal.data.account.AccountData; @@ -36,42 +34,17 @@ public class TransferPrivsTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(new Account(this.repository, transferPrivsTransactionData.getRecipient())); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getSender().getAddress())) - return true; - - if (address.equals(transferPrivsTransactionData.getRecipient())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - String senderAddress = this.getSender().getAddress(); - - if (address.equals(senderAddress)) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.transferPrivsTransactionData.getRecipient()); } // Navigation - public Account getSender() throws DataException { - return new PublicKeyAccount(this.repository, this.transferPrivsTransactionData.getSenderPublicKey()); + public Account getSender() { + return this.getCreator(); } - public Account getRecipient() throws DataException { + public Account getRecipient() { return new Account(this.repository, this.transferPrivsTransactionData.getRecipient()); } @@ -167,9 +140,9 @@ public class TransferPrivsTransaction extends Transaction { super.processReferencesAndFees(); // If recipient has no last-reference then use this transaction's signature as last-reference so they can spend their block rewards - Account recipient = new Account(this.repository, transferPrivsTransactionData.getRecipient()); + Account recipient = new Account(this.repository, this.transferPrivsTransactionData.getRecipient()); if (recipient.getLastReference() == null) - recipient.setLastReference(transferPrivsTransactionData.getSignature()); + recipient.setLastReference(this.transferPrivsTransactionData.getSignature()); } @Override @@ -249,8 +222,8 @@ public class TransferPrivsTransaction extends Transaction { super.orphanReferencesAndFees(); // If recipient didn't have a last-reference prior to this transaction then remove it - Account recipient = new Account(this.repository, transferPrivsTransactionData.getRecipient()); - if (Arrays.equals(recipient.getLastReference(), transferPrivsTransactionData.getSignature())) + Account recipient = new Account(this.repository, this.transferPrivsTransactionData.getRecipient()); + if (Arrays.equals(recipient.getLastReference(), this.transferPrivsTransactionData.getSignature())) recipient.setLastReference(null); } diff --git a/src/main/java/org/qortal/transaction/UpdateAssetTransaction.java b/src/main/java/org/qortal/transaction/UpdateAssetTransaction.java index fc0760f5..2a7af23c 100644 --- a/src/main/java/org/qortal/transaction/UpdateAssetTransaction.java +++ b/src/main/java/org/qortal/transaction/UpdateAssetTransaction.java @@ -1,6 +1,5 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; @@ -32,42 +31,14 @@ public class UpdateAssetTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(getNewOwner()); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getOwner().getAddress())) - return true; - - if (address.equals(this.getNewOwner().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getOwner().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.updateAssetTransactionData.getNewOwner()); } // Navigation - public PublicKeyAccount getOwner() throws DataException { - return new PublicKeyAccount(this.repository, this.updateAssetTransactionData.getOwnerPublicKey()); - } - - public Account getNewOwner() throws DataException { - return new Account(this.repository, this.updateAssetTransactionData.getNewOwner()); + public PublicKeyAccount getOwner() { + return this.getCreator(); } // Processing @@ -75,37 +46,33 @@ public class UpdateAssetTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { // Check asset actually exists - AssetData assetData = this.repository.getAssetRepository().fromAssetId(updateAssetTransactionData.getAssetId()); + AssetData assetData = this.repository.getAssetRepository().fromAssetId(this.updateAssetTransactionData.getAssetId()); if (assetData == null) return ValidationResult.ASSET_DOES_NOT_EXIST; // Check new owner address is valid - if (!Crypto.isValidAddress(updateAssetTransactionData.getNewOwner())) + if (!Crypto.isValidAddress(this.updateAssetTransactionData.getNewOwner())) return ValidationResult.INVALID_ADDRESS; // Check new description size bounds. Note: zero length means DO NOT CHANGE description - int newDescriptionLength = Utf8.encodedLength(updateAssetTransactionData.getNewDescription()); + int newDescriptionLength = Utf8.encodedLength(this.updateAssetTransactionData.getNewDescription()); if (newDescriptionLength > Asset.MAX_DESCRIPTION_SIZE) return ValidationResult.INVALID_DATA_LENGTH; // Check new data size bounds. Note: zero length means DO NOT CHANGE data - int newDataLength = Utf8.encodedLength(updateAssetTransactionData.getNewData()); + int newDataLength = Utf8.encodedLength(this.updateAssetTransactionData.getNewData()); if (newDataLength > Asset.MAX_DATA_SIZE) return ValidationResult.INVALID_DATA_LENGTH; // As this transaction type could require approval, check txGroupId // matches groupID at creation - if (assetData.getCreationGroupId() != updateAssetTransactionData.getTxGroupId()) + if (assetData.getCreationGroupId() != this.updateAssetTransactionData.getTxGroupId()) return ValidationResult.TX_GROUP_ID_MISMATCH; - // Check fee is positive - if (updateAssetTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - Account currentOwner = getOwner(); // Check current owner has enough funds - if (currentOwner.getConfirmedBalance(Asset.QORT).compareTo(updateAssetTransactionData.getFee()) < 0) + if (currentOwner.getConfirmedBalance(Asset.QORT) < this.updateAssetTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -115,7 +82,7 @@ public class UpdateAssetTransaction extends Transaction { public ValidationResult isProcessable() throws DataException { // Check transaction's public key matches asset's current owner Account currentOwner = getOwner(); - AssetData assetData = this.repository.getAssetRepository().fromAssetId(updateAssetTransactionData.getAssetId()); + AssetData assetData = this.repository.getAssetRepository().fromAssetId(this.updateAssetTransactionData.getAssetId()); if (!assetData.getOwner().equals(currentOwner.getAddress())) return ValidationResult.INVALID_ASSET_OWNER; @@ -126,21 +93,21 @@ public class UpdateAssetTransaction extends Transaction { @Override public void process() throws DataException { // Update Asset - Asset asset = new Asset(this.repository, updateAssetTransactionData.getAssetId()); - asset.update(updateAssetTransactionData); + Asset asset = new Asset(this.repository, this.updateAssetTransactionData.getAssetId()); + asset.update(this.updateAssetTransactionData); // Save this transaction, with updated "name reference" to previous transaction that updated name - this.repository.getTransactionRepository().save(updateAssetTransactionData); + this.repository.getTransactionRepository().save(this.updateAssetTransactionData); } @Override public void orphan() throws DataException { // Revert asset - Asset asset = new Asset(this.repository, updateAssetTransactionData.getAssetId()); - asset.revert(updateAssetTransactionData); + Asset asset = new Asset(this.repository, this.updateAssetTransactionData.getAssetId()); + asset.revert(this.updateAssetTransactionData); // Save this transaction, with removed "name reference" to previous transaction that updated name - this.repository.getTransactionRepository().save(updateAssetTransactionData); + this.repository.getTransactionRepository().save(this.updateAssetTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/UpdateGroupTransaction.java b/src/main/java/org/qortal/transaction/UpdateGroupTransaction.java index 26b35c36..6751be33 100644 --- a/src/main/java/org/qortal/transaction/UpdateGroupTransaction.java +++ b/src/main/java/org/qortal/transaction/UpdateGroupTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.data.group.GroupData; @@ -33,38 +31,14 @@ public class UpdateGroupTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(getNewOwner()); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getOwner().getAddress())) - return true; - - if (address.equals(this.getNewOwner().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getOwner().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.updateGroupTransactionData.getNewOwner()); } // Navigation - public Account getOwner() throws DataException { - return new PublicKeyAccount(this.repository, this.updateGroupTransactionData.getOwnerPublicKey()); + public Account getOwner() { + return this.getCreator(); } public Account getNewOwner() throws DataException { @@ -76,46 +50,42 @@ public class UpdateGroupTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { // Check new owner address is valid - if (!Crypto.isValidAddress(updateGroupTransactionData.getNewOwner())) + if (!Crypto.isValidAddress(this.updateGroupTransactionData.getNewOwner())) return ValidationResult.INVALID_ADDRESS; // Check new approval threshold is valid - if (updateGroupTransactionData.getNewApprovalThreshold() == null) + if (this.updateGroupTransactionData.getNewApprovalThreshold() == null) return ValidationResult.INVALID_GROUP_APPROVAL_THRESHOLD; // Check min/max block delay values - if (updateGroupTransactionData.getNewMinimumBlockDelay() < 0) + if (this.updateGroupTransactionData.getNewMinimumBlockDelay() < 0) return ValidationResult.INVALID_GROUP_BLOCK_DELAY; - if (updateGroupTransactionData.getNewMaximumBlockDelay() < 1) + if (this.updateGroupTransactionData.getNewMaximumBlockDelay() < 1) return ValidationResult.INVALID_GROUP_BLOCK_DELAY; - if (updateGroupTransactionData.getNewMaximumBlockDelay() < updateGroupTransactionData.getNewMinimumBlockDelay()) + if (this.updateGroupTransactionData.getNewMaximumBlockDelay() < this.updateGroupTransactionData.getNewMinimumBlockDelay()) return ValidationResult.INVALID_GROUP_BLOCK_DELAY; // Check new description size bounds - int newDescriptionLength = Utf8.encodedLength(updateGroupTransactionData.getNewDescription()); + int newDescriptionLength = Utf8.encodedLength(this.updateGroupTransactionData.getNewDescription()); if (newDescriptionLength < 1 || newDescriptionLength > Group.MAX_DESCRIPTION_SIZE) return ValidationResult.INVALID_DESCRIPTION_LENGTH; - GroupData groupData = this.repository.getGroupRepository().fromGroupId(updateGroupTransactionData.getGroupId()); + GroupData groupData = this.repository.getGroupRepository().fromGroupId(this.updateGroupTransactionData.getGroupId()); // Check group exists if (groupData == null) return ValidationResult.GROUP_DOES_NOT_EXIST; // As this transaction type could require approval, check txGroupId matches groupID at creation - if (groupData.getCreationGroupId() != updateGroupTransactionData.getTxGroupId()) + if (groupData.getCreationGroupId() != this.updateGroupTransactionData.getTxGroupId()) return ValidationResult.TX_GROUP_ID_MISMATCH; Account owner = getOwner(); - // Check fee is positive - if (updateGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check creator has enough funds - if (owner.getConfirmedBalance(Asset.QORT).compareTo(updateGroupTransactionData.getFee()) < 0) + if (owner.getConfirmedBalance(Asset.QORT) < this.updateGroupTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -123,7 +93,7 @@ public class UpdateGroupTransaction extends Transaction { @Override public ValidationResult isProcessable() throws DataException { - GroupData groupData = this.repository.getGroupRepository().fromGroupId(updateGroupTransactionData.getGroupId()); + GroupData groupData = this.repository.getGroupRepository().fromGroupId(this.updateGroupTransactionData.getGroupId()); Account owner = getOwner(); // Check transaction's public key matches group's current owner @@ -133,31 +103,30 @@ public class UpdateGroupTransaction extends Transaction { Account newOwner = getNewOwner(); // Check new owner is not banned - if (this.repository.getGroupRepository().banExists(updateGroupTransactionData.getGroupId(), newOwner.getAddress())) + if (this.repository.getGroupRepository().banExists(this.updateGroupTransactionData.getGroupId(), newOwner.getAddress())) return ValidationResult.BANNED_FROM_GROUP; - return ValidationResult.OK; } @Override public void process() throws DataException { // Update Group - Group group = new Group(this.repository, updateGroupTransactionData.getGroupId()); - group.updateGroup(updateGroupTransactionData); + Group group = new Group(this.repository, this.updateGroupTransactionData.getGroupId()); + group.updateGroup(this.updateGroupTransactionData); // Save this transaction, now with updated "group reference" to previous transaction that updated group - this.repository.getTransactionRepository().save(updateGroupTransactionData); + this.repository.getTransactionRepository().save(this.updateGroupTransactionData); } @Override public void orphan() throws DataException { // Revert Group update - Group group = new Group(this.repository, updateGroupTransactionData.getGroupId()); - group.unupdateGroup(updateGroupTransactionData); + Group group = new Group(this.repository, this.updateGroupTransactionData.getGroupId()); + group.unupdateGroup(this.updateGroupTransactionData); // Save this transaction, now with removed "group reference" - this.repository.getTransactionRepository().save(updateGroupTransactionData); + this.repository.getTransactionRepository().save(this.updateGroupTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/UpdateNameTransaction.java b/src/main/java/org/qortal/transaction/UpdateNameTransaction.java index 5cc28c8e..2b731bd1 100644 --- a/src/main/java/org/qortal/transaction/UpdateNameTransaction.java +++ b/src/main/java/org/qortal/transaction/UpdateNameTransaction.java @@ -1,11 +1,9 @@ package org.qortal.transaction; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.crypto.Crypto; import org.qortal.data.naming.NameData; @@ -33,41 +31,17 @@ public class UpdateNameTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() throws DataException { - return Collections.singletonList(getNewOwner()); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getOwner().getAddress())) - return true; - - if (address.equals(this.getNewOwner().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getOwner().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.singletonList(this.updateNameTransactionData.getNewOwner()); } // Navigation - public Account getOwner() throws DataException { - return new PublicKeyAccount(this.repository, this.updateNameTransactionData.getOwnerPublicKey()); + public Account getOwner() { + return this.getCreator(); } - public Account getNewOwner() throws DataException { + public Account getNewOwner() { return new Account(this.repository, this.updateNameTransactionData.getNewOwner()); } @@ -75,42 +49,40 @@ public class UpdateNameTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { + String name = this.updateNameTransactionData.getName(); + // Check new owner address is valid - if (!Crypto.isValidAddress(updateNameTransactionData.getNewOwner())) + if (!Crypto.isValidAddress(this.updateNameTransactionData.getNewOwner())) return ValidationResult.INVALID_ADDRESS; // Check name size bounds - int nameLength = Utf8.encodedLength(updateNameTransactionData.getName()); + int nameLength = Utf8.encodedLength(name); if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check new data size bounds - int newDataLength = Utf8.encodedLength(updateNameTransactionData.getNewData()); + int newDataLength = Utf8.encodedLength(this.updateNameTransactionData.getNewData()); if (newDataLength < 1 || newDataLength > Name.MAX_DATA_SIZE) return ValidationResult.INVALID_DATA_LENGTH; // Check name is lowercase - if (!updateNameTransactionData.getName().equals(updateNameTransactionData.getName().toLowerCase())) + if (!name.equals(name.toLowerCase())) return ValidationResult.NAME_NOT_LOWER_CASE; - NameData nameData = this.repository.getNameRepository().fromName(updateNameTransactionData.getName()); + NameData nameData = this.repository.getNameRepository().fromName(name); // Check name exists if (nameData == null) return ValidationResult.NAME_DOES_NOT_EXIST; // As this transaction type could require approval, check txGroupId matches groupID at creation - if (nameData.getCreationGroupId() != updateNameTransactionData.getTxGroupId()) + if (nameData.getCreationGroupId() != this.updateNameTransactionData.getTxGroupId()) return ValidationResult.TX_GROUP_ID_MISMATCH; Account owner = getOwner(); - // Check fee is positive - if (updateNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - - // Check issuer has enough funds - if (owner.getConfirmedBalance(Asset.QORT).compareTo(updateNameTransactionData.getFee()) < 0) + // Check owner has enough funds + if (owner.getConfirmedBalance(Asset.QORT) < this.updateNameTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -118,7 +90,7 @@ public class UpdateNameTransaction extends Transaction { @Override public ValidationResult isProcessable() throws DataException { - NameData nameData = this.repository.getNameRepository().fromName(updateNameTransactionData.getName()); + NameData nameData = this.repository.getNameRepository().fromName(this.updateNameTransactionData.getName()); // Check name isn't currently for sale if (nameData.getIsForSale()) @@ -136,21 +108,21 @@ public class UpdateNameTransaction extends Transaction { @Override public void process() throws DataException { // Update Name - Name name = new Name(this.repository, updateNameTransactionData.getName()); - name.update(updateNameTransactionData); + Name name = new Name(this.repository, this.updateNameTransactionData.getName()); + name.update(this.updateNameTransactionData); // Save this transaction, now with updated "name reference" to previous transaction that updated name - this.repository.getTransactionRepository().save(updateNameTransactionData); + this.repository.getTransactionRepository().save(this.updateNameTransactionData); } @Override public void orphan() throws DataException { // Revert name - Name name = new Name(this.repository, updateNameTransactionData.getName()); - name.revert(updateNameTransactionData); + Name name = new Name(this.repository, this.updateNameTransactionData.getName()); + name.revert(this.updateNameTransactionData); // Save this transaction, now with removed "name reference" - this.repository.getTransactionRepository().save(updateNameTransactionData); + this.repository.getTransactionRepository().save(this.updateNameTransactionData); } } diff --git a/src/main/java/org/qortal/transaction/VoteOnPollTransaction.java b/src/main/java/org/qortal/transaction/VoteOnPollTransaction.java index de19da8a..3d781caf 100644 --- a/src/main/java/org/qortal/transaction/VoteOnPollTransaction.java +++ b/src/main/java/org/qortal/transaction/VoteOnPollTransaction.java @@ -1,13 +1,11 @@ package org.qortal.transaction; -import java.math.BigDecimal; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.qortal.account.Account; -import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.data.transaction.TransactionData; import org.qortal.data.transaction.VoteOnPollTransactionData; @@ -39,72 +37,55 @@ public class VoteOnPollTransaction extends Transaction { // More information @Override - public List getRecipientAccounts() { - return new ArrayList<>(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - return account.getAddress().equals(this.getCreator().getAddress()); - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (account.getAddress().equals(this.getCreator().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; + public List getRecipientAddresses() throws DataException { + return Collections.emptyList(); } // Navigation - public Account getVoter() throws DataException { - return new PublicKeyAccount(this.repository, voteOnPollTransactionData.getVoterPublicKey()); + public Account getVoter() { + return this.getCreator(); } // Processing @Override public ValidationResult isValid() throws DataException { + String pollName = this.voteOnPollTransactionData.getPollName(); + // Check name size bounds - int pollNameLength = Utf8.encodedLength(voteOnPollTransactionData.getPollName()); + int pollNameLength = Utf8.encodedLength(pollName); if (pollNameLength < 1 || pollNameLength > Poll.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check poll name is lowercase - if (!voteOnPollTransactionData.getPollName().equals(voteOnPollTransactionData.getPollName().toLowerCase())) + if (!pollName.equals(pollName.toLowerCase())) return ValidationResult.NAME_NOT_LOWER_CASE; VotingRepository votingRepository = this.repository.getVotingRepository(); // Check poll exists - PollData pollData = votingRepository.fromPollName(voteOnPollTransactionData.getPollName()); + PollData pollData = votingRepository.fromPollName(pollName); if (pollData == null) return ValidationResult.POLL_DOES_NOT_EXIST; // Check poll option index is within bounds List pollOptions = pollData.getPollOptions(); - int optionIndex = voteOnPollTransactionData.getOptionIndex(); + int optionIndex = this.voteOnPollTransactionData.getOptionIndex(); if (optionIndex < 0 || optionIndex > pollOptions.size() - 1) return ValidationResult.POLL_OPTION_DOES_NOT_EXIST; // Check if vote already exists - VoteOnPollData voteOnPollData = votingRepository.getVote(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey()); + VoteOnPollData voteOnPollData = votingRepository.getVote(pollName, this.voteOnPollTransactionData.getVoterPublicKey()); if (voteOnPollData != null && voteOnPollData.getOptionIndex() == optionIndex) return ValidationResult.ALREADY_VOTED_FOR_THAT_OPTION; - // Check fee is positive - if (voteOnPollTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - // Check reference is correct Account voter = getVoter(); // Check voter has enough funds - if (voter.getConfirmedBalance(Asset.QORT).compareTo(voteOnPollTransactionData.getFee()) < 0) + if (voter.getConfirmedBalance(Asset.QORT) < this.voteOnPollTransactionData.getFee()) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -112,27 +93,28 @@ public class VoteOnPollTransaction extends Transaction { @Override public void process() throws DataException { + String pollName = this.voteOnPollTransactionData.getPollName(); + Account voter = getVoter(); VotingRepository votingRepository = this.repository.getVotingRepository(); // Check for previous vote so we can save option in case of orphaning - VoteOnPollData previousVoteOnPollData = votingRepository.getVote(voteOnPollTransactionData.getPollName(), - voteOnPollTransactionData.getVoterPublicKey()); + VoteOnPollData previousVoteOnPollData = votingRepository.getVote(pollName, this.voteOnPollTransactionData.getVoterPublicKey()); if (previousVoteOnPollData != null) { voteOnPollTransactionData.setPreviousOptionIndex(previousVoteOnPollData.getOptionIndex()); - LOGGER.trace("Previous vote by " + voter.getAddress() + " on poll \"" + voteOnPollTransactionData.getPollName() + "\" was option index " - + previousVoteOnPollData.getOptionIndex()); + LOGGER.trace(() -> String.format("Previous vote by %s on poll \"%s\" was option index %d", + voter.getAddress(), pollName, previousVoteOnPollData.getOptionIndex())); } // Save this transaction, now with possible previous vote this.repository.getTransactionRepository().save(voteOnPollTransactionData); // Apply vote to poll - LOGGER.trace("Vote by " + voter.getAddress() + " on poll \"" + voteOnPollTransactionData.getPollName() + "\" with option index " - + voteOnPollTransactionData.getOptionIndex()); - VoteOnPollData newVoteOnPollData = new VoteOnPollData(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey(), - voteOnPollTransactionData.getOptionIndex()); + LOGGER.trace(() -> String.format("Vote by %s on poll \"%s\" with option index %d", + voter.getAddress(), pollName, this.voteOnPollTransactionData.getOptionIndex())); + VoteOnPollData newVoteOnPollData = new VoteOnPollData(pollName, this.voteOnPollTransactionData.getVoterPublicKey(), + this.voteOnPollTransactionData.getOptionIndex()); votingRepository.save(newVoteOnPollData); } @@ -142,22 +124,24 @@ public class VoteOnPollTransaction extends Transaction { // Does this transaction have previous vote info? VotingRepository votingRepository = this.repository.getVotingRepository(); - Integer previousOptionIndex = voteOnPollTransactionData.getPreviousOptionIndex(); + Integer previousOptionIndex = this.voteOnPollTransactionData.getPreviousOptionIndex(); if (previousOptionIndex != null) { // Reinstate previous vote - LOGGER.trace(() -> String.format("Reinstating previous vote by %s on poll \"%s\" with option index %d", voter.getAddress(), voteOnPollTransactionData.getPollName(), previousOptionIndex)); - VoteOnPollData previousVoteOnPollData = new VoteOnPollData(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey(), + LOGGER.trace(() -> String.format("Reinstating previous vote by %s on poll \"%s\" with option index %d", + voter.getAddress(), this.voteOnPollTransactionData.getPollName(), previousOptionIndex)); + VoteOnPollData previousVoteOnPollData = new VoteOnPollData(this.voteOnPollTransactionData.getPollName(), this.voteOnPollTransactionData.getVoterPublicKey(), previousOptionIndex); votingRepository.save(previousVoteOnPollData); } else { // Delete vote - LOGGER.trace(() -> String.format("Deleting vote by %s on poll \"%s\" with option index %d", voter.getAddress(), voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getOptionIndex())); - votingRepository.delete(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey()); + LOGGER.trace(() -> String.format("Deleting vote by %s on poll \"%s\" with option index %d", + voter.getAddress(), this.voteOnPollTransactionData.getPollName(), this.voteOnPollTransactionData.getOptionIndex())); + votingRepository.delete(this.voteOnPollTransactionData.getPollName(), this.voteOnPollTransactionData.getVoterPublicKey()); } // Save this transaction, with removed previous vote info - voteOnPollTransactionData.setPreviousOptionIndex(null); - this.repository.getTransactionRepository().save(voteOnPollTransactionData); + this.voteOnPollTransactionData.setPreviousOptionIndex(null); + this.repository.getTransactionRepository().save(this.voteOnPollTransactionData); } } diff --git a/src/main/java/org/qortal/transform/PaymentTransformer.java b/src/main/java/org/qortal/transform/PaymentTransformer.java index 98b34143..56aa7ad1 100644 --- a/src/main/java/org/qortal/transform/PaymentTransformer.java +++ b/src/main/java/org/qortal/transform/PaymentTransformer.java @@ -2,10 +2,8 @@ package org.qortal.transform; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; import org.qortal.data.PaymentData; import org.qortal.utils.Serialization; @@ -15,8 +13,6 @@ public class PaymentTransformer extends Transformer { // Property lengths private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; - private static final int ASSET_ID_LENGTH = LONG_LENGTH; - private static final int AMOUNT_LENGTH = 12; private static final int TOTAL_LENGTH = RECIPIENT_LENGTH + ASSET_ID_LENGTH + AMOUNT_LENGTH; @@ -25,7 +21,7 @@ public class PaymentTransformer extends Transformer { long assetId = byteBuffer.getLong(); - BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer, AMOUNT_LENGTH); + long amount = byteBuffer.getLong(); return new PaymentData(recipient, assetId, amount); } @@ -42,7 +38,7 @@ public class PaymentTransformer extends Transformer { bytes.write(Longs.toByteArray(paymentData.getAssetId())); - Serialization.serializeBigDecimal(bytes, paymentData.getAmount(), AMOUNT_LENGTH); + bytes.write(Longs.toByteArray(paymentData.getAmount())); return bytes.toByteArray(); } catch (IOException | ClassCastException e) { @@ -50,24 +46,4 @@ public class PaymentTransformer extends Transformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(PaymentData paymentData) throws TransformationException { - JSONObject json = new JSONObject(); - - try { - json.put("recipient", paymentData.getRecipient()); - - // For gen1 support: - json.put("asset", paymentData.getAssetId()); - // Gen2 version: - json.put("assetId", paymentData.getAssetId()); - - json.put("amount", paymentData.getAmount().toPlainString()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qortal/transform/Transformer.java b/src/main/java/org/qortal/transform/Transformer.java index 927e45d4..341d545b 100644 --- a/src/main/java/org/qortal/transform/Transformer.java +++ b/src/main/java/org/qortal/transform/Transformer.java @@ -6,7 +6,9 @@ public abstract class Transformer { public static final int BYTE_LENGTH = 1; public static final int INT_LENGTH = 4; public static final int LONG_LENGTH = 8; - public static final int BIG_DECIMAL_LENGTH = 8; + + public static final int ASSET_ID_LENGTH = LONG_LENGTH; + public static final int AMOUNT_LENGTH = LONG_LENGTH; // Raw, not Base58-encoded public static final int ADDRESS_LENGTH = 25; diff --git a/src/main/java/org/qortal/transform/block/BlockTransformer.java b/src/main/java/org/qortal/transform/block/BlockTransformer.java index 86d5036a..6210c207 100644 --- a/src/main/java/org/qortal/transform/block/BlockTransformer.java +++ b/src/main/java/org/qortal/transform/block/BlockTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.block; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; @@ -36,7 +35,6 @@ public class BlockTransformer extends Transformer { private static final int TRANSACTIONS_SIGNATURE_LENGTH = SIGNATURE_LENGTH; private static final int MINTER_SIGNATURE_LENGTH = SIGNATURE_LENGTH; private static final int BLOCK_REFERENCE_LENGTH = MINTER_SIGNATURE_LENGTH + TRANSACTIONS_SIGNATURE_LENGTH; - private static final int TIMESTAMP_LENGTH = LONG_LENGTH; private static final int MINTER_PUBLIC_KEY_LENGTH = PUBLIC_KEY_LENGTH; private static final int TRANSACTION_COUNT_LENGTH = INT_LENGTH; @@ -44,17 +42,19 @@ public class BlockTransformer extends Transformer { + TRANSACTIONS_SIGNATURE_LENGTH + MINTER_SIGNATURE_LENGTH + TRANSACTION_COUNT_LENGTH; public static final int BLOCK_SIGNATURE_LENGTH = MINTER_SIGNATURE_LENGTH + TRANSACTIONS_SIGNATURE_LENGTH; + protected static final int TRANSACTION_SIZE_LENGTH = INT_LENGTH; // per transaction + protected static final int AT_BYTES_LENGTH = INT_LENGTH; - protected static final int AT_FEES_LENGTH = LONG_LENGTH; + protected static final int AT_FEES_LENGTH = AMOUNT_LENGTH; protected static final int AT_LENGTH = AT_FEES_LENGTH + AT_BYTES_LENGTH; protected static final int ONLINE_ACCOUNTS_COUNT_LENGTH = INT_LENGTH; protected static final int ONLINE_ACCOUNTS_SIZE_LENGTH = INT_LENGTH; - protected static final int ONLINE_ACCOUNTS_TIMESTAMP_LENGTH = LONG_LENGTH; + protected static final int ONLINE_ACCOUNTS_TIMESTAMP_LENGTH = TIMESTAMP_LENGTH; protected static final int ONLINE_ACCOUNTS_SIGNATURES_COUNT_LENGTH = INT_LENGTH; - protected static final int AT_ENTRY_LENGTH = ADDRESS_LENGTH + SHA256_LENGTH + BIG_DECIMAL_LENGTH; + protected static final int AT_ENTRY_LENGTH = ADDRESS_LENGTH + SHA256_LENGTH + AMOUNT_LENGTH; /** * Extract block data and transaction data from serialized bytes. @@ -104,10 +104,10 @@ public class BlockTransformer extends Transformer { byte[] minterSignature = new byte[MINTER_SIGNATURE_LENGTH]; byteBuffer.get(minterSignature); - BigDecimal totalFees = BigDecimal.ZERO.setScale(8); + long totalFees = 0; int atCount = 0; - BigDecimal atFees = BigDecimal.ZERO.setScale(8); + long atFees = 0; List atStates = new ArrayList<>(); int atBytesLength = byteBuffer.getInt(); @@ -130,9 +130,10 @@ public class BlockTransformer extends Transformer { byte[] stateHash = new byte[SHA256_LENGTH]; atByteBuffer.get(stateHash); - BigDecimal fees = Serialization.deserializeBigDecimal(atByteBuffer); + long fees = atByteBuffer.getLong(); + // Add this AT's fees to our total - atFees = atFees.add(fees); + atFees += fees; atStates.add(new ATStateData(atAddress, stateHash, fees)); } @@ -141,7 +142,7 @@ public class BlockTransformer extends Transformer { atCount = atStates.size(); // Add AT fees to totalFees - totalFees = totalFees.add(atFees); + totalFees += atFees; int transactionCount = byteBuffer.getInt(); @@ -166,7 +167,7 @@ public class BlockTransformer extends Transformer { TransactionData transactionData = TransactionTransformer.fromBytes(transactionBytes); transactions.add(transactionData); - totalFees = totalFees.add(transactionData.getFee()); + totalFees += transactionData.getFee(); } // Online accounts info? @@ -265,7 +266,7 @@ public class BlockTransformer extends Transformer { for (ATStateData atStateData : block.getATStates()) { bytes.write(Base58.decode(atStateData.getATAddress())); bytes.write(atStateData.getStateHash()); - Serialization.serializeBigDecimal(bytes, atStateData.getFees()); + bytes.write(Longs.toByteArray(atStateData.getFees())); } // Transactions diff --git a/src/main/java/org/qortal/transform/transaction/AccountFlagsTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/AccountFlagsTransactionTransformer.java index 5ba1ebbc..7fe1cb61 100644 --- a/src/main/java/org/qortal/transform/transaction/AccountFlagsTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/AccountFlagsTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.AccountFlagsTransactionData; @@ -13,6 +12,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class AccountFlagsTransactionTransformer extends TransactionTransformer { @@ -57,7 +57,7 @@ public class AccountFlagsTransactionTransformer extends TransactionTransformer { int orMask = byteBuffer.getInt(); int xorMask = byteBuffer.getInt(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -85,7 +85,7 @@ public class AccountFlagsTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(accountFlagsTransactionData.getOrMask())); bytes.write(Ints.toByteArray(accountFlagsTransactionData.getXorMask())); - Serialization.serializeBigDecimal(bytes, accountFlagsTransactionData.getFee()); + bytes.write(Longs.toByteArray(accountFlagsTransactionData.getFee())); if (accountFlagsTransactionData.getSignature() != null) bytes.write(accountFlagsTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/AccountLevelTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/AccountLevelTransactionTransformer.java index e0e14fc9..7572ebf7 100644 --- a/src/main/java/org/qortal/transform/transaction/AccountLevelTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/AccountLevelTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.AccountLevelTransactionData; @@ -12,6 +11,8 @@ import org.qortal.transaction.Transaction.TransactionType; import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; +import com.google.common.primitives.Longs; + public class AccountLevelTransactionTransformer extends TransactionTransformer { @@ -50,7 +51,7 @@ public class AccountLevelTransactionTransformer extends TransactionTransformer { byte level = byteBuffer.get(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -76,7 +77,7 @@ public class AccountLevelTransactionTransformer extends TransactionTransformer { bytes.write(accountLevelTransactionData.getLevel()); - Serialization.serializeBigDecimal(bytes, accountLevelTransactionData.getFee()); + bytes.write(Longs.toByteArray(accountLevelTransactionData.getFee())); if (accountLevelTransactionData.getSignature() != null) bytes.write(accountLevelTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/AddGroupAdminTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/AddGroupAdminTransactionTransformer.java index d597322e..62e011af 100644 --- a/src/main/java/org/qortal/transform/transaction/AddGroupAdminTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/AddGroupAdminTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.AddGroupAdminTransactionData; @@ -13,6 +12,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class AddGroupAdminTransactionTransformer extends TransactionTransformer { @@ -51,7 +51,7 @@ public class AddGroupAdminTransactionTransformer extends TransactionTransformer String member = Serialization.deserializeAddress(byteBuffer); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -77,7 +77,7 @@ public class AddGroupAdminTransactionTransformer extends TransactionTransformer Serialization.serializeAddress(bytes, addGroupAdminTransactionData.getMember()); - Serialization.serializeBigDecimal(bytes, addGroupAdminTransactionData.getFee()); + bytes.write(Longs.toByteArray(addGroupAdminTransactionData.getFee())); if (addGroupAdminTransactionData.getSignature() != null) bytes.write(addGroupAdminTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/ArbitraryTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/ArbitraryTransactionTransformer.java index db7cdc63..3402ca66 100644 --- a/src/main/java/org/qortal/transform/transaction/ArbitraryTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/ArbitraryTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -21,6 +20,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class ArbitraryTransactionTransformer extends TransactionTransformer { @@ -91,7 +91,7 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer { byte[] data = new byte[dataSize]; byteBuffer.get(data); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -133,7 +133,7 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(arbitraryTransactionData.getData().length)); bytes.write(arbitraryTransactionData.getData()); - Serialization.serializeBigDecimal(bytes, arbitraryTransactionData.getFee()); + bytes.write(Longs.toByteArray(arbitraryTransactionData.getFee())); if (arbitraryTransactionData.getSignature() != null) bytes.write(arbitraryTransactionData.getSignature()); @@ -182,7 +182,7 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer { break; } - Serialization.serializeBigDecimal(bytes, arbitraryTransactionData.getFee()); + bytes.write(Longs.toByteArray(arbitraryTransactionData.getFee())); // Never append signature diff --git a/src/main/java/org/qortal/transform/transaction/AtTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/AtTransactionTransformer.java index 6f966f7a..d9c1a586 100644 --- a/src/main/java/org/qortal/transform/transaction/AtTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/AtTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.ATTransactionData; @@ -17,8 +16,6 @@ public class AtTransactionTransformer extends TransactionTransformer { private static final int SENDER_LENGTH = ADDRESS_LENGTH; private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; - private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; - private static final int ASSET_ID_LENGTH = LONG_LENGTH; private static final int DATA_SIZE_LENGTH = INT_LENGTH; private static final int EXTRAS_LENGTH = SENDER_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH + ASSET_ID_LENGTH + DATA_SIZE_LENGTH; @@ -51,8 +48,8 @@ public class AtTransactionTransformer extends TransactionTransformer { Serialization.serializeAddress(bytes, atTransactionData.getRecipient()); // Only emit amount if greater than zero (safer than checking assetId) - if (atTransactionData.getAmount().compareTo(BigDecimal.ZERO) > 0) { - Serialization.serializeBigDecimal(bytes, atTransactionData.getAmount()); + if (atTransactionData.getAmount() > 0) { + bytes.write(Longs.toByteArray(atTransactionData.getAmount())); bytes.write(Longs.toByteArray(atTransactionData.getAssetId())); } @@ -64,7 +61,7 @@ public class AtTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(0)); } - Serialization.serializeBigDecimal(bytes, atTransactionData.getFee()); + bytes.write(Longs.toByteArray(atTransactionData.getFee())); if (atTransactionData.getSignature() != null) bytes.write(atTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/BuyNameTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/BuyNameTransactionTransformer.java index d0e6e1e0..2a00b615 100644 --- a/src/main/java/org/qortal/transform/transaction/BuyNameTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/BuyNameTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -14,12 +13,12 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.base.Utf8; +import com.google.common.primitives.Longs; public class BuyNameTransactionTransformer extends TransactionTransformer { // Property lengths private static final int NAME_SIZE_LENGTH = INT_LENGTH; - private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; private static final int SELLER_LENGTH = ADDRESS_LENGTH; private static final int EXTRAS_LENGTH = NAME_SIZE_LENGTH + AMOUNT_LENGTH + SELLER_LENGTH; @@ -53,11 +52,11 @@ public class BuyNameTransactionTransformer extends TransactionTransformer { String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE); - BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer); + long amount = byteBuffer.getLong(); String seller = Serialization.deserializeAddress(byteBuffer); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -83,11 +82,11 @@ public class BuyNameTransactionTransformer extends TransactionTransformer { Serialization.serializeSizedString(bytes, buyNameTransactionData.getName()); - Serialization.serializeBigDecimal(bytes, buyNameTransactionData.getAmount()); + bytes.write(Longs.toByteArray(buyNameTransactionData.getAmount())); Serialization.serializeAddress(bytes, buyNameTransactionData.getSeller()); - Serialization.serializeBigDecimal(bytes, buyNameTransactionData.getFee()); + bytes.write(Longs.toByteArray(buyNameTransactionData.getFee())); if (buyNameTransactionData.getSignature() != null) bytes.write(buyNameTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/CancelAssetOrderTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/CancelAssetOrderTransactionTransformer.java index 14a93f70..d65a3f37 100644 --- a/src/main/java/org/qortal/transform/transaction/CancelAssetOrderTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/CancelAssetOrderTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -12,6 +11,8 @@ import org.qortal.transaction.Transaction.TransactionType; import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; +import com.google.common.primitives.Longs; + public class CancelAssetOrderTransactionTransformer extends TransactionTransformer { // Property lengths @@ -46,7 +47,7 @@ public class CancelAssetOrderTransactionTransformer extends TransactionTransform byte[] orderId = new byte[ORDER_ID_LENGTH]; byteBuffer.get(orderId); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -70,7 +71,7 @@ public class CancelAssetOrderTransactionTransformer extends TransactionTransform bytes.write(cancelOrderTransactionData.getOrderId()); - Serialization.serializeBigDecimal(bytes, cancelOrderTransactionData.getFee()); + bytes.write(Longs.toByteArray(cancelOrderTransactionData.getFee())); if (cancelOrderTransactionData.getSignature() != null) bytes.write(cancelOrderTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/CancelGroupBanTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/CancelGroupBanTransactionTransformer.java index f2ab25dc..1444c890 100644 --- a/src/main/java/org/qortal/transform/transaction/CancelGroupBanTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/CancelGroupBanTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -13,6 +12,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class CancelGroupBanTransactionTransformer extends TransactionTransformer { @@ -51,7 +51,7 @@ public class CancelGroupBanTransactionTransformer extends TransactionTransformer String member = Serialization.deserializeAddress(byteBuffer); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -77,7 +77,7 @@ public class CancelGroupBanTransactionTransformer extends TransactionTransformer Serialization.serializeAddress(bytes, groupUnbanTransactionData.getMember()); - Serialization.serializeBigDecimal(bytes, groupUnbanTransactionData.getFee()); + bytes.write(Longs.toByteArray(groupUnbanTransactionData.getFee())); if (groupUnbanTransactionData.getSignature() != null) bytes.write(groupUnbanTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/CancelGroupInviteTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/CancelGroupInviteTransactionTransformer.java index bd42307b..7cacbaa0 100644 --- a/src/main/java/org/qortal/transform/transaction/CancelGroupInviteTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/CancelGroupInviteTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -13,6 +12,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class CancelGroupInviteTransactionTransformer extends TransactionTransformer { @@ -51,7 +51,7 @@ public class CancelGroupInviteTransactionTransformer extends TransactionTransfor String invitee = Serialization.deserializeAddress(byteBuffer); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -77,7 +77,7 @@ public class CancelGroupInviteTransactionTransformer extends TransactionTransfor Serialization.serializeAddress(bytes, cancelGroupInviteTransactionData.getInvitee()); - Serialization.serializeBigDecimal(bytes, cancelGroupInviteTransactionData.getFee()); + bytes.write(Longs.toByteArray(cancelGroupInviteTransactionData.getFee())); if (cancelGroupInviteTransactionData.getSignature() != null) bytes.write(cancelGroupInviteTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/CancelSellNameTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/CancelSellNameTransactionTransformer.java index 1f766d76..c91a78a4 100644 --- a/src/main/java/org/qortal/transform/transaction/CancelSellNameTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/CancelSellNameTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -14,6 +13,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.base.Utf8; +import com.google.common.primitives.Longs; public class CancelSellNameTransactionTransformer extends TransactionTransformer { @@ -49,7 +49,7 @@ public class CancelSellNameTransactionTransformer extends TransactionTransformer String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -75,7 +75,7 @@ public class CancelSellNameTransactionTransformer extends TransactionTransformer Serialization.serializeSizedString(bytes, cancelSellNameTransactionData.getName()); - Serialization.serializeBigDecimal(bytes, cancelSellNameTransactionData.getFee()); + bytes.write(Longs.toByteArray(cancelSellNameTransactionData.getFee())); if (cancelSellNameTransactionData.getSignature() != null) bytes.write(cancelSellNameTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/CreateAssetOrderTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/CreateAssetOrderTransactionTransformer.java index ab56f3b9..29363332 100644 --- a/src/main/java/org/qortal/transform/transaction/CreateAssetOrderTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/CreateAssetOrderTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -17,8 +16,6 @@ import com.google.common.primitives.Longs; public class CreateAssetOrderTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int ASSET_ID_LENGTH = LONG_LENGTH; - private static final int AMOUNT_LENGTH = 12; // Not standard BIG_DECIMAL_LENGTH private static final int EXTRAS_LENGTH = (ASSET_ID_LENGTH + AMOUNT_LENGTH) * 2; @@ -33,8 +30,8 @@ public class CreateAssetOrderTransactionTransformer extends TransactionTransform layout.add("order creator's public key", TransformationType.PUBLIC_KEY); layout.add("ID of asset of offer", TransformationType.LONG); layout.add("ID of asset wanted", TransformationType.LONG); - layout.add("amount of asset on offer", TransformationType.ASSET_QUANTITY); - layout.add("trade price in (lowest-assetID asset)/(highest-assetID asset)", TransformationType.ASSET_QUANTITY); + layout.add("amount of asset on offer", TransformationType.AMOUNT); + layout.add("trade price in (lowest-assetID asset)/(highest-assetID asset)", TransformationType.AMOUNT); layout.add("fee", TransformationType.AMOUNT); layout.add("signature", TransformationType.SIGNATURE); } @@ -53,11 +50,11 @@ public class CreateAssetOrderTransactionTransformer extends TransactionTransform long wantAssetId = byteBuffer.getLong(); - BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer, AMOUNT_LENGTH); + long amount = byteBuffer.getLong(); - BigDecimal price = Serialization.deserializeBigDecimal(byteBuffer, AMOUNT_LENGTH); + long price = byteBuffer.getLong(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -83,11 +80,11 @@ public class CreateAssetOrderTransactionTransformer extends TransactionTransform bytes.write(Longs.toByteArray(createOrderTransactionData.getWantAssetId())); - Serialization.serializeBigDecimal(bytes, createOrderTransactionData.getAmount(), AMOUNT_LENGTH); + bytes.write(Longs.toByteArray(createOrderTransactionData.getAmount())); - Serialization.serializeBigDecimal(bytes, createOrderTransactionData.getPrice(), AMOUNT_LENGTH); + bytes.write(Longs.toByteArray(createOrderTransactionData.getPrice())); - Serialization.serializeBigDecimal(bytes, createOrderTransactionData.getFee()); + bytes.write(Longs.toByteArray(createOrderTransactionData.getFee())); if (createOrderTransactionData.getSignature() != null) bytes.write(createOrderTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/CreateGroupTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/CreateGroupTransactionTransformer.java index 845b0220..8a3d16f0 100644 --- a/src/main/java/org/qortal/transform/transaction/CreateGroupTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/CreateGroupTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -16,6 +15,7 @@ import org.qortal.utils.Serialization; import com.google.common.base.Utf8; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class CreateGroupTransactionTransformer extends TransactionTransformer { @@ -76,7 +76,7 @@ public class CreateGroupTransactionTransformer extends TransactionTransformer { int maxBlockDelay = byteBuffer.getInt(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -115,7 +115,7 @@ public class CreateGroupTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(createGroupTransactionData.getMaximumBlockDelay())); - Serialization.serializeBigDecimal(bytes, createGroupTransactionData.getFee()); + bytes.write(Longs.toByteArray(createGroupTransactionData.getFee())); if (createGroupTransactionData.getSignature() != null) bytes.write(createGroupTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/CreatePollTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/CreatePollTransactionTransformer.java index b324ff57..442693df 100644 --- a/src/main/java/org/qortal/transform/transaction/CreatePollTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/CreatePollTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -18,6 +17,7 @@ import org.qortal.voting.Poll; import com.google.common.base.Utf8; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class CreatePollTransactionTransformer extends TransactionTransformer { @@ -77,7 +77,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer { pollOptions.add(new PollOptionData(optionName)); } - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -121,7 +121,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer { for (PollOptionData pollOptionData : pollOptions) Serialization.serializeSizedString(bytes, pollOptionData.getOptionName()); - Serialization.serializeBigDecimal(bytes, createPollTransactionData.getFee()); + bytes.write(Longs.toByteArray(createPollTransactionData.getFee())); if (createPollTransactionData.getSignature() != null) bytes.write(createPollTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/DeployAtTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/DeployAtTransactionTransformer.java index 4ff7e886..4a201338 100644 --- a/src/main/java/org/qortal/transform/transaction/DeployAtTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/DeployAtTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -25,8 +24,6 @@ public class DeployAtTransactionTransformer extends TransactionTransformer { private static final int AT_TYPE_SIZE_LENGTH = INT_LENGTH; private static final int TAGS_SIZE_LENGTH = INT_LENGTH; private static final int CREATION_BYTES_SIZE_LENGTH = INT_LENGTH; - private static final int AMOUNT_LENGTH = LONG_LENGTH; - private static final int ASSET_ID_LENGTH = LONG_LENGTH; private static final int EXTRAS_LENGTH = NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH + AT_TYPE_SIZE_LENGTH + TAGS_SIZE_LENGTH + CREATION_BYTES_SIZE_LENGTH + AMOUNT_LENGTH + ASSET_ID_LENGTH; @@ -68,7 +65,7 @@ public class DeployAtTransactionTransformer extends TransactionTransformer { String description = Serialization.deserializeSizedString(byteBuffer, DeployAtTransaction.MAX_DESCRIPTION_SIZE); - String ATType = Serialization.deserializeSizedString(byteBuffer, DeployAtTransaction.MAX_AT_TYPE_SIZE); + String atType = Serialization.deserializeSizedString(byteBuffer, DeployAtTransaction.MAX_AT_TYPE_SIZE); String tags = Serialization.deserializeSizedString(byteBuffer, DeployAtTransaction.MAX_TAGS_SIZE); @@ -79,18 +76,18 @@ public class DeployAtTransactionTransformer extends TransactionTransformer { byte[] creationBytes = new byte[creationBytesSize]; byteBuffer.get(creationBytes); - BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer); + long amount = byteBuffer.getLong(); long assetId = byteBuffer.getLong(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, creatorPublicKey, fee, signature); - return new DeployAtTransactionData(baseTransactionData, name, description, ATType, tags, creationBytes, amount, assetId); + return new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, amount, assetId); } public static int getDataLength(TransactionData transactionData) throws TransformationException { @@ -125,11 +122,11 @@ public class DeployAtTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(creationBytes.length)); bytes.write(creationBytes); - Serialization.serializeBigDecimal(bytes, deployATTransactionData.getAmount()); + bytes.write(Longs.toByteArray(deployATTransactionData.getAmount())); bytes.write(Longs.toByteArray(deployATTransactionData.getAssetId())); - Serialization.serializeBigDecimal(bytes, deployATTransactionData.getFee()); + bytes.write(Longs.toByteArray(deployATTransactionData.getFee())); if (deployATTransactionData.getSignature() != null) bytes.write(deployATTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/GenesisTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/GenesisTransactionTransformer.java index 4527d186..89f7cf16 100644 --- a/src/main/java/org/qortal/transform/transaction/GenesisTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/GenesisTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.account.NullAccount; @@ -23,8 +22,6 @@ public class GenesisTransactionTransformer extends TransactionTransformer { // Property lengths private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; - private static final int AMOUNT_LENGTH = LONG_LENGTH; - private static final int ASSET_ID_LENGTH = LONG_LENGTH; private static final int TOTAL_LENGTH = TYPE_LENGTH + TIMESTAMP_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH + ASSET_ID_LENGTH; @@ -45,11 +42,11 @@ public class GenesisTransactionTransformer extends TransactionTransformer { String recipient = Serialization.deserializeAddress(byteBuffer); - BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer); + long amount = byteBuffer.getLong(); long assetId = byteBuffer.getLong(); - BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, null, NullAccount.PUBLIC_KEY, BigDecimal.ZERO, null); + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, null, NullAccount.PUBLIC_KEY, 0L, null); return new GenesisTransactionData(baseTransactionData, recipient, amount, assetId); } @@ -70,7 +67,7 @@ public class GenesisTransactionTransformer extends TransactionTransformer { Serialization.serializeAddress(bytes, genesisTransactionData.getRecipient()); - Serialization.serializeBigDecimal(bytes, genesisTransactionData.getAmount()); + bytes.write(Longs.toByteArray(genesisTransactionData.getAmount())); bytes.write(Longs.toByteArray(genesisTransactionData.getAssetId())); diff --git a/src/main/java/org/qortal/transform/transaction/GroupApprovalTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/GroupApprovalTransactionTransformer.java index 5eec7d54..227e291a 100644 --- a/src/main/java/org/qortal/transform/transaction/GroupApprovalTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/GroupApprovalTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -12,6 +11,8 @@ import org.qortal.transaction.Transaction.TransactionType; import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; +import com.google.common.primitives.Longs; + public class GroupApprovalTransactionTransformer extends TransactionTransformer { // Property lengths @@ -50,7 +51,7 @@ public class GroupApprovalTransactionTransformer extends TransactionTransformer boolean approval = byteBuffer.get() != 0; - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -76,7 +77,7 @@ public class GroupApprovalTransactionTransformer extends TransactionTransformer bytes.write((byte) (groupApprovalTransactionData.getApproval() ? 1 : 0)); - Serialization.serializeBigDecimal(bytes, groupApprovalTransactionData.getFee()); + bytes.write(Longs.toByteArray(groupApprovalTransactionData.getFee())); if (groupApprovalTransactionData.getSignature() != null) bytes.write(groupApprovalTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/GroupBanTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/GroupBanTransactionTransformer.java index f9ddfae6..9454390b 100644 --- a/src/main/java/org/qortal/transform/transaction/GroupBanTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/GroupBanTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -15,6 +14,7 @@ import org.qortal.utils.Serialization; import com.google.common.base.Utf8; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class GroupBanTransactionTransformer extends TransactionTransformer { @@ -62,7 +62,7 @@ public class GroupBanTransactionTransformer extends TransactionTransformer { int timeToLive = byteBuffer.getInt(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -94,7 +94,7 @@ public class GroupBanTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(groupBanTransactionData.getTimeToLive())); - Serialization.serializeBigDecimal(bytes, groupBanTransactionData.getFee()); + bytes.write(Longs.toByteArray(groupBanTransactionData.getFee())); if (groupBanTransactionData.getSignature() != null) bytes.write(groupBanTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/GroupInviteTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/GroupInviteTransactionTransformer.java index f0384e47..93d2c895 100644 --- a/src/main/java/org/qortal/transform/transaction/GroupInviteTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/GroupInviteTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -13,6 +12,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class GroupInviteTransactionTransformer extends TransactionTransformer { @@ -55,7 +55,7 @@ public class GroupInviteTransactionTransformer extends TransactionTransformer { int timeToLive = byteBuffer.getInt(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -83,7 +83,7 @@ public class GroupInviteTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(groupInviteTransactionData.getTimeToLive())); - Serialization.serializeBigDecimal(bytes, groupInviteTransactionData.getFee()); + bytes.write(Longs.toByteArray(groupInviteTransactionData.getFee())); if (groupInviteTransactionData.getSignature() != null) bytes.write(groupInviteTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/GroupKickTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/GroupKickTransactionTransformer.java index 8c07c658..0e5ac2f2 100644 --- a/src/main/java/org/qortal/transform/transaction/GroupKickTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/GroupKickTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -15,6 +14,7 @@ import org.qortal.utils.Serialization; import com.google.common.base.Utf8; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class GroupKickTransactionTransformer extends TransactionTransformer { @@ -58,7 +58,7 @@ public class GroupKickTransactionTransformer extends TransactionTransformer { String reason = Serialization.deserializeSizedString(byteBuffer, Group.MAX_REASON_SIZE); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -88,7 +88,7 @@ public class GroupKickTransactionTransformer extends TransactionTransformer { Serialization.serializeSizedString(bytes, groupKickTransactionData.getReason()); - Serialization.serializeBigDecimal(bytes, groupKickTransactionData.getFee()); + bytes.write(Longs.toByteArray(groupKickTransactionData.getFee())); if (groupKickTransactionData.getSignature() != null) bytes.write(groupKickTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/IssueAssetTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/IssueAssetTransactionTransformer.java index f4419fb0..c420dbef 100644 --- a/src/main/java/org/qortal/transform/transaction/IssueAssetTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/IssueAssetTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.asset.Asset; @@ -22,7 +21,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { private static final int OWNER_LENGTH = ADDRESS_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; private static final int DESCRIPTION_SIZE_LENGTH = INT_LENGTH; - private static final int QUANTITY_LENGTH = LONG_LENGTH; + private static final int QUANTITY_LENGTH = AMOUNT_LENGTH; private static final int IS_DIVISIBLE_LENGTH = BOOLEAN_LENGTH; private static final int DATA_SIZE_LENGTH = INT_LENGTH; private static final int IS_UNSPENDABLE_LENGTH = BOOLEAN_LENGTH; @@ -44,7 +43,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { layout.add("asset name", TransformationType.STRING); layout.add("asset description length", TransformationType.INT); layout.add("asset description", TransformationType.STRING); - layout.add("asset quantity", TransformationType.LONG); + layout.add("asset quantity", TransformationType.AMOUNT); layout.add("can asset quantities be fractional?", TransformationType.BOOLEAN); layout.add("asset data length", TransformationType.INT); layout.add("asset data", TransformationType.STRING); @@ -77,7 +76,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { boolean isUnspendable = byteBuffer.get() != 0; - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -117,7 +116,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { bytes.write((byte) (issueAssetTransactionData.getIsUnspendable() ? 1 : 0)); - Serialization.serializeBigDecimal(bytes, issueAssetTransactionData.getFee()); + bytes.write(Longs.toByteArray(issueAssetTransactionData.getFee())); if (issueAssetTransactionData.getSignature() != null) bytes.write(issueAssetTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/JoinGroupTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/JoinGroupTransactionTransformer.java index 8ccbedcf..a4f33e48 100644 --- a/src/main/java/org/qortal/transform/transaction/JoinGroupTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/JoinGroupTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -13,6 +12,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class JoinGroupTransactionTransformer extends TransactionTransformer { @@ -47,7 +47,7 @@ public class JoinGroupTransactionTransformer extends TransactionTransformer { int groupId = byteBuffer.getInt(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -71,7 +71,7 @@ public class JoinGroupTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(joinGroupTransactionData.getGroupId())); - Serialization.serializeBigDecimal(bytes, joinGroupTransactionData.getFee()); + bytes.write(Longs.toByteArray(joinGroupTransactionData.getFee())); if (joinGroupTransactionData.getSignature() != null) bytes.write(joinGroupTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/LeaveGroupTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/LeaveGroupTransactionTransformer.java index 9e0cc1a4..ee42ffeb 100644 --- a/src/main/java/org/qortal/transform/transaction/LeaveGroupTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/LeaveGroupTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -13,6 +12,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class LeaveGroupTransactionTransformer extends TransactionTransformer { @@ -47,7 +47,7 @@ public class LeaveGroupTransactionTransformer extends TransactionTransformer { int groupId = byteBuffer.getInt(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -71,7 +71,7 @@ public class LeaveGroupTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(leaveGroupTransactionData.getGroupId())); - Serialization.serializeBigDecimal(bytes, leaveGroupTransactionData.getFee()); + bytes.write(Longs.toByteArray(leaveGroupTransactionData.getFee())); if (leaveGroupTransactionData.getSignature() != null) bytes.write(leaveGroupTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/MessageTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/MessageTransactionTransformer.java index 48ff742f..7a008521 100644 --- a/src/main/java/org/qortal/transform/transaction/MessageTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/MessageTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -21,8 +20,6 @@ public class MessageTransactionTransformer extends TransactionTransformer { // Property lengths private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; - private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; - private static final int ASSET_ID_LENGTH = LONG_LENGTH; private static final int DATA_SIZE_LENGTH = INT_LENGTH; private static final int IS_TEXT_LENGTH = BOOLEAN_LENGTH; private static final int IS_ENCRYPTED_LENGTH = BOOLEAN_LENGTH; @@ -65,7 +62,7 @@ public class MessageTransactionTransformer extends TransactionTransformer { long assetId = byteBuffer.getLong(); - BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer); + long amount = byteBuffer.getLong(); int dataSize = byteBuffer.getInt(); // Don't allow invalid dataSize here to avoid run-time issues @@ -79,7 +76,7 @@ public class MessageTransactionTransformer extends TransactionTransformer { boolean isText = byteBuffer.get() != 0; - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -107,7 +104,7 @@ public class MessageTransactionTransformer extends TransactionTransformer { bytes.write(Longs.toByteArray(messageTransactionData.getAssetId())); - Serialization.serializeBigDecimal(bytes, messageTransactionData.getAmount()); + bytes.write(Longs.toByteArray(messageTransactionData.getAmount())); bytes.write(Ints.toByteArray(messageTransactionData.getData().length)); @@ -117,7 +114,7 @@ public class MessageTransactionTransformer extends TransactionTransformer { bytes.write((byte) (messageTransactionData.getIsText() ? 1 : 0)); - Serialization.serializeBigDecimal(bytes, messageTransactionData.getFee()); + bytes.write(Longs.toByteArray(messageTransactionData.getFee())); if (messageTransactionData.getSignature() != null) bytes.write(messageTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/MultiPaymentTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/MultiPaymentTransactionTransformer.java index 1d7612f3..1f77243f 100644 --- a/src/main/java/org/qortal/transform/transaction/MultiPaymentTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/MultiPaymentTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -17,6 +16,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class MultiPaymentTransactionTransformer extends TransactionTransformer { @@ -58,7 +58,7 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer { for (int i = 0; i < paymentsCount; ++i) payments.add(PaymentTransformer.fromByteBuffer(byteBuffer)); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -88,7 +88,7 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer { for (PaymentData paymentData : payments) bytes.write(PaymentTransformer.toBytes(paymentData)); - Serialization.serializeBigDecimal(bytes, multiPaymentTransactionData.getFee()); + bytes.write(Longs.toByteArray(multiPaymentTransactionData.getFee())); if (multiPaymentTransactionData.getSignature() != null) bytes.write(multiPaymentTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/PaymentTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/PaymentTransactionTransformer.java index c38b5e8e..af194470 100644 --- a/src/main/java/org/qortal/transform/transaction/PaymentTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/PaymentTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -12,11 +11,12 @@ import org.qortal.transaction.Transaction.TransactionType; import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; +import com.google.common.primitives.Longs; + public class PaymentTransactionTransformer extends TransactionTransformer { // Property lengths private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; - private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; private static final int EXTRAS_LENGTH = RECIPIENT_LENGTH + AMOUNT_LENGTH; @@ -47,9 +47,9 @@ public class PaymentTransactionTransformer extends TransactionTransformer { String recipient = Serialization.deserializeAddress(byteBuffer); - BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer); + long amount = byteBuffer.getLong(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -73,9 +73,9 @@ public class PaymentTransactionTransformer extends TransactionTransformer { Serialization.serializeAddress(bytes, paymentTransactionData.getRecipient()); - Serialization.serializeBigDecimal(bytes, paymentTransactionData.getAmount()); + bytes.write(Longs.toByteArray(paymentTransactionData.getAmount())); - Serialization.serializeBigDecimal(bytes, paymentTransactionData.getFee()); + bytes.write(Longs.toByteArray(paymentTransactionData.getFee())); if (paymentTransactionData.getSignature() != null) bytes.write(paymentTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/RegisterNameTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/RegisterNameTransactionTransformer.java index ce15e46e..1bf4057e 100644 --- a/src/main/java/org/qortal/transform/transaction/RegisterNameTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/RegisterNameTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -14,6 +13,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.base.Utf8; +import com.google.common.primitives.Longs; public class RegisterNameTransactionTransformer extends TransactionTransformer { @@ -58,7 +58,7 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { String data = Serialization.deserializeSizedString(byteBuffer, Name.MAX_DATA_SIZE); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -89,7 +89,7 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { Serialization.serializeSizedString(bytes, registerNameTransactionData.getData()); - Serialization.serializeBigDecimal(bytes, registerNameTransactionData.getFee()); + bytes.write(Longs.toByteArray(registerNameTransactionData.getFee())); if (registerNameTransactionData.getSignature() != null) bytes.write(registerNameTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/RemoveGroupAdminTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/RemoveGroupAdminTransactionTransformer.java index 6d7de32e..58a8d07d 100644 --- a/src/main/java/org/qortal/transform/transaction/RemoveGroupAdminTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/RemoveGroupAdminTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -13,6 +12,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class RemoveGroupAdminTransactionTransformer extends TransactionTransformer { @@ -51,7 +51,7 @@ public class RemoveGroupAdminTransactionTransformer extends TransactionTransform String admin = Serialization.deserializeAddress(byteBuffer); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -77,7 +77,7 @@ public class RemoveGroupAdminTransactionTransformer extends TransactionTransform Serialization.serializeAddress(bytes, removeGroupAdminTransactionData.getAdmin()); - Serialization.serializeBigDecimal(bytes, removeGroupAdminTransactionData.getFee()); + bytes.write(Longs.toByteArray(removeGroupAdminTransactionData.getFee())); if (removeGroupAdminTransactionData.getSignature() != null) bytes.write(removeGroupAdminTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/RewardShareTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/RewardShareTransactionTransformer.java index 0114fa3b..3611e07e 100644 --- a/src/main/java/org/qortal/transform/transaction/RewardShareTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/RewardShareTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -12,11 +11,13 @@ import org.qortal.transaction.Transaction.TransactionType; import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; +import com.google.common.primitives.Longs; + public class RewardShareTransactionTransformer extends TransactionTransformer { // Property lengths private static final int TARGET_LENGTH = ADDRESS_LENGTH; - private static final int REWARD_SHARE_LENGTH = BIG_DECIMAL_LENGTH; + private static final int REWARD_SHARE_LENGTH = LONG_LENGTH; private static final int EXTRAS_LENGTH = TARGET_LENGTH + PUBLIC_KEY_LENGTH + REWARD_SHARE_LENGTH; @@ -31,7 +32,8 @@ public class RewardShareTransactionTransformer extends TransactionTransformer { layout.add("minter's public key", TransformationType.PUBLIC_KEY); layout.add("recipient account's address", TransformationType.ADDRESS); layout.add("reward-share public key", TransformationType.PUBLIC_KEY); - layout.add("recipient's percentage share of block rewards", TransformationType.AMOUNT); + // XXX This needs to become an int, scaled *100 + layout.add("recipient's percentage share of block rewards (unscaled)", TransformationType.LONG); layout.add("fee", TransformationType.AMOUNT); layout.add("signature", TransformationType.SIGNATURE); } @@ -50,9 +52,9 @@ public class RewardShareTransactionTransformer extends TransactionTransformer { byte[] rewardSharePublicKey = Serialization.deserializePublicKey(byteBuffer); - BigDecimal sharePercent = Serialization.deserializeBigDecimal(byteBuffer); + int sharePercent = (int) byteBuffer.getLong(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -78,9 +80,9 @@ public class RewardShareTransactionTransformer extends TransactionTransformer { bytes.write(rewardShareTransactionData.getRewardSharePublicKey()); - Serialization.serializeBigDecimal(bytes, rewardShareTransactionData.getSharePercent()); + bytes.write(Longs.toByteArray(rewardShareTransactionData.getSharePercent())); - Serialization.serializeBigDecimal(bytes, rewardShareTransactionData.getFee()); + bytes.write(Longs.toByteArray(rewardShareTransactionData.getFee())); if (rewardShareTransactionData.getSignature() != null) bytes.write(rewardShareTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/SellNameTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/SellNameTransactionTransformer.java index 211de889..3fded96e 100644 --- a/src/main/java/org/qortal/transform/transaction/SellNameTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/SellNameTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -14,12 +13,12 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.base.Utf8; +import com.google.common.primitives.Longs; public class SellNameTransactionTransformer extends TransactionTransformer { // Property lengths private static final int NAME_SIZE_LENGTH = INT_LENGTH; - private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; private static final int EXTRAS_LENGTH = NAME_SIZE_LENGTH + AMOUNT_LENGTH; @@ -51,9 +50,9 @@ public class SellNameTransactionTransformer extends TransactionTransformer { String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE); - BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer); + long amount = byteBuffer.getLong(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -79,9 +78,9 @@ public class SellNameTransactionTransformer extends TransactionTransformer { Serialization.serializeSizedString(bytes, sellNameTransactionData.getName()); - Serialization.serializeBigDecimal(bytes, sellNameTransactionData.getAmount()); + bytes.write(Longs.toByteArray(sellNameTransactionData.getAmount())); - Serialization.serializeBigDecimal(bytes, sellNameTransactionData.getFee()); + bytes.write(Longs.toByteArray(sellNameTransactionData.getFee())); if (sellNameTransactionData.getSignature() != null) bytes.write(sellNameTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/SetGroupTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/SetGroupTransactionTransformer.java index f110a33a..586e084a 100644 --- a/src/main/java/org/qortal/transform/transaction/SetGroupTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/SetGroupTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -13,6 +12,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class SetGroupTransactionTransformer extends TransactionTransformer { @@ -47,7 +47,7 @@ public class SetGroupTransactionTransformer extends TransactionTransformer { int defaultGroupId = byteBuffer.getInt(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -71,7 +71,7 @@ public class SetGroupTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(setGroupTransactionData.getDefaultGroupId())); - Serialization.serializeBigDecimal(bytes, setGroupTransactionData.getFee()); + bytes.write(Longs.toByteArray(setGroupTransactionData.getFee())); if (setGroupTransactionData.getSignature() != null) bytes.write(setGroupTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/TransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/TransactionTransformer.java index 96b35cb0..ae029eaf 100644 --- a/src/main/java/org/qortal/transform/transaction/TransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/TransactionTransformer.java @@ -35,7 +35,7 @@ public abstract class TransactionTransformer extends Transformer { protected static final int TYPE_LENGTH = INT_LENGTH; protected static final int GROUPID_LENGTH = INT_LENGTH; protected static final int REFERENCE_LENGTH = SIGNATURE_LENGTH; - protected static final int FEE_LENGTH = BIG_DECIMAL_LENGTH; + protected static final int FEE_LENGTH = AMOUNT_LENGTH; /** Description of one component of raw transaction layout */ public enum TransformationType { @@ -43,8 +43,7 @@ public abstract class TransactionTransformer extends Transformer { SIGNATURE("transaction signature", SIGNATURE_LENGTH), PUBLIC_KEY("public key", PUBLIC_KEY_LENGTH), ADDRESS("address", ADDRESS_LENGTH), - AMOUNT("amount", BIG_DECIMAL_LENGTH), - ASSET_QUANTITY("asset-related quantity", 12), + AMOUNT("unscaled amount", AMOUNT_LENGTH), INT("int", INT_LENGTH), LONG("long", LONG_LENGTH), STRING("UTF-8 string of variable length", null), diff --git a/src/main/java/org/qortal/transform/transaction/TransferAssetTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/TransferAssetTransactionTransformer.java index ff07c026..1f124289 100644 --- a/src/main/java/org/qortal/transform/transaction/TransferAssetTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/TransferAssetTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -18,8 +17,6 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer // Property lengths private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; - private static final int ASSET_ID_LENGTH = LONG_LENGTH; - private static final int AMOUNT_LENGTH = 12; private static final int EXTRAS_LENGTH = RECIPIENT_LENGTH + ASSET_ID_LENGTH + AMOUNT_LENGTH; @@ -34,7 +31,7 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer layout.add("asset owner's public key", TransformationType.PUBLIC_KEY); layout.add("recipient", TransformationType.ADDRESS); layout.add("asset ID", TransformationType.LONG); - layout.add("asset quantity", TransformationType.ASSET_QUANTITY); + layout.add("asset quantity", TransformationType.AMOUNT); layout.add("fee", TransformationType.AMOUNT); layout.add("signature", TransformationType.SIGNATURE); } @@ -53,9 +50,9 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer long assetId = byteBuffer.getLong(); - BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer, AMOUNT_LENGTH); + long amount = byteBuffer.getLong(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -81,9 +78,9 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer bytes.write(Longs.toByteArray(transferAssetTransactionData.getAssetId())); - Serialization.serializeBigDecimal(bytes, transferAssetTransactionData.getAmount(), AMOUNT_LENGTH); + bytes.write(Longs.toByteArray(transferAssetTransactionData.getAmount())); - Serialization.serializeBigDecimal(bytes, transferAssetTransactionData.getFee()); + bytes.write(Longs.toByteArray(transferAssetTransactionData.getFee())); if (transferAssetTransactionData.getSignature() != null) bytes.write(transferAssetTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/TransferPrivsTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/TransferPrivsTransactionTransformer.java index 47fa4d0d..59c3dd2a 100644 --- a/src/main/java/org/qortal/transform/transaction/TransferPrivsTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/TransferPrivsTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -12,6 +11,8 @@ import org.qortal.transaction.Transaction.TransactionType; import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; +import com.google.common.primitives.Longs; + public class TransferPrivsTransactionTransformer extends TransactionTransformer { // Property lengths @@ -45,7 +46,7 @@ public class TransferPrivsTransactionTransformer extends TransactionTransformer String recipient = Serialization.deserializeAddress(byteBuffer); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -69,7 +70,7 @@ public class TransferPrivsTransactionTransformer extends TransactionTransformer Serialization.serializeAddress(bytes, transferPrivsTransactionData.getRecipient()); - Serialization.serializeBigDecimal(bytes, transferPrivsTransactionData.getFee()); + bytes.write(Longs.toByteArray(transferPrivsTransactionData.getFee())); if (transferPrivsTransactionData.getSignature() != null) bytes.write(transferPrivsTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/UpdateAssetTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/UpdateAssetTransactionTransformer.java index 250c1bdc..ba27d939 100644 --- a/src/main/java/org/qortal/transform/transaction/UpdateAssetTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/UpdateAssetTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.asset.Asset; @@ -19,7 +18,6 @@ import com.google.common.primitives.Longs; public class UpdateAssetTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int ASSET_ID_LENGTH = LONG_LENGTH; private static final int NEW_OWNER_LENGTH = ADDRESS_LENGTH; private static final int NEW_DESCRIPTION_SIZE_LENGTH = INT_LENGTH; private static final int NEW_DATA_SIZE_LENGTH = INT_LENGTH; @@ -64,7 +62,7 @@ public class UpdateAssetTransactionTransformer extends TransactionTransformer { String newData = Serialization.deserializeSizedString(byteBuffer, Asset.MAX_DATA_SIZE); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -98,7 +96,7 @@ public class UpdateAssetTransactionTransformer extends TransactionTransformer { Serialization.serializeSizedString(bytes, updateAssetTransactionData.getNewData()); - Serialization.serializeBigDecimal(bytes, updateAssetTransactionData.getFee()); + bytes.write(Longs.toByteArray(updateAssetTransactionData.getFee())); if (updateAssetTransactionData.getSignature() != null) bytes.write(updateAssetTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/UpdateGroupTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/UpdateGroupTransactionTransformer.java index 7fa2320e..673694c3 100644 --- a/src/main/java/org/qortal/transform/transaction/UpdateGroupTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/UpdateGroupTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -16,6 +15,7 @@ import org.qortal.utils.Serialization; import com.google.common.base.Utf8; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class UpdateGroupTransactionTransformer extends TransactionTransformer { @@ -76,7 +76,7 @@ public class UpdateGroupTransactionTransformer extends TransactionTransformer { int newMaxBlockDelay = byteBuffer.getInt(); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -115,7 +115,7 @@ public class UpdateGroupTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(updateGroupTransactionData.getNewMaximumBlockDelay())); - Serialization.serializeBigDecimal(bytes, updateGroupTransactionData.getFee()); + bytes.write(Longs.toByteArray(updateGroupTransactionData.getFee())); if (updateGroupTransactionData.getSignature() != null) bytes.write(updateGroupTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/UpdateNameTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/UpdateNameTransactionTransformer.java index e9e843cc..56a4f988 100644 --- a/src/main/java/org/qortal/transform/transaction/UpdateNameTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/UpdateNameTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -14,6 +13,7 @@ import org.qortal.transform.TransformationException; import org.qortal.utils.Serialization; import com.google.common.base.Utf8; +import com.google.common.primitives.Longs; public class UpdateNameTransactionTransformer extends TransactionTransformer { @@ -58,7 +58,7 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { String newData = Serialization.deserializeSizedString(byteBuffer, Name.MAX_DATA_SIZE); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -89,7 +89,7 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { Serialization.serializeSizedString(bytes, updateNameTransactionData.getNewData()); - Serialization.serializeBigDecimal(bytes, updateNameTransactionData.getFee()); + bytes.write(Longs.toByteArray(updateNameTransactionData.getFee())); if (updateNameTransactionData.getSignature() != null) bytes.write(updateNameTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/transform/transaction/VoteOnPollTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/VoteOnPollTransactionTransformer.java index e256d290..10045e3f 100644 --- a/src/main/java/org/qortal/transform/transaction/VoteOnPollTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/VoteOnPollTransactionTransformer.java @@ -2,7 +2,6 @@ package org.qortal.transform.transaction; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import org.qortal.data.transaction.BaseTransactionData; @@ -15,6 +14,7 @@ import org.qortal.voting.Poll; import com.google.common.base.Utf8; import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public class VoteOnPollTransactionTransformer extends TransactionTransformer { @@ -56,7 +56,7 @@ public class VoteOnPollTransactionTransformer extends TransactionTransformer { if (optionIndex < 0 || optionIndex >= Poll.MAX_OPTIONS) throw new TransformationException("Invalid option number for VoteOnPollTransaction"); - BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + long fee = byteBuffer.getLong(); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); @@ -84,7 +84,7 @@ public class VoteOnPollTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(voteOnPollTransactionData.getOptionIndex())); - Serialization.serializeBigDecimal(bytes, voteOnPollTransactionData.getFee()); + bytes.write(Longs.toByteArray(voteOnPollTransactionData.getFee())); if (voteOnPollTransactionData.getSignature() != null) bytes.write(voteOnPollTransactionData.getSignature()); diff --git a/src/main/java/org/qortal/utils/Amounts.java b/src/main/java/org/qortal/utils/Amounts.java new file mode 100644 index 00000000..39e42e24 --- /dev/null +++ b/src/main/java/org/qortal/utils/Amounts.java @@ -0,0 +1,44 @@ +package org.qortal.utils; + +import java.math.BigDecimal; + +public abstract class Amounts { + + public static String prettyAmount(long amount) { + StringBuilder stringBuilder = new StringBuilder(20); + + stringBuilder.append(amount / 100000000L); + + stringBuilder.append('.'); + + int dpLength = stringBuilder.length(); + + stringBuilder.append(amount % 100000000L); + + int paddingRequired = 8 - (stringBuilder.length() - dpLength); + if (paddingRequired > 0) + stringBuilder.append("00000000", 0, paddingRequired); + + return stringBuilder.toString(); + } + + public static BigDecimal toBigDecimal(long amount) { + return BigDecimal.valueOf(amount, 8); + } + + public static long greatestCommonDivisor(long a, long b) { + if (b == 0) + return Math.abs(a); + else if (a == 0) + return Math.abs(b); + + while (b != 0) { + long r = a % b; + a = b; + b = r; + } + + return Math.abs(a); + } + +} diff --git a/src/test/java/org/qortal/test/AccountBalanceTests.java b/src/test/java/org/qortal/test/AccountBalanceTests.java index b46e87fb..35a9fb17 100644 --- a/src/test/java/org/qortal/test/AccountBalanceTests.java +++ b/src/test/java/org/qortal/test/AccountBalanceTests.java @@ -2,7 +2,6 @@ package org.qortal.test; import static org.junit.Assert.*; -import java.math.BigDecimal; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; @@ -48,18 +47,18 @@ public class AccountBalanceTests extends Common { } } - private BigDecimal testNewerBalance(Repository repository, TestAccount testAccount) throws DataException { + private long testNewerBalance(Repository repository, TestAccount testAccount) throws DataException { // Grab initial balance - BigDecimal initialBalance = testAccount.getConfirmedBalance(Asset.QORT); + long initialBalance = testAccount.getConfirmedBalance(Asset.QORT); // Mint block to cause newer balance BlockUtils.mintBlock(repository); // Grab newer balance - BigDecimal newerBalance = testAccount.getConfirmedBalance(Asset.QORT); + long newerBalance = testAccount.getConfirmedBalance(Asset.QORT); // Confirm newer balance is greater than initial balance - assertTrue("Newer balance should be greater than initial balance", newerBalance.compareTo(initialBalance) > 0); + assertTrue("Newer balance should be greater than initial balance", newerBalance > initialBalance); return initialBalance; } @@ -70,15 +69,15 @@ public class AccountBalanceTests extends Common { try (final Repository repository = RepositoryManager.getRepository()) { TestAccount alice = Common.getTestAccount(repository, "alice"); - BigDecimal initialBalance = testNewerBalance(repository, alice); + long initialBalance = testNewerBalance(repository, alice); BlockUtils.orphanLastBlock(repository); // Grab post-orphan balance - BigDecimal orphanedBalance = alice.getConfirmedBalance(Asset.QORT); + long orphanedBalance = alice.getConfirmedBalance(Asset.QORT); // Confirm post-orphan balance is same as initial - assertEqualBigDecimals("Post-orphan balance should match initial", initialBalance, orphanedBalance); + assertEquals("Post-orphan balance should match initial", initialBalance, orphanedBalance); } } @@ -111,7 +110,7 @@ public class AccountBalanceTests extends Common { for (int i = 0; i < 100000; ++i) { Account account = accounts.get(random.nextInt(accounts.size())); int assetId = random.nextInt(2); - BigDecimal balance = BigDecimal.valueOf(random.nextInt(100000)); + long balance = random.nextInt(100000); AccountBalanceData accountBalanceData = new AccountBalanceData(account.getAddress(), assetId, balance); repository.getAccountRepository().save(accountBalanceData); diff --git a/src/test/java/org/qortal/test/AccountRefCacheTests.java b/src/test/java/org/qortal/test/AccountRefCacheTests.java index 4ccd8016..184a8537 100644 --- a/src/test/java/org/qortal/test/AccountRefCacheTests.java +++ b/src/test/java/org/qortal/test/AccountRefCacheTests.java @@ -2,7 +2,6 @@ package org.qortal.test; import static org.junit.Assert.*; -import java.math.BigDecimal; import java.util.Arrays; import java.util.Random; @@ -247,7 +246,7 @@ public class AccountRefCacheTests extends Common { // Test Block support @Test public void testBlockSupport() throws DataException { - final BigDecimal amount = BigDecimal.valueOf(12345670000L, 8); + final long amount = 12345670000L; try (final Repository repository = RepositoryManager.getRepository()) { TestAccount alice = Common.getTestAccount(repository, "alice"); diff --git a/src/test/java/org/qortal/test/BlockTests.java b/src/test/java/org/qortal/test/BlockTests.java index bb2ecd56..285e6d81 100644 --- a/src/test/java/org/qortal/test/BlockTests.java +++ b/src/test/java/org/qortal/test/BlockTests.java @@ -1,6 +1,5 @@ package org.qortal.test; -import java.math.BigDecimal; import java.util.List; import org.junit.Before; @@ -56,7 +55,7 @@ public class BlockTests extends Common { if (transactionData.getType() != Transaction.TransactionType.GENESIS) continue; - assertTrue(transactionData.getFee().compareTo(BigDecimal.ZERO) == 0); + assertEquals(0L, (long) transactionData.getFee()); assertTrue(transaction.isSignatureValid()); assertEquals(Transaction.ValidationResult.OK, transaction.isValid()); @@ -68,7 +67,7 @@ public class BlockTests extends Common { assertNotNull(transactionData); assertEquals(Transaction.TransactionType.GENESIS, transactionData.getType()); - assertTrue(transactionData.getFee().compareTo(BigDecimal.ZERO) == 0); + assertEquals(0L, (long) transactionData.getFee()); // assertNull(transactionData.getReference()); Transaction transaction = Transaction.fromData(repository, transactionData); diff --git a/src/test/java/org/qortal/test/RepositoryTests.java b/src/test/java/org/qortal/test/RepositoryTests.java index 55cff418..c9532d27 100644 --- a/src/test/java/org/qortal/test/RepositoryTests.java +++ b/src/test/java/org/qortal/test/RepositoryTests.java @@ -13,7 +13,6 @@ import org.qortal.test.common.Common; import static org.junit.Assert.*; -import java.math.BigDecimal; import java.sql.SQLException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -84,14 +83,14 @@ public class RepositoryTests extends Common { try (final Repository repository2 = RepositoryManager.getRepository()) { // Update account in 2 Account account2 = Common.getTestAccount(repository2, "alice"); - account2.setConfirmedBalance(Asset.QORT, BigDecimal.valueOf(1234L)); + account2.setConfirmedBalance(Asset.QORT, 1234L); repository2.saveChanges(); } repository1.discardChanges(); // Update account in 1 - account1.setConfirmedBalance(Asset.QORT, BigDecimal.valueOf(5678L)); + account1.setConfirmedBalance(Asset.QORT, 5678L); repository1.saveChanges(); } } diff --git a/src/test/java/org/qortal/test/TransferPrivsTests.java b/src/test/java/org/qortal/test/TransferPrivsTests.java index f81d7e34..0329da72 100644 --- a/src/test/java/org/qortal/test/TransferPrivsTests.java +++ b/src/test/java/org/qortal/test/TransferPrivsTests.java @@ -24,7 +24,6 @@ import org.qortal.transform.Transformer; import static org.junit.Assert.*; -import java.math.BigDecimal; import java.util.List; import java.util.Random; @@ -166,11 +165,11 @@ public class TransferPrivsTests extends Common { public void testMultipleIntoChloeTransferPrivs() throws DataException { try (final Repository repository = RepositoryManager.getRepository()) { // Alice needs to mint block containing REWARD_SHARE BEFORE Alice loses minting privs - byte[] aliceChloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "chloe", BigDecimal.ZERO); // Block minted by Alice + byte[] aliceChloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "chloe", 0); // Block minted by Alice PrivateKeyAccount aliceChloeRewardShareAccount = new PrivateKeyAccount(repository, aliceChloeRewardSharePrivateKey); // Alice needs to mint block containing REWARD_SHARE BEFORE Alice loses minting privs - byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", BigDecimal.ZERO); // Block minted by Alice + byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0); // Block minted by Alice PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey); TestAccount alice = Common.getTestAccount(repository, "alice"); @@ -281,7 +280,7 @@ public class TransferPrivsTests extends Common { byte[] reference = senderAccount.getLastReference(); long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1; int txGroupId = 0; - BigDecimal fee = BigDecimal.ONE.setScale(8); + long fee = 1L; 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/assets/GranularityTests.java b/src/test/java/org/qortal/test/assets/GranularityTests.java index 2fcb2f6a..7a6e976b 100644 --- a/src/test/java/org/qortal/test/assets/GranularityTests.java +++ b/src/test/java/org/qortal/test/assets/GranularityTests.java @@ -76,9 +76,10 @@ public class GranularityTests extends Common { } private void testGranularity(boolean isAmountAssetDivisible, boolean isReturnAssetDivisible, String dividend, String divisor, String expectedGranularity) { - final BigDecimal price = new BigDecimal(dividend).setScale(8).divide(new BigDecimal(divisor).setScale(8), RoundingMode.DOWN); + BigDecimal bdPrice = new BigDecimal(dividend).setScale(8).divide(new BigDecimal(divisor).setScale(8), RoundingMode.DOWN); + long price = bdPrice.unscaledValue().longValue(); - BigDecimal granularity = Order.calculateAmountGranularity(isAmountAssetDivisible, isReturnAssetDivisible, price); + BigDecimal granularity = BigDecimal.valueOf(Order.calculateAmountGranularity(isAmountAssetDivisible, isReturnAssetDivisible, price), 8); assertEqualBigDecimals("Granularity incorrect", new BigDecimal(expectedGranularity), granularity); } diff --git a/src/test/java/org/qortal/test/common/AccountUtils.java b/src/test/java/org/qortal/test/common/AccountUtils.java index 8b304b50..4bd45b4c 100644 --- a/src/test/java/org/qortal/test/common/AccountUtils.java +++ b/src/test/java/org/qortal/test/common/AccountUtils.java @@ -1,6 +1,7 @@ package org.qortal.test.common; -import java.math.BigDecimal; +import static org.junit.Assert.assertEquals; + import java.util.HashMap; import java.util.Map; @@ -16,9 +17,9 @@ import org.qortal.repository.Repository; public class AccountUtils { public static final int txGroupId = Group.NO_GROUP; - public static final BigDecimal fee = BigDecimal.ONE.setScale(8); + public static final long fee = 1L; - public static void pay(Repository repository, String sender, String recipient, BigDecimal amount) throws DataException { + public static void pay(Repository repository, String sender, String recipient, long amount) throws DataException { PrivateKeyAccount sendingAccount = Common.getTestAccount(repository, sender); PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient); @@ -31,7 +32,7 @@ public class AccountUtils { TransactionUtils.signAndMint(repository, transactionData, sendingAccount); } - public static TransactionData createRewardShare(Repository repository, String minter, String recipient, BigDecimal sharePercent) throws DataException { + public static TransactionData createRewardShare(Repository repository, String minter, String recipient, int sharePercent) throws DataException { PrivateKeyAccount mintingAccount = Common.getTestAccount(repository, minter); PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient); @@ -47,7 +48,7 @@ public class AccountUtils { return transactionData; } - public static byte[] rewardShare(Repository repository, String minter, String recipient, BigDecimal sharePercent) throws DataException { + public static byte[] rewardShare(Repository repository, String minter, String recipient, int sharePercent) throws DataException { TransactionData transactionData = createRewardShare(repository, minter, recipient, sharePercent); PrivateKeyAccount rewardShareAccount = Common.getTestAccount(repository, minter); @@ -59,16 +60,16 @@ public class AccountUtils { return rewardSharePrivateKey; } - public static Map> getBalances(Repository repository, long... assetIds) throws DataException { - Map> balances = new HashMap<>(); + public static Map> getBalances(Repository repository, long... assetIds) throws DataException { + Map> balances = new HashMap<>(); for (TestAccount account : Common.getTestAccounts(repository)) for (Long assetId : assetIds) { - BigDecimal balance = account.getConfirmedBalance(assetId); + long balance = account.getConfirmedBalance(assetId); balances.compute(account.accountName, (key, value) -> { if (value == null) - value = new HashMap(); + value = new HashMap(); value.put(assetId, balance); @@ -79,15 +80,15 @@ public class AccountUtils { return balances; } - public static BigDecimal getBalance(Repository repository, String accountName, long assetId) throws DataException { + public static long getBalance(Repository repository, String accountName, long assetId) throws DataException { return Common.getTestAccount(repository, accountName).getConfirmedBalance(assetId); } - public static void assertBalance(Repository repository, String accountName, long assetId, BigDecimal expectedBalance) throws DataException { - BigDecimal actualBalance = getBalance(repository, accountName, assetId); + public static void assertBalance(Repository repository, String accountName, long assetId, long expectedBalance) throws DataException { + long actualBalance = getBalance(repository, accountName, assetId); String assetName = repository.getAssetRepository().fromAssetId(assetId).getName(); - Common.assertEqualBigDecimals(String.format("%s's %s [%d] balance incorrect", accountName, assetName, assetId), expectedBalance, actualBalance); + assertEquals(String.format("%s's %s [%d] balance incorrect", accountName, assetName, assetId), expectedBalance, actualBalance); } } diff --git a/src/test/java/org/qortal/test/common/AssetUtils.java b/src/test/java/org/qortal/test/common/AssetUtils.java index 66930ddf..251651d6 100644 --- a/src/test/java/org/qortal/test/common/AssetUtils.java +++ b/src/test/java/org/qortal/test/common/AssetUtils.java @@ -1,8 +1,8 @@ package org.qortal.test.common; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import java.math.BigDecimal; import java.util.Base64; import java.util.Base64.Encoder; @@ -26,7 +26,7 @@ import java.util.Random; public class AssetUtils { public static final int txGroupId = Group.NO_GROUP; - public static final BigDecimal fee = BigDecimal.ONE.setScale(8); + public static final long fee = 1L; // QORT: 0, LEGACY_QORA: 1, QORT_FROM_QORA: 2 public static final long testAssetId = 3L; // Owned by Alice @@ -47,7 +47,7 @@ public class AssetUtils { return repository.getAssetRepository().fromAssetName(assetName).getAssetId(); } - public static void transferAsset(Repository repository, String fromAccountName, String toAccountName, long assetId, BigDecimal amount) throws DataException { + public static void transferAsset(Repository repository, String fromAccountName, String toAccountName, long assetId, long amount) throws DataException { PrivateKeyAccount fromAccount = Common.getTestAccount(repository, fromAccountName); PrivateKeyAccount toAccount = Common.getTestAccount(repository, toAccountName); @@ -60,7 +60,7 @@ public class AssetUtils { TransactionUtils.signAndMint(repository, transactionData, fromAccount); } - public static byte[] createOrder(Repository repository, String accountName, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price) throws DataException { + public static byte[] createOrder(Repository repository, String accountName, long haveAssetId, long wantAssetId, long amount, long price) throws DataException { PrivateKeyAccount account = Common.getTestAccount(repository, accountName); byte[] reference = account.getLastReference(); @@ -94,12 +94,12 @@ public class AssetUtils { } public static void genericTradeTest(long haveAssetId, long wantAssetId, - BigDecimal aliceAmount, BigDecimal alicePrice, - BigDecimal bobAmount, BigDecimal bobPrice, - BigDecimal aliceCommitment, BigDecimal bobCommitment, - BigDecimal aliceReturn, BigDecimal bobReturn, BigDecimal bobSaving) throws DataException { + long aliceAmount, long alicePrice, + long bobAmount, long bobPrice, + long aliceCommitment, long bobCommitment, + long aliceReturn, long bobReturn, long bobSaving) throws DataException { try (Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, haveAssetId, wantAssetId); + Map> initialBalances = AccountUtils.getBalances(repository, haveAssetId, wantAssetId); // Create target order byte[] targetOrderId = createOrder(repository, "alice", haveAssetId, wantAssetId, aliceAmount, alicePrice); @@ -108,36 +108,36 @@ public class AssetUtils { byte[] initiatingOrderId = createOrder(repository, "bob", wantAssetId, haveAssetId, bobAmount, bobPrice); // Check balances to check expected outcome - BigDecimal expectedBalance; + long expectedBalance; OrderData targetOrderData = repository.getAssetRepository().fromOrderId(targetOrderId); OrderData initiatingOrderData = repository.getAssetRepository().fromOrderId(initiatingOrderId); // Alice selling have asset - expectedBalance = initialBalances.get("alice").get(haveAssetId).subtract(aliceCommitment); + expectedBalance = initialBalances.get("alice").get(haveAssetId) - aliceCommitment; AccountUtils.assertBalance(repository, "alice", haveAssetId, expectedBalance); // Alice buying want asset - expectedBalance = initialBalances.get("alice").get(wantAssetId).add(aliceReturn); + expectedBalance = initialBalances.get("alice").get(wantAssetId) + aliceReturn; AccountUtils.assertBalance(repository, "alice", wantAssetId, expectedBalance); // Bob selling want asset - expectedBalance = initialBalances.get("bob").get(wantAssetId).subtract(bobCommitment).add(bobSaving); + expectedBalance = initialBalances.get("bob").get(wantAssetId) - bobCommitment + bobSaving; AccountUtils.assertBalance(repository, "bob", wantAssetId, expectedBalance); // Bob buying have asset - expectedBalance = initialBalances.get("bob").get(haveAssetId).add(bobReturn); + expectedBalance = initialBalances.get("bob").get(haveAssetId) + bobReturn; AccountUtils.assertBalance(repository, "bob", haveAssetId, expectedBalance); // Check orders - BigDecimal expectedFulfilled = (initiatingOrderData.getHaveAssetId() < initiatingOrderData.getWantAssetId()) ? bobReturn : aliceReturn; + long expectedFulfilled = (initiatingOrderData.getHaveAssetId() < initiatingOrderData.getWantAssetId()) ? bobReturn : aliceReturn; // Check matching order assertNotNull("matching order missing", initiatingOrderData); - Common.assertEqualBigDecimals(String.format("Bob's order \"fulfilled\" incorrect"), expectedFulfilled, initiatingOrderData.getFulfilled()); + assertEquals(String.format("Bob's order \"fulfilled\" incorrect"), expectedFulfilled, initiatingOrderData.getFulfilled()); // Check initial order assertNotNull("initial order missing", targetOrderData); - Common.assertEqualBigDecimals(String.format("Alice's order \"fulfilled\" incorrect"), expectedFulfilled, targetOrderData.getFulfilled()); + assertEquals(String.format("Alice's order \"fulfilled\" incorrect"), expectedFulfilled, targetOrderData.getFulfilled()); } } diff --git a/src/test/java/org/qortal/test/common/BlockUtils.java b/src/test/java/org/qortal/test/common/BlockUtils.java index 2bc02931..c85f6d01 100644 --- a/src/test/java/org/qortal/test/common/BlockUtils.java +++ b/src/test/java/org/qortal/test/common/BlockUtils.java @@ -1,7 +1,5 @@ package org.qortal.test.common; -import java.math.BigDecimal; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.qortal.account.PrivateKeyAccount; @@ -22,7 +20,7 @@ public class BlockUtils { BlockMinter.mintTestingBlock(repository, mintingAccount); } - public static BigDecimal getNextBlockReward(Repository repository) throws DataException { + public static Long getNextBlockReward(Repository repository) throws DataException { int currentHeight = repository.getBlockRepository().getBlockchainHeight(); return BlockChain.getInstance().getRewardAtHeight(currentHeight + 1); diff --git a/src/test/java/org/qortal/test/common/Common.java b/src/test/java/org/qortal/test/common/Common.java index 0972986b..24c86690 100644 --- a/src/test/java/org/qortal/test/common/Common.java +++ b/src/test/java/org/qortal/test/common/Common.java @@ -152,7 +152,7 @@ public class Common { checkOrphanedLists("group", initialGroups, remainingGroups, GroupData::getGroupId, GroupData::getGroupId); List remainingBalances = repository.getAccountRepository().getAssetBalances(Collections.emptyList(), Collections.emptyList(), BalanceOrdering.ASSET_ACCOUNT, false, null, null, null); - checkOrphanedLists("account balance", initialBalances, remainingBalances, entry -> entry.getAddress() + " [" + entry.getAssetName() + "]", entry -> entry.getBalance().toPlainString()); + checkOrphanedLists("account balance", initialBalances, remainingBalances, entry -> entry.getAddress() + " [" + entry.getAssetName() + "]", entry -> entry.getBalance()); assertEquals("remainingBalances is different size", initialBalances.size(), remainingBalances.size()); // Actually compare balances @@ -163,7 +163,7 @@ public class Common { assertEquals("Remaining balance's address differs", initialBalance.getAddress(), remainingBalance.getAddress()); assertEquals(initialBalance.getAddress() + " remaining balance's asset differs", initialBalance.getAssetId(), remainingBalance.getAssetId()); - assertEqualBigDecimals(initialBalance.getAddress() + " remaining balance differs", initialBalance.getBalance(), remainingBalance.getBalance()); + assertEquals(initialBalance.getAddress() + " remaining balance differs", initialBalance.getBalance(), remainingBalance.getBalance()); } } } diff --git a/src/test/java/org/qortal/test/common/GroupUtils.java b/src/test/java/org/qortal/test/common/GroupUtils.java index 75aa8a99..b7240096 100644 --- a/src/test/java/org/qortal/test/common/GroupUtils.java +++ b/src/test/java/org/qortal/test/common/GroupUtils.java @@ -1,7 +1,5 @@ package org.qortal.test.common; -import java.math.BigDecimal; - import org.qortal.account.PrivateKeyAccount; import org.qortal.data.transaction.BaseTransactionData; import org.qortal.data.transaction.CreateGroupTransactionData; @@ -17,7 +15,7 @@ import org.qortal.transaction.Transaction.ApprovalStatus; public class GroupUtils { public static final int txGroupId = Group.NO_GROUP; - public static final BigDecimal fee = BigDecimal.ONE.setScale(8); + public static final long fee = 1L; 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/minting/RewardShareTests.java b/src/test/java/org/qortal/test/minting/RewardShareTests.java index 04e208b0..25a4a38c 100644 --- a/src/test/java/org/qortal/test/minting/RewardShareTests.java +++ b/src/test/java/org/qortal/test/minting/RewardShareTests.java @@ -2,8 +2,6 @@ package org.qortal.test.minting; import static org.junit.Assert.*; -import java.math.BigDecimal; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -23,7 +21,7 @@ import org.qortal.utils.Base58; public class RewardShareTests extends Common { - private static final BigDecimal CANCEL_SHARE_PERCENT = BigDecimal.ONE.negate(); + private static final int CANCEL_SHARE_PERCENT = -1; @Before public void beforeTest() throws DataException { @@ -37,7 +35,7 @@ public class RewardShareTests extends Common { @Test public void testCreateRewardShare() throws DataException { - final BigDecimal sharePercent = new BigDecimal("12.8"); + final int sharePercent = 12_80; // 12.80% try (final Repository repository = RepositoryManager.getRepository()) { PrivateKeyAccount aliceAccount = Common.getTestAccount(repository, "alice"); @@ -53,13 +51,13 @@ public class RewardShareTests extends Common { RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(rewardShareAccount.getPublicKey()); assertEquals("Incorrect minter public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(rewardShareData.getMinterPublicKey())); assertEquals("Incorrect recipient", bobAccount.getAddress(), rewardShareData.getRecipient()); - assertEqualBigDecimals("Incorrect share percentage", sharePercent, rewardShareData.getSharePercent()); + assertEquals("Incorrect share percentage", sharePercent, rewardShareData.getSharePercent()); // Fetch using minter public key and recipient address combination rewardShareData = repository.getAccountRepository().getRewardShare(aliceAccount.getPublicKey(), bobAccount.getAddress()); assertEquals("Incorrect minter public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(rewardShareData.getMinterPublicKey())); assertEquals("Incorrect recipient", bobAccount.getAddress(), rewardShareData.getRecipient()); - assertEqualBigDecimals("Incorrect share percentage", sharePercent, rewardShareData.getSharePercent()); + assertEquals("Incorrect share percentage", sharePercent, rewardShareData.getSharePercent()); // Delete reward-share byte[] newRewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "bob", CANCEL_SHARE_PERCENT); @@ -89,14 +87,14 @@ public class RewardShareTests extends Common { assertNotNull("Reward-share should have been restored", newRewardShareData); assertEquals("Incorrect minter public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(newRewardShareData.getMinterPublicKey())); assertEquals("Incorrect recipient", bobAccount.getAddress(), newRewardShareData.getRecipient()); - assertEqualBigDecimals("Incorrect share percentage", sharePercent, newRewardShareData.getSharePercent()); + assertEquals("Incorrect share percentage", sharePercent, newRewardShareData.getSharePercent()); // Fetch using minter public key and recipient address combination newRewardShareData = repository.getAccountRepository().getRewardShare(aliceAccount.getPublicKey(), bobAccount.getAddress()); assertNotNull("Reward-share should have been restored", newRewardShareData); assertEquals("Incorrect minter public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(newRewardShareData.getMinterPublicKey())); assertEquals("Incorrect recipient", bobAccount.getAddress(), newRewardShareData.getRecipient()); - assertEqualBigDecimals("Incorrect share percentage", sharePercent, newRewardShareData.getSharePercent()); + assertEquals("Incorrect share percentage", sharePercent, newRewardShareData.getSharePercent()); // Orphan another block to remove initial reward-share BlockUtils.orphanLastBlock(repository); @@ -137,7 +135,7 @@ public class RewardShareTests extends Common { // PrivateKeyAccount rewardShareAccount = new PrivateKeyAccount(repository, rewardSharePrivateKey); // Create self-reward-share - TransactionData transactionData = AccountUtils.createRewardShare(repository, testAccountName, testAccountName, BigDecimal.valueOf(100L)); + TransactionData transactionData = AccountUtils.createRewardShare(repository, testAccountName, testAccountName, 100_00); Transaction transaction = Transaction.fromData(repository, transactionData); // Confirm self-share is valid @@ -145,14 +143,14 @@ public class RewardShareTests extends Common { assertEquals("Initial self-share should be valid", ValidationResult.OK, validationResult); // Check zero fee is valid - transactionData.setFee(BigDecimal.ZERO); + transactionData.setFee(0L); validationResult = transaction.isValidUnconfirmed(); assertEquals("Zero-fee self-share should be valid", ValidationResult.OK, validationResult); TransactionUtils.signAndMint(repository, transactionData, signingAccount); // Subsequent non-terminating (0% share) self-reward-share should be invalid - TransactionData newTransactionData = AccountUtils.createRewardShare(repository, testAccountName, testAccountName, BigDecimal.valueOf(99L)); + TransactionData newTransactionData = AccountUtils.createRewardShare(repository, testAccountName, testAccountName, 99_00); Transaction newTransaction = Transaction.fromData(repository, newTransactionData); // Confirm subsequent self-reward-share is actually invalid @@ -160,7 +158,7 @@ public class RewardShareTests extends Common { assertNotSame("Subsequent self-share should be invalid", ValidationResult.OK, validationResult); // Recheck with zero fee - newTransactionData.setFee(BigDecimal.ZERO); + newTransactionData.setFee(0L); validationResult = newTransaction.isValidUnconfirmed(); assertNotSame("Subsequent zero-fee self-share should be invalid", ValidationResult.OK, validationResult); @@ -173,7 +171,7 @@ public class RewardShareTests extends Common { assertEquals("Subsequent self-share cancel should be valid", ValidationResult.OK, validationResult); // Confirm terminating reward-share with zero fee is invalid - newTransactionData.setFee(BigDecimal.ZERO); + newTransactionData.setFee(0L); validationResult = newTransaction.isValidUnconfirmed(); assertNotSame("Subsequent zero-fee self-share cancel should be invalid", ValidationResult.OK, validationResult); } diff --git a/src/test/java/org/qortal/test/minting/RewardTests.java b/src/test/java/org/qortal/test/minting/RewardTests.java index 5b2bfdbf..ff025816 100644 --- a/src/test/java/org/qortal/test/minting/RewardTests.java +++ b/src/test/java/org/qortal/test/minting/RewardTests.java @@ -41,13 +41,13 @@ public class RewardTests extends Common { @Test public void testSimpleReward() throws DataException { try (final Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORT); + Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORT); - BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); + Long blockReward = BlockUtils.getNextBlockReward(repository); BlockUtils.mintBlock(repository); - BigDecimal expectedBalance = initialBalances.get("alice").get(Asset.QORT).add(blockReward); + long expectedBalance = initialBalances.get("alice").get(Asset.QORT) + blockReward; AccountUtils.assertBalance(repository, "alice", Asset.QORT, expectedBalance); } } @@ -57,12 +57,12 @@ public class RewardTests extends Common { List rewardsByHeight = BlockChain.getInstance().getBlockRewardsByHeight(); try (final Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORT); + Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORT); int rewardIndex = rewardsByHeight.size() - 1; RewardByHeight rewardInfo = rewardsByHeight.get(rewardIndex); - BigDecimal expectedBalance = initialBalances.get("alice").get(Asset.QORT); + Long expectedBalance = initialBalances.get("alice").get(Asset.QORT); for (int height = rewardInfo.height; height > 1; --height) { if (height < rewardInfo.height) { @@ -72,7 +72,7 @@ public class RewardTests extends Common { BlockUtils.mintBlock(repository); - expectedBalance = expectedBalance.add(rewardInfo.reward); + expectedBalance += rewardInfo.unscaledReward; } AccountUtils.assertBalance(repository, "alice", Asset.QORT, expectedBalance); @@ -81,24 +81,24 @@ public class RewardTests extends Common { @Test public void testRewardSharing() throws DataException { - final BigDecimal share = new BigDecimal("12.8"); + final int share = 12_80; // 12.80% try (final Repository repository = RepositoryManager.getRepository()) { byte[] rewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "bob", share); PrivateKeyAccount rewardShareAccount = new PrivateKeyAccount(repository, rewardSharePrivateKey); - Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORT); - BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); + Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORT); + Long blockReward = BlockUtils.getNextBlockReward(repository); BlockMinter.mintTestingBlock(repository, rewardShareAccount); // We're expecting reward * 12.8% to Bob, the rest to Alice - BigDecimal bobShare = blockReward.multiply(share.movePointLeft(2)).setScale(8, RoundingMode.DOWN); - AccountUtils.assertBalance(repository, "bob", Asset.QORT, initialBalances.get("bob").get(Asset.QORT).add(bobShare)); + long bobShare = (blockReward * share) / 100L; + AccountUtils.assertBalance(repository, "bob", Asset.QORT, initialBalances.get("bob").get(Asset.QORT) + bobShare); - BigDecimal aliceShare = blockReward.subtract(bobShare); - AccountUtils.assertBalance(repository, "alice", Asset.QORT, initialBalances.get("alice").get(Asset.QORT).add(aliceShare)); + long aliceShare = blockReward - bobShare; + AccountUtils.assertBalance(repository, "alice", Asset.QORT, initialBalances.get("alice").get(Asset.QORT) + aliceShare); } } @@ -107,19 +107,19 @@ public class RewardTests extends Common { public void testLegacyQoraReward() throws DataException { Common.useSettings("test-settings-v2-qora-holder.json"); - BigDecimal qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShare(); - BigDecimal qoraPerQort = BlockChain.getInstance().getQoraPerQortReward(); + long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersUnscaledShare(); + 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); - BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); + Long blockReward = BlockUtils.getNextBlockReward(repository); // Fetch all legacy QORA holder balances List qoraHolders = repository.getAccountRepository().getAssetBalances(Asset.LEGACY_QORA, true); - BigDecimal totalQoraHeld = BigDecimal.ZERO.setScale(8); + long totalQoraHeld = 0L; for (AccountBalanceData accountBalanceData : qoraHolders) - totalQoraHeld = totalQoraHeld.add(accountBalanceData.getBalance()); + totalQoraHeld += accountBalanceData.getBalance(); BlockUtils.mintBlock(repository); @@ -142,19 +142,19 @@ public class RewardTests extends Common { */ // Expected reward - BigDecimal qoraHoldersReward = blockReward.multiply(qoraHoldersShare); - assertTrue("QORA-holders share of block reward should be less than total block reward", qoraHoldersReward.compareTo(blockReward) < 0); + long qoraHoldersReward = (blockReward * qoraHoldersShare) / Asset.MULTIPLIER; + assertTrue("QORA-holders share of block reward should be less than total block reward", qoraHoldersReward < blockReward); - BigDecimal ourQoraHeld = initialBalances.get("chloe").get(Asset.LEGACY_QORA); - BigDecimal ourQoraReward = qoraHoldersReward.multiply(ourQoraHeld).divide(totalQoraHeld, RoundingMode.DOWN).setScale(8, RoundingMode.DOWN); - assertTrue("Our QORA-related reward should be less than total QORA-holders share of block reward", ourQoraReward.compareTo(qoraHoldersReward) < 0); + long ourQoraHeld = initialBalances.get("chloe").get(Asset.LEGACY_QORA); + long ourQoraReward = (qoraHoldersReward * ourQoraHeld) / totalQoraHeld; + assertTrue("Our QORA-related reward should be less than total QORA-holders share of block reward", ourQoraReward < qoraHoldersReward); - BigDecimal ourQortFromQoraCap = ourQoraHeld.divide(qoraPerQort, RoundingMode.DOWN); + long ourQortFromQoraCap = ourQoraHeld / qoraPerQort; - BigDecimal expectedReward = ourQoraReward.min(ourQortFromQoraCap); - AccountUtils.assertBalance(repository, "chloe", Asset.QORT, initialBalances.get("chloe").get(Asset.QORT).add(expectedReward)); + long expectedReward = Math.min(ourQoraReward, ourQortFromQoraCap); + AccountUtils.assertBalance(repository, "chloe", Asset.QORT, initialBalances.get("chloe").get(Asset.QORT) + expectedReward); - AccountUtils.assertBalance(repository, "chloe", Asset.QORT_FROM_QORA, initialBalances.get("chloe").get(Asset.QORT_FROM_QORA).add(expectedReward)); + AccountUtils.assertBalance(repository, "chloe", Asset.QORT_FROM_QORA, initialBalances.get("chloe").get(Asset.QORT_FROM_QORA) + expectedReward); } }