mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-22 20:26:50 +00:00
Merge branch 'message-wo-recipient' into launch
This commit is contained in:
@@ -815,9 +815,10 @@ public class CrossChainResource {
|
||||
|
||||
Long fee = null;
|
||||
long amount = 0L;
|
||||
Long assetId = null; // no assetId as amount is zero
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, senderPublicKey, fee, null);
|
||||
TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, 4, atAddress, Asset.QORT, amount, messageData, false, false);
|
||||
TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, 4, atAddress, amount, assetId, messageData, false, false);
|
||||
|
||||
MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
|
||||
|
||||
|
@@ -5,7 +5,6 @@ import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.transaction.Transaction.TransactionType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
@@ -16,14 +15,24 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
public class MessageTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
|
||||
private byte[] senderPublicKey;
|
||||
|
||||
private int version;
|
||||
|
||||
// Not always present
|
||||
private String recipient;
|
||||
private Long assetId;
|
||||
|
||||
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||
private long amount;
|
||||
|
||||
// Not present if amount is zero
|
||||
private Long assetId;
|
||||
|
||||
private byte[] data;
|
||||
|
||||
private boolean isText;
|
||||
|
||||
private boolean isEncrypted;
|
||||
|
||||
// Constructors
|
||||
@@ -38,19 +47,14 @@ public class MessageTransactionData extends TransactionData {
|
||||
}
|
||||
|
||||
public MessageTransactionData(BaseTransactionData baseTransactionData,
|
||||
int version, String recipient, Long assetId, long amount, byte[] data, boolean isText, boolean isEncrypted) {
|
||||
int version, String recipient, long amount, Long assetId, byte[] data, boolean isText, boolean isEncrypted) {
|
||||
super(TransactionType.MESSAGE, baseTransactionData);
|
||||
|
||||
this.senderPublicKey = baseTransactionData.creatorPublicKey;
|
||||
this.version = version;
|
||||
this.recipient = recipient;
|
||||
|
||||
if (assetId != null)
|
||||
this.assetId = assetId;
|
||||
else
|
||||
this.assetId = Asset.QORT;
|
||||
|
||||
this.amount = amount;
|
||||
this.assetId = assetId;
|
||||
this.data = data;
|
||||
this.isText = isText;
|
||||
this.isEncrypted = isEncrypted;
|
||||
@@ -70,23 +74,23 @@ public class MessageTransactionData extends TransactionData {
|
||||
return this.recipient;
|
||||
}
|
||||
|
||||
public Long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public long getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public Long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public boolean getIsText() {
|
||||
public boolean isText() {
|
||||
return this.isText;
|
||||
}
|
||||
|
||||
public boolean getIsEncrypted() {
|
||||
public boolean isEncrypted() {
|
||||
return this.isEncrypted;
|
||||
}
|
||||
|
||||
|
@@ -269,8 +269,8 @@ public class HSQLDBDatabaseUpdates {
|
||||
case 6:
|
||||
// Message Transactions
|
||||
stmt.execute("CREATE TABLE MessageTransactions (signature Signature, version TINYINT NOT NULL, "
|
||||
+ "sender QortalPublicKey NOT NULL, recipient QortalAddress NOT NULL, "
|
||||
+ "is_text BOOLEAN NOT NULL, is_encrypted BOOLEAN NOT NULL, amount QortalAmount NOT NULL, asset_id AssetID NOT NULL, data MessageData NOT NULL, "
|
||||
+ "sender QortalPublicKey NOT NULL, recipient QortalAddress, amount QortalAmount NOT NULL, asset_id AssetID, "
|
||||
+ "is_text BOOLEAN NOT NULL, is_encrypted BOOLEAN NOT NULL, data MessageData NOT NULL, "
|
||||
+ TRANSACTION_KEYS + ")");
|
||||
break;
|
||||
|
||||
|
@@ -36,7 +36,7 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit
|
||||
|
||||
byte[] data = resultSet.getBytes(7);
|
||||
|
||||
return new MessageTransactionData(baseTransactionData, version, recipient, assetId, amount, data, isText, isEncrypted);
|
||||
return new MessageTransactionData(baseTransactionData, version, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch message transaction from repository", e);
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit
|
||||
|
||||
saveHelper.bind("signature", messageTransactionData.getSignature()).bind("version", messageTransactionData.getVersion())
|
||||
.bind("sender", messageTransactionData.getSenderPublicKey()).bind("recipient", messageTransactionData.getRecipient())
|
||||
.bind("is_text", messageTransactionData.getIsText()).bind("is_encrypted", messageTransactionData.getIsEncrypted())
|
||||
.bind("is_text", messageTransactionData.isText()).bind("is_encrypted", messageTransactionData.isEncrypted())
|
||||
.bind("amount", messageTransactionData.getAmount()).bind("asset_id", messageTransactionData.getAssetId())
|
||||
.bind("data", messageTransactionData.getData());
|
||||
|
||||
|
@@ -4,6 +4,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.data.PaymentData;
|
||||
import org.qortal.data.transaction.MessageTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
@@ -13,14 +14,17 @@ import org.qortal.repository.Repository;
|
||||
|
||||
public class MessageTransaction extends Transaction {
|
||||
|
||||
// Useful constants
|
||||
|
||||
public static final int MAX_DATA_SIZE = 4000;
|
||||
|
||||
// Properties
|
||||
|
||||
private MessageTransactionData messageTransactionData;
|
||||
|
||||
/** Cached, lazy-instantiated payment data. Use {@link #getPaymentData()} instead! */
|
||||
private PaymentData paymentData = null;
|
||||
|
||||
// Other useful constants
|
||||
public static final int MAX_DATA_SIZE = 4000;
|
||||
private static final boolean isZeroAmountValid = true;
|
||||
|
||||
// Constructors
|
||||
|
||||
@@ -34,6 +38,9 @@ public class MessageTransaction extends Transaction {
|
||||
|
||||
@Override
|
||||
public List<String> getRecipientAddresses() throws DataException {
|
||||
if (this.messageTransactionData.getRecipient() == null)
|
||||
return Collections.emptyList();
|
||||
|
||||
return Collections.singletonList(this.messageTransactionData.getRecipient());
|
||||
}
|
||||
|
||||
@@ -62,42 +69,80 @@ public class MessageTransaction extends Transaction {
|
||||
if (this.messageTransactionData.getData().length < 1 || this.messageTransactionData.getData().length > MAX_DATA_SIZE)
|
||||
return ValidationResult.INVALID_DATA_LENGTH;
|
||||
|
||||
// If message has no recipient then it cannot have a payment
|
||||
if (this.messageTransactionData.getRecipient() == null && this.messageTransactionData.getAmount() != 0)
|
||||
return ValidationResult.INVALID_AMOUNT;
|
||||
|
||||
// If message has no payment then we only need to do a simple balance check for fee
|
||||
if (this.messageTransactionData.getAmount() == 0) {
|
||||
if (getSender().getConfirmedBalance(Asset.QORT) < this.messageTransactionData.getFee())
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
// Wrap and delegate final payment checks to Payment class
|
||||
return new Payment(this.repository).isValid(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(),
|
||||
isZeroAmountValid);
|
||||
return new Payment(this.repository).isValid(this.messageTransactionData.getSenderPublicKey(), getPaymentData(),
|
||||
this.messageTransactionData.getFee(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult isProcessable() throws DataException {
|
||||
// If we have no amount then we can always process
|
||||
if (this.messageTransactionData.getAmount() == 0L)
|
||||
return ValidationResult.OK;
|
||||
|
||||
// Wrap and delegate final processable checks to Payment class
|
||||
return new Payment(this.repository).isProcessable(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(),
|
||||
isZeroAmountValid);
|
||||
return new Payment(this.repository).isProcessable(this.messageTransactionData.getSenderPublicKey(),
|
||||
getPaymentData(), this.messageTransactionData.getFee(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process() throws DataException {
|
||||
// If we have no amount then there's nothing to do
|
||||
if (this.messageTransactionData.getAmount() == 0L)
|
||||
return;
|
||||
|
||||
// Wrap and delegate payment processing to Payment class.
|
||||
new Payment(this.repository).process(this.messageTransactionData.getSenderPublicKey(), getPaymentData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processReferencesAndFees() throws DataException {
|
||||
// If we have no amount then we only need to process sender's reference and fees
|
||||
if (this.messageTransactionData.getAmount() == 0L) {
|
||||
super.processReferencesAndFees();
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrap and delegate references processing to Payment class. Only update recipient's last reference if transferring QORT.
|
||||
new Payment(this.repository).processReferencesAndFees(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(),
|
||||
this.messageTransactionData.getSignature(), false);
|
||||
new Payment(this.repository).processReferencesAndFees(this.messageTransactionData.getSenderPublicKey(),
|
||||
getPaymentData(), this.messageTransactionData.getFee(), this.messageTransactionData.getSignature(),
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orphan() throws DataException {
|
||||
// If we have no amount then there's nothing to do
|
||||
if (this.messageTransactionData.getAmount() == 0L)
|
||||
return;
|
||||
|
||||
// Wrap and delegate payment processing to Payment class.
|
||||
new Payment(this.repository).orphan(this.messageTransactionData.getSenderPublicKey(), getPaymentData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orphanReferencesAndFees() throws DataException {
|
||||
// If we have no amount then we only need to orphan sender's reference and fees
|
||||
if (this.messageTransactionData.getAmount() == 0L) {
|
||||
super.orphanReferencesAndFees();
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrap and delegate references processing to Payment class. Only revert recipient's last reference if transferring QORT.
|
||||
new Payment(this.repository).orphanReferencesAndFees(this.messageTransactionData.getSenderPublicKey(), getPaymentData(), this.messageTransactionData.getFee(),
|
||||
this.messageTransactionData.getSignature(), this.messageTransactionData.getReference(), false);
|
||||
new Payment(this.repository).orphanReferencesAndFees(this.messageTransactionData.getSenderPublicKey(),
|
||||
getPaymentData(), this.messageTransactionData.getFee(), this.messageTransactionData.getSignature(),
|
||||
this.messageTransactionData.getReference(), false);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -19,12 +19,13 @@ import com.google.common.primitives.Longs;
|
||||
public class MessageTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
// Property lengths
|
||||
private static final int HAS_RECIPIENT_LENGTH = BOOLEAN_LENGTH;
|
||||
private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH;
|
||||
private static final int DATA_SIZE_LENGTH = INT_LENGTH;
|
||||
private static final int IS_TEXT_LENGTH = BOOLEAN_LENGTH;
|
||||
private static final int IS_ENCRYPTED_LENGTH = BOOLEAN_LENGTH;
|
||||
|
||||
private static final int EXTRAS_LENGTH = RECIPIENT_LENGTH + ASSET_ID_LENGTH + AMOUNT_LENGTH + DATA_SIZE_LENGTH + IS_ENCRYPTED_LENGTH + IS_TEXT_LENGTH;
|
||||
private static final int EXTRAS_LENGTH = HAS_RECIPIENT_LENGTH + AMOUNT_LENGTH + DATA_SIZE_LENGTH + IS_ENCRYPTED_LENGTH + IS_TEXT_LENGTH;
|
||||
|
||||
protected static final TransactionLayout layout;
|
||||
|
||||
@@ -35,9 +36,10 @@ public class MessageTransactionTransformer extends TransactionTransformer {
|
||||
layout.add("transaction's groupID", TransformationType.INT);
|
||||
layout.add("reference", TransformationType.SIGNATURE);
|
||||
layout.add("sender's public key", TransformationType.PUBLIC_KEY);
|
||||
layout.add("recipient", TransformationType.ADDRESS);
|
||||
layout.add("asset ID of payment", TransformationType.LONG);
|
||||
layout.add("has recipient?", TransformationType.BOOLEAN);
|
||||
layout.add("? recipient", TransformationType.ADDRESS);
|
||||
layout.add("payment (can be zero)", TransformationType.AMOUNT);
|
||||
layout.add("asset ID of payment (if payment not zero)", TransformationType.LONG);
|
||||
layout.add("message length", TransformationType.INT);
|
||||
layout.add("message", TransformationType.DATA);
|
||||
layout.add("is message encrypted?", TransformationType.BOOLEAN);
|
||||
@@ -58,12 +60,13 @@ public class MessageTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
byte[] senderPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||
|
||||
String recipient = Serialization.deserializeAddress(byteBuffer);
|
||||
|
||||
long assetId = byteBuffer.getLong();
|
||||
boolean hasRecipient = byteBuffer.get() != 0;
|
||||
String recipient = hasRecipient ? Serialization.deserializeAddress(byteBuffer) : null;
|
||||
|
||||
long amount = byteBuffer.getLong();
|
||||
|
||||
Long assetId = amount != 0 ? byteBuffer.getLong() : null;
|
||||
|
||||
int dataSize = byteBuffer.getInt();
|
||||
// Don't allow invalid dataSize here to avoid run-time issues
|
||||
if (dataSize > MessageTransaction.MAX_DATA_SIZE)
|
||||
@@ -83,13 +86,21 @@ public class MessageTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, senderPublicKey, fee, signature);
|
||||
|
||||
return new MessageTransactionData(baseTransactionData, version, recipient, assetId, amount, data, isText, isEncrypted);
|
||||
return new MessageTransactionData(baseTransactionData, version, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
}
|
||||
|
||||
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||
MessageTransactionData messageTransactionData = (MessageTransactionData) transactionData;
|
||||
|
||||
return getBaseLength(transactionData) + EXTRAS_LENGTH + messageTransactionData.getData().length;
|
||||
int dataLength = getBaseLength(transactionData) + EXTRAS_LENGTH + messageTransactionData.getData().length;
|
||||
|
||||
if (messageTransactionData.getRecipient() != null)
|
||||
dataLength += RECIPIENT_LENGTH;
|
||||
|
||||
if (messageTransactionData.getAmount() != 0)
|
||||
dataLength += ASSET_ID_LENGTH;
|
||||
|
||||
return dataLength;
|
||||
}
|
||||
|
||||
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
|
||||
@@ -100,19 +111,25 @@ public class MessageTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
transformCommonBytes(transactionData, bytes);
|
||||
|
||||
Serialization.serializeAddress(bytes, messageTransactionData.getRecipient());
|
||||
|
||||
bytes.write(Longs.toByteArray(messageTransactionData.getAssetId()));
|
||||
if (messageTransactionData.getRecipient() != null) {
|
||||
bytes.write((byte) 1);
|
||||
Serialization.serializeAddress(bytes, messageTransactionData.getRecipient());
|
||||
} else {
|
||||
bytes.write((byte) 0);
|
||||
}
|
||||
|
||||
bytes.write(Longs.toByteArray(messageTransactionData.getAmount()));
|
||||
|
||||
if (messageTransactionData.getAmount() != 0)
|
||||
bytes.write(Longs.toByteArray(messageTransactionData.getAssetId()));
|
||||
|
||||
bytes.write(Ints.toByteArray(messageTransactionData.getData().length));
|
||||
|
||||
bytes.write(messageTransactionData.getData());
|
||||
|
||||
bytes.write((byte) (messageTransactionData.getIsEncrypted() ? 1 : 0));
|
||||
bytes.write((byte) (messageTransactionData.isEncrypted() ? 1 : 0));
|
||||
|
||||
bytes.write((byte) (messageTransactionData.getIsText() ? 1 : 0));
|
||||
bytes.write((byte) (messageTransactionData.isText() ? 1 : 0));
|
||||
|
||||
bytes.write(Longs.toByteArray(messageTransactionData.getFee()));
|
||||
|
||||
|
Reference in New Issue
Block a user