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); } }