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);
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);
// 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);
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);
// 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);
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);
// Add to our transactions

View File

@ -29,6 +29,7 @@ public class ATTransactionData extends TransactionData {
// Not always present
private Long assetId;
// Not always present
private byte[] message;
// Constructors
@ -42,7 +43,7 @@ public class ATTransactionData extends TransactionData {
this.creatorPublicKey = NullAccount.PUBLIC_KEY;
}
/** From repository */
/** Constructing from repository */
public ATTransactionData(BaseTransactionData baseTransactionData, String atAddress, String recipient, Long amount, Long assetId, byte[] message) {
super(TransactionType.AT, baseTransactionData);
@ -54,6 +55,16 @@ public class ATTransactionData extends TransactionData {
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
public String getATAddress() {

View File

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

View File

@ -14,25 +14,18 @@ import com.google.common.primitives.Longs;
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;
// Property lengths
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 {
ATTransactionData atTransactionData = (ATTransactionData) transactionData;
return getBaseLength(transactionData) + EXTRAS_LENGTH + atTransactionData.getMessage().length;
throw new TransformationException("Serialized AT transactions should not exist!");
}
// Used for generating fake transaction signatures
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
try {
ATTransactionData atTransactionData = (ATTransactionData) transactionData;
@ -47,18 +40,16 @@ public class AtTransactionTransformer extends TransactionTransformer {
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();
if (message.length > 0) {
if (message != null) {
// MESSAGE-type
bytes.write(Ints.toByteArray(message.length));
bytes.write(message);
} 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()));

View File

@ -4,12 +4,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
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.TransactionData;
import org.qortal.group.Group;
import org.qortal.transaction.Transaction.TransactionType;
import org.qortal.transform.TransformationException;
import org.qortal.utils.Serialization;
@ -18,43 +14,17 @@ import com.google.common.primitives.Longs;
public class GenesisTransactionTransformer extends TransactionTransformer {
// Note that Genesis transactions don't require reference, fee or signature
// 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);
}
protected static final TransactionLayout layout = null;
public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
long timestamp = byteBuffer.getLong();
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);
throw new TransformationException("Serialized GENESIS transactions should not exist!");
}
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 {
try {
GenesisTransactionData genesisTransactionData = (GenesisTransactionData) transactionData;