Another significant upgrade of arbitrary transactions

Adds "name", "method", "secret", and "compression" properties. These are the foundations needed in order to handle updates, encryption, and name registration. Compression has been added so that we have the option of switching to different algorithms whilst maintaining support for existing transactions.
This commit is contained in:
CalDescent 2021-07-15 09:27:49 +01:00
parent 53f44a4029
commit bb76fa80cd
10 changed files with 199 additions and 34 deletions

View File

@ -263,6 +263,12 @@ public class ArbitraryResource {
} }
byte[] creatorPublicKey = Base58.decode(creatorPublicKeyBase58); byte[] creatorPublicKey = Base58.decode(creatorPublicKeyBase58);
String name = null;
byte[] secret = null;
ArbitraryTransactionData.Method method = ArbitraryTransactionData.Method.PUT;
ArbitraryTransactionData.Service service = ArbitraryTransactionData.Service.ARBITRARY_DATA;
ArbitraryTransactionData.Compression compression = ArbitraryTransactionData.Compression.NONE;
// Check if a file or directory has been supplied // Check if a file or directory has been supplied
File file = new File(path); File file = new File(path);
if (!file.isFile()) { if (!file.isFile()) {
@ -309,7 +315,8 @@ public class ArbitraryResource {
List<PaymentData> payments = new ArrayList<>(); List<PaymentData> payments = new ArrayList<>();
ArbitraryTransactionData transactionData = new ArbitraryTransactionData(baseTransactionData, ArbitraryTransactionData transactionData = new ArbitraryTransactionData(baseTransactionData,
5, ArbitraryTransaction.SERVICE_ARBITRARY_DATA, 0, size, digest, dataType, chunkHashes, payments); 5, service, 0, size, name, method,
secret, compression, digest, dataType, chunkHashes, payments);
ArbitraryTransaction transaction = (ArbitraryTransaction) Transaction.fromData(repository, transactionData); ArbitraryTransaction transaction = (ArbitraryTransaction) Transaction.fromData(repository, transactionData);
transaction.computeNonce(); transaction.computeNonce();

View File

@ -95,6 +95,12 @@ public class WebsiteResource {
} }
byte[] creatorPublicKey = Base58.decode(creatorPublicKeyBase58); byte[] creatorPublicKey = Base58.decode(creatorPublicKeyBase58);
String name = null;
byte[] secret = null;
ArbitraryTransactionData.Method method = ArbitraryTransactionData.Method.PUT;
ArbitraryTransactionData.Service service = ArbitraryTransactionData.Service.WEBSITE;
ArbitraryTransactionData.Compression compression = ArbitraryTransactionData.Compression.ZIP;
DataFile dataFile = this.hostWebsite(path); DataFile dataFile = this.hostWebsite(path);
if (dataFile == null) { if (dataFile == null) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA); throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
@ -120,7 +126,8 @@ public class WebsiteResource {
List<PaymentData> payments = new ArrayList<>(); List<PaymentData> payments = new ArrayList<>();
ArbitraryTransactionData transactionData = new ArbitraryTransactionData(baseTransactionData, ArbitraryTransactionData transactionData = new ArbitraryTransactionData(baseTransactionData,
5, ArbitraryTransaction.SERVICE_WEBSITE, 0, size, digest, dataType, chunkHashes, payments); 5, service, 0, size, name, method,
secret, compression, digest, dataType, chunkHashes, payments);
ArbitraryTransaction transaction = (ArbitraryTransaction) Transaction.fromData(repository, transactionData); ArbitraryTransaction transaction = (ArbitraryTransaction) Transaction.fromData(repository, transactionData);
transaction.computeNonce(); transaction.computeNonce();
@ -318,7 +325,10 @@ public class WebsiteResource {
} }
try { try {
ZipUtils.unzip(dataFile.getFilePath(), destPath); // TODO: compression types
//if (transactionData.getCompression() == ArbitraryTransactionData.Compression.ZIP) {
ZipUtils.unzip(dataFile.getFilePath(), destPath);
//}
} catch (IOException e) { } catch (IOException e) {
LOGGER.info("Unable to unzip file"); LOGGER.info("Unable to unzip file");
} }

View File

@ -1,6 +1,7 @@
package org.qortal.data.transaction; package org.qortal.data.transaction;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.xml.bind.Unmarshaller; import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessType;
@ -12,6 +13,9 @@ import org.qortal.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;
// All properties to be converted to JSON via JAXB // All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class }) @Schema(allOf = { TransactionData.class })
@ -25,21 +29,87 @@ public class ArbitraryTransactionData extends TransactionData {
DATA_HASH; DATA_HASH;
} }
// Service types
public enum Service {
AUTO_UPDATE(1),
ARBITRARY_DATA(100),
WEBSITE(200),
GIT_REPOSITORY(300),
BLOG_POST(777),
BLOG_COMMENT(778);
public final int value;
private static final Map<Integer, Service> map = stream(Service.values())
.collect(toMap(service -> service.value, service -> service));
Service(int value) {
this.value = value;
}
public static Service valueOf(int value) {
return map.get(value);
}
}
// Methods
public enum Method {
PUT(0), // A complete replacement of a resource
PATCH(1); // An update / partial replacement of a resource
public final int value;
private static final Map<Integer, Method> map = stream(Method.values())
.collect(toMap(method -> method.value, method -> method));
Method(int value) {
this.value = value;
}
public static Method valueOf(int value) {
return map.get(value);
}
}
// Compression types
public enum Compression {
NONE(0),
ZIP(1);
public final int value;
private static final Map<Integer, Compression> map = stream(Compression.values())
.collect(toMap(compression -> compression.value, compression -> compression));
Compression(int value) {
this.value = value;
}
public static Compression valueOf(int value) {
return map.get(value);
}
}
// Properties // Properties
private int version; private int version;
@Schema(example = "sender_public_key") @Schema(example = "sender_public_key")
private byte[] senderPublicKey; private byte[] senderPublicKey;
private int service; private Service service;
private int nonce; private int nonce;
private int size; private int size;
private String name;
private Method method;
private byte[] secret;
private Compression compression;
@Schema(example = "raw_data_in_base58") @Schema(example = "raw_data_in_base58")
private byte[] data; private byte[] data;
private DataType dataType; private DataType dataType;
@Schema(example = "chunk_hashes_in_base58") @Schema(example = "chunk_hashes_in_base58")
private byte[] chunkHashes; private byte[] chunkHashes;
private List<PaymentData> payments; private List<PaymentData> payments;
// Constructors // Constructors
@ -54,8 +124,9 @@ public class ArbitraryTransactionData extends TransactionData {
} }
public ArbitraryTransactionData(BaseTransactionData baseTransactionData, public ArbitraryTransactionData(BaseTransactionData baseTransactionData,
int version, int service, int nonce, int size, byte[] data, int version, Service service, int nonce, int size,
DataType dataType, byte[] chunkHashes, List<PaymentData> payments) { String name, Method method, byte[] secret, Compression compression,
byte[] data, DataType dataType, byte[] chunkHashes, List<PaymentData> payments) {
super(TransactionType.ARBITRARY, baseTransactionData); super(TransactionType.ARBITRARY, baseTransactionData);
this.senderPublicKey = baseTransactionData.creatorPublicKey; this.senderPublicKey = baseTransactionData.creatorPublicKey;
@ -63,6 +134,10 @@ public class ArbitraryTransactionData extends TransactionData {
this.service = service; this.service = service;
this.nonce = nonce; this.nonce = nonce;
this.size = size; this.size = size;
this.name = name;
this.method = method;
this.secret = secret;
this.compression = compression;
this.data = data; this.data = data;
this.dataType = dataType; this.dataType = dataType;
this.chunkHashes = chunkHashes; this.chunkHashes = chunkHashes;
@ -79,7 +154,7 @@ public class ArbitraryTransactionData extends TransactionData {
return this.version; return this.version;
} }
public int getService() { public Service getService() {
return this.service; return this.service;
} }
@ -95,6 +170,22 @@ public class ArbitraryTransactionData extends TransactionData {
return this.size; return this.size;
} }
public String getName() {
return this.name;
}
public Method getMethod() {
return this.method;
}
public byte[] getSecret() {
return this.secret;
}
public Compression getCompression() {
return this.compression;
}
public byte[] getData() { public byte[] getData() {
return this.data; return this.data;
} }

View File

@ -4,8 +4,6 @@ import java.sql.Connection;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -781,10 +779,18 @@ public class HSQLDBDatabaseUpdates {
stmt.execute("ALTER TABLE ArbitraryTransactions ADD chunk_hashes ArbitraryDataHashes"); stmt.execute("ALTER TABLE ArbitraryTransactions ADD chunk_hashes ArbitraryDataHashes");
// For finding data files by hash // For finding data files by hash
stmt.execute("CREATE INDEX ArbitraryDataIndex ON ArbitraryTransactions (is_data_raw, data)"); stmt.execute("CREATE INDEX ArbitraryDataIndex ON ArbitraryTransactions (is_data_raw, data)");
// TODO: resource ID, compression, layers
break; break;
case 35:
// We need the ability for arbitrary transactions to be associated with a name
stmt.execute("ALTER TABLE ArbitraryTransactions ADD name RegisteredName");
// A "method" specifies how the data should be applied (e.g. PUT or PATCH)
stmt.execute("ALTER TABLE ArbitraryTransactions ADD update_method INTEGER NOT NULL DEFAULT 0");
// For public data, the AES shared secret needs to be available. This is more for data obfuscation as apposed to actual encryption.
stmt.execute("ALTER TABLE ArbitraryTransactions ADD secret VARBINARY(32)");
// We want to support compressed and uncompressed data, as well as different compression algorithms
stmt.execute("ALTER TABLE ArbitraryTransactions ADD compression INTEGER NOT NULL DEFAULT 0");
default: default:
// nothing to do // nothing to do
return false; return false;

View File

@ -20,7 +20,8 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
} }
TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException { TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException {
String sql = "SELECT version, nonce, service, size, is_data_raw, data, chunk_hashes from ArbitraryTransactions WHERE signature = ?"; String sql = "SELECT version, nonce, service, size, is_data_raw, data, chunk_hashes, " +
"name, update_method, secret, compression from ArbitraryTransactions WHERE signature = ?";
try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) { try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) {
if (resultSet == null) if (resultSet == null)
@ -28,15 +29,20 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
int version = resultSet.getInt(1); int version = resultSet.getInt(1);
int nonce = resultSet.getInt(2); int nonce = resultSet.getInt(2);
int service = resultSet.getInt(3); ArbitraryTransactionData.Service service = ArbitraryTransactionData.Service.valueOf(resultSet.getInt(3));
int size = resultSet.getInt(4); int size = resultSet.getInt(4);
boolean isDataRaw = resultSet.getBoolean(5); // NOT NULL, so no null to false boolean isDataRaw = resultSet.getBoolean(5); // NOT NULL, so no null to false
DataType dataType = isDataRaw ? DataType.RAW_DATA : DataType.DATA_HASH; DataType dataType = isDataRaw ? DataType.RAW_DATA : DataType.DATA_HASH;
byte[] data = resultSet.getBytes(6); byte[] data = resultSet.getBytes(6);
byte[] chunkHashes = resultSet.getBytes(7); byte[] chunkHashes = resultSet.getBytes(7);
String name = resultSet.getString(8);
ArbitraryTransactionData.Method method = ArbitraryTransactionData.Method.valueOf(resultSet.getInt(9));
byte[] secret = resultSet.getBytes(10);
ArbitraryTransactionData.Compression compression = ArbitraryTransactionData.Compression.valueOf(resultSet.getInt(11));
List<PaymentData> payments = this.getPaymentsFromSignature(baseTransactionData.getSignature()); List<PaymentData> payments = this.getPaymentsFromSignature(baseTransactionData.getSignature());
return new ArbitraryTransactionData(baseTransactionData, version, service, nonce, size, data, dataType, chunkHashes, payments); return new ArbitraryTransactionData(baseTransactionData, version, service, nonce, size, name, method,
secret, compression, data, dataType, chunkHashes, payments);
} catch (SQLException e) { } catch (SQLException e) {
throw new DataException("Unable to fetch arbitrary transaction from repository", e); throw new DataException("Unable to fetch arbitrary transaction from repository", e);
} }
@ -56,7 +62,9 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
.bind("version", arbitraryTransactionData.getVersion()).bind("service", arbitraryTransactionData.getService()) .bind("version", arbitraryTransactionData.getVersion()).bind("service", arbitraryTransactionData.getService())
.bind("nonce", arbitraryTransactionData.getNonce()).bind("size", arbitraryTransactionData.getSize()) .bind("nonce", arbitraryTransactionData.getNonce()).bind("size", arbitraryTransactionData.getSize())
.bind("is_data_raw", arbitraryTransactionData.getDataType() == DataType.RAW_DATA).bind("data", arbitraryTransactionData.getData()) .bind("is_data_raw", arbitraryTransactionData.getDataType() == DataType.RAW_DATA).bind("data", arbitraryTransactionData.getData())
.bind("chunk_hashes", arbitraryTransactionData.getChunkHashes()); .bind("chunk_hashes", arbitraryTransactionData.getChunkHashes()).bind("name", arbitraryTransactionData.getName())
.bind("method", arbitraryTransactionData.getMethod()).bind("secret", arbitraryTransactionData.getSecret())
.bind("compression", arbitraryTransactionData.getCompression());
try { try {
saveHelper.execute(this.repository); saveHelper.execute(this.repository);

View File

@ -24,15 +24,6 @@ public class ArbitraryTransaction extends Transaction {
// Properties // Properties
private ArbitraryTransactionData arbitraryTransactionData; private ArbitraryTransactionData arbitraryTransactionData;
// Services
public static final int SERVICE_AUTO_UPDATE = 1;
public static final int SERVICE_NAME_STORAGE = 10;
public static final int SERVICE_ARBITRARY_DATA = 100;
public static final int SERVICE_WEBSITE = 200;
public static final int SERVICE_GIT_REPOSITORY = 300;
public static final int SERVICE_BLOG_POST = 777;
public static final int SERVICE_BLOG_COMMENT = 778;
// Other useful constants // Other useful constants
public static final int MAX_DATA_SIZE = 4000; public static final int MAX_DATA_SIZE = 4000;
public static final int MAX_CHUNK_HASHES_LENGTH = 8000; public static final int MAX_CHUNK_HASHES_LENGTH = 8000;

View File

@ -20,5 +20,6 @@ public abstract class Transformer {
public static final int MD5_LENGTH = 16; public static final int MD5_LENGTH = 16;
public static final int SHA256_LENGTH = 32; public static final int SHA256_LENGTH = 32;
public static final int AES256_LENGTH = 32;
} }

View File

@ -6,12 +6,14 @@ import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.google.common.base.Utf8;
import org.qortal.crypto.Crypto; import org.qortal.crypto.Crypto;
import org.qortal.data.PaymentData; import org.qortal.data.PaymentData;
import org.qortal.data.transaction.ArbitraryTransactionData; import org.qortal.data.transaction.ArbitraryTransactionData;
import org.qortal.data.transaction.BaseTransactionData; import org.qortal.data.transaction.BaseTransactionData;
import org.qortal.data.transaction.TransactionData; import org.qortal.data.transaction.TransactionData;
import org.qortal.data.transaction.ArbitraryTransactionData.DataType; import org.qortal.data.transaction.ArbitraryTransactionData.DataType;
import org.qortal.naming.Name;
import org.qortal.transaction.ArbitraryTransaction; import org.qortal.transaction.ArbitraryTransaction;
import org.qortal.transaction.Transaction; import org.qortal.transaction.Transaction;
import org.qortal.transaction.Transaction.TransactionType; import org.qortal.transaction.Transaction.TransactionType;
@ -32,8 +34,12 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
private static final int RAW_DATA_SIZE_LENGTH = INT_LENGTH; private static final int RAW_DATA_SIZE_LENGTH = INT_LENGTH;
private static final int CHUNKS_SIZE_LENGTH = INT_LENGTH; private static final int CHUNKS_SIZE_LENGTH = INT_LENGTH;
private static final int NUMBER_PAYMENTS_LENGTH = INT_LENGTH; private static final int NUMBER_PAYMENTS_LENGTH = INT_LENGTH;
private static final int NAME_SIZE_LENGTH = INT_LENGTH;
private static final int SECRET_LENGTH = AES256_LENGTH;
private static final int COMPRESSION_LENGTH = INT_LENGTH;
private static final int EXTRAS_LENGTH = SERVICE_LENGTH + NONCE_LENGTH + DATA_TYPE_LENGTH + DATA_SIZE_LENGTH + RAW_DATA_SIZE_LENGTH + CHUNKS_SIZE_LENGTH; private static final int EXTRAS_LENGTH = SERVICE_LENGTH + NONCE_LENGTH + NAME_SIZE_LENGTH + SERVICE_LENGTH +
COMPRESSION_LENGTH + DATA_TYPE_LENGTH + DATA_SIZE_LENGTH + RAW_DATA_SIZE_LENGTH + CHUNKS_SIZE_LENGTH;
protected static final TransactionLayout layout; protected static final TransactionLayout layout;
@ -46,6 +52,11 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
layout.add("sender's public key", TransformationType.PUBLIC_KEY); layout.add("sender's public key", TransformationType.PUBLIC_KEY);
layout.add("nonce", TransformationType.INT); // Version 5+ layout.add("nonce", TransformationType.INT); // Version 5+
layout.add("name", TransformationType.DATA); // Version 5+
layout.add("method", TransformationType.INT); // Version 5+
layout.add("secret", TransformationType.DATA); // Version 5+
layout.add("compression", TransformationType.INT); // Version 5+
layout.add("number of payments", TransformationType.INT); layout.add("number of payments", TransformationType.INT);
layout.add("* recipient", TransformationType.ADDRESS); layout.add("* recipient", TransformationType.ADDRESS);
layout.add("* asset ID of payment", TransformationType.LONG); layout.add("* asset ID of payment", TransformationType.LONG);
@ -77,8 +88,22 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
byte[] senderPublicKey = Serialization.deserializePublicKey(byteBuffer); byte[] senderPublicKey = Serialization.deserializePublicKey(byteBuffer);
int nonce = 0; int nonce = 0;
String name = null;
ArbitraryTransactionData.Method method = null;
byte[] secret = null;
ArbitraryTransactionData.Compression compression = null;
if (version >= 5) { if (version >= 5) {
nonce = byteBuffer.getInt(); nonce = byteBuffer.getInt();
name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE);
method = ArbitraryTransactionData.Method.valueOf(byteBuffer.getInt());
secret = new byte[SECRET_LENGTH];
byteBuffer.get(secret);
compression = ArbitraryTransactionData.Compression.valueOf(byteBuffer.getInt());
} }
// Always return a list of payments, even if empty // Always return a list of payments, even if empty
@ -90,7 +115,7 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
payments.add(PaymentTransformer.fromByteBuffer(byteBuffer)); payments.add(PaymentTransformer.fromByteBuffer(byteBuffer));
} }
int service = byteBuffer.getInt(); ArbitraryTransactionData.Service service = ArbitraryTransactionData.Service.valueOf(byteBuffer.getInt());
// We might be receiving hash of data instead of actual raw data // We might be receiving hash of data instead of actual raw data
boolean isRaw = byteBuffer.get() != 0; boolean isRaw = byteBuffer.get() != 0;
@ -124,16 +149,17 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, senderPublicKey, fee, signature); BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, senderPublicKey, fee, signature);
return new ArbitraryTransactionData(baseTransactionData, version, service, nonce, size, data, dataType, chunkHashes, payments); return new ArbitraryTransactionData(baseTransactionData, version, service, nonce, size, name, method, secret, compression, data, dataType, chunkHashes, payments);
} }
public static int getDataLength(TransactionData transactionData) throws TransformationException { public static int getDataLength(TransactionData transactionData) throws TransformationException {
ArbitraryTransactionData arbitraryTransactionData = (ArbitraryTransactionData) transactionData; ArbitraryTransactionData arbitraryTransactionData = (ArbitraryTransactionData) transactionData;
int nameLength = Utf8.encodedLength(arbitraryTransactionData.getName());
int dataLength = (arbitraryTransactionData.getData() != null) ? arbitraryTransactionData.getData().length : 0; int dataLength = (arbitraryTransactionData.getData() != null) ? arbitraryTransactionData.getData().length : 0;
int chunkHashesLength = (arbitraryTransactionData.getChunkHashes() != null) ? arbitraryTransactionData.getChunkHashes().length : 0; int chunkHashesLength = (arbitraryTransactionData.getChunkHashes() != null) ? arbitraryTransactionData.getChunkHashes().length : 0;
int length = getBaseLength(transactionData) + EXTRAS_LENGTH + dataLength + chunkHashesLength; int length = getBaseLength(transactionData) + EXTRAS_LENGTH + nameLength + dataLength + chunkHashesLength;
// Optional payments // Optional payments
length += NUMBER_PAYMENTS_LENGTH + arbitraryTransactionData.getPayments().size() * PaymentTransformer.getDataLength(); length += NUMBER_PAYMENTS_LENGTH + arbitraryTransactionData.getPayments().size() * PaymentTransformer.getDataLength();
@ -151,6 +177,14 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
if (arbitraryTransactionData.getVersion() >= 5) { if (arbitraryTransactionData.getVersion() >= 5) {
bytes.write(Ints.toByteArray(arbitraryTransactionData.getNonce())); bytes.write(Ints.toByteArray(arbitraryTransactionData.getNonce()));
Serialization.serializeSizedString(bytes, arbitraryTransactionData.getName());
bytes.write(Ints.toByteArray(arbitraryTransactionData.getMethod().value));
bytes.write(arbitraryTransactionData.getSecret());
bytes.write(Ints.toByteArray(arbitraryTransactionData.getCompression().value));
} }
List<PaymentData> payments = arbitraryTransactionData.getPayments(); List<PaymentData> payments = arbitraryTransactionData.getPayments();
@ -159,7 +193,7 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
for (PaymentData paymentData : payments) for (PaymentData paymentData : payments)
bytes.write(PaymentTransformer.toBytes(paymentData)); bytes.write(PaymentTransformer.toBytes(paymentData));
bytes.write(Ints.toByteArray(arbitraryTransactionData.getService())); bytes.write(Ints.toByteArray(arbitraryTransactionData.getService().value));
bytes.write((byte) (arbitraryTransactionData.getDataType() == DataType.RAW_DATA ? 1 : 0)); bytes.write((byte) (arbitraryTransactionData.getDataType() == DataType.RAW_DATA ? 1 : 0));
@ -204,6 +238,14 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
if (arbitraryTransactionData.getVersion() >= 5) { if (arbitraryTransactionData.getVersion() >= 5) {
bytes.write(Ints.toByteArray(arbitraryTransactionData.getNonce())); bytes.write(Ints.toByteArray(arbitraryTransactionData.getNonce()));
Serialization.serializeSizedString(bytes, arbitraryTransactionData.getName());
bytes.write(Ints.toByteArray(arbitraryTransactionData.getMethod().value));
bytes.write(arbitraryTransactionData.getSecret());
bytes.write(Ints.toByteArray(arbitraryTransactionData.getCompression().value));
} }
if (arbitraryTransactionData.getVersion() != 1) { if (arbitraryTransactionData.getVersion() != 1) {
@ -214,7 +256,7 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
bytes.write(PaymentTransformer.toBytes(paymentData)); bytes.write(PaymentTransformer.toBytes(paymentData));
} }
bytes.write(Ints.toByteArray(arbitraryTransactionData.getService())); bytes.write(Ints.toByteArray(arbitraryTransactionData.getService().value));
bytes.write(Ints.toByteArray(arbitraryTransactionData.getData().length)); bytes.write(Ints.toByteArray(arbitraryTransactionData.getData().length));

View File

@ -36,10 +36,14 @@ public class ArbitraryTransactionTests extends Common {
TestAccount alice = Common.getTestAccount(repository, "alice"); TestAccount alice = Common.getTestAccount(repository, "alice");
ArbitraryTransactionData.DataType dataType = ArbitraryTransactionData.DataType.DATA_HASH; ArbitraryTransactionData.DataType dataType = ArbitraryTransactionData.DataType.DATA_HASH;
ArbitraryTransactionData.Service service = ArbitraryTransactionData.Service.ARBITRARY_DATA;
ArbitraryTransactionData.Method method = ArbitraryTransactionData.Method.PUT;
ArbitraryTransactionData.Compression compression = ArbitraryTransactionData.Compression.NONE;
List<PaymentData> payments = new ArrayList<>(); List<PaymentData> payments = new ArrayList<>();
ArbitraryTransactionData transactionData = new ArbitraryTransactionData(TestTransaction.generateBase(alice), ArbitraryTransactionData transactionData = new ArbitraryTransactionData(TestTransaction.generateBase(alice),
5, ArbitraryTransaction.SERVICE_ARBITRARY_DATA, 0, 0, null, dataType, null, payments); 5, service, 0, 0, null, method,
null, compression, null, dataType, null, payments);
ArbitraryTransaction transaction = (ArbitraryTransaction) Transaction.fromData(repository, transactionData); ArbitraryTransaction transaction = (ArbitraryTransaction) Transaction.fromData(repository, transactionData);
assertEquals(12, transaction.difficultyForFileSize(1)); assertEquals(12, transaction.difficultyForFileSize(1));

View File

@ -17,9 +17,13 @@ public class ArbitraryTestTransaction extends TestTransaction {
public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException { public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException {
final int version = 4; final int version = 4;
final int service = 123; final ArbitraryTransactionData.Service service = ArbitraryTransactionData.Service.ARBITRARY_DATA;
final int nonce = 0; // Version 4 doesn't need a nonce final int nonce = 0; // Version 4 doesn't need a nonce
final int size = 0; // Version 4 doesn't need a size final int size = 0; // Version 4 doesn't need a size
final String name = null; // Version 4 doesn't need a name
final ArbitraryTransactionData.Method method = ArbitraryTransactionData.Method.PUT; // Version 4 doesn't need a method
final byte[] secret = null; // Version 4 doesn't need a secret
final ArbitraryTransactionData.Compression compression = ArbitraryTransactionData.Compression.NONE; // Version 4 doesn't use compression
final byte[] chunkHashes = null; // Version 4 doesn't use chunk hashes final byte[] chunkHashes = null; // Version 4 doesn't use chunk hashes
byte[] data = new byte[1024]; byte[] data = new byte[1024];
@ -34,7 +38,8 @@ public class ArbitraryTestTransaction extends TestTransaction {
List<PaymentData> payments = new ArrayList<>(); List<PaymentData> payments = new ArrayList<>();
payments.add(new PaymentData(recipient, assetId, amount)); payments.add(new PaymentData(recipient, assetId, amount));
return new ArbitraryTransactionData(generateBase(account), version, service, nonce, size, data, dataType, chunkHashes, payments); return new ArbitraryTransactionData(generateBase(account), version, service, nonce, size, name, method,
secret, compression, data, dataType, chunkHashes, payments);
} }
} }