When validating an ARBITRARY transaction, ensure that the supplied name exists and is registered to the account that is signing the transaction.

This ensures that only the owner of a name is able to update data associated with that name.

Note that this doesn't take into account the ability for group members to update a resource, so this will need modifying when that feature is ultimately introduced (likely after v3.0)
This commit is contained in:
CalDescent 2021-10-25 18:58:33 +01:00
parent 35a7a70b93
commit a55fc4fff9
2 changed files with 76 additions and 0 deletions

View File

@ -2,12 +2,14 @@ package org.qortal.transaction;
import java.math.BigInteger;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.qortal.account.Account;
import org.qortal.crypto.Crypto;
import org.qortal.crypto.MemoryPoW;
import org.qortal.data.PaymentData;
import org.qortal.data.naming.NameData;
import org.qortal.data.transaction.ArbitraryTransactionData;
import org.qortal.data.transaction.TransactionData;
import org.qortal.payment.Payment;
@ -147,6 +149,21 @@ public class ArbitraryTransaction extends Transaction {
}
}
// Check name if one has been included
if (arbitraryTransactionData.getName() != null) {
NameData nameData = this.repository.getNameRepository().fromName(arbitraryTransactionData.getName());
// Check the name is registered
if (nameData == null) {
return ValidationResult.NAME_DOES_NOT_EXIST;
}
// Check that the transaction signer owns the name
if (!Objects.equals(this.getCreator().getAddress(), nameData.getOwner())) {
return ValidationResult.INVALID_NAME_OWNER;
}
}
// Wrap and delegate final payment validity checks to Payment class
return new Payment(this.repository).isValid(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
arbitraryTransactionData.getFee());

View File

@ -10,12 +10,14 @@ import org.qortal.arbitrary.ArbitraryDataTransactionBuilder;
import org.qortal.arbitrary.metadata.ArbitraryDataMetadataPatch;
import org.qortal.data.transaction.ArbitraryTransactionData;
import org.qortal.data.transaction.ArbitraryTransactionData.*;
import org.qortal.data.transaction.RegisterNameTransactionData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.test.common.BlockUtils;
import org.qortal.test.common.Common;
import org.qortal.test.common.TransactionUtils;
import org.qortal.test.common.transaction.TestTransaction;
import org.qortal.transaction.Transaction;
import org.qortal.utils.Base58;
@ -41,6 +43,10 @@ public class ArbitraryDataTests extends Common {
String name = "TEST"; // Can be anything for this test
Service service = Service.WEBSITE; // Can be anything for this test
// Register the name to Alice
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "");
TransactionUtils.signAndMint(repository, transactionData, alice);
// Create PUT transaction
Path path1 = Paths.get("src/test/resources/arbitrary/demo1");
this.createAndMintTxn(repository, publicKey58, path1, name, Method.PUT, service, alice);
@ -98,6 +104,59 @@ public class ArbitraryDataTests extends Common {
}
}
@Test
public void testNameDoesNotExist() throws DataException, IOException {
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
Service service = Service.WEBSITE; // Can be anything for this test
// Ensure the name doesn't exist
assertNull(repository.getNameRepository().fromName(name));
// 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);
fail("Creating transaction should fail due to the name being unregistered");
} catch (DataException expectedException) {
assertEquals("Arbitrary transaction invalid: NAME_DOES_NOT_EXIST", expectedException.getMessage());
}
}
}
@Test
public void testUpdateResourceOwnedByAnotherCreator() throws DataException, IOException {
try (final Repository repository = RepositoryManager.getRepository()) {
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
String name = "TEST"; // Can be anything for this test
Service service = Service.WEBSITE; // Can be anything for this test
// Register the name to Alice
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "");
TransactionUtils.signAndMint(repository, transactionData, alice);
// 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);
// Bob attempts to update Alice's data
PrivateKeyAccount bob = Common.getTestAccount(repository, "bob");
// 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);
fail("Creating transaction should fail due to the name being registered to Alice instead of Bob");
} catch (DataException expectedException) {
assertEquals("Arbitrary transaction invalid: INVALID_NAME_OWNER", expectedException.getMessage());
}
}
}
private void createAndMintTxn(Repository repository, String publicKey58, Path path, String name,
Method method, Service service, PrivateKeyAccount account) throws DataException {