forked from Qortal/qortal
Added PoW to MESSAGE (for zero fee). DB and tx layout changes.
This commit is contained in:
parent
50b912e229
commit
d0b4a1f12f
@ -807,12 +807,14 @@ public class CrossChainResource {
|
||||
if (lastReference == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_REFERENCE);
|
||||
|
||||
Long fee = null;
|
||||
int version = 4;
|
||||
int nonce = 0;
|
||||
long amount = 0L;
|
||||
Long assetId = null; // no assetId as amount is zero
|
||||
Long fee = null;
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, senderPublicKey, fee, null);
|
||||
TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, 4, atAddress, amount, assetId, messageData, false, false);
|
||||
TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, atAddress, amount, assetId, messageData, false, false);
|
||||
|
||||
MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
|
||||
|
||||
|
@ -8,6 +8,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
import org.qortal.transaction.Transaction.TransactionType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
|
||||
|
||||
// All properties to be converted to JSON via JAXB
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ -20,6 +21,9 @@ public class MessageTransactionData extends TransactionData {
|
||||
|
||||
private int version;
|
||||
|
||||
@Schema(accessMode = AccessMode.READ_ONLY)
|
||||
private int nonce;
|
||||
|
||||
// Not always present
|
||||
private String recipient;
|
||||
|
||||
@ -47,11 +51,12 @@ public class MessageTransactionData extends TransactionData {
|
||||
}
|
||||
|
||||
public MessageTransactionData(BaseTransactionData baseTransactionData,
|
||||
int version, String recipient, long amount, Long assetId, byte[] data, boolean isText, boolean isEncrypted) {
|
||||
int version, int nonce, String recipient, long amount, Long assetId, byte[] data, boolean isText, boolean isEncrypted) {
|
||||
super(TransactionType.MESSAGE, baseTransactionData);
|
||||
|
||||
this.senderPublicKey = baseTransactionData.creatorPublicKey;
|
||||
this.version = version;
|
||||
this.nonce = nonce;
|
||||
this.recipient = recipient;
|
||||
this.amount = amount;
|
||||
this.assetId = assetId;
|
||||
@ -70,6 +75,14 @@ public class MessageTransactionData extends TransactionData {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public int getNonce() {
|
||||
return this.nonce;
|
||||
}
|
||||
|
||||
public void setNonce(int nonce) {
|
||||
this.nonce = nonce;
|
||||
}
|
||||
|
||||
public String getRecipient() {
|
||||
return this.recipient;
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ public class HSQLDBDatabaseUpdates {
|
||||
|
||||
case 6:
|
||||
// Message Transactions
|
||||
stmt.execute("CREATE TABLE MessageTransactions (signature Signature, version TINYINT NOT NULL, "
|
||||
stmt.execute("CREATE TABLE MessageTransactions (signature Signature, version TINYINT NOT NULL, nonce INT 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 + ")");
|
||||
|
@ -17,26 +17,27 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit
|
||||
}
|
||||
|
||||
TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException {
|
||||
String sql = "SELECT version, recipient, is_text, is_encrypted, amount, asset_id, data FROM MessageTransactions WHERE signature = ?";
|
||||
String sql = "SELECT version, nonce, recipient, is_text, is_encrypted, amount, asset_id, data FROM MessageTransactions WHERE signature = ?";
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) {
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
int version = resultSet.getInt(1);
|
||||
String recipient = resultSet.getString(2);
|
||||
boolean isText = resultSet.getBoolean(3);
|
||||
boolean isEncrypted = resultSet.getBoolean(4);
|
||||
long amount = resultSet.getLong(5);
|
||||
int nonce = resultSet.getInt(2);
|
||||
String recipient = resultSet.getString(3);
|
||||
boolean isText = resultSet.getBoolean(4);
|
||||
boolean isEncrypted = resultSet.getBoolean(5);
|
||||
long amount = resultSet.getLong(6);
|
||||
|
||||
// Special null-checking for asset ID
|
||||
Long assetId = resultSet.getLong(6);
|
||||
Long assetId = resultSet.getLong(7);
|
||||
if (assetId == 0 && resultSet.wasNull())
|
||||
assetId = null;
|
||||
|
||||
byte[] data = resultSet.getBytes(7);
|
||||
byte[] data = resultSet.getBytes(8);
|
||||
|
||||
return new MessageTransactionData(baseTransactionData, version, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
return new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch message transaction from repository", e);
|
||||
}
|
||||
@ -52,7 +53,7 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit
|
||||
.bind("sender", messageTransactionData.getSenderPublicKey()).bind("recipient", messageTransactionData.getRecipient())
|
||||
.bind("is_text", messageTransactionData.isText()).bind("is_encrypted", messageTransactionData.isEncrypted())
|
||||
.bind("amount", messageTransactionData.getAmount()).bind("asset_id", messageTransactionData.getAssetId())
|
||||
.bind("data", messageTransactionData.getData());
|
||||
.bind("nonce", messageTransactionData.getNonce()).bind("data", messageTransactionData.getData());
|
||||
|
||||
try {
|
||||
saveHelper.execute(this.repository);
|
||||
|
@ -136,8 +136,10 @@ public class ChatTransaction extends Transaction {
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// Nonce checking is done via isSignatureValid() as that method is only called once per import
|
||||
|
||||
// If we exist in the repository then we've been imported as unconfirmed,
|
||||
// but we don't want to make it into a block, so return false.
|
||||
// but we don't want to make it into a block, so return fake non-OK result.
|
||||
if (this.repository.getTransactionRepository().exists(this.chatTransactionData.getSignature()))
|
||||
return ValidationResult.CHAT;
|
||||
|
||||
@ -150,8 +152,6 @@ public class ChatTransaction extends Transaction {
|
||||
if (chatTransactionData.getData().length < 1 || chatTransactionData.getData().length > MAX_DATA_SIZE)
|
||||
return ValidationResult.INVALID_DATA_LENGTH;
|
||||
|
||||
// Nonce checking is done via isSignatureValid() as that method is only called once per import
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
|
@ -4,19 +4,30 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.account.PublicKeyAccount;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.crypto.MemoryPoW;
|
||||
import org.qortal.data.PaymentData;
|
||||
import org.qortal.data.transaction.MessageTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.group.Group;
|
||||
import org.qortal.payment.Payment;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.GroupRepository;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.transform.TransformationException;
|
||||
import org.qortal.transform.transaction.ChatTransactionTransformer;
|
||||
import org.qortal.transform.transaction.MessageTransactionTransformer;
|
||||
import org.qortal.transform.transaction.TransactionTransformer;
|
||||
|
||||
public class MessageTransaction extends Transaction {
|
||||
|
||||
// Useful constants
|
||||
|
||||
public static final int MAX_DATA_SIZE = 4000;
|
||||
public static final int POW_BUFFER_SIZE = 8 * 1024 * 1024; // bytes
|
||||
public static final int POW_DIFFICULTY = 14; // leading zero bits
|
||||
|
||||
// Properties
|
||||
|
||||
@ -63,8 +74,76 @@ public class MessageTransaction extends Transaction {
|
||||
return this.paymentData;
|
||||
}
|
||||
|
||||
public void computeNonce() throws DataException {
|
||||
byte[] transactionBytes;
|
||||
|
||||
try {
|
||||
transactionBytes = TransactionTransformer.toBytesForSigning(this.transactionData);
|
||||
} catch (TransformationException e) {
|
||||
throw new RuntimeException("Unable to transform transaction to byte array for verification", e);
|
||||
}
|
||||
|
||||
// Clear nonce from transactionBytes
|
||||
MessageTransactionTransformer.clearNonce(transactionBytes);
|
||||
|
||||
// Calculate nonce
|
||||
this.messageTransactionData.setNonce(MemoryPoW.compute2(transactionBytes, POW_BUFFER_SIZE, POW_DIFFICULTY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether MESSAGE transaction has valid txGroupId.
|
||||
* <p>
|
||||
* For MESSAGE transactions, a non-NO_GROUP txGroupId represents
|
||||
* sending to a group, rather than to everyone.
|
||||
* <p>
|
||||
* If txGroupId is not NO_GROUP, then the sender needs to be
|
||||
* a member of that group. The recipient, if supplied, also
|
||||
* needs to be a member of that group.
|
||||
*/
|
||||
@Override
|
||||
protected boolean isValidTxGroupId() throws DataException {
|
||||
int txGroupId = this.transactionData.getTxGroupId();
|
||||
|
||||
// txGroupId represents recipient group, unless NO_GROUP
|
||||
|
||||
// Anyone can use NO_GROUP
|
||||
if (txGroupId == Group.NO_GROUP)
|
||||
return true;
|
||||
|
||||
// Group even exist?
|
||||
if (!this.repository.getGroupRepository().groupExists(txGroupId))
|
||||
return false;
|
||||
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
|
||||
// Is transaction's creator is group member?
|
||||
PublicKeyAccount creator = this.getCreator();
|
||||
if (!groupRepository.memberExists(txGroupId, creator.getAddress()))
|
||||
return false;
|
||||
|
||||
// If recipient address present, check they belong to group too.
|
||||
String recipient = this.messageTransactionData.getRecipient();
|
||||
if (recipient != null && !groupRepository.memberExists(txGroupId, recipient))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult isFeeValid() throws DataException {
|
||||
// Allow zero or positive fee.
|
||||
// Actual enforcement of fee vs nonce is done in isSignatureValid().
|
||||
|
||||
if (this.transactionData.getFee() < 0)
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// Nonce checking is done via isSignatureValid() as that method is only called once per import
|
||||
|
||||
// Check data length
|
||||
if (this.messageTransactionData.getData().length < 1 || this.messageTransactionData.getData().length > MAX_DATA_SIZE)
|
||||
return ValidationResult.INVALID_DATA_LENGTH;
|
||||
@ -86,6 +165,36 @@ public class MessageTransaction extends Transaction {
|
||||
this.messageTransactionData.getFee(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSignatureValid() {
|
||||
byte[] signature = this.transactionData.getSignature();
|
||||
if (signature == null)
|
||||
return false;
|
||||
|
||||
byte[] transactionBytes;
|
||||
|
||||
try {
|
||||
transactionBytes = ChatTransactionTransformer.toBytesForSigning(this.transactionData);
|
||||
} catch (TransformationException e) {
|
||||
throw new RuntimeException("Unable to transform transaction to byte array for verification", e);
|
||||
}
|
||||
|
||||
if (!Crypto.verify(this.transactionData.getCreatorPublicKey(), signature, transactionBytes))
|
||||
return false;
|
||||
|
||||
// If feee is non-zero then we don't check nonce
|
||||
if (this.messageTransactionData.getFee() > 0)
|
||||
return true;
|
||||
|
||||
int nonce = this.messageTransactionData.getNonce();
|
||||
|
||||
// Clear nonce from transactionBytes
|
||||
MessageTransactionTransformer.clearNonce(transactionBytes);
|
||||
|
||||
// Check nonce
|
||||
return MemoryPoW.verify2(transactionBytes, POW_BUFFER_SIZE, POW_DIFFICULTY, nonce);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult isProcessable() throws DataException {
|
||||
// If we have no amount then we can always process
|
||||
|
@ -19,13 +19,14 @@ import com.google.common.primitives.Longs;
|
||||
public class MessageTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
// Property lengths
|
||||
private static final int NONCE_LENGTH = INT_LENGTH;
|
||||
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 = HAS_RECIPIENT_LENGTH + AMOUNT_LENGTH + DATA_SIZE_LENGTH + IS_ENCRYPTED_LENGTH + IS_TEXT_LENGTH;
|
||||
private static final int EXTRAS_LENGTH = NONCE_LENGTH + HAS_RECIPIENT_LENGTH + AMOUNT_LENGTH + DATA_SIZE_LENGTH + IS_ENCRYPTED_LENGTH + IS_TEXT_LENGTH;
|
||||
|
||||
protected static final TransactionLayout layout;
|
||||
|
||||
@ -36,10 +37,11 @@ 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("proof-of-work nonce (zero if fee not zero)", TransformationType.INT);
|
||||
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("? 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);
|
||||
@ -60,6 +62,8 @@ public class MessageTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
byte[] senderPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||
|
||||
int nonce = byteBuffer.getInt();
|
||||
|
||||
boolean hasRecipient = byteBuffer.get() != 0;
|
||||
String recipient = hasRecipient ? Serialization.deserializeAddress(byteBuffer) : null;
|
||||
|
||||
@ -86,7 +90,7 @@ public class MessageTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, senderPublicKey, fee, signature);
|
||||
|
||||
return new MessageTransactionData(baseTransactionData, version, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
return new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
}
|
||||
|
||||
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||
@ -111,6 +115,8 @@ public class MessageTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
transformCommonBytes(transactionData, bytes);
|
||||
|
||||
bytes.write(Ints.toByteArray(messageTransactionData.getNonce()));
|
||||
|
||||
if (messageTransactionData.getRecipient() != null) {
|
||||
bytes.write((byte) 1);
|
||||
Serialization.serializeAddress(bytes, messageTransactionData.getRecipient());
|
||||
@ -142,4 +148,13 @@ public class MessageTransactionTransformer extends TransactionTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearNonce(byte[] transactionBytes) {
|
||||
int nonceIndex = TYPE_LENGTH + TIMESTAMP_LENGTH + GROUPID_LENGTH + REFERENCE_LENGTH + PUBLIC_KEY_LENGTH;
|
||||
|
||||
transactionBytes[nonceIndex++] = (byte) 0;
|
||||
transactionBytes[nonceIndex++] = (byte) 0;
|
||||
transactionBytes[nonceIndex++] = (byte) 0;
|
||||
transactionBytes[nonceIndex++] = (byte) 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import static org.junit.Assert.*;
|
||||
|
||||
public class MessageTests extends Common {
|
||||
|
||||
private static final int version = 3;
|
||||
private static final int version = 4;
|
||||
private static final String recipient = Common.getTestAccount(null, "bob").getAddress();
|
||||
|
||||
|
||||
@ -69,6 +69,26 @@ public class MessageTests extends Common {
|
||||
assertFalse(isValid(newGroupId, null, 0L, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noFeeNoNonce() throws DataException {
|
||||
testFeeNonce(false, false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withFeeNoNonce() throws DataException {
|
||||
testFeeNonce(true, false, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noFeeWithNonce() throws DataException {
|
||||
testFeeNonce(false, true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withFeeWithNonce() throws DataException {
|
||||
testFeeNonce(true, true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withRecipentNoAmount() throws DataException {
|
||||
testMessage(Group.NO_GROUP, recipient, 0L, null);
|
||||
@ -105,8 +125,13 @@ public class MessageTests extends Common {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount alice = Common.getTestAccount(repository, "alice");
|
||||
|
||||
int nonce = 0;
|
||||
byte[] data = new byte[1];
|
||||
boolean isText = false;
|
||||
boolean isEncrypted = false;
|
||||
|
||||
MessageTransactionData transactionData = new MessageTransactionData(TestTransaction.generateBase(alice, txGroupId),
|
||||
version, recipient, amount, assetId, new byte[1], false, false);
|
||||
version, nonce, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
|
||||
Transaction transaction = new MessageTransaction(repository, transactionData);
|
||||
|
||||
@ -114,12 +139,51 @@ public class MessageTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
private void testFeeNonce(boolean withFee, boolean withNonce, boolean isValid) throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount alice = Common.getTestAccount(repository, "alice");
|
||||
|
||||
int txGroupId = 0;
|
||||
int nonce = 0;
|
||||
long amount = 0;
|
||||
long assetId = Asset.QORT;
|
||||
byte[] data = new byte[1];
|
||||
boolean isText = false;
|
||||
boolean isEncrypted = false;
|
||||
|
||||
MessageTransactionData transactionData = new MessageTransactionData(TestTransaction.generateBase(alice, txGroupId),
|
||||
version, nonce, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
|
||||
MessageTransaction transaction = new MessageTransaction(repository, transactionData);
|
||||
|
||||
if (withFee)
|
||||
transactionData.setFee(transaction.calcRecommendedFee());
|
||||
else
|
||||
transactionData.setFee(0L);
|
||||
|
||||
if (withNonce) {
|
||||
transaction.computeNonce();
|
||||
} else {
|
||||
transactionData.setNonce(-1);
|
||||
}
|
||||
|
||||
transaction.sign(alice);
|
||||
|
||||
assertEquals(isValid, transaction.isSignatureValid());
|
||||
}
|
||||
}
|
||||
|
||||
private void testMessage(int txGroupId, String recipient, long amount, Long assetId) throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount alice = Common.getTestAccount(repository, "alice");
|
||||
|
||||
int nonce = 0;
|
||||
byte[] data = new byte[1];
|
||||
boolean isText = false;
|
||||
boolean isEncrypted = false;
|
||||
|
||||
MessageTransactionData transactionData = new MessageTransactionData(TestTransaction.generateBase(alice, txGroupId),
|
||||
version, recipient, amount, assetId, new byte[1], false, false);
|
||||
version, nonce, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
|
||||
TransactionUtils.signAndMint(repository, transactionData, alice);
|
||||
|
||||
@ -131,8 +195,13 @@ public class MessageTests extends Common {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount alice = Common.getTestAccount(repository, "alice");
|
||||
|
||||
int nonce = 0;
|
||||
byte[] data = new byte[1];
|
||||
boolean isText = false;
|
||||
boolean isEncrypted = false;
|
||||
|
||||
MessageTransactionData expectedTransactionData = new MessageTransactionData(TestTransaction.generateBase(alice),
|
||||
version, recipient, amount, assetId, new byte[1], false, false);
|
||||
version, nonce, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
|
||||
Transaction transaction = new MessageTransaction(repository, expectedTransactionData);
|
||||
transaction.sign(alice);
|
||||
|
@ -473,11 +473,13 @@ public class AtTests extends Common {
|
||||
}
|
||||
|
||||
Long fee = null;
|
||||
int version = 4;
|
||||
int nonce = 0;
|
||||
long amount = 0;
|
||||
Long assetId = null; // because amount is zero
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
|
||||
TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, 4, recipient, amount, assetId, data, false, false);
|
||||
TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
|
||||
|
||||
MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
|
||||
|
||||
|
@ -11,7 +11,8 @@ import org.qortal.utils.Amounts;
|
||||
public class MessageTestTransaction extends TestTransaction {
|
||||
|
||||
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
|
||||
final int version = 3;
|
||||
final int version = 4;
|
||||
final int nonce = 0;
|
||||
String recipient = account.getAddress();
|
||||
final long assetId = Asset.QORT;
|
||||
long amount = 123L * Amounts.MULTIPLIER;
|
||||
@ -19,7 +20,7 @@ public class MessageTestTransaction extends TestTransaction {
|
||||
final boolean isText = true;
|
||||
final boolean isEncrypted = false;
|
||||
|
||||
return new MessageTransactionData(generateBase(account), version, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
return new MessageTransactionData(generateBase(account), version, nonce, recipient, amount, assetId, data, isText, isEncrypted);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user