3
0
mirror of https://github.com/Qortal/qortal.git synced 2025-02-12 02:05:50 +00:00

AT transactions now either have null message or null amount&assetId.

AT transaction transformer changed to refuse to deserialize AT transactions,
as they should never appear on the wire.

Ditto for GENESIS transactions.
This commit is contained in:
catbref 2020-05-01 10:42:32 +01:00
parent e1f3b9a7a3
commit 233ace23de
5 changed files with 35 additions and 63 deletions

View File

@ -376,7 +376,7 @@ public class QortalATAPI extends API {
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, 0L, null); BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, 0L, null);
ATTransactionData atTransactionData = new ATTransactionData(baseTransactionData, this.atData.getATAddress(), ATTransactionData atTransactionData = new ATTransactionData(baseTransactionData, this.atData.getATAddress(),
recipient.getAddress(), amount, this.atData.getAssetId(), new byte[0]); recipient.getAddress(), amount, this.atData.getAssetId());
AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData); AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData);
// Add to our transactions // Add to our transactions
@ -393,7 +393,7 @@ public class QortalATAPI extends API {
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, 0L, null); BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, 0L, null);
ATTransactionData atTransactionData = new ATTransactionData(baseTransactionData, this.atData.getATAddress(), ATTransactionData atTransactionData = new ATTransactionData(baseTransactionData, this.atData.getATAddress(),
recipient.getAddress(), 0L, this.atData.getAssetId(), message); recipient.getAddress(), message);
AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData); AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData);
// Add to our transactions // Add to our transactions
@ -422,7 +422,7 @@ public class QortalATAPI extends API {
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, 0L, null); BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, NullAccount.PUBLIC_KEY, 0L, null);
ATTransactionData atTransactionData = new ATTransactionData(baseTransactionData, this.atData.getATAddress(), ATTransactionData atTransactionData = new ATTransactionData(baseTransactionData, this.atData.getATAddress(),
creator.getAddress(), finalBalance, this.atData.getAssetId(), new byte[0]); creator.getAddress(), finalBalance, this.atData.getAssetId());
AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData); AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData);
// Add to our transactions // Add to our transactions

View File

@ -29,6 +29,7 @@ public class ATTransactionData extends TransactionData {
// Not always present // Not always present
private Long assetId; private Long assetId;
// Not always present
private byte[] message; private byte[] message;
// Constructors // Constructors
@ -42,7 +43,7 @@ public class ATTransactionData extends TransactionData {
this.creatorPublicKey = NullAccount.PUBLIC_KEY; this.creatorPublicKey = NullAccount.PUBLIC_KEY;
} }
/** From repository */ /** Constructing from repository */
public ATTransactionData(BaseTransactionData baseTransactionData, String atAddress, String recipient, Long amount, Long assetId, byte[] message) { public ATTransactionData(BaseTransactionData baseTransactionData, String atAddress, String recipient, Long amount, Long assetId, byte[] message) {
super(TransactionType.AT, baseTransactionData); super(TransactionType.AT, baseTransactionData);
@ -54,6 +55,16 @@ public class ATTransactionData extends TransactionData {
this.message = message; this.message = message;
} }
/** Constructing a new MESSAGE-type AT transaction */
public ATTransactionData(BaseTransactionData baseTransactionData, String atAddress, String recipient, byte[] message) {
this(baseTransactionData, atAddress, recipient, null, null, message);
}
/** Constructing a new PAYMENT-type AT transaction */
public ATTransactionData(BaseTransactionData baseTransactionData, String atAddress, String recipient, long amount, long assetId) {
this(baseTransactionData, atAddress, recipient, amount, assetId, null);
}
// Getters/Setters // Getters/Setters
public String getATAddress() { public String getATAddress() {

View File

@ -87,27 +87,27 @@ public class AtTransaction extends Transaction {
return ValidationResult.INVALID_ADDRESS; return ValidationResult.INVALID_ADDRESS;
Long amount = this.atTransactionData.getAmount(); Long amount = this.atTransactionData.getAmount();
Long assetId = this.atTransactionData.getAssetId();
byte[] message = this.atTransactionData.getMessage(); byte[] message = this.atTransactionData.getMessage();
// We can only have either message or amount boolean hasPayment = amount != null && assetId != null;
boolean amountIsNull = amount == null; boolean hasMessage = message != null; // empty message OK
boolean messageIsEmpty = message == null || message.length == 0;
if ((messageIsEmpty && amountIsNull) || (!messageIsEmpty && !amountIsNull)) // We can only have either message or payment, not both, nor neither
if ((hasMessage && hasPayment) || (!hasMessage && !hasPayment))
return ValidationResult.INVALID_AT_TRANSACTION; return ValidationResult.INVALID_AT_TRANSACTION;
if (!messageIsEmpty && message.length > MAX_DATA_SIZE) if (hasMessage && message.length > MAX_DATA_SIZE)
return ValidationResult.INVALID_DATA_LENGTH; return ValidationResult.INVALID_DATA_LENGTH;
// If we have no payment then we're done // If we have no payment then we're done
if (amountIsNull) if (!hasPayment)
return ValidationResult.OK; return ValidationResult.OK;
// Check amount is zero or positive // Check amount is zero or positive
if (amount < 0) if (amount < 0)
return ValidationResult.NEGATIVE_AMOUNT; return ValidationResult.NEGATIVE_AMOUNT;
long assetId = this.atTransactionData.getAssetId();
AssetData assetData = this.repository.getAssetRepository().fromAssetId(assetId); AssetData assetData = this.repository.getAssetRepository().fromAssetId(assetId);
// Check asset even exists // Check asset even exists
if (assetData == null) if (assetData == null)

View File

@ -14,25 +14,18 @@ import com.google.common.primitives.Longs;
public class AtTransactionTransformer extends TransactionTransformer { public class AtTransactionTransformer extends TransactionTransformer {
private static final int SENDER_LENGTH = ADDRESS_LENGTH;
private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH;
private static final int DATA_SIZE_LENGTH = INT_LENGTH;
private static final int EXTRAS_LENGTH = SENDER_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH + ASSET_ID_LENGTH + DATA_SIZE_LENGTH;
protected static final TransactionLayout layout = null; protected static final TransactionLayout layout = null;
// Property lengths // Property lengths
public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
throw new TransformationException("Serialized AT Transactions should not exist!"); throw new TransformationException("Serialized AT transactions should not exist!");
} }
public static int getDataLength(TransactionData transactionData) throws TransformationException { public static int getDataLength(TransactionData transactionData) throws TransformationException {
ATTransactionData atTransactionData = (ATTransactionData) transactionData; throw new TransformationException("Serialized AT transactions should not exist!");
return getBaseLength(transactionData) + EXTRAS_LENGTH + atTransactionData.getMessage().length;
} }
// Used for generating fake transaction signatures
public static byte[] toBytes(TransactionData transactionData) throws TransformationException { public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
try { try {
ATTransactionData atTransactionData = (ATTransactionData) transactionData; ATTransactionData atTransactionData = (ATTransactionData) transactionData;
@ -47,18 +40,16 @@ public class AtTransactionTransformer extends TransactionTransformer {
Serialization.serializeAddress(bytes, atTransactionData.getRecipient()); Serialization.serializeAddress(bytes, atTransactionData.getRecipient());
// Only emit amount if greater than zero (safer than checking assetId)
if (atTransactionData.getAmount() > 0) {
bytes.write(Longs.toByteArray(atTransactionData.getAmount()));
bytes.write(Longs.toByteArray(atTransactionData.getAssetId()));
}
byte[] message = atTransactionData.getMessage(); byte[] message = atTransactionData.getMessage();
if (message.length > 0) {
if (message != null) {
// MESSAGE-type
bytes.write(Ints.toByteArray(message.length)); bytes.write(Ints.toByteArray(message.length));
bytes.write(message); bytes.write(message);
} else { } else {
bytes.write(Ints.toByteArray(0)); // PAYMENT-type
bytes.write(Longs.toByteArray(atTransactionData.getAssetId()));
bytes.write(Longs.toByteArray(atTransactionData.getAmount()));
} }
bytes.write(Longs.toByteArray(atTransactionData.getFee())); bytes.write(Longs.toByteArray(atTransactionData.getFee()));

View File

@ -4,12 +4,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.qortal.account.NullAccount;
import org.qortal.data.transaction.BaseTransactionData;
import org.qortal.data.transaction.GenesisTransactionData; import org.qortal.data.transaction.GenesisTransactionData;
import org.qortal.data.transaction.TransactionData; import org.qortal.data.transaction.TransactionData;
import org.qortal.group.Group;
import org.qortal.transaction.Transaction.TransactionType;
import org.qortal.transform.TransformationException; import org.qortal.transform.TransformationException;
import org.qortal.utils.Serialization; import org.qortal.utils.Serialization;
@ -18,43 +14,17 @@ import com.google.common.primitives.Longs;
public class GenesisTransactionTransformer extends TransactionTransformer { public class GenesisTransactionTransformer extends TransactionTransformer {
// Note that Genesis transactions don't require reference, fee or signature protected static final TransactionLayout layout = null;
// Property lengths
private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH;
private static final int TOTAL_LENGTH = TYPE_LENGTH + TIMESTAMP_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH + ASSET_ID_LENGTH;
protected static final TransactionLayout layout;
static {
layout = new TransactionLayout();
layout.add("txType: " + TransactionType.GENESIS.valueString, TransformationType.INT);
layout.add("timestamp", TransformationType.TIMESTAMP);
layout.add("recipient", TransformationType.ADDRESS);
layout.add("amount", TransformationType.AMOUNT);
layout.add("asset ID", TransformationType.LONG);
layout.add("signature", TransformationType.SIGNATURE);
}
public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
long timestamp = byteBuffer.getLong(); throw new TransformationException("Serialized GENESIS transactions should not exist!");
String recipient = Serialization.deserializeAddress(byteBuffer);
long amount = byteBuffer.getLong();
long assetId = byteBuffer.getLong();
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, null, NullAccount.PUBLIC_KEY, 0L, null);
return new GenesisTransactionData(baseTransactionData, recipient, amount, assetId);
} }
public static int getDataLength(TransactionData transactionData) throws TransformationException { public static int getDataLength(TransactionData transactionData) throws TransformationException {
return TOTAL_LENGTH; throw new TransformationException("Serialized GENESIS transactions should not exist!");
} }
// Used when generating fake signatures for genesis block
public static byte[] toBytes(TransactionData transactionData) throws TransformationException { public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
try { try {
GenesisTransactionData genesisTransactionData = (GenesisTransactionData) transactionData; GenesisTransactionData genesisTransactionData = (GenesisTransactionData) transactionData;