mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-14 11:15:49 +00:00
WIP: PRESENCE transactions - repository support, removing older versions, tests
This commit is contained in:
parent
e0210635e3
commit
15d25649b2
@ -23,9 +23,6 @@ public class PresenceTransactionData extends TransactionData {
|
||||
@Schema(description = "sender's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||
private byte[] senderPublicKey;
|
||||
|
||||
@Schema(accessMode = AccessMode.READ_ONLY)
|
||||
private String sender;
|
||||
|
||||
@Schema(accessMode = AccessMode.READ_ONLY)
|
||||
private int nonce;
|
||||
|
||||
|
@ -771,6 +771,13 @@ public class HSQLDBDatabaseUpdates {
|
||||
stmt.execute("CHECKPOINT");
|
||||
break;
|
||||
|
||||
case 31:
|
||||
// PRESENCE transactions
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS PresenceTransactions ("
|
||||
+ "signature Signature, nonce INT NOT NULL, presence_type INT NOT NULL, "
|
||||
+ "timestamp_signature Signature NOT NULL, " + TRANSACTION_KEYS + ")");
|
||||
break;
|
||||
|
||||
default:
|
||||
// nothing to do
|
||||
return false;
|
||||
|
@ -0,0 +1,57 @@
|
||||
package org.qortal.repository.hsqldb.transaction;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.qortal.data.transaction.BaseTransactionData;
|
||||
import org.qortal.data.transaction.PresenceTransactionData;
|
||||
import org.qortal.data.transaction.PresenceTransactionData.PresenceType;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.hsqldb.HSQLDBRepository;
|
||||
import org.qortal.repository.hsqldb.HSQLDBSaver;
|
||||
|
||||
public class HSQLDBPresenceTransactionRepository extends HSQLDBTransactionRepository {
|
||||
|
||||
public HSQLDBPresenceTransactionRepository(HSQLDBRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException {
|
||||
String sql = "SELECT nonce, presence_type, timestamp_signature FROM PresenceTransactions WHERE signature = ?";
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) {
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
int nonce = resultSet.getInt(1);
|
||||
int presenceTypeValue = resultSet.getInt(2);
|
||||
PresenceType presenceType = PresenceType.valueOf(presenceTypeValue);
|
||||
|
||||
byte[] timestampSignature = resultSet.getBytes(3);
|
||||
|
||||
return new PresenceTransactionData(baseTransactionData, nonce, presenceType, timestampSignature);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch presence transaction from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(TransactionData transactionData) throws DataException {
|
||||
PresenceTransactionData presenceTransactionData = (PresenceTransactionData) transactionData;
|
||||
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("PresenceTransactions");
|
||||
|
||||
saveHelper.bind("signature", presenceTransactionData.getSignature())
|
||||
.bind("nonce", presenceTransactionData.getNonce())
|
||||
.bind("presence_type", presenceTransactionData.getPresenceType().value)
|
||||
.bind("timestamp_signature", presenceTransactionData.getTimestampSignature());
|
||||
|
||||
try {
|
||||
saveHelper.execute(this.repository);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to save chat transaction into repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -3,8 +3,9 @@ package org.qortal.transaction;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.crypto.MemoryPoW;
|
||||
import org.qortal.data.transaction.PresenceTransactionData;
|
||||
@ -15,19 +16,20 @@ import org.qortal.repository.Repository;
|
||||
import org.qortal.transform.TransformationException;
|
||||
import org.qortal.transform.transaction.PresenceTransactionTransformer;
|
||||
import org.qortal.transform.transaction.TransactionTransformer;
|
||||
import org.qortal.utils.Base58;
|
||||
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
public class PresenceTransaction extends Transaction {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(PresenceTransaction.class);
|
||||
|
||||
// Properties
|
||||
private PresenceTransactionData presenceTransactionData;
|
||||
|
||||
// Other useful constants
|
||||
public static final int MAX_DATA_SIZE = 256;
|
||||
public static final int POW_BUFFER_SIZE = 8 * 1024 * 1024; // bytes
|
||||
public static final int POW_DIFFICULTY_WITH_QORT = 8; // leading zero bits
|
||||
public static final int POW_DIFFICULTY_NO_QORT = 14; // leading zero bits
|
||||
public static final int POW_DIFFICULTY = 8; // leading zero bits
|
||||
|
||||
// Constructors
|
||||
|
||||
@ -64,10 +66,8 @@ public class PresenceTransaction extends Transaction {
|
||||
// Clear nonce from transactionBytes
|
||||
PresenceTransactionTransformer.clearNonce(transactionBytes);
|
||||
|
||||
int difficulty = this.getSender().getConfirmedBalance(Asset.QORT) > 0 ? POW_DIFFICULTY_WITH_QORT : POW_DIFFICULTY_NO_QORT;
|
||||
|
||||
// Calculate nonce
|
||||
this.presenceTransactionData.setNonce(MemoryPoW.compute2(transactionBytes, POW_BUFFER_SIZE, difficulty));
|
||||
this.presenceTransactionData.setNonce(MemoryPoW.compute2(transactionBytes, POW_BUFFER_SIZE, POW_DIFFICULTY));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,15 +135,27 @@ public class PresenceTransaction extends Transaction {
|
||||
// Clear nonce from transactionBytes
|
||||
PresenceTransactionTransformer.clearNonce(transactionBytes);
|
||||
|
||||
int difficulty;
|
||||
try {
|
||||
difficulty = this.getSender().getConfirmedBalance(Asset.QORT) > 0 ? POW_DIFFICULTY_WITH_QORT : POW_DIFFICULTY_NO_QORT;
|
||||
} catch (DataException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check nonce
|
||||
return MemoryPoW.verify2(transactionBytes, POW_BUFFER_SIZE, difficulty, nonce);
|
||||
return MemoryPoW.verify2(transactionBytes, POW_BUFFER_SIZE, POW_DIFFICULTY, nonce);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any PRESENCE transactions by the same signer that have older timestamps.
|
||||
*/
|
||||
@Override
|
||||
protected void onImportAsUnconfirmed() throws DataException {
|
||||
byte[] creatorPublicKey = this.transactionData.getCreatorPublicKey();
|
||||
List<TransactionData> creatorsPresenceTransactions = this.repository.getTransactionRepository().getUnconfirmedTransactions(TransactionType.PRESENCE, creatorPublicKey);
|
||||
|
||||
if (creatorsPresenceTransactions.isEmpty())
|
||||
return;
|
||||
|
||||
// List should contain oldest transaction first, so remove all but last from repository.
|
||||
creatorsPresenceTransactions.remove(creatorsPresenceTransactions.size() - 1);
|
||||
for (TransactionData transactionData : creatorsPresenceTransactions) {
|
||||
LOGGER.info(() -> String.format("Deleting older PRESENCE transaction %s", Base58.encode(transactionData.getSignature())));
|
||||
this.repository.getTransactionRepository().delete(transactionData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -12,6 +12,7 @@ import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.test.common.Common;
|
||||
import org.qortal.test.common.TransactionUtils;
|
||||
import org.qortal.transaction.PresenceTransaction;
|
||||
import org.qortal.transaction.Transaction;
|
||||
import org.qortal.transaction.Transaction.ValidationResult;
|
||||
@ -51,19 +52,44 @@ public class PresenceTests extends Common {
|
||||
assertTrue(isValid(Group.NO_GROUP, this.signer, timestamp, timestampSignature));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newestOnlyTests() throws DataException {
|
||||
long OLDER_TIMESTAMP = System.currentTimeMillis() - 2000L;
|
||||
long NEWER_TIMESTAMP = OLDER_TIMESTAMP + 1000L;
|
||||
|
||||
PresenceTransaction older = buildPresenceTransaction(Group.NO_GROUP, this.signer, OLDER_TIMESTAMP, null);
|
||||
older.computeNonce();
|
||||
TransactionUtils.signAndImportValid(repository, older.getTransactionData(), this.signer);
|
||||
|
||||
assertTrue(this.repository.getTransactionRepository().exists(older.getTransactionData().getSignature()));
|
||||
|
||||
PresenceTransaction newer = buildPresenceTransaction(Group.NO_GROUP, this.signer, NEWER_TIMESTAMP, null);
|
||||
newer.computeNonce();
|
||||
TransactionUtils.signAndImportValid(repository, newer.getTransactionData(), this.signer);
|
||||
|
||||
assertTrue(this.repository.getTransactionRepository().exists(newer.getTransactionData().getSignature()));
|
||||
assertFalse(this.repository.getTransactionRepository().exists(older.getTransactionData().getSignature()));
|
||||
}
|
||||
|
||||
private boolean isValid(int txGroupId, PrivateKeyAccount signer, long timestamp, byte[] timestampSignature) throws DataException {
|
||||
Transaction transaction = buildPresenceTransaction(txGroupId, signer, timestamp, timestampSignature);
|
||||
return transaction.isValidUnconfirmed() == ValidationResult.OK;
|
||||
}
|
||||
|
||||
private PresenceTransaction buildPresenceTransaction(int txGroupId, PrivateKeyAccount signer, long timestamp, byte[] timestampSignature) throws DataException {
|
||||
int nonce = 0;
|
||||
|
||||
byte[] reference = signer.getLastReference();
|
||||
byte[] creatorPublicKey = signer.getPublicKey();
|
||||
long fee = 0L;
|
||||
|
||||
if (timestampSignature == null)
|
||||
timestampSignature = this.signer.sign(Longs.toByteArray(timestamp));
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, creatorPublicKey, fee, null);
|
||||
PresenceTransactionData transactionData = new PresenceTransactionData(baseTransactionData, nonce, PresenceType.REWARD_SHARE, timestampSignature);
|
||||
|
||||
Transaction transaction = new PresenceTransaction(this.repository, transactionData);
|
||||
|
||||
return transaction.isValidUnconfirmed() == ValidationResult.OK;
|
||||
return new PresenceTransaction(this.repository, transactionData);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user