Compare commits

...

1 Commits

Author SHA1 Message Date
CalDescent
3be2abdf8d Possible optimization of sleeping ATs. 2023-08-12 10:01:18 +01:00
3 changed files with 66 additions and 13 deletions

View File

@ -1,5 +1,6 @@
package org.qortal.at;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -10,6 +11,7 @@ import org.qortal.crypto.Crypto;
import org.qortal.data.at.ATData;
import org.qortal.data.at.ATStateData;
import org.qortal.data.transaction.DeployAtTransactionData;
import org.qortal.data.transaction.TransactionData;
import org.qortal.repository.ATRepository;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
@ -22,6 +24,8 @@ public class AT {
private ATData atData;
private ATStateData atStateData;
private List<TransactionData> parentBlockTransactions = new ArrayList<>();
// Constructors
public AT(Repository repository, ATData atData, ATStateData atStateData) {
@ -72,6 +76,10 @@ public class AT {
return this.atStateData;
}
public void setParentBlockTransactions(List<TransactionData> transactions) {
this.parentBlockTransactions = transactions;
}
// Processing
public void deploy() throws DataException {
@ -105,7 +113,7 @@ public class AT {
QortalATAPI api = new QortalATAPI(repository, this.atData, blockTimestamp);
QortalAtLoggerFactory loggerFactory = QortalAtLoggerFactory.getInstance();
if (!api.willExecute(blockHeight))
if (!api.willExecute(blockHeight, this.parentBlockTransactions))
// this.atStateData will be null
return Collections.emptyList();

View File

@ -3,6 +3,7 @@ package org.qortal.at;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -23,11 +24,7 @@ import org.qortal.crypto.Crypto;
import org.qortal.data.at.ATData;
import org.qortal.data.block.BlockData;
import org.qortal.data.block.BlockSummaryData;
import org.qortal.data.transaction.ATTransactionData;
import org.qortal.data.transaction.BaseTransactionData;
import org.qortal.data.transaction.MessageTransactionData;
import org.qortal.data.transaction.PaymentTransactionData;
import org.qortal.data.transaction.TransactionData;
import org.qortal.data.transaction.*;
import org.qortal.group.Group;
import org.qortal.repository.ATRepository;
import org.qortal.repository.DataException;
@ -75,7 +72,7 @@ public class QortalATAPI extends API {
return this.transactions;
}
public boolean willExecute(int blockHeight) throws DataException {
public boolean willExecute(int blockHeight, List<TransactionData> parentBlockTransactions) throws DataException {
// Sleep-until-message/height checking
Long sleepUntilMessageTimestamp = this.atData.getSleepUntilMessageTimestamp();
@ -87,13 +84,27 @@ public class QortalATAPI extends API {
boolean wakeDueToMessage = false;
if (!wakeDueToHeight) {
// No avoiding asking repository
Timestamp previousTxTimestamp = new Timestamp(sleepUntilMessageTimestamp);
NextTransactionInfo nextTransactionInfo = this.repository.getATRepository().findNextTransaction(this.atData.getATAddress(),
previousTxTimestamp.blockHeight,
previousTxTimestamp.transactionSequence);
// Check parent block's transactions to see if any relate to this AT
for (TransactionData transactionData : parentBlockTransactions) {
if (this.wasTransactionSentToThisAT(transactionData)) {
wakeDueToMessage = true;
}
}
wakeDueToMessage = nextTransactionInfo != null;
if (wakeDueToMessage) {
// Double check with repository that this AT should be executed, to filter out cases such as TRANSFER_ASSET
Timestamp previousTxTimestamp = new Timestamp(sleepUntilMessageTimestamp);
NextTransactionInfo nextTransactionInfo = this.repository.getATRepository().findNextTransaction(this.atData.getATAddress(),
previousTxTimestamp.blockHeight,
previousTxTimestamp.transactionSequence);
wakeDueToMessage = nextTransactionInfo != null;
}
else {
// No relevant transactions in previous block, so there is no need to check the db
// TODO: do we need to handle ATs that were previously frozen and have now recovered, or
// would we always detect a transaction in the last block in these cases?
}
}
// Can we skip?
@ -104,6 +115,32 @@ public class QortalATAPI extends API {
return true;
}
private boolean wasTransactionSentToThisAT(TransactionData transactionData) {
switch (transactionData.getType()) {
case PAYMENT: {
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) transactionData;
return Objects.equals(paymentTransactionData.getRecipient(), this.atData.getATAddress());
}
case TRANSFER_ASSET: {
// ATs don't check for TRANSFER_ASSET, but an AT's balance could be topped up using TRANSFER_ASSET,
// therefore unfreezing it.
TransferAssetTransactionData transferAssetTransactionData = (TransferAssetTransactionData) transactionData;
return Objects.equals(transferAssetTransactionData.getRecipient(), this.atData.getATAddress());
}
case MESSAGE: {
MessageTransactionData messageTransactionData = (MessageTransactionData) transactionData;
return Objects.equals(messageTransactionData.getRecipient(), this.atData.getATAddress());
}
case AT: {
ATTransactionData atTransactionData = (ATTransactionData) transactionData;
return Objects.equals(atTransactionData.getRecipient(), this.atData.getATAddress());
}
default: {
return false;
}
}
}
public void preExecute(MachineState state) {
// Sleep-until-message/height checking
Long sleepUntilMessageTimestamp = this.atData.getSleepUntilMessageTimestamp();

View File

@ -1378,6 +1378,8 @@ public class Block {
*
*/
private void executeATs() throws DataException {
Long startTime = NTP.getTime();
// We're expecting a lack of AT state data at this point.
if (this.ourAtStates != null)
throw new IllegalStateException("Attempted to execute ATs when block's local AT state data already exists");
@ -1391,9 +1393,13 @@ public class Block {
// Find all executable ATs, ordered by earliest creation date first
List<ATData> executableATs = this.repository.getATRepository().getAllExecutableATs();
// Get all transactions from the parent block. These are used to avoid unnecessary AT executions / db lookups.
List<TransactionData> parentBlockTransactions = repository.getBlockRepository().getTransactionsFromSignature(this.blockData.getReference());
// Run each AT, appends AT-Transactions and corresponding AT states, to our lists
for (ATData atData : executableATs) {
AT at = new AT(this.repository, atData);
at.setParentBlockTransactions(parentBlockTransactions);
List<AtTransaction> atTransactions = at.run(this.blockData.getHeight(), this.blockData.getTimestamp());
ATStateData atStateData = at.getATStateData();
// Didn't execute? (e.g. sleeping)
@ -1417,6 +1423,8 @@ public class Block {
// AT Transactions do not affect block's transaction count
// AT Transactions do not affect block's transaction signature
LOGGER.info("Executing {} ATs in block {} took {} ms", executableATs.size(), this.blockData.getHeight(), (NTP.getTime()-startTime));
}
/** Returns whether block's minter is actually allowed to mint this block. */