forked from Qortal/qortal
Added RegisterNameTransactions
Fixed CreatePollTransaction transformer bugs
This commit is contained in:
parent
70d25f24ce
commit
4de2caaa28
44
src/data/naming/NameData.java
Normal file
44
src/data/naming/NameData.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package data.naming;
|
||||||
|
|
||||||
|
public class NameData {
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
private byte[] registrantPublicKey;
|
||||||
|
private String owner;
|
||||||
|
private String name;
|
||||||
|
private String value;
|
||||||
|
private long registered;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
public NameData(byte[] registrantPublicKey, String owner, String name, String value, long timestamp) {
|
||||||
|
this.registrantPublicKey = registrantPublicKey;
|
||||||
|
this.owner = owner;
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
this.registered = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters / setters
|
||||||
|
|
||||||
|
public byte[] getRegistrantPublicKey() {
|
||||||
|
return this.registrantPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOwner() {
|
||||||
|
return this.owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRegistered() {
|
||||||
|
return this.registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
src/data/transaction/RegisterNameTransactionData.java
Normal file
49
src/data/transaction/RegisterNameTransactionData.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package data.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import qora.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
|
public class RegisterNameTransactionData extends TransactionData {
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
private byte[] registrantPublicKey;
|
||||||
|
private String owner;
|
||||||
|
private String name;
|
||||||
|
private String data;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
public RegisterNameTransactionData(byte[] registrantPublicKey, String owner, String name, String data, BigDecimal fee, long timestamp, byte[] reference,
|
||||||
|
byte[] signature) {
|
||||||
|
super(TransactionType.REGISTER_NAME, fee, registrantPublicKey, timestamp, reference, signature);
|
||||||
|
|
||||||
|
this.registrantPublicKey = registrantPublicKey;
|
||||||
|
this.owner = owner;
|
||||||
|
this.name = name;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegisterNameTransactionData(byte[] registrantPublicKey, String owner, String name, String data, BigDecimal fee, long timestamp, byte[] reference) {
|
||||||
|
this(registrantPublicKey, owner, name, data, fee, timestamp, reference, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters / setters
|
||||||
|
|
||||||
|
public byte[] getRegistrantPublicKey() {
|
||||||
|
return this.registrantPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOwner() {
|
||||||
|
return this.owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
src/qora/naming/Name.java
Normal file
54
src/qora/naming/Name.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package qora.naming;
|
||||||
|
|
||||||
|
import data.naming.NameData;
|
||||||
|
import data.transaction.RegisterNameTransactionData;
|
||||||
|
import repository.DataException;
|
||||||
|
import repository.Repository;
|
||||||
|
|
||||||
|
public class Name {
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
private Repository repository;
|
||||||
|
private NameData nameData;
|
||||||
|
|
||||||
|
// Useful constants
|
||||||
|
public static final int MAX_NAME_SIZE = 400;
|
||||||
|
public static final int MAX_VALUE_SIZE = 4000;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct Name business object using info from register name transaction.
|
||||||
|
*
|
||||||
|
* @param repository
|
||||||
|
* @param registerNameTransactionData
|
||||||
|
*/
|
||||||
|
public Name(Repository repository, RegisterNameTransactionData registerNameTransactionData) {
|
||||||
|
this.repository = repository;
|
||||||
|
this.nameData = new NameData(registerNameTransactionData.getRegistrantPublicKey(), registerNameTransactionData.getOwner(),
|
||||||
|
registerNameTransactionData.getName(), registerNameTransactionData.getData(), registerNameTransactionData.getTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct Name business object using existing name in repository.
|
||||||
|
*
|
||||||
|
* @param repository
|
||||||
|
* @param name
|
||||||
|
* @throws DataException
|
||||||
|
*/
|
||||||
|
public Name(Repository repository, String name) throws DataException {
|
||||||
|
this.repository = repository;
|
||||||
|
this.nameData = this.repository.getNameRepository().fromName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing
|
||||||
|
|
||||||
|
public void register() throws DataException {
|
||||||
|
this.repository.getNameRepository().save(this.nameData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregister() throws DataException {
|
||||||
|
this.repository.getNameRepository().delete(this.nameData.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -167,11 +167,11 @@ public class CreatePollTransaction extends Transaction {
|
|||||||
// Delete this transaction itself
|
// Delete this transaction itself
|
||||||
this.repository.getTransactionRepository().delete(createPollTransactionData);
|
this.repository.getTransactionRepository().delete(createPollTransactionData);
|
||||||
|
|
||||||
// Update issuer's balance
|
// Update creator's balance
|
||||||
Account creator = new PublicKeyAccount(this.repository, createPollTransactionData.getCreatorPublicKey());
|
Account creator = new PublicKeyAccount(this.repository, createPollTransactionData.getCreatorPublicKey());
|
||||||
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(createPollTransactionData.getFee()));
|
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(createPollTransactionData.getFee()));
|
||||||
|
|
||||||
// Update issuer's reference
|
// Update creator's reference
|
||||||
creator.setLastReference(createPollTransactionData.getReference());
|
creator.setLastReference(createPollTransactionData.getReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
147
src/qora/transaction/RegisterNameTransaction.java
Normal file
147
src/qora/transaction/RegisterNameTransaction.java
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package qora.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import data.transaction.RegisterNameTransactionData;
|
||||||
|
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 RegisterNameTransaction extends Transaction {
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
private RegisterNameTransactionData registerNameTransactionData;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
public RegisterNameTransaction(Repository repository, TransactionData transactionData) {
|
||||||
|
super(repository, transactionData);
|
||||||
|
|
||||||
|
this.registerNameTransactionData = (RegisterNameTransactionData) this.transactionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
|
return Collections.singletonList(getOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
|
String address = account.getAddress();
|
||||||
|
|
||||||
|
if (address.equals(this.getRegistrant().getAddress()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (address.equals(this.getOwner().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.getRegistrant().getAddress()))
|
||||||
|
amount = amount.subtract(this.transactionData.getFee());
|
||||||
|
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
|
||||||
|
public Account getRegistrant() throws DataException {
|
||||||
|
return new PublicKeyAccount(this.repository, this.registerNameTransactionData.getRegistrantPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account getOwner() throws DataException {
|
||||||
|
return new Account(this.repository, this.registerNameTransactionData.getOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidationResult isValid() throws DataException {
|
||||||
|
// Check owner address is valid
|
||||||
|
if (!Crypto.isValidAddress(registerNameTransactionData.getOwner()))
|
||||||
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
|
// Check name size bounds
|
||||||
|
if (registerNameTransactionData.getName().length() < 1 || registerNameTransactionData.getName().length() > Name.MAX_NAME_SIZE)
|
||||||
|
return ValidationResult.INVALID_NAME_LENGTH;
|
||||||
|
|
||||||
|
// Check value size bounds
|
||||||
|
if (registerNameTransactionData.getData().length() < 1 || registerNameTransactionData.getData().length() > Name.MAX_VALUE_SIZE)
|
||||||
|
return ValidationResult.INVALID_DATA_LENGTH;
|
||||||
|
|
||||||
|
// Check name is lowercase
|
||||||
|
if (!registerNameTransactionData.getName().equals(registerNameTransactionData.getName().toLowerCase()))
|
||||||
|
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||||
|
|
||||||
|
// Check the name isn't already taken
|
||||||
|
if (this.repository.getNameRepository().nameExists(registerNameTransactionData.getName()))
|
||||||
|
return ValidationResult.NAME_ALREADY_REGISTERED;
|
||||||
|
|
||||||
|
// Check fee is positive
|
||||||
|
if (registerNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
||||||
|
return ValidationResult.NEGATIVE_FEE;
|
||||||
|
|
||||||
|
// Check reference is correct
|
||||||
|
PublicKeyAccount registrant = new PublicKeyAccount(this.repository, registerNameTransactionData.getRegistrantPublicKey());
|
||||||
|
|
||||||
|
if (!Arrays.equals(registrant.getLastReference(), registerNameTransactionData.getReference()))
|
||||||
|
return ValidationResult.INVALID_REFERENCE;
|
||||||
|
|
||||||
|
// Check issuer has enough funds
|
||||||
|
if (registrant.getConfirmedBalance(Asset.QORA).compareTo(registerNameTransactionData.getFee()) == -1)
|
||||||
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
|
return ValidationResult.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process() throws DataException {
|
||||||
|
// Register Name
|
||||||
|
Name name = new Name(this.repository, registerNameTransactionData);
|
||||||
|
name.register();
|
||||||
|
|
||||||
|
// Save this transaction
|
||||||
|
this.repository.getTransactionRepository().save(registerNameTransactionData);
|
||||||
|
|
||||||
|
// Update registrant's balance
|
||||||
|
Account registrant = new PublicKeyAccount(this.repository, registerNameTransactionData.getRegistrantPublicKey());
|
||||||
|
registrant.setConfirmedBalance(Asset.QORA, registrant.getConfirmedBalance(Asset.QORA).subtract(registerNameTransactionData.getFee()));
|
||||||
|
|
||||||
|
// Update registrant's reference
|
||||||
|
registrant.setLastReference(registerNameTransactionData.getSignature());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void orphan() throws DataException {
|
||||||
|
// Unregister name
|
||||||
|
Name name = new Name(this.repository, registerNameTransactionData.getName());
|
||||||
|
name.unregister();
|
||||||
|
|
||||||
|
// Delete this transaction itself
|
||||||
|
this.repository.getTransactionRepository().delete(registerNameTransactionData);
|
||||||
|
|
||||||
|
// Update registrant's balance
|
||||||
|
Account registrant = new PublicKeyAccount(this.repository, registerNameTransactionData.getRegistrantPublicKey());
|
||||||
|
registrant.setConfirmedBalance(Asset.QORA, registrant.getConfirmedBalance(Asset.QORA).add(registerNameTransactionData.getFee()));
|
||||||
|
|
||||||
|
// Update registrant's reference
|
||||||
|
registrant.setLastReference(registerNameTransactionData.getReference());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -43,11 +43,11 @@ 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_AMOUNT(
|
OK(1), INVALID_ADDRESS(2), NEGATIVE_AMOUNT(3), NEGATIVE_FEE(4), NO_BALANCE(5), INVALID_REFERENCE(6), INVALID_NAME_LENGTH(7), INVALID_VALUE_LENGTH(
|
||||||
15), NAME_NOT_LOWER_CASE(17), INVALID_DESCRIPTION_LENGTH(18), INVALID_OPTIONS_COUNT(19), INVALID_OPTION_LENGTH(20), DUPLICATE_OPTION(
|
8), NAME_ALREADY_REGISTERED(9), INVALID_AMOUNT(15), NAME_NOT_LOWER_CASE(17), INVALID_DESCRIPTION_LENGTH(18), INVALID_OPTIONS_COUNT(
|
||||||
21), POLL_ALREADY_EXISTS(22), POLL_DOES_NOT_EXIST(24), POLL_OPTION_DOES_NOT_EXIST(25), ALREADY_VOTED_FOR_THAT_OPTION(
|
19), INVALID_OPTION_LENGTH(20), DUPLICATE_OPTION(21), POLL_ALREADY_EXISTS(22), POLL_DOES_NOT_EXIST(24), POLL_OPTION_DOES_NOT_EXIST(
|
||||||
26), INVALID_DATA_LENGTH(27), INVALID_QUANTITY(28), ASSET_DOES_NOT_EXIST(29), INVALID_RETURN(30), HAVE_EQUALS_WANT(
|
25), ALREADY_VOTED_FOR_THAT_OPTION(26), INVALID_DATA_LENGTH(27), INVALID_QUANTITY(28), ASSET_DOES_NOT_EXIST(29), INVALID_RETURN(
|
||||||
31), ORDER_DOES_NOT_EXIST(32), INVALID_ORDER_CREATOR(
|
30), HAVE_EQUALS_WANT(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);
|
33), INVALID_PAYMENTS_COUNT(34), NEGATIVE_PRICE(35), ASSET_ALREADY_EXISTS(43), NOT_YET_RELEASED(1000);
|
||||||
|
|
||||||
public final int value;
|
public final int value;
|
||||||
@ -104,6 +104,9 @@ public abstract class Transaction {
|
|||||||
case PAYMENT:
|
case PAYMENT:
|
||||||
return new PaymentTransaction(repository, transactionData);
|
return new PaymentTransaction(repository, transactionData);
|
||||||
|
|
||||||
|
case REGISTER_NAME:
|
||||||
|
return new RegisterNameTransaction(repository, transactionData);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return new CreatePollTransaction(repository, transactionData);
|
return new CreatePollTransaction(repository, transactionData);
|
||||||
|
|
||||||
|
15
src/repository/NameRepository.java
Normal file
15
src/repository/NameRepository.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package repository;
|
||||||
|
|
||||||
|
import data.naming.NameData;
|
||||||
|
|
||||||
|
public interface NameRepository {
|
||||||
|
|
||||||
|
public NameData fromName(String name) throws DataException;
|
||||||
|
|
||||||
|
public boolean nameExists(String name) throws DataException;
|
||||||
|
|
||||||
|
public void save(NameData nameData) throws DataException;
|
||||||
|
|
||||||
|
public void delete(String name) throws DataException;
|
||||||
|
|
||||||
|
}
|
@ -8,6 +8,8 @@ public interface Repository extends AutoCloseable {
|
|||||||
|
|
||||||
public BlockRepository getBlockRepository();
|
public BlockRepository getBlockRepository();
|
||||||
|
|
||||||
|
public NameRepository getNameRepository();
|
||||||
|
|
||||||
public TransactionRepository getTransactionRepository();
|
public TransactionRepository getTransactionRepository();
|
||||||
|
|
||||||
public VotingRepository getVotingRepository();
|
public VotingRepository getVotingRepository();
|
||||||
|
68
src/repository/hsqldb/HSQLDBNameRepository.java
Normal file
68
src/repository/hsqldb/HSQLDBNameRepository.java
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package repository.hsqldb;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import data.naming.NameData;
|
||||||
|
import repository.NameRepository;
|
||||||
|
import repository.DataException;
|
||||||
|
|
||||||
|
public class HSQLDBNameRepository implements NameRepository {
|
||||||
|
|
||||||
|
protected HSQLDBRepository repository;
|
||||||
|
|
||||||
|
public HSQLDBNameRepository(HSQLDBRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NameData fromName(String name) throws DataException {
|
||||||
|
try {
|
||||||
|
ResultSet resultSet = this.repository.checkedExecute("SELECT registrant, owner, name, registered 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 timestamp = resultSet.getLong(4);
|
||||||
|
|
||||||
|
return new NameData(registrantPublicKey, owner, name, data, timestamp);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to fetch name info from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean nameExists(String name) throws DataException {
|
||||||
|
try {
|
||||||
|
return this.repository.exists("Names", "name = ?", name);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to check for name in repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(NameData nameData) throws DataException {
|
||||||
|
HSQLDBSaver saveHelper = new HSQLDBSaver("Names");
|
||||||
|
|
||||||
|
saveHelper.bind("registrant", nameData.getRegistrantPublicKey()).bind("owner", nameData.getOwner()).bind("name", nameData.getName())
|
||||||
|
.bind("data", nameData.getData()).bind("registered", nameData.getRegistered());
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveHelper.execute(this.repository);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to save name info into repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String name) throws DataException {
|
||||||
|
try {
|
||||||
|
this.repository.delete("Names", "name = ?", name);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to delete name info from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,6 +11,7 @@ import repository.AccountRepository;
|
|||||||
import repository.AssetRepository;
|
import repository.AssetRepository;
|
||||||
import repository.BlockRepository;
|
import repository.BlockRepository;
|
||||||
import repository.DataException;
|
import repository.DataException;
|
||||||
|
import repository.NameRepository;
|
||||||
import repository.Repository;
|
import repository.Repository;
|
||||||
import repository.TransactionRepository;
|
import repository.TransactionRepository;
|
||||||
import repository.VotingRepository;
|
import repository.VotingRepository;
|
||||||
@ -40,6 +41,11 @@ public class HSQLDBRepository implements Repository {
|
|||||||
return new HSQLDBBlockRepository(this);
|
return new HSQLDBBlockRepository(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NameRepository getNameRepository() {
|
||||||
|
return new HSQLDBNameRepository(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransactionRepository getTransactionRepository() {
|
public TransactionRepository getTransactionRepository() {
|
||||||
return new HSQLDBTransactionRepository(this);
|
return new HSQLDBTransactionRepository(this);
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
package repository.hsqldb.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import data.transaction.RegisterNameTransactionData;
|
||||||
|
import data.transaction.TransactionData;
|
||||||
|
import repository.DataException;
|
||||||
|
import repository.hsqldb.HSQLDBRepository;
|
||||||
|
import repository.hsqldb.HSQLDBSaver;
|
||||||
|
|
||||||
|
public class HSQLDBRegisterNameTransactionRepository extends HSQLDBTransactionRepository {
|
||||||
|
|
||||||
|
public HSQLDBRegisterNameTransactionRepository(HSQLDBRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] registrantPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
|
try {
|
||||||
|
ResultSet rs = this.repository.checkedExecute("SELECT owner, name, data FROM RegisterNameTransactions WHERE signature = ?", signature);
|
||||||
|
if (rs == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String owner = rs.getString(1);
|
||||||
|
String name = rs.getString(2);
|
||||||
|
String data = rs.getString(3);
|
||||||
|
|
||||||
|
return new RegisterNameTransactionData(registrantPublicKey, owner, name, data, fee, timestamp, reference, signature);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to fetch register name transaction from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(TransactionData transactionData) throws DataException {
|
||||||
|
RegisterNameTransactionData registerNameTransactionData = (RegisterNameTransactionData) transactionData;
|
||||||
|
|
||||||
|
HSQLDBSaver saveHelper = new HSQLDBSaver("RegisterNameTransactions");
|
||||||
|
|
||||||
|
saveHelper.bind("signature", registerNameTransactionData.getSignature()).bind("registrant", registerNameTransactionData.getRegistrantPublicKey())
|
||||||
|
.bind("owner", registerNameTransactionData.getOwner()).bind("name", registerNameTransactionData.getName())
|
||||||
|
.bind("data", registerNameTransactionData.getData());
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveHelper.execute(this.repository);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to save register name transaction into repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -21,6 +21,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
protected HSQLDBRepository repository;
|
protected HSQLDBRepository repository;
|
||||||
private HSQLDBGenesisTransactionRepository genesisTransactionRepository;
|
private HSQLDBGenesisTransactionRepository genesisTransactionRepository;
|
||||||
private HSQLDBPaymentTransactionRepository paymentTransactionRepository;
|
private HSQLDBPaymentTransactionRepository paymentTransactionRepository;
|
||||||
|
private HSQLDBRegisterNameTransactionRepository registerNameTransactionRepository;
|
||||||
private HSQLDBCreatePollTransactionRepository createPollTransactionRepository;
|
private HSQLDBCreatePollTransactionRepository createPollTransactionRepository;
|
||||||
private HSQLDBVoteOnPollTransactionRepository voteOnPollTransactionRepository;
|
private HSQLDBVoteOnPollTransactionRepository voteOnPollTransactionRepository;
|
||||||
private HSQLDBIssueAssetTransactionRepository issueAssetTransactionRepository;
|
private HSQLDBIssueAssetTransactionRepository issueAssetTransactionRepository;
|
||||||
@ -34,6 +35,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
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.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);
|
||||||
@ -92,6 +94,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
case PAYMENT:
|
case PAYMENT:
|
||||||
return this.paymentTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
return this.paymentTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
|
|
||||||
|
case REGISTER_NAME:
|
||||||
|
return this.registerNameTransactionRepository.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);
|
||||||
|
|
||||||
@ -220,6 +225,10 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
this.paymentTransactionRepository.save(transactionData);
|
this.paymentTransactionRepository.save(transactionData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case REGISTER_NAME:
|
||||||
|
this.registerNameTransactionRepository.save(transactionData);
|
||||||
|
break;
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
this.createPollTransactionRepository.save(transactionData);
|
this.createPollTransactionRepository.save(transactionData);
|
||||||
break;
|
break;
|
||||||
|
@ -2,7 +2,6 @@ package test;
|
|||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -85,9 +84,43 @@ public class SerializationTests extends Common {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMessageSerialization() throws SQLException, TransformationException {
|
public void testMessageSerialization() throws TransformationException {
|
||||||
// Message transactions went live block 99000
|
// Message transactions went live block 99000
|
||||||
// Some transactions to be found in block 99001/2/5/6
|
// Some transactions to be found in block 99001/2/5/6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegisterNameSerialization() throws TransformationException, DataException {
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
// Block 120 has only name registration transactions
|
||||||
|
BlockData blockData = repository.getBlockRepository().fromHeight(120);
|
||||||
|
assertNotNull("Block 120 is required for this test", blockData);
|
||||||
|
|
||||||
|
Block block = new Block(repository, blockData);
|
||||||
|
|
||||||
|
List<Transaction> transactions = block.getTransactions();
|
||||||
|
assertNotNull(transactions);
|
||||||
|
|
||||||
|
for (Transaction transaction : transactions)
|
||||||
|
testGenericSerialization(transaction.getTransactionData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatePollSerialization() throws TransformationException, DataException {
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
// Block 10537 has only create poll transactions
|
||||||
|
BlockData blockData = repository.getBlockRepository().fromHeight(10537);
|
||||||
|
assertNotNull("Block 10537 is required for this test", blockData);
|
||||||
|
|
||||||
|
Block block = new Block(repository, blockData);
|
||||||
|
|
||||||
|
List<Transaction> transactions = block.getTransactions();
|
||||||
|
assertNotNull(transactions);
|
||||||
|
|
||||||
|
for (Transaction transaction : transactions)
|
||||||
|
testGenericSerialization(transaction.getTransactionData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -26,12 +26,14 @@ import utils.Serialization;
|
|||||||
public class CreatePollTransactionTransformer extends TransactionTransformer {
|
public class CreatePollTransactionTransformer extends TransactionTransformer {
|
||||||
|
|
||||||
// Property lengths
|
// Property lengths
|
||||||
|
private static final int CREATOR_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 DESCRIPTION_SIZE_LENGTH = INT_LENGTH;
|
private static final int DESCRIPTION_SIZE_LENGTH = INT_LENGTH;
|
||||||
private static final int OPTIONS_SIZE_LENGTH = INT_LENGTH;
|
private static final int OPTIONS_SIZE_LENGTH = INT_LENGTH;
|
||||||
|
|
||||||
private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH;
|
private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + CREATOR_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH
|
||||||
|
+ OPTIONS_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)
|
||||||
@ -79,11 +81,12 @@ public class CreatePollTransactionTransformer extends TransactionTransformer {
|
|||||||
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||||
CreatePollTransactionData createPollTransactionData = (CreatePollTransactionData) transactionData;
|
CreatePollTransactionData createPollTransactionData = (CreatePollTransactionData) transactionData;
|
||||||
|
|
||||||
int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + createPollTransactionData.getPollName().length();
|
int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + createPollTransactionData.getPollName().length()
|
||||||
|
+ createPollTransactionData.getDescription().length();
|
||||||
|
|
||||||
// Add lengths for each poll options
|
// Add lengths for each poll options
|
||||||
for (PollOptionData pollOptionData : createPollTransactionData.getPollOptions())
|
for (PollOptionData pollOptionData : createPollTransactionData.getPollOptions())
|
||||||
dataLength += OPTIONS_SIZE_LENGTH + pollOptionData.getOptionName().length();
|
dataLength += INT_LENGTH + pollOptionData.getOptionName().length();
|
||||||
|
|
||||||
return dataLength;
|
return dataLength;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
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.RegisterNameTransactionData;
|
||||||
|
import data.transaction.TransactionData;
|
||||||
|
import qora.account.PublicKeyAccount;
|
||||||
|
import qora.naming.Name;
|
||||||
|
import transform.TransformationException;
|
||||||
|
import utils.Base58;
|
||||||
|
import utils.Serialization;
|
||||||
|
|
||||||
|
public class RegisterNameTransactionTransformer 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 VALUE_SIZE_LENGTH = INT_LENGTH;
|
||||||
|
|
||||||
|
private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + REGISTRANT_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + VALUE_SIZE_LENGTH;
|
||||||
|
|
||||||
|
static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
||||||
|
if (byteBuffer.remaining() < TYPELESS_DATALESS_LENGTH)
|
||||||
|
throw new TransformationException("Byte data too short for RegisterNameTransaction");
|
||||||
|
|
||||||
|
long timestamp = byteBuffer.getLong();
|
||||||
|
|
||||||
|
byte[] reference = new byte[REFERENCE_LENGTH];
|
||||||
|
byteBuffer.get(reference);
|
||||||
|
|
||||||
|
byte[] registrantPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||||
|
|
||||||
|
String owner = Serialization.deserializeRecipient(byteBuffer);
|
||||||
|
|
||||||
|
String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE);
|
||||||
|
String value = Serialization.deserializeSizedString(byteBuffer, Name.MAX_VALUE_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 RegisterNameTransaction");
|
||||||
|
|
||||||
|
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
||||||
|
|
||||||
|
byte[] signature = new byte[SIGNATURE_LENGTH];
|
||||||
|
byteBuffer.get(signature);
|
||||||
|
|
||||||
|
return new RegisterNameTransactionData(registrantPublicKey, owner, name, value, fee, timestamp, reference, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||||
|
RegisterNameTransactionData registerNameTransactionData = (RegisterNameTransactionData) transactionData;
|
||||||
|
|
||||||
|
int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + registerNameTransactionData.getName().length() + registerNameTransactionData.getData().length();
|
||||||
|
|
||||||
|
return dataLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
|
||||||
|
try {
|
||||||
|
RegisterNameTransactionData registerNameTransactionData = (RegisterNameTransactionData) transactionData;
|
||||||
|
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
bytes.write(Ints.toByteArray(registerNameTransactionData.getType().value));
|
||||||
|
bytes.write(Longs.toByteArray(registerNameTransactionData.getTimestamp()));
|
||||||
|
bytes.write(registerNameTransactionData.getReference());
|
||||||
|
|
||||||
|
bytes.write(registerNameTransactionData.getRegistrantPublicKey());
|
||||||
|
bytes.write(Base58.decode(registerNameTransactionData.getOwner()));
|
||||||
|
Serialization.serializeSizedString(bytes, registerNameTransactionData.getName());
|
||||||
|
Serialization.serializeSizedString(bytes, registerNameTransactionData.getData());
|
||||||
|
|
||||||
|
Serialization.serializeBigDecimal(bytes, registerNameTransactionData.getFee());
|
||||||
|
|
||||||
|
if (registerNameTransactionData.getSignature() != null)
|
||||||
|
bytes.write(registerNameTransactionData.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 {
|
||||||
|
RegisterNameTransactionData registerNameTransactionData = (RegisterNameTransactionData) transactionData;
|
||||||
|
|
||||||
|
byte[] registrantPublicKey = registerNameTransactionData.getRegistrantPublicKey();
|
||||||
|
|
||||||
|
json.put("registrant", PublicKeyAccount.getAddress(registrantPublicKey));
|
||||||
|
json.put("registrantPublicKey", HashCode.fromBytes(registrantPublicKey).toString());
|
||||||
|
|
||||||
|
json.put("owner", registerNameTransactionData.getOwner());
|
||||||
|
json.put("name", registerNameTransactionData.getName());
|
||||||
|
json.put("value", registerNameTransactionData.getData());
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new TransformationException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -37,6 +37,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case PAYMENT:
|
case PAYMENT:
|
||||||
return PaymentTransactionTransformer.fromByteBuffer(byteBuffer);
|
return PaymentTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||||
|
|
||||||
|
case REGISTER_NAME:
|
||||||
|
return RegisterNameTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return CreatePollTransactionTransformer.fromByteBuffer(byteBuffer);
|
return CreatePollTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||||
|
|
||||||
@ -74,6 +77,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case PAYMENT:
|
case PAYMENT:
|
||||||
return PaymentTransactionTransformer.getDataLength(transactionData);
|
return PaymentTransactionTransformer.getDataLength(transactionData);
|
||||||
|
|
||||||
|
case REGISTER_NAME:
|
||||||
|
return RegisterNameTransactionTransformer.getDataLength(transactionData);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return CreatePollTransactionTransformer.getDataLength(transactionData);
|
return CreatePollTransactionTransformer.getDataLength(transactionData);
|
||||||
|
|
||||||
@ -111,6 +117,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case PAYMENT:
|
case PAYMENT:
|
||||||
return PaymentTransactionTransformer.toBytes(transactionData);
|
return PaymentTransactionTransformer.toBytes(transactionData);
|
||||||
|
|
||||||
|
case REGISTER_NAME:
|
||||||
|
return RegisterNameTransactionTransformer.toBytes(transactionData);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return CreatePollTransactionTransformer.toBytes(transactionData);
|
return CreatePollTransactionTransformer.toBytes(transactionData);
|
||||||
|
|
||||||
@ -148,6 +157,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case PAYMENT:
|
case PAYMENT:
|
||||||
return PaymentTransactionTransformer.toJSON(transactionData);
|
return PaymentTransactionTransformer.toJSON(transactionData);
|
||||||
|
|
||||||
|
case REGISTER_NAME:
|
||||||
|
return RegisterNameTransactionTransformer.toJSON(transactionData);
|
||||||
|
|
||||||
case CREATE_POLL:
|
case CREATE_POLL:
|
||||||
return CreatePollTransactionTransformer.toJSON(transactionData);
|
return CreatePollTransactionTransformer.toJSON(transactionData);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user