mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-23 04:36:50 +00:00
Improved AT PUT_TX_AFTER_TIMESTAMP_INTO_A function
Previous version fetched all the blocks from previous 'timestamp' to current height, checking each transaction. (very slow) New implementation leverages repository to do the heavy lifting. Could potentially benefit from some DB indexes in the future? Added unit test to cover.
This commit is contained in:
@@ -17,7 +17,6 @@ import org.qortal.account.Account;
|
||||
import org.qortal.account.NullAccount;
|
||||
import org.qortal.account.PublicKeyAccount;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.block.Block;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.block.BlockChain.CiyamAtSettings;
|
||||
import org.qortal.crypto.Crypto;
|
||||
@@ -30,11 +29,10 @@ import org.qortal.data.transaction.MessageTransactionData;
|
||||
import org.qortal.data.transaction.PaymentTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.group.Group;
|
||||
import org.qortal.repository.BlockRepository;
|
||||
import org.qortal.repository.ATRepository;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.transaction.AtTransaction;
|
||||
import org.qortal.transaction.Transaction;
|
||||
import org.qortal.transaction.Transaction.TransactionType;
|
||||
import org.qortal.utils.Base58;
|
||||
import org.qortal.utils.BitTwiddling;
|
||||
@@ -150,59 +148,27 @@ public class QortalATAPI extends API {
|
||||
int height = timestamp.blockHeight;
|
||||
int sequence = timestamp.transactionSequence + 1;
|
||||
|
||||
BlockRepository blockRepository = this.getRepository().getBlockRepository();
|
||||
|
||||
ATRepository.NextTransactionInfo nextTransactionInfo;
|
||||
try {
|
||||
int currentHeight = blockRepository.getBlockchainHeight();
|
||||
List<Transaction> blockTransactions = null;
|
||||
|
||||
while (height <= currentHeight) {
|
||||
if (blockTransactions == null) {
|
||||
BlockData blockData = blockRepository.fromHeight(height);
|
||||
|
||||
if (blockData == null)
|
||||
throw new DataException("Unable to fetch block " + height + " from repository?");
|
||||
|
||||
Block block = new Block(this.getRepository(), blockData);
|
||||
|
||||
blockTransactions = block.getTransactions();
|
||||
}
|
||||
|
||||
// No more transactions in this block? Try next block
|
||||
if (sequence >= blockTransactions.size()) {
|
||||
++height;
|
||||
sequence = 0;
|
||||
blockTransactions = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
Transaction transaction = blockTransactions.get(sequence);
|
||||
|
||||
// Transaction needs to be sent to specified recipient
|
||||
List<String> recipientAddresses = transaction.getRecipientAddresses();
|
||||
if (recipientAddresses.contains(atAddress)) {
|
||||
// Found a transaction
|
||||
|
||||
this.setA1(state, new Timestamp(height, timestamp.blockchainId, sequence).longValue());
|
||||
|
||||
// Copy transaction's partial signature into the other three A fields for future verification that it's the same transaction
|
||||
byte[] signature = transaction.getTransactionData().getSignature();
|
||||
this.setA2(state, BitTwiddling.longFromBEBytes(signature, 8));
|
||||
this.setA3(state, BitTwiddling.longFromBEBytes(signature, 16));
|
||||
this.setA4(state, BitTwiddling.longFromBEBytes(signature, 24));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Transaction wasn't for us - keep going
|
||||
++sequence;
|
||||
}
|
||||
|
||||
// No more transactions - zero A and exit
|
||||
this.zeroA(state);
|
||||
nextTransactionInfo = this.getRepository().getATRepository().findNextTransaction(atAddress, height, sequence);
|
||||
} catch (DataException e) {
|
||||
throw new RuntimeException("AT API unable to fetch next transaction?", e);
|
||||
}
|
||||
|
||||
if (nextTransactionInfo == null) {
|
||||
// No more transactions for AT at this time - zero A and exit
|
||||
this.zeroA(state);
|
||||
return;
|
||||
}
|
||||
|
||||
// Found a transaction
|
||||
|
||||
this.setA1(state, new Timestamp(nextTransactionInfo.height, timestamp.blockchainId, nextTransactionInfo.sequence).longValue());
|
||||
|
||||
// Copy transaction's partial signature into the other three A fields for future verification that it's the same transaction
|
||||
this.setA2(state, BitTwiddling.longFromBEBytes(nextTransactionInfo.signature, 8));
|
||||
this.setA3(state, BitTwiddling.longFromBEBytes(nextTransactionInfo.signature, 16));
|
||||
this.setA4(state, BitTwiddling.longFromBEBytes(nextTransactionInfo.signature, 24));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -88,4 +88,28 @@ public interface ATRepository {
|
||||
/** Delete state data for all ATs at this height */
|
||||
public void deleteATStates(int height) throws DataException;
|
||||
|
||||
// Finding transactions for ATs to process
|
||||
|
||||
static class NextTransactionInfo {
|
||||
public final int height;
|
||||
public final int sequence;
|
||||
public final byte[] signature;
|
||||
|
||||
public NextTransactionInfo(int height, int sequence, byte[] signature) {
|
||||
this.height = height;
|
||||
this.sequence = sequence;
|
||||
this.signature = signature;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find next transaction for AT to process.
|
||||
* <p>
|
||||
* @param recipient AT address
|
||||
* @param height starting height
|
||||
* @param sequence starting sequence
|
||||
* @return next transaction info, or null if none found
|
||||
*/
|
||||
public NextTransactionInfo findNextTransaction(String recipient, int height, int sequence) throws DataException;
|
||||
|
||||
}
|
||||
|
@@ -341,4 +341,40 @@ public class HSQLDBATRepository implements ATRepository {
|
||||
}
|
||||
}
|
||||
|
||||
// Finding transactions for ATs to process
|
||||
|
||||
public NextTransactionInfo findNextTransaction(String recipient, int height, int sequence) throws DataException {
|
||||
// We only need to search for a subset of transaction types: MESSAGE, PAYMENT or AT
|
||||
|
||||
String sql = "SELECT height, sequence, Transactions.signature "
|
||||
+ "FROM ("
|
||||
+ "SELECT signature FROM PaymentTransactions WHERE recipient = ? "
|
||||
+ "UNION "
|
||||
+ "SELECT signature FROM MessageTransactions WHERE recipient = ? "
|
||||
+ "UNION "
|
||||
+ "SELECT signature FROM ATTransactions WHERE recipient = ?"
|
||||
+ ") AS Transactions "
|
||||
+ "JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature "
|
||||
+ "JOIN Blocks ON Blocks.signature = BlockTransactions.block_signature "
|
||||
+ "WHERE (height > ? OR (height = ? AND sequence > ?)) "
|
||||
+ "ORDER BY height ASC, sequence ASC "
|
||||
+ "LIMIT 1";
|
||||
|
||||
Object[] bindParams = new Object[] { recipient, recipient, recipient, height, height, sequence };
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql, bindParams)) {
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
int nextHeight = resultSet.getInt(1);
|
||||
int nextSequence = resultSet.getInt(2);
|
||||
byte[] nextSignature = resultSet.getBytes(3);
|
||||
|
||||
return new NextTransactionInfo(nextHeight, nextSequence, nextSignature);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to find next transaction to AT from repository", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user