Added "identifier" property to arbitrary transactions

Until now we have been limited to one data resource per name/service combination. This meant that each name could only have a single website, git repo, image, video, etc, and adding another would overwrite the previous data. The identifier property now allows an optional string to be supplied with each resource, therefore allowing an unlimited amount of resources per name/service combination.

Some examples of what this will allow us to do:

- Create a video library app which holds multiple videos per name
- Same as above but for photos
- Store multiple images against each name, such as an avatar, website thumbnails, video thumbnails, etc. This will be necessary for many "system level" features.
- Attach multiple websites to each name. The default website (with blank/null identifier) would remain the entry point, but other websites could be hosted essentially as subdomains, and then linked from the default site. This also provides a means to go beyond the 500MB website size limit.

Not all of these features will exist initially, but having this identifier included in the protocol layer allows them to be added at any time.
This commit is contained in:
CalDescent 2021-11-07 18:39:43 +00:00
parent a0fe1a85f1
commit 991125034e
11 changed files with 88 additions and 49 deletions

View File

@ -353,11 +353,12 @@ public class ArbitraryResource {
)
public String post(@PathParam("service") String serviceString,
@PathParam("name") String name,
@QueryParam("identifier") String identifier,
String path) {
Security.checkApiCallAllowed(request);
// TODO: automatic PUT/PATCH
return this.upload(Method.PUT, Service.valueOf(serviceString), name, path);
return this.upload(Method.PUT, Service.valueOf(serviceString), name, identifier, path);
}
@PUT
@ -388,10 +389,11 @@ public class ArbitraryResource {
)
public String put(@PathParam("service") String serviceString,
@PathParam("name") String name,
@QueryParam("identifier") String identifier,
String path) {
Security.checkApiCallAllowed(request);
return this.upload(Method.PUT, Service.valueOf(serviceString), name, path);
return this.upload(Method.PUT, Service.valueOf(serviceString), name, identifier, path);
}
@PATCH
@ -422,14 +424,15 @@ public class ArbitraryResource {
}
)
public String patch(@PathParam("service") String serviceString,
@PathParam("name") String name,
@PathParam("name") String name,
@QueryParam("identifier") String identifier,
String path) {
Security.checkApiCallAllowed(request);
return this.upload(Method.PATCH, Service.valueOf(serviceString), name, path);
return this.upload(Method.PATCH, Service.valueOf(serviceString), name, identifier, path);
}
private String upload(Method method, Service service, String name, String path) {
private String upload(Method method, Service service, String name, String identifier, String path) {
// It's too dangerous to allow user-supplied file paths in weaker security contexts
if (Settings.getInstance().isApiRestricted()) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.NON_PRODUCTION);
@ -452,7 +455,7 @@ public class ArbitraryResource {
try {
ArbitraryDataTransactionBuilder transactionBuilder = new ArbitraryDataTransactionBuilder(
publicKey58, Paths.get(path), name, method, service
publicKey58, Paths.get(path), name, method, service, identifier
);
ArbitraryTransactionData transactionData = transactionBuilder.build();

View File

@ -36,13 +36,16 @@ public class ArbitraryDataTransactionBuilder {
private String name;
private Method method;
private Service service;
private String identifier;
public ArbitraryDataTransactionBuilder(String publicKey58, Path path, String name, Method method, Service service) {
public ArbitraryDataTransactionBuilder(String publicKey58, Path path, String name,
Method method, Service service, String identifier) {
this.publicKey58 = publicKey58;
this.path = path;
this.name = name;
this.method = method;
this.service = service;
this.identifier = identifier;
}
public ArbitraryTransactionData build() throws DataException {
@ -97,7 +100,7 @@ public class ArbitraryDataTransactionBuilder {
final List<PaymentData> payments = new ArrayList<>();
ArbitraryTransactionData transactionData = new ArbitraryTransactionData(baseTransactionData,
version, service, nonce, size, name, method,
version, service, nonce, size, name, identifier, method,
secret, compression, digest, dataType, chunkHashes, payments);
ArbitraryTransaction transaction = (ArbitraryTransaction) Transaction.fromData(repository, transactionData);

View File

@ -100,6 +100,7 @@ public class ArbitraryTransactionData extends TransactionData {
private int size;
private String name;
private String identifier;
private Method method;
private byte[] secret;
private Compression compression;
@ -125,7 +126,7 @@ public class ArbitraryTransactionData extends TransactionData {
public ArbitraryTransactionData(BaseTransactionData baseTransactionData,
int version, Service service, int nonce, int size,
String name, Method method, byte[] secret, Compression compression,
String name, String identifier, Method method, byte[] secret, Compression compression,
byte[] data, DataType dataType, byte[] chunkHashes, List<PaymentData> payments) {
super(TransactionType.ARBITRARY, baseTransactionData);
@ -135,6 +136,7 @@ public class ArbitraryTransactionData extends TransactionData {
this.nonce = nonce;
this.size = size;
this.name = name;
this.identifier = identifier;
this.method = method;
this.secret = secret;
this.compression = compression;
@ -174,6 +176,10 @@ public class ArbitraryTransactionData extends TransactionData {
return this.name;
}
public String getIdentifier() {
return this.identifier;
}
public Method getMethod() {
return this.method;
}

View File

@ -157,7 +157,7 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository {
String sql = "SELECT type, reference, signature, creator, created_when, fee, " +
"tx_group_id, block_height, approval_status, approval_height, " +
"version, nonce, service, size, is_data_raw, data, chunk_hashes, " +
"name, update_method, secret, compression FROM ArbitraryTransactions " +
"name, identifier, update_method, secret, compression FROM ArbitraryTransactions " +
"JOIN Transactions USING (signature) " +
"WHERE lower(name) = ? AND service = ? AND created_when >= ? " +
"ORDER BY created_when ASC";
@ -201,14 +201,15 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository {
byte[] data = resultSet.getBytes(16);
byte[] chunkHashes = resultSet.getBytes(17);
String nameResult = resultSet.getString(18);
Method method = Method.valueOf(resultSet.getInt(19));
byte[] secret = resultSet.getBytes(20);
Compression compression = Compression.valueOf(resultSet.getInt(21));
String identifierResult = resultSet.getString(19);
Method method = Method.valueOf(resultSet.getInt(20));
byte[] secret = resultSet.getBytes(21);
Compression compression = Compression.valueOf(resultSet.getInt(22));
List<PaymentData> payments = new ArrayList<>(); // TODO: this.getPaymentsFromSignature(baseTransactionData.getSignature());
ArbitraryTransactionData transactionData = new ArbitraryTransactionData(baseTransactionData,
version, serviceResult, nonce, size, nameResult, method, secret, compression, data,
dataType, chunkHashes, payments);
version, serviceResult, nonce, size, nameResult, identifierResult, method, secret,
compression, data, dataType, chunkHashes, payments);
arbitraryTransactionData.add(transactionData);
} while (resultSet.next());
@ -226,7 +227,7 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository {
sql.append("SELECT type, reference, signature, creator, created_when, fee, " +
"tx_group_id, block_height, approval_status, approval_height, " +
"version, nonce, service, size, is_data_raw, data, chunk_hashes, " +
"name, update_method, secret, compression FROM ArbitraryTransactions " +
"name, identifier, update_method, secret, compression FROM ArbitraryTransactions " +
"JOIN Transactions USING (signature) " +
"WHERE lower(name) = ? AND service = ?");
@ -274,14 +275,15 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository {
byte[] data = resultSet.getBytes(16);
byte[] chunkHashes = resultSet.getBytes(17);
String nameResult = resultSet.getString(18);
Method methodResult = Method.valueOf(resultSet.getInt(19));
byte[] secret = resultSet.getBytes(20);
Compression compression = Compression.valueOf(resultSet.getInt(21));
String identifierResult = resultSet.getString(19);
Method methodResult = Method.valueOf(resultSet.getInt(20));
byte[] secret = resultSet.getBytes(21);
Compression compression = Compression.valueOf(resultSet.getInt(22));
List<PaymentData> payments = new ArrayList<>(); // TODO: this.getPaymentsFromSignature(baseTransactionData.getSignature());
ArbitraryTransactionData transactionData = new ArbitraryTransactionData(baseTransactionData,
version, serviceResult, nonce, size, nameResult, methodResult, secret, compression, data,
dataType, chunkHashes, payments);
version, serviceResult, nonce, size, nameResult, identifierResult, methodResult, secret,
compression, data, dataType, chunkHashes, payments);
return transactionData;
} catch (SQLException e) {

View File

@ -920,6 +920,8 @@ public class HSQLDBDatabaseUpdates {
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");
// An optional identifier string can be used to allow more than one resource per user/service combo
stmt.execute("ALTER TABLE ArbitraryTransactions ADD identifier VARCHAR(64)");
// For finding transactions by registered name
stmt.execute("CREATE INDEX ArbitraryNameIndex ON ArbitraryTransactions (name)");
break;

View File

@ -21,7 +21,8 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException {
String sql = "SELECT version, nonce, service, size, is_data_raw, data, chunk_hashes, " +
"name, update_method, secret, compression from ArbitraryTransactions WHERE signature = ?";
"name, identifier, update_method, secret, compression from ArbitraryTransactions " +
"WHERE signature = ?";
try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) {
if (resultSet == null)
@ -36,13 +37,14 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
byte[] data = resultSet.getBytes(6);
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));
String identifier = resultSet.getString(9);
ArbitraryTransactionData.Method method = ArbitraryTransactionData.Method.valueOf(resultSet.getInt(10));
byte[] secret = resultSet.getBytes(11);
ArbitraryTransactionData.Compression compression = ArbitraryTransactionData.Compression.valueOf(resultSet.getInt(12));
List<PaymentData> payments = this.getPaymentsFromSignature(baseTransactionData.getSignature());
return new ArbitraryTransactionData(baseTransactionData, version, service, nonce, size, name, method,
secret, compression, data, dataType, chunkHashes, payments);
return new ArbitraryTransactionData(baseTransactionData, version, service, nonce, size, name,
identifier, method, secret, compression, data, dataType, chunkHashes, payments);
} catch (SQLException e) {
throw new DataException("Unable to fetch arbitrary transaction from repository", e);
}
@ -63,8 +65,8 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
.bind("nonce", arbitraryTransactionData.getNonce()).bind("size", arbitraryTransactionData.getSize())
.bind("is_data_raw", arbitraryTransactionData.getDataType() == DataType.RAW_DATA).bind("data", arbitraryTransactionData.getData())
.bind("chunk_hashes", arbitraryTransactionData.getChunkHashes()).bind("name", arbitraryTransactionData.getName())
.bind("update_method", arbitraryTransactionData.getMethod().value).bind("secret", arbitraryTransactionData.getSecret())
.bind("compression", arbitraryTransactionData.getCompression().value);
.bind("identifier", arbitraryTransactionData.getIdentifier()).bind("update_method", arbitraryTransactionData.getMethod().value)
.bind("secret", arbitraryTransactionData.getSecret()).bind("compression", arbitraryTransactionData.getCompression().value);
try {
saveHelper.execute(this.repository);

View File

@ -35,6 +35,7 @@ public class ArbitraryTransaction extends Transaction {
public static final int POW_MIN_DIFFICULTY = 12; // leading zero bits
public static final int POW_MAX_DIFFICULTY = 19; // leading zero bits
public static final long MAX_FILE_SIZE = ArbitraryDataFile.MAX_FILE_SIZE;
public static final int MAX_IDENTIFIER_LENGTH = 64;
// Constructors

View File

@ -35,14 +35,15 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
private static final int CHUNKS_SIZE_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 IDENTIFIER_SIZE_LENGTH = INT_LENGTH;
private static final int COMPRESSION_LENGTH = INT_LENGTH;
private static final int METHOD_LENGTH = INT_LENGTH;
private static final int SECRET_LENGTH = INT_LENGTH;
private static final int EXTRAS_LENGTH = SERVICE_LENGTH + DATA_TYPE_LENGTH + DATA_SIZE_LENGTH;
private static final int EXTRAS_V5_LENGTH = NONCE_LENGTH + NAME_SIZE_LENGTH + METHOD_LENGTH + SECRET_LENGTH +
COMPRESSION_LENGTH + RAW_DATA_SIZE_LENGTH + CHUNKS_SIZE_LENGTH;
private static final int EXTRAS_V5_LENGTH = NONCE_LENGTH + NAME_SIZE_LENGTH + IDENTIFIER_SIZE_LENGTH +
METHOD_LENGTH + SECRET_LENGTH + COMPRESSION_LENGTH + RAW_DATA_SIZE_LENGTH + CHUNKS_SIZE_LENGTH;
protected static final TransactionLayout layout;
@ -57,6 +58,8 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
layout.add("name length", TransformationType.INT); // Version 5+
layout.add("name", TransformationType.DATA); // Version 5+
layout.add("identifier length", TransformationType.INT); // Version 5+
layout.add("identifier", TransformationType.DATA); // Version 5+
layout.add("method", TransformationType.INT); // Version 5+
layout.add("secret length", TransformationType.INT); // Version 5+
layout.add("secret", TransformationType.DATA); // Version 5+
@ -94,6 +97,7 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
int nonce = 0;
String name = null;
String identifier = null;
ArbitraryTransactionData.Method method = null;
byte[] secret = null;
ArbitraryTransactionData.Compression compression = null;
@ -103,6 +107,8 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE);
identifier = Serialization.deserializeSizedString(byteBuffer, ArbitraryTransaction.MAX_IDENTIFIER_LENGTH);
method = ArbitraryTransactionData.Method.valueOf(byteBuffer.getInt());
int secretLength = byteBuffer.getInt();
@ -160,18 +166,20 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, senderPublicKey, fee, signature);
return new ArbitraryTransactionData(baseTransactionData, version, service, nonce, size, name, method, secret, compression, data, dataType, chunkHashes, payments);
return new ArbitraryTransactionData(baseTransactionData, version, service, nonce, size, name, identifier,
method, secret, compression, data, dataType, chunkHashes, payments);
}
public static int getDataLength(TransactionData transactionData) throws TransformationException {
ArbitraryTransactionData arbitraryTransactionData = (ArbitraryTransactionData) transactionData;
int nameLength = (arbitraryTransactionData.getName() != null) ? Utf8.encodedLength(arbitraryTransactionData.getName()) : 0;
int identifierLength = (arbitraryTransactionData.getIdentifier() != null) ? Utf8.encodedLength(arbitraryTransactionData.getIdentifier()) : 0;
int secretLength = (arbitraryTransactionData.getSecret() != null) ? arbitraryTransactionData.getSecret().length : 0;
int dataLength = (arbitraryTransactionData.getData() != null) ? arbitraryTransactionData.getData().length : 0;
int chunkHashesLength = (arbitraryTransactionData.getChunkHashes() != null) ? arbitraryTransactionData.getChunkHashes().length : 0;
int length = getBaseLength(transactionData) + EXTRAS_LENGTH + nameLength + secretLength + dataLength + chunkHashesLength;
int length = getBaseLength(transactionData) + EXTRAS_LENGTH + nameLength + identifierLength + secretLength + dataLength + chunkHashesLength;
if (arbitraryTransactionData.getVersion() >= 5) {
length += EXTRAS_V5_LENGTH;
@ -196,6 +204,8 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
Serialization.serializeSizedString(bytes, arbitraryTransactionData.getName());
Serialization.serializeSizedString(bytes, arbitraryTransactionData.getIdentifier());
bytes.write(Ints.toByteArray(arbitraryTransactionData.getMethod().value));
byte[] secret = arbitraryTransactionData.getSecret();
@ -265,6 +275,8 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
Serialization.serializeSizedString(bytes, arbitraryTransactionData.getName());
Serialization.serializeSizedString(bytes, arbitraryTransactionData.getIdentifier());
bytes.write(Ints.toByteArray(arbitraryTransactionData.getMethod().value));
byte[] secret = arbitraryTransactionData.getSecret();

View File

@ -43,6 +43,7 @@ public class ArbitraryDataTests extends Common {
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
String publicKey58 = Base58.encode(alice.getPublicKey());
String name = "TEST"; // Can be anything for this test
String identifier = null; // Not used for this test
Service service = Service.WEBSITE; // Can be anything for this test
// Register the name to Alice
@ -51,15 +52,15 @@ public class ArbitraryDataTests extends Common {
// Create PUT transaction
Path path1 = Paths.get("src/test/resources/arbitrary/demo1");
this.createAndMintTxn(repository, publicKey58, path1, name, Method.PUT, service, alice);
this.createAndMintTxn(repository, publicKey58, path1, name, identifier, Method.PUT, service, alice);
// Create PATCH transaction
Path path2 = Paths.get("src/test/resources/arbitrary/demo2");
this.createAndMintTxn(repository, publicKey58, path2, name, Method.PATCH, service, alice);
this.createAndMintTxn(repository, publicKey58, path2, name, identifier, Method.PATCH, service, alice);
// Create another PATCH transaction
Path path3 = Paths.get("src/test/resources/arbitrary/demo3");
this.createAndMintTxn(repository, publicKey58, path3, name, Method.PATCH, service, alice);
this.createAndMintTxn(repository, publicKey58, path3, name, identifier, Method.PATCH, service, alice);
// Now build the latest data state for this name
ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(name, ResourceIdType.NAME, service);
@ -90,12 +91,13 @@ public class ArbitraryDataTests extends Common {
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
String publicKey58 = Base58.encode(alice.getPublicKey());
String name = "TEST"; // Can be anything for this test
String identifier = null; // Not used for this test
Service service = Service.WEBSITE; // Can be anything for this test
// Create PATCH transaction, ensuring that an exception is thrown
try {
Path path1 = Paths.get("src/test/resources/arbitrary/demo1");
this.createAndMintTxn(repository, publicKey58, path1, name, Method.PATCH, service, alice);
this.createAndMintTxn(repository, publicKey58, path1, name, identifier, Method.PATCH, service, alice);
fail("Creating transaction should fail due to nonexistent PUT transaction");
} catch (DataException expectedException) {
@ -107,11 +109,12 @@ public class ArbitraryDataTests extends Common {
}
@Test
public void testNameDoesNotExist() throws DataException, IOException {
public void testNameDoesNotExist() throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
String publicKey58 = Base58.encode(alice.getPublicKey());
String name = "TEST"; // Can be anything for this test
String identifier = null; // Not used for this test
Service service = Service.WEBSITE; // Can be anything for this test
// Ensure the name doesn't exist
@ -120,7 +123,7 @@ public class ArbitraryDataTests extends Common {
// Create PUT transaction, ensuring that an exception is thrown
try {
Path path1 = Paths.get("src/test/resources/arbitrary/demo1");
this.createAndMintTxn(repository, publicKey58, path1, name, Method.PUT, service, alice);
this.createAndMintTxn(repository, publicKey58, path1, name, identifier, Method.PUT, service, alice);
fail("Creating transaction should fail due to the name being unregistered");
} catch (DataException expectedException) {
@ -130,10 +133,11 @@ public class ArbitraryDataTests extends Common {
}
@Test
public void testUpdateResourceOwnedByAnotherCreator() throws DataException, IOException {
public void testUpdateResourceOwnedByAnotherCreator() throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
String name = "TEST"; // Can be anything for this test
String identifier = null; // Not used for this test
Service service = Service.WEBSITE; // Can be anything for this test
// Register the name to Alice
@ -142,7 +146,7 @@ public class ArbitraryDataTests extends Common {
// Create PUT transaction
Path path1 = Paths.get("src/test/resources/arbitrary/demo1");
this.createAndMintTxn(repository, Base58.encode(alice.getPublicKey()), path1, name, Method.PUT, service, alice);
this.createAndMintTxn(repository, Base58.encode(alice.getPublicKey()), path1, name, identifier, Method.PUT, service, alice);
// Bob attempts to update Alice's data
PrivateKeyAccount bob = Common.getTestAccount(repository, "bob");
@ -150,7 +154,7 @@ public class ArbitraryDataTests extends Common {
// Create PATCH transaction, ensuring that an exception is thrown
try {
Path path2 = Paths.get("src/test/resources/arbitrary/demo2");
this.createAndMintTxn(repository, Base58.encode(bob.getPublicKey()), path2, name, Method.PATCH, service, bob);
this.createAndMintTxn(repository, Base58.encode(bob.getPublicKey()), path2, name, identifier, Method.PATCH, service, bob);
fail("Creating transaction should fail due to the name being registered to Alice instead of Bob");
} catch (DataException expectedException) {
@ -165,6 +169,7 @@ public class ArbitraryDataTests extends Common {
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
String publicKey58 = Base58.encode(alice.getPublicKey());
String name = "TEST"; // Can be anything for this test
String identifier = null; // Not used for this test
Service service = Service.WEBSITE; // Can be anything for this test
// Register the name to Alice
@ -173,7 +178,7 @@ public class ArbitraryDataTests extends Common {
// Create PUT transaction
Path path1 = Paths.get("src/test/resources/arbitrary/demo1");
this.createAndMintTxn(repository, publicKey58, path1, name, Method.PUT, service, alice);
this.createAndMintTxn(repository, publicKey58, path1, name, identifier, Method.PUT, service, alice);
// Now build the latest data state for this name
ArbitraryDataReader arbitraryDataReader1 = new ArbitraryDataReader(name, ResourceIdType.NAME, service);
@ -184,7 +189,7 @@ public class ArbitraryDataTests extends Common {
// Create PATCH transaction
Path path2 = Paths.get("src/test/resources/arbitrary/demo2");
this.createAndMintTxn(repository, publicKey58, path2, name, Method.PATCH, service, alice);
this.createAndMintTxn(repository, publicKey58, path2, name, identifier, Method.PATCH, service, alice);
// Rebuild the latest state
ArbitraryDataReader arbitraryDataReader2 = new ArbitraryDataReader(name, ResourceIdType.NAME, service);
@ -203,10 +208,12 @@ public class ArbitraryDataTests extends Common {
}
}
private void createAndMintTxn(Repository repository, String publicKey58, Path path, String name,
private void createAndMintTxn(Repository repository, String publicKey58, Path path, String name, String identifier,
Method method, Service service, PrivateKeyAccount account) throws DataException {
ArbitraryDataTransactionBuilder txnBuilder = new ArbitraryDataTransactionBuilder(publicKey58, path, name, method, service);
ArbitraryDataTransactionBuilder txnBuilder = new ArbitraryDataTransactionBuilder(
publicKey58, path, name, method, service, identifier);
ArbitraryTransactionData transactionData = txnBuilder.build();
Transaction.ValidationResult result = TransactionUtils.signAndImport(repository, transactionData, account);
assertEquals(Transaction.ValidationResult.OK, result);

View File

@ -42,7 +42,7 @@ public class ArbitraryTransactionTests extends Common {
List<PaymentData> payments = new ArrayList<>();
ArbitraryTransactionData transactionData = new ArbitraryTransactionData(TestTransaction.generateBase(alice),
5, service, 0, 0, null, method,
5, service, 0, 0, null, null, method,
null, compression, null, dataType, null, payments);
ArbitraryTransaction transaction = (ArbitraryTransaction) Transaction.fromData(repository, transactionData);

View File

@ -21,6 +21,7 @@ public class ArbitraryTestTransaction extends TestTransaction {
final int nonce = 0;
final int size = 4 * 1024 * 1024;
final String name = "TEST";
final String identifier = "qortal_avatar";
final ArbitraryTransactionData.Method method = ArbitraryTransactionData.Method.PUT;
final byte[] secret = new byte[32];
@ -43,8 +44,8 @@ public class ArbitraryTestTransaction extends TestTransaction {
List<PaymentData> payments = new ArrayList<>();
payments.add(new PaymentData(recipient, assetId, amount));
return new ArbitraryTransactionData(generateBase(account), version, service, nonce, size, name, method,
secret, compression, data, dataType, chunkHashes, payments);
return new ArbitraryTransactionData(generateBase(account), version, service, nonce, size,name, identifier,
method, secret, compression, data, dataType, chunkHashes, payments);
}
}