forked from Qortal/qortal
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:
parent
53f44a4029
commit
bb76fa80cd
@ -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();
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user