forked from Qortal/qortal
API + fix for names in HSQLDB
Added POST /names/update for building an UPDATE-NAME transaction. BlockGenerator now tries to validate new block after adding each unconfirmed transaction in turn. If block becomes invalid then that transaction is removed/skipped. This should further prevent block jams. Skipped transactions might be deleted as the next block is forged when unconfirmed transactions are collated/filtered/expired. Add Block.deleteTransaction() for use during block generation above. Block.addTransaction() and Block.deleteTransaction() use transaction signatures to test for presence in Block's existing transactions. Names shouldn't have stored registrant's public key! "registrantPublicKey" removed from NameData Java object/bean. Corresponding column removed from HSQLDB using ALTER TABLE but also from the original CREATE TABLE definition. Remove the ALTER TABLE statement just prior to rebuilding database! (This needs to be applied to Polls too as some point). Also, UpdateNameTransactions and BuyNameTransactions tables now allow name_reference to be NULL as this column value isn't set until the corresponding transactions are processed/added to a block. (name_reference is a link to previous name-related transaction that altered Name data like "owner" or "data" so that name-related transactions can be orphaned/undone).
This commit is contained in:
parent
95d640cc8c
commit
22c87a6e08
@ -29,6 +29,7 @@ import org.qora.api.model.NameSummary;
|
||||
import org.qora.crypto.Crypto;
|
||||
import org.qora.data.naming.NameData;
|
||||
import org.qora.data.transaction.RegisterNameTransactionData;
|
||||
import org.qora.data.transaction.UpdateNameTransactionData;
|
||||
import org.qora.repository.DataException;
|
||||
import org.qora.repository.Repository;
|
||||
import org.qora.repository.RepositoryManager;
|
||||
@ -36,6 +37,7 @@ import org.qora.transaction.Transaction;
|
||||
import org.qora.transaction.Transaction.ValidationResult;
|
||||
import org.qora.transform.TransformationException;
|
||||
import org.qora.transform.transaction.RegisterNameTransactionTransformer;
|
||||
import org.qora.transform.transaction.UpdateNameTransactionTransformer;
|
||||
import org.qora.utils.Base58;
|
||||
|
||||
@Path("/names")
|
||||
@ -153,7 +155,7 @@ public class NamesResource {
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
|
||||
public String buildTransaction(RegisterNameTransactionData transactionData) {
|
||||
public String registerName(RegisterNameTransactionData transactionData) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
|
||||
@ -170,4 +172,47 @@ public class NamesResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/update")
|
||||
@Operation(
|
||||
summary = "Build raw, unsigned, UPDATE_NAME transaction",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = UpdateNameTransactionData.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "raw, unsigned, UPDATE_NAME transaction encoded in Base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
|
||||
public String updateName(UpdateNameTransactionData transactionData) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
|
||||
ValidationResult result = transaction.isValidUnconfirmed();
|
||||
if (result != ValidationResult.OK)
|
||||
throw TransactionsResource.createTransactionInvalidException(request, result);
|
||||
|
||||
byte[] bytes = UpdateNameTransactionTransformer.toBytes(transactionData);
|
||||
return Base58.encode(bytes);
|
||||
} catch (TransformationException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -543,8 +543,8 @@ public class Block {
|
||||
if (this.blockData.getGeneratorSignature() == null)
|
||||
throw new IllegalStateException("Cannot calculate transactions signature as block has no generator signature");
|
||||
|
||||
// Already added?
|
||||
if (this.transactions.contains(transactionData))
|
||||
// Already added? (Check using signature)
|
||||
if (this.transactions.stream().anyMatch(transaction -> Arrays.equals(transaction.getTransactionData().getSignature(), transactionData.getSignature())))
|
||||
return true;
|
||||
|
||||
// Check there is space in block
|
||||
@ -573,6 +573,47 @@ public class Block {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a transaction from the block.
|
||||
* <p>
|
||||
* Used when constructing a new block during forging.
|
||||
* <p>
|
||||
* Requires block's {@code generator} being a {@code PrivateKeyAccount} so block's transactions signature can be recalculated.
|
||||
*
|
||||
* @param transactionData
|
||||
* @throws IllegalStateException
|
||||
* if block's {@code generator} is not a {@code PrivateKeyAccount}.
|
||||
*/
|
||||
public void deleteTransaction(TransactionData transactionData) {
|
||||
// Can't add to transactions if we haven't loaded existing ones yet
|
||||
if (this.transactions == null)
|
||||
throw new IllegalStateException("Attempted to add transaction to partially loaded database Block");
|
||||
|
||||
if (!(this.generator instanceof PrivateKeyAccount))
|
||||
throw new IllegalStateException("Block's generator has no private key");
|
||||
|
||||
if (this.blockData.getGeneratorSignature() == null)
|
||||
throw new IllegalStateException("Cannot calculate transactions signature as block has no generator signature");
|
||||
|
||||
// Attempt to remove from block (Check using signature)
|
||||
boolean wasElementRemoved = this.transactions.removeIf(transaction -> Arrays.equals(transaction.getTransactionData().getSignature(), transactionData.getSignature()));
|
||||
if (!wasElementRemoved)
|
||||
// Wasn't there - nothing more to do
|
||||
return;
|
||||
|
||||
// Re-sort
|
||||
this.transactions.sort(Transaction.getComparator());
|
||||
|
||||
// Update transaction count
|
||||
this.blockData.setTransactionCount(this.blockData.getTransactionCount() - 1);
|
||||
|
||||
// Update totalFees
|
||||
this.blockData.setTotalFees(this.blockData.getTotalFees().subtract(transactionData.getFee()));
|
||||
|
||||
// We've removed a transaction, so recalculate transactions signature
|
||||
calcTransactionsSignature();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate block's generator signature.
|
||||
* <p>
|
||||
@ -787,7 +828,7 @@ public class Block {
|
||||
// NOTE: in Gen1 there was an extra block height passed to DeployATTransaction.isValid
|
||||
Transaction.ValidationResult validationResult = transaction.isValid();
|
||||
if (validationResult != Transaction.ValidationResult.OK) {
|
||||
LOGGER.error("Error during transaction validation, tx " + Base58.encode(transaction.getTransactionData().getSignature()) + ": "
|
||||
LOGGER.debug("Error during transaction validation, tx " + Base58.encode(transaction.getTransactionData().getSignature()) + ": "
|
||||
+ validationResult.name());
|
||||
return ValidationResult.TRANSACTION_INVALID;
|
||||
}
|
||||
|
@ -147,9 +147,21 @@ public class BlockGenerator extends Thread {
|
||||
repository.discardChanges();
|
||||
|
||||
// Attempt to add transactions until block is full, or we run out
|
||||
for (TransactionData transactionData : unconfirmedTransactions)
|
||||
// If a transaction makes the block invalid then skip it and it'll either expire or be in next block.
|
||||
for (TransactionData transactionData : unconfirmedTransactions) {
|
||||
if (!newBlock.addTransaction(transactionData))
|
||||
break;
|
||||
|
||||
// Sign to create block's signature
|
||||
newBlock.sign();
|
||||
|
||||
// If newBlock is no longer valid then we can't use transaction
|
||||
ValidationResult validationResult = newBlock.isValid();
|
||||
if (validationResult != ValidationResult.OK) {
|
||||
LOGGER.debug("Skipping invalid transaction " + Base58.encode(transactionData.getSignature()) + " during block generation");
|
||||
newBlock.deleteTransaction(transactionData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
|
@ -10,7 +10,6 @@ import javax.xml.bind.annotation.XmlAccessorType;
|
||||
public class NameData {
|
||||
|
||||
// Properties
|
||||
private byte[] registrantPublicKey;
|
||||
private String owner;
|
||||
private String name;
|
||||
private String data;
|
||||
@ -26,9 +25,8 @@ public class NameData {
|
||||
protected NameData() {
|
||||
}
|
||||
|
||||
public NameData(byte[] registrantPublicKey, String owner, String name, String data, long registered, Long updated, byte[] reference, boolean isForSale,
|
||||
public NameData(String owner, String name, String data, long registered, Long updated, byte[] reference, boolean isForSale,
|
||||
BigDecimal salePrice) {
|
||||
this.registrantPublicKey = registrantPublicKey;
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
@ -39,16 +37,12 @@ public class NameData {
|
||||
this.salePrice = salePrice;
|
||||
}
|
||||
|
||||
public NameData(byte[] registrantPublicKey, String owner, String name, String data, long registered, byte[] reference) {
|
||||
this(registrantPublicKey, owner, name, data, registered, null, reference, false, null);
|
||||
public NameData(String owner, String name, String data, long registered, byte[] reference) {
|
||||
this(owner, name, data, registered, null, reference, false, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getRegistrantPublicKey() {
|
||||
return this.registrantPublicKey;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
import org.qora.transaction.Transaction.TransactionType;
|
||||
|
||||
@ -15,16 +16,24 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
public class UpdateNameTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
@Schema(description = "owner's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||
private byte[] ownerPublicKey;
|
||||
@Schema(description = "new owner's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v")
|
||||
private String newOwner;
|
||||
@Schema(description = "which name to update", example = "my-name")
|
||||
private String name;
|
||||
@Schema(description = "replacement simple name-related info in JSON format", example = "{ \"age\": 30 }")
|
||||
private String newData;
|
||||
// For internal use when orphaning
|
||||
@XmlTransient
|
||||
@Schema(hidden = true)
|
||||
private byte[] nameReference;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected UpdateNameTransactionData() {
|
||||
super(TransactionType.UPDATE_NAME);
|
||||
}
|
||||
|
||||
public UpdateNameTransactionData(byte[] ownerPublicKey, String newOwner, String name, String newData, byte[] nameReference, BigDecimal fee, long timestamp,
|
||||
|
@ -33,7 +33,7 @@ public class Name {
|
||||
*/
|
||||
public Name(Repository repository, RegisterNameTransactionData registerNameTransactionData) {
|
||||
this.repository = repository;
|
||||
this.nameData = new NameData(registerNameTransactionData.getRegistrantPublicKey(), registerNameTransactionData.getOwner(),
|
||||
this.nameData = new NameData(registerNameTransactionData.getOwner(),
|
||||
registerNameTransactionData.getName(), registerNameTransactionData.getData(), registerNameTransactionData.getTimestamp(),
|
||||
registerNameTransactionData.getSignature());
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ public class HSQLDBDatabaseUpdates {
|
||||
case 6:
|
||||
// Update Name Transactions
|
||||
stmt.execute("CREATE TABLE UpdateNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "new_owner QoraAddress NOT NULL, new_data NameData NOT NULL, name_reference Signature NOT NULL, "
|
||||
+ "new_owner QoraAddress NOT NULL, new_data NameData NOT NULL, name_reference Signature, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
@ -203,7 +203,7 @@ public class HSQLDBDatabaseUpdates {
|
||||
case 9:
|
||||
// Buy Name Transactions
|
||||
stmt.execute("CREATE TABLE BuyNameTransactions (signature Signature, buyer QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "seller QoraAddress NOT NULL, amount QoraAmount NOT NULL, name_reference Signature NOT NULL, "
|
||||
+ "seller QoraAddress NOT NULL, amount QoraAmount NOT NULL, name_reference Signature, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
@ -357,7 +357,7 @@ public class HSQLDBDatabaseUpdates {
|
||||
case 26:
|
||||
// Registered Names
|
||||
stmt.execute(
|
||||
"CREATE TABLE Names (name RegisteredName, data VARCHAR(4000) NOT NULL, registrant QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, "
|
||||
"CREATE TABLE Names (name RegisteredName, data VARCHAR(4000) NOT NULL, owner QoraAddress NOT NULL, "
|
||||
+ "registered TIMESTAMP WITH TIME ZONE NOT NULL, updated TIMESTAMP WITH TIME ZONE, reference Signature, is_for_sale BOOLEAN NOT NULL, sale_price QoraAmount, "
|
||||
+ "PRIMARY KEY (name))");
|
||||
break;
|
||||
@ -389,6 +389,15 @@ public class HSQLDBDatabaseUpdates {
|
||||
stmt.execute("CREATE INDEX ATTransactionsIndex on ATTransactions (AT_address)");
|
||||
break;
|
||||
|
||||
case 28:
|
||||
// XXX TEMP fix until database rebuild
|
||||
// Allow name_reference to be NULL while transaction is unconfirmed
|
||||
stmt.execute("ALTER TABLE UpdateNameTransactions ALTER COLUMN name_reference SET NULL");
|
||||
stmt.execute("ALTER TABLE BuyNameTransactions ALTER COLUMN name_reference SET NULL");
|
||||
// Names.registrant shouldn't be there
|
||||
stmt.execute("ALTER TABLE Names DROP COLUMN registrant");
|
||||
break;
|
||||
|
||||
default:
|
||||
// nothing to do
|
||||
return false;
|
||||
|
@ -23,24 +23,23 @@ public class HSQLDBNameRepository implements NameRepository {
|
||||
@Override
|
||||
public NameData fromName(String name) throws DataException {
|
||||
try (ResultSet resultSet = this.repository
|
||||
.checkedExecute("SELECT registrant, owner, data, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE name = ?", name)) {
|
||||
.checkedExecute("SELECT owner, data, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE name = ?", name)) {
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
byte[] registrantPublicKey = resultSet.getBytes(1);
|
||||
String owner = resultSet.getString(2);
|
||||
String data = resultSet.getString(3);
|
||||
long registered = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||
String owner = resultSet.getString(1);
|
||||
String data = resultSet.getString(2);
|
||||
long registered = resultSet.getTimestamp(3, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||
|
||||
// Special handling for possibly-NULL "updated" column
|
||||
Timestamp updatedTimestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC));
|
||||
Timestamp updatedTimestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC));
|
||||
Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime();
|
||||
|
||||
byte[] reference = resultSet.getBytes(6);
|
||||
boolean isForSale = resultSet.getBoolean(7);
|
||||
BigDecimal salePrice = resultSet.getBigDecimal(8);
|
||||
byte[] reference = resultSet.getBytes(5);
|
||||
boolean isForSale = resultSet.getBoolean(6);
|
||||
BigDecimal salePrice = resultSet.getBigDecimal(7);
|
||||
|
||||
return new NameData(registrantPublicKey, owner, name, data, registered, updated, reference, isForSale, salePrice);
|
||||
return new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch name info from repository", e);
|
||||
}
|
||||
@ -60,26 +59,25 @@ public class HSQLDBNameRepository implements NameRepository {
|
||||
List<NameData> names = new ArrayList<>();
|
||||
|
||||
try (ResultSet resultSet = this.repository
|
||||
.checkedExecute("SELECT name, data, registrant, owner, registered, updated, reference, is_for_sale, sale_price FROM Names")) {
|
||||
.checkedExecute("SELECT name, data, owner, registered, updated, reference, is_for_sale, sale_price FROM Names")) {
|
||||
if (resultSet == null)
|
||||
return names;
|
||||
|
||||
do {
|
||||
String name = resultSet.getString(1);
|
||||
String data = resultSet.getString(2);
|
||||
byte[] registrantPublicKey = resultSet.getBytes(3);
|
||||
String owner = resultSet.getString(4);
|
||||
long registered = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||
String owner = resultSet.getString(3);
|
||||
long registered = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||
|
||||
// Special handling for possibly-NULL "updated" column
|
||||
Timestamp updatedTimestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC));
|
||||
Timestamp updatedTimestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC));
|
||||
Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime();
|
||||
|
||||
byte[] reference = resultSet.getBytes(7);
|
||||
boolean isForSale = resultSet.getBoolean(8);
|
||||
BigDecimal salePrice = resultSet.getBigDecimal(9);
|
||||
byte[] reference = resultSet.getBytes(6);
|
||||
boolean isForSale = resultSet.getBoolean(7);
|
||||
BigDecimal salePrice = resultSet.getBigDecimal(8);
|
||||
|
||||
names.add(new NameData(registrantPublicKey, owner, name, data, registered, updated, reference, isForSale, salePrice));
|
||||
names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice));
|
||||
} while (resultSet.next());
|
||||
|
||||
return names;
|
||||
@ -93,25 +91,24 @@ public class HSQLDBNameRepository implements NameRepository {
|
||||
List<NameData> names = new ArrayList<>();
|
||||
|
||||
try (ResultSet resultSet = this.repository
|
||||
.checkedExecute("SELECT name, data, registrant, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE owner = ?", owner)) {
|
||||
.checkedExecute("SELECT name, data, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE owner = ?", owner)) {
|
||||
if (resultSet == null)
|
||||
return names;
|
||||
|
||||
do {
|
||||
String name = resultSet.getString(1);
|
||||
String data = resultSet.getString(2);
|
||||
byte[] registrantPublicKey = resultSet.getBytes(3);
|
||||
long registered = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||
long registered = resultSet.getTimestamp(3, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||
|
||||
// Special handling for possibly-NULL "updated" column
|
||||
Timestamp updatedTimestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC));
|
||||
Timestamp updatedTimestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC));
|
||||
Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime();
|
||||
|
||||
byte[] reference = resultSet.getBytes(6);
|
||||
boolean isForSale = resultSet.getBoolean(7);
|
||||
BigDecimal salePrice = resultSet.getBigDecimal(8);
|
||||
byte[] reference = resultSet.getBytes(5);
|
||||
boolean isForSale = resultSet.getBoolean(6);
|
||||
BigDecimal salePrice = resultSet.getBigDecimal(7);
|
||||
|
||||
names.add(new NameData(registrantPublicKey, owner, name, data, registered, updated, reference, isForSale, salePrice));
|
||||
names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice));
|
||||
} while (resultSet.next());
|
||||
|
||||
return names;
|
||||
@ -128,7 +125,7 @@ public class HSQLDBNameRepository implements NameRepository {
|
||||
Long updated = nameData.getUpdated();
|
||||
Timestamp updatedTimestamp = updated == null ? null : new Timestamp(updated);
|
||||
|
||||
saveHelper.bind("registrant", nameData.getRegistrantPublicKey()).bind("owner", nameData.getOwner()).bind("name", nameData.getName())
|
||||
saveHelper.bind("owner", nameData.getOwner()).bind("name", nameData.getName())
|
||||
.bind("data", nameData.getData()).bind("registered", new Timestamp(nameData.getRegistered())).bind("updated", updatedTimestamp)
|
||||
.bind("reference", nameData.getReference()).bind("is_for_sale", nameData.getIsForSale()).bind("sale_price", nameData.getSalePrice());
|
||||
|
||||
|
@ -441,7 +441,7 @@ public abstract class Transaction {
|
||||
public ValidationResult isValidUnconfirmed() throws DataException {
|
||||
// Transactions with a timestamp prior to latest block's timestamp are too old
|
||||
BlockData latestBlock = repository.getBlockRepository().getLastBlock();
|
||||
if (this.transactionData.getTimestamp() <= latestBlock.getTimestamp())
|
||||
if (this.getDeadline() <= latestBlock.getTimestamp())
|
||||
return ValidationResult.TIMESTAMP_TOO_OLD;
|
||||
|
||||
// Transactions with a timestamp too far into future are too new
|
||||
|
@ -29,6 +29,10 @@ public class UpdateNameTransaction extends Transaction {
|
||||
super(repository, transactionData);
|
||||
|
||||
this.updateNameTransactionData = (UpdateNameTransactionData) this.transactionData;
|
||||
|
||||
// XXX This is horrible - thanks to JAXB unmarshalling not calling constructor
|
||||
if (this.transactionData.getCreatorPublicKey() == null)
|
||||
this.transactionData.setCreatorPublicKey(this.updateNameTransactionData.getOwnerPublicKey());
|
||||
}
|
||||
|
||||
// More information
|
||||
|
Loading…
x
Reference in New Issue
Block a user