Use a random last reference on the very first transaction for an account

This is needed because we want to allow brand new accounts to publish data without a fee. A similar approach to CrossChainResource.buildAtMessage(). We already require PoW on all arbitrary transactions, so no additional logic beyond this should be needed.
This commit is contained in:
CalDescent 2021-08-20 07:49:05 +01:00
parent 1d62ef357d
commit 47ff51ce4e
3 changed files with 38 additions and 6 deletions

View File

@ -15,6 +15,7 @@ import java.net.UnknownHostException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
@ -53,6 +54,7 @@ import org.qortal.transaction.Transaction;
import org.qortal.transaction.Transaction.TransactionType;
import org.qortal.transaction.Transaction.ValidationResult;
import org.qortal.transform.TransformationException;
import org.qortal.transform.Transformer;
import org.qortal.transform.transaction.ArbitraryTransactionTransformer;
import org.qortal.utils.Base58;
import org.qortal.utils.NTP;
@ -269,10 +271,14 @@ public class ArbitraryResource {
}
byte[] creatorPublicKey = Base58.decode(creatorPublicKeyBase58);
final String creatorAddress = Crypto.toAddress(creatorPublicKey);
final byte[] lastReference = repository.getAccountRepository().getLastReference(creatorAddress);
byte[] lastReference = repository.getAccountRepository().getLastReference(creatorAddress);
if (lastReference == null) {
LOGGER.info(String.format("Qortal account %s has no last reference", creatorAddress));
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
// Use a random last reference on the very first transaction for an account
// Code copied from CrossChainResource.buildAtMessage()
// We already require PoW on all arbitrary transactions, so no additional logic is needed
Random random = new Random();
lastReference = new byte[Transformer.SIGNATURE_LENGTH];
random.nextBytes(lastReference);
}
String name = null;

View File

@ -15,6 +15,7 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import com.google.common.io.Resources;
import io.swagger.v3.oas.annotations.Operation;
@ -48,6 +49,7 @@ import org.qortal.arbitrary.ArbitraryDataWriter;
import org.qortal.transaction.ArbitraryTransaction;
import org.qortal.transaction.Transaction;
import org.qortal.transform.TransformationException;
import org.qortal.transform.Transformer;
import org.qortal.transform.transaction.ArbitraryTransactionTransformer;
import org.qortal.utils.Base58;
import org.qortal.utils.NTP;
@ -104,10 +106,14 @@ public class WebsiteResource {
}
byte[] creatorPublicKey = Base58.decode(creatorPublicKeyBase58);
final String creatorAddress = Crypto.toAddress(creatorPublicKey);
final byte[] lastReference = repository.getAccountRepository().getLastReference(creatorAddress);
byte[] lastReference = repository.getAccountRepository().getLastReference(creatorAddress);
if (lastReference == null) {
LOGGER.info(String.format("Qortal account %s has no last reference", creatorAddress));
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
// Use a random last reference on the very first transaction for an account
// Code copied from CrossChainResource.buildAtMessage()
// We already require PoW on all arbitrary transactions, so no additional logic is needed
Random random = new Random();
lastReference = new byte[Transformer.SIGNATURE_LENGTH];
random.nextBytes(lastReference);
}
String name = "CalDescentTest1"; // TODO: dynamic

View File

@ -74,6 +74,26 @@ public class ArbitraryTransaction extends Transaction {
this.arbitraryTransactionData.setNonce(MemoryPoW.compute2(transactionBytes, POW_BUFFER_SIZE, difficulty));
}
@Override
public boolean hasValidReference() throws DataException {
// We shouldn't really get this far, but just in case:
if (this.arbitraryTransactionData.getReference() == null) {
return false;
}
// If the account current doesn't have a last reference, and the fee is 0, we will allow any value.
// This ensures that the first transaction for an account will be valid whilst still validating
// the last reference from the second transaction onwards. By checking for a zero fee, we ensure
// standard last reference validation when fee > 0.
Account creator = getCreator();
Long fee = this.arbitraryTransactionData.getFee();
if (creator.getLastReference() == null && fee == 0) {
return true;
}
return super.hasValidReference();
}
@Override
public ValidationResult isValid() throws DataException {
// Check that some data - or a data hash - has been supplied