Interim commit of *Transaction refactoring

Transaction subclass isValid methods have last reference checks removed.
(Now handled by Transaction.hasValidReference)

Some checks in subclass' isValid moved to isProcessable, typically
for transaction types that require group-approval.

Removed fee extraction and last-reference update code from
subclass' process() method to Transaction.processReferencesAndFees.

Other changes to transaction/block processing.
This commit is contained in:
catbref 2019-06-01 10:26:20 +01:00
parent c9968b3dd2
commit 9bb673ba82
44 changed files with 394 additions and 433 deletions

View File

@ -32,7 +32,6 @@ import org.qora.repository.BlockRepository;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.transaction.AtTransaction;
import org.qora.transaction.GenesisTransaction;
import org.qora.transaction.Transaction;
import org.qora.transaction.Transaction.ApprovalStatus;
import org.qora.transaction.Transaction.TransactionType;
@ -896,33 +895,55 @@ public class Block {
repository.setSavepoint();
for (Transaction transaction : this.getTransactions()) {
TransactionData transactionData = transaction.getTransactionData();
// GenesisTransactions are not allowed (GenesisBlock overrides isValid() to allow them)
if (transaction instanceof GenesisTransaction)
if (transactionData.getType() == TransactionType.GENESIS || transactionData.getType() == TransactionType.ACCOUNT_FLAGS)
return ValidationResult.GENESIS_TRANSACTIONS_INVALID;
// Check timestamp and deadline
if (transaction.getTransactionData().getTimestamp() > this.blockData.getTimestamp()
if (transactionData.getTimestamp() > this.blockData.getTimestamp()
|| transaction.getDeadline() <= this.blockData.getTimestamp())
return ValidationResult.TRANSACTION_TIMESTAMP_INVALID;
// Check transaction isn't already included in a block
if (this.repository.getTransactionRepository().isConfirmed(transaction.getTransactionData().getSignature()))
if (this.repository.getTransactionRepository().isConfirmed(transactionData.getSignature()))
return ValidationResult.TRANSACTION_ALREADY_PROCESSED;
// Check transaction has correct reference, etc.
if (!transaction.hasValidReference()) {
LOGGER.debug("Error during transaction validation, tx " + Base58.encode(transactionData.getSignature()) + ": INVALID_REFERENCE");
return ValidationResult.TRANSACTION_INVALID;
}
// Check transaction is even valid
// NOTE: in Gen1 there was an extra block height passed to DeployATTransaction.isValid
Transaction.ValidationResult validationResult = transaction.isValid();
if (validationResult != Transaction.ValidationResult.OK) {
LOGGER.debug("Error during transaction validation, tx " + Base58.encode(transaction.getTransactionData().getSignature()) + ": "
LOGGER.debug("Error during transaction validation, tx " + Base58.encode(transactionData.getSignature()) + ": "
+ validationResult.name());
return ValidationResult.TRANSACTION_INVALID;
}
// Check transaction can even be processed
validationResult = transaction.isProcessable();
if (validationResult != Transaction.ValidationResult.OK) {
LOGGER.debug("Error during transaction validation, tx " + Base58.encode(transactionData.getSignature()) + ": "
+ validationResult.name());
return ValidationResult.TRANSACTION_INVALID;
}
// Process transaction to make sure other transactions validate properly
try {
transaction.process();
// Only process transactions that don't require group-approval.
// Group-approval transactions are dealt with later.
if (transactionData.getApprovalStatus() == ApprovalStatus.NOT_REQUIRED)
transaction.process();
// Regardless of group-approval, update relevant info for creator (e.g. lastReference)
transaction.processReferencesAndFees();
} catch (Exception e) {
LOGGER.error("Exception during transaction validation, tx " + Base58.encode(transaction.getTransactionData().getSignature()), e);
LOGGER.error("Exception during transaction validation, tx " + Base58.encode(transactionData.getSignature()), e);
e.printStackTrace();
return ValidationResult.TRANSACTION_PROCESSING_FAILED;
}
@ -1056,7 +1077,7 @@ public class Block {
transaction.process();
// Regardless of group-approval, update relevant info for creator (e.g. lastReference)
transaction.processCreatorUpdates();
transaction.processReferencesAndFees();
}
// Group-approval transactions
@ -1133,15 +1154,15 @@ public class Block {
continue;
}
// Approved, but check transaction is still valid
if (transaction.isValid() != Transaction.ValidationResult.OK) {
// Approved, but check transaction can still be processed
if (transaction.isProcessable() != Transaction.ValidationResult.OK) {
transactionData.setApprovalStatus(ApprovalStatus.INVALID);
this.repository.getTransactionRepository().save(transactionData);
continue;
}
// APPROVED, in which case do transaction.process();
transactionData.setApprovalStatus(ApprovalStatus.INVALID);
transactionData.setApprovalStatus(ApprovalStatus.APPROVED);
this.repository.getTransactionRepository().save(transactionData);
transaction.process();

View File

@ -242,15 +242,15 @@ public class BlockGenerator extends Thread {
}
}
// Sign to create block's signature, needed by Block.isValid()
newBlock.sign();
// Attempt to add transactions until block is full, or we run out
// If a transaction makes the block invalid then skip it and it'll either expire or be in next block.
for (TransactionData transactionData : unconfirmedTransactions) {
if (!newBlock.addTransaction(transactionData))
break;
// Sign to create block's signature
newBlock.sign();
// If newBlock is no longer valid then we can't use transaction
ValidationResult validationResult = newBlock.isValid();
if (validationResult != ValidationResult.OK) {

View File

@ -26,6 +26,7 @@ import org.qora.group.Group;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.transaction.Transaction;
import org.qora.transaction.Transaction.ApprovalStatus;
import org.qora.transaction.Transaction.TransactionType;
import org.qora.transform.TransformationException;
import org.qora.transform.transaction.TransactionTransformer;
@ -300,8 +301,11 @@ public class GenesisBlock extends Block {
TransactionData transactionData = transaction.getTransactionData();
Account creator = new PublicKeyAccount(this.repository, transactionData.getCreatorPublicKey());
// Missing reference?
if (transactionData.getReference() == null)
transactionData.setReference(creator.getLastReference());
// Missing signature?
if (transactionData.getSignature() == null) {
byte[] digest = Crypto.digest(TransactionTransformer.toBytesForSigning(transactionData));
byte[] signature = Bytes.concat(digest, digest);
@ -309,7 +313,12 @@ public class GenesisBlock extends Block {
transactionData.setSignature(signature);
}
transaction.process();
// Missing approval status (not used in V1)
transactionData.setApprovalStatus(ApprovalStatus.NOT_REQUIRED);
// Ask transaction to update references, etc.
transaction.processReferencesAndFees();
creator.setLastReference(transactionData.getSignature());
}
} catch (TransformationException e) {

View File

@ -33,7 +33,7 @@ public class Payment {
// Processing
// Validate multiple payments
/** Are payments valid? */
public ValidationResult isValid(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, boolean isZeroAmountValid) throws DataException {
AssetRepository assetRepository = this.repository.getAssetRepository();
@ -100,20 +100,70 @@ public class Payment {
return ValidationResult.OK;
}
/** Are payments valid? */
public ValidationResult isValid(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee) throws DataException {
return isValid(senderPublicKey, payments, fee, false);
}
// Single payment forms
/** Is single payment valid? */
public ValidationResult isValid(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, boolean isZeroAmountValid) throws DataException {
return isValid(senderPublicKey, Collections.singletonList(paymentData), fee, isZeroAmountValid);
}
/** Is single payment valid? */
public ValidationResult isValid(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee) throws DataException {
return isValid(senderPublicKey, paymentData, fee, false);
}
public void process(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, byte[] signature, boolean alwaysInitializeRecipientReference)
/** Are multiple payments processable? */
public ValidationResult isProcessable(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, boolean isZeroAmountValid) throws DataException {
// Essentially the same as isValid...
return isValid(senderPublicKey, payments, fee, isZeroAmountValid);
}
/** Are multiple payments processable? */
public ValidationResult isProcessable(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee) throws DataException {
return isProcessable(senderPublicKey, payments, fee, false);
}
/** Is single payment processable? */
public ValidationResult isProcessable(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, boolean isZeroAmountValid) throws DataException {
return isProcessable(senderPublicKey, Collections.singletonList(paymentData), fee, isZeroAmountValid);
}
/** Is single payment processable? */
public ValidationResult isProcessable(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee) throws DataException {
return isProcessable(senderPublicKey, paymentData, fee, false);
}
/** Multiple payment processing */
public void process(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, byte[] signature)
throws DataException {
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
// Process all payments
for (PaymentData paymentData : payments) {
Account recipient = new Account(this.repository, paymentData.getRecipient());
long assetId = paymentData.getAssetId();
BigDecimal amount = paymentData.getAmount();
// Update sender's balance due to amount
sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId).subtract(amount));
// Update recipient's balance
recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).add(amount));
}
}
/** Single payment processing */
public void process(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature)
throws DataException {
process(senderPublicKey, Collections.singletonList(paymentData), fee, signature);
}
/** Multiple payment reference processing */
public void processReferencesAndFees(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, byte[] signature, boolean alwaysInitializeRecipientReference)
throws DataException {
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
@ -128,13 +178,6 @@ public class Payment {
Account recipient = new Account(this.repository, paymentData.getRecipient());
long assetId = paymentData.getAssetId();
BigDecimal amount = paymentData.getAmount();
// Update sender's balance due to amount
sender.setConfirmedBalance(assetId, sender.getConfirmedBalance(assetId).subtract(amount));
// Update recipient's balance
recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).add(amount));
// For QORA amounts only: if recipient has no reference yet, then this is their starting reference
if ((alwaysInitializeRecipientReference || assetId == Asset.QORA) && recipient.getLastReference() == null)
@ -142,9 +185,10 @@ public class Payment {
}
}
public void process(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature, boolean alwaysInitializeRecipientReference)
/** Multiple payment reference processing */
public void processReferencesAndFees(byte[] senderPublicKey, PaymentData payment, BigDecimal fee, byte[] signature, boolean alwaysInitializeRecipientReference)
throws DataException {
process(senderPublicKey, Collections.singletonList(paymentData), fee, signature, alwaysInitializeRecipientReference);
processReferencesAndFees(senderPublicKey, Collections.singletonList(payment), fee, signature, alwaysInitializeRecipientReference);
}
public void orphan(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, byte[] signature, byte[] reference,

View File

@ -12,6 +12,9 @@ public interface ATRepository {
/** Returns ATData using AT's address or null if none found */
public ATData fromATAddress(String atAddress) throws DataException;
/** Returns where AT with passed address exists in repository */
public boolean exists(String atAddress) throws DataException;
/** Returns list of executable ATs, empty if none found */
public List<ATData> getAllExecutableATs() throws DataException;

View File

@ -45,6 +45,10 @@ public interface GroupRepository {
public void delete(String groupName) throws DataException;
// Group Owner
public String getOwner(int groupId) throws DataException;
// Group Admins
public GroupAdminData getAdmin(int groupId, String address) throws DataException;

View File

@ -57,6 +57,15 @@ public class HSQLDBATRepository implements ATRepository {
}
}
@Override
public boolean exists(String atAddress) throws DataException {
try {
return this.repository.exists("ATs", "AT_address = ?", atAddress);
} catch (SQLException e) {
throw new DataException("Unable to check for AT in repository", e);
}
}
@Override
public List<ATData> getAllExecutableATs() throws DataException {
List<ATData> executableATs = new ArrayList<ATData>();

View File

@ -289,6 +289,22 @@ public class HSQLDBGroupRepository implements GroupRepository {
}
}
// Group Owner
@Override
public String getOwner(int groupId) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT owner FROM Groups WHERE group_id = ?", groupId)) {
if (resultSet == null)
return null;
String owner = resultSet.getString(1);
return owner;
} catch (SQLException e) {
throw new DataException("Unable to fetch group owner from repository", e);
}
}
// Group Admins
@Override

View File

@ -657,7 +657,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
List<TransactionData> transactions = new ArrayList<TransactionData>();
try (ResultSet resultSet = this.repository.checkedExecute(sql, ApprovalStatus.PENDING, blockHeight)) {
try (ResultSet resultSet = this.repository.checkedExecute(sql, ApprovalStatus.PENDING.value, blockHeight)) {
if (resultSet == null)
return transactions;
@ -688,7 +688,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
List<TransactionData> transactions = new ArrayList<TransactionData>();
try (ResultSet resultSet = this.repository.checkedExecute(sql, ApprovalStatus.PENDING, blockHeight)) {
try (ResultSet resultSet = this.repository.checkedExecute(sql, ApprovalStatus.PENDING.value, blockHeight)) {
if (resultSet == null)
return transactions;
@ -861,7 +861,8 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
saver.bind("signature", transactionData.getSignature()).bind("reference", transactionData.getReference())
.bind("type", transactionData.getType().value)
.bind("creator", transactionData.getCreatorPublicKey()).bind("creation", new Timestamp(transactionData.getTimestamp()))
.bind("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);
try {
saver.execute(this.repository);

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -77,10 +76,6 @@ public class AccountFlagsTransaction extends Transaction {
if (accountFlagsTransactionData.getFee().compareTo(BigDecimal.ZERO) < 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(creator.getLastReference(), accountFlagsTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (creator.getConfirmedBalance(Asset.QORA).compareTo(accountFlagsTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -107,13 +102,6 @@ public class AccountFlagsTransaction extends Transaction {
| accountFlagsTransactionData.getOrMask() ^ accountFlagsTransactionData.getXorMask();
target.setFlags(newFlags);
// Update creator's balance
Account creator = getCreator();
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(accountFlagsTransactionData.getFee()));
// Update creator's reference
creator.setLastReference(accountFlagsTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -10,7 +9,6 @@ import org.qora.account.PublicKeyAccount;
import org.qora.asset.Asset;
import org.qora.crypto.Crypto;
import org.qora.data.transaction.AddGroupAdminTransactionData;
import org.qora.data.group.GroupData;
import org.qora.data.transaction.TransactionData;
import org.qora.group.Group;
import org.qora.repository.DataException;
@ -78,16 +76,19 @@ public class AddGroupAdminTransaction extends Transaction {
if (!Crypto.isValidAddress(addGroupAdminTransactionData.getMember()))
return ValidationResult.INVALID_ADDRESS;
GroupData groupData = this.repository.getGroupRepository().fromGroupId(addGroupAdminTransactionData.getGroupId());
// Check group exists
if (groupData == null)
if (!this.repository.getGroupRepository().groupExists(addGroupAdminTransactionData.getGroupId()))
return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check fee is positive
if (addGroupAdminTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
Account owner = getOwner();
String groupOwner = this.repository.getGroupRepository().getOwner(addGroupAdminTransactionData.getGroupId());
// Check transaction's public key matches group's current owner
if (!owner.getAddress().equals(groupData.getOwner()))
if (!owner.getAddress().equals(groupOwner))
return ValidationResult.INVALID_GROUP_OWNER;
Account member = getMember();
@ -100,15 +101,7 @@ public class AddGroupAdminTransaction extends Transaction {
if (this.repository.getGroupRepository().adminExists(addGroupAdminTransactionData.getGroupId(), member.getAddress()))
return ValidationResult.ALREADY_GROUP_ADMIN;
// Check fee is positive
if (addGroupAdminTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(owner.getLastReference(), addGroupAdminTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
// Check group owner has enough funds
if (owner.getConfirmedBalance(Asset.QORA).compareTo(addGroupAdminTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -122,13 +115,6 @@ public class AddGroupAdminTransaction extends Transaction {
group.promoteToAdmin(addGroupAdminTransactionData);
// We would save updated transaction at this point, but it hasn't been modified
// Update owner's balance
Account owner = getOwner();
owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).subtract(addGroupAdminTransactionData.getFee()));
// Update owner's reference
owner.setLastReference(addGroupAdminTransactionData.getSignature());
}
@Override

View File

@ -2,7 +2,6 @@ package org.qora.transaction;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.qora.account.Account;
@ -103,22 +102,31 @@ public class ArbitraryTransaction extends Transaction {
if (arbitraryTransactionData.getData().length < 1 || arbitraryTransactionData.getData().length > MAX_DATA_SIZE)
return ValidationResult.INVALID_DATA_LENGTH;
// Check reference is correct
Account sender = getSender();
if (!Arrays.equals(sender.getLastReference(), arbitraryTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Wrap and delegate final payment checks to Payment class
// Wrap and delegate final payment validity checks to Payment class
return new Payment(this.repository).isValid(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
arbitraryTransactionData.getFee());
}
@Override
public ValidationResult isProcessable() throws DataException {
// Wrap and delegate final payment processable checks to Payment class
return new Payment(this.repository).isProcessable(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
arbitraryTransactionData.getFee());
}
@Override
public void process() throws DataException {
// We would save updated transaction at this point, but it hasn't been modified
// Wrap and delegate payment processing to Payment class. Always update recipients' last references regardless of asset.
// Wrap and delegate payment processing to Payment class.
new Payment(this.repository).process(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature());
}
@Override
public void processReferencesAndFees() throws DataException {
// Wrap and delegate reference and fee processing to Payment class. Always update recipients' last references regardless of asset.
new Payment(this.repository).processReferencesAndFees(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature(), true);
}

View File

@ -108,12 +108,14 @@ public class AtTransaction extends Transaction {
// Processing
@Override
public ValidationResult isValid() throws DataException {
public boolean hasValidReference() throws DataException {
// Check reference is correct
Account atAccount = getATAccount();
if (!Arrays.equals(atAccount.getLastReference(), atTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
return Arrays.equals(atAccount.getLastReference(), atTransactionData.getReference());
}
@Override
public ValidationResult isValid() throws DataException {
if (this.atTransactionData.getMessage().length > MAX_DATA_SIZE)
return ValidationResult.INVALID_DATA_LENGTH;
@ -173,6 +175,14 @@ public class AtTransaction extends Transaction {
// Update recipient's balance
recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).add(amount));
}
}
@Override
public void processReferencesAndFees() throws DataException {
if (this.atTransactionData.getAmount() != null) {
Account recipient = getRecipient();
long assetId = this.atTransactionData.getAssetId();
// For QORA amounts only: if recipient has no reference yet, then this is their starting reference
if (assetId == Asset.QORA && recipient.getLastReference() == null)

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -110,10 +109,6 @@ public class BuyNameTransaction extends Transaction {
if (buyNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference is correct
if (!Arrays.equals(buyer.getLastReference(), buyNameTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check issuer has enough funds
if (buyer.getConfirmedBalance(Asset.QORA).compareTo(buyNameTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -129,13 +124,6 @@ public class BuyNameTransaction extends Transaction {
// Save transaction with updated "name reference" pointing to previous transaction that updated name
this.repository.getTransactionRepository().save(buyNameTransactionData);
// Update buyer's balance
Account buyer = getBuyer();
buyer.setConfirmedBalance(Asset.QORA, buyer.getConfirmedBalance(Asset.QORA).subtract(buyNameTransactionData.getFee()));
// Update buyer's reference
buyer.setLastReference(buyNameTransactionData.getSignature());
}
@Override

View File

@ -2,7 +2,6 @@ package org.qora.transaction;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.qora.account.Account;
@ -90,25 +89,13 @@ public class CancelAssetOrderTransaction extends Transaction {
if (creator.getConfirmedBalance(Asset.QORA).compareTo(cancelOrderTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
// Check reference is correct
if (!Arrays.equals(creator.getLastReference(), cancelOrderTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
return ValidationResult.OK;
}
@Override
public void process() throws DataException {
Account creator = getCreator();
// We would save updated transaction at this point, but it hasn't been modified
// Update creator's balance regarding fee
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(cancelOrderTransactionData.getFee()));
// Update creator's last reference
creator.setLastReference(cancelOrderTransactionData.getSignature());
// Mark Order as completed so no more trades can happen
OrderData orderData = this.repository.getAssetRepository().fromOrderId(cancelOrderTransactionData.getOrderId());
Order order = new Order(this.repository, orderData);

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -100,10 +99,6 @@ public class CancelGroupBanTransaction extends Transaction {
if (groupUnbanTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(admin.getLastReference(), groupUnbanTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (admin.getConfirmedBalance(Asset.QORA).compareTo(groupUnbanTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -119,13 +114,6 @@ public class CancelGroupBanTransaction extends Transaction {
// Save this transaction with updated member/admin references to transactions that can help restore state
this.repository.getTransactionRepository().save(groupUnbanTransactionData);
// Update admin's balance
Account admin = getAdmin();
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).subtract(groupUnbanTransactionData.getFee()));
// Update admin's reference
admin.setLastReference(groupUnbanTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -100,10 +99,6 @@ public class CancelGroupInviteTransaction extends Transaction {
if (cancelGroupInviteTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(admin.getLastReference(), cancelGroupInviteTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (admin.getConfirmedBalance(Asset.QORA).compareTo(cancelGroupInviteTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -119,13 +114,6 @@ public class CancelGroupInviteTransaction extends Transaction {
// Save this transaction with updated member/admin references to transactions that can help restore state
this.repository.getTransactionRepository().save(cancelGroupInviteTransactionData);
// Update admin's balance
Account admin = getAdmin();
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).subtract(cancelGroupInviteTransactionData.getFee()));
// Update admin's reference
admin.setLastReference(cancelGroupInviteTransactionData.getSignature());
}
@Override

View File

@ -2,7 +2,6 @@ package org.qora.transaction;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.qora.account.Account;
@ -96,10 +95,6 @@ public class CancelSellNameTransaction extends Transaction {
if (cancelSellNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference is correct
if (!Arrays.equals(owner.getLastReference(), cancelSellNameTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check issuer has enough funds
if (owner.getConfirmedBalance(Asset.QORA).compareTo(cancelSellNameTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -116,13 +111,6 @@ public class CancelSellNameTransaction extends Transaction {
// Save this transaction, with updated "name reference" to previous transaction that updated name
this.repository.getTransactionRepository().save(cancelSellNameTransactionData);
// Update owner's balance
Account owner = getOwner();
owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).subtract(cancelSellNameTransactionData.getFee()));
// Update owner's reference
owner.setLastReference(cancelSellNameTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -92,20 +91,12 @@ public class CreateGroupTransaction extends Transaction {
if (!createGroupTransactionData.getGroupName().equals(createGroupTransactionData.getGroupName().toLowerCase()))
return ValidationResult.NAME_NOT_LOWER_CASE;
// Check the group name isn't already taken
if (this.repository.getGroupRepository().groupExists(createGroupTransactionData.getGroupName()))
return ValidationResult.GROUP_ALREADY_EXISTS;
// Check fee is positive
if (createGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
Account creator = getCreator();
// Check reference is correct
if (!Arrays.equals(creator.getLastReference(), createGroupTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (creator.getConfirmedBalance(Asset.QORA).compareTo(createGroupTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -113,6 +104,15 @@ public class CreateGroupTransaction extends Transaction {
return ValidationResult.OK;
}
@Override
public ValidationResult isProcessable() throws DataException {
// Check the group name isn't already taken
if (this.repository.getGroupRepository().groupExists(createGroupTransactionData.getGroupName()))
return ValidationResult.GROUP_ALREADY_EXISTS;
return ValidationResult.OK;
}
@Override
public void process() throws DataException {
// Create Group
@ -124,13 +124,6 @@ public class CreateGroupTransaction extends Transaction {
// Save this transaction
this.repository.getTransactionRepository().save(createGroupTransactionData);
// Update creator's balance
Account creator = getCreator();
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(createGroupTransactionData.getFee()));
// Update creator's reference
creator.setLastReference(createGroupTransactionData.getSignature());
}
@Override

View File

@ -2,7 +2,6 @@ package org.qora.transaction;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -102,10 +101,6 @@ public class CreatePollTransaction extends Transaction {
if (!createPollTransactionData.getPollName().equals(createPollTransactionData.getPollName().toLowerCase()))
return ValidationResult.NAME_NOT_LOWER_CASE;
// Check the poll name isn't already taken
if (this.repository.getVotingRepository().pollExists(createPollTransactionData.getPollName()))
return ValidationResult.POLL_ALREADY_EXISTS;
// In gen1 we tested for presence of existing votes but how could there be any if poll doesn't exist?
// Check number of options
@ -137,9 +132,6 @@ public class CreatePollTransaction extends Transaction {
// Check reference is correct
Account creator = getCreator();
if (!Arrays.equals(creator.getLastReference(), createPollTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check issuer has enough funds
if (creator.getConfirmedBalance(Asset.QORA).compareTo(createPollTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -147,6 +139,15 @@ public class CreatePollTransaction extends Transaction {
return ValidationResult.OK;
}
@Override
public ValidationResult isProcessable() throws DataException {
// Check the poll name isn't already taken
if (this.repository.getVotingRepository().pollExists(createPollTransactionData.getPollName()))
return ValidationResult.POLL_ALREADY_EXISTS;
return ValidationResult.OK;
}
@Override
public void process() throws DataException {
// Publish poll to allow voting
@ -154,13 +155,6 @@ public class CreatePollTransaction extends Transaction {
poll.publish();
// We would save updated transaction at this point, but it hasn't been modified
// Update creator's balance
Account creator = getCreator();
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(createPollTransactionData.getFee()));
// Update creator's reference
creator.setLastReference(createPollTransactionData.getSignature());
}
@Override

View File

@ -5,7 +5,6 @@ import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.ciyam.at.MachineState;
@ -171,12 +170,8 @@ public class DeployAtTransaction extends Transaction {
if (deployATTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference is correct
Account creator = getCreator();
if (!Arrays.equals(creator.getLastReference(), deployATTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (assetId == Asset.QORA) {
// Simple case: amount and fee both in Qora
@ -208,6 +203,33 @@ public class DeployAtTransaction extends Transaction {
return ValidationResult.OK;
}
@Override
public ValidationResult isProcessable() throws DataException {
Account creator = getCreator();
long assetId = deployATTransactionData.getAssetId();
// Check creator has enough funds
if (assetId == Asset.QORA) {
// Simple case: amount and fee both in Qora
BigDecimal minimumBalance = deployATTransactionData.getFee().add(deployATTransactionData.getAmount());
if (creator.getConfirmedBalance(Asset.QORA).compareTo(minimumBalance) < 0)
return ValidationResult.NO_BALANCE;
} else {
if (creator.getConfirmedBalance(Asset.QORA).compareTo(deployATTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
if (creator.getConfirmedBalance(assetId).compareTo(deployATTransactionData.getAmount()) < 0)
return ValidationResult.NO_BALANCE;
}
// Check AT doesn't already exist
if (this.repository.getATRepository().exists(deployATTransactionData.getAtAddress()))
return ValidationResult.AT_ALREADY_EXISTS;
return ValidationResult.OK;
}
@Override
public void process() throws DataException {
ensureATAddress();
@ -220,13 +242,9 @@ public class DeployAtTransaction extends Transaction {
long assetId = deployATTransactionData.getAssetId();
// Update creator's balance
// Update creator's balance regarding initial payment to AT
Account creator = getCreator();
creator.setConfirmedBalance(assetId, creator.getConfirmedBalance(assetId).subtract(deployATTransactionData.getAmount()));
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(deployATTransactionData.getFee()));
// Update creator's reference
creator.setLastReference(deployATTransactionData.getSignature());
// Update AT's reference, which also creates AT account
Account atAccount = this.getATAccount();

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -121,10 +120,6 @@ public class EnableForgingTransaction extends Transaction {
if (enableForgingTransactionData.getFee().compareTo(BigDecimal.ZERO) < 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(creator.getLastReference(), enableForgingTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (creator.getConfirmedBalance(Asset.QORA).compareTo(enableForgingTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -153,12 +148,6 @@ public class EnableForgingTransaction extends Transaction {
target.setForgingEnabler(creator.getAddress());
// We would save updated transaction at this point, but it hasn't been modified
// Update creator's balance
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(enableForgingTransactionData.getFee()));
// Update creator's reference
creator.setLastReference(enableForgingTransactionData.getSignature());
}
@Override

View File

@ -140,13 +140,20 @@ public class GenesisTransaction extends Transaction {
Account recipient = new Account(repository, genesisTransactionData.getRecipient());
// Set recipient's starting reference (also creates account)
recipient.setLastReference(genesisTransactionData.getSignature());
// Update recipient's balance
recipient.setConfirmedBalance(Asset.QORA, genesisTransactionData.getAmount());
}
@Override
public void processReferencesAndFees() throws DataException {
// Do not attempt to update non-existent genesis account's reference!
Account recipient = new Account(repository, genesisTransactionData.getRecipient());
// Set recipient's starting reference (also creates account)
recipient.setLastReference(genesisTransactionData.getSignature());
}
@Override
public void orphan() throws DataException {
// We would save updated transaction at this point, but it hasn't been modified

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -83,10 +82,6 @@ public class GroupApprovalTransaction extends Transaction {
if (groupApprovalTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(admin.getLastReference(), groupApprovalTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (admin.getConfirmedBalance(Asset.QORA).compareTo(groupApprovalTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -104,13 +99,6 @@ public class GroupApprovalTransaction extends Transaction {
// Save this transaction with updated prior reference to transaction that can help restore state
this.repository.getTransactionRepository().save(groupApprovalTransactionData);
// Update admin's balance
Account admin = getAdmin();
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).subtract(groupApprovalTransactionData.getFee()));
// Update admin's reference
admin.setLastReference(groupApprovalTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -101,10 +100,6 @@ public class GroupBanTransaction extends Transaction {
if (groupBanTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(admin.getLastReference(), groupBanTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check admin has enough funds
if (admin.getConfirmedBalance(Asset.QORA).compareTo(groupBanTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -120,13 +115,6 @@ public class GroupBanTransaction extends Transaction {
// Save this transaction with updated member/admin references to transactions that can help restore state
this.repository.getTransactionRepository().save(groupBanTransactionData);
// Update admin's balance
Account admin = getAdmin();
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).subtract(groupBanTransactionData.getFee()));
// Update admin's reference
admin.setLastReference(groupBanTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -107,10 +106,6 @@ public class GroupInviteTransaction extends Transaction {
if (groupInviteTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(admin.getLastReference(), groupInviteTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (admin.getConfirmedBalance(Asset.QORA).compareTo(groupInviteTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -126,13 +121,6 @@ public class GroupInviteTransaction extends Transaction {
// Save this transaction with updated member/admin references to transactions that can help restore state
this.repository.getTransactionRepository().save(groupInviteTransactionData);
// Update admin's balance
Account admin = getAdmin();
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).subtract(groupInviteTransactionData.getFee()));
// Update admin's reference
admin.setLastReference(groupInviteTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -107,10 +106,6 @@ public class GroupKickTransaction extends Transaction {
if (groupKickTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(admin.getLastReference(), groupKickTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (admin.getConfirmedBalance(Asset.QORA).compareTo(groupKickTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -126,13 +121,6 @@ public class GroupKickTransaction extends Transaction {
// Save this transaction with updated member/admin references to transactions that can help restore state
this.repository.getTransactionRepository().save(groupKickTransactionData);
// Update admin's balance
Account admin = getAdmin();
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).subtract(groupKickTransactionData.getFee()));
// Update admin's reference
admin.setLastReference(groupKickTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -118,16 +117,17 @@ public class IssueAssetTransaction extends Transaction {
if (issueAssetTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference is correct
Account issuer = getIssuer();
if (!Arrays.equals(issuer.getLastReference(), issueAssetTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check issuer has enough funds
if (issuer.getConfirmedBalance(Asset.QORA).compareTo(issueAssetTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
return ValidationResult.OK;
}
@Override
public ValidationResult isProcessable() throws DataException {
// Check the asset name isn't already taken. This check is not present in gen1.
if (issueAssetTransactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp())
if (this.repository.getAssetRepository().assetExists(issueAssetTransactionData.getAssetName()))
@ -148,13 +148,6 @@ public class IssueAssetTransaction extends Transaction {
// Save this transaction, now with corresponding assetId
this.repository.getTransactionRepository().save(issueAssetTransactionData);
// Update issuer's balance
Account issuer = getIssuer();
issuer.setConfirmedBalance(Asset.QORA, issuer.getConfirmedBalance(Asset.QORA).subtract(issueAssetTransactionData.getFee()));
// Update issuer's reference
issuer.setLastReference(issueAssetTransactionData.getSignature());
// Add asset to owner
Account owner = getOwner();
owner.setConfirmedBalance(issueAssetTransactionData.getAssetId(), BigDecimal.valueOf(issueAssetTransactionData.getQuantity()).setScale(8));

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -87,10 +86,6 @@ public class JoinGroupTransaction extends Transaction {
// Check fee is positive
if (joinGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
if (!Arrays.equals(joiner.getLastReference(), joinGroupTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (joiner.getConfirmedBalance(Asset.QORA).compareTo(joinGroupTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -106,14 +101,6 @@ public class JoinGroupTransaction extends Transaction {
// Save this transaction with cached references to transactions that can help restore state
this.repository.getTransactionRepository().save(joinGroupTransactionData);
// Update joiner's balance
Account joiner = getJoiner();
joiner.setConfirmedBalance(Asset.QORA, joiner.getConfirmedBalance(Asset.QORA).subtract(joinGroupTransactionData.getFee()));
// Update joiner's reference
joiner.setLastReference(joinGroupTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -86,10 +85,6 @@ public class LeaveGroupTransaction extends Transaction {
if (leaveGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(leaver.getLastReference(), leaveGroupTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (leaver.getConfirmedBalance(Asset.QORA).compareTo(leaveGroupTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -105,13 +100,6 @@ public class LeaveGroupTransaction extends Transaction {
// Save this transaction with updated member/admin references to transactions that can help restore state
this.repository.getTransactionRepository().save(leaveGroupTransactionData);
// Update leaver's balance
Account leaver = getLeaver();
leaver.setConfirmedBalance(Asset.QORA, leaver.getConfirmedBalance(Asset.QORA).subtract(leaveGroupTransactionData.getFee()));
// Update leaver's reference
leaver.setLastReference(leaveGroupTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -101,11 +100,6 @@ public class MessageTransaction extends Transaction {
if (messageTransactionData.getData().length < 1 || messageTransactionData.getData().length > MAX_DATA_SIZE)
return ValidationResult.INVALID_DATA_LENGTH;
// Check reference is correct
Account sender = getSender();
if (!Arrays.equals(sender.getLastReference(), messageTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Zero-amount payments (i.e. message-only) only valid for versions later than 1
boolean isZeroAmountValid = messageTransactionData.getVersion() > 1;
@ -114,12 +108,29 @@ public class MessageTransaction extends Transaction {
isZeroAmountValid);
}
@Override
public ValidationResult isProcessable() throws DataException {
// Zero-amount payments (i.e. message-only) only valid for versions later than 1
boolean isZeroAmountValid = messageTransactionData.getVersion() > 1;
// Wrap and delegate final processable checks to Payment class
return new Payment(this.repository).isProcessable(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
isZeroAmountValid);
}
@Override
public void process() throws DataException {
// We would save updated transaction at this point, but it hasn't been modified
// Wrap and delegate payment processing to Payment class. Only update recipient's last reference if transferring QORA.
// Wrap and delegate payment processing to Payment class.
new Payment(this.repository).process(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
messageTransactionData.getSignature());
}
@Override
public void processReferencesAndFees() throws DataException {
// Wrap and delegate references processing to Payment class. Only update recipient's last reference if transferring QORA.
new Payment(this.repository).processReferencesAndFees(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
messageTransactionData.getSignature(), false);
}

View File

@ -2,7 +2,6 @@ package org.qora.transaction;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.qora.account.Account;
@ -102,9 +101,6 @@ public class MultiPaymentTransaction extends Transaction {
// Check reference is correct
Account sender = getSender();
if (!Arrays.equals(sender.getLastReference(), multiPaymentTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check sender has enough funds for fee
// NOTE: in Gen1 pre-POWFIX-RELEASE transactions didn't have this check
if (multiPaymentTransactionData.getTimestamp() >= BlockChain.getInstance().getPowFixReleaseTimestamp()
@ -114,12 +110,28 @@ public class MultiPaymentTransaction extends Transaction {
return new Payment(this.repository).isValid(multiPaymentTransactionData.getSenderPublicKey(), payments, multiPaymentTransactionData.getFee());
}
@Override
public ValidationResult isProcessable() throws DataException {
List<PaymentData> payments = multiPaymentTransactionData.getPayments();
return new Payment(this.repository).isProcessable(multiPaymentTransactionData.getSenderPublicKey(), payments, multiPaymentTransactionData.getFee());
}
@Override
public void process() throws DataException {
// We would save updated transaction at this point, but it hasn't been modified
// Wrap and delegate payment processing to Payment class. Always update recipients' last references regardless of asset.
// Wrap and delegate payment processing to Payment class.
new Payment(this.repository).process(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(),
multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature());
}
@Override
public void processReferencesAndFees() throws DataException {
// We would save updated transaction at this point, but it hasn't been modified
// Wrap and delegate reference processing to Payment class. Always update recipients' last references regardless of asset.
new Payment(this.repository).processReferencesAndFees(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(),
multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), true);
}

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -77,21 +76,29 @@ public class PaymentTransaction extends Transaction {
@Override
public ValidationResult isValid() throws DataException {
// Check reference is correct
Account sender = getSender();
if (!Arrays.equals(sender.getLastReference(), paymentTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Wrap and delegate final payment checks to Payment class
return new Payment(this.repository).isValid(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee());
}
@Override
public ValidationResult isProcessable() throws DataException {
// Wrap and delegate final processable checks to Payment class
return new Payment(this.repository).isProcessable(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee());
}
@Override
public void process() throws DataException {
// We would save updated transaction at this point, but it hasn't been modified
// Wrap and delegate payment processing to Payment class. Only update recipient's last reference if transferring QORA.
// Wrap and delegate payment processing to Payment class.
new Payment(this.repository).process(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(),
paymentTransactionData.getSignature());
}
@Override
public void processReferencesAndFees() throws DataException {
// Wrap and delegate references processing to Payment class. Only update recipient's last reference if transferring QORA.
new Payment(this.repository).processReferencesAndFees(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(),
paymentTransactionData.getSignature(), false);
}

View File

@ -106,10 +106,6 @@ public class ProxyForgingTransaction extends Transaction {
if (proxyForgingTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(creator.getLastReference(), proxyForgingTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (creator.getConfirmedBalance(Asset.QORA).compareTo(proxyForgingTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -134,12 +130,11 @@ public class ProxyForgingTransaction extends Transaction {
// Save proxy forging info
proxyForgerData = new ProxyForgerData(forger.getPublicKey(), proxyForgingTransactionData.getRecipient(), proxyForgingTransactionData.getProxyPublicKey(), proxyForgingTransactionData.getShare());
this.repository.getAccountRepository().save(proxyForgerData);
}
// Update forger's balance
forger.setConfirmedBalance(Asset.QORA, forger.getConfirmedBalance(Asset.QORA).subtract(proxyForgingTransactionData.getFee()));
// Update forger's reference
forger.setLastReference(proxyForgingTransactionData.getSignature());
@Override
public void processReferencesAndFees() throws DataException {
super.processReferencesAndFees();
// If proxy 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, proxyForgingTransactionData.getRecipient());

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -78,10 +77,6 @@ public class RegisterNameTransaction extends Transaction {
public ValidationResult isValid() throws DataException {
Account registrant = getRegistrant();
// If accounts are only allowed one registered name then check for this
if (BlockChain.getInstance().oneNamePerAccount() && !this.repository.getNameRepository().getNamesByOwner(registrant.getAddress()).isEmpty())
return ValidationResult.MULTIPLE_NAMES_FORBIDDEN;
// Check owner address is valid
if (!Crypto.isValidAddress(registerNameTransactionData.getOwner()))
return ValidationResult.INVALID_ADDRESS;
@ -100,18 +95,10 @@ public class RegisterNameTransaction extends Transaction {
if (!registerNameTransactionData.getName().equals(registerNameTransactionData.getName().toLowerCase()))
return ValidationResult.NAME_NOT_LOWER_CASE;
// Check the name isn't already taken
if (this.repository.getNameRepository().nameExists(registerNameTransactionData.getName()))
return ValidationResult.NAME_ALREADY_REGISTERED;
// Check fee is positive
if (registerNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference is correct
if (!Arrays.equals(registrant.getLastReference(), registerNameTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check issuer has enough funds
if (registrant.getConfirmedBalance(Asset.QORA).compareTo(registerNameTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -119,6 +106,21 @@ public class RegisterNameTransaction extends Transaction {
return ValidationResult.OK;
}
@Override
public ValidationResult isProcessable() throws DataException {
// Check the name isn't already taken
if (this.repository.getNameRepository().nameExists(registerNameTransactionData.getName()))
return ValidationResult.NAME_ALREADY_REGISTERED;
Account registrant = getRegistrant();
// If accounts are only allowed one registered name then check for this
if (BlockChain.getInstance().oneNamePerAccount() && !this.repository.getNameRepository().getNamesByOwner(registrant.getAddress()).isEmpty())
return ValidationResult.MULTIPLE_NAMES_FORBIDDEN;
return ValidationResult.OK;
}
@Override
public void process() throws DataException {
// Register Name
@ -126,13 +128,6 @@ public class RegisterNameTransaction extends Transaction {
name.register();
// We would save updated transaction at this point, but it hasn't been modified
// Update registrant's balance
Account registrant = getRegistrant();
registrant.setConfirmedBalance(Asset.QORA, registrant.getConfirmedBalance(Asset.QORA).subtract(registerNameTransactionData.getFee()));
// Update registrant's reference
registrant.setLastReference(registerNameTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -100,10 +99,6 @@ public class RemoveGroupAdminTransaction extends Transaction {
if (removeGroupAdminTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(owner.getLastReference(), removeGroupAdminTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (owner.getConfirmedBalance(Asset.QORA).compareTo(removeGroupAdminTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -119,13 +114,6 @@ public class RemoveGroupAdminTransaction extends Transaction {
// Save this transaction with cached references to transactions that can help restore state
this.repository.getTransactionRepository().save(removeGroupAdminTransactionData);
// Update owner's balance
Account owner = getOwner();
owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).subtract(removeGroupAdminTransactionData.getFee()));
// Update owner's reference
owner.setLastReference(removeGroupAdminTransactionData.getSignature());
}
@Override

View File

@ -2,7 +2,6 @@ package org.qora.transaction;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.qora.account.Account;
@ -105,10 +104,6 @@ public class SellNameTransaction extends Transaction {
if (sellNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference is correct
if (!Arrays.equals(owner.getLastReference(), sellNameTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check issuer has enough funds
if (owner.getConfirmedBalance(Asset.QORA).compareTo(sellNameTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -123,13 +118,6 @@ public class SellNameTransaction extends Transaction {
name.sell(sellNameTransactionData);
// We would save updated transaction at this point, but it hasn't been modified
// Update owner's balance
Account owner = getOwner();
owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).subtract(sellNameTransactionData.getFee()));
// Update owner's reference
owner.setLastReference(sellNameTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -74,10 +73,6 @@ public class SetGroupTransaction extends Transaction {
if (setGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(creator.getLastReference(), setGroupTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (creator.getConfirmedBalance(Asset.QORA).compareTo(setGroupTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -100,12 +95,6 @@ public class SetGroupTransaction extends Transaction {
// Set account's new default groupID
creator.setDefaultGroupId(setGroupTransactionData.getDefaultGroupId());
// Update creator's balance
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(setGroupTransactionData.getFee()));
// Update admin's reference
creator.setLastReference(setGroupTransactionData.getSignature());
}
@Override

View File

@ -229,6 +229,7 @@ public abstract class Transaction {
PUBLIC_KEY_UNKNOWN(78),
INVALID_PUBLIC_KEY(79),
AT_UNKNOWN(80),
AT_ALREADY_EXISTS(81),
NOT_YET_RELEASED(1000);
public final int value;
@ -831,6 +832,33 @@ public abstract class Transaction {
*/
public abstract ValidationResult isValid() throws DataException;
/**
* Returns whether transaction's reference is valid.
*
* @throws DataException
*/
public boolean hasValidReference() throws DataException {
Account creator = getCreator();
return Arrays.equals(transactionData.getReference(), creator.getLastReference());
}
/**
* Returns whether transaction can be processed.
* <p>
* With group-approval, even if a transaction had valid values
* when submitted, by the time it is approved dependency might
* have changed.
* <p>
* For example, with UPDATE_ASSET, the asset owner might have
* changed between submission and approval.
*
* @throws DataException
*/
public ValidationResult isProcessable() throws DataException {
return ValidationResult.OK;
};
/**
* Actually process a transaction, updating the blockchain.
* <p>
@ -841,13 +869,14 @@ public abstract class Transaction {
public abstract void process() throws DataException;
/**
* Update creator's last reference, subtract transaction fee, etc.
* Update last references, subtract transaction fees, etc.
*
* @throws DataException
*/
public void processCreatorUpdates() throws DataException {
// Update transaction creator's balance
public void processReferencesAndFees() throws DataException {
Account creator = getCreator();
// Update transaction creator's balance
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(transactionData.getFee()));
// Update transaction creator's reference

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -89,22 +88,29 @@ public class TransferAssetTransaction extends Transaction {
if (this.transferAssetTransactionData.getTimestamp() < BlockChain.getInstance().getAssetsReleaseTimestamp())
return ValidationResult.NOT_YET_RELEASED;
// Check reference is correct
Account sender = getSender();
if (!Arrays.equals(sender.getLastReference(), transferAssetTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// 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());
}
@Override
public ValidationResult isProcessable() throws DataException {
// Wrap asset transfer as a payment and delegate final processable checks to Payment class
return new Payment(this.repository).isProcessable(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee());
}
@Override
public void process() throws DataException {
// We would save updated transaction at this point, but it hasn't been modified
// Wrap asset transfer as a payment and delegate processing to Payment class. Only update recipient's last reference if transferring QORA.
new Payment(this.repository).process(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(),
transferAssetTransactionData.getSignature());
}
@Override
public void processReferencesAndFees() throws DataException {
// Wrap asset transfer as a payment and delegate processing to Payment class. Only update recipient's last reference if transferring QORA.
new Payment(this.repository).processReferencesAndFees(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(),
transferAssetTransactionData.getSignature(), false);
}

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -85,11 +84,6 @@ public class UpdateAssetTransaction extends Transaction {
if (assetData == null)
return ValidationResult.ASSET_DOES_NOT_EXIST;
// Check transaction's public key matches asset's current owner
PublicKeyAccount currentOwner = getOwner();
if (!assetData.getOwner().equals(currentOwner.getAddress()))
return ValidationResult.INVALID_ASSET_OWNER;
// Check new owner address is valid
if (!Crypto.isValidAddress(updateAssetTransactionData.getNewOwner()))
return ValidationResult.INVALID_ADDRESS;
@ -113,9 +107,7 @@ public class UpdateAssetTransaction extends Transaction {
if (updateAssetTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference is correct
if (!Arrays.equals(currentOwner.getLastReference(), updateAssetTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
Account currentOwner = getOwner();
// Check current owner has enough funds
if (currentOwner.getConfirmedBalance(Asset.QORA).compareTo(updateAssetTransactionData.getFee()) < 0)
@ -124,6 +116,18 @@ public class UpdateAssetTransaction extends Transaction {
return ValidationResult.OK;
}
@Override
public ValidationResult isProcessable() throws DataException {
// Check transaction's public key matches asset's current owner
Account currentOwner = getOwner();
AssetData assetData = this.repository.getAssetRepository().fromAssetId(updateAssetTransactionData.getAssetId());
if (!assetData.getOwner().equals(currentOwner.getAddress()))
return ValidationResult.INVALID_ASSET_OWNER;
return ValidationResult.OK;
}
@Override
public void process() throws DataException {
// Update Asset
@ -132,14 +136,6 @@ public class UpdateAssetTransaction extends Transaction {
// Save this transaction, with updated "name reference" to previous transaction that updated name
this.repository.getTransactionRepository().save(updateAssetTransactionData);
// Update old owner's balance
Account owner = getOwner();
owner.setConfirmedBalance(Asset.QORA,
owner.getConfirmedBalance(Asset.QORA).subtract(updateAssetTransactionData.getFee()));
// Update owner's reference
owner.setLastReference(updateAssetTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -101,6 +100,22 @@ public class UpdateGroupTransaction extends Transaction {
Account owner = getOwner();
// Check fee is positive
if (updateGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check creator has enough funds
if (owner.getConfirmedBalance(Asset.QORA).compareTo(updateGroupTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
return ValidationResult.OK;
}
@Override
public ValidationResult isProcessable() throws DataException {
GroupData groupData = this.repository.getGroupRepository().fromGroupId(updateGroupTransactionData.getGroupId());
Account owner = getOwner();
// Check transaction's public key matches group's current owner
if (!owner.getAddress().equals(groupData.getOwner()))
return ValidationResult.INVALID_GROUP_OWNER;
@ -111,17 +126,6 @@ public class UpdateGroupTransaction extends Transaction {
if (this.repository.getGroupRepository().banExists(updateGroupTransactionData.getGroupId(), newOwner.getAddress()))
return ValidationResult.BANNED_FROM_GROUP;
// Check fee is positive
if (updateGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference is correct
if (!Arrays.equals(owner.getLastReference(), updateGroupTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (owner.getConfirmedBalance(Asset.QORA).compareTo(updateGroupTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
return ValidationResult.OK;
}
@ -134,13 +138,6 @@ public class UpdateGroupTransaction extends Transaction {
// Save this transaction, now with updated "group reference" to previous transaction that updated group
this.repository.getTransactionRepository().save(updateGroupTransactionData);
// Update owner's balance
Account owner = getOwner();
owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).subtract(updateGroupTransactionData.getFee()));
// Update owner's reference
owner.setLastReference(updateGroupTransactionData.getSignature());
}
@Override

View File

@ -1,7 +1,6 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -104,23 +103,12 @@ public class UpdateNameTransaction extends Transaction {
if (nameData.getCreationGroupId() != updateNameTransactionData.getTxGroupId())
return ValidationResult.TX_GROUP_ID_MISMATCH;
// Check name isn't currently for sale
if (nameData.getIsForSale())
return ValidationResult.NAME_ALREADY_FOR_SALE;
// Check transaction's public key matches name's current owner
Account owner = getOwner();
if (!owner.getAddress().equals(nameData.getOwner()))
return ValidationResult.INVALID_NAME_OWNER;
// Check fee is positive
if (updateNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference is correct
if (!Arrays.equals(owner.getLastReference(), updateNameTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check issuer has enough funds
if (owner.getConfirmedBalance(Asset.QORA).compareTo(updateNameTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -128,6 +116,23 @@ public class UpdateNameTransaction extends Transaction {
return ValidationResult.OK;
}
@Override
public ValidationResult isProcessable() throws DataException {
NameData nameData = this.repository.getNameRepository().fromName(updateNameTransactionData.getName());
// Check name isn't currently for sale
if (nameData.getIsForSale())
return ValidationResult.NAME_ALREADY_FOR_SALE;
Account owner = getOwner();
// Check transaction's public key matches name's current owner
if (!owner.getAddress().equals(nameData.getOwner()))
return ValidationResult.INVALID_NAME_OWNER;
return ValidationResult.OK;
}
@Override
public void process() throws DataException {
// Update Name
@ -136,13 +141,6 @@ public class UpdateNameTransaction extends Transaction {
// Save this transaction, now with updated "name reference" to previous transaction that updated name
this.repository.getTransactionRepository().save(updateNameTransactionData);
// Update owner's balance
Account owner = getOwner();
owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).subtract(updateNameTransactionData.getFee()));
// Update owner's reference
owner.setLastReference(updateNameTransactionData.getSignature());
}
@Override

View File

@ -2,7 +2,6 @@ package org.qora.transaction;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.logging.log4j.LogManager;
@ -110,9 +109,6 @@ public class VoteOnPollTransaction extends Transaction {
// Check reference is correct
Account voter = getVoter();
if (!Arrays.equals(voter.getLastReference(), voteOnPollTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check voter has enough funds
if (voter.getConfirmedBalance(Asset.QORA).compareTo(voteOnPollTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
@ -122,12 +118,7 @@ public class VoteOnPollTransaction extends Transaction {
@Override
public void process() throws DataException {
// Update voter's balance
Account voter = getVoter();
voter.setConfirmedBalance(Asset.QORA, voter.getConfirmedBalance(Asset.QORA).subtract(voteOnPollTransactionData.getFee()));
// Update vote's reference
voter.setLastReference(voteOnPollTransactionData.getSignature());
VotingRepository votingRepository = this.repository.getVotingRepository();