mirror of
https://github.com/Qortal/qortal.git
synced 2025-06-01 14:06:58 +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;
|
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.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
@ -54,32 +54,24 @@ public class Account {
|
|||||||
|
|
||||||
// Balance manipulations - assetId is 0 for QORT
|
// 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);
|
AccountBalanceData accountBalanceData = this.repository.getAccountRepository().getBalance(this.address, assetId);
|
||||||
if (accountBalanceData == null)
|
if (accountBalanceData == null)
|
||||||
return BigDecimal.ZERO.setScale(8);
|
return 0;
|
||||||
|
|
||||||
return accountBalanceData.getBalance();
|
return accountBalanceData.getBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getConfirmedBalance(long assetId) throws DataException {
|
public void setConfirmedBalance(long assetId, long balance) 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 {
|
|
||||||
// Safety feature!
|
// Safety feature!
|
||||||
if (balance.compareTo(BigDecimal.ZERO) < 0) {
|
if (balance < 0) {
|
||||||
String message = String.format("Refusing to set negative balance %s [assetId %d] for %s", balance.toPlainString(), assetId, this.address);
|
String message = String.format("Refusing to set negative balance %s [assetId %d] for %s", prettyAmount(balance), assetId, this.address);
|
||||||
LOGGER.error(message);
|
LOGGER.error(message);
|
||||||
throw new DataException(message);
|
throw new DataException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete account balance record instead of setting balance to zero
|
// Delete account balance record instead of setting balance to zero
|
||||||
if (balance.signum() == 0) {
|
if (balance == 0) {
|
||||||
this.repository.getAccountRepository().delete(this.address, assetId);
|
this.repository.getAccountRepository().delete(this.address, assetId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -90,7 +82,7 @@ public class Account {
|
|||||||
AccountBalanceData accountBalanceData = new AccountBalanceData(this.address, assetId, balance);
|
AccountBalanceData accountBalanceData = new AccountBalanceData(this.address, assetId, balance);
|
||||||
this.repository.getAccountRepository().save(accountBalanceData);
|
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 {
|
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;
|
package org.qortal.api.model;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.Marshaller;
|
import javax.xml.bind.Marshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
import org.qortal.data.asset.OrderData;
|
import org.qortal.data.asset.OrderData;
|
||||||
|
|
||||||
@ -29,12 +28,14 @@ public class AggregatedOrder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@XmlElement(name = "price")
|
@XmlElement(name = "price")
|
||||||
public BigDecimal getPrice() {
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
public long getPrice() {
|
||||||
return this.orderData.getPrice();
|
return this.orderData.getPrice();
|
||||||
}
|
}
|
||||||
|
|
||||||
@XmlElement(name = "unfulfilled")
|
@XmlElement(name = "unfulfilled")
|
||||||
public BigDecimal getUnfulfilled() {
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
public long getUnfulfilled() {
|
||||||
return this.orderData.getAmount();
|
return this.orderData.getAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ import org.qortal.transaction.Transaction.ValidationResult;
|
|||||||
import org.qortal.transform.TransformationException;
|
import org.qortal.transform.TransformationException;
|
||||||
import org.qortal.transform.Transformer;
|
import org.qortal.transform.Transformer;
|
||||||
import org.qortal.transform.transaction.RewardShareTransactionTransformer;
|
import org.qortal.transform.transaction.RewardShareTransactionTransformer;
|
||||||
|
import org.qortal.utils.Amounts;
|
||||||
import org.qortal.utils.Base58;
|
import org.qortal.utils.Base58;
|
||||||
|
|
||||||
@Path("/addresses")
|
@Path("/addresses")
|
||||||
@ -195,7 +196,7 @@ public class AddressesResource {
|
|||||||
else if (!repository.getAssetRepository().assetExists(assetId))
|
else if (!repository.getAssetRepository().assetExists(assetId))
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||||
|
|
||||||
return account.getBalance(assetId);
|
return Amounts.toBigDecimal(account.getConfirmedBalance(assetId));
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
|
@ -25,7 +25,8 @@ public class Asset {
|
|||||||
public static final int MAX_DESCRIPTION_SIZE = 4000;
|
public static final int MAX_DESCRIPTION_SIZE = 4000;
|
||||||
public static final int MAX_DATA_SIZE = 400000;
|
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
|
// Properties
|
||||||
private Repository repository;
|
private Repository repository;
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package org.qortal.asset;
|
package org.qortal.asset;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import static org.qortal.utils.Amounts.prettyAmount;
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.math.RoundingMode;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -14,9 +13,9 @@ import org.qortal.account.PublicKeyAccount;
|
|||||||
import org.qortal.data.asset.AssetData;
|
import org.qortal.data.asset.AssetData;
|
||||||
import org.qortal.data.asset.OrderData;
|
import org.qortal.data.asset.OrderData;
|
||||||
import org.qortal.data.asset.TradeData;
|
import org.qortal.data.asset.TradeData;
|
||||||
import org.qortal.repository.AssetRepository;
|
|
||||||
import org.qortal.repository.DataException;
|
import org.qortal.repository.DataException;
|
||||||
import org.qortal.repository.Repository;
|
import org.qortal.repository.Repository;
|
||||||
|
import org.qortal.utils.Amounts;
|
||||||
import org.qortal.utils.Base58;
|
import org.qortal.utils.Base58;
|
||||||
|
|
||||||
public class Order {
|
public class Order {
|
||||||
@ -57,16 +56,16 @@ public class Order {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
public static BigDecimal getAmountLeft(OrderData orderData) {
|
public static long getAmountLeft(OrderData orderData) {
|
||||||
return orderData.getAmount().subtract(orderData.getFulfilled());
|
return orderData.getAmount() - orderData.getFulfilled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmountLeft() {
|
public long getAmountLeft() {
|
||||||
return Order.getAmountLeft(this.orderData);
|
return Order.getAmountLeft(this.orderData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isFulfilled(OrderData orderData) {
|
public static boolean isFulfilled(OrderData orderData) {
|
||||||
return orderData.getFulfilled().compareTo(orderData.getAmount()) == 0;
|
return orderData.getFulfilled() == orderData.getAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFulfilled() {
|
public boolean isFulfilled() {
|
||||||
@ -83,31 +82,28 @@ public class Order {
|
|||||||
* <p>
|
* <p>
|
||||||
* @return granularity of matched-amount
|
* @return granularity of matched-amount
|
||||||
*/
|
*/
|
||||||
public static BigDecimal calculateAmountGranularity(boolean isAmountAssetDivisible, boolean isReturnAssetDivisible, BigDecimal price) {
|
public static long calculateAmountGranularity(boolean isAmountAssetDivisible, boolean isReturnAssetDivisible, long price) {
|
||||||
// Multiplier to scale BigDecimal fractional amounts into integer domain
|
|
||||||
BigInteger multiplier = BigInteger.valueOf(1_0000_0000L);
|
|
||||||
|
|
||||||
// Calculate the minimum increment for matched-amount using greatest-common-divisor
|
// Calculate the minimum increment for matched-amount using greatest-common-divisor
|
||||||
BigInteger returnAmount = multiplier; // 1 unit (* multiplier)
|
long returnAmount = Asset.MULTIPLIER; // 1 unit * multiplier
|
||||||
BigInteger matchedAmount = price.movePointRight(8).toBigInteger();
|
long matchedAmount = price;
|
||||||
|
|
||||||
BigInteger gcd = returnAmount.gcd(matchedAmount);
|
long gcd = Amounts.greatestCommonDivisor(returnAmount, matchedAmount);
|
||||||
returnAmount = returnAmount.divide(gcd);
|
returnAmount /= gcd;
|
||||||
matchedAmount = matchedAmount.divide(gcd);
|
matchedAmount /= gcd;
|
||||||
|
|
||||||
// Calculate GCD in combination with divisibility
|
// Calculate GCD in combination with divisibility
|
||||||
if (isAmountAssetDivisible)
|
if (isAmountAssetDivisible)
|
||||||
returnAmount = returnAmount.multiply(multiplier);
|
returnAmount *= Asset.MULTIPLIER;
|
||||||
|
|
||||||
if (isReturnAssetDivisible)
|
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
|
// Calculate the granularity at which we have to buy
|
||||||
BigDecimal granularity = new BigDecimal(returnAmount.divide(gcd));
|
long granularity = returnAmount / gcd;
|
||||||
if (isAmountAssetDivisible)
|
if (isAmountAssetDivisible)
|
||||||
granularity = granularity.movePointLeft(8);
|
granularity /= Asset.MULTIPLIER;
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
return granularity;
|
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. */
|
/** Returns amount of have-asset to remove from order's creator's balance on placing this order. */
|
||||||
private BigDecimal calcHaveAssetCommittment() {
|
private long calcHaveAssetCommittment() {
|
||||||
BigDecimal committedCost = this.orderData.getAmount();
|
long committedCost = this.orderData.getAmount();
|
||||||
|
|
||||||
// If "amount" is in want-asset then we need to convert
|
// If "amount" is in want-asset then we need to convert
|
||||||
if (haveAssetId < wantAssetId)
|
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;
|
return committedCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns amount of remaining have-asset to refund to order's creator's balance on cancelling this order. */
|
/** Returns amount of remaining have-asset to refund to order's creator's balance on cancelling this order. */
|
||||||
private BigDecimal calcHaveAssetRefund() {
|
private long calcHaveAssetRefund() {
|
||||||
BigDecimal refund = getAmountLeft();
|
long refund = getAmountLeft();
|
||||||
|
|
||||||
// If "amount" is in want-asset then we need to convert
|
// If "amount" is in want-asset then we need to convert
|
||||||
if (haveAssetId < wantAssetId)
|
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;
|
return refund;
|
||||||
}
|
}
|
||||||
@ -229,37 +225,30 @@ public class Order {
|
|||||||
final AssetData amountAssetData = this.repository.getAssetRepository().fromAssetId(amountAssetId);
|
final AssetData amountAssetData = this.repository.getAssetRepository().fromAssetId(amountAssetId);
|
||||||
final AssetData returnAssetData = this.repository.getAssetRepository().fromAssetId(returnAssetId);
|
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,
|
LOGGER.trace(() -> String.format("%s amount: %s (ordered) - %s (fulfilled) = %s %s left", ourTheir,
|
||||||
orderData.getAmount().stripTrailingZeros().toPlainString(),
|
prettyAmount(orderData.getAmount()),
|
||||||
orderData.getFulfilled().stripTrailingZeros().toPlainString(),
|
prettyAmount(orderData.getFulfilled()),
|
||||||
Order.getAmountLeft(orderData).stripTrailingZeros().toPlainString(),
|
prettyAmount(Order.getAmountLeft(orderData)),
|
||||||
amountAssetData.getName()));
|
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,
|
LOGGER.trace(() -> String.format("%s price: %s %s (%s %s tradable)", ourTheir,
|
||||||
orderData.getPrice().toPlainString(), getPricePair(),
|
prettyAmount(orderData.getPrice()),
|
||||||
maxReturnAmount.stripTrailingZeros().toPlainString(), returnAssetData.getName()));
|
pricePair,
|
||||||
|
prettyAmount(maxReturnAmount),
|
||||||
|
returnAssetData.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process() throws DataException {
|
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
|
// Subtract have-asset from creator
|
||||||
Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey());
|
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
|
// Save this order into repository so it's available for matching, possibly by itself
|
||||||
this.repository.getAssetRepository().save(this.orderData);
|
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.
|
// Fetch corresponding open orders that might potentially match, hence reversed want/have assetIDs.
|
||||||
// Returned orders are sorted with lowest "price" first.
|
// Returned orders are sorted with lowest "price" first.
|
||||||
List<OrderData> orders = assetRepository.getOpenOrdersForTrading(wantAssetId, haveAssetId, this.orderData.getPrice());
|
List<OrderData> orders = this.repository.getAssetRepository().getOpenOrdersForTrading(wantAssetId, haveAssetId, this.orderData.getPrice());
|
||||||
LOGGER.trace("Open orders fetched from repository: " + orders.size());
|
LOGGER.trace(() -> String.format("Open orders fetched from repository: %d", orders.size()));
|
||||||
|
|
||||||
if (orders.isEmpty())
|
if (orders.isEmpty())
|
||||||
return;
|
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
|
// 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.
|
* 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) {
|
for (OrderData theirOrderData : orders) {
|
||||||
logOrder("Considering order", false, theirOrderData);
|
logOrder("Considering order", false, theirOrderData);
|
||||||
|
|
||||||
// Determine their order price
|
// Determine their order price
|
||||||
BigDecimal theirPrice;
|
long theirPrice = theirOrderData.getPrice();
|
||||||
|
LOGGER.trace(() -> String.format("Their price: %s %s", prettyAmount(theirPrice), pricePair));
|
||||||
// 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()));
|
|
||||||
|
|
||||||
// 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 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)
|
if ((haveAssetId < wantAssetId && theirPrice > ourPrice) || (haveAssetId > wantAssetId && theirPrice < ourPrice))
|
||||||
break;
|
|
||||||
if (haveAssetId > wantAssetId && theirPrice.compareTo(ourPrice) < 0)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Calculate how much we could buy at their price, "amount" is expressed in terms of asset with highest assetID.
|
// Calculate how much we could buy at their price, "amount" is expressed in terms of asset with highest assetID.
|
||||||
BigDecimal ourMaxAmount = this.getAmountLeft();
|
long ourMaxAmount = this.getAmountLeft();
|
||||||
LOGGER.trace("ourMaxAmount (max we could trade at their price): " + ourMaxAmount.stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName());
|
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.
|
// How much is remaining available in their order.
|
||||||
BigDecimal theirAmountLeft = Order.getAmountLeft(theirOrderData);
|
long theirAmountLeft = Order.getAmountLeft(theirOrderData);
|
||||||
LOGGER.trace("theirAmountLeft (max amount remaining in their order): " + theirAmountLeft.stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName());
|
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
|
// So matchable want-asset amount is the minimum of above two values
|
||||||
BigDecimal matchedAmount = ourMaxAmount.min(theirAmountLeft);
|
long interimMatchedAmount = Math.min(ourMaxAmount, theirAmountLeft);
|
||||||
LOGGER.trace("matchedAmount: " + matchedAmount.stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName());
|
LOGGER.trace(() -> String.format("matchedAmount: %s %s", prettyAmount(interimMatchedAmount), matchingAssetData.getName()));
|
||||||
|
|
||||||
// If we can't buy anything then try another order
|
// If we can't buy anything then try another order
|
||||||
if (matchedAmount.compareTo(BigDecimal.ZERO) <= 0)
|
if (interimMatchedAmount <= 0)
|
||||||
continue;
|
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.)
|
// 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());
|
long granularity = calculateAmountGranularity(matchingAssetData.getIsDivisible(), returnAssetData.getIsDivisible(), theirOrderData.getPrice());
|
||||||
LOGGER.trace("granularity (amount granularity): " + granularity.stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName());
|
LOGGER.trace(() -> String.format("granularity (amount granularity): %s %s", prettyAmount(granularity), matchingAssetData.getName()));
|
||||||
|
|
||||||
// Reduce matched amount (if need be) to fit granularity
|
// Reduce matched amount (if need be) to fit granularity
|
||||||
matchedAmount = matchedAmount.subtract(matchedAmount.remainder(granularity));
|
long matchedAmount = interimMatchedAmount - interimMatchedAmount % granularity;
|
||||||
LOGGER.trace("matchedAmount adjusted for granularity: " + matchedAmount.stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName());
|
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 we can't buy anything then try another order
|
||||||
if (matchedAmount.compareTo(BigDecimal.ZERO) <= 0)
|
if (matchedAmount <= 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Safety check
|
// Safety check
|
||||||
if (!matchingAssetData.getIsDivisible() && matchedAmount.stripTrailingZeros().scale() > 0) {
|
checkDivisibility(matchingAssetData, matchedAmount, theirOrderData);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trade can go ahead!
|
// Trade can go ahead!
|
||||||
|
|
||||||
// Calculate the total cost to us, in return-asset, based on their price
|
// Calculate the total cost to us, in return-asset, based on their price
|
||||||
BigDecimal returnAmountTraded = matchedAmount.multiply(theirOrderData.getPrice()).setScale(8, RoundingMode.DOWN);
|
long returnAmountTraded = matchedAmount * theirOrderData.getPrice();
|
||||||
LOGGER.trace("returnAmountTraded: " + returnAmountTraded.stripTrailingZeros().toPlainString() + " " + returnAssetData.getName());
|
LOGGER.trace(() -> String.format("returnAmountTraded: %s %s", prettyAmount(returnAmountTraded), returnAssetData.getName()));
|
||||||
|
|
||||||
// Safety check
|
// Safety check
|
||||||
if (!returnAssetData.getIsDivisible() && returnAmountTraded.stripTrailingZeros().scale() > 0) {
|
checkDivisibility(returnAssetData, returnAmountTraded, this.orderData);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigDecimal tradedWantAmount = (haveAssetId > wantAssetId) ? returnAmountTraded : matchedAmount;
|
long tradedWantAmount = (haveAssetId > wantAssetId) ? returnAmountTraded : matchedAmount;
|
||||||
BigDecimal tradedHaveAmount = (haveAssetId > wantAssetId) ? matchedAmount : returnAmountTraded;
|
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)
|
// 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)",
|
LOGGER.trace(() -> String.format("We traded %s %s (have-asset) for %s %s (want-asset), saving %s %s (have-asset)",
|
||||||
tradedHaveAmount.toPlainString(), haveAssetData.getName(),
|
prettyAmount(tradedHaveAmount), haveAssetData.getName(),
|
||||||
tradedWantAmount.toPlainString(), wantAssetData.getName(),
|
prettyAmount(tradedWantAmount), wantAssetData.getName(),
|
||||||
haveAssetRefund.toPlainString(), haveAssetData.getName()));
|
prettyAmount(haveAssetRefund), haveAssetData.getName()));
|
||||||
|
|
||||||
// Construct trade
|
// Construct trade
|
||||||
TradeData tradeData = new TradeData(this.orderData.getOrderId(), theirOrderData.getOrderId(),
|
TradeData tradeData = new TradeData(this.orderData.getOrderId(), theirOrderData.getOrderId(),
|
||||||
@ -384,17 +369,33 @@ public class Order {
|
|||||||
trade.process();
|
trade.process();
|
||||||
|
|
||||||
// Update our order in terms of fulfilment, etc. but do not save into repository as that's handled by Trade above
|
// Update our order in terms of fulfilment, etc. but do not save into repository as that's handled by Trade above
|
||||||
BigDecimal amountFulfilled = matchedAmount;
|
long amountFulfilled = matchedAmount;
|
||||||
this.orderData.setFulfilled(this.orderData.getFulfilled().add(amountFulfilled));
|
this.orderData.setFulfilled(this.orderData.getFulfilled() + amountFulfilled);
|
||||||
LOGGER.trace("Updated our order's fulfilled amount to: " + this.orderData.getFulfilled().stripTrailingZeros().toPlainString() + " " + matchingAssetData.getName());
|
LOGGER.trace(() -> String.format("Updated our order's fulfilled amount to: %s %s", prettyAmount(this.orderData.getFulfilled()), matchingAssetData.getName()));
|
||||||
LOGGER.trace("Our order's amount remaining: " + this.getAmountLeft().stripTrailingZeros().toPlainString() + " " + 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
|
// 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;
|
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 {
|
public void orphan() throws DataException {
|
||||||
// Orphan trades that occurred as a result of this order
|
// Orphan trades that occurred as a result of this order
|
||||||
for (TradeData tradeData : getTrades())
|
for (TradeData tradeData : getTrades())
|
||||||
@ -408,7 +409,7 @@ public class Order {
|
|||||||
|
|
||||||
// Return asset to creator
|
// Return asset to creator
|
||||||
Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey());
|
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
|
// 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
|
// Update creator's balance with unfulfilled amount
|
||||||
Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey());
|
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
|
// Opposite of cancel() above for use during orphaning
|
||||||
public void reopen() throws DataException {
|
public void reopen() throws DataException {
|
||||||
// Update creator's balance with unfulfilled amount
|
// Update creator's balance with unfulfilled amount
|
||||||
Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey());
|
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.orderData.setIsClosed(false);
|
||||||
this.repository.getAssetRepository().save(this.orderData);
|
this.repository.getAssetRepository().save(this.orderData);
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.qortal.asset;
|
package org.qortal.asset;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
import org.qortal.account.PublicKeyAccount;
|
||||||
import org.qortal.data.asset.OrderData;
|
import org.qortal.data.asset.OrderData;
|
||||||
@ -20,7 +18,7 @@ public class Trade {
|
|||||||
|
|
||||||
private OrderData initiatingOrder;
|
private OrderData initiatingOrder;
|
||||||
private OrderData targetOrder;
|
private OrderData targetOrder;
|
||||||
private BigDecimal fulfilled;
|
private long fulfilled;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -42,7 +40,7 @@ public class Trade {
|
|||||||
|
|
||||||
// "amount" and "fulfilled" are the same asset for both orders
|
// "amount" and "fulfilled" are the same asset for both orders
|
||||||
// which is the matchedAmount in asset with highest assetID
|
// 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 {
|
public void process() throws DataException {
|
||||||
@ -55,13 +53,13 @@ public class Trade {
|
|||||||
commonPrep();
|
commonPrep();
|
||||||
|
|
||||||
// Update corresponding Orders on both sides of trade
|
// Update corresponding Orders on both sides of trade
|
||||||
initiatingOrder.setFulfilled(initiatingOrder.getFulfilled().add(fulfilled));
|
initiatingOrder.setFulfilled(initiatingOrder.getFulfilled() + fulfilled);
|
||||||
initiatingOrder.setIsFulfilled(Order.isFulfilled(initiatingOrder));
|
initiatingOrder.setIsFulfilled(Order.isFulfilled(initiatingOrder));
|
||||||
// Set isClosed to true if isFulfilled now true
|
// Set isClosed to true if isFulfilled now true
|
||||||
initiatingOrder.setIsClosed(initiatingOrder.getIsFulfilled());
|
initiatingOrder.setIsClosed(initiatingOrder.getIsFulfilled());
|
||||||
assetRepository.save(initiatingOrder);
|
assetRepository.save(initiatingOrder);
|
||||||
|
|
||||||
targetOrder.setFulfilled(targetOrder.getFulfilled().add(fulfilled));
|
targetOrder.setFulfilled(targetOrder.getFulfilled() + fulfilled);
|
||||||
targetOrder.setIsFulfilled(Order.isFulfilled(targetOrder));
|
targetOrder.setIsFulfilled(Order.isFulfilled(targetOrder));
|
||||||
// Set isClosed to true if isFulfilled now true
|
// Set isClosed to true if isFulfilled now true
|
||||||
targetOrder.setIsClosed(targetOrder.getIsFulfilled());
|
targetOrder.setIsClosed(targetOrder.getIsFulfilled());
|
||||||
@ -69,33 +67,31 @@ public class Trade {
|
|||||||
|
|
||||||
// Actually transfer asset balances
|
// Actually transfer asset balances
|
||||||
Account initiatingCreator = new PublicKeyAccount(this.repository, initiatingOrder.getCreatorPublicKey());
|
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());
|
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
|
// Possible partial saving to refund to initiator
|
||||||
BigDecimal initiatorSaving = this.tradeData.getInitiatorSaving();
|
long initiatorSaving = this.tradeData.getInitiatorSaving();
|
||||||
if (initiatorSaving.compareTo(BigDecimal.ZERO) > 0)
|
if (initiatorSaving > 0)
|
||||||
initiatingCreator.setConfirmedBalance(initiatingOrder.getHaveAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getHaveAssetId()).add(initiatorSaving));
|
initiatingCreator.setConfirmedBalance(initiatingOrder.getHaveAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getHaveAssetId()) + initiatorSaving);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
AssetRepository assetRepository = this.repository.getAssetRepository();
|
|
||||||
|
|
||||||
// Note: targetAmount is amount traded FROM target order
|
// Note: targetAmount is amount traded FROM target order
|
||||||
// Note: initiatorAmount is amount traded FROM initiating order
|
// Note: initiatorAmount is amount traded FROM initiating order
|
||||||
|
|
||||||
commonPrep();
|
commonPrep();
|
||||||
|
|
||||||
// Revert corresponding Orders on both sides of trade
|
// Revert corresponding Orders on both sides of trade
|
||||||
initiatingOrder.setFulfilled(initiatingOrder.getFulfilled().subtract(fulfilled));
|
initiatingOrder.setFulfilled(initiatingOrder.getFulfilled() - fulfilled);
|
||||||
initiatingOrder.setIsFulfilled(Order.isFulfilled(initiatingOrder));
|
initiatingOrder.setIsFulfilled(Order.isFulfilled(initiatingOrder));
|
||||||
// Set isClosed to false if isFulfilled now false
|
// Set isClosed to false if isFulfilled now false
|
||||||
initiatingOrder.setIsClosed(initiatingOrder.getIsFulfilled());
|
initiatingOrder.setIsClosed(initiatingOrder.getIsFulfilled());
|
||||||
assetRepository.save(initiatingOrder);
|
assetRepository.save(initiatingOrder);
|
||||||
|
|
||||||
targetOrder.setFulfilled(targetOrder.getFulfilled().subtract(fulfilled));
|
targetOrder.setFulfilled(targetOrder.getFulfilled() - fulfilled);
|
||||||
targetOrder.setIsFulfilled(Order.isFulfilled(targetOrder));
|
targetOrder.setIsFulfilled(Order.isFulfilled(targetOrder));
|
||||||
// Set isClosed to false if isFulfilled now false
|
// Set isClosed to false if isFulfilled now false
|
||||||
targetOrder.setIsClosed(targetOrder.getIsFulfilled());
|
targetOrder.setIsClosed(targetOrder.getIsFulfilled());
|
||||||
@ -103,15 +99,15 @@ public class Trade {
|
|||||||
|
|
||||||
// Reverse asset transfers
|
// Reverse asset transfers
|
||||||
Account initiatingCreator = new PublicKeyAccount(this.repository, initiatingOrder.getCreatorPublicKey());
|
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());
|
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
|
// Possible partial saving to claw back from initiator
|
||||||
BigDecimal initiatorSaving = this.tradeData.getInitiatorSaving();
|
long initiatorSaving = this.tradeData.getInitiatorSaving();
|
||||||
if (initiatorSaving.compareTo(BigDecimal.ZERO) > 0)
|
if (initiatorSaving > 0)
|
||||||
initiatingCreator.setConfirmedBalance(initiatingOrder.getHaveAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getHaveAssetId()).subtract(initiatorSaving));
|
initiatingCreator.setConfirmedBalance(initiatingOrder.getHaveAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getHaveAssetId()) - initiatorSaving);
|
||||||
|
|
||||||
// Remove trade from repository
|
// Remove trade from repository
|
||||||
assetRepository.delete(tradeData);
|
assetRepository.delete(tradeData);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.at;
|
package org.qortal.at;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.ciyam.at.MachineState;
|
import org.ciyam.at.MachineState;
|
||||||
@ -51,7 +50,7 @@ public class AT {
|
|||||||
byte[] stateData = machineState.toBytes();
|
byte[] stateData = machineState.toBytes();
|
||||||
byte[] stateHash = Crypto.digest(stateData);
|
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
|
// Getters / setters
|
||||||
@ -97,7 +96,7 @@ public class AT {
|
|||||||
long creation = this.atData.getCreation();
|
long creation = this.atData.getCreation();
|
||||||
byte[] stateData = state.toBytes();
|
byte[] stateData = state.toBytes();
|
||||||
byte[] stateHash = Crypto.digest(stateData);
|
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);
|
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.Arrays.stream;
|
||||||
import static java.util.stream.Collectors.toMap;
|
import static java.util.stream.Collectors.toMap;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -85,16 +84,16 @@ public enum BlockchainAPI {
|
|||||||
|
|
||||||
switch (transactionData.getType()) {
|
switch (transactionData.getType()) {
|
||||||
case PAYMENT:
|
case PAYMENT:
|
||||||
return ((PaymentTransactionData) transactionData).getAmount().unscaledValue().longValue();
|
return ((PaymentTransactionData) transactionData).getAmount();
|
||||||
|
|
||||||
case AT:
|
case AT:
|
||||||
BigDecimal amount = ((ATTransactionData) transactionData).getAmount();
|
Long amount = ((ATTransactionData) transactionData).getAmount();
|
||||||
|
|
||||||
if (amount != null)
|
if (amount == null)
|
||||||
return amount.unscaledValue().longValue();
|
|
||||||
else
|
|
||||||
return 0xffffffffffffffffL;
|
return 0xffffffffffffffffL;
|
||||||
|
|
||||||
|
return amount;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0xffffffffffffffffL;
|
return 0xffffffffffffffffL;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.at;
|
package org.qortal.at;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -34,7 +33,7 @@ import com.google.common.primitives.Bytes;
|
|||||||
public class QortalATAPI extends API {
|
public class QortalATAPI extends API {
|
||||||
|
|
||||||
// Useful constants
|
// 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 MAX_STEPS_PER_ROUND = 500;
|
||||||
private static final int STEPS_PER_FUNCTION_CALL = 10;
|
private static final int STEPS_PER_FUNCTION_CALL = 10;
|
||||||
private static final int MINUTES_PER_BLOCK = 10;
|
private static final int MINUTES_PER_BLOCK = 10;
|
||||||
@ -62,8 +61,8 @@ public class QortalATAPI extends API {
|
|||||||
return this.transactions;
|
return this.transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal calcFinalFees(MachineState state) {
|
public long calcFinalFees(MachineState state) {
|
||||||
return FEE_PER_STEP.multiply(BigDecimal.valueOf(state.getSteps()));
|
return state.getSteps() * FEE_PER_STEP;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inherited methods from CIYAM AT API
|
// Inherited methods from CIYAM AT API
|
||||||
@ -83,7 +82,7 @@ public class QortalATAPI extends API {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getFeePerStep() {
|
public long getFeePerStep() {
|
||||||
return FEE_PER_STEP.unscaledValue().longValue();
|
return FEE_PER_STEP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -254,23 +253,22 @@ public class QortalATAPI extends API {
|
|||||||
try {
|
try {
|
||||||
Account atAccount = this.getATAccount();
|
Account atAccount = this.getATAccount();
|
||||||
|
|
||||||
return atAccount.getConfirmedBalance(Asset.QORT).unscaledValue().longValue();
|
return atAccount.getConfirmedBalance(Asset.QORT);
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw new RuntimeException("AT API unable to fetch AT's current balance?", e);
|
throw new RuntimeException("AT API unable to fetch AT's current balance?", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void payAmountToB(long unscaledAmount, MachineState state) {
|
public void payAmountToB(long amount, MachineState state) {
|
||||||
byte[] publicKey = state.getB();
|
byte[] publicKey = state.getB();
|
||||||
|
|
||||||
PublicKeyAccount recipient = new PublicKeyAccount(this.repository, publicKey);
|
PublicKeyAccount recipient = new PublicKeyAccount(this.repository, publicKey);
|
||||||
|
|
||||||
long timestamp = this.getNextTransactionTimestamp();
|
long timestamp = this.getNextTransactionTimestamp();
|
||||||
byte[] reference = this.getLastReference();
|
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(),
|
ATTransactionData atTransactionData = new ATTransactionData(baseTransactionData, this.atData.getATAddress(),
|
||||||
recipient.getAddress(), amount, this.atData.getAssetId(), new byte[0]);
|
recipient.getAddress(), amount, this.atData.getAssetId(), new byte[0]);
|
||||||
AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData);
|
AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData);
|
||||||
@ -289,9 +287,9 @@ public class QortalATAPI extends API {
|
|||||||
long timestamp = this.getNextTransactionTimestamp();
|
long timestamp = this.getNextTransactionTimestamp();
|
||||||
byte[] reference = this.getLastReference();
|
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(),
|
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);
|
AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData);
|
||||||
|
|
||||||
// Add to our transactions
|
// Add to our transactions
|
||||||
@ -314,11 +312,10 @@ public class QortalATAPI extends API {
|
|||||||
Account creator = this.getCreator();
|
Account creator = this.getCreator();
|
||||||
long timestamp = this.getNextTransactionTimestamp();
|
long timestamp = this.getNextTransactionTimestamp();
|
||||||
byte[] reference = this.getLastReference();
|
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(),
|
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);
|
AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData);
|
||||||
|
|
||||||
// Add to our transactions
|
// Add to our transactions
|
||||||
|
@ -50,6 +50,7 @@ import org.qortal.transform.TransformationException;
|
|||||||
import org.qortal.transform.Transformer;
|
import org.qortal.transform.Transformer;
|
||||||
import org.qortal.transform.block.BlockTransformer;
|
import org.qortal.transform.block.BlockTransformer;
|
||||||
import org.qortal.transform.transaction.TransactionTransformer;
|
import org.qortal.transform.transaction.TransactionTransformer;
|
||||||
|
import org.qortal.utils.Amounts;
|
||||||
import org.qortal.utils.Base58;
|
import org.qortal.utils.Base58;
|
||||||
import org.qortal.utils.NTP;
|
import org.qortal.utils.NTP;
|
||||||
import org.roaringbitmap.IntIterator;
|
import org.roaringbitmap.IntIterator;
|
||||||
@ -123,15 +124,14 @@ public class Block {
|
|||||||
/** Locally-generated AT states */
|
/** Locally-generated AT states */
|
||||||
protected List<ATStateData> ourAtStates;
|
protected List<ATStateData> ourAtStates;
|
||||||
/** Locally-generated AT fees */
|
/** Locally-generated AT fees */
|
||||||
protected BigDecimal ourAtFees; // Generated locally
|
protected long ourAtFees; // Generated locally
|
||||||
|
|
||||||
/** Lazy-instantiated expanded info on block's online accounts. */
|
/** Lazy-instantiated expanded info on block's online accounts. */
|
||||||
static class ExpandedAccount {
|
static class ExpandedAccount {
|
||||||
private static final BigDecimal oneHundred = BigDecimal.valueOf(100L);
|
|
||||||
|
|
||||||
private final Repository repository;
|
private final Repository repository;
|
||||||
|
|
||||||
private final RewardShareData rewardShareData;
|
private final RewardShareData rewardShareData;
|
||||||
|
private final int sharePercent;
|
||||||
private final boolean isRecipientAlsoMinter;
|
private final boolean isRecipientAlsoMinter;
|
||||||
|
|
||||||
private final Account mintingAccount;
|
private final Account mintingAccount;
|
||||||
@ -145,6 +145,7 @@ public class Block {
|
|||||||
ExpandedAccount(Repository repository, int accountIndex) throws DataException {
|
ExpandedAccount(Repository repository, int accountIndex) throws DataException {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.rewardShareData = repository.getAccountRepository().getRewardShareByIndex(accountIndex);
|
this.rewardShareData = repository.getAccountRepository().getRewardShareByIndex(accountIndex);
|
||||||
|
this.sharePercent = this.rewardShareData.getSharePercent();
|
||||||
|
|
||||||
this.mintingAccount = new Account(repository, this.rewardShareData.getMinter());
|
this.mintingAccount = new Account(repository, this.rewardShareData.getMinter());
|
||||||
this.mintingAccountData = repository.getAccountRepository().getAccount(this.mintingAccount.getAddress());
|
this.mintingAccountData = repository.getAccountRepository().getAccount(this.mintingAccount.getAddress());
|
||||||
@ -187,26 +188,23 @@ public class Block {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void distribute(BigDecimal accountAmount) throws DataException {
|
void distribute(long accountAmount) throws DataException {
|
||||||
if (this.isRecipientAlsoMinter) {
|
if (this.isRecipientAlsoMinter) {
|
||||||
// minter & recipient the same - simpler case
|
// minter & recipient the same - simpler case
|
||||||
LOGGER.trace(() -> String.format("Minter/recipient account %s share: %s", this.mintingAccount.getAddress(), accountAmount.toPlainString()));
|
LOGGER.trace(() -> String.format("Minter/recipient account %s share: %s", this.mintingAccount.getAddress(), Amounts.prettyAmount(accountAmount)));
|
||||||
if (accountAmount.signum() != 0)
|
if (accountAmount != 0)
|
||||||
// this.mintingAccount.setConfirmedBalance(Asset.QORT, this.mintingAccount.getConfirmedBalance(Asset.QORT).add(accountAmount));
|
|
||||||
this.repository.getAccountRepository().modifyAssetBalance(this.mintingAccount.getAddress(), Asset.QORT, accountAmount);
|
this.repository.getAccountRepository().modifyAssetBalance(this.mintingAccount.getAddress(), Asset.QORT, accountAmount);
|
||||||
} else {
|
} else {
|
||||||
// minter & recipient different - extra work needed
|
// minter & recipient different - extra work needed
|
||||||
BigDecimal recipientAmount = accountAmount.multiply(this.rewardShareData.getSharePercent()).divide(oneHundred, RoundingMode.DOWN);
|
long recipientAmount = (accountAmount * this.sharePercent) / 100L / 100L; // because scaled by 2dp and 'percent' means "per 1e2"
|
||||||
BigDecimal minterAmount = accountAmount.subtract(recipientAmount);
|
long minterAmount = accountAmount - recipientAmount;
|
||||||
|
|
||||||
LOGGER.trace(() -> String.format("Minter account %s share: %s", this.mintingAccount.getAddress(), minterAmount.toPlainString()));
|
LOGGER.trace(() -> String.format("Minter account %s share: %s", this.mintingAccount.getAddress(), Amounts.prettyAmount(minterAmount)));
|
||||||
if (minterAmount.signum() != 0)
|
if (minterAmount != 0)
|
||||||
// this.mintingAccount.setConfirmedBalance(Asset.QORT, this.mintingAccount.getConfirmedBalance(Asset.QORT).add(minterAmount));
|
|
||||||
this.repository.getAccountRepository().modifyAssetBalance(this.mintingAccount.getAddress(), Asset.QORT, minterAmount);
|
this.repository.getAccountRepository().modifyAssetBalance(this.mintingAccount.getAddress(), Asset.QORT, minterAmount);
|
||||||
|
|
||||||
LOGGER.trace(() -> String.format("Recipient account %s share: %s", this.recipientAccount.getAddress(), recipientAmount.toPlainString()));
|
LOGGER.trace(() -> String.format("Recipient account %s share: %s", this.recipientAccount.getAddress(), Amounts.prettyAmount(recipientAmount)));
|
||||||
if (recipientAmount.signum() != 0)
|
if (recipientAmount != 0)
|
||||||
// this.recipientAccount.setConfirmedBalance(Asset.QORT, this.recipientAccount.getConfirmedBalance(Asset.QORT).add(recipientAmount));
|
|
||||||
this.repository.getAccountRepository().modifyAssetBalance(this.recipientAccount.getAddress(), Asset.QORT, recipientAmount);
|
this.repository.getAccountRepository().modifyAssetBalance(this.recipientAccount.getAddress(), Asset.QORT, recipientAmount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,17 +254,17 @@ public class Block {
|
|||||||
|
|
||||||
this.transactions = new ArrayList<>();
|
this.transactions = new ArrayList<>();
|
||||||
|
|
||||||
BigDecimal totalFees = BigDecimal.ZERO.setScale(8);
|
long totalFees = 0;
|
||||||
|
|
||||||
// We have to sum fees too
|
// We have to sum fees too
|
||||||
for (TransactionData transactionData : transactions) {
|
for (TransactionData transactionData : transactions) {
|
||||||
this.transactions.add(Transaction.fromData(repository, transactionData));
|
this.transactions.add(Transaction.fromData(repository, transactionData));
|
||||||
totalFees = totalFees.add(transactionData.getFee());
|
totalFees += transactionData.getFee();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.atStates = atStates;
|
this.atStates = atStates;
|
||||||
for (ATStateData atState : atStates)
|
for (ATStateData atState : atStates)
|
||||||
totalFees = totalFees.add(atState.getFees());
|
totalFees += atState.getFees();
|
||||||
|
|
||||||
this.blockData.setTotalFees(totalFees);
|
this.blockData.setTotalFees(totalFees);
|
||||||
}
|
}
|
||||||
@ -361,8 +359,8 @@ public class Block {
|
|||||||
int height = parentBlockData.getHeight() + 1;
|
int height = parentBlockData.getHeight() + 1;
|
||||||
|
|
||||||
int atCount = 0;
|
int atCount = 0;
|
||||||
BigDecimal atFees = BigDecimal.ZERO.setScale(8);
|
long atFees = 0;
|
||||||
BigDecimal totalFees = atFees;
|
long totalFees = 0;
|
||||||
|
|
||||||
// This instance used for AT processing
|
// This instance used for AT processing
|
||||||
BlockData preAtBlockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp,
|
BlockData preAtBlockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp,
|
||||||
@ -427,12 +425,12 @@ public class Block {
|
|||||||
|
|
||||||
newBlock.transactions = this.transactions;
|
newBlock.transactions = this.transactions;
|
||||||
int transactionCount = this.blockData.getTransactionCount();
|
int transactionCount = this.blockData.getTransactionCount();
|
||||||
BigDecimal totalFees = this.blockData.getTotalFees();
|
long totalFees = this.blockData.getTotalFees();
|
||||||
byte[] transactionsSignature = null; // We'll calculate this later
|
byte[] transactionsSignature = null; // We'll calculate this later
|
||||||
Integer height = this.blockData.getHeight();
|
Integer height = this.blockData.getHeight();
|
||||||
|
|
||||||
int atCount = newBlock.ourAtStates.size();
|
int atCount = newBlock.ourAtStates.size();
|
||||||
BigDecimal atFees = newBlock.ourAtFees;
|
long atFees = newBlock.ourAtFees;
|
||||||
|
|
||||||
byte[] encodedOnlineAccounts = this.blockData.getEncodedOnlineAccounts();
|
byte[] encodedOnlineAccounts = this.blockData.getEncodedOnlineAccounts();
|
||||||
int onlineAccountsCount = this.blockData.getOnlineAccountsCount();
|
int onlineAccountsCount = this.blockData.getOnlineAccountsCount();
|
||||||
@ -648,7 +646,7 @@ public class Block {
|
|||||||
this.blockData.setTransactionCount(this.blockData.getTransactionCount() + 1);
|
this.blockData.setTransactionCount(this.blockData.getTransactionCount() + 1);
|
||||||
|
|
||||||
// Update totalFees
|
// 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
|
// We've added a transaction, so recalculate transactions signature
|
||||||
calcTransactionsSignature();
|
calcTransactionsSignature();
|
||||||
@ -691,7 +689,7 @@ public class Block {
|
|||||||
this.blockData.setTransactionCount(this.blockData.getTransactionCount() - 1);
|
this.blockData.setTransactionCount(this.blockData.getTransactionCount() - 1);
|
||||||
|
|
||||||
// Update totalFees
|
// 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
|
// We've removed a transaction, so recalculate transactions signature
|
||||||
calcTransactionsSignature();
|
calcTransactionsSignature();
|
||||||
@ -1118,7 +1116,7 @@ public class Block {
|
|||||||
if (this.ourAtStates.size() != this.blockData.getATCount())
|
if (this.ourAtStates.size() != this.blockData.getATCount())
|
||||||
return ValidationResult.AT_STATES_MISMATCH;
|
return ValidationResult.AT_STATES_MISMATCH;
|
||||||
|
|
||||||
if (this.ourAtFees.compareTo(this.blockData.getATFees()) != 0)
|
if (this.ourAtFees != this.blockData.getATFees())
|
||||||
return ValidationResult.AT_STATES_MISMATCH;
|
return ValidationResult.AT_STATES_MISMATCH;
|
||||||
|
|
||||||
// Note: this.atStates fully loaded thanks to this.getATStates() call above
|
// 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()))
|
if (!Arrays.equals(ourAtState.getStateHash(), theirAtState.getStateHash()))
|
||||||
return ValidationResult.AT_STATES_MISMATCH;
|
return ValidationResult.AT_STATES_MISMATCH;
|
||||||
|
|
||||||
if (ourAtState.getFees().compareTo(theirAtState.getFees()) != 0)
|
if (ourAtState.getFees() != theirAtState.getFees())
|
||||||
return ValidationResult.AT_STATES_MISMATCH;
|
return ValidationResult.AT_STATES_MISMATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1167,7 +1165,7 @@ public class Block {
|
|||||||
List<AtTransaction> allAtTransactions = new ArrayList<>();
|
List<AtTransaction> allAtTransactions = new ArrayList<>();
|
||||||
|
|
||||||
this.ourAtStates = 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
|
// Find all executable ATs, ordered by earliest creation date first
|
||||||
List<ATData> executableATs = this.repository.getATRepository().getAllExecutableATs();
|
List<ATData> executableATs = this.repository.getATRepository().getAllExecutableATs();
|
||||||
@ -1182,7 +1180,7 @@ public class Block {
|
|||||||
ATStateData atStateData = at.getATStateData();
|
ATStateData atStateData = at.getATStateData();
|
||||||
this.ourAtStates.add(atStateData);
|
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
|
// Prepend our entire AT-Transactions/states to block's transactions
|
||||||
@ -1313,7 +1311,7 @@ public class Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void processBlockRewards() throws DataException {
|
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?
|
// No reward for our height?
|
||||||
if (reward == null)
|
if (reward == null)
|
||||||
@ -1396,10 +1394,10 @@ public class Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void rewardTransactionFees() throws DataException {
|
protected void rewardTransactionFees() throws DataException {
|
||||||
BigDecimal blockFees = this.blockData.getTotalFees();
|
long blockFees = this.blockData.getTotalFees();
|
||||||
|
|
||||||
// No transaction fees?
|
// No transaction fees?
|
||||||
if (blockFees.compareTo(BigDecimal.ZERO) <= 0)
|
if (blockFees <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
distributeBlockReward(blockFees);
|
distributeBlockReward(blockFees);
|
||||||
@ -1412,7 +1410,7 @@ public class Block {
|
|||||||
Account atAccount = new Account(this.repository, atState.getATAddress());
|
Account atAccount = new Account(this.repository, atState.getATAddress());
|
||||||
|
|
||||||
// Subtract AT-generated fees from AT accounts
|
// 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);
|
atRepository.save(atState);
|
||||||
}
|
}
|
||||||
@ -1439,8 +1437,7 @@ public class Block {
|
|||||||
// No longer unconfirmed
|
// No longer unconfirmed
|
||||||
transactionRepository.confirmTransaction(transactionData.getSignature());
|
transactionRepository.confirmTransaction(transactionData.getSignature());
|
||||||
|
|
||||||
List<Account> participants = transaction.getInvolvedAccounts();
|
List<String> participantAddresses = transaction.getInvolvedAddresses();
|
||||||
List<String> participantAddresses = participants.stream().map(Account::getAddress).collect(Collectors.toList());
|
|
||||||
transactionRepository.saveParticipants(transactionData, participantAddresses);
|
transactionRepository.saveParticipants(transactionData, participantAddresses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1547,23 +1544,23 @@ public class Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void orphanBlockRewards() throws DataException {
|
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?
|
// No reward for our height?
|
||||||
if (reward == null)
|
if (reward == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
distributeBlockReward(reward.negate());
|
distributeBlockReward(0 - reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deductTransactionFees() throws DataException {
|
protected void deductTransactionFees() throws DataException {
|
||||||
BigDecimal blockFees = this.blockData.getTotalFees();
|
long blockFees = this.blockData.getTotalFees();
|
||||||
|
|
||||||
// No transaction fees?
|
// No transaction fees?
|
||||||
if (blockFees.compareTo(BigDecimal.ZERO) <= 0)
|
if (blockFees <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
distributeBlockReward(blockFees.negate());
|
distributeBlockReward(0 - blockFees);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void orphanAtFeesAndStates() throws DataException {
|
protected void orphanAtFeesAndStates() throws DataException {
|
||||||
@ -1572,7 +1569,7 @@ public class Block {
|
|||||||
Account atAccount = new Account(this.repository, atState.getATAddress());
|
Account atAccount = new Account(this.repository, atState.getATAddress());
|
||||||
|
|
||||||
// Return AT-generated fees to AT accounts
|
// 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
|
// Delete ATStateData for this height
|
||||||
@ -1630,175 +1627,168 @@ public class Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void distributeBlockReward(BigDecimal totalAmount) throws DataException {
|
protected void distributeBlockReward(long totalAmount) throws DataException {
|
||||||
LOGGER.trace(() -> String.format("Distributing: %s", totalAmount.toPlainString()));
|
LOGGER.trace(() -> String.format("Distributing: %s", Amounts.prettyAmount(totalAmount)));
|
||||||
|
|
||||||
// Distribute according to account level
|
// Distribute according to account level
|
||||||
BigDecimal sharedByLevelAmount = distributeBlockRewardByLevel(totalAmount);
|
long sharedByLevelAmount = distributeBlockRewardByLevel(totalAmount);
|
||||||
LOGGER.trace(() -> String.format("Shared %s of %s based on account levels", sharedByLevelAmount.toPlainString(), totalAmount.toPlainString()));
|
LOGGER.trace(() -> String.format("Shared %s of %s based on account levels", Amounts.prettyAmount(sharedByLevelAmount), Amounts.prettyAmount(totalAmount)));
|
||||||
|
|
||||||
// Distribute amongst legacy QORA holders
|
// Distribute amongst legacy QORA holders
|
||||||
BigDecimal sharedByQoraHoldersAmount = distributeBlockRewardToQoraHolders(totalAmount);
|
long sharedByQoraHoldersAmount = distributeBlockRewardToQoraHolders(totalAmount);
|
||||||
LOGGER.trace(() -> String.format("Shared %s of %s to legacy QORA holders", sharedByQoraHoldersAmount.toPlainString(), totalAmount.toPlainString()));
|
LOGGER.trace(() -> String.format("Shared %s of %s to legacy QORA holders", Amounts.prettyAmount(sharedByQoraHoldersAmount), Amounts.prettyAmount(totalAmount)));
|
||||||
|
|
||||||
// Spread remainder across founder accounts
|
// Spread remainder across founder accounts
|
||||||
BigDecimal foundersAmount = totalAmount.subtract(sharedByLevelAmount).subtract(sharedByQoraHoldersAmount);
|
long foundersAmount = totalAmount - sharedByLevelAmount - sharedByQoraHoldersAmount;
|
||||||
distributeBlockRewardToFounders(foundersAmount);
|
distributeBlockRewardToFounders(foundersAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal distributeBlockRewardByLevel(BigDecimal totalAmount) throws DataException {
|
private long distributeBlockRewardByLevel(long totalAmount) throws DataException {
|
||||||
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
||||||
List<ShareByLevel> sharesByLevel = BlockChain.getInstance().getBlockSharesByLevel();
|
List<ShareByLevel> sharesByLevel = BlockChain.getInstance().getBlockSharesByLevel();
|
||||||
|
|
||||||
// Distribute amount across bins
|
// Distribute amount across bins
|
||||||
BigDecimal sharedAmount = BigDecimal.ZERO;
|
long sharedAmount = 0;
|
||||||
for (int s = 0; s < sharesByLevel.size(); ++s) {
|
for (int s = 0; s < sharesByLevel.size(); ++s) {
|
||||||
final int binIndex = s;
|
final int binIndex = s;
|
||||||
|
|
||||||
BigDecimal binAmount = sharesByLevel.get(binIndex).share.multiply(totalAmount).setScale(8, RoundingMode.DOWN);
|
long binAmount = (totalAmount * sharesByLevel.get(binIndex).unscaledShare) / 100000000L;
|
||||||
LOGGER.trace(() -> String.format("Bin %d share of %s: %s", binIndex, totalAmount.toPlainString(), binAmount.toPlainString()));
|
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.
|
// 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());
|
List<ExpandedAccount> binnedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.getShareBin() == binIndex).collect(Collectors.toList());
|
||||||
if (binnedAccounts.isEmpty())
|
if (binnedAccounts.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
BigDecimal binSize = BigDecimal.valueOf(binnedAccounts.size());
|
long perAccountAmount = binAmount / binnedAccounts.size();
|
||||||
BigDecimal accountAmount = binAmount.divide(binSize, RoundingMode.DOWN);
|
|
||||||
|
|
||||||
for (int a = 0; a < binnedAccounts.size(); ++a) {
|
for (int a = 0; a < binnedAccounts.size(); ++a) {
|
||||||
ExpandedAccount expandedAccount = binnedAccounts.get(a);
|
ExpandedAccount expandedAccount = binnedAccounts.get(a);
|
||||||
expandedAccount.distribute(accountAmount);
|
expandedAccount.distribute(perAccountAmount);
|
||||||
sharedAmount = sharedAmount.add(accountAmount);
|
sharedAmount += perAccountAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharedAmount;
|
return sharedAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal distributeBlockRewardToQoraHolders(BigDecimal totalAmount) throws DataException {
|
private long distributeBlockRewardToQoraHolders(long totalAmount) throws DataException {
|
||||||
BigDecimal qoraHoldersAmount = BlockChain.getInstance().getQoraHoldersShare().multiply(totalAmount).setScale(8, RoundingMode.DOWN);
|
long qoraHoldersAmount = (BlockChain.getInstance().getQoraHoldersUnscaledShare() * totalAmount) / 100000000L;
|
||||||
LOGGER.trace(() -> String.format("Legacy QORA holders share of %s: %s", totalAmount.toPlainString(), qoraHoldersAmount.toPlainString()));
|
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());
|
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)
|
for (int i = 0; i < qoraHolders.size(); ++i)
|
||||||
totalQoraHeld = totalQoraHeld.add(qoraHolders.get(i).getBalance());
|
totalQoraHeld += qoraHolders.get(i).getBalance();
|
||||||
|
|
||||||
BigDecimal finalTotalQoraHeld = totalQoraHeld;
|
long finalTotalQoraHeld = totalQoraHeld;
|
||||||
LOGGER.trace(() -> String.format("Total legacy QORA held: %s", finalTotalQoraHeld.toPlainString()));
|
LOGGER.trace(() -> String.format("Total legacy QORA held: %s", Amounts.prettyAmount(finalTotalQoraHeld)));
|
||||||
|
|
||||||
BigDecimal sharedAmount = BigDecimal.ZERO;
|
long sharedAmount = 0;
|
||||||
if (totalQoraHeld.signum() <= 0)
|
if (totalQoraHeld <= 0)
|
||||||
return sharedAmount;
|
return sharedAmount;
|
||||||
|
|
||||||
for (int h = 0; h < qoraHolders.size(); ++h) {
|
for (int h = 0; h < qoraHolders.size(); ++h) {
|
||||||
AccountBalanceData qoraHolder = qoraHolders.get(h);
|
AccountBalanceData qoraHolder = qoraHolders.get(h);
|
||||||
|
|
||||||
BigDecimal holderReward = qoraHoldersAmount.multiply(qoraHolder.getBalance()).divide(totalQoraHeld, RoundingMode.DOWN).setScale(8, RoundingMode.DOWN);
|
long holderReward = (qoraHoldersAmount * qoraHolder.getBalance()) / totalQoraHeld;
|
||||||
BigDecimal finalHolderReward = holderReward;
|
long finalHolderReward = holderReward;
|
||||||
LOGGER.trace(() -> String.format("QORA holder %s has %s / %s QORA so share: %s",
|
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?
|
// Too small to register this time?
|
||||||
if (holderReward.signum() == 0)
|
if (holderReward == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Account qoraHolderAccount = new Account(repository, qoraHolder.getAddress());
|
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 processing, make sure we don't overpay
|
||||||
if (isProcessingNotOrphaning) {
|
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
|
// Reduce final QORT-from-QORA payment to match max
|
||||||
BigDecimal adjustment = newQortFromQoraBalance.subtract(maxQortFromQora);
|
long adjustment = newQortFromQoraBalance - maxQortFromQora;
|
||||||
|
|
||||||
holderReward = holderReward.subtract(adjustment);
|
holderReward -= adjustment;
|
||||||
newQortFromQoraBalance = newQortFromQoraBalance.subtract(adjustment);
|
newQortFromQoraBalance -= adjustment;
|
||||||
|
|
||||||
// This is also the QORA holder's final QORT-from-QORA block
|
// This is also the QORA holder's final QORT-from-QORA block
|
||||||
QortFromQoraData qortFromQoraData = new QortFromQoraData(qoraHolder.getAddress(), holderReward, this.blockData.getHeight());
|
QortFromQoraData qortFromQoraData = new QortFromQoraData(qoraHolder.getAddress(), holderReward, this.blockData.getHeight());
|
||||||
this.repository.getAccountRepository().save(qortFromQoraData);
|
this.repository.getAccountRepository().save(qortFromQoraData);
|
||||||
|
|
||||||
BigDecimal finalAdjustedHolderReward = holderReward;
|
long finalAdjustedHolderReward = holderReward;
|
||||||
LOGGER.trace(() -> String.format("QORA holder %s final share %s at height %d",
|
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 {
|
} else {
|
||||||
// Orphaning
|
// Orphaning
|
||||||
QortFromQoraData qortFromQoraData = this.repository.getAccountRepository().getQortFromQoraInfo(qoraHolder.getAddress());
|
QortFromQoraData qortFromQoraData = this.repository.getAccountRepository().getQortFromQoraInfo(qoraHolder.getAddress());
|
||||||
if (qortFromQoraData != null) {
|
if (qortFromQoraData != null) {
|
||||||
// Final QORT-from-QORA amount from repository was stored during processing, and hence positive.
|
// 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.
|
// So we use + here as qortFromQora is negative during orphaning.
|
||||||
// More efficient than holderReward.subtract(final-qort-from-qora.negate())
|
// More efficient than "holderReward - (0 - final-qort-from-qora)"
|
||||||
BigDecimal adjustment = holderReward.add(qortFromQoraData.getFinalQortFromQora());
|
long adjustment = holderReward + qortFromQoraData.getFinalQortFromQora();
|
||||||
|
|
||||||
holderReward = holderReward.subtract(adjustment);
|
holderReward -= adjustment;
|
||||||
newQortFromQoraBalance = newQortFromQoraBalance.subtract(adjustment);
|
newQortFromQoraBalance -= adjustment;
|
||||||
|
|
||||||
this.repository.getAccountRepository().deleteQortFromQoraInfo(qoraHolder.getAddress());
|
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",
|
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);
|
this.repository.getAccountRepository().modifyAssetBalance(qoraHolder.getAddress(), Asset.QORT, holderReward);
|
||||||
|
|
||||||
if (newQortFromQoraBalance.signum() > 0)
|
if (newQortFromQoraBalance > 0)
|
||||||
qoraHolderAccount.setConfirmedBalance(Asset.QORT_FROM_QORA, newQortFromQoraBalance);
|
qoraHolderAccount.setConfirmedBalance(Asset.QORT_FROM_QORA, newQortFromQoraBalance);
|
||||||
else
|
else
|
||||||
// Remove QORT_FROM_QORA balance as it's zero
|
// Remove QORT_FROM_QORA balance as it's zero
|
||||||
qoraHolderAccount.deleteBalance(Asset.QORT_FROM_QORA);
|
qoraHolderAccount.deleteBalance(Asset.QORT_FROM_QORA);
|
||||||
|
|
||||||
sharedAmount = sharedAmount.add(holderReward);
|
sharedAmount += holderReward;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharedAmount;
|
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
|
// Remaining reward portion is spread across all founders, online or not
|
||||||
List<AccountData> founderAccounts = this.repository.getAccountRepository().getFlaggedAccounts(Account.FOUNDER_FLAG);
|
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",
|
LOGGER.trace(() -> String.format("Sharing remaining %s to %d founder%s, %s each",
|
||||||
foundersAmount.toPlainString(), founderAccounts.size(), (founderAccounts.size() != 1 ? "s" : ""),
|
Amounts.prettyAmount(foundersAmount),
|
||||||
perFounderAmount.toPlainString()));
|
founderAccounts.size(), (founderAccounts.size() != 1 ? "s" : ""),
|
||||||
|
Amounts.prettyAmount(perFounderAmount)));
|
||||||
|
|
||||||
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
||||||
for (int a = 0; a < founderAccounts.size(); ++a) {
|
for (int a = 0; a < founderAccounts.size(); ++a) {
|
||||||
Account founderAccount = new Account(this.repository, founderAccounts.get(a).getAddress());
|
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.
|
// 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(
|
List<ExpandedAccount> founderExpandedAccounts = expandedAccounts.stream().filter(
|
||||||
accountInfo -> accountInfo.isMinterFounder &&
|
accountInfo -> accountInfo.isMinterFounder &&
|
||||||
accountInfo.mintingAccountData.getAddress().equals(founderAccount.getAddress())
|
accountInfo.mintingAccountData.getAddress().equals(founderAccount.getAddress())
|
||||||
).collect(Collectors.toList());
|
).collect(Collectors.toList());
|
||||||
*/
|
|
||||||
|
|
||||||
// Broken version:
|
|
||||||
List<ExpandedAccount> founderExpandedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.isMinterFounder).collect(Collectors.toList());
|
|
||||||
|
|
||||||
if (founderExpandedAccounts.isEmpty()) {
|
if (founderExpandedAccounts.isEmpty()) {
|
||||||
// Simple case: no founder-as-minter reward-shares online so founder gets whole amount.
|
// 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);
|
this.repository.getAccountRepository().modifyAssetBalance(founderAccount.getAddress(), Asset.QORT, perFounderAmount);
|
||||||
} else {
|
} else {
|
||||||
// Distribute over reward-shares
|
// 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)
|
for (int fea = 0; fea < founderExpandedAccounts.size(); ++fea)
|
||||||
founderExpandedAccounts.get(fea).distribute(perFounderRewardShareAmount);
|
founderExpandedAccounts.get(fea).distribute(perFounderRewardShareAmount);
|
||||||
|
@ -4,7 +4,6 @@ import java.io.File;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -57,8 +56,9 @@ public class BlockChain {
|
|||||||
private long transactionExpiryPeriod;
|
private long transactionExpiryPeriod;
|
||||||
|
|
||||||
private BigDecimal unitFee;
|
private BigDecimal unitFee;
|
||||||
private BigDecimal maxBytesPerUnitFee;
|
private long unscaledUnitFee; // calculated after unmarshal
|
||||||
private BigDecimal minFeePerByte;
|
|
||||||
|
private int maxBytesPerUnitFee;
|
||||||
|
|
||||||
/** Maximum acceptable timestamp disagreement offset in milliseconds. */
|
/** Maximum acceptable timestamp disagreement offset in milliseconds. */
|
||||||
private long blockTimestampMargin;
|
private long blockTimestampMargin;
|
||||||
@ -87,6 +87,7 @@ public class BlockChain {
|
|||||||
public static class RewardByHeight {
|
public static class RewardByHeight {
|
||||||
public int height;
|
public int height;
|
||||||
public BigDecimal reward;
|
public BigDecimal reward;
|
||||||
|
public long unscaledReward; // reward * 1e8, calculated after unmarshal
|
||||||
}
|
}
|
||||||
List<RewardByHeight> rewardsByHeight;
|
List<RewardByHeight> rewardsByHeight;
|
||||||
|
|
||||||
@ -94,13 +95,18 @@ public class BlockChain {
|
|||||||
public static class ShareByLevel {
|
public static class ShareByLevel {
|
||||||
public List<Integer> levels;
|
public List<Integer> levels;
|
||||||
public BigDecimal share;
|
public BigDecimal share;
|
||||||
|
public long unscaledShare; // share * 1e8, calculated after unmarshal
|
||||||
}
|
}
|
||||||
List<ShareByLevel> sharesByLevel;
|
List<ShareByLevel> sharesByLevel;
|
||||||
|
|
||||||
/** Share of block reward/fees to legacy QORA coin holders */
|
/** Share of block reward/fees to legacy QORA coin holders */
|
||||||
BigDecimal qoraHoldersShare;
|
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. */
|
/** How many legacy QORA per 1 QORT of block reward. */
|
||||||
BigDecimal qoraPerQortReward;
|
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.
|
* Number of minted blocks required to reach next level from previous.
|
||||||
@ -265,12 +271,12 @@ public class BlockChain {
|
|||||||
return this.unitFee;
|
return this.unitFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getMaxBytesPerUnitFee() {
|
public long getUnscaledUnitFee() {
|
||||||
return this.maxBytesPerUnitFee;
|
return this.unscaledUnitFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getMinFeePerByte() {
|
public int getMaxBytesPerUnitFee() {
|
||||||
return this.minFeePerByte;
|
return this.maxBytesPerUnitFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTransactionExpiryPeriod() {
|
public long getTransactionExpiryPeriod() {
|
||||||
@ -318,10 +324,18 @@ public class BlockChain {
|
|||||||
return this.qoraHoldersShare;
|
return this.qoraHoldersShare;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getQoraHoldersUnscaledShare() {
|
||||||
|
return this.qoraHoldersUnscaledShare;
|
||||||
|
}
|
||||||
|
|
||||||
public BigDecimal getQoraPerQortReward() {
|
public BigDecimal getQoraPerQortReward() {
|
||||||
return this.qoraPerQortReward;
|
return this.qoraPerQortReward;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getUnscaledQoraPerQortReward() {
|
||||||
|
return this.unscaledQoraPerQortReward;
|
||||||
|
}
|
||||||
|
|
||||||
public int getMinAccountLevelToMint() {
|
public int getMinAccountLevelToMint() {
|
||||||
return this.minAccountLevelToMint;
|
return this.minAccountLevelToMint;
|
||||||
}
|
}
|
||||||
@ -350,11 +364,11 @@ public class BlockChain {
|
|||||||
|
|
||||||
// More complex getters for aspects that change by height or timestamp
|
// 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
|
// Scan through for reward at our height
|
||||||
for (int i = rewardsByHeight.size() - 1; i >= 0; --i)
|
for (int i = rewardsByHeight.size() - 1; i >= 0; --i)
|
||||||
if (rewardsByHeight.get(i).height <= ourHeight)
|
if (rewardsByHeight.get(i).height <= ourHeight)
|
||||||
return rewardsByHeight.get(i).reward;
|
return rewardsByHeight.get(i).unscaledReward;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -416,9 +430,8 @@ public class BlockChain {
|
|||||||
|
|
||||||
/** Minor normalization, cached value generation, etc. */
|
/** Minor normalization, cached value generation, etc. */
|
||||||
private void fixUp() {
|
private void fixUp() {
|
||||||
this.maxBytesPerUnitFee = this.maxBytesPerUnitFee.setScale(8);
|
|
||||||
this.unitFee = this.unitFee.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
|
// Pre-calculate cumulative blocks required for each level
|
||||||
int cumulativeBlocks = 0;
|
int cumulativeBlocks = 0;
|
||||||
@ -430,6 +443,17 @@ public class BlockChain {
|
|||||||
cumulativeBlocks += this.blocksNeededByLevel.get(level);
|
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
|
// Convert collections to unmodifiable form
|
||||||
this.rewardsByHeight = Collections.unmodifiableList(this.rewardsByHeight);
|
this.rewardsByHeight = Collections.unmodifiableList(this.rewardsByHeight);
|
||||||
this.sharesByLevel = Collections.unmodifiableList(this.sharesByLevel);
|
this.sharesByLevel = Collections.unmodifiableList(this.sharesByLevel);
|
||||||
|
@ -2,7 +2,6 @@ package org.qortal.block;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -86,7 +85,7 @@ public class GenesisBlock extends Block {
|
|||||||
// Add default values to transactions
|
// Add default values to transactions
|
||||||
transactionsData.stream().forEach(transactionData -> {
|
transactionsData.stream().forEach(transactionData -> {
|
||||||
if (transactionData.getFee() == null)
|
if (transactionData.getFee() == null)
|
||||||
transactionData.setFee(BigDecimal.ZERO.setScale(8));
|
transactionData.setFee(0L);
|
||||||
|
|
||||||
if (transactionData.getCreatorPublicKey() == null)
|
if (transactionData.getCreatorPublicKey() == null)
|
||||||
transactionData.setCreatorPublicKey(NullAccount.PUBLIC_KEY);
|
transactionData.setCreatorPublicKey(NullAccount.PUBLIC_KEY);
|
||||||
@ -97,14 +96,14 @@ public class GenesisBlock extends Block {
|
|||||||
|
|
||||||
byte[] reference = GENESIS_BLOCK_REFERENCE;
|
byte[] reference = GENESIS_BLOCK_REFERENCE;
|
||||||
int transactionCount = transactionsData.size();
|
int transactionCount = transactionsData.size();
|
||||||
BigDecimal totalFees = BigDecimal.ZERO.setScale(8);
|
long totalFees = 0;
|
||||||
byte[] minterPublicKey = NullAccount.PUBLIC_KEY;
|
byte[] minterPublicKey = NullAccount.PUBLIC_KEY;
|
||||||
byte[] bytesForSignature = getBytesForMinterSignature(info.timestamp, reference, minterPublicKey);
|
byte[] bytesForSignature = getBytesForMinterSignature(info.timestamp, reference, minterPublicKey);
|
||||||
byte[] minterSignature = calcGenesisMinterSignature(bytesForSignature);
|
byte[] minterSignature = calcGenesisMinterSignature(bytesForSignature);
|
||||||
byte[] transactionsSignature = calcGenesisTransactionsSignature();
|
byte[] transactionsSignature = calcGenesisTransactionsSignature();
|
||||||
int height = 1;
|
int height = 1;
|
||||||
int atCount = 0;
|
int atCount = 0;
|
||||||
BigDecimal atFees = BigDecimal.ZERO.setScale(8);
|
long atFees = 0;
|
||||||
|
|
||||||
genesisBlockData = new BlockData(info.version, reference, transactionCount, totalFees, transactionsSignature, height, info.timestamp,
|
genesisBlockData = new BlockData(info.version, reference, transactionCount, totalFees, transactionsSignature, height, info.timestamp,
|
||||||
minterPublicKey, minterSignature, atCount, atFees);
|
minterPublicKey, minterSignature, atCount, atFees);
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
package org.qortal.data;
|
package org.qortal.data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
// All properties to be converted to JSON via JAXB
|
// All properties to be converted to JSON via JAXB
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
public class PaymentData {
|
public class PaymentData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private String recipient;
|
private String recipient;
|
||||||
|
|
||||||
private long assetId;
|
private long assetId;
|
||||||
private BigDecimal amount;
|
|
||||||
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
private long amount;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -20,7 +23,7 @@ public class PaymentData {
|
|||||||
protected PaymentData() {
|
protected PaymentData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public PaymentData(String recipient, long assetId, BigDecimal amount) {
|
public PaymentData(String recipient, long assetId, long amount) {
|
||||||
this.recipient = recipient;
|
this.recipient = recipient;
|
||||||
this.assetId = assetId;
|
this.assetId = assetId;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
@ -36,7 +39,7 @@ public class PaymentData {
|
|||||||
return this.assetId;
|
return this.assetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package org.qortal.data.account;
|
package org.qortal.data.account;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
// All properties to be converted to JSON via JAXB
|
// All properties to be converted to JSON via JAXB
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
@ -12,7 +12,9 @@ public class AccountBalanceData {
|
|||||||
// Properties
|
// Properties
|
||||||
private String address;
|
private String address;
|
||||||
private long assetId;
|
private long assetId;
|
||||||
private BigDecimal balance;
|
|
||||||
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
private long balance;
|
||||||
|
|
||||||
// Not always present:
|
// Not always present:
|
||||||
private Integer height;
|
private Integer height;
|
||||||
@ -24,19 +26,19 @@ public class AccountBalanceData {
|
|||||||
protected AccountBalanceData() {
|
protected AccountBalanceData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountBalanceData(String address, long assetId, BigDecimal balance) {
|
public AccountBalanceData(String address, long assetId, long balance) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.assetId = assetId;
|
this.assetId = assetId;
|
||||||
this.balance = balance;
|
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(address, assetId, balance);
|
||||||
|
|
||||||
this.height = height;
|
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(address, assetId, balance);
|
||||||
|
|
||||||
this.assetName = assetName;
|
this.assetName = assetName;
|
||||||
@ -52,11 +54,11 @@ public class AccountBalanceData {
|
|||||||
return this.assetId;
|
return this.assetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getBalance() {
|
public long getBalance() {
|
||||||
return this.balance;
|
return this.balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBalance(BigDecimal balance) {
|
public void setBalance(long balance) {
|
||||||
this.balance = balance;
|
this.balance = balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
package org.qortal.data.account;
|
package org.qortal.data.account;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
// All properties to be converted to JSON via JAXB
|
// All properties to be converted to JSON via JAXB
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
public class QortFromQoraData {
|
public class QortFromQoraData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private String address;
|
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;
|
private Integer finalBlockHeight;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
@ -21,7 +26,7 @@ public class QortFromQoraData {
|
|||||||
protected QortFromQoraData() {
|
protected QortFromQoraData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public QortFromQoraData(String address, BigDecimal finalQortFromQora, Integer finalBlockHeight) {
|
public QortFromQoraData(String address, Long finalQortFromQora, Integer finalBlockHeight) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.finalQortFromQora = finalQortFromQora;
|
this.finalQortFromQora = finalQortFromQora;
|
||||||
this.finalBlockHeight = finalBlockHeight;
|
this.finalBlockHeight = finalBlockHeight;
|
||||||
@ -33,11 +38,11 @@ public class QortFromQoraData {
|
|||||||
return this.address;
|
return this.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getFinalQortFromQora() {
|
public Long getFinalQortFromQora() {
|
||||||
return this.finalQortFromQora;
|
return this.finalQortFromQora;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFinalQortFromQora(BigDecimal finalQortFromQora) {
|
public void setFinalQortFromQora(Long finalQortFromQora) {
|
||||||
this.finalQortFromQora = finalQortFromQora;
|
this.finalQortFromQora = finalQortFromQora;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,11 @@ public class RewardShareData {
|
|||||||
|
|
||||||
private String recipient;
|
private String recipient;
|
||||||
private byte[] rewardSharePublicKey;
|
private byte[] rewardSharePublicKey;
|
||||||
private BigDecimal sharePercent;
|
|
||||||
|
// JAXB to use separate getter
|
||||||
|
@XmlTransient
|
||||||
|
@Schema(hidden = true)
|
||||||
|
private int sharePercent;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -32,7 +36,7 @@ public class RewardShareData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Used when fetching from repository
|
// 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.minterPublicKey = minterPublicKey;
|
||||||
this.minter = minter;
|
this.minter = minter;
|
||||||
this.recipient = recipient;
|
this.recipient = recipient;
|
||||||
@ -58,13 +62,21 @@ public class RewardShareData {
|
|||||||
return this.rewardSharePublicKey;
|
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;
|
return this.sharePercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some JAXB/API-related getters
|
||||||
|
|
||||||
@XmlElement(name = "mintingAccount")
|
@XmlElement(name = "mintingAccount")
|
||||||
public String getMintingAccount() {
|
public String getMintingAccount() {
|
||||||
return this.minter;
|
return this.minter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "sharePercent")
|
||||||
|
public BigDecimal getSharePercentJaxb() {
|
||||||
|
return BigDecimal.valueOf(this.sharePercent, 2);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ public class AssetData {
|
|||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
// necessary for JAX-RS serialization
|
// necessary for JAXB serialization
|
||||||
protected AssetData() {
|
protected AssetData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package org.qortal.data.asset;
|
package org.qortal.data.asset;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.Marshaller;
|
import javax.xml.bind.Marshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
|
|
||||||
@ -26,13 +25,16 @@ public class OrderData implements Comparable<OrderData> {
|
|||||||
private long wantAssetId;
|
private long wantAssetId;
|
||||||
|
|
||||||
@Schema(description = "amount of highest-assetID asset to trade")
|
@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")
|
@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")
|
@Schema(description = "how much of \"amount\" has traded")
|
||||||
private BigDecimal fulfilled;
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
private long fulfilled;
|
||||||
|
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
|
|
||||||
@ -73,26 +75,23 @@ public class OrderData implements Comparable<OrderData> {
|
|||||||
if (this.creator == null && this.creatorPublicKey != null)
|
if (this.creator == null && this.creatorPublicKey != null)
|
||||||
this.creator = Crypto.toAddress(this.creatorPublicKey);
|
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 we don't have the extra asset name fields then we can't fill in the others
|
||||||
if (this.haveAssetName == null)
|
if (this.haveAssetName == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO: fill in for 'old' pricing scheme
|
|
||||||
|
|
||||||
// 'new' pricing scheme
|
|
||||||
if (this.haveAssetId < this.wantAssetId) {
|
if (this.haveAssetId < this.wantAssetId) {
|
||||||
this.amountAssetId = this.wantAssetId;
|
|
||||||
this.amountAssetName = this.wantAssetName;
|
this.amountAssetName = this.wantAssetName;
|
||||||
this.pricePair = this.haveAssetName + "/" + this.wantAssetName;
|
this.pricePair = this.haveAssetName + "/" + this.wantAssetName;
|
||||||
} else {
|
} else {
|
||||||
this.amountAssetId = this.haveAssetId;
|
|
||||||
this.amountAssetName = this.haveAssetName;
|
this.amountAssetName = this.haveAssetName;
|
||||||
this.pricePair = this.wantAssetName + "/" + this.haveAssetName;
|
this.pricePair = this.wantAssetName + "/" + this.haveAssetName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Constructs OrderData using data from repository, including optional API fields. */
|
/** 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) {
|
boolean isClosed, boolean isFulfilled, String haveAssetName, String wantAssetName) {
|
||||||
this.orderId = orderId;
|
this.orderId = orderId;
|
||||||
this.creatorPublicKey = creatorPublicKey;
|
this.creatorPublicKey = creatorPublicKey;
|
||||||
@ -110,13 +109,13 @@ public class OrderData implements Comparable<OrderData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Constructs OrderData using data from repository, excluding optional API fields. */
|
/** 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);
|
this(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, fulfilled, price, timestamp, isClosed, isFulfilled, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Constructs OrderData using data typically received from network. */
|
/** Constructs OrderData using data typically received from network. */
|
||||||
public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, long timestamp) {
|
public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, long amount, long price, long timestamp) {
|
||||||
this(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, BigDecimal.ZERO.setScale(8), price, timestamp, false, false);
|
this(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, 0 /*fulfilled*/, price, timestamp, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters/setters
|
// Getters/setters
|
||||||
@ -137,19 +136,19 @@ public class OrderData implements Comparable<OrderData> {
|
|||||||
return this.wantAssetId;
|
return this.wantAssetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getFulfilled() {
|
public long getFulfilled() {
|
||||||
return this.fulfilled;
|
return this.fulfilled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFulfilled(BigDecimal fulfilled) {
|
public void setFulfilled(long fulfilled) {
|
||||||
this.fulfilled = fulfilled;
|
this.fulfilled = fulfilled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getPrice() {
|
public long getPrice() {
|
||||||
return this.price;
|
return this.price;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +197,7 @@ public class OrderData implements Comparable<OrderData> {
|
|||||||
@Override
|
@Override
|
||||||
public int compareTo(OrderData orderData) {
|
public int compareTo(OrderData orderData) {
|
||||||
// Compare using prices
|
// 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;
|
private BigDecimal amount;
|
||||||
|
|
||||||
@Schema(
|
@Schema(description = "when trade happened")
|
||||||
description = "when trade happened"
|
|
||||||
)
|
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
@ -31,11 +29,11 @@ public class RecentTradeData {
|
|||||||
protected 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.assetId = assetId;
|
||||||
this.otherAssetId = otherAssetId;
|
this.otherAssetId = otherAssetId;
|
||||||
this.otherAmount = otherAmount;
|
this.otherAmount = BigDecimal.valueOf(otherAmount, 8);
|
||||||
this.amount = amount;
|
this.amount = BigDecimal.valueOf(amount, 8);
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package org.qortal.data.asset;
|
package org.qortal.data.asset;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.Marshaller;
|
import javax.xml.bind.Marshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
import javax.xml.bind.annotation.XmlTransient;
|
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;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
|
import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
|
||||||
@ -24,17 +23,17 @@ public class TradeData {
|
|||||||
@XmlElement(name = "targetOrderId")
|
@XmlElement(name = "targetOrderId")
|
||||||
private byte[] target;
|
private byte[] target;
|
||||||
|
|
||||||
@Schema(name = "targetAmount", description = "amount traded from target")
|
@Schema(description = "amount traded from target")
|
||||||
@XmlElement(name = "targetAmount")
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
private BigDecimal targetAmount;
|
private long targetAmount;
|
||||||
|
|
||||||
@Schema(name = "initiatorAmount", description = "amount traded from initiator")
|
@Schema(description = "amount traded from initiator")
|
||||||
@XmlElement(name = "initiatorAmount")
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
private BigDecimal initiatorAmount;
|
private long initiatorAmount;
|
||||||
|
|
||||||
@Schema(name = "initiatorSaving", description = "amount refunded to initiator due to price improvement")
|
@Schema(description = "amount refunded to initiator due to price improvement")
|
||||||
@XmlElement(name = "initiatorSaving")
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
private BigDecimal initiatorSaving;
|
private long initiatorSaving;
|
||||||
|
|
||||||
@Schema(description = "when trade happened")
|
@Schema(description = "when trade happened")
|
||||||
private long timestamp;
|
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) {
|
Long haveAssetId, String haveAssetName, Long wantAssetId, String wantAssetName) {
|
||||||
this.initiator = initiator;
|
this.initiator = initiator;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
@ -110,7 +109,7 @@ public class TradeData {
|
|||||||
this.wantAssetName = wantAssetName;
|
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);
|
this(initiator, target, targetAmount, initiatorAmount, initiatorSaving, timestamp, null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,15 +123,15 @@ public class TradeData {
|
|||||||
return this.target;
|
return this.target;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getTargetAmount() {
|
public long getTargetAmount() {
|
||||||
return this.targetAmount;
|
return this.targetAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getInitiatorAmount() {
|
public long getInitiatorAmount() {
|
||||||
return this.initiatorAmount;
|
return this.initiatorAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getInitiatorSaving() {
|
public long getInitiatorSaving() {
|
||||||
return this.initiatorSaving;
|
return this.initiatorSaving;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.qortal.data.at;
|
package org.qortal.data.at;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
public class ATData {
|
public class ATData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
@ -16,12 +14,12 @@ public class ATData {
|
|||||||
private boolean isFinished;
|
private boolean isFinished;
|
||||||
private boolean hadFatalError;
|
private boolean hadFatalError;
|
||||||
private boolean isFrozen;
|
private boolean isFrozen;
|
||||||
private BigDecimal frozenBalance;
|
private Long frozenBalance;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
public ATData(String ATAddress, byte[] creatorPublicKey, long creation, int version, long assetId, byte[] codeBytes, boolean isSleeping,
|
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.ATAddress = ATAddress;
|
||||||
this.creatorPublicKey = creatorPublicKey;
|
this.creatorPublicKey = creatorPublicKey;
|
||||||
this.creation = creation;
|
this.creation = creation;
|
||||||
@ -36,16 +34,6 @@ public class ATData {
|
|||||||
this.frozenBalance = frozenBalance;
|
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
|
// Getters / setters
|
||||||
|
|
||||||
public String getATAddress() {
|
public String getATAddress() {
|
||||||
@ -112,11 +100,11 @@ public class ATData {
|
|||||||
this.isFrozen = isFrozen;
|
this.isFrozen = isFrozen;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getFrozenBalance() {
|
public Long getFrozenBalance() {
|
||||||
return this.frozenBalance;
|
return this.frozenBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFrozenBalance(BigDecimal frozenBalance) {
|
public void setFrozenBalance(Long frozenBalance) {
|
||||||
this.frozenBalance = frozenBalance;
|
this.frozenBalance = frozenBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.qortal.data.at;
|
package org.qortal.data.at;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
public class ATStateData {
|
public class ATStateData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
@ -10,12 +8,12 @@ public class ATStateData {
|
|||||||
private Long creation;
|
private Long creation;
|
||||||
private byte[] stateData;
|
private byte[] stateData;
|
||||||
private byte[] stateHash;
|
private byte[] stateHash;
|
||||||
private BigDecimal fees;
|
private Long fees;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/** Create new ATStateData */
|
/** 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.ATAddress = ATAddress;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.creation = creation;
|
this.creation = creation;
|
||||||
@ -25,7 +23,7 @@ public class ATStateData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** For recreating per-block ATStateData from repository where not all info is needed */
|
/** 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);
|
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 */
|
/** 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);
|
this(ATAddress, null, null, null, stateHash, fees);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +64,7 @@ public class ATStateData {
|
|||||||
return this.stateHash;
|
return this.stateHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getFees() {
|
public Long getFees() {
|
||||||
return this.fees;
|
return this.fees;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,11 +3,11 @@ package org.qortal.data.block;
|
|||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
|
|
||||||
@ -23,14 +23,20 @@ public class BlockData implements Serializable {
|
|||||||
private int version;
|
private int version;
|
||||||
private byte[] reference;
|
private byte[] reference;
|
||||||
private int transactionCount;
|
private int transactionCount;
|
||||||
private BigDecimal totalFees;
|
|
||||||
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
private long totalFees;
|
||||||
|
|
||||||
private byte[] transactionsSignature;
|
private byte[] transactionsSignature;
|
||||||
private Integer height;
|
private Integer height;
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
private byte[] minterPublicKey;
|
private byte[] minterPublicKey;
|
||||||
private byte[] minterSignature;
|
private byte[] minterSignature;
|
||||||
private int atCount;
|
private int atCount;
|
||||||
private BigDecimal atFees;
|
|
||||||
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
private long atFees;
|
||||||
|
|
||||||
private byte[] encodedOnlineAccounts;
|
private byte[] encodedOnlineAccounts;
|
||||||
private int onlineAccountsCount;
|
private int onlineAccountsCount;
|
||||||
private Long onlineAccountsTimestamp;
|
private Long onlineAccountsTimestamp;
|
||||||
@ -42,8 +48,8 @@ public class BlockData implements Serializable {
|
|||||||
protected BlockData() {
|
protected BlockData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp,
|
public BlockData(int version, byte[] reference, int transactionCount, long totalFees, byte[] transactionsSignature, Integer height, long timestamp,
|
||||||
byte[] minterPublicKey, byte[] minterSignature, int atCount, BigDecimal atFees,
|
byte[] minterPublicKey, byte[] minterSignature, int atCount, long atFees,
|
||||||
byte[] encodedOnlineAccounts, int onlineAccountsCount, Long onlineAccountsTimestamp, byte[] onlineAccountsSignatures) {
|
byte[] encodedOnlineAccounts, int onlineAccountsCount, Long onlineAccountsTimestamp, byte[] onlineAccountsSignatures) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.reference = reference;
|
this.reference = reference;
|
||||||
@ -67,8 +73,8 @@ public class BlockData implements Serializable {
|
|||||||
this.signature = null;
|
this.signature = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp,
|
public BlockData(int version, byte[] reference, int transactionCount, long totalFees, byte[] transactionsSignature, Integer height, long timestamp,
|
||||||
byte[] minterPublicKey, byte[] minterSignature, int atCount, BigDecimal atFees) {
|
byte[] minterPublicKey, byte[] minterSignature, int atCount, long atFees) {
|
||||||
this(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, minterPublicKey, minterSignature, atCount, atFees,
|
this(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, minterPublicKey, minterSignature, atCount, atFees,
|
||||||
null, 0, null, null);
|
null, 0, null, null);
|
||||||
}
|
}
|
||||||
@ -103,11 +109,11 @@ public class BlockData implements Serializable {
|
|||||||
this.transactionCount = transactionCount;
|
this.transactionCount = transactionCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getTotalFees() {
|
public long getTotalFees() {
|
||||||
return this.totalFees;
|
return this.totalFees;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTotalFees(BigDecimal totalFees) {
|
public void setTotalFees(long totalFees) {
|
||||||
this.totalFees = totalFees;
|
this.totalFees = totalFees;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,11 +157,11 @@ public class BlockData implements Serializable {
|
|||||||
this.atCount = atCount;
|
this.atCount = atCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getATFees() {
|
public long getATFees() {
|
||||||
return this.atFees;
|
return this.atFees;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setATFees(BigDecimal atFees) {
|
public void setATFees(long atFees) {
|
||||||
this.atFees = atFees;
|
this.atFees = atFees;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package org.qortal.data.naming;
|
package org.qortal.data.naming;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import javax.xml.bind.annotation.XmlTransient;
|
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;
|
||||||
|
|
||||||
@ -20,17 +19,14 @@ public class NameData {
|
|||||||
private Long updated;
|
private Long updated;
|
||||||
// No need to expose this via API
|
// No need to expose this via API
|
||||||
@XmlTransient
|
@XmlTransient
|
||||||
@Schema(
|
@Schema(hidden = true)
|
||||||
hidden = true
|
|
||||||
)
|
|
||||||
private byte[] reference;
|
private byte[] reference;
|
||||||
private boolean isForSale;
|
private boolean isForSale;
|
||||||
private BigDecimal salePrice;
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
private Long salePrice;
|
||||||
// For internal use
|
// For internal use
|
||||||
@XmlTransient
|
@XmlTransient
|
||||||
@Schema(
|
@Schema(hidden = true)
|
||||||
hidden = true
|
|
||||||
)
|
|
||||||
private int creationGroupId;
|
private int creationGroupId;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
@ -39,7 +35,7 @@ public class NameData {
|
|||||||
protected 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) {
|
int creationGroupId) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@ -106,11 +102,11 @@ public class NameData {
|
|||||||
this.isForSale = isForSale;
|
this.isForSale = isForSale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getSalePrice() {
|
public Long getSalePrice() {
|
||||||
return this.salePrice;
|
return this.salePrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSalePrice(BigDecimal salePrice) {
|
public void setSalePrice(Long salePrice) {
|
||||||
this.salePrice = salePrice;
|
this.salePrice = salePrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import java.math.BigDecimal;
|
|||||||
import javax.xml.bind.Unmarshaller;
|
import javax.xml.bind.Unmarshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlTransient;
|
||||||
|
|
||||||
import org.qortal.account.NullAccount;
|
import org.qortal.account.NullAccount;
|
||||||
import org.qortal.transaction.Transaction.TransactionType;
|
import org.qortal.transaction.Transaction.TransactionType;
|
||||||
@ -17,10 +18,19 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
public class ATTransactionData extends TransactionData {
|
public class ATTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private String atAddress;
|
private String atAddress;
|
||||||
|
|
||||||
private String recipient;
|
private String recipient;
|
||||||
private BigDecimal amount;
|
|
||||||
|
@XmlTransient
|
||||||
|
@Schema(hidden = true)
|
||||||
|
// Not always present
|
||||||
|
private Long amount;
|
||||||
|
|
||||||
|
// Not always present
|
||||||
private Long assetId;
|
private Long assetId;
|
||||||
|
|
||||||
private byte[] message;
|
private byte[] message;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
@ -35,7 +45,7 @@ public class ATTransactionData extends TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** From repository */
|
/** 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);
|
super(TransactionType.AT, baseTransactionData);
|
||||||
|
|
||||||
this.creatorPublicKey = NullAccount.PUBLIC_KEY;
|
this.creatorPublicKey = NullAccount.PUBLIC_KEY;
|
||||||
@ -56,7 +66,7 @@ public class ATTransactionData extends TransactionData {
|
|||||||
return this.recipient;
|
return this.recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public Long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,4 +78,14 @@ public class ATTransactionData extends TransactionData {
|
|||||||
return this.message;
|
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;
|
package org.qortal.data.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import org.qortal.transaction.Transaction.ApprovalStatus;
|
import org.qortal.transaction.Transaction.ApprovalStatus;
|
||||||
|
|
||||||
public class BaseTransactionData extends TransactionData {
|
public class BaseTransactionData extends TransactionData {
|
||||||
|
|
||||||
/** Constructor for use by repository. */
|
/** 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) {
|
ApprovalStatus approvalStatus, Integer blockHeight, Integer approvalHeight, byte[] signature) {
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.txGroupId = txGroupId;
|
this.txGroupId = txGroupId;
|
||||||
@ -21,7 +19,7 @@ public class BaseTransactionData extends TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Constructor for use by transaction transformer. */
|
/** 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);
|
this(timestamp, txGroupId, reference, creatorPublicKey, fee, null, null, null, signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.qortal.data.transaction;
|
package org.qortal.data.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.Unmarshaller;
|
import javax.xml.bind.Unmarshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
@ -18,16 +16,17 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
public class BuyNameTransactionData extends TransactionData {
|
public class BuyNameTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
@Schema(description = "buyer's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
@Schema(description = "buyer's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||||
private byte[] buyerPublicKey;
|
private byte[] buyerPublicKey;
|
||||||
|
|
||||||
@Schema(description = "which name to buy", example = "my-name")
|
@Schema(description = "which name to buy", example = "my-name")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Schema(description = "selling price", example = "123.456")
|
@Schema(description = "selling price", example = "123.456")
|
||||||
@XmlJavaTypeAdapter(
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
type = BigDecimal.class,
|
private long amount;
|
||||||
value = org.qortal.api.BigDecimalTypeAdapter.class
|
|
||||||
)
|
|
||||||
private BigDecimal amount;
|
|
||||||
@Schema(description = "seller's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v")
|
@Schema(description = "seller's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v")
|
||||||
private String seller;
|
private String seller;
|
||||||
|
|
||||||
@ -49,7 +48,7 @@ public class BuyNameTransactionData extends TransactionData {
|
|||||||
|
|
||||||
/** From repository */
|
/** From repository */
|
||||||
public BuyNameTransactionData(BaseTransactionData baseTransactionData,
|
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);
|
super(TransactionType.BUY_NAME, baseTransactionData);
|
||||||
|
|
||||||
this.buyerPublicKey = baseTransactionData.creatorPublicKey;
|
this.buyerPublicKey = baseTransactionData.creatorPublicKey;
|
||||||
@ -60,7 +59,7 @@ public class BuyNameTransactionData extends TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** From network/API */
|
/** 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);
|
this(baseTransactionData, name, amount, seller, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +73,7 @@ public class BuyNameTransactionData extends TransactionData {
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package org.qortal.data.transaction;
|
package org.qortal.data.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.Marshaller;
|
import javax.xml.bind.Marshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
import org.qortal.transaction.Transaction.TransactionType;
|
import org.qortal.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
@ -18,14 +17,20 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
|
|||||||
public class CreateAssetOrderTransactionData extends TransactionData {
|
public class CreateAssetOrderTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
@Schema(description = "ID of asset on offer to give by order creator", example = "1")
|
@Schema(description = "ID of asset on offer to give by order creator", example = "1")
|
||||||
private long haveAssetId;
|
private long haveAssetId;
|
||||||
|
|
||||||
@Schema(description = "ID of asset wanted to receive by order creator", example = "0")
|
@Schema(description = "ID of asset wanted to receive by order creator", example = "0")
|
||||||
private long wantAssetId;
|
private long wantAssetId;
|
||||||
|
|
||||||
@Schema(description = "amount of highest-assetID asset to trade")
|
@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")
|
@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
|
// Used by API - not always present
|
||||||
|
|
||||||
@ -70,7 +75,7 @@ public class CreateAssetOrderTransactionData extends TransactionData {
|
|||||||
|
|
||||||
/** Constructs using data from repository, including optional asset names. */
|
/** Constructs using data from repository, including optional asset names. */
|
||||||
public CreateAssetOrderTransactionData(BaseTransactionData baseTransactionData,
|
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);
|
super(TransactionType.CREATE_ASSET_ORDER, baseTransactionData);
|
||||||
|
|
||||||
this.haveAssetId = haveAssetId;
|
this.haveAssetId = haveAssetId;
|
||||||
@ -83,7 +88,7 @@ public class CreateAssetOrderTransactionData extends TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Constructor excluding optional asset names. */
|
/** 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);
|
this(baseTransactionData, haveAssetId, wantAssetId, amount, price, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,11 +102,11 @@ public class CreateAssetOrderTransactionData extends TransactionData {
|
|||||||
return this.wantAssetId;
|
return this.wantAssetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getPrice() {
|
public long getPrice() {
|
||||||
return this.price;
|
return this.price;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package org.qortal.data.transaction;
|
package org.qortal.data.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
import org.qortal.transaction.Transaction.TransactionType;
|
import org.qortal.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
@ -20,7 +19,8 @@ public class DeployAtTransactionData extends TransactionData {
|
|||||||
private String aTType;
|
private String aTType;
|
||||||
private String tags;
|
private String tags;
|
||||||
private byte[] creationBytes;
|
private byte[] creationBytes;
|
||||||
private BigDecimal amount;
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
private long amount;
|
||||||
private long assetId;
|
private long assetId;
|
||||||
private String aTAddress;
|
private String aTAddress;
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ public class DeployAtTransactionData extends TransactionData {
|
|||||||
|
|
||||||
/** From repository */
|
/** From repository */
|
||||||
public DeployAtTransactionData(BaseTransactionData baseTransactionData,
|
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);
|
super(TransactionType.DEPLOY_AT, baseTransactionData);
|
||||||
|
|
||||||
this.aTAddress = aTAddress;
|
this.aTAddress = aTAddress;
|
||||||
@ -48,7 +48,7 @@ public class DeployAtTransactionData extends TransactionData {
|
|||||||
|
|
||||||
/** From network/API */
|
/** From network/API */
|
||||||
public DeployAtTransactionData(BaseTransactionData baseTransactionData,
|
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);
|
this(baseTransactionData, null, name, description, aTType, tags, creationBytes, amount, assetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ public class DeployAtTransactionData extends TransactionData {
|
|||||||
return this.creationBytes;
|
return this.creationBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package org.qortal.data.transaction;
|
package org.qortal.data.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
|
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
|
||||||
import org.qortal.asset.Asset;
|
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
|
// All properties to be converted to JSON via JAXB
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
@Schema(
|
@Schema(allOf = {TransactionData.class})
|
||||||
allOf = {
|
|
||||||
TransactionData.class
|
|
||||||
}
|
|
||||||
)
|
|
||||||
//JAXB: use this subclass if XmlDiscriminatorNode matches XmlDiscriminatorValue below:
|
//JAXB: use this subclass if XmlDiscriminatorNode matches XmlDiscriminatorValue below:
|
||||||
@XmlDiscriminatorValue("GENESIS")
|
@XmlDiscriminatorValue("GENESIS")
|
||||||
public class GenesisTransactionData extends TransactionData {
|
public class GenesisTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private String recipient;
|
private String recipient;
|
||||||
private BigDecimal amount;
|
|
||||||
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
private long amount;
|
||||||
|
|
||||||
private long assetId;
|
private long assetId;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
@ -35,7 +34,7 @@ public class GenesisTransactionData extends TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** From repository */
|
/** 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);
|
super(TransactionType.GENESIS, baseTransactionData);
|
||||||
|
|
||||||
this.recipient = recipient;
|
this.recipient = recipient;
|
||||||
@ -44,7 +43,7 @@ public class GenesisTransactionData extends TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** From repository (where asset locked to QORT) */
|
/** 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);
|
this(baseTransactionData, recipient, amount, Asset.QORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +53,7 @@ public class GenesisTransactionData extends TransactionData {
|
|||||||
return this.recipient;
|
return this.recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package org.qortal.data.transaction;
|
package org.qortal.data.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.Unmarshaller;
|
import javax.xml.bind.Unmarshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.transaction.Transaction.TransactionType;
|
import org.qortal.transaction.Transaction.TransactionType;
|
||||||
@ -21,7 +20,8 @@ public class MessageTransactionData extends TransactionData {
|
|||||||
private int version;
|
private int version;
|
||||||
private String recipient;
|
private String recipient;
|
||||||
private Long assetId;
|
private Long assetId;
|
||||||
private BigDecimal amount;
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
private long amount;
|
||||||
private byte[] data;
|
private byte[] data;
|
||||||
private boolean isText;
|
private boolean isText;
|
||||||
private boolean isEncrypted;
|
private boolean isEncrypted;
|
||||||
@ -38,7 +38,7 @@ public class MessageTransactionData extends TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MessageTransactionData(BaseTransactionData baseTransactionData,
|
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);
|
super(TransactionType.MESSAGE, baseTransactionData);
|
||||||
|
|
||||||
this.senderPublicKey = baseTransactionData.creatorPublicKey;
|
this.senderPublicKey = baseTransactionData.creatorPublicKey;
|
||||||
@ -74,7 +74,7 @@ public class MessageTransactionData extends TransactionData {
|
|||||||
return this.assetId;
|
return this.assetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.qortal.data.transaction;
|
package org.qortal.data.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.Unmarshaller;
|
import javax.xml.bind.Unmarshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
@ -17,16 +15,16 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
public class PaymentTransactionData extends TransactionData {
|
public class PaymentTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
@Schema(description = "sender's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
@Schema(description = "sender's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||||
private byte[] senderPublicKey;
|
private byte[] senderPublicKey;
|
||||||
|
|
||||||
@Schema(description = "recipient's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v")
|
@Schema(description = "recipient's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v")
|
||||||
private String recipient;
|
private String recipient;
|
||||||
|
|
||||||
@Schema(description = "amount to send", example = "123.456")
|
@Schema(description = "amount to send", example = "123.456")
|
||||||
@XmlJavaTypeAdapter(
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
type = BigDecimal.class,
|
private long amount;
|
||||||
value = org.qortal.api.BigDecimalTypeAdapter.class
|
|
||||||
)
|
|
||||||
private BigDecimal amount;
|
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -40,7 +38,7 @@ public class PaymentTransactionData extends TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** From repository */
|
/** From repository */
|
||||||
public PaymentTransactionData(BaseTransactionData baseTransactionData, String recipient, BigDecimal amount) {
|
public PaymentTransactionData(BaseTransactionData baseTransactionData, String recipient, long amount) {
|
||||||
super(TransactionType.PAYMENT, baseTransactionData);
|
super(TransactionType.PAYMENT, baseTransactionData);
|
||||||
|
|
||||||
this.senderPublicKey = baseTransactionData.creatorPublicKey;
|
this.senderPublicKey = baseTransactionData.creatorPublicKey;
|
||||||
@ -58,7 +56,7 @@ public class PaymentTransactionData extends TransactionData {
|
|||||||
return this.recipient;
|
return this.recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import java.math.BigDecimal;
|
|||||||
import javax.xml.bind.Unmarshaller;
|
import javax.xml.bind.Unmarshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
import javax.xml.bind.annotation.XmlTransient;
|
import javax.xml.bind.annotation.XmlTransient;
|
||||||
|
|
||||||
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
|
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
|
||||||
@ -27,13 +28,15 @@ public class RewardShareTransactionData extends TransactionData {
|
|||||||
@Schema(example = "reward_share_public_key")
|
@Schema(example = "reward_share_public_key")
|
||||||
private byte[] rewardSharePublicKey;
|
private byte[] rewardSharePublicKey;
|
||||||
|
|
||||||
@Schema(description = "Percentage of block rewards that minter shares to recipient, or negative value to cancel existing reward-share")
|
// JAXB will use special getter below
|
||||||
private BigDecimal sharePercent;
|
@XmlTransient
|
||||||
|
@Schema(hidden = true)
|
||||||
|
private int sharePercent;
|
||||||
|
|
||||||
// No need to ever expose this via API
|
// No need to ever expose this via API
|
||||||
@XmlTransient
|
@XmlTransient
|
||||||
@Schema(hidden = true)
|
@Schema(hidden = true)
|
||||||
private BigDecimal previousSharePercent;
|
private Integer previousSharePercent;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -48,7 +51,7 @@ public class RewardShareTransactionData extends TransactionData {
|
|||||||
|
|
||||||
/** From repository */
|
/** From repository */
|
||||||
public RewardShareTransactionData(BaseTransactionData baseTransactionData,
|
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);
|
super(TransactionType.REWARD_SHARE, baseTransactionData);
|
||||||
|
|
||||||
this.minterPublicKey = baseTransactionData.creatorPublicKey;
|
this.minterPublicKey = baseTransactionData.creatorPublicKey;
|
||||||
@ -60,7 +63,7 @@ public class RewardShareTransactionData extends TransactionData {
|
|||||||
|
|
||||||
/** From network/API */
|
/** From network/API */
|
||||||
public RewardShareTransactionData(BaseTransactionData baseTransactionData,
|
public RewardShareTransactionData(BaseTransactionData baseTransactionData,
|
||||||
String recipient, byte[] rewardSharePublicKey, BigDecimal sharePercent) {
|
String recipient, byte[] rewardSharePublicKey, int sharePercent) {
|
||||||
this(baseTransactionData, recipient, rewardSharePublicKey, sharePercent, null);
|
this(baseTransactionData, recipient, rewardSharePublicKey, sharePercent, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,16 +81,24 @@ public class RewardShareTransactionData extends TransactionData {
|
|||||||
return this.rewardSharePublicKey;
|
return this.rewardSharePublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getSharePercent() {
|
public int getSharePercent() {
|
||||||
return this.sharePercent;
|
return this.sharePercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getPreviousSharePercent() {
|
public Integer getPreviousSharePercent() {
|
||||||
return this.previousSharePercent;
|
return this.previousSharePercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPreviousSharePercent(BigDecimal previousSharePercent) {
|
public void setPreviousSharePercent(Integer previousSharePercent) {
|
||||||
this.previousSharePercent = 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;
|
package org.qortal.data.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.Unmarshaller;
|
import javax.xml.bind.Unmarshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
@ -17,16 +15,16 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
public class SellNameTransactionData extends TransactionData {
|
public class SellNameTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
@Schema(description = "owner's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
@Schema(description = "owner's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||||
private byte[] ownerPublicKey;
|
private byte[] ownerPublicKey;
|
||||||
|
|
||||||
@Schema(description = "which name to sell", example = "my-name")
|
@Schema(description = "which name to sell", example = "my-name")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Schema(description = "selling price", example = "123.456")
|
@Schema(description = "selling price", example = "123.456")
|
||||||
@XmlJavaTypeAdapter(
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
type = BigDecimal.class,
|
private long amount;
|
||||||
value = org.qortal.api.BigDecimalTypeAdapter.class
|
|
||||||
)
|
|
||||||
private BigDecimal amount;
|
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -39,7 +37,7 @@ public class SellNameTransactionData extends TransactionData {
|
|||||||
this.creatorPublicKey = this.ownerPublicKey;
|
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);
|
super(TransactionType.SELL_NAME, baseTransactionData);
|
||||||
|
|
||||||
this.ownerPublicKey = baseTransactionData.creatorPublicKey;
|
this.ownerPublicKey = baseTransactionData.creatorPublicKey;
|
||||||
@ -57,7 +55,7 @@ public class SellNameTransactionData extends TransactionData {
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,8 +58,9 @@ public abstract class TransactionData {
|
|||||||
protected long timestamp;
|
protected long timestamp;
|
||||||
@Schema(description = "sender's last transaction ID", example = "real_transaction_reference_in_base58")
|
@Schema(description = "sender's last transaction ID", example = "real_transaction_reference_in_base58")
|
||||||
protected byte[] reference;
|
protected byte[] reference;
|
||||||
@Schema(description = "fee for processing transaction", example = "1.0")
|
@XmlTransient
|
||||||
protected BigDecimal fee;
|
@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")
|
@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;
|
protected byte[] signature;
|
||||||
@Schema(description = "groupID for this transaction")
|
@Schema(description = "groupID for this transaction")
|
||||||
@ -138,11 +139,11 @@ public abstract class TransactionData {
|
|||||||
this.creatorPublicKey = creatorPublicKey;
|
this.creatorPublicKey = creatorPublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getFee() {
|
public Long getFee() {
|
||||||
return this.fee;
|
return this.fee;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFee(BigDecimal fee) {
|
public void setFee(Long fee) {
|
||||||
this.fee = fee;
|
this.fee = fee;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,6 +184,14 @@ public abstract class TransactionData {
|
|||||||
|
|
||||||
// JAXB special
|
// 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")
|
@XmlElement(name = "creatorAddress")
|
||||||
protected String getCreatorAddress() {
|
protected String getCreatorAddress() {
|
||||||
return Crypto.toAddress(this.creatorPublicKey);
|
return Crypto.toAddress(this.creatorPublicKey);
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package org.qortal.data.transaction;
|
package org.qortal.data.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import javax.xml.bind.Unmarshaller;
|
import javax.xml.bind.Unmarshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
import org.qortal.transaction.Transaction.TransactionType;
|
import org.qortal.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
@ -17,11 +16,15 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
|
|||||||
public class TransferAssetTransactionData extends TransactionData {
|
public class TransferAssetTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
@Schema(example = "sender_public_key")
|
@Schema(example = "sender_public_key")
|
||||||
private byte[] senderPublicKey;
|
private byte[] senderPublicKey;
|
||||||
|
|
||||||
private String recipient;
|
private String recipient;
|
||||||
private BigDecimal amount;
|
|
||||||
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
|
private long amount;
|
||||||
|
|
||||||
private long assetId;
|
private long assetId;
|
||||||
|
|
||||||
// Used by API - not always present
|
// Used by API - not always present
|
||||||
@ -40,7 +43,7 @@ public class TransferAssetTransactionData extends TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Constructs using data from repository, including optional assetName. */
|
/** 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);
|
super(TransactionType.TRANSFER_ASSET, baseTransactionData);
|
||||||
|
|
||||||
this.senderPublicKey = baseTransactionData.creatorPublicKey;
|
this.senderPublicKey = baseTransactionData.creatorPublicKey;
|
||||||
@ -51,7 +54,7 @@ public class TransferAssetTransactionData extends TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Constructor excluding optional assetName. */
|
/** 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);
|
this(baseTransactionData, recipient, amount, assetId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +68,7 @@ public class TransferAssetTransactionData extends TransactionData {
|
|||||||
return this.recipient;
|
return this.recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
public long getAmount() {
|
||||||
return this.amount;
|
return this.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,13 +158,13 @@ public class Name {
|
|||||||
|
|
||||||
// Update seller's balance
|
// Update seller's balance
|
||||||
Account seller = new Account(this.repository, this.nameData.getOwner());
|
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
|
// Set new owner
|
||||||
Account buyer = new PublicKeyAccount(this.repository, buyNameTransactionData.getBuyerPublicKey());
|
Account buyer = new PublicKeyAccount(this.repository, buyNameTransactionData.getBuyerPublicKey());
|
||||||
this.nameData.setOwner(buyer.getAddress());
|
this.nameData.setOwner(buyer.getAddress());
|
||||||
// Update buyer's balance
|
// 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
|
// Update reference in transaction data
|
||||||
buyNameTransactionData.setNameReference(this.nameData.getReference());
|
buyNameTransactionData.setNameReference(this.nameData.getReference());
|
||||||
@ -189,14 +189,14 @@ public class Name {
|
|||||||
|
|
||||||
// Revert buyer's balance
|
// Revert buyer's balance
|
||||||
Account buyer = new PublicKeyAccount(this.repository, buyNameTransactionData.getBuyerPublicKey());
|
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
|
// Previous Name's owner and/or data taken from referenced transaction
|
||||||
this.revert();
|
this.revert();
|
||||||
|
|
||||||
// Revert seller's balance
|
// Revert seller's balance
|
||||||
Account seller = new Account(this.repository, this.nameData.getOwner());
|
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
|
// Save reverted name data
|
||||||
this.repository.getNameRepository().save(this.nameData);
|
this.repository.getNameRepository().save(this.nameData);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.payment;
|
package org.qortal.payment;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -37,15 +36,15 @@ public class Payment {
|
|||||||
// isValid
|
// isValid
|
||||||
|
|
||||||
/** Are payments valid? */
|
/** 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();
|
AssetRepository assetRepository = this.repository.getAssetRepository();
|
||||||
|
|
||||||
// Check fee is positive
|
// Check fee is positive
|
||||||
if (fee.compareTo(BigDecimal.ZERO) <= 0)
|
if (fee <= 0)
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
return ValidationResult.NEGATIVE_FEE;
|
||||||
|
|
||||||
// Total up payment amounts by assetId
|
// Total up payment amounts by assetId
|
||||||
Map<Long, BigDecimal> amountsByAssetId = new HashMap<>();
|
Map<Long, Long> amountsByAssetId = new HashMap<>();
|
||||||
// Add transaction fee to start with
|
// Add transaction fee to start with
|
||||||
amountsByAssetId.put(Asset.QORT, fee);
|
amountsByAssetId.put(Asset.QORT, fee);
|
||||||
|
|
||||||
@ -55,11 +54,11 @@ public class Payment {
|
|||||||
// Check payments, and calculate amount total by assetId
|
// Check payments, and calculate amount total by assetId
|
||||||
for (PaymentData paymentData : payments) {
|
for (PaymentData paymentData : payments) {
|
||||||
// Check amount is zero or positive
|
// Check amount is zero or positive
|
||||||
if (paymentData.getAmount().compareTo(BigDecimal.ZERO) < 0)
|
if (paymentData.getAmount() < 0)
|
||||||
return ValidationResult.NEGATIVE_AMOUNT;
|
return ValidationResult.NEGATIVE_AMOUNT;
|
||||||
|
|
||||||
// Optional zero-amount check
|
// Optional zero-amount check
|
||||||
if (!isZeroAmountValid && paymentData.getAmount().compareTo(BigDecimal.ZERO) <= 0)
|
if (!isZeroAmountValid && paymentData.getAmount() <= 0)
|
||||||
return ValidationResult.NEGATIVE_AMOUNT;
|
return ValidationResult.NEGATIVE_AMOUNT;
|
||||||
|
|
||||||
// Check recipient address is valid
|
// Check recipient address is valid
|
||||||
@ -94,64 +93,63 @@ public class Payment {
|
|||||||
return ValidationResult.ASSET_DOES_NOT_MATCH_AT;
|
return ValidationResult.ASSET_DOES_NOT_MATCH_AT;
|
||||||
|
|
||||||
// Check asset amount is integer if asset is not divisible
|
// 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;
|
return ValidationResult.INVALID_AMOUNT;
|
||||||
|
|
||||||
// Set or add amount into amounts-by-asset map
|
// 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
|
// Check sender has enough of each asset
|
||||||
for (Entry<Long, BigDecimal> pair : amountsByAssetId.entrySet())
|
for (Entry<Long, Long> pair : amountsByAssetId.entrySet())
|
||||||
if (sender.getConfirmedBalance(pair.getKey()).compareTo(pair.getValue()) < 0)
|
if (sender.getConfirmedBalance(pair.getKey()) < pair.getValue())
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Are payments valid? */
|
/** 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);
|
return isValid(senderPublicKey, payments, fee, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Is single payment valid? */
|
/** 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);
|
return isValid(senderPublicKey, Collections.singletonList(paymentData), fee, isZeroAmountValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Is single payment valid? */
|
/** 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);
|
return isValid(senderPublicKey, paymentData, fee, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// isProcessable
|
// isProcessable
|
||||||
|
|
||||||
/** Are multiple payments processable? */
|
/** 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...
|
// Essentially the same as isValid...
|
||||||
return isValid(senderPublicKey, payments, fee, isZeroAmountValid);
|
return isValid(senderPublicKey, payments, fee, isZeroAmountValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Are multiple payments processable? */
|
/** 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);
|
return isProcessable(senderPublicKey, payments, fee, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Is single payment processable? */
|
/** 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);
|
return isProcessable(senderPublicKey, Collections.singletonList(paymentData), fee, isZeroAmountValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Is single payment processable? */
|
/** 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);
|
return isProcessable(senderPublicKey, paymentData, fee, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// process
|
// process
|
||||||
|
|
||||||
/** Multiple payment processing */
|
/** Multiple payment processing */
|
||||||
public void process(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, byte[] signature)
|
public void process(byte[] senderPublicKey, List<PaymentData> payments, byte[] signature) throws DataException {
|
||||||
throws DataException {
|
|
||||||
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
||||||
|
|
||||||
// Process all payments
|
// Process all payments
|
||||||
@ -159,31 +157,30 @@ public class Payment {
|
|||||||
Account recipient = new Account(this.repository, paymentData.getRecipient());
|
Account recipient = new Account(this.repository, paymentData.getRecipient());
|
||||||
|
|
||||||
long assetId = paymentData.getAssetId();
|
long assetId = paymentData.getAssetId();
|
||||||
BigDecimal amount = paymentData.getAmount();
|
long amount = paymentData.getAmount();
|
||||||
|
|
||||||
// Update sender's balance due to amount
|
// 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
|
// Update recipient's balance
|
||||||
recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).add(amount));
|
recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId) + amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Single payment processing */
|
/** Single payment processing */
|
||||||
public void process(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature)
|
public void process(byte[] senderPublicKey, PaymentData paymentData, byte[] signature) throws DataException {
|
||||||
throws DataException {
|
process(senderPublicKey, Collections.singletonList(paymentData), signature);
|
||||||
process(senderPublicKey, Collections.singletonList(paymentData), fee, signature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// processReferenceAndFees
|
// processReferenceAndFees
|
||||||
|
|
||||||
/** Multiple payment reference processing */
|
/** 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 {
|
throws DataException {
|
||||||
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
||||||
|
|
||||||
// Update sender's balance due to fee
|
// 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
|
// Update sender's reference
|
||||||
sender.setLastReference(signature);
|
sender.setLastReference(signature);
|
||||||
@ -201,42 +198,42 @@ public class Payment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Multiple payment reference processing */
|
/** 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 {
|
throws DataException {
|
||||||
processReferencesAndFees(senderPublicKey, Collections.singletonList(payment), fee, signature, alwaysInitializeRecipientReference);
|
processReferencesAndFees(senderPublicKey, Collections.singletonList(payment), fee, signature, alwaysInitializeRecipientReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
// orphan
|
// 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);
|
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
||||||
|
|
||||||
// Orphan all payments
|
// Orphan all payments
|
||||||
for (PaymentData paymentData : payments) {
|
for (PaymentData paymentData : payments) {
|
||||||
Account recipient = new Account(this.repository, paymentData.getRecipient());
|
Account recipient = new Account(this.repository, paymentData.getRecipient());
|
||||||
long assetId = paymentData.getAssetId();
|
long assetId = paymentData.getAssetId();
|
||||||
BigDecimal amount = paymentData.getAmount();
|
long amount = paymentData.getAmount();
|
||||||
|
|
||||||
// Update sender's balance due to amount
|
// 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
|
// 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 {
|
public void orphan(byte[] senderPublicKey, PaymentData paymentData, byte[] signature, byte[] reference) throws DataException {
|
||||||
orphan(senderPublicKey, Collections.singletonList(paymentData), fee, signature, reference);
|
orphan(senderPublicKey, Collections.singletonList(paymentData), signature, reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
// orphanReferencesAndFees
|
// 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 {
|
boolean alwaysUninitializeRecipientReference) throws DataException {
|
||||||
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
||||||
|
|
||||||
// Update sender's balance due to fee
|
// 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
|
// Update sender's reference
|
||||||
sender.setLastReference(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 {
|
boolean alwaysUninitializeRecipientReference) throws DataException {
|
||||||
orphanReferencesAndFees(senderPublicKey, Collections.singletonList(paymentData), fee, signature, reference, alwaysUninitializeRecipientReference);
|
orphanReferencesAndFees(senderPublicKey, Collections.singletonList(paymentData), fee, signature, reference, alwaysUninitializeRecipientReference);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository;
|
package org.qortal.repository;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.data.account.AccountBalanceData;
|
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 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;
|
public void save(AccountBalanceData accountBalanceData) throws DataException;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository;
|
package org.qortal.repository;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.data.asset.AssetData;
|
import org.qortal.data.asset.AssetData;
|
||||||
@ -45,7 +44,7 @@ public interface AssetRepository {
|
|||||||
return getOpenOrders(haveAssetId, wantAssetId, null, null, null);
|
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;
|
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;
|
package org.qortal.repository.hsqldb;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
@ -46,7 +45,9 @@ public class HSQLDBATRepository implements ATRepository {
|
|||||||
boolean hadFatalError = resultSet.getBoolean(9);
|
boolean hadFatalError = resultSet.getBoolean(9);
|
||||||
boolean isFrozen = resultSet.getBoolean(10);
|
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,
|
return new ATData(atAddress, creatorPublicKey, creation, version, assetId, codeBytes, isSleeping, sleepUntilHeight, isFinished, hadFatalError,
|
||||||
isFrozen, frozenBalance);
|
isFrozen, frozenBalance);
|
||||||
@ -92,7 +93,9 @@ public class HSQLDBATRepository implements ATRepository {
|
|||||||
boolean hadFatalError = resultSet.getBoolean(9);
|
boolean hadFatalError = resultSet.getBoolean(9);
|
||||||
boolean isFrozen = resultSet.getBoolean(10);
|
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,
|
ATData atData = new ATData(atAddress, creatorPublicKey, creation, version, assetId, codeBytes, isSleeping, sleepUntilHeight, isFinished,
|
||||||
hadFatalError, isFrozen, frozenBalance);
|
hadFatalError, isFrozen, frozenBalance);
|
||||||
@ -159,7 +162,7 @@ public class HSQLDBATRepository implements ATRepository {
|
|||||||
long creation = resultSet.getTimestamp(1, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long creation = resultSet.getTimestamp(1, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||||
byte[] stateData = resultSet.getBytes(2); // Actually BLOB
|
byte[] stateData = resultSet.getBytes(2); // Actually BLOB
|
||||||
byte[] stateHash = resultSet.getBytes(3);
|
byte[] stateHash = resultSet.getBytes(3);
|
||||||
BigDecimal fees = resultSet.getBigDecimal(4);
|
long fees = resultSet.getLong(4);
|
||||||
|
|
||||||
return new ATStateData(atAddress, height, creation, stateData, stateHash, fees);
|
return new ATStateData(atAddress, height, creation, stateData, stateHash, fees);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@ -178,7 +181,7 @@ public class HSQLDBATRepository implements ATRepository {
|
|||||||
long creation = resultSet.getTimestamp(2, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long creation = resultSet.getTimestamp(2, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||||
byte[] stateData = resultSet.getBytes(3); // Actually BLOB
|
byte[] stateData = resultSet.getBytes(3); // Actually BLOB
|
||||||
byte[] stateHash = resultSet.getBytes(4);
|
byte[] stateHash = resultSet.getBytes(4);
|
||||||
BigDecimal fees = resultSet.getBigDecimal(5);
|
long fees = resultSet.getLong(5);
|
||||||
|
|
||||||
return new ATStateData(atAddress, height, creation, stateData, stateHash, fees);
|
return new ATStateData(atAddress, height, creation, stateData, stateHash, fees);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@ -199,7 +202,7 @@ public class HSQLDBATRepository implements ATRepository {
|
|||||||
do {
|
do {
|
||||||
String atAddress = resultSet.getString(1);
|
String atAddress = resultSet.getString(1);
|
||||||
byte[] stateHash = resultSet.getBytes(2);
|
byte[] stateHash = resultSet.getBytes(2);
|
||||||
BigDecimal fees = resultSet.getBigDecimal(3);
|
long fees = resultSet.getLong(3);
|
||||||
|
|
||||||
ATStateData atStateData = new ATStateData(atAddress, height, stateHash, fees);
|
ATStateData atStateData = new ATStateData(atAddress, height, stateHash, fees);
|
||||||
atStates.add(atStateData);
|
atStates.add(atStateData);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb;
|
package org.qortal.repository.hsqldb;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -319,7 +318,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
BigDecimal balance = resultSet.getBigDecimal(1).setScale(8);
|
long balance = resultSet.getLong(1);
|
||||||
|
|
||||||
return new AccountBalanceData(address, assetId, balance);
|
return new AccountBalanceData(address, assetId, balance);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@ -342,7 +341,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
String address = resultSet.getString(1);
|
String address = resultSet.getString(1);
|
||||||
BigDecimal balance = resultSet.getBigDecimal(2).setScale(8);
|
long balance = resultSet.getLong(2);
|
||||||
|
|
||||||
accountBalances.add(new AccountBalanceData(address, assetId, balance));
|
accountBalances.add(new AccountBalanceData(address, assetId, balance));
|
||||||
} while (resultSet.next());
|
} while (resultSet.next());
|
||||||
@ -445,7 +444,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
do {
|
do {
|
||||||
String address = resultSet.getString(1);
|
String address = resultSet.getString(1);
|
||||||
long assetId = resultSet.getLong(2);
|
long assetId = resultSet.getLong(2);
|
||||||
BigDecimal balance = resultSet.getBigDecimal(3).setScale(8);
|
long balance = resultSet.getLong(3);
|
||||||
String assetName = resultSet.getString(4);
|
String assetName = resultSet.getString(4);
|
||||||
|
|
||||||
accountBalances.add(new AccountBalanceData(address, assetId, balance, assetName));
|
accountBalances.add(new AccountBalanceData(address, assetId, balance, assetName));
|
||||||
@ -458,13 +457,13 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 is zero then do nothing
|
||||||
if (deltaBalance.signum() == 0)
|
if (deltaBalance == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If deltaBalance is negative then we assume AccountBalances & parent Accounts rows exist
|
// If deltaBalance is negative then we assume AccountBalances & parent Accounts rows exist
|
||||||
if (deltaBalance.signum() < 0) {
|
if (deltaBalance < 0) {
|
||||||
// Perform actual balance change
|
// Perform actual balance change
|
||||||
String sql = "UPDATE AccountBalances set balance = balance + ? WHERE account = ? AND asset_id = ?";
|
String sql = "UPDATE AccountBalances set balance = balance + ? WHERE account = ? AND asset_id = ?";
|
||||||
try {
|
try {
|
||||||
@ -496,8 +495,8 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
public void save(AccountBalanceData accountBalanceData) throws DataException {
|
public void save(AccountBalanceData accountBalanceData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountBalances");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountBalances");
|
||||||
|
|
||||||
saveHelper.bind("account", accountBalanceData.getAddress()).bind("asset_id", accountBalanceData.getAssetId()).bind("balance",
|
saveHelper.bind("account", accountBalanceData.getAddress()).bind("asset_id", accountBalanceData.getAssetId())
|
||||||
accountBalanceData.getBalance());
|
.bind("balance", accountBalanceData.getBalance());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
saveHelper.execute(this.repository);
|
saveHelper.execute(this.repository);
|
||||||
@ -527,7 +526,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
|
|
||||||
String minter = resultSet.getString(1);
|
String minter = resultSet.getString(1);
|
||||||
byte[] rewardSharePublicKey = resultSet.getBytes(2);
|
byte[] rewardSharePublicKey = resultSet.getBytes(2);
|
||||||
BigDecimal sharePercent = resultSet.getBigDecimal(3);
|
int sharePercent = resultSet.getInt(3);
|
||||||
|
|
||||||
return new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent);
|
return new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@ -546,7 +545,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
byte[] minterPublicKey = resultSet.getBytes(1);
|
byte[] minterPublicKey = resultSet.getBytes(1);
|
||||||
String minter = resultSet.getString(2);
|
String minter = resultSet.getString(2);
|
||||||
String recipient = resultSet.getString(3);
|
String recipient = resultSet.getString(3);
|
||||||
BigDecimal sharePercent = resultSet.getBigDecimal(4);
|
int sharePercent = resultSet.getInt(4);
|
||||||
|
|
||||||
return new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent);
|
return new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@ -588,7 +587,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
byte[] minterPublicKey = resultSet.getBytes(1);
|
byte[] minterPublicKey = resultSet.getBytes(1);
|
||||||
String minter = resultSet.getString(2);
|
String minter = resultSet.getString(2);
|
||||||
String recipient = resultSet.getString(3);
|
String recipient = resultSet.getString(3);
|
||||||
BigDecimal sharePercent = resultSet.getBigDecimal(4);
|
int sharePercent = resultSet.getInt(4);
|
||||||
byte[] rewardSharePublicKey = resultSet.getBytes(5);
|
byte[] rewardSharePublicKey = resultSet.getBytes(5);
|
||||||
|
|
||||||
rewardShares.add(new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent));
|
rewardShares.add(new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent));
|
||||||
@ -676,7 +675,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
byte[] minterPublicKey = resultSet.getBytes(1);
|
byte[] minterPublicKey = resultSet.getBytes(1);
|
||||||
String minter = resultSet.getString(2);
|
String minter = resultSet.getString(2);
|
||||||
String recipient = resultSet.getString(3);
|
String recipient = resultSet.getString(3);
|
||||||
BigDecimal sharePercent = resultSet.getBigDecimal(4);
|
int sharePercent = resultSet.getInt(4);
|
||||||
byte[] rewardSharePublicKey = resultSet.getBytes(5);
|
byte[] rewardSharePublicKey = resultSet.getBytes(5);
|
||||||
|
|
||||||
rewardShares.add(new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent));
|
rewardShares.add(new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent));
|
||||||
@ -715,7 +714,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
byte[] minterPublicKey = resultSet.getBytes(1);
|
byte[] minterPublicKey = resultSet.getBytes(1);
|
||||||
String minter = resultSet.getString(2);
|
String minter = resultSet.getString(2);
|
||||||
String recipient = resultSet.getString(3);
|
String recipient = resultSet.getString(3);
|
||||||
BigDecimal sharePercent = resultSet.getBigDecimal(4);
|
int sharePercent = resultSet.getInt(4);
|
||||||
byte[] rewardSharePublicKey = resultSet.getBytes(5);
|
byte[] rewardSharePublicKey = resultSet.getBytes(5);
|
||||||
|
|
||||||
return new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent);
|
return new RewardShareData(minterPublicKey, minter, recipient, rewardSharePublicKey, sharePercent);
|
||||||
@ -820,7 +819,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
String address = resultSet.getString(1);
|
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));
|
accountBalances.add(new AccountBalanceData(address, Asset.LEGACY_QORA, balance));
|
||||||
} while (resultSet.next());
|
} while (resultSet.next());
|
||||||
@ -839,7 +838,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
BigDecimal finalQortFromQora = resultSet.getBigDecimal(1);
|
long finalQortFromQora = resultSet.getLong(1);
|
||||||
Integer finalBlockHeight = resultSet.getInt(2);
|
Integer finalBlockHeight = resultSet.getInt(2);
|
||||||
if (finalBlockHeight == 0 && resultSet.wasNull())
|
if (finalBlockHeight == 0 && resultSet.wasNull())
|
||||||
finalBlockHeight = null;
|
finalBlockHeight = null;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package org.qortal.repository.hsqldb;
|
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.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -171,8 +171,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
|
|
||||||
if (assetData.getAssetId() == null) {
|
if (assetData.getAssetId() == null) {
|
||||||
// Fetch new assetId
|
// Fetch new assetId
|
||||||
try (ResultSet resultSet = this.repository
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT asset_id FROM Assets WHERE reference = ?", assetData.getReference())) {
|
||||||
.checkedExecute("SELECT asset_id FROM Assets WHERE reference = ?", assetData.getReference())) {
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
throw new DataException("Unable to fetch new asset ID from repository");
|
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);
|
byte[] creatorPublicKey = resultSet.getBytes(1);
|
||||||
long haveAssetId = resultSet.getLong(2);
|
long haveAssetId = resultSet.getLong(2);
|
||||||
long wantAssetId = resultSet.getLong(3);
|
long wantAssetId = resultSet.getLong(3);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(4);
|
long amount = resultSet.getLong(4);
|
||||||
BigDecimal fulfilled = resultSet.getBigDecimal(5);
|
long fulfilled = resultSet.getLong(5);
|
||||||
BigDecimal price = resultSet.getBigDecimal(6);
|
long price = resultSet.getLong(6);
|
||||||
long timestamp = resultSet.getTimestamp(7, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 7);
|
||||||
boolean isClosed = resultSet.getBoolean(8);
|
boolean isClosed = resultSet.getBoolean(8);
|
||||||
boolean isFulfilled = resultSet.getBoolean(9);
|
boolean isFulfilled = resultSet.getBoolean(9);
|
||||||
String haveAssetName = resultSet.getString(10);
|
String haveAssetName = resultSet.getString(10);
|
||||||
@ -263,10 +262,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
do {
|
do {
|
||||||
byte[] creatorPublicKey = resultSet.getBytes(1);
|
byte[] creatorPublicKey = resultSet.getBytes(1);
|
||||||
byte[] orderId = resultSet.getBytes(2);
|
byte[] orderId = resultSet.getBytes(2);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(3);
|
long amount = resultSet.getLong(3);
|
||||||
BigDecimal fulfilled = resultSet.getBigDecimal(4);
|
long fulfilled = resultSet.getLong(4);
|
||||||
BigDecimal price = resultSet.getBigDecimal(5);
|
long price = resultSet.getLong(5);
|
||||||
long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 6);
|
||||||
boolean isClosed = false;
|
boolean isClosed = false;
|
||||||
boolean isFulfilled = false;
|
boolean isFulfilled = false;
|
||||||
|
|
||||||
@ -282,7 +281,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
List<Object> bindParams = new ArrayList<>(3);
|
||||||
|
|
||||||
StringBuilder sql = new StringBuilder(512);
|
StringBuilder sql = new StringBuilder(512);
|
||||||
@ -317,10 +316,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
do {
|
do {
|
||||||
byte[] creatorPublicKey = resultSet.getBytes(1);
|
byte[] creatorPublicKey = resultSet.getBytes(1);
|
||||||
byte[] orderId = resultSet.getBytes(2);
|
byte[] orderId = resultSet.getBytes(2);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(3);
|
long amount = resultSet.getLong(3);
|
||||||
BigDecimal fulfilled = resultSet.getBigDecimal(4);
|
long fulfilled = resultSet.getLong(4);
|
||||||
BigDecimal price = resultSet.getBigDecimal(5);
|
long price = resultSet.getLong(5);
|
||||||
long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 6);
|
||||||
boolean isClosed = false;
|
boolean isClosed = false;
|
||||||
boolean isFulfilled = false;
|
boolean isFulfilled = false;
|
||||||
|
|
||||||
@ -366,11 +365,11 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
return orders;
|
return orders;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
BigDecimal price = resultSet.getBigDecimal(1);
|
long price = resultSet.getLong(1);
|
||||||
BigDecimal totalUnfulfilled = resultSet.getBigDecimal(2);
|
long totalUnfulfilled = resultSet.getLong(2);
|
||||||
long timestamp = resultSet.getTimestamp(3).getTime();
|
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());
|
price, timestamp, false, false, haveAssetData.getName(), wantAssetData.getName());
|
||||||
orders.add(order);
|
orders.add(order);
|
||||||
} while (resultSet.next());
|
} while (resultSet.next());
|
||||||
@ -417,10 +416,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
byte[] orderId = resultSet.getBytes(1);
|
byte[] orderId = resultSet.getBytes(1);
|
||||||
long haveAssetId = resultSet.getLong(2);
|
long haveAssetId = resultSet.getLong(2);
|
||||||
long wantAssetId = resultSet.getLong(3);
|
long wantAssetId = resultSet.getLong(3);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(4);
|
long amount = resultSet.getLong(4);
|
||||||
BigDecimal fulfilled = resultSet.getBigDecimal(5);
|
long fulfilled = resultSet.getLong(5);
|
||||||
BigDecimal price = resultSet.getBigDecimal(6);
|
long price = resultSet.getLong(6);
|
||||||
long timestamp = resultSet.getTimestamp(7, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 7);
|
||||||
boolean isClosed = resultSet.getBoolean(8);
|
boolean isClosed = resultSet.getBoolean(8);
|
||||||
boolean isFulfilled = resultSet.getBoolean(9);
|
boolean isFulfilled = resultSet.getBoolean(9);
|
||||||
String haveAssetName = resultSet.getString(10);
|
String haveAssetName = resultSet.getString(10);
|
||||||
@ -478,10 +477,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
byte[] orderId = resultSet.getBytes(1);
|
byte[] orderId = resultSet.getBytes(1);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(2);
|
long amount = resultSet.getLong(2);
|
||||||
BigDecimal fulfilled = resultSet.getBigDecimal(3);
|
long fulfilled = resultSet.getLong(3);
|
||||||
BigDecimal price = resultSet.getBigDecimal(4);
|
long price = resultSet.getLong(4);
|
||||||
long timestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 5);
|
||||||
boolean isClosed = resultSet.getBoolean(6);
|
boolean isClosed = resultSet.getBoolean(6);
|
||||||
boolean isFulfilled = resultSet.getBoolean(7);
|
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())
|
saveHelper.bind("asset_order_id", orderData.getOrderId()).bind("creator", orderData.getCreatorPublicKey())
|
||||||
.bind("have_asset_id", orderData.getHaveAssetId()).bind("want_asset_id", orderData.getWantAssetId())
|
.bind("have_asset_id", orderData.getHaveAssetId()).bind("want_asset_id", orderData.getWantAssetId())
|
||||||
.bind("amount", orderData.getAmount()).bind("fulfilled", orderData.getFulfilled())
|
.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());
|
.bind("is_closed", orderData.getIsClosed()).bind("is_fulfilled", orderData.getIsFulfilled());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -556,10 +555,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
do {
|
do {
|
||||||
byte[] initiatingOrderId = resultSet.getBytes(1);
|
byte[] initiatingOrderId = resultSet.getBytes(1);
|
||||||
byte[] targetOrderId = resultSet.getBytes(2);
|
byte[] targetOrderId = resultSet.getBytes(2);
|
||||||
BigDecimal targetAmount = resultSet.getBigDecimal(3);
|
long targetAmount = resultSet.getLong(3);
|
||||||
BigDecimal initiatorAmount = resultSet.getBigDecimal(4);
|
long initiatorAmount = resultSet.getLong(4);
|
||||||
BigDecimal initiatorSaving = resultSet.getBigDecimal(5);
|
long initiatorSaving = resultSet.getLong(5);
|
||||||
long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 6);
|
||||||
|
|
||||||
TradeData trade = new TradeData(initiatingOrderId, targetOrderId, targetAmount, initiatorAmount, initiatorSaving,
|
TradeData trade = new TradeData(initiatingOrderId, targetOrderId, targetAmount, initiatorAmount, initiatorSaving,
|
||||||
timestamp, haveAssetId, haveAssetData.getName(), wantAssetId, wantAssetData.getName());
|
timestamp, haveAssetId, haveAssetData.getName(), wantAssetId, wantAssetData.getName());
|
||||||
@ -648,9 +647,9 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
do {
|
do {
|
||||||
long haveAssetId = resultSet.getLong(1);
|
long haveAssetId = resultSet.getLong(1);
|
||||||
long wantAssetId = resultSet.getLong(2);
|
long wantAssetId = resultSet.getLong(2);
|
||||||
BigDecimal otherAmount = resultSet.getBigDecimal(3);
|
long otherAmount = resultSet.getLong(3);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(4);
|
long amount = resultSet.getLong(4);
|
||||||
long timestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 5);
|
||||||
|
|
||||||
RecentTradeData recentTrade = new RecentTradeData(haveAssetId, wantAssetId, otherAmount, amount,
|
RecentTradeData recentTrade = new RecentTradeData(haveAssetId, wantAssetId, otherAmount, amount,
|
||||||
timestamp);
|
timestamp);
|
||||||
@ -689,10 +688,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
do {
|
do {
|
||||||
byte[] initiatingOrderId = resultSet.getBytes(1);
|
byte[] initiatingOrderId = resultSet.getBytes(1);
|
||||||
byte[] targetOrderId = resultSet.getBytes(2);
|
byte[] targetOrderId = resultSet.getBytes(2);
|
||||||
BigDecimal targetAmount = resultSet.getBigDecimal(3);
|
long targetAmount = resultSet.getLong(3);
|
||||||
BigDecimal initiatorAmount = resultSet.getBigDecimal(4);
|
long initiatorAmount = resultSet.getLong(4);
|
||||||
BigDecimal initiatorSaving = resultSet.getBigDecimal(5);
|
long initiatorSaving = resultSet.getLong(5);
|
||||||
long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 6);
|
||||||
|
|
||||||
long haveAssetId = resultSet.getLong(7);
|
long haveAssetId = resultSet.getLong(7);
|
||||||
String haveAssetName = resultSet.getString(8);
|
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())
|
saveHelper.bind("initiating_order_id", tradeData.getInitiator()).bind("target_order_id", tradeData.getTarget())
|
||||||
.bind("target_amount", tradeData.getTargetAmount()).bind("initiator_amount", tradeData.getInitiatorAmount())
|
.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 {
|
try {
|
||||||
saveHelper.execute(this.repository);
|
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.getZonedTimestampMilli;
|
||||||
import static org.qortal.repository.hsqldb.HSQLDBRepository.toOffsetDateTime;
|
import static org.qortal.repository.hsqldb.HSQLDBRepository.toOffsetDateTime;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -39,14 +38,14 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
int version = resultSet.getInt(1);
|
int version = resultSet.getInt(1);
|
||||||
byte[] reference = resultSet.getBytes(2);
|
byte[] reference = resultSet.getBytes(2);
|
||||||
int transactionCount = resultSet.getInt(3);
|
int transactionCount = resultSet.getInt(3);
|
||||||
BigDecimal totalFees = resultSet.getBigDecimal(4);
|
long totalFees = resultSet.getLong(4);
|
||||||
byte[] transactionsSignature = resultSet.getBytes(5);
|
byte[] transactionsSignature = resultSet.getBytes(5);
|
||||||
int height = resultSet.getInt(6);
|
int height = resultSet.getInt(6);
|
||||||
long timestamp = getZonedTimestampMilli(resultSet, 7);
|
long timestamp = getZonedTimestampMilli(resultSet, 7);
|
||||||
byte[] minterPublicKey = resultSet.getBytes(8);
|
byte[] minterPublicKey = resultSet.getBytes(8);
|
||||||
byte[] minterSignature = resultSet.getBytes(9);
|
byte[] minterSignature = resultSet.getBytes(9);
|
||||||
int atCount = resultSet.getInt(10);
|
int atCount = resultSet.getInt(10);
|
||||||
BigDecimal atFees = resultSet.getBigDecimal(11);
|
long atFees = resultSet.getLong(11);
|
||||||
byte[] encodedOnlineAccounts = resultSet.getBytes(12);
|
byte[] encodedOnlineAccounts = resultSet.getBytes(12);
|
||||||
int onlineAccountsCount = resultSet.getInt(13);
|
int onlineAccountsCount = resultSet.getInt(13);
|
||||||
Long onlineAccountsTimestamp = getZonedTimestampMilli(resultSet, 14);
|
Long onlineAccountsTimestamp = getZonedTimestampMilli(resultSet, 14);
|
||||||
|
@ -99,7 +99,7 @@ public class HSQLDBDatabaseUpdates {
|
|||||||
stmt.execute("CREATE TYPE Signature AS VARBINARY(64)");
|
stmt.execute("CREATE TYPE Signature AS VARBINARY(64)");
|
||||||
stmt.execute("CREATE TYPE QortalAddress AS VARCHAR(36)");
|
stmt.execute("CREATE TYPE QortalAddress AS VARCHAR(36)");
|
||||||
stmt.execute("CREATE TYPE QortalPublicKey AS VARBINARY(32)");
|
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 GenericDescription AS VARCHAR(4000)");
|
||||||
stmt.execute("CREATE TYPE RegisteredName AS VARCHAR(128) COLLATE SQL_TEXT_NO_PAD");
|
stmt.execute("CREATE TYPE RegisteredName AS VARCHAR(128) COLLATE SQL_TEXT_NO_PAD");
|
||||||
stmt.execute("CREATE TYPE NameData AS VARCHAR(4000)");
|
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 GroupID AS INTEGER");
|
||||||
stmt.execute("CREATE TYPE GroupName AS VARCHAR(400) COLLATE SQL_TEXT_UCC_NO_PAD");
|
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 GroupReason AS VARCHAR(128) COLLATE SQL_TEXT_UCC_NO_PAD");
|
||||||
|
stmt.execute("CREATE TYPE RewardSharePercent AS INT");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
@ -713,10 +714,10 @@ public class HSQLDBDatabaseUpdates {
|
|||||||
case 46:
|
case 46:
|
||||||
// Proxy forging
|
// Proxy forging
|
||||||
// Transaction emitted by forger announcing they are forging on behalf of recipient
|
// 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, "
|
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 DECIMAL(5,2), PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "previous_share RewardSharePercent, PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
// Table of current shares
|
// 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))");
|
+ "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
|
// 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)");
|
stmt.execute("CREATE INDEX ProxyForgersProxyPublicKeyIndex ON ProxyForgers (proxy_public_key)");
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package org.qortal.repository.hsqldb;
|
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.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
@ -38,7 +40,11 @@ public class HSQLDBNameRepository implements NameRepository {
|
|||||||
|
|
||||||
byte[] reference = resultSet.getBytes(5);
|
byte[] reference = resultSet.getBytes(5);
|
||||||
boolean isForSale = resultSet.getBoolean(6);
|
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);
|
int creationGroupId = resultSet.getInt(8);
|
||||||
|
|
||||||
return new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId);
|
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 name = resultSet.getString(1);
|
||||||
String data = resultSet.getString(2);
|
String data = resultSet.getString(2);
|
||||||
String owner = resultSet.getString(3);
|
String owner = resultSet.getString(3);
|
||||||
long registered = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long registered = getZonedTimestampMilli(resultSet, 4);
|
||||||
|
Long updated = getZonedTimestampMilli(resultSet, 5); // can be null
|
||||||
// Special handling for possibly-NULL "updated" column
|
|
||||||
Timestamp updatedTimestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC));
|
|
||||||
Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime();
|
|
||||||
|
|
||||||
byte[] reference = resultSet.getBytes(6);
|
byte[] reference = resultSet.getBytes(6);
|
||||||
boolean isForSale = resultSet.getBoolean(7);
|
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);
|
int creationGroupId = resultSet.getInt(9);
|
||||||
|
|
||||||
names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId));
|
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 name = resultSet.getString(1);
|
||||||
String data = resultSet.getString(2);
|
String data = resultSet.getString(2);
|
||||||
String owner = resultSet.getString(3);
|
String owner = resultSet.getString(3);
|
||||||
long registered = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long registered = getZonedTimestampMilli(resultSet, 4);
|
||||||
|
Long updated = getZonedTimestampMilli(resultSet, 5); // can be null
|
||||||
// Special handling for possibly-NULL "updated" column
|
|
||||||
Timestamp updatedTimestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC));
|
|
||||||
Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime();
|
|
||||||
|
|
||||||
byte[] reference = resultSet.getBytes(6);
|
byte[] reference = resultSet.getBytes(6);
|
||||||
boolean isForSale = true;
|
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);
|
int creationGroupId = resultSet.getInt(8);
|
||||||
|
|
||||||
names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId));
|
names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId));
|
||||||
@ -152,15 +158,15 @@ public class HSQLDBNameRepository implements NameRepository {
|
|||||||
do {
|
do {
|
||||||
String name = resultSet.getString(1);
|
String name = resultSet.getString(1);
|
||||||
String data = resultSet.getString(2);
|
String data = resultSet.getString(2);
|
||||||
long registered = resultSet.getTimestamp(3, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long registered = getZonedTimestampMilli(resultSet, 3);
|
||||||
|
Long updated = getZonedTimestampMilli(resultSet, 4); // can be null
|
||||||
// Special handling for possibly-NULL "updated" column
|
|
||||||
Timestamp updatedTimestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC));
|
|
||||||
Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime();
|
|
||||||
|
|
||||||
byte[] reference = resultSet.getBytes(5);
|
byte[] reference = resultSet.getBytes(5);
|
||||||
boolean isForSale = resultSet.getBoolean(6);
|
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);
|
int creationGroupId = resultSet.getInt(8);
|
||||||
|
|
||||||
names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId));
|
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 {
|
public void save(NameData nameData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("Names");
|
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())
|
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("is_for_sale", nameData.getIsForSale()).bind("sale_price", nameData.getSalePrice())
|
||||||
.bind("creation_group_id", nameData.getCreationGroupId());
|
.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. */
|
/** 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)
|
if (timestamp == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -771,12 +771,12 @@ public class HSQLDBRepository implements Repository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Converts OffsetDateTime from TIMESTAMP WITH TIME ZONE column to milliseconds from epoch. */
|
/** 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();
|
return offsetDateTime.toInstant().toEpochMilli();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns TIMESTAMP WITH TIME ZONE column value as milliseconds from epoch, or null. */
|
/** 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);
|
OffsetDateTime offsetDateTime = resultSet.getObject(columnIndex, OffsetDateTime.class);
|
||||||
if (offsetDateTime == null)
|
if (offsetDateTime == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
package org.qortal.repository.hsqldb.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -27,7 +26,9 @@ public class HSQLDBAtTransactionRepository extends HSQLDBTransactionRepository {
|
|||||||
String atAddress = resultSet.getString(1);
|
String atAddress = resultSet.getString(1);
|
||||||
String recipient = resultSet.getString(2);
|
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);
|
Long assetId = resultSet.getLong(4);
|
||||||
if (assetId == 0 && resultSet.wasNull())
|
if (assetId == 0 && resultSet.wasNull())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
package org.qortal.repository.hsqldb.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ public class HSQLDBBuyNameTransactionRepository extends HSQLDBTransactionReposit
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
String name = resultSet.getString(1);
|
String name = resultSet.getString(1);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(2);
|
long amount = resultSet.getLong(2);
|
||||||
String seller = resultSet.getString(3);
|
String seller = resultSet.getString(3);
|
||||||
byte[] nameReference = resultSet.getBytes(4);
|
byte[] nameReference = resultSet.getBytes(4);
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
package org.qortal.repository.hsqldb.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -30,9 +29,9 @@ public class HSQLDBCreateAssetOrderTransactionRepository extends HSQLDBTransacti
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
long haveAssetId = resultSet.getLong(1);
|
long haveAssetId = resultSet.getLong(1);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(2);
|
long amount = resultSet.getLong(2);
|
||||||
long wantAssetId = resultSet.getLong(3);
|
long wantAssetId = resultSet.getLong(3);
|
||||||
BigDecimal price = resultSet.getBigDecimal(4);
|
long price = resultSet.getLong(4);
|
||||||
String haveAssetName = resultSet.getString(5);
|
String haveAssetName = resultSet.getString(5);
|
||||||
String wantAssetName = resultSet.getString(6);
|
String wantAssetName = resultSet.getString(6);
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
package org.qortal.repository.hsqldb.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -29,7 +28,7 @@ public class HSQLDBDeployAtTransactionRepository extends HSQLDBTransactionReposi
|
|||||||
String atType = resultSet.getString(3);
|
String atType = resultSet.getString(3);
|
||||||
String tags = resultSet.getString(4);
|
String tags = resultSet.getString(4);
|
||||||
byte[] creationBytes = resultSet.getBytes(5);
|
byte[] creationBytes = resultSet.getBytes(5);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(6).setScale(8);
|
long amount = resultSet.getLong(6);
|
||||||
long assetId = resultSet.getLong(7);
|
long assetId = resultSet.getLong(7);
|
||||||
|
|
||||||
// Special null-checking for AT address
|
// Special null-checking for AT address
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
package org.qortal.repository.hsqldb.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ public class HSQLDBGenesisTransactionRepository extends HSQLDBTransactionReposit
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
String recipient = resultSet.getString(1);
|
String recipient = resultSet.getString(1);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(2).setScale(8);
|
long amount = resultSet.getLong(2);
|
||||||
long assetId = resultSet.getLong(3);
|
long assetId = resultSet.getLong(3);
|
||||||
|
|
||||||
return new GenesisTransactionData(baseTransactionData, recipient, amount, assetId);
|
return new GenesisTransactionData(baseTransactionData, recipient, amount, assetId);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
package org.qortal.repository.hsqldb.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -28,7 +27,7 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit
|
|||||||
String recipient = resultSet.getString(2);
|
String recipient = resultSet.getString(2);
|
||||||
boolean isText = resultSet.getBoolean(3);
|
boolean isText = resultSet.getBoolean(3);
|
||||||
boolean isEncrypted = resultSet.getBoolean(4);
|
boolean isEncrypted = resultSet.getBoolean(4);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(5);
|
long amount = resultSet.getLong(5);
|
||||||
|
|
||||||
// Special null-checking for asset ID
|
// Special null-checking for asset ID
|
||||||
Long assetId = resultSet.getLong(6);
|
Long assetId = resultSet.getLong(6);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
package org.qortal.repository.hsqldb.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ public class HSQLDBPaymentTransactionRepository extends HSQLDBTransactionReposit
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
String recipient = resultSet.getString(1);
|
String recipient = resultSet.getString(1);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(2);
|
long amount = resultSet.getLong(2);
|
||||||
|
|
||||||
return new PaymentTransactionData(baseTransactionData, recipient, amount);
|
return new PaymentTransactionData(baseTransactionData, recipient, amount);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
package org.qortal.repository.hsqldb.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -26,8 +25,11 @@ public class HSQLDBRewardShareTransactionRepository extends HSQLDBTransactionRep
|
|||||||
|
|
||||||
String recipient = resultSet.getString(1);
|
String recipient = resultSet.getString(1);
|
||||||
byte[] rewardSharePublicKey = resultSet.getBytes(2);
|
byte[] rewardSharePublicKey = resultSet.getBytes(2);
|
||||||
BigDecimal sharePercent = resultSet.getBigDecimal(3);
|
int sharePercent = resultSet.getInt(3);
|
||||||
BigDecimal previousSharePercent = resultSet.getBigDecimal(4);
|
|
||||||
|
Integer previousSharePercent = resultSet.getInt(4);
|
||||||
|
if (previousSharePercent == 0 && resultSet.wasNull())
|
||||||
|
previousSharePercent = null;
|
||||||
|
|
||||||
return new RewardShareTransactionData(baseTransactionData, recipient, rewardSharePublicKey, sharePercent, previousSharePercent);
|
return new RewardShareTransactionData(baseTransactionData, recipient, rewardSharePublicKey, sharePercent, previousSharePercent);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
package org.qortal.repository.hsqldb.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ public class HSQLDBSellNameTransactionRepository extends HSQLDBTransactionReposi
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
String name = resultSet.getString(1);
|
String name = resultSet.getString(1);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(2);
|
long amount = resultSet.getLong(2);
|
||||||
|
|
||||||
return new SellNameTransactionData(baseTransactionData, name, amount);
|
return new SellNameTransactionData(baseTransactionData, name, amount);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
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 static org.qortal.transaction.Transaction.TransactionType.*;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -135,8 +135,12 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
|
|
||||||
byte[] reference = resultSet.getBytes(2);
|
byte[] reference = resultSet.getBytes(2);
|
||||||
byte[] creatorPublicKey = resultSet.getBytes(3);
|
byte[] creatorPublicKey = resultSet.getBytes(3);
|
||||||
long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 4);
|
||||||
BigDecimal fee = resultSet.getBigDecimal(5).setScale(8);
|
|
||||||
|
Long fee = resultSet.getLong(5);
|
||||||
|
if (fee == 0 && resultSet.wasNull())
|
||||||
|
fee = null;
|
||||||
|
|
||||||
int txGroupId = resultSet.getInt(6);
|
int txGroupId = resultSet.getInt(6);
|
||||||
|
|
||||||
Integer blockHeight = resultSet.getInt(7);
|
Integer blockHeight = resultSet.getInt(7);
|
||||||
@ -168,8 +172,12 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
|
|
||||||
byte[] signature = resultSet.getBytes(2);
|
byte[] signature = resultSet.getBytes(2);
|
||||||
byte[] creatorPublicKey = resultSet.getBytes(3);
|
byte[] creatorPublicKey = resultSet.getBytes(3);
|
||||||
long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 4);
|
||||||
BigDecimal fee = resultSet.getBigDecimal(5).setScale(8);
|
|
||||||
|
Long fee = resultSet.getLong(5);
|
||||||
|
if (fee == 0 && resultSet.wasNull())
|
||||||
|
fee = null;
|
||||||
|
|
||||||
int txGroupId = resultSet.getInt(6);
|
int txGroupId = resultSet.getInt(6);
|
||||||
|
|
||||||
Integer blockHeight = resultSet.getInt(7);
|
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
|
// NOTE: do-while because checkedExecute() above has already called rs.next() for us
|
||||||
do {
|
do {
|
||||||
String recipient = resultSet.getString(1);
|
String recipient = resultSet.getString(1);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(2);
|
long amount = resultSet.getLong(2);
|
||||||
long assetId = resultSet.getLong(3);
|
long assetId = resultSet.getLong(3);
|
||||||
|
|
||||||
payments.add(new PaymentData(recipient, assetId, amount));
|
payments.add(new PaymentData(recipient, assetId, amount));
|
||||||
@ -673,10 +681,10 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
return assetTransfers;
|
return assetTransfers;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
long timestamp = resultSet.getTimestamp(1, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
long timestamp = getZonedTimestampMilli(resultSet, 1);
|
||||||
int txGroupId = resultSet.getInt(2);
|
int txGroupId = resultSet.getInt(2);
|
||||||
byte[] reference = resultSet.getBytes(3);
|
byte[] reference = resultSet.getBytes(3);
|
||||||
BigDecimal fee = resultSet.getBigDecimal(4).setScale(8);
|
long fee = resultSet.getLong(4);
|
||||||
byte[] signature = resultSet.getBytes(5);
|
byte[] signature = resultSet.getBytes(5);
|
||||||
byte[] creatorPublicKey = resultSet.getBytes(6);
|
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);
|
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, creatorPublicKey, fee, approvalStatus, blockHeight, approvalHeight, signature);
|
||||||
|
|
||||||
String recipient = resultSet.getString(10);
|
String recipient = resultSet.getString(10);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(11);
|
long amount = resultSet.getLong(11);
|
||||||
String assetName = resultSet.getString(12);
|
String assetName = resultSet.getString(12);
|
||||||
|
|
||||||
assetTransfers.add(new TransferAssetTransactionData(baseTransactionData, recipient, amount, assetId, assetName));
|
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 {
|
public void unconfirmTransaction(TransactionData transactionData) throws DataException {
|
||||||
HSQLDBSaver saver = new HSQLDBSaver("UnconfirmedTransactions");
|
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 {
|
try {
|
||||||
saver.execute(repository);
|
saver.execute(repository);
|
||||||
@ -1044,7 +1052,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
|
|
||||||
saver.bind("signature", transactionData.getSignature()).bind("reference", transactionData.getReference())
|
saver.bind("signature", transactionData.getSignature()).bind("reference", transactionData.getReference())
|
||||||
.bind("type", transactionData.getType().value)
|
.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("fee", transactionData.getFee()).bind("milestone_block", null).bind("tx_group_id", transactionData.getTxGroupId())
|
||||||
.bind("approval_status", transactionData.getApprovalStatus().value);
|
.bind("approval_status", transactionData.getApprovalStatus().value);
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.repository.hsqldb.transaction;
|
package org.qortal.repository.hsqldb.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -27,7 +26,7 @@ public class HSQLDBTransferAssetTransactionRepository extends HSQLDBTransactionR
|
|||||||
|
|
||||||
String recipient = resultSet.getString(1);
|
String recipient = resultSet.getString(1);
|
||||||
long assetId = resultSet.getLong(2);
|
long assetId = resultSet.getLong(2);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(3);
|
long amount = resultSet.getLong(3);
|
||||||
String assetName = resultSet.getString(4);
|
String assetName = resultSet.getString(4);
|
||||||
|
|
||||||
return new TransferAssetTransactionData(baseTransactionData, recipient, amount, assetId, assetName);
|
return new TransferAssetTransactionData(baseTransactionData, recipient, amount, assetId, assetName);
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
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.AccountFlagsTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
import org.qortal.repository.DataException;
|
import org.qortal.repository.DataException;
|
||||||
@ -15,7 +12,9 @@ import org.qortal.repository.Repository;
|
|||||||
public class AccountFlagsTransaction extends Transaction {
|
public class AccountFlagsTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private AccountFlagsTransactionData accountFlagsTransactionData;
|
private AccountFlagsTransactionData accountFlagsTransactionData;
|
||||||
|
private Account targetAccount = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -28,78 +27,46 @@ public class AccountFlagsTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
return Collections.singletonList(this.accountFlagsTransactionData.getTarget());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getTarget() {
|
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
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
Account creator = getCreator();
|
// Invalid outside of genesis block
|
||||||
|
return ValidationResult.NO_FLAG_PERMISSION;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
Account target = getTarget();
|
Account target = this.getTarget();
|
||||||
Integer previousFlags = target.getFlags();
|
Integer previousFlags = target.getFlags();
|
||||||
|
|
||||||
accountFlagsTransactionData.setPreviousFlags(previousFlags);
|
this.accountFlagsTransactionData.setPreviousFlags(previousFlags);
|
||||||
|
|
||||||
// Save this transaction with target account's previous flags value
|
// 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 account doesn't have entry in database yet (e.g. genesis block) then flags are zero
|
||||||
if (previousFlags == null)
|
if (previousFlags == null)
|
||||||
previousFlags = 0;
|
previousFlags = 0;
|
||||||
|
|
||||||
// Set account's new flags
|
// Set account's new flags
|
||||||
int newFlags = previousFlags & accountFlagsTransactionData.getAndMask()
|
int newFlags = previousFlags
|
||||||
| accountFlagsTransactionData.getOrMask() ^ accountFlagsTransactionData.getXorMask();
|
& this.accountFlagsTransactionData.getAndMask()
|
||||||
|
| this.accountFlagsTransactionData.getOrMask()
|
||||||
|
^ this.accountFlagsTransactionData.getXorMask();
|
||||||
|
|
||||||
target.setFlags(newFlags);
|
target.setFlags(newFlags);
|
||||||
}
|
}
|
||||||
@ -107,15 +74,14 @@ public class AccountFlagsTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void processReferencesAndFees() throws DataException {
|
public void processReferencesAndFees() throws DataException {
|
||||||
// Set account's reference
|
// Set account's reference
|
||||||
getTarget().setLastReference(this.accountFlagsTransactionData.getSignature());
|
this.getTarget().setLastReference(this.accountFlagsTransactionData.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert
|
// Revert
|
||||||
Account target = getTarget();
|
Account target = getTarget();
|
||||||
|
Integer previousFlags = this.accountFlagsTransactionData.getPreviousFlags();
|
||||||
Integer previousFlags = accountFlagsTransactionData.getPreviousFlags();
|
|
||||||
|
|
||||||
// If previousFlags are null then account didn't exist before this transaction
|
// If previousFlags are null then account didn't exist before this transaction
|
||||||
if (previousFlags == null)
|
if (previousFlags == null)
|
||||||
@ -124,7 +90,7 @@ public class AccountFlagsTransaction extends Transaction {
|
|||||||
target.setFlags(previousFlags);
|
target.setFlags(previousFlags);
|
||||||
|
|
||||||
// Remove previous flags from transaction itself
|
// Remove previous flags from transaction itself
|
||||||
accountFlagsTransactionData.setPreviousFlags(null);
|
this.accountFlagsTransactionData.setPreviousFlags(null);
|
||||||
this.repository.getTransactionRepository().save(accountFlagsTransactionData);
|
this.repository.getTransactionRepository().save(accountFlagsTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.NullAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
|
||||||
import org.qortal.block.BlockChain;
|
import org.qortal.block.BlockChain;
|
||||||
import org.qortal.data.transaction.AccountLevelTransactionData;
|
import org.qortal.data.transaction.AccountLevelTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
@ -16,7 +13,9 @@ import org.qortal.repository.Repository;
|
|||||||
public class AccountLevelTransaction extends Transaction {
|
public class AccountLevelTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private AccountLevelTransactionData accountLevelTransactionData;
|
private AccountLevelTransactionData accountLevelTransactionData;
|
||||||
|
private Account targetAccount = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -29,59 +28,25 @@ public class AccountLevelTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
return Collections.singletonList(this.accountLevelTransactionData.getTarget());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getTarget() {
|
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
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
Account creator = getCreator();
|
// Invalid outside of genesis block
|
||||||
|
return ValidationResult.NO_FLAG_PERMISSION;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -89,13 +54,13 @@ public class AccountLevelTransaction extends Transaction {
|
|||||||
Account target = getTarget();
|
Account target = getTarget();
|
||||||
|
|
||||||
// Save this transaction
|
// Save this transaction
|
||||||
this.repository.getTransactionRepository().save(accountLevelTransactionData);
|
this.repository.getTransactionRepository().save(this.accountLevelTransactionData);
|
||||||
|
|
||||||
// Set account's initial level
|
// Set account's initial level
|
||||||
target.setLevel(this.accountLevelTransactionData.getLevel());
|
target.setLevel(this.accountLevelTransactionData.getLevel());
|
||||||
|
|
||||||
// Set account's blocks minted adjustment
|
// 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());
|
int blocksMintedAdjustment = cumulativeBlocksByLevel.get(this.accountLevelTransactionData.getLevel());
|
||||||
target.setBlocksMintedAdjustment(blocksMintedAdjustment);
|
target.setBlocksMintedAdjustment(blocksMintedAdjustment);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.transaction.AddGroupAdminTransactionData;
|
import org.qortal.data.transaction.AddGroupAdminTransactionData;
|
||||||
@ -17,7 +15,9 @@ import org.qortal.repository.Repository;
|
|||||||
public class AddGroupAdminTransaction extends Transaction {
|
public class AddGroupAdminTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private AddGroupAdminTransactionData addGroupAdminTransactionData;
|
private AddGroupAdminTransactionData addGroupAdminTransactionData;
|
||||||
|
Account memberAccount = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -30,79 +30,55 @@ public class AddGroupAdminTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
return Collections.singletonList(this.addGroupAdminTransactionData.getMember());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getOwner() throws DataException {
|
public Account getOwner() {
|
||||||
return new PublicKeyAccount(this.repository, this.addGroupAdminTransactionData.getOwnerPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getMember() throws DataException {
|
public Account getMember() {
|
||||||
return new Account(this.repository, this.addGroupAdminTransactionData.getMember());
|
if (this.memberAccount == null)
|
||||||
|
this.memberAccount = new Account(this.repository, this.addGroupAdminTransactionData.getMember());
|
||||||
|
|
||||||
|
return this.memberAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
int groupId = this.addGroupAdminTransactionData.getGroupId();
|
||||||
|
String memberAddress = this.addGroupAdminTransactionData.getMember();
|
||||||
|
|
||||||
// Check member address is valid
|
// Check member address is valid
|
||||||
if (!Crypto.isValidAddress(addGroupAdminTransactionData.getMember()))
|
if (!Crypto.isValidAddress(memberAddress))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
// Check group exists
|
// Check group exists
|
||||||
if (!this.repository.getGroupRepository().groupExists(addGroupAdminTransactionData.getGroupId()))
|
if (!this.repository.getGroupRepository().groupExists(groupId))
|
||||||
return ValidationResult.GROUP_DOES_NOT_EXIST;
|
return ValidationResult.GROUP_DOES_NOT_EXIST;
|
||||||
|
|
||||||
// Check fee is positive
|
|
||||||
if (addGroupAdminTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
Account owner = getOwner();
|
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
|
// Check transaction's public key matches group's current owner
|
||||||
if (!owner.getAddress().equals(groupOwner))
|
if (!owner.getAddress().equals(groupOwner))
|
||||||
return ValidationResult.INVALID_GROUP_OWNER;
|
return ValidationResult.INVALID_GROUP_OWNER;
|
||||||
|
|
||||||
Account member = getMember();
|
// Check address is a group member
|
||||||
|
if (!this.repository.getGroupRepository().memberExists(groupId, memberAddress))
|
||||||
// Check address is a member
|
|
||||||
if (!this.repository.getGroupRepository().memberExists(addGroupAdminTransactionData.getGroupId(), member.getAddress()))
|
|
||||||
return ValidationResult.NOT_GROUP_MEMBER;
|
return ValidationResult.NOT_GROUP_MEMBER;
|
||||||
|
|
||||||
// Check member is not already an admin
|
// Check group member is not already an admin
|
||||||
if (this.repository.getGroupRepository().adminExists(addGroupAdminTransactionData.getGroupId(), member.getAddress()))
|
if (this.repository.getGroupRepository().adminExists(groupId, memberAddress))
|
||||||
return ValidationResult.ALREADY_GROUP_ADMIN;
|
return ValidationResult.ALREADY_GROUP_ADMIN;
|
||||||
|
|
||||||
// Check group owner has enough funds
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -111,15 +87,15 @@ public class AddGroupAdminTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Group adminship
|
// Update Group adminship
|
||||||
Group group = new Group(this.repository, addGroupAdminTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.addGroupAdminTransactionData.getGroupId());
|
||||||
group.promoteToAdmin(addGroupAdminTransactionData);
|
group.promoteToAdmin(this.addGroupAdminTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert group adminship
|
// Revert group adminship
|
||||||
Group group = new Group(this.repository, addGroupAdminTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.addGroupAdminTransactionData.getGroupId());
|
||||||
group.unpromoteToAdmin(addGroupAdminTransactionData);
|
group.unpromoteToAdmin(this.addGroupAdminTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,12 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
|
||||||
import org.qortal.data.PaymentData;
|
import org.qortal.data.PaymentData;
|
||||||
import org.qortal.data.transaction.ArbitraryTransactionData;
|
import org.qortal.data.transaction.ArbitraryTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
@ -33,57 +30,14 @@ public class ArbitraryTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
List<Account> recipients = new ArrayList<>();
|
return this.arbitraryTransactionData.getPayments().stream().map(PaymentData::getRecipient).collect(Collectors.toList());
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getSender() throws DataException {
|
public Account getSender() {
|
||||||
return new PublicKeyAccount(this.repository, this.arbitraryTransactionData.getSenderPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
@ -110,7 +64,7 @@ public class ArbitraryTransaction extends Transaction {
|
|||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Wrap and delegate payment processing to Payment class.
|
// Wrap and delegate payment processing to Payment class.
|
||||||
new Payment(this.repository).process(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
|
new Payment(this.repository).process(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
|
||||||
arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature());
|
arbitraryTransactionData.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -124,7 +78,7 @@ public class ArbitraryTransaction extends Transaction {
|
|||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Wrap and delegate payment processing to Payment class.
|
// Wrap and delegate payment processing to Payment class.
|
||||||
new Payment(this.repository).orphan(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
|
new Payment(this.repository).orphan(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
|
||||||
arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature(), arbitraryTransactionData.getReference());
|
arbitraryTransactionData.getSignature(), arbitraryTransactionData.getReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
@ -22,7 +19,10 @@ import com.google.common.primitives.Bytes;
|
|||||||
public class AtTransaction extends Transaction {
|
public class AtTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private ATTransactionData atTransactionData;
|
private ATTransactionData atTransactionData;
|
||||||
|
private Account atAccount = null;
|
||||||
|
private Account recipientAccount = null;
|
||||||
|
|
||||||
// Other useful constants
|
// Other useful constants
|
||||||
public static final int MAX_DATA_SIZE = 256;
|
public static final int MAX_DATA_SIZE = 256;
|
||||||
@ -50,97 +50,62 @@ public class AtTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, this.atTransactionData.getRecipient()));
|
return Arrays.asList(this.atTransactionData.getATAddress(), 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getATAccount() throws DataException {
|
public Account getATAccount() {
|
||||||
return new Account(this.repository, this.atTransactionData.getATAddress());
|
if (this.atAccount == null)
|
||||||
|
this.atAccount = new Account(this.repository, this.atTransactionData.getATAddress());
|
||||||
|
|
||||||
|
return this.atAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getRecipient() throws DataException {
|
public Account getRecipient() {
|
||||||
return new Account(this.repository, this.atTransactionData.getRecipient());
|
if (this.recipientAccount == null)
|
||||||
|
this.recipientAccount = new Account(this.repository, this.atTransactionData.getRecipient());
|
||||||
|
|
||||||
|
return this.recipientAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasValidReference() throws DataException {
|
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();
|
Account atAccount = getATAccount();
|
||||||
return Arrays.equals(atAccount.getLastReference(), atTransactionData.getReference());
|
return Arrays.equals(atAccount.getLastReference(), atTransactionData.getReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
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
|
// Check recipient address is valid
|
||||||
if (!Crypto.isValidAddress(this.atTransactionData.getRecipient()))
|
if (!Crypto.isValidAddress(this.atTransactionData.getRecipient()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
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();
|
long assetId = this.atTransactionData.getAssetId();
|
||||||
AssetData assetData = this.repository.getAssetRepository().fromAssetId(assetId);
|
AssetData assetData = this.repository.getAssetRepository().fromAssetId(assetId);
|
||||||
// Check asset even exists
|
// Check asset even exists
|
||||||
@ -148,12 +113,12 @@ public class AtTransaction extends Transaction {
|
|||||||
return ValidationResult.ASSET_DOES_NOT_EXIST;
|
return ValidationResult.ASSET_DOES_NOT_EXIST;
|
||||||
|
|
||||||
// Check asset amount is integer if asset is not divisible
|
// 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;
|
return ValidationResult.INVALID_AMOUNT;
|
||||||
|
|
||||||
Account sender = getATAccount();
|
Account sender = getATAccount();
|
||||||
// Check sender has enough of asset
|
// Check sender has enough of asset
|
||||||
if (sender.getConfirmedBalance(assetId).compareTo(amount) < 0)
|
if (sender.getConfirmedBalance(assetId) < amount)
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -161,18 +126,19 @@ public class AtTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
if (this.atTransactionData.getAmount() != null) {
|
Long amount = this.atTransactionData.getAmount();
|
||||||
|
|
||||||
|
if (amount != null) {
|
||||||
Account sender = getATAccount();
|
Account sender = getATAccount();
|
||||||
Account recipient = getRecipient();
|
Account recipient = getRecipient();
|
||||||
|
|
||||||
long assetId = this.atTransactionData.getAssetId();
|
long assetId = this.atTransactionData.getAssetId();
|
||||||
BigDecimal amount = this.atTransactionData.getAmount();
|
|
||||||
|
|
||||||
// Update sender's balance due to amount
|
// 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
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
if (this.atTransactionData.getAmount() != null) {
|
Long amount = this.atTransactionData.getAmount();
|
||||||
|
|
||||||
|
if (amount != null) {
|
||||||
Account sender = getATAccount();
|
Account sender = getATAccount();
|
||||||
Account recipient = getRecipient();
|
Account recipient = getRecipient();
|
||||||
|
|
||||||
long assetId = this.atTransactionData.getAssetId();
|
long assetId = this.atTransactionData.getAssetId();
|
||||||
BigDecimal amount = this.atTransactionData.getAmount();
|
|
||||||
|
|
||||||
// Update sender's balance due to amount
|
// 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
|
// 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
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.naming.NameData;
|
import org.qortal.data.naming.NameData;
|
||||||
import org.qortal.data.transaction.BuyNameTransactionData;
|
import org.qortal.data.transaction.BuyNameTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
@ -19,6 +18,7 @@ import com.google.common.base.Utf8;
|
|||||||
public class BuyNameTransaction extends Transaction {
|
public class BuyNameTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private BuyNameTransactionData buyNameTransactionData;
|
private BuyNameTransactionData buyNameTransactionData;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
@ -32,57 +32,36 @@ public class BuyNameTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, this.buyNameTransactionData.getSeller()));
|
return Collections.singletonList(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getBuyer() throws DataException {
|
public Account getBuyer() {
|
||||||
return new PublicKeyAccount(this.repository, this.buyNameTransactionData.getBuyerPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
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
|
// Check name size bounds
|
||||||
int nameLength = Utf8.encodedLength(buyNameTransactionData.getName());
|
int nameLength = Utf8.encodedLength(name);
|
||||||
if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE)
|
if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE)
|
||||||
return ValidationResult.INVALID_NAME_LENGTH;
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
// Check name is lowercase
|
// Check name is lowercase
|
||||||
if (!buyNameTransactionData.getName().equals(buyNameTransactionData.getName().toLowerCase()))
|
if (!name.equals(name.toLowerCase()))
|
||||||
return ValidationResult.NAME_NOT_LOWER_CASE;
|
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||||
|
|
||||||
NameData nameData = this.repository.getNameRepository().fromName(buyNameTransactionData.getName());
|
NameData nameData = this.repository.getNameRepository().fromName(name);
|
||||||
|
|
||||||
// Check name exists
|
// Check name exists
|
||||||
if (nameData == null)
|
if (nameData == null)
|
||||||
@ -98,19 +77,15 @@ public class BuyNameTransaction extends Transaction {
|
|||||||
return ValidationResult.BUYER_ALREADY_OWNER;
|
return ValidationResult.BUYER_ALREADY_OWNER;
|
||||||
|
|
||||||
// Check expected seller currently owns name
|
// Check expected seller currently owns name
|
||||||
if (!buyNameTransactionData.getSeller().equals(nameData.getOwner()))
|
if (!this.buyNameTransactionData.getSeller().equals(nameData.getOwner()))
|
||||||
return ValidationResult.INVALID_SELLER;
|
return ValidationResult.INVALID_SELLER;
|
||||||
|
|
||||||
// Check amounts agree
|
// Check amounts agree
|
||||||
if (buyNameTransactionData.getAmount().compareTo(nameData.getSalePrice()) != 0)
|
if (this.buyNameTransactionData.getAmount() != nameData.getSalePrice())
|
||||||
return ValidationResult.INVALID_AMOUNT;
|
return ValidationResult.INVALID_AMOUNT;
|
||||||
|
|
||||||
// Check fee is positive
|
|
||||||
if (buyNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
// Check issuer has enough funds
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -119,21 +94,21 @@ public class BuyNameTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Name
|
// Update Name
|
||||||
Name name = new Name(this.repository, buyNameTransactionData.getName());
|
Name name = new Name(this.repository, this.buyNameTransactionData.getName());
|
||||||
name.buy(buyNameTransactionData);
|
name.buy(this.buyNameTransactionData);
|
||||||
|
|
||||||
// Save transaction with updated "name reference" pointing to previous transaction that updated name
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert name
|
// Revert name
|
||||||
Name name = new Name(this.repository, buyNameTransactionData.getName());
|
Name name = new Name(this.repository, this.buyNameTransactionData.getName());
|
||||||
name.unbuy(buyNameTransactionData);
|
name.unbuy(this.buyNameTransactionData);
|
||||||
|
|
||||||
// Save this transaction, with removed "name reference"
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.util.Arrays;
|
||||||
import java.util.ArrayList;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.asset.Order;
|
import org.qortal.asset.Order;
|
||||||
import org.qortal.crypto.Crypto;
|
|
||||||
import org.qortal.data.asset.OrderData;
|
import org.qortal.data.asset.OrderData;
|
||||||
import org.qortal.data.transaction.CancelAssetOrderTransactionData;
|
import org.qortal.data.transaction.CancelAssetOrderTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
@ -32,30 +30,8 @@ public class CancelAssetOrderTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() {
|
public List<String> getRecipientAddresses() {
|
||||||
return new ArrayList<>();
|
return Collections.emptyList();
|
||||||
}
|
|
||||||
|
|
||||||
@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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
@ -64,12 +40,8 @@ public class CancelAssetOrderTransaction extends Transaction {
|
|||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
AssetRepository assetRepository = this.repository.getAssetRepository();
|
AssetRepository assetRepository = this.repository.getAssetRepository();
|
||||||
|
|
||||||
// Check fee is positive
|
|
||||||
if (cancelOrderTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
// Check order even exists
|
// Check order even exists
|
||||||
OrderData orderData = assetRepository.fromOrderId(cancelOrderTransactionData.getOrderId());
|
OrderData orderData = assetRepository.fromOrderId(this.cancelOrderTransactionData.getOrderId());
|
||||||
|
|
||||||
if (orderData == null)
|
if (orderData == null)
|
||||||
return ValidationResult.ORDER_DOES_NOT_EXIST;
|
return ValidationResult.ORDER_DOES_NOT_EXIST;
|
||||||
@ -77,19 +49,14 @@ public class CancelAssetOrderTransaction extends Transaction {
|
|||||||
if (orderData.getIsClosed())
|
if (orderData.getIsClosed())
|
||||||
return ValidationResult.ORDER_ALREADY_CLOSED;
|
return ValidationResult.ORDER_ALREADY_CLOSED;
|
||||||
|
|
||||||
Account creator = getCreator();
|
// Check transaction creator matches order creator
|
||||||
|
if (!Arrays.equals(this.transactionData.getCreatorPublicKey(), orderData.getCreatorPublicKey()))
|
||||||
// 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()))
|
|
||||||
return ValidationResult.INVALID_ORDER_CREATOR;
|
return ValidationResult.INVALID_ORDER_CREATOR;
|
||||||
|
|
||||||
|
Account creator = getCreator();
|
||||||
|
|
||||||
// Check creator has enough QORT for fee
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -98,7 +65,7 @@ public class CancelAssetOrderTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Mark Order as completed so no more trades can happen
|
// 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 order = new Order(this.repository, orderData);
|
||||||
order.cancel();
|
order.cancel();
|
||||||
}
|
}
|
||||||
@ -106,7 +73,7 @@ public class CancelAssetOrderTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Unmark Order as completed so trades can happen again
|
// 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 order = new Order(this.repository, orderData);
|
||||||
order.reopen();
|
order.reopen();
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.group.GroupData;
|
import org.qortal.data.group.GroupData;
|
||||||
@ -18,7 +16,9 @@ import org.qortal.repository.Repository;
|
|||||||
public class CancelGroupBanTransaction extends Transaction {
|
public class CancelGroupBanTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private CancelGroupBanTransactionData groupUnbanTransactionData;
|
private CancelGroupBanTransactionData groupUnbanTransactionData;
|
||||||
|
private Account memberAccount = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -31,53 +31,34 @@ public class CancelGroupBanTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
return Collections.singletonList(this.groupUnbanTransactionData.getMember());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getAdmin() throws DataException {
|
public Account getAdmin() {
|
||||||
return new PublicKeyAccount(this.repository, this.groupUnbanTransactionData.getAdminPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getMember() throws DataException {
|
public Account getMember() {
|
||||||
return new Account(this.repository, this.groupUnbanTransactionData.getMember());
|
if (this.memberAccount == null)
|
||||||
|
this.memberAccount = new Account(this.repository, this.groupUnbanTransactionData.getMember());
|
||||||
|
|
||||||
|
return this.memberAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
int groupId = this.groupUnbanTransactionData.getGroupId();
|
||||||
|
|
||||||
// Check member address is valid
|
// Check member address is valid
|
||||||
if (!Crypto.isValidAddress(groupUnbanTransactionData.getMember()))
|
if (!Crypto.isValidAddress(this.groupUnbanTransactionData.getMember()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupUnbanTransactionData.getGroupId());
|
GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupId);
|
||||||
|
|
||||||
// Check group exists
|
// Check group exists
|
||||||
if (groupData == null)
|
if (groupData == null)
|
||||||
@ -86,21 +67,17 @@ public class CancelGroupBanTransaction extends Transaction {
|
|||||||
Account admin = getAdmin();
|
Account admin = getAdmin();
|
||||||
|
|
||||||
// Can't unban if not an admin
|
// 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;
|
return ValidationResult.NOT_GROUP_ADMIN;
|
||||||
|
|
||||||
Account member = getMember();
|
Account member = getMember();
|
||||||
|
|
||||||
// Check ban actually exists
|
// 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;
|
return ValidationResult.BAN_UNKNOWN;
|
||||||
|
|
||||||
// Check fee is positive
|
// Check admin has enough funds
|
||||||
if (groupUnbanTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
if (admin.getConfirmedBalance(Asset.QORT) < this.groupUnbanTransactionData.getFee())
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
// Check creator has enough funds
|
|
||||||
if (admin.getConfirmedBalance(Asset.QORT).compareTo(groupUnbanTransactionData.getFee()) < 0)
|
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -109,21 +86,21 @@ public class CancelGroupBanTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Group Membership
|
// Update Group Membership
|
||||||
Group group = new Group(this.repository, groupUnbanTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.groupUnbanTransactionData.getGroupId());
|
||||||
group.cancelBan(groupUnbanTransactionData);
|
group.cancelBan(this.groupUnbanTransactionData);
|
||||||
|
|
||||||
// Save this transaction with updated member/admin references to transactions that can help restore state
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert group membership
|
// Revert group membership
|
||||||
Group group = new Group(this.repository, groupUnbanTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.groupUnbanTransactionData.getGroupId());
|
||||||
group.uncancelBan(groupUnbanTransactionData);
|
group.uncancelBan(this.groupUnbanTransactionData);
|
||||||
|
|
||||||
// Save this transaction with removed member/admin references
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.group.GroupData;
|
import org.qortal.data.group.GroupData;
|
||||||
@ -18,7 +16,9 @@ import org.qortal.repository.Repository;
|
|||||||
public class CancelGroupInviteTransaction extends Transaction {
|
public class CancelGroupInviteTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private CancelGroupInviteTransactionData cancelGroupInviteTransactionData;
|
private CancelGroupInviteTransactionData cancelGroupInviteTransactionData;
|
||||||
|
private Account inviteeAccount = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -31,53 +31,34 @@ public class CancelGroupInviteTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
return Collections.singletonList(this.cancelGroupInviteTransactionData.getInvitee());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getAdmin() throws DataException {
|
public Account getAdmin() {
|
||||||
return new PublicKeyAccount(this.repository, this.cancelGroupInviteTransactionData.getAdminPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getInvitee() throws DataException {
|
public Account getInvitee() {
|
||||||
return new Account(this.repository, this.cancelGroupInviteTransactionData.getInvitee());
|
if (this.inviteeAccount == null)
|
||||||
|
this.inviteeAccount = new Account(this.repository, this.cancelGroupInviteTransactionData.getInvitee());
|
||||||
|
|
||||||
|
return this.inviteeAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
int groupId = this.cancelGroupInviteTransactionData.getGroupId();
|
||||||
|
|
||||||
// Check invitee address is valid
|
// Check invitee address is valid
|
||||||
if (!Crypto.isValidAddress(cancelGroupInviteTransactionData.getInvitee()))
|
if (!Crypto.isValidAddress(this.cancelGroupInviteTransactionData.getInvitee()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
GroupData groupData = this.repository.getGroupRepository().fromGroupId(cancelGroupInviteTransactionData.getGroupId());
|
GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupId);
|
||||||
|
|
||||||
// Check group exists
|
// Check group exists
|
||||||
if (groupData == null)
|
if (groupData == null)
|
||||||
@ -86,21 +67,17 @@ public class CancelGroupInviteTransaction extends Transaction {
|
|||||||
Account admin = getAdmin();
|
Account admin = getAdmin();
|
||||||
|
|
||||||
// Check admin is actually an admin
|
// 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;
|
return ValidationResult.NOT_GROUP_ADMIN;
|
||||||
|
|
||||||
Account invitee = getInvitee();
|
Account invitee = getInvitee();
|
||||||
|
|
||||||
// Check invite exists
|
// Check invite exists
|
||||||
if (!this.repository.getGroupRepository().inviteExists(cancelGroupInviteTransactionData.getGroupId(), invitee.getAddress()))
|
if (!this.repository.getGroupRepository().inviteExists(groupId, invitee.getAddress()))
|
||||||
return ValidationResult.INVITE_UNKNOWN;
|
return ValidationResult.INVITE_UNKNOWN;
|
||||||
|
|
||||||
// Check fee is positive
|
|
||||||
if (cancelGroupInviteTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
// Check creator has enough funds
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -109,21 +86,21 @@ public class CancelGroupInviteTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Group Membership
|
// Update Group Membership
|
||||||
Group group = new Group(this.repository, cancelGroupInviteTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.cancelGroupInviteTransactionData.getGroupId());
|
||||||
group.cancelInvite(cancelGroupInviteTransactionData);
|
group.cancelInvite(this.cancelGroupInviteTransactionData);
|
||||||
|
|
||||||
// Save this transaction with updated member/admin references to transactions that can help restore state
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert group membership
|
// Revert group membership
|
||||||
Group group = new Group(this.repository, cancelGroupInviteTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.cancelGroupInviteTransactionData.getGroupId());
|
||||||
group.uncancelInvite(cancelGroupInviteTransactionData);
|
group.uncancelInvite(this.cancelGroupInviteTransactionData);
|
||||||
|
|
||||||
// Save this transaction with removed member/admin references
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.util.Collections;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.data.naming.NameData;
|
import org.qortal.data.naming.NameData;
|
||||||
import org.qortal.data.transaction.CancelSellNameTransactionData;
|
import org.qortal.data.transaction.CancelSellNameTransactionData;
|
||||||
@ -32,51 +30,32 @@ public class CancelSellNameTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return new ArrayList<>();
|
return Collections.emptyList();
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getOwner() throws DataException {
|
public Account getOwner() {
|
||||||
return new PublicKeyAccount(this.repository, this.cancelSellNameTransactionData.getOwnerPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
String name = this.cancelSellNameTransactionData.getName();
|
||||||
|
|
||||||
// Check name size bounds
|
// Check name size bounds
|
||||||
int nameLength = Utf8.encodedLength(cancelSellNameTransactionData.getName());
|
int nameLength = Utf8.encodedLength(name);
|
||||||
if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE)
|
if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE)
|
||||||
return ValidationResult.INVALID_NAME_LENGTH;
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
// Check name is lowercase
|
// Check name is lowercase
|
||||||
if (!cancelSellNameTransactionData.getName().equals(cancelSellNameTransactionData.getName().toLowerCase()))
|
if (!name.equals(name.toLowerCase()))
|
||||||
return ValidationResult.NAME_NOT_LOWER_CASE;
|
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||||
|
|
||||||
NameData nameData = this.repository.getNameRepository().fromName(cancelSellNameTransactionData.getName());
|
NameData nameData = this.repository.getNameRepository().fromName(name);
|
||||||
|
|
||||||
// Check name exists
|
// Check name exists
|
||||||
if (nameData == null)
|
if (nameData == null)
|
||||||
@ -86,17 +65,13 @@ public class CancelSellNameTransaction extends Transaction {
|
|||||||
if (!nameData.getIsForSale())
|
if (!nameData.getIsForSale())
|
||||||
return ValidationResult.NAME_NOT_FOR_SALE;
|
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();
|
Account owner = getOwner();
|
||||||
if (!owner.getAddress().equals(nameData.getOwner()))
|
if (!owner.getAddress().equals(nameData.getOwner()))
|
||||||
return ValidationResult.INVALID_NAME_OWNER;
|
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
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.util.Collections;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.asset.Order;
|
import org.qortal.asset.Order;
|
||||||
import org.qortal.data.asset.AssetData;
|
import org.qortal.data.asset.AssetData;
|
||||||
@ -32,32 +30,12 @@ public class CreateAssetOrderTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return new ArrayList<>();
|
return Collections.emptyList();
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
// Navigation
|
||||||
|
|
||||||
@Override
|
|
||||||
public PublicKeyAccount getCreator() throws DataException {
|
|
||||||
return new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Order getOrder() throws DataException {
|
public Order getOrder() throws DataException {
|
||||||
// orderId is the transaction signature
|
// orderId is the transaction signature
|
||||||
OrderData orderData = this.repository.getAssetRepository().fromOrderId(this.createOrderTransactionData.getSignature());
|
OrderData orderData = this.repository.getAssetRepository().fromOrderId(this.createOrderTransactionData.getSignature());
|
||||||
@ -68,25 +46,21 @@ public class CreateAssetOrderTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
long haveAssetId = createOrderTransactionData.getHaveAssetId();
|
long haveAssetId = this.createOrderTransactionData.getHaveAssetId();
|
||||||
long wantAssetId = createOrderTransactionData.getWantAssetId();
|
long wantAssetId = this.createOrderTransactionData.getWantAssetId();
|
||||||
|
|
||||||
// Check have/want assets are not the same
|
// Check have/want assets are not the same
|
||||||
if (haveAssetId == wantAssetId)
|
if (haveAssetId == wantAssetId)
|
||||||
return ValidationResult.HAVE_EQUALS_WANT;
|
return ValidationResult.HAVE_EQUALS_WANT;
|
||||||
|
|
||||||
// Check amount is positive
|
// Check amount is positive
|
||||||
if (createOrderTransactionData.getAmount().compareTo(BigDecimal.ZERO) <= 0)
|
if (this.createOrderTransactionData.getAmount() <= 0)
|
||||||
return ValidationResult.NEGATIVE_AMOUNT;
|
return ValidationResult.NEGATIVE_AMOUNT;
|
||||||
|
|
||||||
// Check price is positive
|
// Check price is positive
|
||||||
if (createOrderTransactionData.getPrice().compareTo(BigDecimal.ZERO) <= 0)
|
if (this.createOrderTransactionData.getPrice() <= 0)
|
||||||
return ValidationResult.NEGATIVE_PRICE;
|
return ValidationResult.NEGATIVE_PRICE;
|
||||||
|
|
||||||
// Check fee is positive
|
|
||||||
if (createOrderTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
AssetRepository assetRepository = this.repository.getAssetRepository();
|
AssetRepository assetRepository = this.repository.getAssetRepository();
|
||||||
|
|
||||||
// Check "have" asset exists
|
// Check "have" asset exists
|
||||||
@ -105,8 +79,8 @@ public class CreateAssetOrderTransaction extends Transaction {
|
|||||||
|
|
||||||
Account creator = getCreator();
|
Account creator = getCreator();
|
||||||
|
|
||||||
BigDecimal committedCost;
|
long committedCost;
|
||||||
BigDecimal maxOtherAmount;
|
long maxOtherAmount;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "amount" might be either have-asset or want-asset, whichever has the highest assetID.
|
* "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) {
|
if (isAmountWantAsset) {
|
||||||
// have/commit 49200 QORT, want/return 123 GOLD
|
// have/commit 49200 QORT, want/return 123 GOLD
|
||||||
committedCost = createOrderTransactionData.getAmount().multiply(createOrderTransactionData.getPrice());
|
committedCost = this.createOrderTransactionData.getAmount() * this.createOrderTransactionData.getPrice();
|
||||||
maxOtherAmount = createOrderTransactionData.getAmount();
|
maxOtherAmount = this.createOrderTransactionData.getAmount();
|
||||||
} else {
|
} else {
|
||||||
// have/commit 123 GOLD, want/return 49200 QORT
|
// have/commit 123 GOLD, want/return 49200 QORT
|
||||||
committedCost = createOrderTransactionData.getAmount();
|
committedCost = this.createOrderTransactionData.getAmount();
|
||||||
maxOtherAmount = createOrderTransactionData.getAmount().multiply(createOrderTransactionData.getPrice());
|
maxOtherAmount = this.createOrderTransactionData.getAmount() * this.createOrderTransactionData.getPrice();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check amount is integer if amount's asset is not divisible
|
// 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;
|
return ValidationResult.INVALID_AMOUNT;
|
||||||
|
|
||||||
// Check total return from fulfilled order would be integer if return's asset is not divisible
|
// 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;
|
return ValidationResult.INVALID_RETURN;
|
||||||
|
|
||||||
// Check order creator has enough asset balance AFTER removing fee, in case asset is QORT
|
// 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 asset is QORT then we need to check amount + fee in one go
|
||||||
if (haveAssetId == Asset.QORT) {
|
if (haveAssetId == Asset.QORT) {
|
||||||
// Check creator has enough funds for amount + fee in 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;
|
return ValidationResult.NO_BALANCE;
|
||||||
} else {
|
} else {
|
||||||
// Check creator has enough funds for amount in whatever asset
|
// 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;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
// Check creator has enough funds for fee in QORT
|
// 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;
|
return ValidationResult.NO_BALANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,12 +134,13 @@ public class CreateAssetOrderTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Order Id is transaction's signature
|
// Order Id is transaction's signature
|
||||||
byte[] orderId = createOrderTransactionData.getSignature();
|
byte[] orderId = this.createOrderTransactionData.getSignature();
|
||||||
|
|
||||||
// Process the order itself
|
// Process the order itself
|
||||||
OrderData orderData = new OrderData(orderId, createOrderTransactionData.getCreatorPublicKey(), createOrderTransactionData.getHaveAssetId(),
|
OrderData orderData = new OrderData(orderId, this.createOrderTransactionData.getCreatorPublicKey(),
|
||||||
createOrderTransactionData.getWantAssetId(), createOrderTransactionData.getAmount(), createOrderTransactionData.getPrice(),
|
this.createOrderTransactionData.getHaveAssetId(), this.createOrderTransactionData.getWantAssetId(),
|
||||||
createOrderTransactionData.getTimestamp());
|
this.createOrderTransactionData.getAmount(), this.createOrderTransactionData.getPrice(),
|
||||||
|
this.createOrderTransactionData.getTimestamp());
|
||||||
|
|
||||||
new Order(this.repository, orderData).process();
|
new Order(this.repository, orderData).process();
|
||||||
}
|
}
|
||||||
@ -173,7 +148,7 @@ public class CreateAssetOrderTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Order Id is transaction's signature
|
// Order Id is transaction's signature
|
||||||
byte[] orderId = createOrderTransactionData.getSignature();
|
byte[] orderId = this.createOrderTransactionData.getSignature();
|
||||||
|
|
||||||
// Orphan the order itself
|
// Orphan the order itself
|
||||||
OrderData orderData = this.repository.getAssetRepository().fromOrderId(orderId);
|
OrderData orderData = this.repository.getAssetRepository().fromOrderId(orderId);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -31,38 +30,14 @@ public class CreateGroupTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(getOwner());
|
return Collections.singletonList(this.createGroupTransactionData.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getOwner() throws DataException {
|
public Account getOwner() {
|
||||||
return new Account(this.repository, this.createGroupTransactionData.getOwner());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
@ -70,45 +45,41 @@ public class CreateGroupTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Check owner address is valid
|
// Check owner address is valid
|
||||||
if (!Crypto.isValidAddress(createGroupTransactionData.getOwner()))
|
if (!Crypto.isValidAddress(this.createGroupTransactionData.getOwner()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
// Check approval threshold is valid
|
// Check approval threshold is valid
|
||||||
if (createGroupTransactionData.getApprovalThreshold() == null)
|
if (this.createGroupTransactionData.getApprovalThreshold() == null)
|
||||||
return ValidationResult.INVALID_GROUP_APPROVAL_THRESHOLD;
|
return ValidationResult.INVALID_GROUP_APPROVAL_THRESHOLD;
|
||||||
|
|
||||||
// Check min/max block delay values
|
// Check min/max block delay values
|
||||||
if (createGroupTransactionData.getMinimumBlockDelay() < 0)
|
if (this.createGroupTransactionData.getMinimumBlockDelay() < 0)
|
||||||
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
||||||
|
|
||||||
if (createGroupTransactionData.getMaximumBlockDelay() < 1)
|
if (this.createGroupTransactionData.getMaximumBlockDelay() < 1)
|
||||||
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
||||||
|
|
||||||
if (createGroupTransactionData.getMaximumBlockDelay() < createGroupTransactionData.getMinimumBlockDelay())
|
if (this.createGroupTransactionData.getMaximumBlockDelay() < this.createGroupTransactionData.getMinimumBlockDelay())
|
||||||
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
||||||
|
|
||||||
// Check group name size bounds
|
// 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)
|
if (groupNameLength < 1 || groupNameLength > Group.MAX_NAME_SIZE)
|
||||||
return ValidationResult.INVALID_NAME_LENGTH;
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
// Check description size bounds
|
// 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)
|
if (descriptionLength < 1 || descriptionLength > Group.MAX_DESCRIPTION_SIZE)
|
||||||
return ValidationResult.INVALID_DESCRIPTION_LENGTH;
|
return ValidationResult.INVALID_DESCRIPTION_LENGTH;
|
||||||
|
|
||||||
// Check group name is lowercase
|
// 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;
|
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||||
|
|
||||||
// Check fee is positive
|
|
||||||
if (createGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
Account creator = getCreator();
|
Account creator = getCreator();
|
||||||
|
|
||||||
// Check creator has enough funds
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -117,7 +88,7 @@ public class CreateGroupTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
public ValidationResult isProcessable() throws DataException {
|
||||||
// Check the group name isn't already taken
|
// 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.GROUP_ALREADY_EXISTS;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -126,27 +97,27 @@ public class CreateGroupTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Create Group
|
// Create Group
|
||||||
Group group = new Group(this.repository, createGroupTransactionData);
|
Group group = new Group(this.repository, this.createGroupTransactionData);
|
||||||
group.create(createGroupTransactionData);
|
group.create(this.createGroupTransactionData);
|
||||||
|
|
||||||
// Note newly assigned group ID in our transaction record
|
// 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
|
// Save this transaction with newly assigned group ID
|
||||||
this.repository.getTransactionRepository().save(createGroupTransactionData);
|
this.repository.getTransactionRepository().save(this.createGroupTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Uncreate group
|
// Uncreate group
|
||||||
Group group = new Group(this.repository, createGroupTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.createGroupTransactionData.getGroupId());
|
||||||
group.uncreate();
|
group.uncreate();
|
||||||
|
|
||||||
// Remove assigned group ID from transaction record
|
// Remove assigned group ID from transaction record
|
||||||
createGroupTransactionData.setGroupId(null);
|
this.createGroupTransactionData.setGroupId(null);
|
||||||
|
|
||||||
// Save this transaction with removed group ID
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.transaction.CreatePollTransactionData;
|
import org.qortal.data.transaction.CreatePollTransactionData;
|
||||||
@ -34,42 +32,13 @@ public class CreatePollTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(getOwner());
|
return Collections.singletonList(this.createPollTransactionData.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
@Override
|
public Account getOwner() {
|
||||||
public PublicKeyAccount getCreator() throws DataException {
|
|
||||||
return new PublicKeyAccount(this.repository, this.createPollTransactionData.getCreatorPublicKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Account getOwner() throws DataException {
|
|
||||||
return new Account(this.repository, this.createPollTransactionData.getOwner());
|
return new Account(this.repository, this.createPollTransactionData.getOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,33 +47,31 @@ public class CreatePollTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Check owner address is valid
|
// Check owner address is valid
|
||||||
if (!Crypto.isValidAddress(createPollTransactionData.getOwner()))
|
if (!Crypto.isValidAddress(this.createPollTransactionData.getOwner()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
// Check name size bounds
|
// 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)
|
if (pollNameLength < 1 || pollNameLength > Poll.MAX_NAME_SIZE)
|
||||||
return ValidationResult.INVALID_NAME_LENGTH;
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
// Check description size bounds
|
// 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)
|
if (pollDescriptionLength < 1 || pollDescriptionLength > Poll.MAX_DESCRIPTION_SIZE)
|
||||||
return ValidationResult.INVALID_DESCRIPTION_LENGTH;
|
return ValidationResult.INVALID_DESCRIPTION_LENGTH;
|
||||||
|
|
||||||
// Check poll name is lowercase
|
// 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;
|
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
|
// Check number of options
|
||||||
List<PollOptionData> pollOptions = createPollTransactionData.getPollOptions();
|
List<PollOptionData> pollOptions = this.createPollTransactionData.getPollOptions();
|
||||||
int pollOptionsCount = pollOptions.size();
|
int pollOptionsCount = pollOptions.size();
|
||||||
if (pollOptionsCount < 1 || pollOptionsCount > Poll.MAX_OPTIONS)
|
if (pollOptionsCount < 1 || pollOptionsCount > Poll.MAX_OPTIONS)
|
||||||
return ValidationResult.INVALID_OPTIONS_COUNT;
|
return ValidationResult.INVALID_OPTIONS_COUNT;
|
||||||
|
|
||||||
// Check each option
|
// Check each option
|
||||||
List<String> optionNames = new ArrayList<String>();
|
List<String> optionNames = new ArrayList<>();
|
||||||
for (PollOptionData pollOptionData : pollOptions) {
|
for (PollOptionData pollOptionData : pollOptions) {
|
||||||
// Check option length
|
// Check option length
|
||||||
int optionNameLength = Utf8.encodedLength(pollOptionData.getOptionName());
|
int optionNameLength = Utf8.encodedLength(pollOptionData.getOptionName());
|
||||||
@ -119,15 +86,10 @@ public class CreatePollTransaction extends Transaction {
|
|||||||
optionNames.add(pollOptionData.getOptionName());
|
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();
|
Account creator = getCreator();
|
||||||
|
|
||||||
// Check issuer has enough funds
|
// Check creator has enough funds
|
||||||
if (creator.getConfirmedBalance(Asset.QORT).compareTo(createPollTransactionData.getFee()) < 0)
|
if (creator.getConfirmedBalance(Asset.QORT) < this.createPollTransactionData.getFee())
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -136,7 +98,7 @@ public class CreatePollTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
public ValidationResult isProcessable() throws DataException {
|
||||||
// Check the poll name isn't already taken
|
// 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.POLL_ALREADY_EXISTS;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -145,14 +107,14 @@ public class CreatePollTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Publish poll to allow voting
|
// Publish poll to allow voting
|
||||||
Poll poll = new Poll(this.repository, createPollTransactionData);
|
Poll poll = new Poll(this.repository, this.createPollTransactionData);
|
||||||
poll.publish();
|
poll.publish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Unpublish poll
|
// Unpublish poll
|
||||||
Poll poll = new Poll(this.repository, createPollTransactionData.getPollName());
|
Poll poll = new Poll(this.repository, this.createPollTransactionData.getPollName());
|
||||||
poll.unpublish();
|
poll.unpublish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.ciyam.at.MachineState;
|
import org.ciyam.at.MachineState;
|
||||||
@ -44,35 +43,8 @@ public class DeployAtTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return new ArrayList<>();
|
return Collections.singletonList(this.deployATTransactionData.getAtAddress());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns AT version from the header bytes */
|
/** Returns AT version from the header bytes */
|
||||||
@ -124,30 +96,30 @@ public class DeployAtTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Check name size bounds
|
// Check name size bounds
|
||||||
int nameLength = Utf8.encodedLength(deployATTransactionData.getName());
|
int nameLength = Utf8.encodedLength(this.deployATTransactionData.getName());
|
||||||
if (nameLength < 1 || nameLength > MAX_NAME_SIZE)
|
if (nameLength < 1 || nameLength > MAX_NAME_SIZE)
|
||||||
return ValidationResult.INVALID_NAME_LENGTH;
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
// Check description size bounds
|
// Check description size bounds
|
||||||
int descriptionlength = Utf8.encodedLength(deployATTransactionData.getDescription());
|
int descriptionlength = Utf8.encodedLength(this.deployATTransactionData.getDescription());
|
||||||
if (descriptionlength < 1 || descriptionlength > MAX_DESCRIPTION_SIZE)
|
if (descriptionlength < 1 || descriptionlength > MAX_DESCRIPTION_SIZE)
|
||||||
return ValidationResult.INVALID_DESCRIPTION_LENGTH;
|
return ValidationResult.INVALID_DESCRIPTION_LENGTH;
|
||||||
|
|
||||||
// Check AT-type size bounds
|
// Check AT-type size bounds
|
||||||
int ATTypeLength = Utf8.encodedLength(deployATTransactionData.getAtType());
|
int atTypeLength = Utf8.encodedLength(this.deployATTransactionData.getAtType());
|
||||||
if (ATTypeLength < 1 || ATTypeLength > MAX_AT_TYPE_SIZE)
|
if (atTypeLength < 1 || atTypeLength > MAX_AT_TYPE_SIZE)
|
||||||
return ValidationResult.INVALID_AT_TYPE_LENGTH;
|
return ValidationResult.INVALID_AT_TYPE_LENGTH;
|
||||||
|
|
||||||
// Check tags size bounds
|
// Check tags size bounds
|
||||||
int tagsLength = Utf8.encodedLength(deployATTransactionData.getTags());
|
int tagsLength = Utf8.encodedLength(this.deployATTransactionData.getTags());
|
||||||
if (tagsLength < 1 || tagsLength > MAX_TAGS_SIZE)
|
if (tagsLength < 1 || tagsLength > MAX_TAGS_SIZE)
|
||||||
return ValidationResult.INVALID_TAGS_LENGTH;
|
return ValidationResult.INVALID_TAGS_LENGTH;
|
||||||
|
|
||||||
// Check amount is positive
|
// Check amount is positive
|
||||||
if (deployATTransactionData.getAmount().compareTo(BigDecimal.ZERO) <= 0)
|
if (this.deployATTransactionData.getAmount() <= 0)
|
||||||
return ValidationResult.NEGATIVE_AMOUNT;
|
return ValidationResult.NEGATIVE_AMOUNT;
|
||||||
|
|
||||||
long assetId = deployATTransactionData.getAssetId();
|
long assetId = this.deployATTransactionData.getAssetId();
|
||||||
AssetData assetData = this.repository.getAssetRepository().fromAssetId(assetId);
|
AssetData assetData = this.repository.getAssetRepository().fromAssetId(assetId);
|
||||||
// Check asset even exists
|
// Check asset even exists
|
||||||
if (assetData == null)
|
if (assetData == null)
|
||||||
@ -158,27 +130,23 @@ public class DeployAtTransaction extends Transaction {
|
|||||||
return ValidationResult.ASSET_NOT_SPENDABLE;
|
return ValidationResult.ASSET_NOT_SPENDABLE;
|
||||||
|
|
||||||
// Check asset amount is integer if asset is not divisible
|
// 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;
|
return ValidationResult.INVALID_AMOUNT;
|
||||||
|
|
||||||
// Check fee is positive
|
Account creator = this.getCreator();
|
||||||
if (deployATTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
Account creator = getCreator();
|
|
||||||
|
|
||||||
// Check creator has enough funds
|
// Check creator has enough funds
|
||||||
if (assetId == Asset.QORT) {
|
if (assetId == Asset.QORT) {
|
||||||
// Simple case: amount and fee both in 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;
|
return ValidationResult.NO_BALANCE;
|
||||||
} else {
|
} else {
|
||||||
if (creator.getConfirmedBalance(Asset.QORT).compareTo(deployATTransactionData.getFee()) < 0)
|
if (creator.getConfirmedBalance(Asset.QORT) < this.deployATTransactionData.getFee())
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
if (creator.getConfirmedBalance(assetId).compareTo(deployATTransactionData.getAmount()) < 0)
|
if (creator.getConfirmedBalance(assetId) < this.deployATTransactionData.getAmount())
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +154,7 @@ public class DeployAtTransaction extends Transaction {
|
|||||||
if (this.getVersion() >= 2) {
|
if (this.getVersion() >= 2) {
|
||||||
// Do actual validation
|
// Do actual validation
|
||||||
try {
|
try {
|
||||||
new MachineState(deployATTransactionData.getCreationBytes());
|
new MachineState(this.deployATTransactionData.getCreationBytes());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// Not valid
|
// Not valid
|
||||||
return ValidationResult.INVALID_CREATION_BYTES;
|
return ValidationResult.INVALID_CREATION_BYTES;
|
||||||
@ -201,25 +169,25 @@ public class DeployAtTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
public ValidationResult isProcessable() throws DataException {
|
||||||
Account creator = getCreator();
|
Account creator = getCreator();
|
||||||
long assetId = deployATTransactionData.getAssetId();
|
long assetId = this.deployATTransactionData.getAssetId();
|
||||||
|
|
||||||
// Check creator has enough funds
|
// Check creator has enough funds
|
||||||
if (assetId == Asset.QORT) {
|
if (assetId == Asset.QORT) {
|
||||||
// Simple case: amount and fee both in 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;
|
return ValidationResult.NO_BALANCE;
|
||||||
} else {
|
} else {
|
||||||
if (creator.getConfirmedBalance(Asset.QORT).compareTo(deployATTransactionData.getFee()) < 0)
|
if (creator.getConfirmedBalance(Asset.QORT) < this.deployATTransactionData.getFee())
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
if (creator.getConfirmedBalance(assetId).compareTo(deployATTransactionData.getAmount()) < 0)
|
if (creator.getConfirmedBalance(assetId) < this.deployATTransactionData.getAmount())
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check AT doesn't already exist
|
// 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.AT_ALREADY_EXISTS;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -227,24 +195,24 @@ public class DeployAtTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
ensureATAddress();
|
this.ensureATAddress();
|
||||||
|
|
||||||
// Deploy AT, saving into repository
|
// Deploy AT, saving into repository
|
||||||
AT at = new AT(this.repository, this.deployATTransactionData);
|
AT at = new AT(this.repository, this.deployATTransactionData);
|
||||||
at.deploy();
|
at.deploy();
|
||||||
|
|
||||||
long assetId = deployATTransactionData.getAssetId();
|
long assetId = this.deployATTransactionData.getAssetId();
|
||||||
|
|
||||||
// Update creator's balance regarding initial payment to AT
|
// Update creator's balance regarding initial payment to AT
|
||||||
Account creator = getCreator();
|
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
|
// Update AT's reference, which also creates AT account
|
||||||
Account atAccount = this.getATAccount();
|
Account atAccount = this.getATAccount();
|
||||||
atAccount.setLastReference(deployATTransactionData.getSignature());
|
atAccount.setLastReference(this.deployATTransactionData.getSignature());
|
||||||
|
|
||||||
// Update AT's balance
|
// Update AT's balance
|
||||||
atAccount.setConfirmedBalance(assetId, deployATTransactionData.getAmount());
|
atAccount.setConfirmedBalance(assetId, this.deployATTransactionData.getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -253,11 +221,11 @@ public class DeployAtTransaction extends Transaction {
|
|||||||
AT at = new AT(this.repository, this.deployATTransactionData);
|
AT at = new AT(this.repository, this.deployATTransactionData);
|
||||||
at.undeploy();
|
at.undeploy();
|
||||||
|
|
||||||
long assetId = deployATTransactionData.getAssetId();
|
long assetId = this.deployATTransactionData.getAssetId();
|
||||||
|
|
||||||
// Update creator's balance regarding initial payment to AT
|
// Update creator's balance regarding initial payment to AT
|
||||||
Account creator = getCreator();
|
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)
|
// Delete AT's account (and hence its balance)
|
||||||
this.repository.getAccountRepository().delete(this.deployATTransactionData.getAtAddress());
|
this.repository.getAccountRepository().delete(this.deployATTransactionData.getAtAddress());
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -36,40 +35,8 @@ public class GenesisTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, genesisTransactionData.getRecipient()));
|
return Collections.singletonList(this.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
@ -123,11 +90,11 @@ public class GenesisTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() {
|
public ValidationResult isValid() {
|
||||||
// Check amount is zero or positive
|
// Check amount is zero or positive
|
||||||
if (genesisTransactionData.getAmount().compareTo(BigDecimal.ZERO) < 0)
|
if (this.genesisTransactionData.getAmount() < 0)
|
||||||
return ValidationResult.NEGATIVE_AMOUNT;
|
return ValidationResult.NEGATIVE_AMOUNT;
|
||||||
|
|
||||||
// Check recipient address is valid
|
// Check recipient address is valid
|
||||||
if (!Crypto.isValidAddress(genesisTransactionData.getRecipient()))
|
if (!Crypto.isValidAddress(this.genesisTransactionData.getRecipient()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -135,26 +102,26 @@ public class GenesisTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
Account recipient = new Account(repository, genesisTransactionData.getRecipient());
|
Account recipient = new Account(repository, this.genesisTransactionData.getRecipient());
|
||||||
|
|
||||||
// Update recipient's balance
|
// Update recipient's balance
|
||||||
recipient.setConfirmedBalance(genesisTransactionData.getAssetId(), genesisTransactionData.getAmount());
|
recipient.setConfirmedBalance(this.genesisTransactionData.getAssetId(), this.genesisTransactionData.getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processReferencesAndFees() throws DataException {
|
public void processReferencesAndFees() throws DataException {
|
||||||
// Do not attempt to update non-existent genesis account's reference!
|
// 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)
|
// Set recipient's starting reference (also creates account)
|
||||||
recipient.setLastReference(genesisTransactionData.getSignature());
|
recipient.setLastReference(this.genesisTransactionData.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Delete recipient's account (and balance)
|
// Delete recipient's account (and balance)
|
||||||
this.repository.getAccountRepository().delete(genesisTransactionData.getRecipient());
|
this.repository.getAccountRepository().delete(this.genesisTransactionData.getRecipient());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.data.transaction.GroupApprovalTransactionData;
|
import org.qortal.data.transaction.GroupApprovalTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
@ -28,35 +26,14 @@ public class GroupApprovalTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
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
|
// Navigation
|
||||||
|
|
||||||
public Account getAdmin() throws DataException {
|
public Account getAdmin() {
|
||||||
return new PublicKeyAccount(this.repository, this.groupApprovalTransactionData.getAdminPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
@ -64,7 +41,7 @@ public class GroupApprovalTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Grab pending transaction's data
|
// 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)
|
if (pendingTransactionData == null)
|
||||||
return ValidationResult.TRANSACTION_UNKNOWN;
|
return ValidationResult.TRANSACTION_UNKNOWN;
|
||||||
|
|
||||||
@ -82,12 +59,8 @@ public class GroupApprovalTransaction extends Transaction {
|
|||||||
if (!this.repository.getGroupRepository().adminExists(pendingTransactionData.getTxGroupId(), admin.getAddress()))
|
if (!this.repository.getGroupRepository().adminExists(pendingTransactionData.getTxGroupId(), admin.getAddress()))
|
||||||
return ValidationResult.NOT_GROUP_ADMIN;
|
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
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -96,20 +69,20 @@ public class GroupApprovalTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Find previous approval decision (if any) by this admin for pending transaction
|
// 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)
|
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
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Save this transaction with removed prior reference
|
// Save this transaction with removed prior reference
|
||||||
groupApprovalTransactionData.setPriorReference(null);
|
this.groupApprovalTransactionData.setPriorReference(null);
|
||||||
this.repository.getTransactionRepository().save(groupApprovalTransactionData);
|
this.repository.getTransactionRepository().save(this.groupApprovalTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.group.GroupData;
|
import org.qortal.data.group.GroupData;
|
||||||
@ -18,7 +16,9 @@ import org.qortal.repository.Repository;
|
|||||||
public class GroupBanTransaction extends Transaction {
|
public class GroupBanTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private GroupBanTransactionData groupBanTransactionData;
|
private GroupBanTransactionData groupBanTransactionData;
|
||||||
|
private Account offenderAccount = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -31,53 +31,34 @@ public class GroupBanTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
return Collections.singletonList(this.groupBanTransactionData.getOffender());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getAdmin() throws DataException {
|
public Account getAdmin() {
|
||||||
return new PublicKeyAccount(this.repository, this.groupBanTransactionData.getAdminPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getOffender() throws DataException {
|
public Account getOffender() {
|
||||||
return new Account(this.repository, this.groupBanTransactionData.getOffender());
|
if (this.offenderAccount == null)
|
||||||
|
this.offenderAccount = new Account(this.repository, this.groupBanTransactionData.getOffender());
|
||||||
|
|
||||||
|
return this.offenderAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
int groupId = this.groupBanTransactionData.getGroupId();
|
||||||
|
|
||||||
// Check offender address is valid
|
// Check offender address is valid
|
||||||
if (!Crypto.isValidAddress(groupBanTransactionData.getOffender()))
|
if (!Crypto.isValidAddress(this.groupBanTransactionData.getOffender()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupBanTransactionData.getGroupId());
|
GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupId);
|
||||||
|
|
||||||
// Check group exists
|
// Check group exists
|
||||||
if (groupData == null)
|
if (groupData == null)
|
||||||
@ -86,22 +67,17 @@ public class GroupBanTransaction extends Transaction {
|
|||||||
Account admin = getAdmin();
|
Account admin = getAdmin();
|
||||||
|
|
||||||
// Can't ban if not an admin
|
// 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;
|
return ValidationResult.NOT_GROUP_ADMIN;
|
||||||
|
|
||||||
Account offender = getOffender();
|
Account offender = getOffender();
|
||||||
|
|
||||||
// Can't ban another admin unless the group owner
|
// Can't ban another admin unless the group owner
|
||||||
if (!admin.getAddress().equals(groupData.getOwner())
|
if (!admin.getAddress().equals(groupData.getOwner()) && this.repository.getGroupRepository().adminExists(groupId, offender.getAddress()))
|
||||||
&& this.repository.getGroupRepository().adminExists(groupBanTransactionData.getGroupId(), offender.getAddress()))
|
|
||||||
return ValidationResult.INVALID_GROUP_OWNER;
|
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
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -110,21 +86,21 @@ public class GroupBanTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Group Membership
|
// Update Group Membership
|
||||||
Group group = new Group(this.repository, groupBanTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.groupBanTransactionData.getGroupId());
|
||||||
group.ban(groupBanTransactionData);
|
group.ban(this.groupBanTransactionData);
|
||||||
|
|
||||||
// Save this transaction with updated member/admin references to transactions that can help restore state
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert group membership
|
// Revert group membership
|
||||||
Group group = new Group(this.repository, groupBanTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.groupBanTransactionData.getGroupId());
|
||||||
group.unban(groupBanTransactionData);
|
group.unban(this.groupBanTransactionData);
|
||||||
|
|
||||||
// Save this transaction with removed member/admin references
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.transaction.GroupInviteTransactionData;
|
import org.qortal.data.transaction.GroupInviteTransactionData;
|
||||||
@ -17,7 +15,9 @@ import org.qortal.repository.Repository;
|
|||||||
public class GroupInviteTransaction extends Transaction {
|
public class GroupInviteTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private GroupInviteTransactionData groupInviteTransactionData;
|
private GroupInviteTransactionData groupInviteTransactionData;
|
||||||
|
private Account inviteeAccount = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -30,56 +30,35 @@ public class GroupInviteTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
return Collections.singletonList(this.groupInviteTransactionData.getInvitee());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getAdmin() throws DataException {
|
public Account getAdmin() {
|
||||||
return new PublicKeyAccount(this.repository, this.groupInviteTransactionData.getAdminPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getInvitee() throws DataException {
|
public Account getInvitee() {
|
||||||
return new Account(this.repository, this.groupInviteTransactionData.getInvitee());
|
if (this.inviteeAccount == null)
|
||||||
|
this.inviteeAccount = new Account(this.repository, this.groupInviteTransactionData.getInvitee());
|
||||||
|
|
||||||
|
return this.inviteeAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
int groupId = groupInviteTransactionData.getGroupId();
|
int groupId = this.groupInviteTransactionData.getGroupId();
|
||||||
|
|
||||||
// Check time to live zero (infinite) or positive
|
// Check time to live zero (infinite) or positive
|
||||||
if (groupInviteTransactionData.getTimeToLive() < 0)
|
if (this.groupInviteTransactionData.getTimeToLive() < 0)
|
||||||
return ValidationResult.INVALID_LIFETIME;
|
return ValidationResult.INVALID_LIFETIME;
|
||||||
|
|
||||||
// Check member address is valid
|
// Check member address is valid
|
||||||
if (!Crypto.isValidAddress(groupInviteTransactionData.getInvitee()))
|
if (!Crypto.isValidAddress(this.groupInviteTransactionData.getInvitee()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
// Check group exists
|
// Check group exists
|
||||||
@ -102,12 +81,8 @@ public class GroupInviteTransaction extends Transaction {
|
|||||||
if (this.repository.getGroupRepository().banExists(groupId, invitee.getAddress()))
|
if (this.repository.getGroupRepository().banExists(groupId, invitee.getAddress()))
|
||||||
return ValidationResult.BANNED_FROM_GROUP;
|
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
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -116,21 +91,21 @@ public class GroupInviteTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Group Membership
|
// Update Group Membership
|
||||||
Group group = new Group(this.repository, groupInviteTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.groupInviteTransactionData.getGroupId());
|
||||||
group.invite(groupInviteTransactionData);
|
group.invite(this.groupInviteTransactionData);
|
||||||
|
|
||||||
// Save this transaction with updated member/admin references to transactions that can help restore state
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert group membership
|
// Revert group membership
|
||||||
Group group = new Group(this.repository, groupInviteTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.groupInviteTransactionData.getGroupId());
|
||||||
group.uninvite(groupInviteTransactionData);
|
group.uninvite(this.groupInviteTransactionData);
|
||||||
|
|
||||||
// Save this transaction with removed member/admin references
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -32,32 +31,8 @@ public class GroupKickTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
return Collections.singletonList(this.groupKickTransactionData.getMember());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
@ -74,12 +49,13 @@ public class GroupKickTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
int groupId = this.groupKickTransactionData.getGroupId();
|
||||||
|
|
||||||
// Check member address is valid
|
// Check member address is valid
|
||||||
if (!Crypto.isValidAddress(groupKickTransactionData.getMember()))
|
if (!Crypto.isValidAddress(this.groupKickTransactionData.getMember()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||||
int groupId = groupKickTransactionData.getGroupId();
|
|
||||||
GroupData groupData = groupRepository.fromGroupId(groupId);
|
GroupData groupData = groupRepository.fromGroupId(groupId);
|
||||||
|
|
||||||
// Check group exists
|
// Check group exists
|
||||||
@ -102,12 +78,8 @@ public class GroupKickTransaction extends Transaction {
|
|||||||
if (!admin.getAddress().equals(groupData.getOwner()) && groupRepository.adminExists(groupId, member.getAddress()))
|
if (!admin.getAddress().equals(groupData.getOwner()) && groupRepository.adminExists(groupId, member.getAddress()))
|
||||||
return ValidationResult.INVALID_GROUP_OWNER;
|
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
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -116,21 +88,21 @@ public class GroupKickTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Group Membership
|
// Update Group Membership
|
||||||
Group group = new Group(this.repository, groupKickTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.groupKickTransactionData.getGroupId());
|
||||||
group.kick(groupKickTransactionData);
|
group.kick(this.groupKickTransactionData);
|
||||||
|
|
||||||
// Save this transaction with updated member/admin references to transactions that can help restore state
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert group membership
|
// Revert group membership
|
||||||
Group group = new Group(this.repository, groupKickTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.groupKickTransactionData.getGroupId());
|
||||||
group.unkick(groupKickTransactionData);
|
group.unkick(this.groupKickTransactionData);
|
||||||
|
|
||||||
// Save this transaction with removed member/admin references
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.transaction.IssueAssetTransactionData;
|
import org.qortal.data.transaction.IssueAssetTransactionData;
|
||||||
@ -18,7 +16,9 @@ import com.google.common.base.Utf8;
|
|||||||
public class IssueAssetTransaction extends Transaction {
|
public class IssueAssetTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private IssueAssetTransactionData issueAssetTransactionData;
|
private IssueAssetTransactionData issueAssetTransactionData;
|
||||||
|
private Account ownerAccount = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -31,82 +31,59 @@ public class IssueAssetTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(getOwner());
|
return Collections.singletonList(this.issueAssetTransactionData.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getIssuer() throws DataException {
|
public Account getIssuer() {
|
||||||
return new PublicKeyAccount(this.repository, this.issueAssetTransactionData.getIssuerPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getOwner() throws DataException {
|
public Account getOwner() {
|
||||||
return new Account(this.repository, this.issueAssetTransactionData.getOwner());
|
if (this.ownerAccount == null)
|
||||||
|
this.ownerAccount = new Account(this.repository, this.issueAssetTransactionData.getOwner());
|
||||||
|
|
||||||
|
return this.ownerAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
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
|
// Check data field
|
||||||
String data = this.issueAssetTransactionData.getData();
|
String data = this.issueAssetTransactionData.getData();
|
||||||
int dataLength = Utf8.encodedLength(data);
|
int dataLength = Utf8.encodedLength(data);
|
||||||
if (data == null || dataLength < 1 || dataLength > Asset.MAX_DATA_SIZE)
|
if (data == null || dataLength < 1 || dataLength > Asset.MAX_DATA_SIZE)
|
||||||
return ValidationResult.INVALID_DATA_LENGTH;
|
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
|
// 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;
|
return ValidationResult.INVALID_QUANTITY;
|
||||||
|
|
||||||
// Check fee is positive
|
// Check quantity versus indivisibility
|
||||||
if (issueAssetTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
if (!this.issueAssetTransactionData.getIsDivisible() && this.issueAssetTransactionData.getQuantity() % Asset.MULTIPLIER != 0)
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
return ValidationResult.INVALID_QUANTITY;
|
||||||
|
|
||||||
Account issuer = getIssuer();
|
Account issuer = getIssuer();
|
||||||
|
|
||||||
// Check issuer has enough funds
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -115,7 +92,7 @@ public class IssueAssetTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
public ValidationResult isProcessable() throws DataException {
|
||||||
// Check the asset name isn't already taken.
|
// 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.ASSET_ALREADY_EXISTS;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -124,35 +101,35 @@ public class IssueAssetTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Issue asset
|
// Issue asset
|
||||||
Asset asset = new Asset(this.repository, issueAssetTransactionData);
|
Asset asset = new Asset(this.repository, this.issueAssetTransactionData);
|
||||||
asset.issue();
|
asset.issue();
|
||||||
|
|
||||||
// Add asset to owner
|
// Add asset to owner
|
||||||
Account owner = getOwner();
|
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
|
// 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
|
// Save this transaction with newly assigned assetId
|
||||||
this.repository.getTransactionRepository().save(issueAssetTransactionData);
|
this.repository.getTransactionRepository().save(this.issueAssetTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Remove asset from owner
|
// Remove asset from owner
|
||||||
Account owner = getOwner();
|
Account owner = getOwner();
|
||||||
owner.deleteBalance(issueAssetTransactionData.getAssetId());
|
owner.deleteBalance(this.issueAssetTransactionData.getAssetId());
|
||||||
|
|
||||||
// Deissue asset
|
// Deissue asset
|
||||||
Asset asset = new Asset(this.repository, issueAssetTransactionData.getAssetId());
|
Asset asset = new Asset(this.repository, this.issueAssetTransactionData.getAssetId());
|
||||||
asset.deissue();
|
asset.deissue();
|
||||||
|
|
||||||
// Remove assigned asset ID from transaction info
|
// Remove assigned asset ID from transaction info
|
||||||
issueAssetTransactionData.setAssetId(null);
|
this.issueAssetTransactionData.setAssetId(null);
|
||||||
|
|
||||||
// Save this transaction, with removed assetId
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.data.transaction.JoinGroupTransactionData;
|
import org.qortal.data.transaction.JoinGroupTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
@ -29,42 +27,21 @@ public class JoinGroupTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
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
|
// Navigation
|
||||||
|
|
||||||
public Account getJoiner() throws DataException {
|
public Account getJoiner() {
|
||||||
return new PublicKeyAccount(this.repository, this.joinGroupTransactionData.getJoinerPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
int groupId = joinGroupTransactionData.getGroupId();
|
int groupId = this.joinGroupTransactionData.getGroupId();
|
||||||
|
|
||||||
// Check group exists
|
// Check group exists
|
||||||
if (!this.repository.getGroupRepository().groupExists(groupId))
|
if (!this.repository.getGroupRepository().groupExists(groupId))
|
||||||
@ -83,11 +60,8 @@ public class JoinGroupTransaction extends Transaction {
|
|||||||
if (this.repository.getGroupRepository().joinRequestExists(groupId, joiner.getAddress()))
|
if (this.repository.getGroupRepository().joinRequestExists(groupId, joiner.getAddress()))
|
||||||
return ValidationResult.JOIN_REQUEST_EXISTS;
|
return ValidationResult.JOIN_REQUEST_EXISTS;
|
||||||
|
|
||||||
// Check fee is positive
|
// Check joiner has enough funds
|
||||||
if (joinGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
if (joiner.getConfirmedBalance(Asset.QORT) < this.joinGroupTransactionData.getFee())
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
// Check creator has enough funds
|
|
||||||
if (joiner.getConfirmedBalance(Asset.QORT).compareTo(joinGroupTransactionData.getFee()) < 0)
|
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -96,21 +70,21 @@ public class JoinGroupTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Group Membership
|
// Update Group Membership
|
||||||
Group group = new Group(this.repository, joinGroupTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.joinGroupTransactionData.getGroupId());
|
||||||
group.join(joinGroupTransactionData);
|
group.join(this.joinGroupTransactionData);
|
||||||
|
|
||||||
// Save this transaction with cached references to transactions that can help restore state
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert group membership
|
// Revert group membership
|
||||||
Group group = new Group(this.repository, joinGroupTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.joinGroupTransactionData.getGroupId());
|
||||||
group.unjoin(joinGroupTransactionData);
|
group.unjoin(this.joinGroupTransactionData);
|
||||||
|
|
||||||
// Save this transaction with removed references
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.data.group.GroupData;
|
import org.qortal.data.group.GroupData;
|
||||||
import org.qortal.data.transaction.LeaveGroupTransactionData;
|
import org.qortal.data.transaction.LeaveGroupTransactionData;
|
||||||
@ -30,42 +28,23 @@ public class LeaveGroupTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
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
|
// Navigation
|
||||||
|
|
||||||
public Account getLeaver() throws DataException {
|
public Account getLeaver() {
|
||||||
return new PublicKeyAccount(this.repository, this.leaveGroupTransactionData.getLeaverPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
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
|
// Check group exists
|
||||||
if (groupData == null)
|
if (groupData == null)
|
||||||
@ -78,15 +57,11 @@ public class LeaveGroupTransaction extends Transaction {
|
|||||||
return ValidationResult.GROUP_OWNER_CANNOT_LEAVE;
|
return ValidationResult.GROUP_OWNER_CANNOT_LEAVE;
|
||||||
|
|
||||||
// Check leaver is actually a member of group
|
// 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;
|
return ValidationResult.NOT_GROUP_MEMBER;
|
||||||
|
|
||||||
// Check fee is positive
|
// Check leaver has enough funds
|
||||||
if (leaveGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
if (leaver.getConfirmedBalance(Asset.QORT) < this.leaveGroupTransactionData.getFee())
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
// Check creator has enough funds
|
|
||||||
if (leaver.getConfirmedBalance(Asset.QORT).compareTo(leaveGroupTransactionData.getFee()) < 0)
|
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -95,21 +70,21 @@ public class LeaveGroupTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Group Membership
|
// Update Group Membership
|
||||||
Group group = new Group(this.repository, leaveGroupTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.leaveGroupTransactionData.getGroupId());
|
||||||
group.leave(leaveGroupTransactionData);
|
group.leave(this.leaveGroupTransactionData);
|
||||||
|
|
||||||
// Save this transaction with updated member/admin references to transactions that can help restore state
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert group membership
|
// Revert group membership
|
||||||
Group group = new Group(this.repository, leaveGroupTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.leaveGroupTransactionData.getGroupId());
|
||||||
group.unleave(leaveGroupTransactionData);
|
group.unleave(this.leaveGroupTransactionData);
|
||||||
|
|
||||||
// Save this transaction with removed member/admin references
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
|
||||||
import org.qortal.data.PaymentData;
|
import org.qortal.data.PaymentData;
|
||||||
import org.qortal.data.transaction.MessageTransactionData;
|
import org.qortal.data.transaction.MessageTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
@ -17,10 +14,13 @@ import org.qortal.repository.Repository;
|
|||||||
public class MessageTransaction extends Transaction {
|
public class MessageTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private MessageTransactionData messageTransactionData;
|
private MessageTransactionData messageTransactionData;
|
||||||
|
private PaymentData paymentData = null;
|
||||||
|
|
||||||
// Other useful constants
|
// Other useful constants
|
||||||
public static final int MAX_DATA_SIZE = 4000;
|
public static final int MAX_DATA_SIZE = 4000;
|
||||||
|
private static final boolean isZeroAmountValid = true;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -33,109 +33,71 @@ public class MessageTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, messageTransactionData.getRecipient()));
|
return Collections.singletonList(this.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getSender() throws DataException {
|
public Account getSender() {
|
||||||
return new PublicKeyAccount(this.repository, this.messageTransactionData.getSenderPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getRecipient() throws DataException {
|
public Account getRecipient() {
|
||||||
return new Account(this.repository, this.messageTransactionData.getRecipient());
|
return new Account(this.repository, this.messageTransactionData.getRecipient());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
private PaymentData getPaymentData() {
|
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
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Check data length
|
// 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;
|
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
|
// 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);
|
isZeroAmountValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
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
|
// 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);
|
isZeroAmountValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Wrap and delegate payment processing to Payment class.
|
// Wrap and delegate payment processing to Payment class.
|
||||||
new Payment(this.repository).process(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
|
new Payment(this.repository).process(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getSignature());
|
||||||
messageTransactionData.getSignature());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processReferencesAndFees() throws DataException {
|
public void processReferencesAndFees() throws DataException {
|
||||||
// Wrap and delegate references processing to Payment class. Only update recipient's last reference if transferring QORT.
|
// 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(),
|
new Payment(this.repository).processReferencesAndFees(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(),
|
||||||
messageTransactionData.getSignature(), false);
|
this.messageTransactionData.getSignature(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Wrap and delegate payment processing to Payment class.
|
// Wrap and delegate payment processing to Payment class.
|
||||||
new Payment(this.repository).orphan(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
|
new Payment(this.repository).orphan(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getSignature(), this.messageTransactionData.getReference());
|
||||||
messageTransactionData.getSignature(), messageTransactionData.getReference());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphanReferencesAndFees() throws DataException {
|
public void orphanReferencesAndFees() throws DataException {
|
||||||
// Wrap and delegate references processing to Payment class. Only revert recipient's last reference if transferring QORT.
|
// 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(),
|
new Payment(this.repository).orphanReferencesAndFees(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(),
|
||||||
messageTransactionData.getSignature(), messageTransactionData.getReference(), false);
|
this.messageTransactionData.getSignature(), this.messageTransactionData.getReference(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.data.PaymentData;
|
import org.qortal.data.PaymentData;
|
||||||
import org.qortal.data.transaction.MultiPaymentTransactionData;
|
import org.qortal.data.transaction.MultiPaymentTransactionData;
|
||||||
@ -33,109 +31,67 @@ public class MultiPaymentTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
List<Account> recipients = new ArrayList<>();
|
return this.multiPaymentTransactionData.getPayments().stream().map(PaymentData::getRecipient).collect(Collectors.toList());
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getSender() throws DataException {
|
public Account getSender() {
|
||||||
return new PublicKeyAccount(this.repository, this.multiPaymentTransactionData.getSenderPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
List<PaymentData> payments = multiPaymentTransactionData.getPayments();
|
List<PaymentData> payments = this.multiPaymentTransactionData.getPayments();
|
||||||
|
|
||||||
// Check number of payments
|
// Check number of payments
|
||||||
if (payments.isEmpty() || payments.size() > MAX_PAYMENTS_COUNT)
|
if (payments.isEmpty() || payments.size() > MAX_PAYMENTS_COUNT)
|
||||||
return ValidationResult.INVALID_PAYMENTS_COUNT;
|
return ValidationResult.INVALID_PAYMENTS_COUNT;
|
||||||
|
|
||||||
// Check reference is correct
|
|
||||||
Account sender = getSender();
|
Account sender = getSender();
|
||||||
|
|
||||||
// Check sender has enough funds for fee
|
// 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 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
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
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
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Wrap and delegate payment processing to Payment class.
|
// Wrap and delegate payment processing to Payment class.
|
||||||
new Payment(this.repository).process(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(),
|
new Payment(this.repository).process(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments(), this.multiPaymentTransactionData.getSignature());
|
||||||
multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processReferencesAndFees() throws DataException {
|
public void processReferencesAndFees() throws DataException {
|
||||||
// Wrap and delegate reference processing to Payment class. Always update recipients' last references regardless of asset.
|
// 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(),
|
new Payment(this.repository).processReferencesAndFees(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments(),
|
||||||
multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), true);
|
this.multiPaymentTransactionData.getFee(), this.multiPaymentTransactionData.getSignature(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Wrap and delegate payment processing to Payment class. Always revert recipients' last references regardless of asset.
|
// 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(),
|
new Payment(this.repository).orphan(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments(),
|
||||||
multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), multiPaymentTransactionData.getReference());
|
this.multiPaymentTransactionData.getSignature(), this.multiPaymentTransactionData.getReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphanReferencesAndFees() throws DataException {
|
public void orphanReferencesAndFees() throws DataException {
|
||||||
// Wrap and delegate reference processing to Payment class. Always revert recipients' last references regardless of asset.
|
// 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(),
|
new Payment(this.repository).orphanReferencesAndFees(this.multiPaymentTransactionData.getSenderPublicKey(), this.multiPaymentTransactionData.getPayments(),
|
||||||
multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), multiPaymentTransactionData.getReference(), true);
|
this.multiPaymentTransactionData.getFee(), this.multiPaymentTransactionData.getSignature(), this.multiPaymentTransactionData.getReference(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.data.PaymentData;
|
import org.qortal.data.PaymentData;
|
||||||
import org.qortal.data.transaction.PaymentTransactionData;
|
import org.qortal.data.transaction.PaymentTransactionData;
|
||||||
@ -17,7 +15,9 @@ import org.qortal.repository.Repository;
|
|||||||
public class PaymentTransaction extends Transaction {
|
public class PaymentTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private PaymentTransactionData paymentTransactionData;
|
private PaymentTransactionData paymentTransactionData;
|
||||||
|
private PaymentData paymentData = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -30,88 +30,62 @@ public class PaymentTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, paymentTransactionData.getRecipient()));
|
return Collections.singletonList(this.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getSender() throws DataException {
|
public Account getSender() {
|
||||||
return new PublicKeyAccount(this.repository, this.paymentTransactionData.getSenderPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
private PaymentData getPaymentData() {
|
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
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Wrap and delegate final payment checks to Payment class
|
// 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
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
public ValidationResult isProcessable() throws DataException {
|
||||||
// Wrap and delegate final processable checks to Payment class
|
// 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
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Wrap and delegate payment processing to Payment class.
|
// Wrap and delegate payment processing to Payment class.
|
||||||
new Payment(this.repository).process(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(),
|
new Payment(this.repository).process(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), this.paymentTransactionData.getSignature());
|
||||||
paymentTransactionData.getSignature());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processReferencesAndFees() throws DataException {
|
public void processReferencesAndFees() throws DataException {
|
||||||
// Wrap and delegate references processing to Payment class. Only update recipient's last reference if transferring QORT.
|
// 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(),
|
new Payment(this.repository).processReferencesAndFees(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), this.paymentTransactionData.getFee(),
|
||||||
paymentTransactionData.getSignature(), false);
|
this.paymentTransactionData.getSignature(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Wrap and delegate payment processing to Payment class. Only revert recipient's last reference if transferring QORT.
|
// 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(),
|
new Payment(this.repository).orphan(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(),
|
||||||
paymentTransactionData.getSignature(), paymentTransactionData.getReference());
|
this.paymentTransactionData.getSignature(), this.paymentTransactionData.getReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphanReferencesAndFees() throws DataException {
|
public void orphanReferencesAndFees() throws DataException {
|
||||||
// Wrap and delegate payment processing to Payment class. Only revert recipient's last reference if transferring QORT.
|
// 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(),
|
new Payment(this.repository).orphanReferencesAndFees(this.paymentTransactionData.getSenderPublicKey(), getPaymentData(), this.paymentTransactionData.getFee(),
|
||||||
paymentTransactionData.getSignature(), paymentTransactionData.getReference(), false);
|
this.paymentTransactionData.getSignature(), this.paymentTransactionData.getReference(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.block.BlockChain;
|
import org.qortal.block.BlockChain;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
@ -33,42 +31,14 @@ public class RegisterNameTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(getOwner());
|
return Collections.singletonList(this.registerNameTransactionData.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getRegistrant() throws DataException {
|
public Account getRegistrant() {
|
||||||
return new PublicKeyAccount(this.repository, this.registerNameTransactionData.getRegistrantPublicKey());
|
return this.getCreator();
|
||||||
}
|
|
||||||
|
|
||||||
public Account getOwner() throws DataException {
|
|
||||||
return new Account(this.repository, this.registerNameTransactionData.getOwner());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
@ -78,29 +48,25 @@ public class RegisterNameTransaction extends Transaction {
|
|||||||
Account registrant = getRegistrant();
|
Account registrant = getRegistrant();
|
||||||
|
|
||||||
// Check owner address is valid
|
// Check owner address is valid
|
||||||
if (!Crypto.isValidAddress(registerNameTransactionData.getOwner()))
|
if (!Crypto.isValidAddress(this.registerNameTransactionData.getOwner()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
// Check name size bounds
|
// 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)
|
if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE)
|
||||||
return ValidationResult.INVALID_NAME_LENGTH;
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
// Check data size bounds
|
// 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)
|
if (dataLength < 1 || dataLength > Name.MAX_DATA_SIZE)
|
||||||
return ValidationResult.INVALID_DATA_LENGTH;
|
return ValidationResult.INVALID_DATA_LENGTH;
|
||||||
|
|
||||||
// Check name is lowercase
|
// 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;
|
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||||
|
|
||||||
// Check fee is positive
|
// Check registrant has enough funds
|
||||||
if (registerNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
if (registrant.getConfirmedBalance(Asset.QORT) < this.registerNameTransactionData.getFee())
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
// Check issuer has enough funds
|
|
||||||
if (registrant.getConfirmedBalance(Asset.QORT).compareTo(registerNameTransactionData.getFee()) < 0)
|
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -109,7 +75,7 @@ public class RegisterNameTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
public ValidationResult isProcessable() throws DataException {
|
||||||
// Check the name isn't already taken
|
// 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;
|
return ValidationResult.NAME_ALREADY_REGISTERED;
|
||||||
|
|
||||||
Account registrant = getRegistrant();
|
Account registrant = getRegistrant();
|
||||||
@ -124,14 +90,14 @@ public class RegisterNameTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Register Name
|
// Register Name
|
||||||
Name name = new Name(this.repository, registerNameTransactionData);
|
Name name = new Name(this.repository, this.registerNameTransactionData);
|
||||||
name.register();
|
name.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Unregister name
|
// Unregister name
|
||||||
Name name = new Name(this.repository, registerNameTransactionData.getName());
|
Name name = new Name(this.repository, this.registerNameTransactionData.getName());
|
||||||
name.unregister();
|
name.unregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.group.GroupData;
|
import org.qortal.data.group.GroupData;
|
||||||
@ -18,7 +16,9 @@ import org.qortal.repository.Repository;
|
|||||||
public class RemoveGroupAdminTransaction extends Transaction {
|
public class RemoveGroupAdminTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private RemoveGroupAdminTransactionData removeGroupAdminTransactionData;
|
private RemoveGroupAdminTransactionData removeGroupAdminTransactionData;
|
||||||
|
private Account adminAccount = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -31,53 +31,34 @@ public class RemoveGroupAdminTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
return Collections.singletonList(this.removeGroupAdminTransactionData.getAdmin());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getOwner() throws DataException {
|
public Account getOwner() {
|
||||||
return new PublicKeyAccount(this.repository, this.removeGroupAdminTransactionData.getOwnerPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getAdmin() throws DataException {
|
public Account getAdmin() {
|
||||||
return new Account(this.repository, this.removeGroupAdminTransactionData.getAdmin());
|
if (this.adminAccount == null)
|
||||||
|
this.adminAccount = new Account(this.repository, this.removeGroupAdminTransactionData.getAdmin());
|
||||||
|
|
||||||
|
return this.adminAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
int groupId = this.removeGroupAdminTransactionData.getGroupId();
|
||||||
|
|
||||||
// Check admin address is valid
|
// Check admin address is valid
|
||||||
if (!Crypto.isValidAddress(removeGroupAdminTransactionData.getAdmin()))
|
if (!Crypto.isValidAddress(this.removeGroupAdminTransactionData.getAdmin()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
GroupData groupData = this.repository.getGroupRepository().fromGroupId(removeGroupAdminTransactionData.getGroupId());
|
GroupData groupData = this.repository.getGroupRepository().fromGroupId(groupId);
|
||||||
|
|
||||||
// Check group exists
|
// Check group exists
|
||||||
if (groupData == null)
|
if (groupData == null)
|
||||||
@ -92,15 +73,11 @@ public class RemoveGroupAdminTransaction extends Transaction {
|
|||||||
Account admin = getAdmin();
|
Account admin = getAdmin();
|
||||||
|
|
||||||
// Check member is an admin
|
// 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;
|
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
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -109,21 +86,21 @@ public class RemoveGroupAdminTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Group adminship
|
// Update Group adminship
|
||||||
Group group = new Group(this.repository, removeGroupAdminTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.removeGroupAdminTransactionData.getGroupId());
|
||||||
group.demoteFromAdmin(removeGroupAdminTransactionData);
|
group.demoteFromAdmin(this.removeGroupAdminTransactionData);
|
||||||
|
|
||||||
// Save this transaction with cached references to transactions that can help restore state
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert group adminship
|
// Revert group adminship
|
||||||
Group group = new Group(this.repository, removeGroupAdminTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.removeGroupAdminTransactionData.getGroupId());
|
||||||
group.undemoteFromAdmin(removeGroupAdminTransactionData);
|
group.undemoteFromAdmin(this.removeGroupAdminTransactionData);
|
||||||
|
|
||||||
// Save this transaction with removed group references
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -19,8 +18,13 @@ import org.qortal.transform.Transformer;
|
|||||||
|
|
||||||
public class RewardShareTransaction extends Transaction {
|
public class RewardShareTransaction extends Transaction {
|
||||||
|
|
||||||
|
public static final int MAX_SHARE = 100 * 100; // unscaled
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private RewardShareTransactionData rewardShareTransactionData;
|
private RewardShareTransactionData rewardShareTransactionData;
|
||||||
|
private boolean haveCheckedForExistingRewardShare = false;
|
||||||
|
private RewardShareData existingRewardShareData = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -33,42 +37,23 @@ public class RewardShareTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
return Collections.singletonList(this.rewardShareTransactionData.getRecipient());
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RewardShareData getExistingRewardShare() throws DataException {
|
private RewardShareData getExistingRewardShare() throws DataException {
|
||||||
// Look up any existing reward-share (using transaction's reward-share public key)
|
if (this.haveCheckedForExistingRewardShare == false) {
|
||||||
RewardShareData existingRewardShareData = this.repository.getAccountRepository().getRewardShare(this.rewardShareTransactionData.getRewardSharePublicKey());
|
this.haveCheckedForExistingRewardShare = true;
|
||||||
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());
|
|
||||||
|
|
||||||
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) {
|
private boolean doesRewardShareMatch(RewardShareData rewardShareData) {
|
||||||
@ -80,7 +65,7 @@ public class RewardShareTransaction extends Transaction {
|
|||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public PublicKeyAccount getMintingAccount() {
|
public PublicKeyAccount getMintingAccount() {
|
||||||
return new PublicKeyAccount(this.repository, this.rewardShareTransactionData.getMinterPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getRecipient() {
|
public Account getRecipient() {
|
||||||
@ -89,12 +74,11 @@ public class RewardShareTransaction extends Transaction {
|
|||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
private static final BigDecimal MAX_SHARE = BigDecimal.valueOf(100).setScale(2);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isFeeValid() throws DataException {
|
public ValidationResult isFeeValid() throws DataException {
|
||||||
// Look up any existing reward-share (using transaction's reward-share public key)
|
// Look up any existing reward-share (using transaction's reward-share public key)
|
||||||
RewardShareData existingRewardShareData = this.getExistingRewardShare();
|
RewardShareData existingRewardShareData = this.getExistingRewardShare();
|
||||||
|
|
||||||
// If we have an existing reward-share then minter/recipient/reward-share-public-key should all match.
|
// 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,
|
// 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.
|
// 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;
|
return ValidationResult.INVALID_PUBLIC_KEY;
|
||||||
|
|
||||||
final boolean isRecipientAlsoMinter = getCreator().getAddress().equals(this.rewardShareTransactionData.getRecipient());
|
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
|
// 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 ValidationResult.OK;
|
||||||
|
|
||||||
return super.isFeeValid();
|
return super.isFeeValid();
|
||||||
@ -114,7 +98,7 @@ public class RewardShareTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Check reward share given to recipient. Negative is potentially OK to end a current reward-share. Zero also fine.
|
// 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;
|
return ValidationResult.INVALID_REWARD_SHARE_PERCENT;
|
||||||
|
|
||||||
// Check reward-share public key is correct length
|
// Check reward-share public key is correct length
|
||||||
@ -127,7 +111,7 @@ public class RewardShareTransaction extends Transaction {
|
|||||||
|
|
||||||
PublicKeyAccount creator = getCreator();
|
PublicKeyAccount creator = getCreator();
|
||||||
Account recipient = getRecipient();
|
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)
|
// Creator themselves needs to be allowed to mint (unless cancelling)
|
||||||
if (!isCancellingSharePercent && !creator.canMint())
|
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)
|
// Look up any existing reward-share (using transaction's reward-share public key)
|
||||||
RewardShareData existingRewardShareData = this.getExistingRewardShare();
|
RewardShareData existingRewardShareData = this.getExistingRewardShare();
|
||||||
|
|
||||||
// If we have an existing reward-share then minter/recipient/reward-share-public-key should all match.
|
// 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,
|
// 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.
|
// 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
|
// Fee checking needed if not setting up new self-share
|
||||||
if (!(isRecipientAlsoMinter && existingRewardShareData == null))
|
if (!(isRecipientAlsoMinter && existingRewardShareData == null))
|
||||||
// Check creator has enough funds
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -180,24 +165,24 @@ public class RewardShareTransaction extends Transaction {
|
|||||||
|
|
||||||
// Grab any previous share info for orphaning purposes
|
// Grab any previous share info for orphaning purposes
|
||||||
RewardShareData rewardShareData = this.repository.getAccountRepository().getRewardShare(mintingAccount.getPublicKey(),
|
RewardShareData rewardShareData = this.repository.getAccountRepository().getRewardShare(mintingAccount.getPublicKey(),
|
||||||
rewardShareTransactionData.getRecipient());
|
this.rewardShareTransactionData.getRecipient());
|
||||||
|
|
||||||
if (rewardShareData != null)
|
if (rewardShareData != null)
|
||||||
rewardShareTransactionData.setPreviousSharePercent(rewardShareData.getSharePercent());
|
this.rewardShareTransactionData.setPreviousSharePercent(rewardShareData.getSharePercent());
|
||||||
|
|
||||||
// Save this transaction, with previous share info
|
// 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
|
// Negative share is actually a request to delete existing reward-share
|
||||||
if (isSharePercentNegative) {
|
if (isSharePercentNegative) {
|
||||||
this.repository.getAccountRepository().delete(mintingAccount.getPublicKey(), rewardShareTransactionData.getRecipient());
|
this.repository.getAccountRepository().delete(mintingAccount.getPublicKey(), this.rewardShareTransactionData.getRecipient());
|
||||||
} else {
|
} else {
|
||||||
// Save reward-share info
|
// Save reward-share info
|
||||||
rewardShareData = new RewardShareData(mintingAccount.getPublicKey(), mintingAccount.getAddress(),
|
rewardShareData = new RewardShareData(mintingAccount.getPublicKey(), mintingAccount.getAddress(),
|
||||||
rewardShareTransactionData.getRecipient(), rewardShareTransactionData.getRewardSharePublicKey(),
|
this.rewardShareTransactionData.getRecipient(), this.rewardShareTransactionData.getRewardSharePublicKey(),
|
||||||
rewardShareTransactionData.getSharePercent());
|
this.rewardShareTransactionData.getSharePercent());
|
||||||
this.repository.getAccountRepository().save(rewardShareData);
|
this.repository.getAccountRepository().save(rewardShareData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,9 +192,9 @@ public class RewardShareTransaction extends Transaction {
|
|||||||
super.processReferencesAndFees();
|
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
|
// 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)
|
if (recipient.getLastReference() == null)
|
||||||
recipient.setLastReference(rewardShareTransactionData.getSignature());
|
recipient.setLastReference(this.rewardShareTransactionData.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -217,21 +202,21 @@ public class RewardShareTransaction extends Transaction {
|
|||||||
// Revert
|
// Revert
|
||||||
PublicKeyAccount mintingAccount = getMintingAccount();
|
PublicKeyAccount mintingAccount = getMintingAccount();
|
||||||
|
|
||||||
if (rewardShareTransactionData.getPreviousSharePercent() != null) {
|
if (this.rewardShareTransactionData.getPreviousSharePercent() != null) {
|
||||||
// Revert previous sharing arrangement
|
// Revert previous sharing arrangement
|
||||||
RewardShareData rewardShareData = new RewardShareData(mintingAccount.getPublicKey(), mintingAccount.getAddress(),
|
RewardShareData rewardShareData = new RewardShareData(mintingAccount.getPublicKey(), mintingAccount.getAddress(),
|
||||||
rewardShareTransactionData.getRecipient(), rewardShareTransactionData.getRewardSharePublicKey(),
|
this.rewardShareTransactionData.getRecipient(), this.rewardShareTransactionData.getRewardSharePublicKey(),
|
||||||
rewardShareTransactionData.getPreviousSharePercent());
|
this.rewardShareTransactionData.getPreviousSharePercent());
|
||||||
|
|
||||||
this.repository.getAccountRepository().save(rewardShareData);
|
this.repository.getAccountRepository().save(rewardShareData);
|
||||||
} else {
|
} else {
|
||||||
// No previous arrangement so simply delete
|
// 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
|
// Save this transaction, with removed previous share info
|
||||||
rewardShareTransactionData.setPreviousSharePercent(null);
|
this.rewardShareTransactionData.setPreviousSharePercent(null);
|
||||||
this.repository.getTransactionRepository().save(rewardShareTransactionData);
|
this.repository.getTransactionRepository().save(this.rewardShareTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -239,8 +224,8 @@ public class RewardShareTransaction extends Transaction {
|
|||||||
super.orphanReferencesAndFees();
|
super.orphanReferencesAndFees();
|
||||||
|
|
||||||
// If recipient didn't have a last-reference prior to this transaction then remove it
|
// If recipient didn't have a last-reference prior to this transaction then remove it
|
||||||
Account recipient = new Account(this.repository, rewardShareTransactionData.getRecipient());
|
Account recipient = new Account(this.repository, this.rewardShareTransactionData.getRecipient());
|
||||||
if (Arrays.equals(recipient.getLastReference(), rewardShareTransactionData.getSignature()))
|
if (Arrays.equals(recipient.getLastReference(), this.rewardShareTransactionData.getSignature()))
|
||||||
recipient.setLastReference(null);
|
recipient.setLastReference(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.util.Collections;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.data.naming.NameData;
|
import org.qortal.data.naming.NameData;
|
||||||
import org.qortal.data.transaction.SellNameTransactionData;
|
import org.qortal.data.transaction.SellNameTransactionData;
|
||||||
@ -19,7 +17,7 @@ import com.google.common.base.Utf8;
|
|||||||
public class SellNameTransaction extends Transaction {
|
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. */
|
/** 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
|
// Properties
|
||||||
private SellNameTransactionData sellNameTransactionData;
|
private SellNameTransactionData sellNameTransactionData;
|
||||||
@ -35,51 +33,32 @@ public class SellNameTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return new ArrayList<>();
|
return Collections.emptyList();
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getOwner() throws DataException {
|
public Account getOwner() {
|
||||||
return new PublicKeyAccount(this.repository, this.sellNameTransactionData.getOwnerPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
String name = this.sellNameTransactionData.getName();
|
||||||
|
|
||||||
// Check name size bounds
|
// Check name size bounds
|
||||||
int nameLength = Utf8.encodedLength(sellNameTransactionData.getName());
|
int nameLength = Utf8.encodedLength(name);
|
||||||
if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE)
|
if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE)
|
||||||
return ValidationResult.INVALID_NAME_LENGTH;
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
// Check name is lowercase
|
// Check name is lowercase
|
||||||
if (!sellNameTransactionData.getName().equals(sellNameTransactionData.getName().toLowerCase()))
|
if (!name.equals(name.toLowerCase()))
|
||||||
return ValidationResult.NAME_NOT_LOWER_CASE;
|
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||||
|
|
||||||
NameData nameData = this.repository.getNameRepository().fromName(sellNameTransactionData.getName());
|
NameData nameData = this.repository.getNameRepository().fromName(name);
|
||||||
|
|
||||||
// Check name exists
|
// Check name exists
|
||||||
if (nameData == null)
|
if (nameData == null)
|
||||||
@ -95,19 +74,15 @@ public class SellNameTransaction extends Transaction {
|
|||||||
return ValidationResult.INVALID_NAME_OWNER;
|
return ValidationResult.INVALID_NAME_OWNER;
|
||||||
|
|
||||||
// Check amount is positive
|
// Check amount is positive
|
||||||
if (sellNameTransactionData.getAmount().compareTo(BigDecimal.ZERO) <= 0)
|
if (this.sellNameTransactionData.getAmount() <= 0)
|
||||||
return ValidationResult.NEGATIVE_AMOUNT;
|
return ValidationResult.NEGATIVE_AMOUNT;
|
||||||
|
|
||||||
// Check amount within bounds
|
// Check amount within bounds
|
||||||
if (sellNameTransactionData.getAmount().compareTo(MAX_AMOUNT) >= 0)
|
if (this.sellNameTransactionData.getAmount() >= MAX_AMOUNT)
|
||||||
return ValidationResult.INVALID_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
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -116,15 +91,15 @@ public class SellNameTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Sell Name
|
// Sell Name
|
||||||
Name name = new Name(this.repository, sellNameTransactionData.getName());
|
Name name = new Name(this.repository, this.sellNameTransactionData.getName());
|
||||||
name.sell(sellNameTransactionData);
|
name.sell(this.sellNameTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert name
|
// Revert name
|
||||||
Name name = new Name(this.repository, sellNameTransactionData.getName());
|
Name name = new Name(this.repository, this.sellNameTransactionData.getName());
|
||||||
name.unsell(sellNameTransactionData);
|
name.unsell(this.sellNameTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -28,53 +27,30 @@ public class SetGroupTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.emptyList();
|
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
|
// Navigation
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
int defaultGroupId = this.setGroupTransactionData.getDefaultGroupId();
|
||||||
|
|
||||||
// Check group exists
|
// Check group exists
|
||||||
if (!this.repository.getGroupRepository().groupExists(setGroupTransactionData.getDefaultGroupId()))
|
if (!this.repository.getGroupRepository().groupExists(defaultGroupId))
|
||||||
return ValidationResult.GROUP_DOES_NOT_EXIST;
|
return ValidationResult.GROUP_DOES_NOT_EXIST;
|
||||||
|
|
||||||
Account creator = getCreator();
|
Account creator = getCreator();
|
||||||
|
|
||||||
// Must be member of group
|
// 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;
|
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
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -88,13 +64,13 @@ public class SetGroupTransaction extends Transaction {
|
|||||||
if (previousDefaultGroupId == null)
|
if (previousDefaultGroupId == null)
|
||||||
previousDefaultGroupId = Group.NO_GROUP;
|
previousDefaultGroupId = Group.NO_GROUP;
|
||||||
|
|
||||||
setGroupTransactionData.setPreviousDefaultGroupId(previousDefaultGroupId);
|
this.setGroupTransactionData.setPreviousDefaultGroupId(previousDefaultGroupId);
|
||||||
|
|
||||||
// Save this transaction with account's previous defaultGroupId value
|
// 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
|
// Set account's new default groupID
|
||||||
creator.setDefaultGroupId(setGroupTransactionData.getDefaultGroupId());
|
creator.setDefaultGroupId(this.setGroupTransactionData.getDefaultGroupId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -102,15 +78,15 @@ public class SetGroupTransaction extends Transaction {
|
|||||||
// Revert
|
// Revert
|
||||||
Account creator = getCreator();
|
Account creator = getCreator();
|
||||||
|
|
||||||
Integer previousDefaultGroupId = setGroupTransactionData.getPreviousDefaultGroupId();
|
Integer previousDefaultGroupId = this.setGroupTransactionData.getPreviousDefaultGroupId();
|
||||||
if (previousDefaultGroupId == null)
|
if (previousDefaultGroupId == null)
|
||||||
previousDefaultGroupId = Group.NO_GROUP;
|
previousDefaultGroupId = Group.NO_GROUP;
|
||||||
|
|
||||||
creator.setDefaultGroupId(previousDefaultGroupId);
|
creator.setDefaultGroupId(previousDefaultGroupId);
|
||||||
|
|
||||||
// Save this transaction with removed previous defaultGroupId value
|
// Save this transaction with removed previous defaultGroupId value
|
||||||
setGroupTransactionData.setPreviousDefaultGroupId(null);
|
this.setGroupTransactionData.setPreviousDefaultGroupId(null);
|
||||||
this.repository.getTransactionRepository().save(setGroupTransactionData);
|
this.repository.getTransactionRepository().save(this.setGroupTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.math.MathContext;
|
|
||||||
import java.math.RoundingMode;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@ -261,8 +258,11 @@ public abstract class Transaction {
|
|||||||
private static final Logger LOGGER = LogManager.getLogger(Transaction.class);
|
private static final Logger LOGGER = LogManager.getLogger(Transaction.class);
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
protected Repository repository;
|
protected Repository repository;
|
||||||
protected TransactionData transactionData;
|
protected TransactionData transactionData;
|
||||||
|
/** Cached creator account. Use <tt>getCreator()</tt> to access. */
|
||||||
|
private PublicKeyAccount creator = null;
|
||||||
|
|
||||||
// Constructors
|
// 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. */
|
/** Returns whether transaction's fee is at least minimum unit fee as specified in blockchain config. */
|
||||||
public boolean hasMinimumFee() {
|
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 {
|
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) {
|
} catch (TransformationException e) {
|
||||||
throw new IllegalStateException("Unable to get transaction byte length?");
|
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. */
|
/** Returns whether transaction's fee is at least amount needed to cover byte-length of transaction. */
|
||||||
public boolean hasMinimumFeePerByte() {
|
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;
|
int dataLength;
|
||||||
try {
|
try {
|
||||||
dataLength = TransactionTransformer.getDataLength(this.transactionData);
|
dataLength = TransactionTransformer.getDataLength(this.transactionData);
|
||||||
@ -344,12 +347,11 @@ public abstract class Transaction {
|
|||||||
throw new IllegalStateException("Unable to get transaction byte length?");
|
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 BlockChain.getInstance().getUnscaledUnitFee() * unitFeeCount;
|
||||||
return recommendedFee;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -399,45 +401,32 @@ public abstract class Transaction {
|
|||||||
* @return list of recipients accounts, or empty list if none
|
* @return list of recipients accounts, or empty list if none
|
||||||
* @throws DataException
|
* @throws DataException
|
||||||
*/
|
*/
|
||||||
public abstract List<Account> getRecipientAccounts() throws DataException;
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
|
throw new DataException("Placeholder for new AT code");
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether passed account is an involved party in this transaction.
|
* Returns a list of recipient addresses for this transaction.
|
||||||
* <p>
|
|
||||||
* Account could be sender, or any one of the potential recipients.
|
|
||||||
*
|
*
|
||||||
* @param account
|
* @return list of recipients addresses, or empty list if none
|
||||||
* @return true if account is involved, false otherwise
|
|
||||||
* @throws DataException
|
* @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>
|
* <p>
|
||||||
* Amounts "lost", e.g. sent by sender and fees, are returned as negative values.<br>
|
* "Involved" means sender or recipient.
|
||||||
* Amounts "gained", e.g. QORT sent to recipient, are returned as positive values.
|
|
||||||
*
|
*
|
||||||
* @param account
|
* @return list of involved addresses, or empty list if none
|
||||||
* @return Amount of QORT lost/gained by account, or BigDecimal.ZERO otherwise
|
|
||||||
* @throws DataException
|
* @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
|
// Navigation
|
||||||
|
|
||||||
@ -447,11 +436,11 @@ public abstract class Transaction {
|
|||||||
* @return creator
|
* @return creator
|
||||||
* @throws DataException
|
* @throws DataException
|
||||||
*/
|
*/
|
||||||
protected PublicKeyAccount getCreator() throws DataException {
|
protected PublicKeyAccount getCreator() {
|
||||||
if (this.transactionData.getCreatorPublicKey() == null)
|
if (this.creator == null)
|
||||||
return 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)
|
* @return Parent's TransactionData, or null if no parent found (which should not happen)
|
||||||
* @throws DataException
|
* @throws DataException
|
||||||
*/
|
*/
|
||||||
public TransactionData getParent() throws DataException {
|
protected TransactionData getParent() throws DataException {
|
||||||
byte[] reference = this.transactionData.getReference();
|
byte[] reference = this.transactionData.getReference();
|
||||||
if (reference == null)
|
if (reference == null)
|
||||||
return null;
|
return null;
|
||||||
@ -474,7 +463,7 @@ public abstract class Transaction {
|
|||||||
* @return Child's TransactionData, or null if no child found
|
* @return Child's TransactionData, or null if no child found
|
||||||
* @throws DataException
|
* @throws DataException
|
||||||
*/
|
*/
|
||||||
public TransactionData getChild() throws DataException {
|
protected TransactionData getChild() throws DataException {
|
||||||
byte[] signature = this.transactionData.getSignature();
|
byte[] signature = this.transactionData.getSignature();
|
||||||
if (signature == null)
|
if (signature == null)
|
||||||
return null;
|
return null;
|
||||||
@ -925,9 +914,9 @@ public abstract class Transaction {
|
|||||||
Account creator = getCreator();
|
Account creator = getCreator();
|
||||||
|
|
||||||
// Update transaction creator's balance
|
// 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());
|
creator.setLastReference(transactionData.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -949,9 +938,9 @@ public abstract class Transaction {
|
|||||||
Account creator = getCreator();
|
Account creator = getCreator();
|
||||||
|
|
||||||
// Update transaction creator's balance
|
// 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());
|
creator.setLastReference(transactionData.getReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
|
||||||
import org.qortal.data.PaymentData;
|
import org.qortal.data.PaymentData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
import org.qortal.data.transaction.TransferAssetTransactionData;
|
import org.qortal.data.transaction.TransferAssetTransactionData;
|
||||||
@ -17,7 +14,9 @@ import org.qortal.repository.Repository;
|
|||||||
public class TransferAssetTransaction extends Transaction {
|
public class TransferAssetTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
private TransferAssetTransactionData transferAssetTransactionData;
|
private TransferAssetTransactionData transferAssetTransactionData;
|
||||||
|
private PaymentData paymentData = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -30,94 +29,63 @@ public class TransferAssetTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, transferAssetTransactionData.getRecipient()));
|
return Collections.singletonList(this.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getSender() throws DataException {
|
public Account getSender() {
|
||||||
return new PublicKeyAccount(this.repository, this.transferAssetTransactionData.getSenderPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
private PaymentData getPaymentData() {
|
private PaymentData getPaymentData() {
|
||||||
return new PaymentData(transferAssetTransactionData.getRecipient(), transferAssetTransactionData.getAssetId(),
|
if (this.paymentData == null)
|
||||||
transferAssetTransactionData.getAmount());
|
this.paymentData = new PaymentData(this.transferAssetTransactionData.getRecipient(), this.transferAssetTransactionData.getAssetId(),
|
||||||
|
this.transferAssetTransactionData.getAmount());
|
||||||
|
|
||||||
|
return this.paymentData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Wrap asset transfer as a payment and delegate final payment checks to Payment class
|
// 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
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
public ValidationResult isProcessable() throws DataException {
|
||||||
// Wrap asset transfer as a payment and delegate final processable checks to Payment class
|
// 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
|
@Override
|
||||||
public void process() throws DataException {
|
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.
|
// Wrap asset transfer as a payment and delegate processing to Payment class.
|
||||||
new Payment(this.repository).process(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(),
|
new Payment(this.repository).process(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), this.transferAssetTransactionData.getSignature());
|
||||||
transferAssetTransactionData.getSignature());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processReferencesAndFees() throws DataException {
|
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.
|
// 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(),
|
new Payment(this.repository).processReferencesAndFees(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), this.transferAssetTransactionData.getFee(),
|
||||||
transferAssetTransactionData.getSignature(), false);
|
this.transferAssetTransactionData.getSignature(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan() throws DataException {
|
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.
|
// Wrap asset transfer as a payment and delegate processing to Payment class.
|
||||||
new Payment(this.repository).orphan(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(),
|
new Payment(this.repository).orphan(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(),
|
||||||
transferAssetTransactionData.getSignature(), transferAssetTransactionData.getReference());
|
this.transferAssetTransactionData.getSignature(), this.transferAssetTransactionData.getReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphanReferencesAndFees() throws DataException {
|
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.
|
// 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(),
|
new Payment(this.repository).orphanReferencesAndFees(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), this.transferAssetTransactionData.getFee(),
|
||||||
transferAssetTransactionData.getSignature(), transferAssetTransactionData.getReference(), false);
|
this.transferAssetTransactionData.getSignature(), this.transferAssetTransactionData.getReference(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -8,7 +7,6 @@ import java.util.List;
|
|||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.block.BlockChain;
|
import org.qortal.block.BlockChain;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.account.AccountData;
|
import org.qortal.data.account.AccountData;
|
||||||
@ -36,42 +34,17 @@ public class TransferPrivsTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, transferPrivsTransactionData.getRecipient()));
|
return Collections.singletonList(this.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getSender() throws DataException {
|
public Account getSender() {
|
||||||
return new PublicKeyAccount(this.repository, this.transferPrivsTransactionData.getSenderPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getRecipient() throws DataException {
|
public Account getRecipient() {
|
||||||
return new Account(this.repository, this.transferPrivsTransactionData.getRecipient());
|
return new Account(this.repository, this.transferPrivsTransactionData.getRecipient());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,9 +140,9 @@ public class TransferPrivsTransaction extends Transaction {
|
|||||||
super.processReferencesAndFees();
|
super.processReferencesAndFees();
|
||||||
|
|
||||||
// If recipient has no last-reference then use this transaction's signature as last-reference so they can spend their block rewards
|
// 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)
|
if (recipient.getLastReference() == null)
|
||||||
recipient.setLastReference(transferPrivsTransactionData.getSignature());
|
recipient.setLastReference(this.transferPrivsTransactionData.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -249,8 +222,8 @@ public class TransferPrivsTransaction extends Transaction {
|
|||||||
super.orphanReferencesAndFees();
|
super.orphanReferencesAndFees();
|
||||||
|
|
||||||
// If recipient didn't have a last-reference prior to this transaction then remove it
|
// If recipient didn't have a last-reference prior to this transaction then remove it
|
||||||
Account recipient = new Account(this.repository, transferPrivsTransactionData.getRecipient());
|
Account recipient = new Account(this.repository, this.transferPrivsTransactionData.getRecipient());
|
||||||
if (Arrays.equals(recipient.getLastReference(), transferPrivsTransactionData.getSignature()))
|
if (Arrays.equals(recipient.getLastReference(), this.transferPrivsTransactionData.getSignature()))
|
||||||
recipient.setLastReference(null);
|
recipient.setLastReference(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.qortal.transaction;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -32,42 +31,14 @@ public class UpdateAssetTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(getNewOwner());
|
return Collections.singletonList(this.updateAssetTransactionData.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public PublicKeyAccount getOwner() throws DataException {
|
public PublicKeyAccount getOwner() {
|
||||||
return new PublicKeyAccount(this.repository, this.updateAssetTransactionData.getOwnerPublicKey());
|
return this.getCreator();
|
||||||
}
|
|
||||||
|
|
||||||
public Account getNewOwner() throws DataException {
|
|
||||||
return new Account(this.repository, this.updateAssetTransactionData.getNewOwner());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
@ -75,37 +46,33 @@ public class UpdateAssetTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Check asset actually exists
|
// Check asset actually exists
|
||||||
AssetData assetData = this.repository.getAssetRepository().fromAssetId(updateAssetTransactionData.getAssetId());
|
AssetData assetData = this.repository.getAssetRepository().fromAssetId(this.updateAssetTransactionData.getAssetId());
|
||||||
if (assetData == null)
|
if (assetData == null)
|
||||||
return ValidationResult.ASSET_DOES_NOT_EXIST;
|
return ValidationResult.ASSET_DOES_NOT_EXIST;
|
||||||
|
|
||||||
// Check new owner address is valid
|
// Check new owner address is valid
|
||||||
if (!Crypto.isValidAddress(updateAssetTransactionData.getNewOwner()))
|
if (!Crypto.isValidAddress(this.updateAssetTransactionData.getNewOwner()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
// Check new description size bounds. Note: zero length means DO NOT CHANGE description
|
// 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)
|
if (newDescriptionLength > Asset.MAX_DESCRIPTION_SIZE)
|
||||||
return ValidationResult.INVALID_DATA_LENGTH;
|
return ValidationResult.INVALID_DATA_LENGTH;
|
||||||
|
|
||||||
// Check new data size bounds. Note: zero length means DO NOT CHANGE data
|
// 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)
|
if (newDataLength > Asset.MAX_DATA_SIZE)
|
||||||
return ValidationResult.INVALID_DATA_LENGTH;
|
return ValidationResult.INVALID_DATA_LENGTH;
|
||||||
|
|
||||||
// As this transaction type could require approval, check txGroupId
|
// As this transaction type could require approval, check txGroupId
|
||||||
// matches groupID at creation
|
// matches groupID at creation
|
||||||
if (assetData.getCreationGroupId() != updateAssetTransactionData.getTxGroupId())
|
if (assetData.getCreationGroupId() != this.updateAssetTransactionData.getTxGroupId())
|
||||||
return ValidationResult.TX_GROUP_ID_MISMATCH;
|
return ValidationResult.TX_GROUP_ID_MISMATCH;
|
||||||
|
|
||||||
// Check fee is positive
|
|
||||||
if (updateAssetTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
Account currentOwner = getOwner();
|
Account currentOwner = getOwner();
|
||||||
|
|
||||||
// Check current owner has enough funds
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -115,7 +82,7 @@ public class UpdateAssetTransaction extends Transaction {
|
|||||||
public ValidationResult isProcessable() throws DataException {
|
public ValidationResult isProcessable() throws DataException {
|
||||||
// Check transaction's public key matches asset's current owner
|
// Check transaction's public key matches asset's current owner
|
||||||
Account currentOwner = getOwner();
|
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()))
|
if (!assetData.getOwner().equals(currentOwner.getAddress()))
|
||||||
return ValidationResult.INVALID_ASSET_OWNER;
|
return ValidationResult.INVALID_ASSET_OWNER;
|
||||||
@ -126,21 +93,21 @@ public class UpdateAssetTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Asset
|
// Update Asset
|
||||||
Asset asset = new Asset(this.repository, updateAssetTransactionData.getAssetId());
|
Asset asset = new Asset(this.repository, this.updateAssetTransactionData.getAssetId());
|
||||||
asset.update(updateAssetTransactionData);
|
asset.update(this.updateAssetTransactionData);
|
||||||
|
|
||||||
// Save this transaction, with updated "name reference" to previous transaction that updated name
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert asset
|
// Revert asset
|
||||||
Asset asset = new Asset(this.repository, updateAssetTransactionData.getAssetId());
|
Asset asset = new Asset(this.repository, this.updateAssetTransactionData.getAssetId());
|
||||||
asset.revert(updateAssetTransactionData);
|
asset.revert(this.updateAssetTransactionData);
|
||||||
|
|
||||||
// Save this transaction, with removed "name reference" to previous transaction that updated name
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.group.GroupData;
|
import org.qortal.data.group.GroupData;
|
||||||
@ -33,38 +31,14 @@ public class UpdateGroupTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(getNewOwner());
|
return Collections.singletonList(this.updateGroupTransactionData.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getOwner() throws DataException {
|
public Account getOwner() {
|
||||||
return new PublicKeyAccount(this.repository, this.updateGroupTransactionData.getOwnerPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getNewOwner() throws DataException {
|
public Account getNewOwner() throws DataException {
|
||||||
@ -76,46 +50,42 @@ public class UpdateGroupTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Check new owner address is valid
|
// Check new owner address is valid
|
||||||
if (!Crypto.isValidAddress(updateGroupTransactionData.getNewOwner()))
|
if (!Crypto.isValidAddress(this.updateGroupTransactionData.getNewOwner()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
// Check new approval threshold is valid
|
// Check new approval threshold is valid
|
||||||
if (updateGroupTransactionData.getNewApprovalThreshold() == null)
|
if (this.updateGroupTransactionData.getNewApprovalThreshold() == null)
|
||||||
return ValidationResult.INVALID_GROUP_APPROVAL_THRESHOLD;
|
return ValidationResult.INVALID_GROUP_APPROVAL_THRESHOLD;
|
||||||
|
|
||||||
// Check min/max block delay values
|
// Check min/max block delay values
|
||||||
if (updateGroupTransactionData.getNewMinimumBlockDelay() < 0)
|
if (this.updateGroupTransactionData.getNewMinimumBlockDelay() < 0)
|
||||||
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
||||||
|
|
||||||
if (updateGroupTransactionData.getNewMaximumBlockDelay() < 1)
|
if (this.updateGroupTransactionData.getNewMaximumBlockDelay() < 1)
|
||||||
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
||||||
|
|
||||||
if (updateGroupTransactionData.getNewMaximumBlockDelay() < updateGroupTransactionData.getNewMinimumBlockDelay())
|
if (this.updateGroupTransactionData.getNewMaximumBlockDelay() < this.updateGroupTransactionData.getNewMinimumBlockDelay())
|
||||||
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
return ValidationResult.INVALID_GROUP_BLOCK_DELAY;
|
||||||
|
|
||||||
// Check new description size bounds
|
// 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)
|
if (newDescriptionLength < 1 || newDescriptionLength > Group.MAX_DESCRIPTION_SIZE)
|
||||||
return ValidationResult.INVALID_DESCRIPTION_LENGTH;
|
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
|
// Check group exists
|
||||||
if (groupData == null)
|
if (groupData == null)
|
||||||
return ValidationResult.GROUP_DOES_NOT_EXIST;
|
return ValidationResult.GROUP_DOES_NOT_EXIST;
|
||||||
|
|
||||||
// As this transaction type could require approval, check txGroupId matches groupID at creation
|
// 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;
|
return ValidationResult.TX_GROUP_ID_MISMATCH;
|
||||||
|
|
||||||
Account owner = getOwner();
|
Account owner = getOwner();
|
||||||
|
|
||||||
// Check fee is positive
|
|
||||||
if (updateGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
// Check creator has enough funds
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -123,7 +93,7 @@ public class UpdateGroupTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
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();
|
Account owner = getOwner();
|
||||||
|
|
||||||
// Check transaction's public key matches group's current owner
|
// Check transaction's public key matches group's current owner
|
||||||
@ -133,31 +103,30 @@ public class UpdateGroupTransaction extends Transaction {
|
|||||||
Account newOwner = getNewOwner();
|
Account newOwner = getNewOwner();
|
||||||
|
|
||||||
// Check new owner is not banned
|
// 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.BANNED_FROM_GROUP;
|
||||||
|
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Group
|
// Update Group
|
||||||
Group group = new Group(this.repository, updateGroupTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.updateGroupTransactionData.getGroupId());
|
||||||
group.updateGroup(updateGroupTransactionData);
|
group.updateGroup(this.updateGroupTransactionData);
|
||||||
|
|
||||||
// Save this transaction, now with updated "group reference" to previous transaction that updated group
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert Group update
|
// Revert Group update
|
||||||
Group group = new Group(this.repository, updateGroupTransactionData.getGroupId());
|
Group group = new Group(this.repository, this.updateGroupTransactionData.getGroupId());
|
||||||
group.unupdateGroup(updateGroupTransactionData);
|
group.unupdateGroup(this.updateGroupTransactionData);
|
||||||
|
|
||||||
// Save this transaction, now with removed "group reference"
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.naming.NameData;
|
import org.qortal.data.naming.NameData;
|
||||||
@ -33,41 +31,17 @@ public class UpdateNameTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return Collections.singletonList(getNewOwner());
|
return Collections.singletonList(this.updateNameTransactionData.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
public Account getOwner() throws DataException {
|
public Account getOwner() {
|
||||||
return new PublicKeyAccount(this.repository, this.updateNameTransactionData.getOwnerPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getNewOwner() throws DataException {
|
public Account getNewOwner() {
|
||||||
return new Account(this.repository, this.updateNameTransactionData.getNewOwner());
|
return new Account(this.repository, this.updateNameTransactionData.getNewOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,42 +49,40 @@ public class UpdateNameTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
String name = this.updateNameTransactionData.getName();
|
||||||
|
|
||||||
// Check new owner address is valid
|
// Check new owner address is valid
|
||||||
if (!Crypto.isValidAddress(updateNameTransactionData.getNewOwner()))
|
if (!Crypto.isValidAddress(this.updateNameTransactionData.getNewOwner()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
// Check name size bounds
|
// Check name size bounds
|
||||||
int nameLength = Utf8.encodedLength(updateNameTransactionData.getName());
|
int nameLength = Utf8.encodedLength(name);
|
||||||
if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE)
|
if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE)
|
||||||
return ValidationResult.INVALID_NAME_LENGTH;
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
// Check new data size bounds
|
// 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)
|
if (newDataLength < 1 || newDataLength > Name.MAX_DATA_SIZE)
|
||||||
return ValidationResult.INVALID_DATA_LENGTH;
|
return ValidationResult.INVALID_DATA_LENGTH;
|
||||||
|
|
||||||
// Check name is lowercase
|
// Check name is lowercase
|
||||||
if (!updateNameTransactionData.getName().equals(updateNameTransactionData.getName().toLowerCase()))
|
if (!name.equals(name.toLowerCase()))
|
||||||
return ValidationResult.NAME_NOT_LOWER_CASE;
|
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||||
|
|
||||||
NameData nameData = this.repository.getNameRepository().fromName(updateNameTransactionData.getName());
|
NameData nameData = this.repository.getNameRepository().fromName(name);
|
||||||
|
|
||||||
// Check name exists
|
// Check name exists
|
||||||
if (nameData == null)
|
if (nameData == null)
|
||||||
return ValidationResult.NAME_DOES_NOT_EXIST;
|
return ValidationResult.NAME_DOES_NOT_EXIST;
|
||||||
|
|
||||||
// As this transaction type could require approval, check txGroupId matches groupID at creation
|
// 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;
|
return ValidationResult.TX_GROUP_ID_MISMATCH;
|
||||||
|
|
||||||
Account owner = getOwner();
|
Account owner = getOwner();
|
||||||
|
|
||||||
// Check fee is positive
|
// Check owner has enough funds
|
||||||
if (updateNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
if (owner.getConfirmedBalance(Asset.QORT) < this.updateNameTransactionData.getFee())
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
|
||||||
|
|
||||||
// Check issuer has enough funds
|
|
||||||
if (owner.getConfirmedBalance(Asset.QORT).compareTo(updateNameTransactionData.getFee()) < 0)
|
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -118,7 +90,7 @@ public class UpdateNameTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isProcessable() throws DataException {
|
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
|
// Check name isn't currently for sale
|
||||||
if (nameData.getIsForSale())
|
if (nameData.getIsForSale())
|
||||||
@ -136,21 +108,21 @@ public class UpdateNameTransaction extends Transaction {
|
|||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
// Update Name
|
// Update Name
|
||||||
Name name = new Name(this.repository, updateNameTransactionData.getName());
|
Name name = new Name(this.repository, this.updateNameTransactionData.getName());
|
||||||
name.update(updateNameTransactionData);
|
name.update(this.updateNameTransactionData);
|
||||||
|
|
||||||
// Save this transaction, now with updated "name reference" to previous transaction that updated name
|
// 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
|
@Override
|
||||||
public void orphan() throws DataException {
|
public void orphan() throws DataException {
|
||||||
// Revert name
|
// Revert name
|
||||||
Name name = new Name(this.repository, updateNameTransactionData.getName());
|
Name name = new Name(this.repository, this.updateNameTransactionData.getName());
|
||||||
name.revert(updateNameTransactionData);
|
name.revert(this.updateNameTransactionData);
|
||||||
|
|
||||||
// Save this transaction, now with removed "name reference"
|
// 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;
|
package org.qortal.transaction;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.util.Collections;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
import org.qortal.data.transaction.VoteOnPollTransactionData;
|
import org.qortal.data.transaction.VoteOnPollTransactionData;
|
||||||
@ -39,72 +37,55 @@ public class VoteOnPollTransaction extends Transaction {
|
|||||||
// More information
|
// More information
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Account> getRecipientAccounts() {
|
public List<String> getRecipientAddresses() throws DataException {
|
||||||
return new ArrayList<>();
|
return Collections.emptyList();
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
// Navigation
|
||||||
|
|
||||||
public Account getVoter() throws DataException {
|
public Account getVoter() {
|
||||||
return new PublicKeyAccount(this.repository, voteOnPollTransactionData.getVoterPublicKey());
|
return this.getCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
String pollName = this.voteOnPollTransactionData.getPollName();
|
||||||
|
|
||||||
// Check name size bounds
|
// Check name size bounds
|
||||||
int pollNameLength = Utf8.encodedLength(voteOnPollTransactionData.getPollName());
|
int pollNameLength = Utf8.encodedLength(pollName);
|
||||||
if (pollNameLength < 1 || pollNameLength > Poll.MAX_NAME_SIZE)
|
if (pollNameLength < 1 || pollNameLength > Poll.MAX_NAME_SIZE)
|
||||||
return ValidationResult.INVALID_NAME_LENGTH;
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
// Check poll name is lowercase
|
// Check poll name is lowercase
|
||||||
if (!voteOnPollTransactionData.getPollName().equals(voteOnPollTransactionData.getPollName().toLowerCase()))
|
if (!pollName.equals(pollName.toLowerCase()))
|
||||||
return ValidationResult.NAME_NOT_LOWER_CASE;
|
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||||
|
|
||||||
VotingRepository votingRepository = this.repository.getVotingRepository();
|
VotingRepository votingRepository = this.repository.getVotingRepository();
|
||||||
|
|
||||||
// Check poll exists
|
// Check poll exists
|
||||||
PollData pollData = votingRepository.fromPollName(voteOnPollTransactionData.getPollName());
|
PollData pollData = votingRepository.fromPollName(pollName);
|
||||||
if (pollData == null)
|
if (pollData == null)
|
||||||
return ValidationResult.POLL_DOES_NOT_EXIST;
|
return ValidationResult.POLL_DOES_NOT_EXIST;
|
||||||
|
|
||||||
// Check poll option index is within bounds
|
// Check poll option index is within bounds
|
||||||
List<PollOptionData> pollOptions = pollData.getPollOptions();
|
List<PollOptionData> pollOptions = pollData.getPollOptions();
|
||||||
int optionIndex = voteOnPollTransactionData.getOptionIndex();
|
int optionIndex = this.voteOnPollTransactionData.getOptionIndex();
|
||||||
|
|
||||||
if (optionIndex < 0 || optionIndex > pollOptions.size() - 1)
|
if (optionIndex < 0 || optionIndex > pollOptions.size() - 1)
|
||||||
return ValidationResult.POLL_OPTION_DOES_NOT_EXIST;
|
return ValidationResult.POLL_OPTION_DOES_NOT_EXIST;
|
||||||
|
|
||||||
// Check if vote already exists
|
// 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)
|
if (voteOnPollData != null && voteOnPollData.getOptionIndex() == optionIndex)
|
||||||
return ValidationResult.ALREADY_VOTED_FOR_THAT_OPTION;
|
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
|
// Check reference is correct
|
||||||
Account voter = getVoter();
|
Account voter = getVoter();
|
||||||
|
|
||||||
// Check voter has enough funds
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
@ -112,27 +93,28 @@ public class VoteOnPollTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process() throws DataException {
|
public void process() throws DataException {
|
||||||
|
String pollName = this.voteOnPollTransactionData.getPollName();
|
||||||
|
|
||||||
Account voter = getVoter();
|
Account voter = getVoter();
|
||||||
|
|
||||||
VotingRepository votingRepository = this.repository.getVotingRepository();
|
VotingRepository votingRepository = this.repository.getVotingRepository();
|
||||||
|
|
||||||
// Check for previous vote so we can save option in case of orphaning
|
// Check for previous vote so we can save option in case of orphaning
|
||||||
VoteOnPollData previousVoteOnPollData = votingRepository.getVote(voteOnPollTransactionData.getPollName(),
|
VoteOnPollData previousVoteOnPollData = votingRepository.getVote(pollName, this.voteOnPollTransactionData.getVoterPublicKey());
|
||||||
voteOnPollTransactionData.getVoterPublicKey());
|
|
||||||
if (previousVoteOnPollData != null) {
|
if (previousVoteOnPollData != null) {
|
||||||
voteOnPollTransactionData.setPreviousOptionIndex(previousVoteOnPollData.getOptionIndex());
|
voteOnPollTransactionData.setPreviousOptionIndex(previousVoteOnPollData.getOptionIndex());
|
||||||
LOGGER.trace("Previous vote by " + voter.getAddress() + " on poll \"" + voteOnPollTransactionData.getPollName() + "\" was option index "
|
LOGGER.trace(() -> String.format("Previous vote by %s on poll \"%s\" was option index %d",
|
||||||
+ previousVoteOnPollData.getOptionIndex());
|
voter.getAddress(), pollName, previousVoteOnPollData.getOptionIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save this transaction, now with possible previous vote
|
// Save this transaction, now with possible previous vote
|
||||||
this.repository.getTransactionRepository().save(voteOnPollTransactionData);
|
this.repository.getTransactionRepository().save(voteOnPollTransactionData);
|
||||||
|
|
||||||
// Apply vote to poll
|
// Apply vote to poll
|
||||||
LOGGER.trace("Vote by " + voter.getAddress() + " on poll \"" + voteOnPollTransactionData.getPollName() + "\" with option index "
|
LOGGER.trace(() -> String.format("Vote by %s on poll \"%s\" with option index %d",
|
||||||
+ voteOnPollTransactionData.getOptionIndex());
|
voter.getAddress(), pollName, this.voteOnPollTransactionData.getOptionIndex()));
|
||||||
VoteOnPollData newVoteOnPollData = new VoteOnPollData(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey(),
|
VoteOnPollData newVoteOnPollData = new VoteOnPollData(pollName, this.voteOnPollTransactionData.getVoterPublicKey(),
|
||||||
voteOnPollTransactionData.getOptionIndex());
|
this.voteOnPollTransactionData.getOptionIndex());
|
||||||
votingRepository.save(newVoteOnPollData);
|
votingRepository.save(newVoteOnPollData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,22 +124,24 @@ public class VoteOnPollTransaction extends Transaction {
|
|||||||
|
|
||||||
// Does this transaction have previous vote info?
|
// Does this transaction have previous vote info?
|
||||||
VotingRepository votingRepository = this.repository.getVotingRepository();
|
VotingRepository votingRepository = this.repository.getVotingRepository();
|
||||||
Integer previousOptionIndex = voteOnPollTransactionData.getPreviousOptionIndex();
|
Integer previousOptionIndex = this.voteOnPollTransactionData.getPreviousOptionIndex();
|
||||||
if (previousOptionIndex != null) {
|
if (previousOptionIndex != null) {
|
||||||
// Reinstate previous vote
|
// Reinstate previous vote
|
||||||
LOGGER.trace(() -> String.format("Reinstating previous vote by %s on poll \"%s\" with option index %d", voter.getAddress(), voteOnPollTransactionData.getPollName(), previousOptionIndex));
|
LOGGER.trace(() -> String.format("Reinstating previous vote by %s on poll \"%s\" with option index %d",
|
||||||
VoteOnPollData previousVoteOnPollData = new VoteOnPollData(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey(),
|
voter.getAddress(), this.voteOnPollTransactionData.getPollName(), previousOptionIndex));
|
||||||
|
VoteOnPollData previousVoteOnPollData = new VoteOnPollData(this.voteOnPollTransactionData.getPollName(), this.voteOnPollTransactionData.getVoterPublicKey(),
|
||||||
previousOptionIndex);
|
previousOptionIndex);
|
||||||
votingRepository.save(previousVoteOnPollData);
|
votingRepository.save(previousVoteOnPollData);
|
||||||
} else {
|
} else {
|
||||||
// Delete vote
|
// Delete vote
|
||||||
LOGGER.trace(() -> String.format("Deleting vote by %s on poll \"%s\" with option index %d", voter.getAddress(), voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getOptionIndex()));
|
LOGGER.trace(() -> String.format("Deleting vote by %s on poll \"%s\" with option index %d",
|
||||||
votingRepository.delete(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey());
|
voter.getAddress(), this.voteOnPollTransactionData.getPollName(), this.voteOnPollTransactionData.getOptionIndex()));
|
||||||
|
votingRepository.delete(this.voteOnPollTransactionData.getPollName(), this.voteOnPollTransactionData.getVoterPublicKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save this transaction, with removed previous vote info
|
// Save this transaction, with removed previous vote info
|
||||||
voteOnPollTransactionData.setPreviousOptionIndex(null);
|
this.voteOnPollTransactionData.setPreviousOptionIndex(null);
|
||||||
this.repository.getTransactionRepository().save(voteOnPollTransactionData);
|
this.repository.getTransactionRepository().save(this.voteOnPollTransactionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,8 @@ package org.qortal.transform;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.qortal.data.PaymentData;
|
import org.qortal.data.PaymentData;
|
||||||
import org.qortal.utils.Serialization;
|
import org.qortal.utils.Serialization;
|
||||||
|
|
||||||
@ -15,8 +13,6 @@ public class PaymentTransformer extends Transformer {
|
|||||||
|
|
||||||
// Property lengths
|
// Property lengths
|
||||||
private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH;
|
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;
|
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();
|
long assetId = byteBuffer.getLong();
|
||||||
|
|
||||||
BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer, AMOUNT_LENGTH);
|
long amount = byteBuffer.getLong();
|
||||||
|
|
||||||
return new PaymentData(recipient, assetId, amount);
|
return new PaymentData(recipient, assetId, amount);
|
||||||
}
|
}
|
||||||
@ -42,7 +38,7 @@ public class PaymentTransformer extends Transformer {
|
|||||||
|
|
||||||
bytes.write(Longs.toByteArray(paymentData.getAssetId()));
|
bytes.write(Longs.toByteArray(paymentData.getAssetId()));
|
||||||
|
|
||||||
Serialization.serializeBigDecimal(bytes, paymentData.getAmount(), AMOUNT_LENGTH);
|
bytes.write(Longs.toByteArray(paymentData.getAmount()));
|
||||||
|
|
||||||
return bytes.toByteArray();
|
return bytes.toByteArray();
|
||||||
} catch (IOException | ClassCastException e) {
|
} 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 BYTE_LENGTH = 1;
|
||||||
public static final int INT_LENGTH = 4;
|
public static final int INT_LENGTH = 4;
|
||||||
public static final int LONG_LENGTH = 8;
|
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
|
// Raw, not Base58-encoded
|
||||||
public static final int ADDRESS_LENGTH = 25;
|
public static final int ADDRESS_LENGTH = 25;
|
||||||
|
@ -2,7 +2,6 @@ package org.qortal.transform.block;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
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 TRANSACTIONS_SIGNATURE_LENGTH = SIGNATURE_LENGTH;
|
||||||
private static final int MINTER_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 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 MINTER_PUBLIC_KEY_LENGTH = PUBLIC_KEY_LENGTH;
|
||||||
private static final int TRANSACTION_COUNT_LENGTH = INT_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;
|
+ TRANSACTIONS_SIGNATURE_LENGTH + MINTER_SIGNATURE_LENGTH + TRANSACTION_COUNT_LENGTH;
|
||||||
|
|
||||||
public static final int BLOCK_SIGNATURE_LENGTH = MINTER_SIGNATURE_LENGTH + TRANSACTIONS_SIGNATURE_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 TRANSACTION_SIZE_LENGTH = INT_LENGTH; // per transaction
|
||||||
|
|
||||||
protected static final int AT_BYTES_LENGTH = INT_LENGTH;
|
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 AT_LENGTH = AT_FEES_LENGTH + AT_BYTES_LENGTH;
|
||||||
|
|
||||||
protected static final int ONLINE_ACCOUNTS_COUNT_LENGTH = INT_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_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 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.
|
* 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];
|
byte[] minterSignature = new byte[MINTER_SIGNATURE_LENGTH];
|
||||||
byteBuffer.get(minterSignature);
|
byteBuffer.get(minterSignature);
|
||||||
|
|
||||||
BigDecimal totalFees = BigDecimal.ZERO.setScale(8);
|
long totalFees = 0;
|
||||||
|
|
||||||
int atCount = 0;
|
int atCount = 0;
|
||||||
BigDecimal atFees = BigDecimal.ZERO.setScale(8);
|
long atFees = 0;
|
||||||
List<ATStateData> atStates = new ArrayList<>();
|
List<ATStateData> atStates = new ArrayList<>();
|
||||||
|
|
||||||
int atBytesLength = byteBuffer.getInt();
|
int atBytesLength = byteBuffer.getInt();
|
||||||
@ -130,9 +130,10 @@ public class BlockTransformer extends Transformer {
|
|||||||
byte[] stateHash = new byte[SHA256_LENGTH];
|
byte[] stateHash = new byte[SHA256_LENGTH];
|
||||||
atByteBuffer.get(stateHash);
|
atByteBuffer.get(stateHash);
|
||||||
|
|
||||||
BigDecimal fees = Serialization.deserializeBigDecimal(atByteBuffer);
|
long fees = atByteBuffer.getLong();
|
||||||
|
|
||||||
// Add this AT's fees to our total
|
// Add this AT's fees to our total
|
||||||
atFees = atFees.add(fees);
|
atFees += fees;
|
||||||
|
|
||||||
atStates.add(new ATStateData(atAddress, stateHash, fees));
|
atStates.add(new ATStateData(atAddress, stateHash, fees));
|
||||||
}
|
}
|
||||||
@ -141,7 +142,7 @@ public class BlockTransformer extends Transformer {
|
|||||||
atCount = atStates.size();
|
atCount = atStates.size();
|
||||||
|
|
||||||
// Add AT fees to totalFees
|
// Add AT fees to totalFees
|
||||||
totalFees = totalFees.add(atFees);
|
totalFees += atFees;
|
||||||
|
|
||||||
int transactionCount = byteBuffer.getInt();
|
int transactionCount = byteBuffer.getInt();
|
||||||
|
|
||||||
@ -166,7 +167,7 @@ public class BlockTransformer extends Transformer {
|
|||||||
TransactionData transactionData = TransactionTransformer.fromBytes(transactionBytes);
|
TransactionData transactionData = TransactionTransformer.fromBytes(transactionBytes);
|
||||||
transactions.add(transactionData);
|
transactions.add(transactionData);
|
||||||
|
|
||||||
totalFees = totalFees.add(transactionData.getFee());
|
totalFees += transactionData.getFee();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Online accounts info?
|
// Online accounts info?
|
||||||
@ -265,7 +266,7 @@ public class BlockTransformer extends Transformer {
|
|||||||
for (ATStateData atStateData : block.getATStates()) {
|
for (ATStateData atStateData : block.getATStates()) {
|
||||||
bytes.write(Base58.decode(atStateData.getATAddress()));
|
bytes.write(Base58.decode(atStateData.getATAddress()));
|
||||||
bytes.write(atStateData.getStateHash());
|
bytes.write(atStateData.getStateHash());
|
||||||
Serialization.serializeBigDecimal(bytes, atStateData.getFees());
|
bytes.write(Longs.toByteArray(atStateData.getFees()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transactions
|
// Transactions
|
||||||
|
@ -2,7 +2,6 @@ package org.qortal.transform.transaction;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.qortal.data.transaction.AccountFlagsTransactionData;
|
import org.qortal.data.transaction.AccountFlagsTransactionData;
|
||||||
@ -13,6 +12,7 @@ import org.qortal.transform.TransformationException;
|
|||||||
import org.qortal.utils.Serialization;
|
import org.qortal.utils.Serialization;
|
||||||
|
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
|
import com.google.common.primitives.Longs;
|
||||||
|
|
||||||
public class AccountFlagsTransactionTransformer extends TransactionTransformer {
|
public class AccountFlagsTransactionTransformer extends TransactionTransformer {
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ public class AccountFlagsTransactionTransformer extends TransactionTransformer {
|
|||||||
int orMask = byteBuffer.getInt();
|
int orMask = byteBuffer.getInt();
|
||||||
int xorMask = byteBuffer.getInt();
|
int xorMask = byteBuffer.getInt();
|
||||||
|
|
||||||
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
long fee = byteBuffer.getLong();
|
||||||
|
|
||||||
byte[] signature = new byte[SIGNATURE_LENGTH];
|
byte[] signature = new byte[SIGNATURE_LENGTH];
|
||||||
byteBuffer.get(signature);
|
byteBuffer.get(signature);
|
||||||
@ -85,7 +85,7 @@ public class AccountFlagsTransactionTransformer extends TransactionTransformer {
|
|||||||
bytes.write(Ints.toByteArray(accountFlagsTransactionData.getOrMask()));
|
bytes.write(Ints.toByteArray(accountFlagsTransactionData.getOrMask()));
|
||||||
bytes.write(Ints.toByteArray(accountFlagsTransactionData.getXorMask()));
|
bytes.write(Ints.toByteArray(accountFlagsTransactionData.getXorMask()));
|
||||||
|
|
||||||
Serialization.serializeBigDecimal(bytes, accountFlagsTransactionData.getFee());
|
bytes.write(Longs.toByteArray(accountFlagsTransactionData.getFee()));
|
||||||
|
|
||||||
if (accountFlagsTransactionData.getSignature() != null)
|
if (accountFlagsTransactionData.getSignature() != null)
|
||||||
bytes.write(accountFlagsTransactionData.getSignature());
|
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