forked from Qortal/qortal
Added UpdateNameTransaction
* Added more columns/properties to NameData to support updating timestamps, name sales and reference to last transaction that changed Name (for orphaning support). * Fixed serialization/deserialization bugs in MessageTransactions * More tests
This commit is contained in:
parent
4de2caaa28
commit
c79bec90bc
@ -1,22 +1,37 @@
|
|||||||
package data.naming;
|
package data.naming;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
public class NameData {
|
public class NameData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
private byte[] registrantPublicKey;
|
private byte[] registrantPublicKey;
|
||||||
private String owner;
|
private String owner;
|
||||||
private String name;
|
private String name;
|
||||||
private String value;
|
private String data;
|
||||||
private long registered;
|
private long registered;
|
||||||
|
private Long updated;
|
||||||
|
private byte[] reference;
|
||||||
|
private boolean isForSale;
|
||||||
|
private BigDecimal salePrice;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
public NameData(byte[] registrantPublicKey, String owner, String name, String value, long timestamp) {
|
public NameData(byte[] registrantPublicKey, String owner, String name, String data, long registered, Long updated, byte[] reference, boolean isForSale,
|
||||||
|
BigDecimal salePrice) {
|
||||||
this.registrantPublicKey = registrantPublicKey;
|
this.registrantPublicKey = registrantPublicKey;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.value = value;
|
this.data = data;
|
||||||
this.registered = timestamp;
|
this.registered = registered;
|
||||||
|
this.updated = updated;
|
||||||
|
this.reference = reference;
|
||||||
|
this.isForSale = isForSale;
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters / setters
|
// Getters / setters
|
||||||
@ -29,16 +44,56 @@ public class NameData {
|
|||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOwner(String owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getData() {
|
public String getData() {
|
||||||
return this.value;
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(String data) {
|
||||||
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getRegistered() {
|
public long getRegistered() {
|
||||||
return this.registered;
|
return this.registered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getUpdated() {
|
||||||
|
return this.updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdated(Long updated) {
|
||||||
|
this.updated = updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getReference() {
|
||||||
|
return this.reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReference(byte[] reference) {
|
||||||
|
this.reference = reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getIsForSale() {
|
||||||
|
return this.isForSale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsForSale(boolean isForSale) {
|
||||||
|
this.isForSale = isForSale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getSalePrice() {
|
||||||
|
return this.salePrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSalePrice(BigDecimal salePrice) {
|
||||||
|
this.salePrice = salePrice;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
60
src/data/transaction/UpdateNameTransactionData.java
Normal file
60
src/data/transaction/UpdateNameTransactionData.java
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package data.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import qora.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
|
public class UpdateNameTransactionData extends TransactionData {
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
private byte[] ownerPublicKey;
|
||||||
|
private String newOwner;
|
||||||
|
private String name;
|
||||||
|
private String newData;
|
||||||
|
private byte[] nameReference;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
public UpdateNameTransactionData(byte[] ownerPublicKey, String newOwner, String name, String newData, byte[] nameReference, BigDecimal fee, long timestamp,
|
||||||
|
byte[] reference, byte[] signature) {
|
||||||
|
super(TransactionType.UPDATE_NAME, fee, ownerPublicKey, timestamp, reference, signature);
|
||||||
|
|
||||||
|
this.ownerPublicKey = ownerPublicKey;
|
||||||
|
this.newOwner = newOwner;
|
||||||
|
this.name = name;
|
||||||
|
this.newData = newData;
|
||||||
|
this.nameReference = nameReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdateNameTransactionData(byte[] ownerPublicKey, String newOwner, String name, String newData, byte[] nameReference, BigDecimal fee, long timestamp,
|
||||||
|
byte[] reference) {
|
||||||
|
this(ownerPublicKey, newOwner, name, newData, nameReference, fee, timestamp, reference, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters / setters
|
||||||
|
|
||||||
|
public byte[] getOwnerPublicKey() {
|
||||||
|
return this.ownerPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNewOwner() {
|
||||||
|
return this.newOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNewData() {
|
||||||
|
return this.newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getNameReference() {
|
||||||
|
return this.nameReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNameReference(byte[] nameReference) {
|
||||||
|
this.nameReference = nameReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,6 +28,7 @@ import repository.Repository;
|
|||||||
import transform.TransformationException;
|
import transform.TransformationException;
|
||||||
import transform.block.BlockTransformer;
|
import transform.block.BlockTransformer;
|
||||||
import transform.transaction.TransactionTransformer;
|
import transform.transaction.TransactionTransformer;
|
||||||
|
import utils.Base58;
|
||||||
import utils.NTP;
|
import utils.NTP;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -554,6 +555,9 @@ public class Block {
|
|||||||
transaction.process();
|
transaction.process();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// LOGGER.error("Exception during transaction processing, tx " + Base58.encode(transaction.getSignature()), e);
|
// LOGGER.error("Exception during transaction processing, tx " + Base58.encode(transaction.getSignature()), e);
|
||||||
|
System.err.println("Exception during transaction processing, tx " + Base58.encode(transaction.getTransactionData().getSignature()) + ": "
|
||||||
|
+ e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
return ValidationResult.TRANSACTION_PROCESSING_FAILED;
|
return ValidationResult.TRANSACTION_PROCESSING_FAILED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -565,8 +569,8 @@ public class Block {
|
|||||||
this.repository.discardChanges();
|
this.repository.discardChanges();
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
/*
|
/*
|
||||||
* Discard failure most likely due to prior DataException, so catch discardChanges' DataException and discard.
|
* Discard failure most likely due to prior DataException, so catch discardChanges' DataException and discard. Prior DataException propagates to
|
||||||
* Prior DataException propagates to caller. Successful completion of try-block continues on after discard.
|
* caller. Successful completion of try-block continues on after discard.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ package qora.naming;
|
|||||||
|
|
||||||
import data.naming.NameData;
|
import data.naming.NameData;
|
||||||
import data.transaction.RegisterNameTransactionData;
|
import data.transaction.RegisterNameTransactionData;
|
||||||
|
import data.transaction.TransactionData;
|
||||||
|
import data.transaction.UpdateNameTransactionData;
|
||||||
import repository.DataException;
|
import repository.DataException;
|
||||||
import repository.Repository;
|
import repository.Repository;
|
||||||
|
|
||||||
@ -13,7 +15,7 @@ public class Name {
|
|||||||
|
|
||||||
// Useful constants
|
// Useful constants
|
||||||
public static final int MAX_NAME_SIZE = 400;
|
public static final int MAX_NAME_SIZE = 400;
|
||||||
public static final int MAX_VALUE_SIZE = 4000;
|
public static final int MAX_DATA_SIZE = 4000;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -26,7 +28,8 @@ public class Name {
|
|||||||
public Name(Repository repository, RegisterNameTransactionData registerNameTransactionData) {
|
public Name(Repository repository, RegisterNameTransactionData registerNameTransactionData) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.nameData = new NameData(registerNameTransactionData.getRegistrantPublicKey(), registerNameTransactionData.getOwner(),
|
this.nameData = new NameData(registerNameTransactionData.getRegistrantPublicKey(), registerNameTransactionData.getOwner(),
|
||||||
registerNameTransactionData.getName(), registerNameTransactionData.getData(), registerNameTransactionData.getTimestamp());
|
registerNameTransactionData.getName(), registerNameTransactionData.getData(), registerNameTransactionData.getTimestamp(),
|
||||||
|
registerNameTransactionData.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,4 +54,49 @@ public class Name {
|
|||||||
this.repository.getNameRepository().delete(this.nameData.getName());
|
this.repository.getNameRepository().delete(this.nameData.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update(UpdateNameTransactionData updateNameTransactionData) throws DataException {
|
||||||
|
// Update reference in transaction data
|
||||||
|
updateNameTransactionData.setNameReference(this.nameData.getReference());
|
||||||
|
|
||||||
|
// New name reference is this transaction's signature
|
||||||
|
this.nameData.setReference(updateNameTransactionData.getSignature());
|
||||||
|
|
||||||
|
// Update Name's owner and data
|
||||||
|
this.nameData.setOwner(updateNameTransactionData.getNewOwner());
|
||||||
|
this.nameData.setData(updateNameTransactionData.getNewData());
|
||||||
|
|
||||||
|
// Save updated name data
|
||||||
|
this.repository.getNameRepository().save(this.nameData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void revert(UpdateNameTransactionData updateNameTransactionData) throws DataException {
|
||||||
|
// Previous name reference is taken from this transaction's cached copy
|
||||||
|
this.nameData.setReference(updateNameTransactionData.getNameReference());
|
||||||
|
|
||||||
|
// Previous Name's owner and/or data taken from referenced transaction
|
||||||
|
TransactionData previousTransactionData = this.repository.getTransactionRepository().fromSignature(this.nameData.getReference());
|
||||||
|
if (previousTransactionData == null)
|
||||||
|
throw new DataException("Unable to un-update name as referenced transaction not found in repository");
|
||||||
|
|
||||||
|
switch (previousTransactionData.getType()) {
|
||||||
|
case REGISTER_NAME:
|
||||||
|
RegisterNameTransactionData previousRegisterNameTransactionData = (RegisterNameTransactionData) previousTransactionData;
|
||||||
|
this.nameData.setOwner(previousRegisterNameTransactionData.getOwner());
|
||||||
|
this.nameData.setData(previousRegisterNameTransactionData.getData());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UPDATE_NAME:
|
||||||
|
UpdateNameTransactionData previousUpdateNameTransactionData = (UpdateNameTransactionData) previousTransactionData;
|
||||||
|
this.nameData.setData(previousUpdateNameTransactionData.getNewData());
|
||||||
|
this.nameData.setOwner(previousUpdateNameTransactionData.getNewOwner());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unable to revert update name transaction due to unsupported referenced transaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save reverted name data
|
||||||
|
this.repository.getNameRepository().save(this.nameData);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ public class RegisterNameTransaction extends Transaction {
|
|||||||
return ValidationResult.INVALID_NAME_LENGTH;
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
// Check value size bounds
|
// Check value size bounds
|
||||||
if (registerNameTransactionData.getData().length() < 1 || registerNameTransactionData.getData().length() > Name.MAX_VALUE_SIZE)
|
if (registerNameTransactionData.getData().length() < 1 || registerNameTransactionData.getData().length() > Name.MAX_DATA_SIZE)
|
||||||
return ValidationResult.INVALID_DATA_LENGTH;
|
return ValidationResult.INVALID_DATA_LENGTH;
|
||||||
|
|
||||||
// Check name is lowercase
|
// Check name is lowercase
|
||||||
|
@ -44,11 +44,12 @@ public abstract class Transaction {
|
|||||||
// Validation results
|
// Validation results
|
||||||
public enum ValidationResult {
|
public enum ValidationResult {
|
||||||
OK(1), INVALID_ADDRESS(2), NEGATIVE_AMOUNT(3), NEGATIVE_FEE(4), NO_BALANCE(5), INVALID_REFERENCE(6), INVALID_NAME_LENGTH(7), INVALID_VALUE_LENGTH(
|
OK(1), INVALID_ADDRESS(2), NEGATIVE_AMOUNT(3), NEGATIVE_FEE(4), NO_BALANCE(5), INVALID_REFERENCE(6), INVALID_NAME_LENGTH(7), INVALID_VALUE_LENGTH(
|
||||||
8), NAME_ALREADY_REGISTERED(9), INVALID_AMOUNT(15), NAME_NOT_LOWER_CASE(17), INVALID_DESCRIPTION_LENGTH(18), INVALID_OPTIONS_COUNT(
|
8), NAME_ALREADY_REGISTERED(9), NAME_DOES_NOT_EXIST(10), INVALID_NAME_OWNER(11), NAME_ALREADY_FOR_SALE(12), INVALID_AMOUNT(
|
||||||
19), INVALID_OPTION_LENGTH(20), DUPLICATE_OPTION(21), POLL_ALREADY_EXISTS(22), POLL_DOES_NOT_EXIST(24), POLL_OPTION_DOES_NOT_EXIST(
|
15), NAME_NOT_LOWER_CASE(17), INVALID_DESCRIPTION_LENGTH(18), INVALID_OPTIONS_COUNT(19), INVALID_OPTION_LENGTH(20), DUPLICATE_OPTION(
|
||||||
25), ALREADY_VOTED_FOR_THAT_OPTION(26), INVALID_DATA_LENGTH(27), INVALID_QUANTITY(28), ASSET_DOES_NOT_EXIST(29), INVALID_RETURN(
|
21), POLL_ALREADY_EXISTS(22), POLL_DOES_NOT_EXIST(24), POLL_OPTION_DOES_NOT_EXIST(25), ALREADY_VOTED_FOR_THAT_OPTION(
|
||||||
30), HAVE_EQUALS_WANT(31), ORDER_DOES_NOT_EXIST(32), INVALID_ORDER_CREATOR(
|
26), INVALID_DATA_LENGTH(27), INVALID_QUANTITY(28), ASSET_DOES_NOT_EXIST(29), INVALID_RETURN(30), HAVE_EQUALS_WANT(
|
||||||
33), INVALID_PAYMENTS_COUNT(34), NEGATIVE_PRICE(35), ASSET_ALREADY_EXISTS(43), NOT_YET_RELEASED(1000);
|
31), ORDER_DOES_NOT_EXIST(32), INVALID_ORDER_CREATOR(
|
||||||
|
33), INVALID_PAYMENTS_COUNT(34), NEGATIVE_PRICE(35), ASSET_ALREADY_EXISTS(43), NOT_YET_RELEASED(1000);
|
||||||
|
|
||||||
public final int value;
|
public final int value;
|
||||||
|
|
||||||
@ -107,6 +108,9 @@ public abstract class Transaction {
|
|||||||
case REGISTER_NAME:
|
case REGISTER_NAME:
|
||||||
return new RegisterNameTransaction(repository, transactionData);
|
return new RegisterNameTransaction(repository, transactionData);
|
||||||
|
|
||||||
|
case UPDATE_NAME:
|
||||||
|
return new UpdateNameTransaction(repository, transactionData);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return new CreatePollTransaction(repository, transactionData);
|
return new CreatePollTransaction(repository, transactionData);
|
||||||
|
|
||||||
|
157
src/qora/transaction/UpdateNameTransaction.java
Normal file
157
src/qora/transaction/UpdateNameTransaction.java
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package qora.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import data.transaction.UpdateNameTransactionData;
|
||||||
|
import data.naming.NameData;
|
||||||
|
import data.transaction.TransactionData;
|
||||||
|
import qora.account.Account;
|
||||||
|
import qora.account.PublicKeyAccount;
|
||||||
|
import qora.assets.Asset;
|
||||||
|
import qora.crypto.Crypto;
|
||||||
|
import qora.naming.Name;
|
||||||
|
import repository.DataException;
|
||||||
|
import repository.Repository;
|
||||||
|
|
||||||
|
public class UpdateNameTransaction extends Transaction {
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
private UpdateNameTransactionData updateNameTransactionData;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
public UpdateNameTransaction(Repository repository, TransactionData transactionData) {
|
||||||
|
super(repository, transactionData);
|
||||||
|
|
||||||
|
this.updateNameTransactionData = (UpdateNameTransactionData) this.transactionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
|
return Collections.singletonList(getNewOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
|
String address = account.getAddress();
|
||||||
|
|
||||||
|
if (address.equals(this.getOwner().getAddress()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (address.equals(this.getNewOwner().getAddress()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
|
String address = account.getAddress();
|
||||||
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
|
|
||||||
|
if (address.equals(this.getOwner().getAddress()))
|
||||||
|
amount = amount.subtract(this.transactionData.getFee());
|
||||||
|
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
|
||||||
|
public Account getOwner() throws DataException {
|
||||||
|
return new PublicKeyAccount(this.repository, this.updateNameTransactionData.getOwnerPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account getNewOwner() throws DataException {
|
||||||
|
return new Account(this.repository, this.updateNameTransactionData.getNewOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidationResult isValid() throws DataException {
|
||||||
|
// Check new owner address is valid
|
||||||
|
if (!Crypto.isValidAddress(updateNameTransactionData.getNewOwner()))
|
||||||
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
|
// Check name size bounds
|
||||||
|
if (updateNameTransactionData.getName().length() < 1 || updateNameTransactionData.getName().length() > Name.MAX_NAME_SIZE)
|
||||||
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
|
// Check value size bounds
|
||||||
|
if (updateNameTransactionData.getNewData().length() < 1 || updateNameTransactionData.getNewData().length() > Name.MAX_DATA_SIZE)
|
||||||
|
return ValidationResult.INVALID_DATA_LENGTH;
|
||||||
|
|
||||||
|
// Check name is lowercase
|
||||||
|
if (!updateNameTransactionData.getName().equals(updateNameTransactionData.getName().toLowerCase()))
|
||||||
|
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||||
|
|
||||||
|
NameData nameData = this.repository.getNameRepository().fromName(updateNameTransactionData.getName());
|
||||||
|
|
||||||
|
// Check name exists
|
||||||
|
if (nameData == null)
|
||||||
|
return ValidationResult.NAME_DOES_NOT_EXIST;
|
||||||
|
|
||||||
|
// Check name isn't currently for sale
|
||||||
|
if (nameData.getIsForSale())
|
||||||
|
return ValidationResult.NAME_ALREADY_FOR_SALE;
|
||||||
|
|
||||||
|
// Check transaction's public key matches name's current owner
|
||||||
|
Account owner = new PublicKeyAccount(this.repository, updateNameTransactionData.getOwnerPublicKey());
|
||||||
|
if (!owner.getAddress().equals(nameData.getOwner()))
|
||||||
|
return ValidationResult.INVALID_NAME_OWNER;
|
||||||
|
|
||||||
|
// Check fee is positive
|
||||||
|
if (updateNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
||||||
|
return ValidationResult.NEGATIVE_FEE;
|
||||||
|
|
||||||
|
// Check reference is correct
|
||||||
|
if (!Arrays.equals(owner.getLastReference(), updateNameTransactionData.getReference()))
|
||||||
|
return ValidationResult.INVALID_REFERENCE;
|
||||||
|
|
||||||
|
// Check issuer has enough funds
|
||||||
|
if (owner.getConfirmedBalance(Asset.QORA).compareTo(updateNameTransactionData.getFee()) == -1)
|
||||||
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
|
return ValidationResult.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process() throws DataException {
|
||||||
|
// Update Name
|
||||||
|
Name name = new Name(this.repository, updateNameTransactionData.getName());
|
||||||
|
name.update(updateNameTransactionData);
|
||||||
|
|
||||||
|
// Save this transaction, now with updated "name reference" to previous transaction that updated name
|
||||||
|
this.repository.getTransactionRepository().save(updateNameTransactionData);
|
||||||
|
|
||||||
|
// Update owner's balance
|
||||||
|
Account owner = new PublicKeyAccount(this.repository, updateNameTransactionData.getOwnerPublicKey());
|
||||||
|
owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).subtract(updateNameTransactionData.getFee()));
|
||||||
|
|
||||||
|
// Update owner's reference
|
||||||
|
owner.setLastReference(updateNameTransactionData.getSignature());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void orphan() throws DataException {
|
||||||
|
// Revert name
|
||||||
|
Name name = new Name(this.repository, updateNameTransactionData.getName());
|
||||||
|
name.revert(updateNameTransactionData);
|
||||||
|
|
||||||
|
// Delete this transaction itself
|
||||||
|
this.repository.getTransactionRepository().delete(updateNameTransactionData);
|
||||||
|
|
||||||
|
// Update owner's balance
|
||||||
|
Account owner = new PublicKeyAccount(this.repository, updateNameTransactionData.getOwnerPublicKey());
|
||||||
|
owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).add(updateNameTransactionData.getFee()));
|
||||||
|
|
||||||
|
// Update owner's reference
|
||||||
|
owner.setLastReference(updateNameTransactionData.getReference());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -171,7 +171,7 @@ public class HSQLDBDatabaseUpdates {
|
|||||||
case 6:
|
case 6:
|
||||||
// Update Name Transactions
|
// Update Name Transactions
|
||||||
stmt.execute("CREATE TABLE UpdateNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
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, "
|
+ "new_owner QoraAddress NOT NULL, new_data NameData NOT NULL, name_reference Signature NOT NULL, "
|
||||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -324,6 +324,14 @@ public class HSQLDBDatabaseUpdates {
|
|||||||
stmt.execute("CREATE INDEX PollOwnerIndex on Polls (owner)");
|
stmt.execute("CREATE INDEX PollOwnerIndex on Polls (owner)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 25:
|
||||||
|
// Registered Names
|
||||||
|
stmt.execute(
|
||||||
|
"CREATE TABLE Names (name RegisteredName, data VARCHAR(4000) NOT NULL, registrant QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, "
|
||||||
|
+ "registered TIMESTAMP NOT NULL, updated TIMESTAMP, reference Signature, is_for_sale BOOLEAN NOT NULL, sale_price QoraAmount, "
|
||||||
|
+ "PRIMARY KEY (name))");
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
return false;
|
return false;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package repository.hsqldb;
|
package repository.hsqldb;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
import data.naming.NameData;
|
import data.naming.NameData;
|
||||||
import repository.NameRepository;
|
import repository.NameRepository;
|
||||||
@ -18,16 +20,25 @@ public class HSQLDBNameRepository implements NameRepository {
|
|||||||
@Override
|
@Override
|
||||||
public NameData fromName(String name) throws DataException {
|
public NameData fromName(String name) throws DataException {
|
||||||
try {
|
try {
|
||||||
ResultSet resultSet = this.repository.checkedExecute("SELECT registrant, owner, name, registered FROM Names WHERE name = ?", name);
|
ResultSet resultSet = this.repository
|
||||||
|
.checkedExecute("SELECT registrant, owner, data, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE name = ?", name);
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] registrantPublicKey = resultSet.getBytes(1);
|
byte[] registrantPublicKey = resultSet.getBytes(1);
|
||||||
String owner = resultSet.getString(2);
|
String owner = resultSet.getString(2);
|
||||||
String data = resultSet.getString(3);
|
String data = resultSet.getString(3);
|
||||||
long timestamp = resultSet.getLong(4);
|
long registered = resultSet.getTimestamp(4).getTime();
|
||||||
|
|
||||||
return new NameData(registrantPublicKey, owner, name, data, timestamp);
|
// Special handling for possibly-NULL "updated" column
|
||||||
|
Timestamp updatedTimestamp = resultSet.getTimestamp(5);
|
||||||
|
Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime();
|
||||||
|
|
||||||
|
byte[] reference = resultSet.getBytes(6);
|
||||||
|
boolean isForSale = resultSet.getBoolean(7);
|
||||||
|
BigDecimal salePrice = resultSet.getBigDecimal(8);
|
||||||
|
|
||||||
|
return new NameData(registrantPublicKey, owner, name, data, registered, updated, reference, isForSale, salePrice);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to fetch name info from repository", e);
|
throw new DataException("Unable to fetch name info from repository", e);
|
||||||
}
|
}
|
||||||
@ -46,8 +57,13 @@ public class HSQLDBNameRepository implements NameRepository {
|
|||||||
public void save(NameData nameData) throws DataException {
|
public void save(NameData nameData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("Names");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("Names");
|
||||||
|
|
||||||
|
// Special handling for "updated" timestamp
|
||||||
|
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("registrant", nameData.getRegistrantPublicKey()).bind("owner", nameData.getOwner()).bind("name", nameData.getName())
|
||||||
.bind("data", nameData.getData()).bind("registered", nameData.getRegistered());
|
.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());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
saveHelper.execute(this.repository);
|
saveHelper.execute(this.repository);
|
||||||
|
@ -22,6 +22,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
private HSQLDBGenesisTransactionRepository genesisTransactionRepository;
|
private HSQLDBGenesisTransactionRepository genesisTransactionRepository;
|
||||||
private HSQLDBPaymentTransactionRepository paymentTransactionRepository;
|
private HSQLDBPaymentTransactionRepository paymentTransactionRepository;
|
||||||
private HSQLDBRegisterNameTransactionRepository registerNameTransactionRepository;
|
private HSQLDBRegisterNameTransactionRepository registerNameTransactionRepository;
|
||||||
|
private HSQLDBUpdateNameTransactionRepository updateNameTransactionRepository;
|
||||||
private HSQLDBCreatePollTransactionRepository createPollTransactionRepository;
|
private HSQLDBCreatePollTransactionRepository createPollTransactionRepository;
|
||||||
private HSQLDBVoteOnPollTransactionRepository voteOnPollTransactionRepository;
|
private HSQLDBVoteOnPollTransactionRepository voteOnPollTransactionRepository;
|
||||||
private HSQLDBIssueAssetTransactionRepository issueAssetTransactionRepository;
|
private HSQLDBIssueAssetTransactionRepository issueAssetTransactionRepository;
|
||||||
@ -36,6 +37,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
this.genesisTransactionRepository = new HSQLDBGenesisTransactionRepository(repository);
|
this.genesisTransactionRepository = new HSQLDBGenesisTransactionRepository(repository);
|
||||||
this.paymentTransactionRepository = new HSQLDBPaymentTransactionRepository(repository);
|
this.paymentTransactionRepository = new HSQLDBPaymentTransactionRepository(repository);
|
||||||
this.registerNameTransactionRepository = new HSQLDBRegisterNameTransactionRepository(repository);
|
this.registerNameTransactionRepository = new HSQLDBRegisterNameTransactionRepository(repository);
|
||||||
|
this.updateNameTransactionRepository = new HSQLDBUpdateNameTransactionRepository(repository);
|
||||||
this.createPollTransactionRepository = new HSQLDBCreatePollTransactionRepository(repository);
|
this.createPollTransactionRepository = new HSQLDBCreatePollTransactionRepository(repository);
|
||||||
this.voteOnPollTransactionRepository = new HSQLDBVoteOnPollTransactionRepository(repository);
|
this.voteOnPollTransactionRepository = new HSQLDBVoteOnPollTransactionRepository(repository);
|
||||||
this.issueAssetTransactionRepository = new HSQLDBIssueAssetTransactionRepository(repository);
|
this.issueAssetTransactionRepository = new HSQLDBIssueAssetTransactionRepository(repository);
|
||||||
@ -97,6 +99,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
case REGISTER_NAME:
|
case REGISTER_NAME:
|
||||||
return this.registerNameTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
return this.registerNameTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
|
|
||||||
|
case UPDATE_NAME:
|
||||||
|
return this.updateNameTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return this.createPollTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
return this.createPollTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
|
|
||||||
@ -229,6 +234,10 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
this.registerNameTransactionRepository.save(transactionData);
|
this.registerNameTransactionRepository.save(transactionData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UPDATE_NAME:
|
||||||
|
this.updateNameTransactionRepository.save(transactionData);
|
||||||
|
break;
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
this.createPollTransactionRepository.save(transactionData);
|
this.createPollTransactionRepository.save(transactionData);
|
||||||
break;
|
break;
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package repository.hsqldb.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import data.transaction.UpdateNameTransactionData;
|
||||||
|
import data.transaction.TransactionData;
|
||||||
|
import repository.DataException;
|
||||||
|
import repository.hsqldb.HSQLDBRepository;
|
||||||
|
import repository.hsqldb.HSQLDBSaver;
|
||||||
|
|
||||||
|
public class HSQLDBUpdateNameTransactionRepository extends HSQLDBTransactionRepository {
|
||||||
|
|
||||||
|
public HSQLDBUpdateNameTransactionRepository(HSQLDBRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
|
try {
|
||||||
|
ResultSet rs = this.repository.checkedExecute("SELECT new_owner, name, new_data, name_reference FROM UpdateNameTransactions WHERE signature = ?", signature);
|
||||||
|
if (rs == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String newOwner = rs.getString(1);
|
||||||
|
String name = rs.getString(2);
|
||||||
|
String newData = rs.getString(3);
|
||||||
|
byte[] nameReference = rs.getBytes(4);
|
||||||
|
|
||||||
|
return new UpdateNameTransactionData(ownerPublicKey, newOwner, name, newData, nameReference, fee, timestamp, reference, signature);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to fetch update name transaction from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(TransactionData transactionData) throws DataException {
|
||||||
|
UpdateNameTransactionData updateNameTransactionData = (UpdateNameTransactionData) transactionData;
|
||||||
|
|
||||||
|
HSQLDBSaver saveHelper = new HSQLDBSaver("UpdateNameTransactions");
|
||||||
|
|
||||||
|
saveHelper.bind("signature", updateNameTransactionData.getSignature()).bind("owner", updateNameTransactionData.getOwnerPublicKey())
|
||||||
|
.bind("new_owner", updateNameTransactionData.getNewOwner()).bind("name", updateNameTransactionData.getName())
|
||||||
|
.bind("new_data", updateNameTransactionData.getNewData()).bind("name_reference", updateNameTransactionData.getNameReference());
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveHelper.execute(this.repository);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to save update name transaction into repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,6 +14,7 @@ import qora.block.Block;
|
|||||||
import qora.block.GenesisBlock;
|
import qora.block.GenesisBlock;
|
||||||
import qora.transaction.GenesisTransaction;
|
import qora.transaction.GenesisTransaction;
|
||||||
import qora.transaction.Transaction;
|
import qora.transaction.Transaction;
|
||||||
|
import qora.transaction.Transaction.TransactionType;
|
||||||
import repository.DataException;
|
import repository.DataException;
|
||||||
import repository.Repository;
|
import repository.Repository;
|
||||||
import repository.RepositoryManager;
|
import repository.RepositoryManager;
|
||||||
@ -65,13 +66,10 @@ public class SerializationTests extends Common {
|
|||||||
assertEquals(TransactionTransformer.getDataLength(transactionData), bytes.length);
|
assertEquals(TransactionTransformer.getDataLength(transactionData), bytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private void testSpecificBlockTransactions(int height, TransactionType type) throws DataException, TransformationException {
|
||||||
public void testPaymentSerialization() throws TransformationException, DataException {
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
// Block 949 has lots of varied transactions
|
BlockData blockData = repository.getBlockRepository().fromHeight(height);
|
||||||
// Blocks 390 & 754 have only payment transactions
|
assertNotNull("Block " + height + " is required for this test", blockData);
|
||||||
BlockData blockData = repository.getBlockRepository().fromHeight(754);
|
|
||||||
assertNotNull("Block 754 is required for this test", blockData);
|
|
||||||
|
|
||||||
Block block = new Block(repository, blockData);
|
Block block = new Block(repository, blockData);
|
||||||
|
|
||||||
@ -79,48 +77,39 @@ public class SerializationTests extends Common {
|
|||||||
assertNotNull(transactions);
|
assertNotNull(transactions);
|
||||||
|
|
||||||
for (Transaction transaction : transactions)
|
for (Transaction transaction : transactions)
|
||||||
testGenericSerialization(transaction.getTransactionData());
|
if (transaction.getTransactionData().getType() == type)
|
||||||
|
testGenericSerialization(transaction.getTransactionData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMessageSerialization() throws TransformationException {
|
public void testPaymentSerialization() throws TransformationException, DataException {
|
||||||
// Message transactions went live block 99000
|
// Blocks 390 & 754 have only payment transactions
|
||||||
// Some transactions to be found in block 99001/2/5/6
|
testSpecificBlockTransactions(754, TransactionType.PAYMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRegisterNameSerialization() throws TransformationException, DataException {
|
public void testRegisterNameSerialization() throws TransformationException, DataException {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
// Block 120 has only name registration transactions
|
||||||
// Block 120 has only name registration transactions
|
testSpecificBlockTransactions(120, TransactionType.REGISTER_NAME);
|
||||||
BlockData blockData = repository.getBlockRepository().fromHeight(120);
|
}
|
||||||
assertNotNull("Block 120 is required for this test", blockData);
|
|
||||||
|
|
||||||
Block block = new Block(repository, blockData);
|
@Test
|
||||||
|
public void testUpdateNameSerialization() throws TransformationException, DataException {
|
||||||
List<Transaction> transactions = block.getTransactions();
|
testSpecificBlockTransactions(673, TransactionType.UPDATE_NAME);
|
||||||
assertNotNull(transactions);
|
|
||||||
|
|
||||||
for (Transaction transaction : transactions)
|
|
||||||
testGenericSerialization(transaction.getTransactionData());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreatePollSerialization() throws TransformationException, DataException {
|
public void testCreatePollSerialization() throws TransformationException, DataException {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
// Block 10537 has only create poll transactions
|
||||||
// Block 10537 has only create poll transactions
|
testSpecificBlockTransactions(10537, TransactionType.CREATE_POLL);
|
||||||
BlockData blockData = repository.getBlockRepository().fromHeight(10537);
|
}
|
||||||
assertNotNull("Block 10537 is required for this test", blockData);
|
|
||||||
|
|
||||||
Block block = new Block(repository, blockData);
|
@Test
|
||||||
|
public void testMessageSerialization() throws TransformationException, DataException {
|
||||||
List<Transaction> transactions = block.getTransactions();
|
// Message transactions went live block 99000
|
||||||
assertNotNull(transactions);
|
// Some transactions to be found in block 99001/2/5/6
|
||||||
|
testSpecificBlockTransactions(99001, TransactionType.MESSAGE);
|
||||||
for (Transaction transaction : transactions)
|
|
||||||
testGenericSerialization(transaction.getTransactionData());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -15,8 +15,11 @@ import com.google.common.hash.HashCode;
|
|||||||
import data.account.AccountBalanceData;
|
import data.account.AccountBalanceData;
|
||||||
import data.account.AccountData;
|
import data.account.AccountData;
|
||||||
import data.block.BlockData;
|
import data.block.BlockData;
|
||||||
|
import data.naming.NameData;
|
||||||
import data.transaction.CreatePollTransactionData;
|
import data.transaction.CreatePollTransactionData;
|
||||||
import data.transaction.PaymentTransactionData;
|
import data.transaction.PaymentTransactionData;
|
||||||
|
import data.transaction.RegisterNameTransactionData;
|
||||||
|
import data.transaction.UpdateNameTransactionData;
|
||||||
import data.transaction.VoteOnPollTransactionData;
|
import data.transaction.VoteOnPollTransactionData;
|
||||||
import data.voting.PollData;
|
import data.voting.PollData;
|
||||||
import data.voting.PollOptionData;
|
import data.voting.PollOptionData;
|
||||||
@ -29,8 +32,10 @@ import qora.block.Block;
|
|||||||
import qora.block.BlockChain;
|
import qora.block.BlockChain;
|
||||||
import qora.transaction.CreatePollTransaction;
|
import qora.transaction.CreatePollTransaction;
|
||||||
import qora.transaction.PaymentTransaction;
|
import qora.transaction.PaymentTransaction;
|
||||||
|
import qora.transaction.RegisterNameTransaction;
|
||||||
import qora.transaction.Transaction;
|
import qora.transaction.Transaction;
|
||||||
import qora.transaction.Transaction.ValidationResult;
|
import qora.transaction.Transaction.ValidationResult;
|
||||||
|
import qora.transaction.UpdateNameTransaction;
|
||||||
import qora.transaction.VoteOnPollTransaction;
|
import qora.transaction.VoteOnPollTransaction;
|
||||||
import repository.AccountRepository;
|
import repository.AccountRepository;
|
||||||
import repository.DataException;
|
import repository.DataException;
|
||||||
@ -54,7 +59,7 @@ public class TransactionTests {
|
|||||||
|
|
||||||
private Repository repository;
|
private Repository repository;
|
||||||
private AccountRepository accountRepository;
|
private AccountRepository accountRepository;
|
||||||
private BlockData genesisBlockData;
|
private BlockData parentBlockData;
|
||||||
private PrivateKeyAccount sender;
|
private PrivateKeyAccount sender;
|
||||||
private PrivateKeyAccount generator;
|
private PrivateKeyAccount generator;
|
||||||
private byte[] reference;
|
private byte[] reference;
|
||||||
@ -80,7 +85,7 @@ public class TransactionTests {
|
|||||||
repository = RepositoryManager.getRepository();
|
repository = RepositoryManager.getRepository();
|
||||||
|
|
||||||
// Grab genesis block
|
// Grab genesis block
|
||||||
genesisBlockData = repository.getBlockRepository().fromHeight(1);
|
parentBlockData = repository.getBlockRepository().fromHeight(1);
|
||||||
|
|
||||||
accountRepository = repository.getAccountRepository();
|
accountRepository = repository.getAccountRepository();
|
||||||
|
|
||||||
@ -115,7 +120,7 @@ public class TransactionTests {
|
|||||||
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
||||||
BigDecimal amount = BigDecimal.valueOf(1_000L);
|
BigDecimal amount = BigDecimal.valueOf(1_000L);
|
||||||
BigDecimal fee = BigDecimal.ONE;
|
BigDecimal fee = BigDecimal.ONE;
|
||||||
long timestamp = genesisBlockData.getTimestamp() + 1_000;
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
||||||
PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), recipient.getAddress(), amount, fee, timestamp,
|
PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), recipient.getAddress(), amount, fee, timestamp,
|
||||||
reference);
|
reference);
|
||||||
|
|
||||||
@ -125,7 +130,7 @@ public class TransactionTests {
|
|||||||
assertEquals(ValidationResult.OK, paymentTransaction.isValid());
|
assertEquals(ValidationResult.OK, paymentTransaction.isValid());
|
||||||
|
|
||||||
// Forge new block with payment transaction
|
// Forge new block with payment transaction
|
||||||
Block block = new Block(repository, genesisBlockData, generator, null, null);
|
Block block = new Block(repository, parentBlockData, generator, null, null);
|
||||||
block.addTransaction(paymentTransactionData);
|
block.addTransaction(paymentTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
@ -151,6 +156,106 @@ public class TransactionTests {
|
|||||||
assertTrue("Recipient's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue("Recipient's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegisterNameTransaction() throws DataException {
|
||||||
|
createTestAccounts(null);
|
||||||
|
|
||||||
|
// Make a new register name transaction
|
||||||
|
String name = "test name";
|
||||||
|
String data = "{\"key\":\"value\"}";
|
||||||
|
|
||||||
|
BigDecimal fee = BigDecimal.ONE;
|
||||||
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
||||||
|
RegisterNameTransactionData registerNameTransactionData = new RegisterNameTransactionData(sender.getPublicKey(), sender.getAddress(), name, data, fee,
|
||||||
|
timestamp, reference);
|
||||||
|
|
||||||
|
Transaction registerNameTransaction = new RegisterNameTransaction(repository, registerNameTransactionData);
|
||||||
|
registerNameTransaction.calcSignature(sender);
|
||||||
|
assertTrue(registerNameTransaction.isSignatureValid());
|
||||||
|
assertEquals(ValidationResult.OK, registerNameTransaction.isValid());
|
||||||
|
|
||||||
|
// Forge new block with transaction
|
||||||
|
Block block = new Block(repository, parentBlockData, generator, null, null);
|
||||||
|
block.addTransaction(registerNameTransactionData);
|
||||||
|
block.sign();
|
||||||
|
|
||||||
|
assertTrue("Block signatures invalid", block.isSignatureValid());
|
||||||
|
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
||||||
|
|
||||||
|
block.process();
|
||||||
|
repository.saveChanges();
|
||||||
|
|
||||||
|
// Check sender's balance
|
||||||
|
BigDecimal expectedBalance = senderBalance.subtract(fee);
|
||||||
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
|
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
||||||
|
|
||||||
|
// Fee should be in generator's balance
|
||||||
|
expectedBalance = generatorBalance.add(fee);
|
||||||
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
|
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
||||||
|
|
||||||
|
// Check name was registered
|
||||||
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
||||||
|
assertNotNull(actualNameData);
|
||||||
|
|
||||||
|
// Check sender's reference
|
||||||
|
assertTrue("Sender's new reference incorrect", Arrays.equals(registerNameTransactionData.getSignature(), sender.getLastReference()));
|
||||||
|
|
||||||
|
// Update variables for use by other tests
|
||||||
|
reference = sender.getLastReference();
|
||||||
|
parentBlockData = block.getBlockData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateNamesTransaction() throws DataException {
|
||||||
|
// Register name using another test
|
||||||
|
testRegisterNameTransaction();
|
||||||
|
|
||||||
|
String name = "test name";
|
||||||
|
NameData originalNameData = this.repository.getNameRepository().fromName(name);
|
||||||
|
|
||||||
|
// Update name's owner and data
|
||||||
|
Account newOwner = new PublicKeyAccount(repository, recipientSeed);
|
||||||
|
String newData = "{\"newKey\":\"newValue\"}";
|
||||||
|
byte[] nameReference = reference;
|
||||||
|
|
||||||
|
BigDecimal fee = BigDecimal.ONE;
|
||||||
|
long timestamp = parentBlockData.getTimestamp() + 2_000;
|
||||||
|
UpdateNameTransactionData updateNameTransactionData = new UpdateNameTransactionData(sender.getPublicKey(), newOwner.getAddress(), name, newData,
|
||||||
|
nameReference, fee, timestamp, reference);
|
||||||
|
|
||||||
|
Transaction updateNameTransaction = new UpdateNameTransaction(repository, updateNameTransactionData);
|
||||||
|
updateNameTransaction.calcSignature(sender);
|
||||||
|
assertTrue(updateNameTransaction.isSignatureValid());
|
||||||
|
assertEquals(ValidationResult.OK, updateNameTransaction.isValid());
|
||||||
|
|
||||||
|
// Forge new block with transaction
|
||||||
|
Block block = new Block(repository, parentBlockData, generator, null, null);
|
||||||
|
block.addTransaction(updateNameTransactionData);
|
||||||
|
block.sign();
|
||||||
|
|
||||||
|
assertTrue("Block signatures invalid", block.isSignatureValid());
|
||||||
|
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
||||||
|
|
||||||
|
block.process();
|
||||||
|
repository.saveChanges();
|
||||||
|
|
||||||
|
// Check name was updated
|
||||||
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
||||||
|
assertEquals(newOwner.getAddress(), actualNameData.getOwner());
|
||||||
|
assertEquals(newData, actualNameData.getData());
|
||||||
|
|
||||||
|
// Now orphan block
|
||||||
|
block.orphan();
|
||||||
|
repository.saveChanges();
|
||||||
|
|
||||||
|
// Check name has been reverted correctly
|
||||||
|
actualNameData = this.repository.getNameRepository().fromName(name);
|
||||||
|
assertEquals(originalNameData.getOwner(), actualNameData.getOwner());
|
||||||
|
assertEquals(originalNameData.getData(), actualNameData.getData());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreatePollTransaction() throws DataException {
|
public void testCreatePollTransaction() throws DataException {
|
||||||
// This test requires GenesisBlock's timestamp is set to something after BlockChain.VOTING_RELEASE_TIMESTAMP
|
// This test requires GenesisBlock's timestamp is set to something after BlockChain.VOTING_RELEASE_TIMESTAMP
|
||||||
@ -167,7 +272,7 @@ public class TransactionTests {
|
|||||||
|
|
||||||
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
||||||
BigDecimal fee = BigDecimal.ONE;
|
BigDecimal fee = BigDecimal.ONE;
|
||||||
long timestamp = genesisBlockData.getTimestamp() + 1_000;
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
||||||
CreatePollTransactionData createPollTransactionData = new CreatePollTransactionData(sender.getPublicKey(), recipient.getAddress(), pollName,
|
CreatePollTransactionData createPollTransactionData = new CreatePollTransactionData(sender.getPublicKey(), recipient.getAddress(), pollName,
|
||||||
description, pollOptions, fee, timestamp, reference);
|
description, pollOptions, fee, timestamp, reference);
|
||||||
|
|
||||||
@ -177,7 +282,7 @@ public class TransactionTests {
|
|||||||
assertEquals(ValidationResult.OK, createPollTransaction.isValid());
|
assertEquals(ValidationResult.OK, createPollTransaction.isValid());
|
||||||
|
|
||||||
// Forge new block with transaction
|
// Forge new block with transaction
|
||||||
Block block = new Block(repository, genesisBlockData, generator, null, null);
|
Block block = new Block(repository, parentBlockData, generator, null, null);
|
||||||
block.addTransaction(createPollTransactionData);
|
block.addTransaction(createPollTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
@ -204,8 +309,9 @@ public class TransactionTests {
|
|||||||
// Check sender's reference
|
// Check sender's reference
|
||||||
assertTrue("Sender's new reference incorrect", Arrays.equals(createPollTransactionData.getSignature(), sender.getLastReference()));
|
assertTrue("Sender's new reference incorrect", Arrays.equals(createPollTransactionData.getSignature(), sender.getLastReference()));
|
||||||
|
|
||||||
// Update reference variable for use by other tests
|
// Update variables for use by other tests
|
||||||
reference = sender.getLastReference();
|
reference = sender.getLastReference();
|
||||||
|
parentBlockData = block.getBlockData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -217,8 +323,7 @@ public class TransactionTests {
|
|||||||
String pollName = "test poll";
|
String pollName = "test poll";
|
||||||
int pollOptionsSize = 3;
|
int pollOptionsSize = 3;
|
||||||
BigDecimal fee = BigDecimal.ONE;
|
BigDecimal fee = BigDecimal.ONE;
|
||||||
long timestamp = genesisBlockData.getTimestamp() + 1_000;
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
||||||
BlockData previousBlockData = genesisBlockData;
|
|
||||||
|
|
||||||
for (int optionIndex = 0; optionIndex <= pollOptionsSize; ++optionIndex) {
|
for (int optionIndex = 0; optionIndex <= pollOptionsSize; ++optionIndex) {
|
||||||
// Make a vote-on-poll transaction
|
// Make a vote-on-poll transaction
|
||||||
@ -236,7 +341,7 @@ public class TransactionTests {
|
|||||||
assertEquals(ValidationResult.OK, voteOnPollTransaction.isValid());
|
assertEquals(ValidationResult.OK, voteOnPollTransaction.isValid());
|
||||||
|
|
||||||
// Forge new block with transaction
|
// Forge new block with transaction
|
||||||
Block block = new Block(repository, previousBlockData, generator, null, null);
|
Block block = new Block(repository, parentBlockData, generator, null, null);
|
||||||
block.addTransaction(voteOnPollTransactionData);
|
block.addTransaction(voteOnPollTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
@ -252,7 +357,7 @@ public class TransactionTests {
|
|||||||
assertEquals(optionIndex, actualVoteOnPollData.getOptionIndex());
|
assertEquals(optionIndex, actualVoteOnPollData.getOptionIndex());
|
||||||
|
|
||||||
// update variables for next round
|
// update variables for next round
|
||||||
previousBlockData = block.getBlockData();
|
parentBlockData = block.getBlockData();
|
||||||
timestamp += 1_000;
|
timestamp += 1_000;
|
||||||
reference = voteOnPollTransaction.getTransactionData().getSignature();
|
reference = voteOnPollTransaction.getTransactionData().getSignature();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package transform;
|
|||||||
|
|
||||||
public abstract class Transformer {
|
public abstract class Transformer {
|
||||||
|
|
||||||
public static final int BOOLEAN_LENGTH = 4;
|
public static final int BOOLEAN_LENGTH = 1;
|
||||||
public static final int INT_LENGTH = 4;
|
public static final int INT_LENGTH = 4;
|
||||||
public static final int LONG_LENGTH = 8;
|
public static final int LONG_LENGTH = 8;
|
||||||
public static final int BIG_DECIMAL_LENGTH = 8;
|
public static final int BIG_DECIMAL_LENGTH = 8;
|
||||||
|
@ -64,7 +64,7 @@ public class MessageTransactionTransformer extends TransactionTransformer {
|
|||||||
|
|
||||||
BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer);
|
BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer);
|
||||||
|
|
||||||
int dataSize = byteBuffer.getInt(0);
|
int dataSize = byteBuffer.getInt();
|
||||||
// Don't allow invalid dataSize here to avoid run-time issues
|
// Don't allow invalid dataSize here to avoid run-time issues
|
||||||
if (dataSize > MessageTransaction.MAX_DATA_SIZE)
|
if (dataSize > MessageTransaction.MAX_DATA_SIZE)
|
||||||
throw new TransformationException("MessageTransaction data size too large");
|
throw new TransformationException("MessageTransaction data size too large");
|
||||||
|
@ -25,9 +25,9 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer {
|
|||||||
private static final int REGISTRANT_LENGTH = PUBLIC_KEY_LENGTH;
|
private static final int REGISTRANT_LENGTH = PUBLIC_KEY_LENGTH;
|
||||||
private static final int OWNER_LENGTH = ADDRESS_LENGTH;
|
private static final int OWNER_LENGTH = ADDRESS_LENGTH;
|
||||||
private static final int NAME_SIZE_LENGTH = INT_LENGTH;
|
private static final int NAME_SIZE_LENGTH = INT_LENGTH;
|
||||||
private static final int VALUE_SIZE_LENGTH = INT_LENGTH;
|
private static final int DATA_SIZE_LENGTH = INT_LENGTH;
|
||||||
|
|
||||||
private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + REGISTRANT_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + VALUE_SIZE_LENGTH;
|
private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + REGISTRANT_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + DATA_SIZE_LENGTH;
|
||||||
|
|
||||||
static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
||||||
if (byteBuffer.remaining() < TYPELESS_DATALESS_LENGTH)
|
if (byteBuffer.remaining() < TYPELESS_DATALESS_LENGTH)
|
||||||
@ -43,7 +43,7 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer {
|
|||||||
String owner = Serialization.deserializeRecipient(byteBuffer);
|
String owner = Serialization.deserializeRecipient(byteBuffer);
|
||||||
|
|
||||||
String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE);
|
String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE);
|
||||||
String value = Serialization.deserializeSizedString(byteBuffer, Name.MAX_VALUE_SIZE);
|
String data = Serialization.deserializeSizedString(byteBuffer, Name.MAX_DATA_SIZE);
|
||||||
|
|
||||||
// Still need to make sure there are enough bytes left for remaining fields
|
// Still need to make sure there are enough bytes left for remaining fields
|
||||||
if (byteBuffer.remaining() < FEE_LENGTH + SIGNATURE_LENGTH)
|
if (byteBuffer.remaining() < FEE_LENGTH + SIGNATURE_LENGTH)
|
||||||
@ -54,13 +54,14 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer {
|
|||||||
byte[] signature = new byte[SIGNATURE_LENGTH];
|
byte[] signature = new byte[SIGNATURE_LENGTH];
|
||||||
byteBuffer.get(signature);
|
byteBuffer.get(signature);
|
||||||
|
|
||||||
return new RegisterNameTransactionData(registrantPublicKey, owner, name, value, fee, timestamp, reference, signature);
|
return new RegisterNameTransactionData(registrantPublicKey, owner, name, data, fee, timestamp, reference, signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||||
RegisterNameTransactionData registerNameTransactionData = (RegisterNameTransactionData) transactionData;
|
RegisterNameTransactionData registerNameTransactionData = (RegisterNameTransactionData) transactionData;
|
||||||
|
|
||||||
int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + registerNameTransactionData.getName().length() + registerNameTransactionData.getData().length();
|
int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + registerNameTransactionData.getName().length()
|
||||||
|
+ registerNameTransactionData.getData().length();
|
||||||
|
|
||||||
return dataLength;
|
return dataLength;
|
||||||
}
|
}
|
||||||
@ -105,7 +106,7 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer {
|
|||||||
|
|
||||||
json.put("owner", registerNameTransactionData.getOwner());
|
json.put("owner", registerNameTransactionData.getOwner());
|
||||||
json.put("name", registerNameTransactionData.getName());
|
json.put("name", registerNameTransactionData.getName());
|
||||||
json.put("value", registerNameTransactionData.getData());
|
json.put("data", registerNameTransactionData.getData());
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
throw new TransformationException(e);
|
throw new TransformationException(e);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case REGISTER_NAME:
|
case REGISTER_NAME:
|
||||||
return RegisterNameTransactionTransformer.fromByteBuffer(byteBuffer);
|
return RegisterNameTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||||
|
|
||||||
|
case UPDATE_NAME:
|
||||||
|
return UpdateNameTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return CreatePollTransactionTransformer.fromByteBuffer(byteBuffer);
|
return CreatePollTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||||
|
|
||||||
@ -80,6 +83,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case REGISTER_NAME:
|
case REGISTER_NAME:
|
||||||
return RegisterNameTransactionTransformer.getDataLength(transactionData);
|
return RegisterNameTransactionTransformer.getDataLength(transactionData);
|
||||||
|
|
||||||
|
case UPDATE_NAME:
|
||||||
|
return UpdateNameTransactionTransformer.getDataLength(transactionData);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return CreatePollTransactionTransformer.getDataLength(transactionData);
|
return CreatePollTransactionTransformer.getDataLength(transactionData);
|
||||||
|
|
||||||
@ -120,6 +126,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case REGISTER_NAME:
|
case REGISTER_NAME:
|
||||||
return RegisterNameTransactionTransformer.toBytes(transactionData);
|
return RegisterNameTransactionTransformer.toBytes(transactionData);
|
||||||
|
|
||||||
|
case UPDATE_NAME:
|
||||||
|
return UpdateNameTransactionTransformer.toBytes(transactionData);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return CreatePollTransactionTransformer.toBytes(transactionData);
|
return CreatePollTransactionTransformer.toBytes(transactionData);
|
||||||
|
|
||||||
@ -160,6 +169,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case REGISTER_NAME:
|
case REGISTER_NAME:
|
||||||
return RegisterNameTransactionTransformer.toJSON(transactionData);
|
return RegisterNameTransactionTransformer.toJSON(transactionData);
|
||||||
|
|
||||||
|
case UPDATE_NAME:
|
||||||
|
return UpdateNameTransactionTransformer.toJSON(transactionData);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return CreatePollTransactionTransformer.toJSON(transactionData);
|
return CreatePollTransactionTransformer.toJSON(transactionData);
|
||||||
|
|
||||||
|
117
src/transform/transaction/UpdateNameTransactionTransformer.java
Normal file
117
src/transform/transaction/UpdateNameTransactionTransformer.java
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package transform.transaction;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
|
||||||
|
import com.google.common.hash.HashCode;
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import com.google.common.primitives.Longs;
|
||||||
|
|
||||||
|
import data.transaction.UpdateNameTransactionData;
|
||||||
|
import data.transaction.TransactionData;
|
||||||
|
import qora.account.PublicKeyAccount;
|
||||||
|
import qora.naming.Name;
|
||||||
|
import transform.TransformationException;
|
||||||
|
import utils.Base58;
|
||||||
|
import utils.Serialization;
|
||||||
|
|
||||||
|
public class UpdateNameTransactionTransformer extends TransactionTransformer {
|
||||||
|
|
||||||
|
// Property lengths
|
||||||
|
private static final int REGISTRANT_LENGTH = PUBLIC_KEY_LENGTH;
|
||||||
|
private static final int OWNER_LENGTH = ADDRESS_LENGTH;
|
||||||
|
private static final int NAME_SIZE_LENGTH = INT_LENGTH;
|
||||||
|
private static final int DATA_SIZE_LENGTH = INT_LENGTH;
|
||||||
|
|
||||||
|
private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + REGISTRANT_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + DATA_SIZE_LENGTH;
|
||||||
|
|
||||||
|
static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
||||||
|
if (byteBuffer.remaining() < TYPELESS_DATALESS_LENGTH)
|
||||||
|
throw new TransformationException("Byte data too short for UpdateNameTransaction");
|
||||||
|
|
||||||
|
long timestamp = byteBuffer.getLong();
|
||||||
|
|
||||||
|
byte[] reference = new byte[REFERENCE_LENGTH];
|
||||||
|
byteBuffer.get(reference);
|
||||||
|
|
||||||
|
byte[] ownerPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||||
|
|
||||||
|
String newOwner = Serialization.deserializeRecipient(byteBuffer);
|
||||||
|
|
||||||
|
String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE);
|
||||||
|
String newData = Serialization.deserializeSizedString(byteBuffer, Name.MAX_DATA_SIZE);
|
||||||
|
|
||||||
|
// Still need to make sure there are enough bytes left for remaining fields
|
||||||
|
if (byteBuffer.remaining() < FEE_LENGTH + SIGNATURE_LENGTH)
|
||||||
|
throw new TransformationException("Byte data too short for UpdateNameTransaction");
|
||||||
|
|
||||||
|
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
||||||
|
|
||||||
|
byte[] signature = new byte[SIGNATURE_LENGTH];
|
||||||
|
byteBuffer.get(signature);
|
||||||
|
|
||||||
|
return new UpdateNameTransactionData(ownerPublicKey, newOwner, name, newData, null, fee, timestamp, reference, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||||
|
UpdateNameTransactionData updateNameTransactionData = (UpdateNameTransactionData) transactionData;
|
||||||
|
|
||||||
|
int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + updateNameTransactionData.getName().length()
|
||||||
|
+ updateNameTransactionData.getNewData().length();
|
||||||
|
|
||||||
|
return dataLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
|
||||||
|
try {
|
||||||
|
UpdateNameTransactionData updateNameTransactionData = (UpdateNameTransactionData) transactionData;
|
||||||
|
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
bytes.write(Ints.toByteArray(updateNameTransactionData.getType().value));
|
||||||
|
bytes.write(Longs.toByteArray(updateNameTransactionData.getTimestamp()));
|
||||||
|
bytes.write(updateNameTransactionData.getReference());
|
||||||
|
|
||||||
|
bytes.write(updateNameTransactionData.getOwnerPublicKey());
|
||||||
|
bytes.write(Base58.decode(updateNameTransactionData.getNewOwner()));
|
||||||
|
Serialization.serializeSizedString(bytes, updateNameTransactionData.getName());
|
||||||
|
Serialization.serializeSizedString(bytes, updateNameTransactionData.getNewData());
|
||||||
|
|
||||||
|
Serialization.serializeBigDecimal(bytes, updateNameTransactionData.getFee());
|
||||||
|
|
||||||
|
if (updateNameTransactionData.getSignature() != null)
|
||||||
|
bytes.write(updateNameTransactionData.getSignature());
|
||||||
|
|
||||||
|
return bytes.toByteArray();
|
||||||
|
} catch (IOException | ClassCastException e) {
|
||||||
|
throw new TransformationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
|
||||||
|
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
|
||||||
|
|
||||||
|
try {
|
||||||
|
UpdateNameTransactionData updateNameTransactionData = (UpdateNameTransactionData) transactionData;
|
||||||
|
|
||||||
|
byte[] ownerPublicKey = updateNameTransactionData.getOwnerPublicKey();
|
||||||
|
|
||||||
|
json.put("owner", PublicKeyAccount.getAddress(ownerPublicKey));
|
||||||
|
json.put("ownerPublicKey", HashCode.fromBytes(ownerPublicKey).toString());
|
||||||
|
|
||||||
|
json.put("newOwner", updateNameTransactionData.getNewOwner());
|
||||||
|
json.put("name", updateNameTransactionData.getName());
|
||||||
|
json.put("newData", updateNameTransactionData.getNewData());
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new TransformationException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user