mirror of
https://github.com/Qortal/qortal.git
synced 2025-05-03 16:27:50 +00:00
Asset-related transactions API + performance improvements + fix
Moved as much reflection out to class-static initializers as possible. Renamed some classes to fall in line with transaction type name to class name conversion, e.g. DEPLOY_AT -> DeployAt
This commit is contained in:
parent
4be58514c0
commit
6eb3520295
@ -29,6 +29,7 @@ import org.qora.api.ApiErrors;
|
|||||||
import org.qora.api.ApiException;
|
import org.qora.api.ApiException;
|
||||||
import org.qora.api.ApiExceptionFactory;
|
import org.qora.api.ApiExceptionFactory;
|
||||||
import org.qora.api.model.TradeWithOrderInfo;
|
import org.qora.api.model.TradeWithOrderInfo;
|
||||||
|
import org.qora.api.resource.TransactionsResource.ConfirmationStatus;
|
||||||
import org.qora.crypto.Crypto;
|
import org.qora.crypto.Crypto;
|
||||||
import org.qora.data.account.AccountBalanceData;
|
import org.qora.data.account.AccountBalanceData;
|
||||||
import org.qora.data.account.AccountData;
|
import org.qora.data.account.AccountData;
|
||||||
@ -38,6 +39,7 @@ import org.qora.data.asset.TradeData;
|
|||||||
import org.qora.data.transaction.CancelAssetOrderTransactionData;
|
import org.qora.data.transaction.CancelAssetOrderTransactionData;
|
||||||
import org.qora.data.transaction.CreateAssetOrderTransactionData;
|
import org.qora.data.transaction.CreateAssetOrderTransactionData;
|
||||||
import org.qora.data.transaction.IssueAssetTransactionData;
|
import org.qora.data.transaction.IssueAssetTransactionData;
|
||||||
|
import org.qora.data.transaction.TransactionData;
|
||||||
import org.qora.repository.DataException;
|
import org.qora.repository.DataException;
|
||||||
import org.qora.repository.Repository;
|
import org.qora.repository.Repository;
|
||||||
import org.qora.repository.RepositoryManager;
|
import org.qora.repository.RepositoryManager;
|
||||||
@ -482,6 +484,50 @@ public class AssetsResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/transactions/{assetid}")
|
||||||
|
@Operation(
|
||||||
|
summary = "Transactions related to asset",
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "Asset transactions",
|
||||||
|
content = @Content(
|
||||||
|
array = @ArraySchema(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = TransactionData.class
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ApiErrors({
|
||||||
|
ApiError.INVALID_ADDRESS, ApiError.INVALID_ASSET_ID, ApiError.REPOSITORY_ISSUE
|
||||||
|
})
|
||||||
|
public List<TransactionData> getAssetTransactions(@Parameter(
|
||||||
|
ref = "assetid"
|
||||||
|
) @PathParam("assetid") int assetId, @Parameter(
|
||||||
|
description = "whether to include confirmed, unconfirmed or both",
|
||||||
|
required = true
|
||||||
|
) @QueryParam("confirmationStatus") ConfirmationStatus confirmationStatus, @Parameter(
|
||||||
|
ref = "limit"
|
||||||
|
) @QueryParam("limit") Integer limit, @Parameter(
|
||||||
|
ref = "offset"
|
||||||
|
) @QueryParam("offset") Integer offset, @Parameter(
|
||||||
|
ref = "reverse"
|
||||||
|
) @QueryParam("reverse") Boolean reverse) {
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
if (!repository.getAssetRepository().assetExists(assetId))
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||||
|
|
||||||
|
return repository.getTransactionRepository().getAssetTransactions(assetId, confirmationStatus, limit, offset, reverse);
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (DataException e) {
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("/order/delete")
|
@Path("/order/delete")
|
||||||
@Operation(
|
@Operation(
|
||||||
|
@ -32,6 +32,18 @@ public interface TransactionRepository {
|
|||||||
public List<byte[]> getSignaturesMatchingCriteria(Integer startBlock, Integer blockLimit, TransactionType txType, String address,
|
public List<byte[]> getSignaturesMatchingCriteria(Integer startBlock, Integer blockLimit, TransactionType txType, String address,
|
||||||
ConfirmationStatus confirmationStatus, Integer limit, Integer offset, Boolean reverse) throws DataException;
|
ConfirmationStatus confirmationStatus, Integer limit, Integer offset, Boolean reverse) throws DataException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of transactions relating to specific asset ID.
|
||||||
|
*
|
||||||
|
* @param assetId
|
||||||
|
* @param limit
|
||||||
|
* @param offset
|
||||||
|
* @param reverse
|
||||||
|
* @return list of transactions, or empty if none
|
||||||
|
*/
|
||||||
|
public List<TransactionData> getAssetTransactions(int assetId, ConfirmationStatus confirmationStatus, Integer limit, Integer offset, Boolean reverse)
|
||||||
|
throws DataException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns list of unconfirmed transactions in timestamp-else-signature order.
|
* Returns list of unconfirmed transactions in timestamp-else-signature order.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -5,8 +5,8 @@ import java.sql.PreparedStatement;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,8 +77,7 @@ public class HSQLDBSaver {
|
|||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
private String formatInsertWithPlaceholders() {
|
private String formatInsertWithPlaceholders() {
|
||||||
String[] placeholders = new String[this.columns.size()];
|
List<String> placeholders = Collections.nCopies(this.columns.size(), "?");
|
||||||
Arrays.setAll(placeholders, (int i) -> "?");
|
|
||||||
|
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
output.append("INSERT INTO ");
|
output.append("INSERT INTO ");
|
||||||
|
@ -10,9 +10,9 @@ import org.qora.repository.DataException;
|
|||||||
import org.qora.repository.hsqldb.HSQLDBRepository;
|
import org.qora.repository.hsqldb.HSQLDBRepository;
|
||||||
import org.qora.repository.hsqldb.HSQLDBSaver;
|
import org.qora.repository.hsqldb.HSQLDBSaver;
|
||||||
|
|
||||||
public class HSQLDBATTransactionRepository extends HSQLDBTransactionRepository {
|
public class HSQLDBAtTransactionRepository extends HSQLDBTransactionRepository {
|
||||||
|
|
||||||
public HSQLDBATTransactionRepository(HSQLDBRepository repository) {
|
public HSQLDBAtTransactionRepository(HSQLDBRepository repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
@ -10,9 +10,9 @@ import org.qora.repository.DataException;
|
|||||||
import org.qora.repository.hsqldb.HSQLDBRepository;
|
import org.qora.repository.hsqldb.HSQLDBRepository;
|
||||||
import org.qora.repository.hsqldb.HSQLDBSaver;
|
import org.qora.repository.hsqldb.HSQLDBSaver;
|
||||||
|
|
||||||
public class HSQLDBCancelOrderTransactionRepository extends HSQLDBTransactionRepository {
|
public class HSQLDBCancelAssetOrderTransactionRepository extends HSQLDBTransactionRepository {
|
||||||
|
|
||||||
public HSQLDBCancelOrderTransactionRepository(HSQLDBRepository repository) {
|
public HSQLDBCancelAssetOrderTransactionRepository(HSQLDBRepository repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
@ -10,9 +10,9 @@ import org.qora.repository.DataException;
|
|||||||
import org.qora.repository.hsqldb.HSQLDBRepository;
|
import org.qora.repository.hsqldb.HSQLDBRepository;
|
||||||
import org.qora.repository.hsqldb.HSQLDBSaver;
|
import org.qora.repository.hsqldb.HSQLDBSaver;
|
||||||
|
|
||||||
public class HSQLDBCreateOrderTransactionRepository extends HSQLDBTransactionRepository {
|
public class HSQLDBCreateAssetOrderTransactionRepository extends HSQLDBTransactionRepository {
|
||||||
|
|
||||||
public HSQLDBCreateOrderTransactionRepository(HSQLDBRepository repository) {
|
public HSQLDBCreateAssetOrderTransactionRepository(HSQLDBRepository repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
@ -10,9 +10,9 @@ import org.qora.repository.DataException;
|
|||||||
import org.qora.repository.hsqldb.HSQLDBRepository;
|
import org.qora.repository.hsqldb.HSQLDBRepository;
|
||||||
import org.qora.repository.hsqldb.HSQLDBSaver;
|
import org.qora.repository.hsqldb.HSQLDBSaver;
|
||||||
|
|
||||||
public class HSQLDBDeployATTransactionRepository extends HSQLDBTransactionRepository {
|
public class HSQLDBDeployAtTransactionRepository extends HSQLDBTransactionRepository {
|
||||||
|
|
||||||
public HSQLDBDeployATTransactionRepository(HSQLDBRepository repository) {
|
public HSQLDBDeployAtTransactionRepository(HSQLDBRepository repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
@ -8,8 +8,10 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
@ -22,45 +24,90 @@ import org.qora.repository.hsqldb.HSQLDBRepository;
|
|||||||
import org.qora.repository.hsqldb.HSQLDBSaver;
|
import org.qora.repository.hsqldb.HSQLDBSaver;
|
||||||
import org.qora.transaction.Transaction.TransactionType;
|
import org.qora.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
|
import static org.qora.transaction.Transaction.TransactionType.*;
|
||||||
|
|
||||||
public class HSQLDBTransactionRepository implements TransactionRepository {
|
public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(HSQLDBTransactionRepository.class);
|
private static final Logger LOGGER = LogManager.getLogger(HSQLDBTransactionRepository.class);
|
||||||
|
|
||||||
|
public static class RepositorySubclassInfo {
|
||||||
|
public Class<?> clazz;
|
||||||
|
public Constructor<?> constructor;
|
||||||
|
public Method fromBaseMethod;
|
||||||
|
public Method saveMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final RepositorySubclassInfo[] subclassInfos;
|
||||||
|
static {
|
||||||
|
subclassInfos = new RepositorySubclassInfo[TransactionType.values().length + 1];
|
||||||
|
|
||||||
|
for (TransactionType txType : TransactionType.values()) {
|
||||||
|
RepositorySubclassInfo subclassInfo = new RepositorySubclassInfo();
|
||||||
|
|
||||||
|
try {
|
||||||
|
subclassInfo.clazz = Class.forName(
|
||||||
|
String.join("", HSQLDBTransactionRepository.class.getPackage().getName(), ".", "HSQLDB", txType.className, "TransactionRepository"));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
LOGGER.debug(String.format("HSQLDBTransactionRepository subclass not found for transaction type \"%s\"", txType.name()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
subclassInfo.constructor = subclassInfo.clazz.getConstructor(HSQLDBRepository.class);
|
||||||
|
} catch (NoSuchMethodException | IllegalArgumentException e) {
|
||||||
|
LOGGER.debug(String.format("HSQLDBTransactionRepository subclass constructor not found for transaction type \"%s\"", txType.name()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
subclassInfo.fromBaseMethod = subclassInfo.clazz.getDeclaredMethod("fromBase", byte[].class, byte[].class, byte[].class, long.class,
|
||||||
|
BigDecimal.class);
|
||||||
|
} catch (IllegalArgumentException | SecurityException | NoSuchMethodException e) {
|
||||||
|
LOGGER.debug(String.format("HSQLDBTransactionRepository subclass's \"fromBase\" method not found for transaction type \"%s\"", txType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
subclassInfo.saveMethod = subclassInfo.clazz.getDeclaredMethod("save", TransactionData.class);
|
||||||
|
} catch (IllegalArgumentException | SecurityException | NoSuchMethodException e) {
|
||||||
|
LOGGER.debug(String.format("HSQLDBTransactionRepository subclass's \"save\" method not found for transaction type \"%s\"", txType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
subclassInfos[txType.value] = subclassInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.trace("Static init reflection completed");
|
||||||
|
}
|
||||||
|
|
||||||
private HSQLDBTransactionRepository[] repositoryByTxType;
|
private HSQLDBTransactionRepository[] repositoryByTxType;
|
||||||
|
|
||||||
protected HSQLDBRepository repository;
|
protected HSQLDBRepository repository;
|
||||||
|
|
||||||
public HSQLDBTransactionRepository(HSQLDBRepository repository) {
|
public HSQLDBTransactionRepository(HSQLDBRepository repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
|
|
||||||
this.repositoryByTxType = new HSQLDBTransactionRepository[256];
|
this.repositoryByTxType = new HSQLDBTransactionRepository[TransactionType.values().length + 1];
|
||||||
|
|
||||||
for (TransactionType txType : TransactionType.values()) {
|
for (TransactionType txType : TransactionType.values()) {
|
||||||
Class<?> repositoryClass = getClassByTxType(txType);
|
RepositorySubclassInfo subclassInfo = subclassInfos[txType.value];
|
||||||
if (repositoryClass == null)
|
|
||||||
|
if (subclassInfo == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (subclassInfo.constructor == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Constructor<?> constructor = repositoryClass.getConstructor(HSQLDBRepository.class);
|
this.repositoryByTxType[txType.value] = (HSQLDBTransactionRepository) subclassInfo.constructor.newInstance(repository);
|
||||||
HSQLDBTransactionRepository txRepository = (HSQLDBTransactionRepository) constructor.newInstance(repository);
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
|
||||||
this.repositoryByTxType[txType.value] = txRepository;
|
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Never called
|
||||||
protected HSQLDBTransactionRepository() {
|
protected HSQLDBTransactionRepository() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> getClassByTxType(TransactionType txType) {
|
|
||||||
try {
|
|
||||||
return Class.forName(
|
|
||||||
String.join("", HSQLDBTransactionRepository.class.getPackage().getName(), ".", "HSQLDB", txType.className, "TransactionRepository"));
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransactionData fromSignature(byte[] signature) throws DataException {
|
public TransactionData fromSignature(byte[] signature) throws DataException {
|
||||||
try (ResultSet resultSet = this.repository.checkedExecute("SELECT type, reference, creator, creation, fee FROM Transactions WHERE signature = ?",
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT type, reference, creator, creation, fee FROM Transactions WHERE signature = ?",
|
||||||
@ -132,9 +179,8 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
throw new DataException("Unsupported transaction type [" + type.name() + "] during fetch from HSQLDB repository");
|
throw new DataException("Unsupported transaction type [" + type.name() + "] during fetch from HSQLDB repository");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Method method = txRepository.getClass().getDeclaredMethod("fromBase", byte[].class, byte[].class, byte[].class, long.class, BigDecimal.class);
|
return (TransactionData) subclassInfos[type.value].fromBaseMethod.invoke(txRepository, signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
return (TransactionData) method.invoke(txRepository, signature, reference, creatorPublicKey, timestamp, fee);
|
} catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
|
||||||
throw new DataException("Unsupported transaction type [" + type.name() + "] during fetch from HSQLDB repository");
|
throw new DataException("Unsupported transaction type [" + type.name() + "] during fetch from HSQLDB repository");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -343,6 +389,92 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TransactionData> getAssetTransactions(int assetId, ConfirmationStatus confirmationStatus, Integer limit, Integer offset, Boolean reverse)
|
||||||
|
throws DataException {
|
||||||
|
TransactionType[] transactionTypes = new TransactionType[] {
|
||||||
|
ISSUE_ASSET, TRANSFER_ASSET, CREATE_ASSET_ORDER, CANCEL_ASSET_ORDER
|
||||||
|
};
|
||||||
|
List<String> typeValueStrings = Arrays.asList(transactionTypes).stream().map(type -> String.valueOf(type.value)).collect(Collectors.toList());
|
||||||
|
|
||||||
|
String sql = "SELECT Transactions.signature FROM Transactions";
|
||||||
|
|
||||||
|
// BlockTransactions if we want confirmed transactions
|
||||||
|
switch (confirmationStatus) {
|
||||||
|
case BOTH:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONFIRMED:
|
||||||
|
sql += " JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UNCONFIRMED:
|
||||||
|
sql += " LEFT OUTER JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TransactionType type : transactionTypes)
|
||||||
|
sql += " LEFT OUTER JOIN " + type.className + "Transactions USING (signature)";
|
||||||
|
|
||||||
|
// assetID isn't in Cancel Asset Order so we need to join to the order
|
||||||
|
sql += " LEFT OUTER JOIN AssetOrders ON AssetOrders.asset_order_id = CancelAssetOrderTransactions.asset_order_id";
|
||||||
|
|
||||||
|
sql += " WHERE Transactions.type IN (" + String.join(", ", typeValueStrings) + ")";
|
||||||
|
|
||||||
|
// BlockTransactions if we want confirmed transactions
|
||||||
|
switch (confirmationStatus) {
|
||||||
|
case BOTH:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONFIRMED:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UNCONFIRMED:
|
||||||
|
sql += " AND BlockTransactions.transaction_signature IS NULL";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += " AND (";
|
||||||
|
sql += "IssueAssetTransactions.asset_id = " + assetId;
|
||||||
|
sql += " OR ";
|
||||||
|
sql += "TransferAssetTransactions.asset_id = " + assetId;
|
||||||
|
sql += " OR ";
|
||||||
|
sql += "CreateAssetOrderTransactions.have_asset_id = " + assetId;
|
||||||
|
sql += " OR ";
|
||||||
|
sql += "CreateAssetOrderTransactions.want_asset_id = " + assetId;
|
||||||
|
sql += " OR ";
|
||||||
|
sql += "AssetOrders.have_asset_id = " + assetId;
|
||||||
|
sql += " OR ";
|
||||||
|
sql += "AssetOrders.want_asset_id = " + assetId;
|
||||||
|
sql += ") GROUP BY Transactions.signature, Transactions.creation ORDER BY Transactions.creation";
|
||||||
|
|
||||||
|
sql += (reverse == null || !reverse) ? " ASC" : " DESC";
|
||||||
|
sql += HSQLDBRepository.limitOffsetSql(limit, offset);
|
||||||
|
|
||||||
|
List<TransactionData> transactions = new ArrayList<TransactionData>();
|
||||||
|
|
||||||
|
try (ResultSet resultSet = this.repository.checkedExecute(sql)) {
|
||||||
|
if (resultSet == null)
|
||||||
|
return transactions;
|
||||||
|
|
||||||
|
do {
|
||||||
|
byte[] signature = resultSet.getBytes(1);
|
||||||
|
|
||||||
|
TransactionData transactionData = this.fromSignature(signature);
|
||||||
|
|
||||||
|
if (transactionData == null)
|
||||||
|
// Something inconsistent with the repository
|
||||||
|
throw new DataException("Unable to fetch asset-related transaction from repository?");
|
||||||
|
|
||||||
|
transactions.add(transactionData);
|
||||||
|
} while (resultSet.next());
|
||||||
|
|
||||||
|
return transactions;
|
||||||
|
} catch (SQLException | DataException e) {
|
||||||
|
throw new DataException("Unable to fetch asset-related transactions from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<TransactionData> getUnconfirmedTransactions(Integer limit, Integer offset, Boolean reverse) throws DataException {
|
public List<TransactionData> getUnconfirmedTransactions(Integer limit, Integer offset, Boolean reverse) throws DataException {
|
||||||
String sql = "SELECT signature FROM UnconfirmedTransactions ORDER BY creation";
|
String sql = "SELECT signature FROM UnconfirmedTransactions ORDER BY creation";
|
||||||
@ -417,9 +549,8 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
throw new DataException("Unsupported transaction type [" + type.name() + "] during save into HSQLDB repository");
|
throw new DataException("Unsupported transaction type [" + type.name() + "] during save into HSQLDB repository");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Method method = txRepository.getClass().getDeclaredMethod("save", TransactionData.class);
|
subclassInfos[type.value].saveMethod.invoke(txRepository, transactionData);
|
||||||
method.invoke(txRepository, transactionData);
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
|
||||||
throw new DataException("Unsupported transaction type [" + type.name() + "] during save into HSQLDB repository");
|
throw new DataException("Unsupported transaction type [" + type.name() + "] during save into HSQLDB repository");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import org.qora.data.transaction.TransactionData;
|
|||||||
import org.qora.repository.DataException;
|
import org.qora.repository.DataException;
|
||||||
import org.qora.repository.Repository;
|
import org.qora.repository.Repository;
|
||||||
import org.qora.transform.TransformationException;
|
import org.qora.transform.TransformationException;
|
||||||
import org.qora.transform.transaction.ATTransactionTransformer;
|
import org.qora.transform.transaction.AtTransactionTransformer;
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ public class ATTransaction extends Transaction {
|
|||||||
if (this.atTransactionData.getSignature() == null) {
|
if (this.atTransactionData.getSignature() == null) {
|
||||||
// Signature is SHA2-256 of serialized transaction data, duplicated to make standard signature size of 64 bytes.
|
// Signature is SHA2-256 of serialized transaction data, duplicated to make standard signature size of 64 bytes.
|
||||||
try {
|
try {
|
||||||
byte[] digest = Crypto.digest(ATTransactionTransformer.toBytes(transactionData));
|
byte[] digest = Crypto.digest(AtTransactionTransformer.toBytes(transactionData));
|
||||||
byte[] signature = Bytes.concat(digest, digest);
|
byte[] signature = Bytes.concat(digest, digest);
|
||||||
this.atTransactionData.setSignature(signature);
|
this.atTransactionData.setSignature(signature);
|
||||||
} catch (TransformationException e) {
|
} catch (TransformationException e) {
|
||||||
|
@ -35,6 +35,7 @@ public abstract class Transaction {
|
|||||||
|
|
||||||
// Transaction types
|
// Transaction types
|
||||||
public enum TransactionType {
|
public enum TransactionType {
|
||||||
|
// NOTE: must be contiguous or reflection fails
|
||||||
GENESIS(1),
|
GENESIS(1),
|
||||||
PAYMENT(2),
|
PAYMENT(2),
|
||||||
REGISTER_NAME(3),
|
REGISTER_NAME(3),
|
||||||
@ -71,6 +72,8 @@ public abstract class Transaction {
|
|||||||
public final int value;
|
public final int value;
|
||||||
public final String valueString;
|
public final String valueString;
|
||||||
public final String className;
|
public final String className;
|
||||||
|
public final Class<?> clazz;
|
||||||
|
public final Constructor<?> constructor;
|
||||||
|
|
||||||
private final static Map<Integer, TransactionType> map = stream(TransactionType.values()).collect(toMap(type -> type.value, type -> type));
|
private final static Map<Integer, TransactionType> map = stream(TransactionType.values()).collect(toMap(type -> type.value, type -> type));
|
||||||
|
|
||||||
@ -84,6 +87,25 @@ public abstract class Transaction {
|
|||||||
classNameParts[i] = classNameParts[i].substring(0, 1).toUpperCase().concat(classNameParts[i].substring(1));
|
classNameParts[i] = classNameParts[i].substring(0, 1).toUpperCase().concat(classNameParts[i].substring(1));
|
||||||
|
|
||||||
this.className = String.join("", classNameParts);
|
this.className = String.join("", classNameParts);
|
||||||
|
|
||||||
|
Class<?> clazz = null;
|
||||||
|
try {
|
||||||
|
clazz = Class.forName(String.join("", Transaction.class.getPackage().getName(), ".", this.className, "Transaction"));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
LOGGER.debug(String.format("Transaction subclass not found for transaction type \"%s\"", this.name()));
|
||||||
|
this.clazz = null;
|
||||||
|
this.constructor = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.clazz = clazz;
|
||||||
|
|
||||||
|
Constructor<?> constructor = null;
|
||||||
|
try {
|
||||||
|
constructor = this.clazz.getConstructor(Repository.class, TransactionData.class);
|
||||||
|
} catch (NoSuchMethodException | SecurityException e) {
|
||||||
|
LOGGER.debug(String.format("Transaction subclass constructor not found for transaction type \"%s\"", this.name()));
|
||||||
|
}
|
||||||
|
this.constructor = constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TransactionType valueOf(int value) {
|
public static TransactionType valueOf(int value) {
|
||||||
@ -91,14 +113,6 @@ public abstract class Transaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Class<?> getClassByTxType(TransactionType txType) {
|
|
||||||
try {
|
|
||||||
return Class.forName(String.join("", Transaction.class.getPackage().getName(), ".", txType.className, "Transaction"));
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validation results
|
// Validation results
|
||||||
public enum ValidationResult {
|
public enum ValidationResult {
|
||||||
OK(1),
|
OK(1),
|
||||||
@ -207,13 +221,13 @@ public abstract class Transaction {
|
|||||||
TransactionType type = transactionData.getType();
|
TransactionType type = transactionData.getType();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class<?> transactionClass = Transaction.getClassByTxType(type);
|
Constructor<?> constructor = type.constructor;
|
||||||
if (transactionClass == null)
|
|
||||||
|
if (constructor == null)
|
||||||
throw new IllegalStateException("Unsupported transaction type [" + type.value + "] during fetch from repository");
|
throw new IllegalStateException("Unsupported transaction type [" + type.value + "] during fetch from repository");
|
||||||
|
|
||||||
Constructor<?> constructor = transactionClass.getConstructor(Repository.class, TransactionData.class);
|
|
||||||
return (Transaction) constructor.newInstance(repository, transactionData);
|
return (Transaction) constructor.newInstance(repository, transactionData);
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
|
||||||
throw new IllegalStateException("Internal error with transaction type [" + type.value + "] during fetch from repository");
|
throw new IllegalStateException("Internal error with transaction type [" + type.value + "] during fetch from repository");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import org.qora.utils.Serialization;
|
|||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import com.google.common.primitives.Longs;
|
import com.google.common.primitives.Longs;
|
||||||
|
|
||||||
public class ATTransactionTransformer extends TransactionTransformer {
|
public class AtTransactionTransformer extends TransactionTransformer {
|
||||||
|
|
||||||
// Property lengths
|
// Property lengths
|
||||||
private static final int SENDER_LENGTH = ADDRESS_LENGTH;
|
private static final int SENDER_LENGTH = ADDRESS_LENGTH;
|
@ -35,6 +35,7 @@ public abstract class TransactionTransformer extends Transformer {
|
|||||||
protected static final int FEE_LENGTH = BIG_DECIMAL_LENGTH;
|
protected static final int FEE_LENGTH = BIG_DECIMAL_LENGTH;
|
||||||
protected static final int BASE_TYPELESS_LENGTH = TIMESTAMP_LENGTH + REFERENCE_LENGTH + FEE_LENGTH + SIGNATURE_LENGTH;
|
protected static final int BASE_TYPELESS_LENGTH = TIMESTAMP_LENGTH + REFERENCE_LENGTH + FEE_LENGTH + SIGNATURE_LENGTH;
|
||||||
|
|
||||||
|
/** Description of one component of raw transaction layout */
|
||||||
public enum TransformationType {
|
public enum TransformationType {
|
||||||
TIMESTAMP("milliseconds (long)", TIMESTAMP_LENGTH),
|
TIMESTAMP("milliseconds (long)", TIMESTAMP_LENGTH),
|
||||||
SIGNATURE("transaction signature", SIGNATURE_LENGTH),
|
SIGNATURE("transaction signature", SIGNATURE_LENGTH),
|
||||||
@ -57,6 +58,7 @@ public abstract class TransactionTransformer extends Transformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Description of one component of raw transaction layout for API use */
|
||||||
@XmlAccessorType(XmlAccessType.NONE)
|
@XmlAccessorType(XmlAccessType.NONE)
|
||||||
public static class Transformation {
|
public static class Transformation {
|
||||||
@XmlElement
|
@XmlElement
|
||||||
@ -71,17 +73,22 @@ public abstract class TransactionTransformer extends Transformer {
|
|||||||
this.transformation = format;
|
this.transformation = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
@XmlElement(name = "format")
|
@XmlElement(
|
||||||
|
name = "format"
|
||||||
|
)
|
||||||
public String getFormat() {
|
public String getFormat() {
|
||||||
return this.transformation.description;
|
return this.transformation.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
@XmlElement(name = "length")
|
@XmlElement(
|
||||||
|
name = "length"
|
||||||
|
)
|
||||||
public Integer getLength() {
|
public Integer getLength() {
|
||||||
return this.transformation.length;
|
return this.transformation.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Container for raw transaction layout */
|
||||||
public static class TransactionLayout {
|
public static class TransactionLayout {
|
||||||
private List<Transformation> layout = new ArrayList<>();
|
private List<Transformation> layout = new ArrayList<>();
|
||||||
|
|
||||||
@ -94,23 +101,87 @@ public abstract class TransactionTransformer extends Transformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> getClassByTxType(TransactionType txType) {
|
/** Container for cache of transformer subclass reflection info */
|
||||||
try {
|
public static class TransformerSubclassInfo {
|
||||||
return Class.forName(String.join("", TransactionTransformer.class.getPackage().getName(), ".", txType.className, "TransactionTransformer"));
|
public Class<?> clazz;
|
||||||
} catch (ClassNotFoundException e) {
|
public TransactionLayout transactionLayout;
|
||||||
return null;
|
public Method fromByteBufferMethod;
|
||||||
|
public Method getDataLengthMethod;
|
||||||
|
public Method toBytesMethod;
|
||||||
|
public Method toBytesForSigningImplMethod;
|
||||||
|
public Method toJSONMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cache of transformer subclass info, keyed by transaction type */
|
||||||
|
private static final TransformerSubclassInfo[] subclassInfos;
|
||||||
|
static {
|
||||||
|
subclassInfos = new TransformerSubclassInfo[TransactionType.values().length + 1];
|
||||||
|
|
||||||
|
for (TransactionType txType : TransactionType.values()) {
|
||||||
|
TransformerSubclassInfo subclassInfo = new TransformerSubclassInfo();
|
||||||
|
|
||||||
|
try {
|
||||||
|
subclassInfo.clazz = Class
|
||||||
|
.forName(String.join("", TransactionTransformer.class.getPackage().getName(), ".", txType.className, "TransactionTransformer"));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
LOGGER.debug(String.format("TransactionTransformer subclass not found for transaction type \"%s\"", txType.name()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field layoutField = subclassInfo.clazz.getDeclaredField("layout");
|
||||||
|
subclassInfo.transactionLayout = ((TransactionLayout) layoutField.get(null));
|
||||||
|
} catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException e) {
|
||||||
|
LOGGER.debug(String.format("TransactionTransformer subclass's \"layout\" field not found for transaction type \"%s\"", txType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
subclassInfo.fromByteBufferMethod = subclassInfo.clazz.getDeclaredMethod("fromByteBuffer", ByteBuffer.class);
|
||||||
|
} catch (IllegalArgumentException | SecurityException | NoSuchMethodException e) {
|
||||||
|
LOGGER.debug(String.format("TransactionTransformer subclass's \"fromByteBuffer\" method not found for transaction type \"%s\"", txType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
subclassInfo.getDataLengthMethod = subclassInfo.clazz.getDeclaredMethod("getDataLength", TransactionData.class);
|
||||||
|
} catch (IllegalArgumentException | SecurityException | NoSuchMethodException e) {
|
||||||
|
LOGGER.debug(String.format("TransactionTransformer subclass's \"getDataLength\" method not found for transaction type \"%s\"", txType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
subclassInfo.toBytesMethod = subclassInfo.clazz.getDeclaredMethod("toBytes", TransactionData.class);
|
||||||
|
} catch (IllegalArgumentException | SecurityException | NoSuchMethodException e) {
|
||||||
|
LOGGER.debug(String.format("TransactionTransformer subclass's \"toBytes\" method not found for transaction type \"%s\"", txType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Class<?> transformerClass = subclassInfo.clazz;
|
||||||
|
|
||||||
|
// Check method is actually declared in transformer, otherwise we have to call superclass version
|
||||||
|
if (Arrays.asList(transformerClass.getDeclaredMethods()).stream().noneMatch(method -> method.getName().equals("toBytesForSigningImpl")))
|
||||||
|
transformerClass = transformerClass.getSuperclass();
|
||||||
|
|
||||||
|
subclassInfo.toBytesForSigningImplMethod = transformerClass.getDeclaredMethod("toBytesForSigningImpl", TransactionData.class);
|
||||||
|
} catch (IllegalArgumentException | SecurityException | NoSuchMethodException e) {
|
||||||
|
LOGGER.debug(String.format("TransactionTransformer subclass's \"toBytesFromSigningImp\" method not found for transaction type \"%s\"",
|
||||||
|
txType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
subclassInfo.toJSONMethod = subclassInfo.clazz.getDeclaredMethod("toJSON", TransactionData.class);
|
||||||
|
} catch (IllegalArgumentException | SecurityException | NoSuchMethodException e) {
|
||||||
|
LOGGER.debug(String.format("TransactionTransformer subclass's \"toJSON\" method not found for transaction type \"%s\"", txType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
subclassInfos[txType.value] = subclassInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOGGER.trace("Static init reflection completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Transformation> getLayoutByTxType(TransactionType txType) {
|
public static List<Transformation> getLayoutByTxType(TransactionType txType) {
|
||||||
try {
|
try {
|
||||||
Class<?> transformerClass = TransactionTransformer.getClassByTxType(txType);
|
return subclassInfos[txType.value].transactionLayout.getLayout();
|
||||||
if (transformerClass == null)
|
} catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
|
||||||
return null;
|
|
||||||
|
|
||||||
Field layoutField = transformerClass.getDeclaredField("layout");
|
|
||||||
return ((TransactionLayout) layoutField.get(null)).getLayout();
|
|
||||||
} catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException e) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,16 +201,15 @@ public abstract class TransactionTransformer extends Transformer {
|
|||||||
if (type == null)
|
if (type == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try {
|
Method method = subclassInfos[type.value].fromByteBufferMethod;
|
||||||
Class<?> transformerClass = TransactionTransformer.getClassByTxType(type);
|
if (method == null)
|
||||||
if (transformerClass == null)
|
throw new TransformationException("Unsupported transaction type [" + type.value + "] during conversion from bytes");
|
||||||
throw new TransformationException("Unsupported transaction type [" + type.value + "] during conversion from bytes");
|
|
||||||
|
|
||||||
Method method = transformerClass.getDeclaredMethod("fromByteBuffer", ByteBuffer.class);
|
try {
|
||||||
return (TransactionData) method.invoke(null, byteBuffer);
|
return (TransactionData) method.invoke(null, byteBuffer);
|
||||||
} catch (BufferUnderflowException e) {
|
} catch (BufferUnderflowException e) {
|
||||||
throw new TransformationException("Byte data too short for transaction type [" + type.value + "]");
|
throw new TransformationException("Byte data too short for transaction type [" + type.value + "]");
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
throw new TransformationException("Internal error with transaction type [" + type.value + "] during conversion from bytes");
|
throw new TransformationException("Internal error with transaction type [" + type.value + "] during conversion from bytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,13 +218,9 @@ public abstract class TransactionTransformer extends Transformer {
|
|||||||
TransactionType type = transactionData.getType();
|
TransactionType type = transactionData.getType();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class<?> transformerClass = TransactionTransformer.getClassByTxType(type);
|
Method method = subclassInfos[type.value].getDataLengthMethod;
|
||||||
if (transformerClass == null)
|
|
||||||
throw new TransformationException("Unsupported transaction type [" + type.value + "] when requesting byte length");
|
|
||||||
|
|
||||||
Method method = transformerClass.getDeclaredMethod("getDataLength", TransactionData.class);
|
|
||||||
return (int) method.invoke(null, transactionData);
|
return (int) method.invoke(null, transactionData);
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
throw new TransformationException("Internal error with transaction type [" + type.value + "] when requesting byte length");
|
throw new TransformationException("Internal error with transaction type [" + type.value + "] when requesting byte length");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,13 +229,9 @@ public abstract class TransactionTransformer extends Transformer {
|
|||||||
TransactionType type = transactionData.getType();
|
TransactionType type = transactionData.getType();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class<?> transformerClass = TransactionTransformer.getClassByTxType(type);
|
Method method = subclassInfos[type.value].toBytesMethod;
|
||||||
if (transformerClass == null)
|
|
||||||
throw new TransformationException("Unsupported transaction type [" + type.value + "] during conversion to bytes");
|
|
||||||
|
|
||||||
Method method = transformerClass.getDeclaredMethod("toBytes", TransactionData.class);
|
|
||||||
return (byte[]) method.invoke(null, transactionData);
|
return (byte[]) method.invoke(null, transactionData);
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
throw new TransformationException("Internal error with transaction type [" + type.value + "] during conversion to bytes");
|
throw new TransformationException("Internal error with transaction type [" + type.value + "] during conversion to bytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,17 +249,9 @@ public abstract class TransactionTransformer extends Transformer {
|
|||||||
TransactionType type = transactionData.getType();
|
TransactionType type = transactionData.getType();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class<?> transformerClass = TransactionTransformer.getClassByTxType(type);
|
Method method = subclassInfos[type.value].toBytesForSigningImplMethod;
|
||||||
if (transformerClass == null)
|
|
||||||
throw new TransformationException("Unsupported transaction type [" + type.value + "] during conversion to bytes for signing");
|
|
||||||
|
|
||||||
// Check method is actually declared in transformer, otherwise we have to call superclass version
|
|
||||||
if (Arrays.asList(transformerClass.getDeclaredMethods()).stream().noneMatch(method -> method.getName().equals("toBytesForSigningImpl")))
|
|
||||||
transformerClass = transformerClass.getSuperclass();
|
|
||||||
|
|
||||||
Method method = transformerClass.getDeclaredMethod("toBytesForSigningImpl", TransactionData.class);
|
|
||||||
return (byte[]) method.invoke(null, transactionData);
|
return (byte[]) method.invoke(null, transactionData);
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
throw new TransformationException("Internal error with transaction type [" + type.value + "] during conversion to bytes for signing");
|
throw new TransformationException("Internal error with transaction type [" + type.value + "] during conversion to bytes for signing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,13 +280,9 @@ public abstract class TransactionTransformer extends Transformer {
|
|||||||
TransactionType type = transactionData.getType();
|
TransactionType type = transactionData.getType();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class<?> transformerClass = TransactionTransformer.getClassByTxType(type);
|
Method method = subclassInfos[type.value].toJSONMethod;
|
||||||
if (transformerClass == null)
|
|
||||||
throw new TransformationException("Unsupported transaction type [" + type.value + "] during conversion to JSON");
|
|
||||||
|
|
||||||
Method method = transformerClass.getDeclaredMethod("toJSON", TransactionData.class);
|
|
||||||
return (JSONObject) method.invoke(null, transactionData);
|
return (JSONObject) method.invoke(null, transactionData);
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
throw new TransformationException("Internal error with transaction type [" + type.value + "] during conversion to JSON");
|
throw new TransformationException("Internal error with transaction type [" + type.value + "] during conversion to JSON");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ import org.qora.repository.RepositoryManager;
|
|||||||
import org.qora.repository.hsqldb.HSQLDBRepositoryFactory;
|
import org.qora.repository.hsqldb.HSQLDBRepositoryFactory;
|
||||||
import org.qora.transform.TransformationException;
|
import org.qora.transform.TransformationException;
|
||||||
import org.qora.transform.block.BlockTransformer;
|
import org.qora.transform.block.BlockTransformer;
|
||||||
import org.qora.transform.transaction.ATTransactionTransformer;
|
import org.qora.transform.transaction.AtTransactionTransformer;
|
||||||
import org.qora.utils.Base58;
|
import org.qora.utils.Base58;
|
||||||
import org.qora.utils.Pair;
|
import org.qora.utils.Pair;
|
||||||
import org.qora.utils.Triple;
|
import org.qora.utils.Triple;
|
||||||
@ -498,7 +498,7 @@ public class v1feeder extends Thread {
|
|||||||
TransactionData transactionData = new ATTransactionData(sender, recipient, amount, Asset.QORA, message, fee, timestamp, reference);
|
TransactionData transactionData = new ATTransactionData(sender, recipient, amount, Asset.QORA, message, fee, timestamp, reference);
|
||||||
byte[] digest;
|
byte[] digest;
|
||||||
try {
|
try {
|
||||||
digest = Crypto.digest(ATTransactionTransformer.toBytes(transactionData));
|
digest = Crypto.digest(AtTransactionTransformer.toBytes(transactionData));
|
||||||
byte[] signature = Bytes.concat(digest, digest);
|
byte[] signature = Bytes.concat(digest, digest);
|
||||||
|
|
||||||
transactionData = new ATTransactionData(sender, recipient, amount, Asset.QORA, message, fee, timestamp, reference, signature);
|
transactionData = new ATTransactionData(sender, recipient, amount, Asset.QORA, message, fee, timestamp, reference, signature);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user