mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-11 17:55:50 +00:00
More migration of Assets/Orders and TransferAssetTransactions
This commit is contained in:
parent
2c23acfa74
commit
2f8c160627
74
src/data/assets/OrderData.java
Normal file
74
src/data/assets/OrderData.java
Normal file
@ -0,0 +1,74 @@
|
||||
package data.assets;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class OrderData implements Comparable<OrderData> {
|
||||
|
||||
private byte[] orderId;
|
||||
private byte[] creatorPublicKey;
|
||||
private long haveAssetId;
|
||||
private long wantAssetId;
|
||||
private BigDecimal amount;
|
||||
private BigDecimal fulfilled;
|
||||
private BigDecimal price;
|
||||
private long timestamp;
|
||||
|
||||
public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal fulfilled, BigDecimal price,
|
||||
long timestamp) {
|
||||
this.orderId = orderId;
|
||||
this.creatorPublicKey = creatorPublicKey;
|
||||
this.haveAssetId = haveAssetId;
|
||||
this.wantAssetId = wantAssetId;
|
||||
this.amount = amount;
|
||||
this.fulfilled = fulfilled;
|
||||
this.price = price;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, long timestamp) {
|
||||
this(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, BigDecimal.ZERO.setScale(8), price, timestamp);
|
||||
}
|
||||
|
||||
public byte[] getOrderId() {
|
||||
return this.orderId;
|
||||
}
|
||||
|
||||
public byte[] getCreatorPublicKey() {
|
||||
return this.creatorPublicKey;
|
||||
}
|
||||
|
||||
public long getHaveAssetId() {
|
||||
return this.haveAssetId;
|
||||
}
|
||||
|
||||
public long getWantAssetId() {
|
||||
return this.wantAssetId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public BigDecimal getFulfilled() {
|
||||
return this.fulfilled;
|
||||
}
|
||||
|
||||
public void setFulfilled(BigDecimal fulfilled) {
|
||||
this.fulfilled = fulfilled;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return this.price;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(OrderData orderData) {
|
||||
// Compare using prices
|
||||
return this.price.compareTo(orderData.getPrice());
|
||||
}
|
||||
|
||||
}
|
50
src/data/transaction/TransferAssetTransactionData.java
Normal file
50
src/data/transaction/TransferAssetTransactionData.java
Normal file
@ -0,0 +1,50 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
public class TransferAssetTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private byte[] senderPublicKey;
|
||||
private String recipient;
|
||||
private BigDecimal amount;
|
||||
private long assetId;
|
||||
|
||||
// Constructors
|
||||
|
||||
public TransferAssetTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, long assetId, BigDecimal fee, long timestamp,
|
||||
byte[] reference, byte[] signature) {
|
||||
super(TransactionType.TRANSFER_ASSET, fee, senderPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.senderPublicKey = senderPublicKey;
|
||||
this.recipient = recipient;
|
||||
this.amount = amount;
|
||||
this.assetId = assetId;
|
||||
}
|
||||
|
||||
public TransferAssetTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, long assetId, BigDecimal fee, long timestamp,
|
||||
byte[] reference) {
|
||||
this(senderPublicKey, recipient, amount, assetId, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public byte[] getSenderPublicKey() {
|
||||
return this.senderPublicKey;
|
||||
}
|
||||
|
||||
public String getRecipient() {
|
||||
return this.recipient;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
package qora.assets;
|
||||
|
||||
import data.assets.AssetData;
|
||||
import repository.Repository;
|
||||
|
||||
public class Asset {
|
||||
|
||||
/**
|
||||
@ -7,4 +10,15 @@ public class Asset {
|
||||
*/
|
||||
public static final long QORA = 0L;
|
||||
|
||||
// Properties
|
||||
private Repository repository;
|
||||
private AssetData assetData;
|
||||
|
||||
// Constructors
|
||||
|
||||
public Asset(Repository repository, AssetData assetData) {
|
||||
this.repository = repository;
|
||||
this.assetData = assetData;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,93 +1,39 @@
|
||||
package qora.assets;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import qora.account.Account;
|
||||
import transform.TransformationException;
|
||||
import data.assets.OrderData;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
|
||||
public class Order implements Comparable<Order> {
|
||||
public class Order {
|
||||
|
||||
// Properties
|
||||
private BigInteger id;
|
||||
private Account creator;
|
||||
private long haveAssetId;
|
||||
private long wantAssetId;
|
||||
private BigDecimal amount;
|
||||
private BigDecimal price;
|
||||
private long timestamp;
|
||||
|
||||
// Other properties
|
||||
private BigDecimal fulfilled;
|
||||
private Repository repository;
|
||||
private OrderData orderData;
|
||||
|
||||
// Constructors
|
||||
|
||||
public Order(BigInteger id, Account creator, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, long timestamp) {
|
||||
this.id = id;
|
||||
this.creator = creator;
|
||||
this.haveAssetId = haveAssetId;
|
||||
this.wantAssetId = wantAssetId;
|
||||
this.amount = amount;
|
||||
this.price = price;
|
||||
this.timestamp = timestamp;
|
||||
|
||||
this.fulfilled = BigDecimal.ZERO.setScale(8);
|
||||
public Order(Repository repository, OrderData orderData) {
|
||||
this.repository = repository;
|
||||
this.orderData = orderData;
|
||||
}
|
||||
|
||||
public Order(BigInteger id, Account creator, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal fulfilled, BigDecimal price,
|
||||
long timestamp) {
|
||||
this(id, creator, haveAssetId, wantAssetId, amount, price, timestamp);
|
||||
// Getters/Setters
|
||||
|
||||
this.fulfilled = fulfilled;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public BigInteger getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public Account getCreator() {
|
||||
return this.creator;
|
||||
}
|
||||
|
||||
public long getHaveAssetId() {
|
||||
return this.haveAssetId;
|
||||
}
|
||||
|
||||
public long getWantAssetId() {
|
||||
return this.wantAssetId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return this.price;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
public BigDecimal getFulfilled() {
|
||||
return this.fulfilled;
|
||||
}
|
||||
|
||||
public void setFulfilled(BigDecimal fulfilled) {
|
||||
this.fulfilled = fulfilled;
|
||||
public OrderData getOrderData() {
|
||||
return this.orderData;
|
||||
}
|
||||
|
||||
// More information
|
||||
|
||||
public BigDecimal getAmountLeft() {
|
||||
return this.amount.subtract(this.fulfilled);
|
||||
return this.orderData.getAmount().subtract(this.orderData.getFulfilled());
|
||||
}
|
||||
|
||||
public boolean isFulfilled() {
|
||||
return this.fulfilled.compareTo(this.amount) == 0;
|
||||
return this.orderData.getFulfilled().compareTo(this.orderData.getAmount()) == 0;
|
||||
}
|
||||
|
||||
// TODO
|
||||
@ -96,8 +42,6 @@ public class Order implements Comparable<Order> {
|
||||
// TODO
|
||||
// public boolean isConfirmed() {}
|
||||
|
||||
// Load/Save/Delete
|
||||
|
||||
// Navigation
|
||||
|
||||
// XXX is this getInitiatedTrades() above?
|
||||
@ -107,35 +51,14 @@ public class Order implements Comparable<Order> {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Converters
|
||||
|
||||
public static Order parse(byte[] data) throws TransformationException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
// TODO
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Processing
|
||||
|
||||
// Other
|
||||
|
||||
@Override
|
||||
public int compareTo(Order order) {
|
||||
// Compare using prices
|
||||
return this.price.compareTo(order.getPrice());
|
||||
public void process() throws DataException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public Order copy() {
|
||||
try {
|
||||
return parse(this.toBytes());
|
||||
} catch (TransformationException e) {
|
||||
return null;
|
||||
}
|
||||
public void orphan() throws DataException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,18 @@
|
||||
package qora.transaction;
|
||||
|
||||
import data.transaction.TransactionData;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
|
||||
import data.assets.AssetData;
|
||||
import data.assets.OrderData;
|
||||
import data.transaction.CreateOrderTransactionData;
|
||||
import data.transaction.TransactionData;
|
||||
import qora.account.Account;
|
||||
import qora.account.PublicKeyAccount;
|
||||
import qora.assets.Asset;
|
||||
import qora.assets.Order;
|
||||
import qora.block.Block;
|
||||
import repository.AssetRepository;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
|
||||
@ -27,17 +37,117 @@ public class CreateOrderTransaction extends Transaction {
|
||||
// Processing
|
||||
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// TODO
|
||||
CreateOrderTransactionData createOrderTransactionData = (CreateOrderTransactionData) this.transactionData;
|
||||
long haveAssetId = createOrderTransactionData.getHaveAssetId();
|
||||
long wantAssetId = createOrderTransactionData.getWantAssetId();
|
||||
|
||||
// Check have/want assets are not the same
|
||||
if (haveAssetId == wantAssetId)
|
||||
return ValidationResult.HAVE_EQUALS_WANT;
|
||||
|
||||
// Check amount is positive
|
||||
if (createOrderTransactionData.getAmount().compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_AMOUNT;
|
||||
|
||||
// Check price is positive
|
||||
if (createOrderTransactionData.getPrice().compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_PRICE;
|
||||
|
||||
// Check fee is positive
|
||||
if (createOrderTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
AssetRepository assetRepository = this.repository.getAssetRepository();
|
||||
|
||||
// Check "have" asset exists
|
||||
AssetData haveAssetData = assetRepository.fromAssetId(haveAssetId);
|
||||
if (haveAssetData == null)
|
||||
return ValidationResult.ASSET_DOES_NOT_EXIST;
|
||||
|
||||
// Check "want" asset exists
|
||||
AssetData wantAssetData = assetRepository.fromAssetId(wantAssetId);
|
||||
if (wantAssetData == null)
|
||||
return ValidationResult.ASSET_DOES_NOT_EXIST;
|
||||
|
||||
Account creator = new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey());
|
||||
|
||||
// Check reference is correct
|
||||
if (!Arrays.equals(creator.getLastReference(), createOrderTransactionData.getReference()))
|
||||
return ValidationResult.INVALID_REFERENCE;
|
||||
|
||||
// Check order creator has enough asset balance AFTER removing fee, in case asset is QORA
|
||||
// If asset is QORA then we need to check amount + fee in one go
|
||||
if (haveAssetId == Asset.QORA) {
|
||||
// Check creator has enough funds for amount + fee in QORA
|
||||
if (creator.getConfirmedBalance(Asset.QORA).compareTo(createOrderTransactionData.getAmount().add(createOrderTransactionData.getFee())) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
} else {
|
||||
// Check creator has enough funds for amount in whatever asset
|
||||
if (creator.getConfirmedBalance(haveAssetId).compareTo(createOrderTransactionData.getAmount()) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
// Check creator has enough funds for fee in QORA
|
||||
// NOTE: in Gen1 pre-POWFIX-RELEASE transactions didn't have this check
|
||||
if (createOrderTransactionData.getTimestamp() >= Block.POWFIX_RELEASE_TIMESTAMP
|
||||
&& creator.getConfirmedBalance(Asset.QORA).compareTo(createOrderTransactionData.getFee()) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
}
|
||||
|
||||
// Check "have" amount is integer if "have" asset is not divisible
|
||||
if (!haveAssetData.getIsDivisible() && createOrderTransactionData.getAmount().stripTrailingZeros().scale() > 0)
|
||||
return ValidationResult.INVALID_AMOUNT;
|
||||
|
||||
// Check total return from fulfilled order would be integer if "want" asset is not divisible
|
||||
if (!wantAssetData.getIsDivisible()
|
||||
&& createOrderTransactionData.getAmount().multiply(createOrderTransactionData.getPrice()).stripTrailingZeros().scale() > 0)
|
||||
return ValidationResult.INVALID_RETURN;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
public void process() throws DataException {
|
||||
// TODO
|
||||
CreateOrderTransactionData createOrderTransactionData = (CreateOrderTransactionData) this.transactionData;
|
||||
Account creator = new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey());
|
||||
|
||||
// Update creator's balance due to fee
|
||||
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(createOrderTransactionData.getFee()));
|
||||
|
||||
// Update creator's last reference
|
||||
creator.setLastReference(createOrderTransactionData.getSignature());
|
||||
|
||||
// Save this transaction itself
|
||||
this.repository.getTransactionRepository().save(createOrderTransactionData);
|
||||
|
||||
// Order Id is transaction's signature
|
||||
byte[] orderId = createOrderTransactionData.getSignature();
|
||||
|
||||
// Process the order itself
|
||||
OrderData orderData = new OrderData(orderId, createOrderTransactionData.getCreatorPublicKey(), createOrderTransactionData.getHaveAssetId(),
|
||||
createOrderTransactionData.getWantAssetId(), createOrderTransactionData.getAmount(), createOrderTransactionData.getPrice(),
|
||||
createOrderTransactionData.getTimestamp());
|
||||
|
||||
new Order(this.repository, orderData).process();
|
||||
}
|
||||
|
||||
public void orphan() throws DataException {
|
||||
// TODO
|
||||
CreateOrderTransactionData createOrderTransactionData = (CreateOrderTransactionData) this.transactionData;
|
||||
Account creator = new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey());
|
||||
|
||||
// Update creator's balance due to fee
|
||||
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(createOrderTransactionData.getFee()));
|
||||
|
||||
// Update creator's last reference
|
||||
creator.setLastReference(createOrderTransactionData.getReference());
|
||||
|
||||
// Order Id is transaction's signature
|
||||
byte[] orderId = createOrderTransactionData.getSignature();
|
||||
|
||||
// Orphan the order itself
|
||||
OrderData orderData = this.repository.getAssetRepository().fromOrderId(orderId);
|
||||
new Order(this.repository, orderData).orphan();
|
||||
|
||||
// Delete this transaction
|
||||
this.repository.getTransactionRepository().delete(createOrderTransactionData);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,8 +41,8 @@ public abstract class Transaction {
|
||||
|
||||
// Validation results
|
||||
public enum ValidationResult {
|
||||
OK(1), INVALID_ADDRESS(2), NEGATIVE_AMOUNT(3), NEGATIVE_FEE(4), NO_BALANCE(5), INVALID_REFERENCE(6), INVALID_NAME_LENGTH(7), INVALID_DESCRIPTION_LENGTH(
|
||||
18), INVALID_DATA_LENGTH(27), INVALID_QUANTITY(28), ASSET_DOES_NOT_EXIST(29), ASSET_ALREADY_EXISTS(43), NOT_YET_RELEASED(1000);
|
||||
OK(1), INVALID_ADDRESS(2), NEGATIVE_AMOUNT(3), NEGATIVE_FEE(4), NO_BALANCE(5), INVALID_REFERENCE(6), INVALID_NAME_LENGTH(7), INVALID_AMOUNT(15), INVALID_DESCRIPTION_LENGTH(
|
||||
18), INVALID_DATA_LENGTH(27), INVALID_QUANTITY(28), ASSET_DOES_NOT_EXIST(29), INVALID_RETURN(30), HAVE_EQUALS_WANT(31), NEGATIVE_PRICE(35), ASSET_ALREADY_EXISTS(43), NOT_YET_RELEASED(1000);
|
||||
|
||||
public final int value;
|
||||
|
||||
|
142
src/qora/transaction/TransferAssetTransaction.java
Normal file
142
src/qora/transaction/TransferAssetTransaction.java
Normal file
@ -0,0 +1,142 @@
|
||||
package qora.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
|
||||
import data.assets.AssetData;
|
||||
import data.transaction.TransactionData;
|
||||
import data.transaction.TransferAssetTransactionData;
|
||||
import utils.NTP;
|
||||
import qora.account.Account;
|
||||
import qora.account.PublicKeyAccount;
|
||||
import qora.assets.Asset;
|
||||
import qora.block.Block;
|
||||
import qora.crypto.Crypto;
|
||||
import repository.AssetRepository;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
|
||||
public class TransferAssetTransaction extends Transaction {
|
||||
|
||||
// Constructors
|
||||
|
||||
public TransferAssetTransaction(Repository repository, TransactionData transactionData) {
|
||||
super(repository, transactionData);
|
||||
}
|
||||
|
||||
// Processing
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// Lowest cost checks first
|
||||
|
||||
TransferAssetTransactionData transferAssetTransactionData = (TransferAssetTransactionData) this.transactionData;
|
||||
|
||||
// Are IssueAssetTransactions even allowed at this point?
|
||||
if (NTP.getTime() < Block.ASSETS_RELEASE_TIMESTAMP)
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
// Check recipient address is valid
|
||||
if (!Crypto.isValidAddress(transferAssetTransactionData.getRecipient()))
|
||||
return ValidationResult.INVALID_ADDRESS;
|
||||
|
||||
// Check amount is positive
|
||||
if (transferAssetTransactionData.getAmount().compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
// Check fee is positive
|
||||
if (transferAssetTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
// Check reference is correct
|
||||
PublicKeyAccount sender = new PublicKeyAccount(this.repository, transferAssetTransactionData.getSenderPublicKey());
|
||||
|
||||
if (!Arrays.equals(sender.getLastReference(), transferAssetTransactionData.getReference()))
|
||||
return ValidationResult.INVALID_REFERENCE;
|
||||
|
||||
// Check sender has enough asset balance AFTER removing fee, in case asset is QORA
|
||||
long assetId = transferAssetTransactionData.getAssetId();
|
||||
// If asset is QORA then we need to check amount + fee in one go
|
||||
if (assetId == Asset.QORA) {
|
||||
// Check sender has enough funds for amount + fee in QORA
|
||||
if (sender.getConfirmedBalance(Asset.QORA).compareTo(transferAssetTransactionData.getAmount().add(transferAssetTransactionData.getFee())) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
} else {
|
||||
// Check sender has enough funds for amount in whatever asset
|
||||
if (sender.getConfirmedBalance(assetId).compareTo(transferAssetTransactionData.getAmount()) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
// Check sender has enough funds for fee in QORA
|
||||
// NOTE: in Gen1 pre-POWFIX-RELEASE transactions didn't have this check
|
||||
if (transferAssetTransactionData.getTimestamp() >= Block.POWFIX_RELEASE_TIMESTAMP
|
||||
&& sender.getConfirmedBalance(Asset.QORA).compareTo(transferAssetTransactionData.getFee()) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
}
|
||||
|
||||
// Check asset amount is integer if asset is not divisible
|
||||
AssetRepository assetRepository = this.repository.getAssetRepository();
|
||||
AssetData assetData = assetRepository.fromAssetId(assetId);
|
||||
if (!assetData.getIsDivisible() && transferAssetTransactionData.getAmount().stripTrailingZeros().scale() > 0)
|
||||
return ValidationResult.INVALID_AMOUNT;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
// PROCESS/ORPHAN
|
||||
|
||||
@Override
|
||||
public void process() throws DataException {
|
||||
TransferAssetTransactionData transferAssetTransactionData = (TransferAssetTransactionData) this.transactionData;
|
||||
long assetId = transferAssetTransactionData.getAssetId();
|
||||
|
||||
// Save this transaction itself
|
||||
this.repository.getTransactionRepository().save(this.transactionData);
|
||||
|
||||
// Update sender's balance due to amount
|
||||
Account sender = new PublicKeyAccount(this.repository, transferAssetTransactionData.getSenderPublicKey());
|
||||
sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId).subtract(transferAssetTransactionData.getAmount()));
|
||||
// Update sender's balance due to fee
|
||||
sender.setConfirmedBalance(Asset.QORA, sender.getConfirmedBalance(Asset.QORA).subtract(transferAssetTransactionData.getFee()));
|
||||
|
||||
// Update recipient's balance
|
||||
Account recipient = new Account(this.repository, transferAssetTransactionData.getRecipient());
|
||||
recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).add(transferAssetTransactionData.getAmount()));
|
||||
|
||||
// Update sender's reference
|
||||
sender.setLastReference(transferAssetTransactionData.getSignature());
|
||||
|
||||
// For QORA amounts only: if recipient has no reference yet, then this is their starting reference
|
||||
if (assetId == Asset.QORA && recipient.getLastReference() == null)
|
||||
recipient.setLastReference(transferAssetTransactionData.getSignature());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orphan() throws DataException {
|
||||
TransferAssetTransactionData transferAssetTransactionData = (TransferAssetTransactionData) this.transactionData;
|
||||
long assetId = transferAssetTransactionData.getAssetId();
|
||||
|
||||
// Delete this transaction itself
|
||||
this.repository.getTransactionRepository().delete(this.transactionData);
|
||||
|
||||
// Update sender's balance due to amount
|
||||
Account sender = new PublicKeyAccount(this.repository, transferAssetTransactionData.getSenderPublicKey());
|
||||
sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId).add(transferAssetTransactionData.getAmount()));
|
||||
// Update sender's balance due to fee
|
||||
sender.setConfirmedBalance(Asset.QORA, sender.getConfirmedBalance(Asset.QORA).add(transferAssetTransactionData.getFee()));
|
||||
|
||||
// Update recipient's balance
|
||||
Account recipient = new Account(this.repository, transferAssetTransactionData.getRecipient());
|
||||
recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).subtract(transferAssetTransactionData.getAmount()));
|
||||
|
||||
// Update sender's reference
|
||||
sender.setLastReference(transferAssetTransactionData.getReference());
|
||||
|
||||
/*
|
||||
* For QORA amounts only: If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own (which
|
||||
* would have changed their last reference) thus this is their first reference so remove it.
|
||||
*/
|
||||
if (assetId == Asset.QORA && Arrays.equals(recipient.getLastReference(), transferAssetTransactionData.getSignature()))
|
||||
recipient.setLastReference(null);
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package repository;
|
||||
|
||||
import data.assets.AssetData;
|
||||
import data.assets.OrderData;
|
||||
|
||||
public interface AssetRepository {
|
||||
|
||||
// Assets
|
||||
|
||||
public AssetData fromAssetId(long assetId) throws DataException;
|
||||
|
||||
public boolean assetExists(long assetId) throws DataException;
|
||||
@ -14,4 +17,8 @@ public interface AssetRepository {
|
||||
|
||||
public void delete(long assetId) throws DataException;
|
||||
|
||||
// Orders
|
||||
|
||||
public OrderData fromOrderId(byte[] orderId) throws DataException;
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package repository.hsqldb;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import data.assets.AssetData;
|
||||
import data.assets.OrderData;
|
||||
import repository.AssetRepository;
|
||||
import repository.DataException;
|
||||
|
||||
@ -15,6 +17,8 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
// Assets
|
||||
|
||||
public AssetData fromAssetId(long assetId) throws DataException {
|
||||
try {
|
||||
ResultSet resultSet = this.repository
|
||||
@ -75,4 +79,27 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
}
|
||||
}
|
||||
|
||||
// Orders
|
||||
|
||||
public OrderData fromOrderId(byte[] orderId) throws DataException {
|
||||
try {
|
||||
ResultSet resultSet = this.repository.checkedExecute(
|
||||
"SELECT creator, have_asset_id, want_asset_id, amount, fulfilled, price, timestamp FROM AssetOrders WHERE asset_order_id = ?", orderId);
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
byte[] creatorPublicKey = this.repository.getResultSetBytes(resultSet.getBinaryStream(1));
|
||||
long haveAssetId = resultSet.getLong(2);
|
||||
long wantAssetId = resultSet.getLong(3);
|
||||
BigDecimal amount = resultSet.getBigDecimal(4);
|
||||
BigDecimal fulfilled = resultSet.getBigDecimal(5);
|
||||
BigDecimal price = resultSet.getBigDecimal(6);
|
||||
long timestamp = resultSet.getTimestamp(7).getTime();
|
||||
|
||||
return new OrderData(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, fulfilled, price, timestamp);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch order from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public class HSQLDBDatabaseUpdates {
|
||||
stmt.execute("CREATE TYPE DataHash AS VARCHAR(100)");
|
||||
stmt.execute("CREATE TYPE AssetID AS BIGINT");
|
||||
stmt.execute("CREATE TYPE AssetName AS VARCHAR(400) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE AssetOrderID AS VARCHAR(100)");
|
||||
stmt.execute("CREATE TYPE AssetOrderID AS VARBINARY(64)");
|
||||
stmt.execute("CREATE TYPE ATName AS VARCHAR(200) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE ATType AS VARCHAR(200) COLLATE SQL_TEXT_UCC");
|
||||
break;
|
||||
@ -227,7 +227,7 @@ public class HSQLDBDatabaseUpdates {
|
||||
case 15:
|
||||
// Transfer Asset Transactions
|
||||
stmt.execute("CREATE TABLE TransferAssetTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||
+ "asset_id AssetID NOT NULL, amount QoraAmount NOT NULL, "
|
||||
+ "asset_id AssetID NOT NULL, amount QoraAmount NOT NULL,"
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
@ -241,7 +241,7 @@ public class HSQLDBDatabaseUpdates {
|
||||
case 17:
|
||||
// Cancel Asset Order Transactions
|
||||
stmt.execute("CREATE TABLE CancelAssetOrderTransactions (signature Signature, creator QoraPublicKey NOT NULL, "
|
||||
+ "asset_order AssetOrderID NOT NULL, "
|
||||
+ "asset_order_id AssetOrderID NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
@ -282,6 +282,16 @@ public class HSQLDBDatabaseUpdates {
|
||||
"CREATE TABLE AccountBalances (account QoraAddress, asset_id AssetID, balance QoraAmount NOT NULL, PRIMARY KEY (account, asset_id))");
|
||||
break;
|
||||
|
||||
case 23:
|
||||
// Asset Orders
|
||||
stmt.execute(
|
||||
"CREATE TABLE AssetOrders (asset_order_id AssetOrderID, creator QoraPublicKey NOT NULL, have_asset_id AssetID NOT NULL, want_asset_id AssetID NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, fulfilled QoraAmount NOT NULL, price QoraAmount NOT NULL, ordered TIMESTAMP NOT NULL, "
|
||||
+ "PRIMARY KEY (asset_order_id))");
|
||||
stmt.execute("CREATE INDEX AssetOrderHaveIndex on AssetOrders (have_asset_id)");
|
||||
stmt.execute("CREATE INDEX AssetOrderWantIndex on AssetOrders (want_asset_id)");
|
||||
break;
|
||||
|
||||
default:
|
||||
// nothing to do
|
||||
return false;
|
||||
|
@ -18,8 +18,8 @@ public class HSQLDBCreateOrderTransactionRepository extends HSQLDBTransactionRep
|
||||
|
||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||
try {
|
||||
ResultSet rs = this.repository.checkedExecute("SELECT have_asset_id, amount, want_asset_id, price FROM CreateOrderTransactions WHERE signature = ?",
|
||||
signature);
|
||||
ResultSet rs = this.repository.checkedExecute(
|
||||
"SELECT have_asset_id, amount, want_asset_id, price FROM CreateAssetOrderTransactions WHERE signature = ?", signature);
|
||||
if (rs == null)
|
||||
return null;
|
||||
|
||||
@ -41,7 +41,8 @@ public class HSQLDBCreateOrderTransactionRepository extends HSQLDBTransactionRep
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("CreateAssetOrderTransactions");
|
||||
|
||||
saveHelper.bind("signature", createOrderTransactionData.getSignature()).bind("creator", createOrderTransactionData.getCreatorPublicKey())
|
||||
.bind("have_asset_id", createOrderTransactionData.getHaveAssetId()).bind("amount", createOrderTransactionData.getAmount());
|
||||
.bind("have_asset_id", createOrderTransactionData.getHaveAssetId()).bind("amount", createOrderTransactionData.getAmount())
|
||||
.bind("want_asset_id", createOrderTransactionData.getWantAssetId()).bind("price", createOrderTransactionData.getPrice());
|
||||
|
||||
try {
|
||||
saveHelper.execute(this.repository);
|
||||
|
@ -237,7 +237,7 @@ public class BlockTransformer extends Transformer {
|
||||
List<Trade> trades = order.getTrades();
|
||||
|
||||
// Filter out trades with timestamps that don't match order transaction's timestamp
|
||||
trades.removeIf((Trade trade) -> trade.getTimestamp() != order.getTimestamp());
|
||||
trades.removeIf((Trade trade) -> trade.getTimestamp() != order.getOrderData().getTimestamp());
|
||||
|
||||
// Any trades left?
|
||||
if (!trades.isEmpty()) {
|
||||
|
@ -0,0 +1,107 @@
|
||||
package transform.transaction;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
import data.transaction.TransactionData;
|
||||
import data.transaction.TransferAssetTransactionData;
|
||||
import qora.account.PublicKeyAccount;
|
||||
import transform.TransformationException;
|
||||
import utils.Base58;
|
||||
import utils.Serialization;
|
||||
|
||||
public class TransferAssetTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
// Property lengths
|
||||
private static final int SENDER_LENGTH = PUBLIC_KEY_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 TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + SENDER_LENGTH + RECIPIENT_LENGTH + ASSET_ID_LENGTH + AMOUNT_LENGTH;
|
||||
|
||||
static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
||||
if (byteBuffer.remaining() < TYPELESS_LENGTH)
|
||||
throw new TransformationException("Byte data too short for TransferAssetTransaction");
|
||||
|
||||
long timestamp = byteBuffer.getLong();
|
||||
|
||||
byte[] reference = new byte[REFERENCE_LENGTH];
|
||||
byteBuffer.get(reference);
|
||||
|
||||
byte[] senderPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||
String recipient = Serialization.deserializeRecipient(byteBuffer);
|
||||
long assetId = byteBuffer.getLong();
|
||||
BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer, AMOUNT_LENGTH);
|
||||
|
||||
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
||||
|
||||
byte[] signature = new byte[SIGNATURE_LENGTH];
|
||||
byteBuffer.get(signature);
|
||||
|
||||
return new TransferAssetTransactionData(senderPublicKey, recipient, amount, assetId, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||
return TYPE_LENGTH + TYPELESS_LENGTH;
|
||||
}
|
||||
|
||||
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
|
||||
try {
|
||||
TransferAssetTransactionData transferAssetTransactionData = (TransferAssetTransactionData) transactionData;
|
||||
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
|
||||
bytes.write(Ints.toByteArray(transferAssetTransactionData.getType().value));
|
||||
bytes.write(Longs.toByteArray(transferAssetTransactionData.getTimestamp()));
|
||||
bytes.write(transferAssetTransactionData.getReference());
|
||||
|
||||
bytes.write(transferAssetTransactionData.getSenderPublicKey());
|
||||
bytes.write(Base58.decode(transferAssetTransactionData.getRecipient()));
|
||||
bytes.write(Longs.toByteArray(transferAssetTransactionData.getAssetId()));
|
||||
Serialization.serializeBigDecimal(bytes, transferAssetTransactionData.getAmount(), AMOUNT_LENGTH);
|
||||
|
||||
Serialization.serializeBigDecimal(bytes, transferAssetTransactionData.getFee());
|
||||
bytes.write(transferAssetTransactionData.getSignature());
|
||||
|
||||
return bytes.toByteArray();
|
||||
} catch (IOException | ClassCastException e) {
|
||||
throw new TransformationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
|
||||
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
|
||||
|
||||
try {
|
||||
TransferAssetTransactionData transferAssetTransactionData = (TransferAssetTransactionData) transactionData;
|
||||
|
||||
byte[] senderPublicKey = transferAssetTransactionData.getSenderPublicKey();
|
||||
|
||||
json.put("sender", PublicKeyAccount.getAddress(senderPublicKey));
|
||||
json.put("senderPublicKey", HashCode.fromBytes(senderPublicKey).toString());
|
||||
json.put("recipient", transferAssetTransactionData.getRecipient());
|
||||
|
||||
// For gen1 support:
|
||||
json.put("asset", transferAssetTransactionData.getAssetId());
|
||||
// Gen2 version:
|
||||
json.put("assetId", transferAssetTransactionData.getAssetId());
|
||||
|
||||
json.put("amount", transferAssetTransactionData.getAmount().toPlainString());
|
||||
} catch (ClassCastException e) {
|
||||
throw new TransformationException(e);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user