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

@@ -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);
}
}