mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-14 11:15:49 +00:00
Massive conversion from BigDecimal to long.
Now possible thanks to removing Qora v1 support. Maximum asset quantities now unified to 10_000_000_000, to 8 decimal places, removing prior 10 billion billion indivisible maximum. All values can now fit into a 64bit long. (Except maybe when processing asset trades). Added a general-use JAXB AmountTypeAdapter for converting amounts to/from String/long. Asset trading engine split into more methods for easier readability. Switched to using FIXED founder block reward distribution code, ready for launch. In HSQLDBDatabaseUpdates, QortalAmount changed from DECIMAL(27, 0) to BIGINT RewardSharePercent added to replace DECIMAL(5,2) with INT Ripped out unused Transaction.isInvolved and Transaction.getAmount in all subclasses. Changed Transaction.getRecipientAccounts() : List<Account> to Transaction.getRecipientAddresses() : List<String> as only addresses are ever used. Corrected returned values for above getRecipientAddresses() for some transaction subclasses. Added some account caching to some transactions to reduce repeated loads during validation and then processing. Transaction transformers: Changed serialization of asset amounts from using 12 bytes to now standard 8 byte long. Updated transaction 'layouts' to reflect new sizes. RewardShareTransactionTransformer still uses 8byte long to represent reward share percent. Updated some unit tests - more work needed!
This commit is contained in:
parent
0006911e0a
commit
9eaf31707a
@ -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 {
|
||||
|
27
src/main/java/org/qortal/api/AmountTypeAdapter.java
Normal file
27
src/main/java/org/qortal/api/AmountTypeAdapter.java
Normal file
@ -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<String, Long> {
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
* <p>
|
||||
* @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<OrderData> orders = assetRepository.getOpenOrdersForTrading(wantAssetId, haveAssetId, this.orderData.getPrice());
|
||||
LOGGER.trace("Open orders fetched from repository: " + orders.size());
|
||||
List<OrderData> 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<OrderData> 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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<ATStateData> 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<AtTransaction> 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<ATData> 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<Account> participants = transaction.getInvolvedAccounts();
|
||||
List<String> participantAddresses = participants.stream().map(Account::getAddress).collect(Collectors.toList());
|
||||
List<String> 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<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
||||
List<ShareByLevel> 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<ExpandedAccount> 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<AccountBalanceData> 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<AccountData> 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<ExpandedAccount> 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<ExpandedAccount> founderExpandedAccounts = expandedAccounts.stream().filter(
|
||||
accountInfo -> accountInfo.isMinterFounder &&
|
||||
accountInfo.mintingAccountData.getAddress().equals(founderAccount.getAddress())
|
||||
).collect(Collectors.toList());
|
||||
*/
|
||||
|
||||
// Broken version:
|
||||
List<ExpandedAccount> 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);
|
||||
|
@ -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<RewardByHeight> rewardsByHeight;
|
||||
|
||||
@ -94,13 +95,18 @@ public class BlockChain {
|
||||
public static class ShareByLevel {
|
||||
public List<Integer> levels;
|
||||
public BigDecimal share;
|
||||
public long unscaledShare; // share * 1e8, calculated after unmarshal
|
||||
}
|
||||
List<ShareByLevel> 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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ public class AssetData {
|
||||
|
||||
// Constructors
|
||||
|
||||
// necessary for JAX-RS serialization
|
||||
// necessary for JAXB serialization
|
||||
protected AssetData() {
|
||||
}
|
||||
|
||||
|
@ -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<OrderData> {
|
||||
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<OrderData> {
|
||||
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<OrderData> {
|
||||
}
|
||||
|
||||
/** 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<OrderData> {
|
||||
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<OrderData> {
|
||||
@Override
|
||||
public int compareTo(OrderData orderData) {
|
||||
// Compare using prices
|
||||
return this.price.compareTo(orderData.getPrice());
|
||||
return Long.compare(this.price, orderData.getPrice());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<PaymentData> payments, BigDecimal fee, boolean isZeroAmountValid) throws DataException {
|
||||
public ValidationResult isValid(byte[] senderPublicKey, List<PaymentData> 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<Long, BigDecimal> amountsByAssetId = new HashMap<>();
|
||||
Map<Long, Long> 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<Long, BigDecimal> pair : amountsByAssetId.entrySet())
|
||||
if (sender.getConfirmedBalance(pair.getKey()).compareTo(pair.getValue()) < 0)
|
||||
for (Entry<Long, Long> 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<PaymentData> payments, BigDecimal fee) throws DataException {
|
||||
public ValidationResult isValid(byte[] senderPublicKey, List<PaymentData> 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<PaymentData> payments, BigDecimal fee, boolean isZeroAmountValid) throws DataException {
|
||||
public ValidationResult isProcessable(byte[] senderPublicKey, List<PaymentData> 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<PaymentData> payments, BigDecimal fee) throws DataException {
|
||||
public ValidationResult isProcessable(byte[] senderPublicKey, List<PaymentData> 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<PaymentData> payments, BigDecimal fee, byte[] signature)
|
||||
throws DataException {
|
||||
public void process(byte[] senderPublicKey, List<PaymentData> 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<PaymentData> payments, BigDecimal fee, byte[] signature, boolean alwaysInitializeRecipientReference)
|
||||
public void processReferencesAndFees(byte[] senderPublicKey, List<PaymentData> 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<PaymentData> payments, BigDecimal fee, byte[] signature, byte[] reference) throws DataException {
|
||||
public void orphan(byte[] senderPublicKey, List<PaymentData> 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<PaymentData> payments, BigDecimal fee, byte[] signature, byte[] reference,
|
||||
public void orphanReferencesAndFees(byte[] senderPublicKey, List<PaymentData> 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);
|
||||
}
|
||||
|
@ -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<AccountBalanceData> getAssetBalances(List<String> addresses, List<Long> 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;
|
||||
|
||||
|
@ -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<OrderData> getOpenOrdersForTrading(long haveAssetId, long wantAssetId, BigDecimal minimumPrice) throws DataException;
|
||||
public List<OrderData> getOpenOrdersForTrading(long haveAssetId, long wantAssetId, Long minimumPrice) throws DataException;
|
||||
|
||||
public List<OrderData> getAggregatedOpenOrders(long haveAssetId, long wantAssetId, Integer limit, Integer offset, Boolean reverse) throws DataException;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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<OrderData> getOpenOrdersForTrading(long haveAssetId, long wantAssetId, BigDecimal minimumPrice) throws DataException {
|
||||
public List<OrderData> getOpenOrdersForTrading(long haveAssetId, long wantAssetId, Long minimumPrice) throws DataException {
|
||||
List<Object> 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);
|
||||
|
@ -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);
|
||||
|
@ -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)");
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
|
@ -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<Account> 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<String> 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<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
int blocksMintedAdjustment = cumulativeBlocksByLevel.get(this.accountLevelTransactionData.getLevel());
|
||||
target.setBlocksMintedAdjustment(blocksMintedAdjustment);
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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<Account> getRecipientAccounts() throws DataException {
|
||||
List<Account> 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<String> 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
|
||||
|
@ -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<Account> 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<Account> getInvolvedAccounts() throws DataException {
|
||||
List<Account> 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<String> 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
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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();
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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;
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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<PollOptionData> pollOptions = createPollTransactionData.getPollOptions();
|
||||
List<PollOptionData> pollOptions = this.createPollTransactionData.getPollOptions();
|
||||
int pollOptionsCount = pollOptions.size();
|
||||
if (pollOptionsCount < 1 || pollOptionsCount > Poll.MAX_OPTIONS)
|
||||
return ValidationResult.INVALID_OPTIONS_COUNT;
|
||||
|
||||
// Check each option
|
||||
List<String> optionNames = new ArrayList<String>();
|
||||
List<String> 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();
|
||||
}
|
||||
|
||||
|
@ -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<Account> 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<String> 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());
|
||||
|
@ -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<Account> 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<Account> 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<String> 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
|
||||
|
@ -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<Account> getRecipientAccounts() throws DataException {
|
||||
public List<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> getRecipientAccounts() throws DataException {
|
||||
public List<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> getRecipientAccounts() throws DataException {
|
||||
public List<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> getRecipientAccounts() throws DataException {
|
||||
List<Account> 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<String> 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<PaymentData> payments = multiPaymentTransactionData.getPayments();
|
||||
List<PaymentData> 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<PaymentData> payments = multiPaymentTransactionData.getPayments();
|
||||
List<PaymentData> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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();
|
||||
}
|
||||
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> getRecipientAccounts() throws DataException {
|
||||
public List<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 <tt>getCreator()</tt> 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<Account> getRecipientAccounts() throws DataException;
|
||||
|
||||
/**
|
||||
* Returns a list of involved accounts for this transaction.
|
||||
* <p>
|
||||
* "Involved" means sender or recipient.
|
||||
*
|
||||
* @return list of involved accounts, or empty list if none
|
||||
* @throws DataException
|
||||
*/
|
||||
public List<Account> getInvolvedAccounts() throws DataException {
|
||||
// Typically this is all the recipients plus the transaction creator/sender
|
||||
List<Account> participants = new ArrayList<>(getRecipientAccounts());
|
||||
participants.add(getCreator());
|
||||
return participants;
|
||||
public List<Account> getRecipientAccounts() throws DataException {
|
||||
throw new DataException("Placeholder for new AT code");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether passed account is an involved party in this transaction.
|
||||
* <p>
|
||||
* 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<String> 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.
|
||||
* <p>
|
||||
* Amounts "lost", e.g. sent by sender and fees, are returned as negative values.<br>
|
||||
* 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<String> getInvolvedAddresses() throws DataException {
|
||||
// Typically this is all the recipients plus the transaction creator/sender
|
||||
List<String> 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());
|
||||
}
|
||||
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Account> 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<String> 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<PollOptionData> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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<ATStateData> 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
|
||||
|
@ -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());
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user