mirror of
https://github.com/Qortal/qortal.git
synced 2025-04-19 01:25:54 +00:00
Some payments don't always initialize the recipient's last reference, depending on whether the asset is QORA or not and what type of transaction is being processed. Calls to Payment.process/orphan now take boolean to indicate whether recipient's last reference should always be initialized regardless of asset. ("initialized" here means setting an initial last reference for an account if the current value is null/empty/missing) When matching orders to produce trades, it isn't enough to only sort orders with best price first. Orders with the same price also need to be further sorted by earliest order first. (This was implicitly done in Qora v1). Added additional ORDER BY sub-clause to achieve this and improved the corresponding table index too. Converted a lot of logging from simplistic System.out/err.println to Apache log4j2. Added several "trace"-level logging statements to aid debugging which can be activated given appropriate configuration in log4j2.properties. With voting, detection of previous votes was broken during orphan as previousOptionIndex was set to 0 instead of null when fetching a VoteOnPollTransaction from the HSQLDB repository. This was due to lack of null-checking - now fixed. Corresponding changes made in IssueAsset and Message transaction HSQLDB repository classes. v1feeder now syncs up to block 99055. Block 99056 contains DeployAT. Orphaning back to block 1 and then resync works without issue too.
138 lines
4.7 KiB
Java
138 lines
4.7 KiB
Java
package qora.transaction;
|
|
|
|
import java.math.BigDecimal;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
import data.PaymentData;
|
|
import data.transaction.MessageTransactionData;
|
|
import data.transaction.TransactionData;
|
|
import qora.account.Account;
|
|
import qora.account.PublicKeyAccount;
|
|
import qora.assets.Asset;
|
|
import qora.block.BlockChain;
|
|
import qora.payment.Payment;
|
|
import repository.DataException;
|
|
import repository.Repository;
|
|
|
|
public class MessageTransaction extends Transaction {
|
|
|
|
// Properties
|
|
private MessageTransactionData messageTransactionData;
|
|
|
|
// Other useful constants
|
|
public static final int MAX_DATA_SIZE = 4000;
|
|
|
|
// Constructors
|
|
|
|
public MessageTransaction(Repository repository, TransactionData transactionData) {
|
|
super(repository, transactionData);
|
|
|
|
this.messageTransactionData = (MessageTransactionData) this.transactionData;
|
|
}
|
|
|
|
// More information
|
|
|
|
@Override
|
|
public List<Account> getRecipientAccounts() throws DataException {
|
|
return Collections.singletonList(new Account(this.repository, messageTransactionData.getRecipient()));
|
|
}
|
|
|
|
@Override
|
|
public boolean isInvolved(Account account) throws DataException {
|
|
String address = account.getAddress();
|
|
|
|
if (address.equals(this.getSender().getAddress()))
|
|
return true;
|
|
|
|
if (address.equals(messageTransactionData.getRecipient()))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public BigDecimal getAmount(Account account) throws DataException {
|
|
String address = account.getAddress();
|
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
|
String senderAddress = this.getSender().getAddress();
|
|
|
|
if (address.equals(senderAddress))
|
|
amount = amount.subtract(this.transactionData.getFee());
|
|
|
|
// We're only interested in QORA
|
|
if (messageTransactionData.getAssetId() == Asset.QORA) {
|
|
if (address.equals(messageTransactionData.getRecipient()))
|
|
amount = amount.add(messageTransactionData.getAmount());
|
|
else if (address.equals(senderAddress))
|
|
amount = amount.subtract(messageTransactionData.getAmount());
|
|
}
|
|
|
|
return amount;
|
|
}
|
|
|
|
// Navigation
|
|
|
|
public Account getSender() throws DataException {
|
|
return new PublicKeyAccount(this.repository, this.messageTransactionData.getSenderPublicKey());
|
|
}
|
|
|
|
public Account getRecipient() throws DataException {
|
|
return new Account(this.repository, this.messageTransactionData.getRecipient());
|
|
}
|
|
|
|
// Processing
|
|
|
|
private PaymentData getPaymentData() {
|
|
return new PaymentData(messageTransactionData.getRecipient(), Asset.QORA, messageTransactionData.getAmount());
|
|
}
|
|
|
|
@Override
|
|
public ValidationResult isValid() throws DataException {
|
|
// Are message transactions even allowed at this point?
|
|
if (messageTransactionData.getVersion() != MessageTransaction.getVersionByTimestamp(messageTransactionData.getTimestamp()))
|
|
return ValidationResult.NOT_YET_RELEASED;
|
|
|
|
if (this.repository.getBlockRepository().getBlockchainHeight() < BlockChain.getMessageReleaseHeight())
|
|
return ValidationResult.NOT_YET_RELEASED;
|
|
|
|
// Check data length
|
|
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;
|
|
|
|
// Wrap and delegate final payment checks to Payment class
|
|
return new Payment(this.repository).isValid(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
|
|
isZeroAmountValid);
|
|
}
|
|
|
|
@Override
|
|
public void process() throws DataException {
|
|
// Save this transaction itself
|
|
this.repository.getTransactionRepository().save(this.transactionData);
|
|
|
|
// Wrap and delegate payment processing to Payment class. Only update recipient's last reference if transferring QORA.
|
|
new Payment(this.repository).process(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
|
|
messageTransactionData.getSignature(), false);
|
|
}
|
|
|
|
@Override
|
|
public void orphan() throws DataException {
|
|
// Delete this transaction itself
|
|
this.repository.getTransactionRepository().delete(this.transactionData);
|
|
|
|
// Wrap and delegate payment processing to Payment class. Only revert recipient's last reference if transferring QORA.
|
|
new Payment(this.repository).orphan(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
|
|
messageTransactionData.getSignature(), messageTransactionData.getReference(), false);
|
|
}
|
|
|
|
}
|