Interim safety commit due to large number of changes!

log4j2.properties now has debugging entries removed.
log4j2-test.properties (not in repo) takes priority
 so using that in development instead.

Unconfirmed transactions no longer wiped on start-up
 by default - see Settings

Reworking of {Public,Private,Genesis}Accounts as it seemed
 possible to silently lose public key in repository.
The use of AccountData didn't work and so field-specific
 repository calls have been made instead
 (e.g. setLastReference) that try to opportunistically
 store public key too, if available (i.e. caller is
 PublicKeyAccount subclass, or better).

Added API call GET /addresses/{address} to return
 general account info in one go. (Essentially the
 AccountData object as fetched from repository).

Initial work on adding default groupID to accounts,
 along with corresponding SET_GROUP transaction type.
In additional, added blockchain-wide default groupID
 and flag to allow/disallow no-group/groupless
 transactions.

Initial work on group-admin approval of transactions
 tied to a specific group via txGroupId.

More work needed on transaction's "effective txGroupId"!

API call /transactions/pending to list transactions
 pending group-admin approval. However, this needs more
 work (see effective txGroupId above) and potentially
 offloading to HSQLDB repository if possible.

Minor CIYAM AT renames to help static reflection initializers.

Block.orphan() no longer adds orphaned transactions back to
 unconfirmed pile as they are themselves deleted during
 Transaction.orphan(). Maybe the answer is to NOT delete
 them during Transaction.orphan() but to add them to
 unconfirmed pile at that point? Very old transactions
 leftover from major resync would simply expire, whereas
 recently transactions leftover from minor resync could
 still make it into a new block on synced chain fork.

Changes/tidying/improvements to block generator regarding
 removing invalid transactions and dealing with transactions
 pending group approval.

Approval threshold added to groups.

Mass refactoring of transaction-related classes to unify
 constructors, particularly field ordering, to fall in line
 with raw transaction layout.
e.g. constructors now reflect that raw transactions mostly
 start with type, timestamp, txGroupId, publicKey, reference
e.g. JAXB afterUnmarshal methods added where needed and corresponding
 nasty code in Transaction subclass constructors ripped out.
e.g. TransactionTransformer subclasses contain less duplicated code.

Fixed bug with repository save points thanks to swapping to Deque.

Some fixes to do with missing transaction types being passed to JAXB
 TransactionData subclass constructors.

Ripped out obsolete toJSON in TransactionTransformers as this
 is all nicely taken care of by Swagger/OpenAPI (thanks @Kc)
This commit is contained in:
catbref 2019-02-18 19:00:37 +00:00
parent 82e9e1e7dc
commit 00656f6724
143 changed files with 2591 additions and 1848 deletions

View File

@ -18,23 +18,18 @@ logger.hsqldbDebug.level = debug
logger.jerseyInject.name = org.glassfish.jersey.internal.inject.Providers
logger.jerseyInject.level = error
# Debugging transaction searches
logger.txSearch.name = org.qora.repository.hsqldb.transaction.HSQLDBTransactionRepository
logger.txSearch.level = trace
# Suppress extraneous Jetty entries
# 2019-02-14 11:46:27 INFO ContextHandler:851 - Started o.e.j.s.ServletContextHandler@6949e948{/,null,AVAILABLE}
# 2019-02-14 11:46:27 INFO AbstractConnector:289 - Started ServerConnector@50ad322b{HTTP/1.1,[http/1.1]}{0.0.0.0:9085}
# 2019-02-14 11:46:27 INFO Server:374 - jetty-9.4.11.v20180605; built: 2018-06-05T18:24:03.829Z; git: d5fc0523cfa96bfebfbda19606cad384d772f04c; jvm 1.8.0_181-b13
# 2019-02-14 11:46:27 INFO Server:411 - Started @2539ms
logger.oejsSCH.name = org.eclipse.jetty
logger.oejsSCH.level = warn
# Debug block generator
logger.blockgen.name = org.qora.block.BlockGenerator
logger.blockgen.level = trace
# Debug synchronization
logger.sync.name = org.qora.controller.Synchronizer
logger.sync.level = trace
# Debug networking
logger.network.name = org.qora.network.Network
logger.network.level = trace
logger.peer.name = org.qora.network.Peer
logger.peer.level = trace
# Suppress extraneous slf4j entries
# 2019-02-14 11:46:27 INFO log:193 - Logging initialized @1636ms to org.eclipse.jetty.util.log.Slf4jLog
logger.slf4j.name = org.slf4j
logger.slf4j.level = warn
appender.console.type = Console
appender.console.name = stdout

View File

@ -24,7 +24,7 @@ public class Account {
public static final int ADDRESS_LENGTH = 25;
protected Repository repository;
protected AccountData accountData;
protected String address;
protected Account() {
}
@ -32,11 +32,24 @@ public class Account {
/** Construct Account business object using account's address */
public Account(Repository repository, String address) {
this.repository = repository;
this.accountData = new AccountData(address);
this.address = address;
}
// Simple getters / setters
public String getAddress() {
return this.accountData.getAddress();
return this.address;
}
/**
* Build AccountData object using available account information.
* <p>
* For example, PublicKeyAccount might override and add public key info.
*
* @return
*/
protected AccountData buildAccountData() {
return new AccountData(this.address);
}
// More information
@ -109,7 +122,7 @@ public class Account {
}
public BigDecimal getConfirmedBalance(long assetId) throws DataException {
AccountBalanceData accountBalanceData = this.repository.getAccountRepository().getBalance(this.accountData.getAddress(), assetId);
AccountBalanceData accountBalanceData = this.repository.getAccountRepository().getBalance(this.address, assetId);
if (accountBalanceData == null)
return BigDecimal.ZERO.setScale(8);
@ -118,16 +131,16 @@ public class Account {
public void setConfirmedBalance(long assetId, BigDecimal balance) throws DataException {
// Can't have a balance without an account - make sure it exists!
this.repository.getAccountRepository().create(this.accountData);
this.repository.getAccountRepository().ensureAccount(this.buildAccountData());
AccountBalanceData accountBalanceData = new AccountBalanceData(this.accountData.getAddress(), assetId, balance);
AccountBalanceData accountBalanceData = new AccountBalanceData(this.address, assetId, balance);
this.repository.getAccountRepository().save(accountBalanceData);
LOGGER.trace(this.accountData.getAddress() + " balance now: " + balance.toPlainString() + " [assetId " + assetId + "]");
LOGGER.trace(this.address + " balance now: " + balance.toPlainString() + " [assetId " + assetId + "]");
}
public void deleteBalance(long assetId) throws DataException {
this.repository.getAccountRepository().delete(this.accountData.getAddress(), assetId);
this.repository.getAccountRepository().delete(this.address, assetId);
}
// Reference manipulations
@ -139,17 +152,13 @@ public class Account {
* @throws DataException
*/
public byte[] getLastReference() throws DataException {
AccountData accountData = this.repository.getAccountRepository().getAccount(this.accountData.getAddress());
if (accountData == null)
return null;
return accountData.getReference();
return this.repository.getAccountRepository().getLastReference(this.address);
}
/**
* Fetch last reference for account, considering unconfirmed transactions.
* <p>
* NOTE: <tt>repository.discardChanges()</tt> may be called during execution.
* NOTE: a repository savepoint may be used during execution.
*
* @return byte[] reference, or null if no reference or account not found.
* @throws DataException
@ -163,7 +172,7 @@ public class Account {
for (TransactionData transactionData : unconfirmedTransactions) {
String address = PublicKeyAccount.getAddress(transactionData.getCreatorPublicKey());
if (address.equals(this.accountData.getAddress()))
if (address.equals(this.address))
reference = transactionData.getSignature();
}
@ -182,9 +191,30 @@ public class Account {
* @throws DataException
*/
public void setLastReference(byte[] reference) throws DataException {
AccountData accountData = this.buildAccountData();
accountData.setReference(reference);
this.repository.getAccountRepository().setLastReference(accountData);
}
this.repository.getAccountRepository().save(accountData);
// Default groupID manipulations
/** Returns account's default groupID or null if account doesn't exist. */
public Integer getDefaultGroupId() throws DataException {
return this.repository.getAccountRepository().getDefaultGroupId(this.address);
}
/**
* Sets account's default groupID and saves into repository.
* <p>
* Caller will need to call <tt>repository.saveChanges()</tt>.
*
* @param defaultGroupId
* @throws DataException
*/
public void setDefaultGroupId(int defaultGroupId) throws DataException {
AccountData accountData = this.buildAccountData();
accountData.setDefaultGroupId(defaultGroupId);
this.repository.getAccountRepository().setDefaultGroupId(accountData);
}
}

View File

@ -2,7 +2,6 @@ package org.qora.account;
import org.qora.crypto.Crypto;
import org.qora.crypto.Ed25519;
import org.qora.data.account.AccountData;
import org.qora.repository.Repository;
import org.qora.utils.Pair;
@ -16,15 +15,16 @@ public class PrivateKeyAccount extends PublicKeyAccount {
*
* @param seed
* byte[32] used to create private/public key pair
* @throws IllegalArgumentException if passed invalid seed
* @throws IllegalArgumentException
* if passed invalid seed
*/
public PrivateKeyAccount(Repository repository, byte[] seed) {
this.repository = repository;
this.seed = seed;
this.keyPair = Ed25519.createKeyPair(seed);
byte[] publicKey = keyPair.getB();
this.accountData = new AccountData(Crypto.toAddress(publicKey), null, publicKey);
this.publicKey = keyPair.getB();
this.address = Crypto.toAddress(publicKey);
}
public byte[] getSeed() {

View File

@ -2,25 +2,35 @@ package org.qora.account;
import org.qora.crypto.Crypto;
import org.qora.crypto.Ed25519;
import org.qora.data.account.AccountData;
import org.qora.repository.Repository;
public class PublicKeyAccount extends Account {
protected byte[] publicKey;
public PublicKeyAccount(Repository repository, byte[] publicKey) {
super(repository, Crypto.toAddress(publicKey));
this.accountData.setPublicKey(publicKey);
this.publicKey = publicKey;
}
protected PublicKeyAccount() {
}
public byte[] getPublicKey() {
return this.accountData.getPublicKey();
return this.publicKey;
}
@Override
protected AccountData buildAccountData() {
AccountData accountData = super.buildAccountData();
accountData.setPublicKey(this.publicKey);
return accountData;
}
public boolean verify(byte[] signature, byte[] message) {
return PublicKeyAccount.verify(this.accountData.getPublicKey(), signature, message);
return PublicKeyAccount.verify(this.publicKey, signature, message);
}
public static boolean verify(byte[] publicKey, byte[] signature, byte[] message) {

View File

@ -38,6 +38,31 @@ public class AddressesResource {
@Context
HttpServletRequest request;
@GET
@Path("/{address}")
@Operation(
summary = "Return general account information for the given address",
responses = {
@ApiResponse(
description = "general account information",
content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = AccountData.class))
)
}
)
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
public AccountData getAccountInfo(@PathParam("address") String address) {
if (!Crypto.isValidAddress(address))
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
try (final Repository repository = RepositoryManager.getRepository()) {
return repository.getAccountRepository().getAccount(address);
} catch (ApiException e) {
throw e;
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
@GET
@Path("/lastreference/{address}")
@Operation(

View File

@ -38,6 +38,7 @@ import org.qora.data.group.GroupMemberData;
import org.qora.data.transaction.AddGroupAdminTransactionData;
import org.qora.data.transaction.CancelGroupInviteTransactionData;
import org.qora.data.transaction.CreateGroupTransactionData;
import org.qora.data.transaction.GroupApprovalTransactionData;
import org.qora.data.transaction.GroupBanTransactionData;
import org.qora.data.transaction.GroupInviteTransactionData;
import org.qora.data.transaction.GroupKickTransactionData;
@ -45,6 +46,7 @@ import org.qora.data.transaction.CancelGroupBanTransactionData;
import org.qora.data.transaction.JoinGroupTransactionData;
import org.qora.data.transaction.LeaveGroupTransactionData;
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
import org.qora.data.transaction.SetGroupTransactionData;
import org.qora.data.transaction.UpdateGroupTransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
@ -55,6 +57,7 @@ import org.qora.transform.TransformationException;
import org.qora.transform.transaction.AddGroupAdminTransactionTransformer;
import org.qora.transform.transaction.CancelGroupInviteTransactionTransformer;
import org.qora.transform.transaction.CreateGroupTransactionTransformer;
import org.qora.transform.transaction.GroupApprovalTransactionTransformer;
import org.qora.transform.transaction.GroupBanTransactionTransformer;
import org.qora.transform.transaction.GroupInviteTransactionTransformer;
import org.qora.transform.transaction.GroupKickTransactionTransformer;
@ -62,6 +65,7 @@ import org.qora.transform.transaction.CancelGroupBanTransactionTransformer;
import org.qora.transform.transaction.JoinGroupTransactionTransformer;
import org.qora.transform.transaction.LeaveGroupTransactionTransformer;
import org.qora.transform.transaction.RemoveGroupAdminTransactionTransformer;
import org.qora.transform.transaction.SetGroupTransactionTransformer;
import org.qora.transform.transaction.UpdateGroupTransactionTransformer;
import org.qora.utils.Base58;
@ -800,4 +804,90 @@ public class GroupsResource {
}
}
@POST
@Path("/approval")
@Operation(
summary = "Build raw, unsigned, GROUP_APPROVAL transaction",
requestBody = @RequestBody(
required = true,
content = @Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(
implementation = GroupApprovalTransactionData.class
)
)
),
responses = {
@ApiResponse(
description = "raw, unsigned, GROUP_APPROVAL transaction encoded in Base58",
content = @Content(
mediaType = MediaType.TEXT_PLAIN,
schema = @Schema(
type = "string"
)
)
)
}
)
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
public String groupApproval(GroupApprovalTransactionData transactionData) {
try (final Repository repository = RepositoryManager.getRepository()) {
Transaction transaction = Transaction.fromData(repository, transactionData);
ValidationResult result = transaction.isValidUnconfirmed();
if (result != ValidationResult.OK)
throw TransactionsResource.createTransactionInvalidException(request, result);
byte[] bytes = GroupApprovalTransactionTransformer.toBytes(transactionData);
return Base58.encode(bytes);
} catch (TransformationException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
@POST
@Path("/setdefault")
@Operation(
summary = "Build raw, unsigned, SET_GROUP transaction",
requestBody = @RequestBody(
required = true,
content = @Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(
implementation = SetGroupTransactionData.class
)
)
),
responses = {
@ApiResponse(
description = "raw, unsigned, SET_GROUP transaction encoded in Base58",
content = @Content(
mediaType = MediaType.TEXT_PLAIN,
schema = @Schema(
type = "string"
)
)
)
}
)
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
public String setGroup(SetGroupTransactionData transactionData) {
try (final Repository repository = RepositoryManager.getRepository()) {
Transaction transaction = Transaction.fromData(repository, transactionData);
ValidationResult result = transaction.isValidUnconfirmed();
if (result != ValidationResult.OK)
throw TransactionsResource.createTransactionInvalidException(request, result);
byte[] bytes = SetGroupTransactionTransformer.toBytes(transactionData);
return Base58.encode(bytes);
} catch (TransformationException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
}

View File

@ -225,6 +225,58 @@ public class TransactionsResource {
}
}
@GET
@Path("/pending")
@Operation(
summary = "List transactions pending group approval",
description = "Returns transactions that are pending group-admin approval",
responses = {
@ApiResponse(
description = "transactions",
content = @Content(
array = @ArraySchema(
schema = @Schema(
implementation = TransactionData.class
)
)
)
)
}
)
@ApiErrors({
ApiError.REPOSITORY_ISSUE
})
public List<TransactionData> getPendingTransactions(@QueryParam("groupId") Integer groupId, @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()) {
List<TransactionData> transactions = repository.getTransactionRepository().getUnconfirmedTransactions(null, null, reverse);
transactions.removeIf(transactionData -> {
if (groupId != null && groupId != transactionData.getTxGroupId())
return true;
try {
return !Transaction.fromData(repository, transactionData).needsGroupApproval();
} catch (DataException e) {
return true;
}
});
// Results slicing
return transactions;
} catch (ApiException e) {
throw e;
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
public enum ConfirmationStatus {
CONFIRMED,
UNCONFIRMED,

View File

@ -13,7 +13,7 @@ import org.qora.data.transaction.DeployAtTransactionData;
import org.qora.repository.ATRepository;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.transaction.ATTransaction;
import org.qora.transaction.AtTransaction;
public class AT {
@ -38,7 +38,7 @@ public class AT {
public AT(Repository repository, DeployAtTransactionData deployATTransactionData) throws DataException {
this.repository = repository;
String atAddress = deployATTransactionData.getATAddress();
String atAddress = deployATTransactionData.getAtAddress();
int height = this.repository.getBlockRepository().getBlockchainHeight() + 1;
byte[] creatorPublicKey = deployATTransactionData.getCreatorPublicKey();
long creation = deployATTransactionData.getTimestamp();
@ -126,7 +126,7 @@ public class AT {
this.repository.getATRepository().delete(this.atData.getATAddress());
}
public List<ATTransaction> run(long blockTimestamp) throws DataException {
public List<AtTransaction> run(long blockTimestamp) throws DataException {
String atAddress = this.atData.getATAddress();
QoraATAPI api = new QoraATAPI(repository, this.atData, blockTimestamp);

View File

@ -28,10 +28,11 @@ public enum BlockchainAPI {
int sequence = timestamp.transactionSequence + 1;
QoraATAPI api = (QoraATAPI) state.getAPI();
Account recipientAccount = new Account(api.repository, recipient);
BlockRepository blockRepository = api.repository.getBlockRepository();
try {
Account recipientAccount = new Account(api.repository, recipient);
while (height <= blockRepository.getBlockchainHeight()) {
BlockData blockData = blockRepository.fromHeight(height);

View File

@ -22,9 +22,10 @@ import org.qora.data.block.BlockData;
import org.qora.data.transaction.ATTransactionData;
import org.qora.data.transaction.MessageTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.group.Group;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.transaction.ATTransaction;
import org.qora.transaction.AtTransaction;
import com.google.common.primitives.Bytes;
@ -42,20 +43,20 @@ public class QoraATAPI extends API {
long blockTimestamp;
/** List of generated AT transactions */
List<ATTransaction> transactions;
List<AtTransaction> transactions;
// Constructors
public QoraATAPI(Repository repository, ATData atData, long blockTimestamp) {
this.repository = repository;
this.atData = atData;
this.transactions = new ArrayList<ATTransaction>();
this.transactions = new ArrayList<AtTransaction>();
this.blockTimestamp = blockTimestamp;
}
// Methods specific to Qora AT processing, not inherited
public List<ATTransaction> getTransactions() {
public List<AtTransaction> getTransactions() {
return this.transactions;
}
@ -248,9 +249,9 @@ public class QoraATAPI extends API {
@Override
public long getCurrentBalance(MachineState state) {
Account atAccount = this.getATAccount();
try {
Account atAccount = this.getATAccount();
return atAccount.getConfirmedBalance(Asset.QORA).unscaledValue().longValue();
} catch (DataException e) {
throw new RuntimeException("AT API unable to fetch AT's current balance?", e);
@ -267,9 +268,9 @@ public class QoraATAPI extends API {
byte[] reference = this.getLastReference();
BigDecimal amount = BigDecimal.valueOf(unscaledAmount, 8);
ATTransactionData atTransactionData = new ATTransactionData(this.atData.getATAddress(), recipient.getAddress(), amount, this.atData.getAssetId(),
new byte[0], BigDecimal.ZERO.setScale(8), timestamp, reference);
ATTransaction atTransaction = new ATTransaction(this.repository, atTransactionData);
ATTransactionData atTransactionData = new ATTransactionData(timestamp, Group.DEFAULT_GROUP, reference, this.atData.getATAddress(),
recipient.getAddress(), amount, this.atData.getAssetId(), new byte[0], BigDecimal.ZERO.setScale(8));
AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData);
// Add to our transactions
this.transactions.add(atTransaction);
@ -285,9 +286,9 @@ public class QoraATAPI extends API {
long timestamp = this.getNextTransactionTimestamp();
byte[] reference = this.getLastReference();
ATTransactionData atTransactionData = new ATTransactionData(this.atData.getATAddress(), recipient.getAddress(), BigDecimal.ZERO,
this.atData.getAssetId(), message, BigDecimal.ZERO.setScale(8), timestamp, reference);
ATTransaction atTransaction = new ATTransaction(this.repository, atTransactionData);
ATTransactionData atTransactionData = new ATTransactionData(timestamp, Group.DEFAULT_GROUP, reference,
this.atData.getATAddress(), recipient.getAddress(), BigDecimal.ZERO, this.atData.getAssetId(), message, BigDecimal.ZERO.setScale(8));
AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData);
// Add to our transactions
this.transactions.add(atTransaction);
@ -311,9 +312,9 @@ public class QoraATAPI extends API {
byte[] reference = this.getLastReference();
BigDecimal amount = BigDecimal.valueOf(finalBalance, 8);
ATTransactionData atTransactionData = new ATTransactionData(this.atData.getATAddress(), creator.getAddress(), amount, this.atData.getAssetId(),
new byte[0], BigDecimal.ZERO.setScale(8), timestamp, reference);
ATTransaction atTransaction = new ATTransaction(this.repository, atTransactionData);
ATTransactionData atTransactionData = new ATTransactionData(timestamp, Group.DEFAULT_GROUP, reference, this.atData.getATAddress(),
creator.getAddress(), amount, this.atData.getAssetId(), new byte[0], BigDecimal.ZERO.setScale(8));
AtTransaction atTransaction = new AtTransaction(this.repository, atTransactionData);
// Add to our transactions
this.transactions.add(atTransaction);
@ -422,10 +423,10 @@ public class QoraATAPI extends API {
if (!this.transactions.isEmpty())
return this.transactions.get(this.transactions.size() - 1).getTransactionData().getSignature();
// No transactions yet, so look up AT's account's last reference from repository
Account atAccount = this.getATAccount();
try {
// No transactions yet, so look up AT's account's last reference from repository
Account atAccount = this.getATAccount();
return atAccount.getLastReference();
} catch (DataException e) {
throw new RuntimeException("AT API unable to fetch AT's last reference from repository?", e);

View File

@ -28,7 +28,7 @@ import org.qora.repository.ATRepository;
import org.qora.repository.BlockRepository;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.transaction.ATTransaction;
import org.qora.transaction.AtTransaction;
import org.qora.transaction.GenesisTransaction;
import org.qora.transaction.Transaction;
import org.qora.transform.TransformationException;
@ -706,7 +706,7 @@ public class Block {
* Performs various tests like checking for parent block, correct block timestamp, version, generating balance, etc.
* <p>
* Checks block's transactions by testing their validity then processing them.<br>
* Hence <b>calls repository.discardChanges()</b> before returning.
* Hence uses a repository savepoint during execution.
*
* @return ValidationResult.OK if block is valid, or some other ValidationResult otherwise.
* @throws DataException
@ -895,7 +895,7 @@ public class Block {
throw new IllegalStateException("Attempted to execute ATs when block's local AT state data already exists");
// AT-Transactions generated by running ATs, to be prepended to block's transactions
List<ATTransaction> allATTransactions = new ArrayList<ATTransaction>();
List<AtTransaction> allATTransactions = new ArrayList<AtTransaction>();
this.ourAtStates = new ArrayList<ATStateData>();
this.ourAtFees = BigDecimal.ZERO.setScale(8);
@ -906,7 +906,7 @@ public class Block {
// Run each AT, appends AT-Transactions and corresponding AT states, to our lists
for (ATData atData : executableATs) {
AT at = new AT(this.repository, atData);
List<ATTransaction> atTransactions = at.run(this.blockData.getTimestamp());
List<AtTransaction> atTransactions = at.run(this.blockData.getTimestamp());
allATTransactions.addAll(atTransactions);
@ -1005,7 +1005,8 @@ public class Block {
this.repository.getBlockRepository().delete(blockTransactionData);
// Add to unconfirmed pile
this.repository.getTransactionRepository().unconfirmTransaction(transaction.getTransactionData());
// XXX WE CAN'T ADD TO UNCONFIRMED AS TRANSACTION HAS BEEN DELETED BY transaction.orphan() ABOVE
// this.repository.getTransactionRepository().unconfirmTransaction(transaction.getTransactionData());
this.repository.getTransactionRepository().deleteParticipants(transaction.getTransactionData());
}

View File

@ -13,6 +13,7 @@ import org.apache.logging.log4j.Logger;
import org.json.simple.JSONObject;
import org.qora.data.asset.AssetData;
import org.qora.data.block.BlockData;
import org.qora.group.Group;
import org.qora.repository.BlockRepository;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
@ -48,6 +49,10 @@ public class BlockChain {
private long maxBlockTime;
/** Maximum acceptable timestamp disagreement offset in milliseconds. */
private long blockTimestampMargin;
/** Whether transactions with txGroupId of NO_GROUP are allowed */
private boolean grouplessAllowed;
/** Default groupID when txGroupID and account's default groupID are both zero */
private int defaultGroupId = Group.NO_GROUP;
/** Map of which blockchain features are enabled when (height/timestamp) */
private Map<String, Map<FeatureValueType, Long>> featureTriggers;
@ -101,6 +106,14 @@ public class BlockChain {
return this.blockTimestampMargin;
}
public boolean getGrouplessAllowed() {
return this.grouplessAllowed;
}
public int getDefaultGroupId() {
return this.defaultGroupId;
}
public static boolean getUseBrokenMD160ForAddresses() {
return useBrokenMD160ForAddresses;
}
@ -166,6 +179,22 @@ public class BlockChain {
GenesisBlock.fromJSON((JSONObject) genesisJson);
// Simple blockchain properties
boolean grouplessAllowed = true;
if (json.containsKey("grouplessAllowed"))
grouplessAllowed = (Boolean) Settings.getTypedJson(json, "grouplessAllowed", Boolean.class);
Integer defaultGroupId = null;
if (json.containsKey("defaultGroupId"))
defaultGroupId = ((Long) Settings.getTypedJson(json, "defaultGroupId", Long.class)).intValue();
// If groupless is not allowed the defaultGroupId needs to be set
// XXX we could also check groupID exists, or at least created in genesis block, or in blockchain config
if (!grouplessAllowed && (defaultGroupId == null || defaultGroupId == Group.DEFAULT_GROUP || defaultGroupId == Group.NO_GROUP)) {
LOGGER.error("defaultGroupId must be set to valid groupID in blockchain config if groupless transactions are not allowed");
throw new RuntimeException("defaultGroupId must be set to valid groupID in blockchain config if groupless transactions are not allowed");
}
BigDecimal unitFee = Settings.getJsonBigDecimal(json, "unitFee");
long maxBytesPerUnitFee = (Long) Settings.getTypedJson(json, "maxBytesPerUnitFee", Long.class);
BigDecimal maxBalance = Settings.getJsonBigDecimal(json, "coinSupply");
@ -208,6 +237,9 @@ public class BlockChain {
instance.minBlockTime = minBlockTime;
instance.maxBlockTime = maxBlockTime;
instance.blockTimestampMargin = blockTimestampMargin;
instance.grouplessAllowed = grouplessAllowed;
if (defaultGroupId != null)
instance.defaultGroupId = defaultGroupId;
instance.featureTriggers = featureTriggers;
}

View File

@ -85,6 +85,9 @@ public class BlockGenerator extends Thread {
try {
// Is new block valid yet? (Before adding unconfirmed transactions)
if (newBlock.isValid() == ValidationResult.OK) {
// Delete invalid transactions
deleteInvalidTransactions(repository);
// Add unconfirmed transactions
addUnconfirmedTransactions(repository, newBlock);
@ -131,6 +134,17 @@ public class BlockGenerator extends Thread {
}
}
private void deleteInvalidTransactions(Repository repository) throws DataException {
List<TransactionData> invalidTransactions = Transaction.getInvalidTransactions(repository);
// Actually delete invalid transactions from database
for (TransactionData invalidTransactionData : invalidTransactions) {
LOGGER.trace(String.format("Deleting invalid, unconfirmed transaction %s", Base58.encode(invalidTransactionData.getSignature())));
repository.getTransactionRepository().delete(invalidTransactionData);
}
repository.saveChanges();
}
private void addUnconfirmedTransactions(Repository repository, Block newBlock) throws DataException {
// Grab all valid unconfirmed transactions (already sorted)
List<TransactionData> unconfirmedTransactions = Transaction.getUnconfirmedTransactions(repository);
@ -153,10 +167,17 @@ public class BlockGenerator extends Thread {
--i;
continue;
}
// Ignore transactions that have not met group-admin approval threshold
if (transaction.needsGroupApproval()) {
unconfirmedTransactions.remove(i);
--i;
continue;
}
}
// Discard last-reference changes used to aid transaction validity checks
repository.discardChanges();
// Discard any repository changes used to aid transaction validity checks
// repository.discardChanges(); // XXX possibly not needed any more thanks to savepoints?
// Attempt to add transactions until block is full, or we run out
// If a transaction makes the block invalid then skip it and it'll either expire or be in next block.

View File

@ -98,9 +98,9 @@ public class GenesisBlock extends Block {
// We're into version 4 genesis block territory now
version = 4;
transactions.add(new GenesisTransactionData(recipient, amount, assetId, timestamp));
transactions.add(new GenesisTransactionData(timestamp, recipient, amount, assetId));
} else {
transactions.add(new GenesisTransactionData(recipient, amount, timestamp));
transactions.add(new GenesisTransactionData(timestamp, recipient, amount));
}
}

View File

@ -136,7 +136,7 @@ public class Synchronizer {
return true;
} finally {
repository.discardChanges();
repository.discardChanges(); // Free repository locks, if any, also in case anything went wrong
this.repository = null;
}
}

View File

@ -5,7 +5,7 @@ import java.math.BigDecimal;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
public class AccountBalanceData {
@ -16,9 +16,8 @@ public class AccountBalanceData {
// Constructors
// necessary for JAX-RS serialization
@SuppressWarnings("unused")
private AccountBalanceData() {
// necessary for JAXB
protected AccountBalanceData() {
}
public AccountBalanceData(String address, long assetId, BigDecimal balance) {

View File

@ -1,22 +1,35 @@
package org.qora.data.account;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import org.qora.group.Group;
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
public class AccountData {
// Properties
protected String address;
protected byte[] reference;
protected byte[] publicKey;
protected int defaultGroupId;
// Constructors
public AccountData(String address, byte[] reference, byte[] publicKey) {
// For JAXB
protected AccountData() {
}
public AccountData(String address, byte[] reference, byte[] publicKey, int defaultGroupId) {
this.address = address;
this.reference = reference;
this.publicKey = publicKey;
this.defaultGroupId = defaultGroupId;
}
public AccountData(String address) {
this(address, null, null);
this(address, null, null, Group.DEFAULT_GROUP);
}
// Getters/Setters
@ -41,6 +54,14 @@ public class AccountData {
this.publicKey = publicKey;
}
public int getDefaultGroupId() {
return this.defaultGroupId;
}
public void setDefaultGroupId(int defaultGroupId) {
this.defaultGroupId = defaultGroupId;
}
// Comparison
@Override

View File

@ -4,6 +4,8 @@ import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlTransient;
import org.qora.group.Group.ApprovalThreshold;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
@ -18,6 +20,7 @@ public class GroupData {
private long created;
private Long updated;
private boolean isOpen;
private ApprovalThreshold approvalThreshold;
/** Reference to CREATE_GROUP or UPDATE_GROUP transaction, used to rebuild group during orphaning. */
// No need to ever expose this via API
@XmlTransient
@ -31,7 +34,7 @@ public class GroupData {
}
/** Constructs new GroupData with nullable groupId and nullable updated [timestamp] */
public GroupData(Integer groupId, String owner, String name, String description, long created, Long updated, boolean isOpen, byte[] reference) {
public GroupData(Integer groupId, String owner, String name, String description, long created, Long updated, boolean isOpen, ApprovalThreshold approvalThreshold, byte[] reference) {
this.groupId = groupId;
this.owner = owner;
this.groupName = name;
@ -39,12 +42,13 @@ public class GroupData {
this.created = created;
this.updated = updated;
this.isOpen = isOpen;
this.approvalThreshold = approvalThreshold;
this.reference = reference;
}
/** Constructs new GroupData with unassigned groupId */
public GroupData(String owner, String name, String description, long created, boolean isOpen, byte[] reference) {
this(null, owner, name, description, created, null, isOpen, reference);
public GroupData(String owner, String name, String description, long created, boolean isOpen, ApprovalThreshold approvalThreshold, byte[] reference) {
this(null, owner, name, description, created, null, isOpen, approvalThreshold, reference);
}
// Getters / setters
@ -105,4 +109,12 @@ public class GroupData {
this.isOpen = isOpen;
}
public ApprovalThreshold getApprovalThreshold() {
return this.approvalThreshold;
}
public void setApprovalThreshold(ApprovalThreshold approvalThreshold) {
this.approvalThreshold = approvalThreshold;
}
}

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@ -10,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class ATTransactionData extends TransactionData {
@ -24,13 +25,18 @@ public class ATTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected ATTransactionData() {
super(TransactionType.AT);
}
public ATTransactionData(String atAddress, String recipient, BigDecimal amount, Long assetId, byte[] message, BigDecimal fee, long timestamp,
byte[] reference, byte[] signature) {
super(TransactionType.AT, fee, GenesisAccount.PUBLIC_KEY, timestamp, reference, signature);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = GenesisAccount.PUBLIC_KEY;
}
public ATTransactionData(long timestamp, int txGroupId, byte[] reference, String atAddress, String recipient, BigDecimal amount, Long assetId,
byte[] message, BigDecimal fee, byte[] signature) {
super(TransactionType.AT, timestamp, txGroupId, reference, GenesisAccount.PUBLIC_KEY, fee, signature);
this.atAddress = atAddress;
this.recipient = recipient;
@ -39,9 +45,9 @@ public class ATTransactionData extends TransactionData {
this.message = message;
}
public ATTransactionData(String atAddress, String recipient, BigDecimal amount, Long assetId, byte[] message, BigDecimal fee, long timestamp,
byte[] reference) {
this(atAddress, recipient, amount, assetId, message, fee, timestamp, reference, null);
public ATTransactionData(long timestamp, int txGroupId, byte[] reference, String atAddress, String recipient, BigDecimal amount, Long assetId,
byte[] message, BigDecimal fee) {
this(timestamp, txGroupId, reference, atAddress, recipient, amount, assetId, message, fee, null);
}
// Getters/Setters

View File

@ -10,7 +10,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class AddGroupAdminTransactionData extends TransactionData {
@ -25,7 +25,7 @@ public class AddGroupAdminTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected AddGroupAdminTransactionData() {
super(TransactionType.ADD_GROUP_ADMIN);
}
@ -34,8 +34,8 @@ public class AddGroupAdminTransactionData extends TransactionData {
this.creatorPublicKey = this.ownerPublicKey;
}
public AddGroupAdminTransactionData(byte[] ownerPublicKey, int groupId, String member, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.ADD_GROUP_ADMIN, fee, ownerPublicKey, timestamp, reference, signature);
public AddGroupAdminTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, int groupId, String member, BigDecimal fee, byte[] signature) {
super(TransactionType.ADD_GROUP_ADMIN, timestamp, txGroupId, reference, ownerPublicKey, fee, signature);
this.ownerPublicKey = ownerPublicKey;
this.groupId = groupId;

View File

@ -3,6 +3,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import java.util.List;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@ -11,7 +12,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class ArbitraryTransactionData extends TransactionData {
@ -32,17 +33,22 @@ public class ArbitraryTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected ArbitraryTransactionData() {
super(TransactionType.ARBITRARY);
}
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.senderPublicKey;
}
/** Reconstructing a V3 arbitrary transaction with signature */
public ArbitraryTransactionData(int version, byte[] senderPublicKey, int service, byte[] data, DataType dataType, List<PaymentData> payments,
BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.ARBITRARY, fee, senderPublicKey, timestamp, reference, signature);
public ArbitraryTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, int version, int service,
byte[] data, DataType dataType, List<PaymentData> payments, BigDecimal fee, byte[] signature) {
super(TransactionType.ARBITRARY, timestamp, txGroupId, reference, senderPublicKey, fee, signature);
this.version = version;
this.senderPublicKey = senderPublicKey;
this.version = version;
this.service = service;
this.data = data;
this.dataType = dataType;
@ -50,33 +56,33 @@ public class ArbitraryTransactionData extends TransactionData {
}
/** Constructing a new V3 arbitrary transaction without signature */
public ArbitraryTransactionData(int version, byte[] senderPublicKey, int service, byte[] data, DataType dataType, List<PaymentData> payments,
BigDecimal fee, long timestamp, byte[] reference) {
this(version, senderPublicKey, service, data, dataType, payments, fee, timestamp, reference, null);
public ArbitraryTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, int version, int service,
byte[] data, DataType dataType, List<PaymentData> payments, BigDecimal fee) {
this(timestamp, txGroupId, reference, senderPublicKey, version, service, data, dataType, payments, fee, null);
}
/** Reconstructing a V1 arbitrary transaction with signature */
public ArbitraryTransactionData(int version, byte[] senderPublicKey, int service, byte[] data, DataType dataType, BigDecimal fee, long timestamp,
byte[] reference, byte[] signature) {
this(version, senderPublicKey, service, data, dataType, null, fee, timestamp, reference, signature);
public ArbitraryTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, int version, int service, byte[] data,
DataType dataType, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, senderPublicKey, version, service, data, dataType, null, fee, signature);
}
/** Constructing a new V1 arbitrary transaction without signature */
public ArbitraryTransactionData(int version, byte[] senderPublicKey, int service, byte[] data, DataType dataType, BigDecimal fee, long timestamp,
byte[] reference) {
this(version, senderPublicKey, service, data, dataType, null, fee, timestamp, reference, null);
public ArbitraryTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, int version, int service, byte[] data,
DataType dataType, BigDecimal fee) {
this(timestamp, txGroupId, reference, senderPublicKey, version, service, data, dataType, null, fee, null);
}
// Getters/Setters
public int getVersion() {
return this.version;
}
public byte[] getSenderPublicKey() {
return this.senderPublicKey;
}
public int getVersion() {
return this.version;
}
public int getService() {
return this.service;
}

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlTransient;
@ -11,7 +12,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class BuyNameTransactionData extends TransactionData {
@ -36,14 +37,18 @@ public class BuyNameTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected BuyNameTransactionData() {
super(TransactionType.BUY_NAME);
}
public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, byte[] nameReference, BigDecimal fee, long timestamp,
byte[] reference, byte[] signature) {
super(TransactionType.BUY_NAME, fee, buyerPublicKey, timestamp, reference, signature);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.buyerPublicKey;
}
public BuyNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] buyerPublicKey, String name, BigDecimal amount, String seller,
byte[] nameReference, BigDecimal fee, byte[] signature) {
super(TransactionType.BUY_NAME, timestamp, txGroupId, reference, buyerPublicKey, fee, signature);
this.buyerPublicKey = buyerPublicKey;
this.name = name;
@ -52,18 +57,18 @@ public class BuyNameTransactionData extends TransactionData {
this.nameReference = nameReference;
}
public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, BigDecimal fee, long timestamp, byte[] reference,
byte[] signature) {
this(buyerPublicKey, name, amount, seller, null, fee, timestamp, reference, signature);
public BuyNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] buyerPublicKey, String name, BigDecimal amount, String seller,
BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, buyerPublicKey, name, amount, seller, null, fee, signature);
}
public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, byte[] nameReference, BigDecimal fee, long timestamp,
byte[] reference) {
this(buyerPublicKey, name, amount, seller, nameReference, fee, timestamp, reference, null);
public BuyNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] buyerPublicKey, String name, BigDecimal amount, String seller,
byte[] nameReference, BigDecimal fee) {
this(timestamp, txGroupId, reference, buyerPublicKey, name, amount, seller, nameReference, fee, null);
}
public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, BigDecimal fee, long timestamp, byte[] reference) {
this(buyerPublicKey, name, amount, seller, null, fee, timestamp, reference, null);
public BuyNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] buyerPublicKey, String name, BigDecimal amount, String seller, BigDecimal fee) {
this(timestamp, txGroupId, reference, buyerPublicKey, name, amount, seller, null, fee, null);
}
// Getters / setters

View File

@ -11,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class CancelAssetOrderTransactionData extends TransactionData {
@ -22,19 +22,19 @@ public class CancelAssetOrderTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected CancelAssetOrderTransactionData() {
super(TransactionType.CANCEL_ASSET_ORDER);
}
public CancelAssetOrderTransactionData(byte[] creatorPublicKey, byte[] orderId, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(Transaction.TransactionType.CANCEL_ASSET_ORDER, fee, creatorPublicKey, timestamp, reference, signature);
public CancelAssetOrderTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, byte[] orderId, BigDecimal fee, byte[] signature) {
super(Transaction.TransactionType.CANCEL_ASSET_ORDER, timestamp, txGroupId, reference, creatorPublicKey, fee, signature);
this.orderId = orderId;
}
public CancelAssetOrderTransactionData(byte[] creatorPublicKey, byte[] orderId, BigDecimal fee, long timestamp, byte[] reference) {
this(creatorPublicKey, orderId, fee, timestamp, reference, null);
public CancelAssetOrderTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, byte[] orderId, BigDecimal fee) {
this(timestamp, txGroupId, reference, creatorPublicKey, orderId, fee, null);
}
// Getters/Setters

View File

@ -11,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class CancelGroupBanTransactionData extends TransactionData {
@ -31,7 +31,7 @@ public class CancelGroupBanTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected CancelGroupBanTransactionData() {
super(TransactionType.CANCEL_GROUP_BAN);
}
@ -40,8 +40,8 @@ public class CancelGroupBanTransactionData extends TransactionData {
this.creatorPublicKey = this.adminPublicKey;
}
public CancelGroupBanTransactionData(byte[] adminPublicKey, int groupId, String member, byte[] banReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.CANCEL_GROUP_BAN, fee, adminPublicKey, timestamp, reference, signature);
public CancelGroupBanTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] adminPublicKey, int groupId, String member, byte[] banReference, BigDecimal fee, byte[] signature) {
super(TransactionType.CANCEL_GROUP_BAN, timestamp, txGroupId, reference, adminPublicKey, fee, signature);
this.adminPublicKey = adminPublicKey;
this.groupId = groupId;
@ -50,8 +50,8 @@ public class CancelGroupBanTransactionData extends TransactionData {
}
/** Constructor typically used after deserialization */
public CancelGroupBanTransactionData(byte[] adminPublicKey, int groupId, String member, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
this(adminPublicKey, groupId, member, null, fee, timestamp, reference, signature);
public CancelGroupBanTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] adminPublicKey, int groupId, String member, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, adminPublicKey, groupId, member, null, fee, signature);
}
// Getters / setters

View File

@ -11,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class CancelGroupInviteTransactionData extends TransactionData {
@ -31,7 +31,7 @@ public class CancelGroupInviteTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected CancelGroupInviteTransactionData() {
super(TransactionType.CANCEL_GROUP_INVITE);
}
@ -40,8 +40,8 @@ public class CancelGroupInviteTransactionData extends TransactionData {
this.creatorPublicKey = this.adminPublicKey;
}
public CancelGroupInviteTransactionData(byte[] adminPublicKey, int groupId, String invitee, byte[] inviteReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.CANCEL_GROUP_INVITE, fee, adminPublicKey, timestamp, reference, signature);
public CancelGroupInviteTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] adminPublicKey, int groupId, String invitee, byte[] inviteReference, BigDecimal fee, byte[] signature) {
super(TransactionType.CANCEL_GROUP_INVITE, timestamp, txGroupId, reference, adminPublicKey, fee, signature);
this.adminPublicKey = adminPublicKey;
this.groupId = groupId;
@ -50,8 +50,8 @@ public class CancelGroupInviteTransactionData extends TransactionData {
}
/** Constructor typically used after deserialization */
public CancelGroupInviteTransactionData(byte[] adminPublicKey, int groupId, String invitee, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
this(adminPublicKey, groupId, invitee, null, fee, timestamp, reference, signature);
public CancelGroupInviteTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] adminPublicKey, int groupId, String invitee, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, adminPublicKey, groupId, invitee, null, fee, signature);
}
// Getters / setters

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@ -9,7 +10,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class CancelSellNameTransactionData extends TransactionData {
@ -22,20 +23,24 @@ public class CancelSellNameTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected CancelSellNameTransactionData() {
super(TransactionType.CANCEL_SELL_NAME);
}
public CancelSellNameTransactionData(byte[] ownerPublicKey, String name, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.CANCEL_SELL_NAME, fee, ownerPublicKey, timestamp, reference, signature);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.ownerPublicKey;
}
public CancelSellNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, String name, BigDecimal fee, byte[] signature) {
super(TransactionType.CANCEL_SELL_NAME, timestamp, txGroupId, reference, ownerPublicKey, fee, signature);
this.ownerPublicKey = ownerPublicKey;
this.name = name;
}
public CancelSellNameTransactionData(byte[] ownerPublicKey, String name, BigDecimal fee, long timestamp, byte[] reference) {
this(ownerPublicKey, name, fee, timestamp, reference, null);
public CancelSellNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, String name, BigDecimal fee) {
this(timestamp, txGroupId, reference, ownerPublicKey, name, fee, null);
}
// Getters / setters

View File

@ -10,7 +10,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class CreateAssetOrderTransactionData extends TransactionData {
@ -27,14 +27,14 @@ public class CreateAssetOrderTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected CreateAssetOrderTransactionData() {
super(TransactionType.CREATE_ASSET_ORDER);
}
public CreateAssetOrderTransactionData(byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, BigDecimal fee,
long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.CREATE_ASSET_ORDER, fee, creatorPublicKey, timestamp, reference, signature);
public CreateAssetOrderTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, long haveAssetId, long wantAssetId,
BigDecimal amount, BigDecimal price, BigDecimal fee, byte[] signature) {
super(TransactionType.CREATE_ASSET_ORDER, timestamp, txGroupId, reference, creatorPublicKey, fee, signature);
this.haveAssetId = haveAssetId;
this.wantAssetId = wantAssetId;
@ -42,9 +42,9 @@ public class CreateAssetOrderTransactionData extends TransactionData {
this.price = price;
}
public CreateAssetOrderTransactionData(byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, BigDecimal fee,
long timestamp, byte[] reference) {
this(creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, timestamp, reference, null);
public CreateAssetOrderTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, long haveAssetId, long wantAssetId,
BigDecimal amount, BigDecimal price, BigDecimal fee) {
this(timestamp, txGroupId, reference, creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, null);
}
// Getters/Setters

View File

@ -6,12 +6,13 @@ import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import org.qora.group.Group.ApprovalThreshold;
import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(
allOf = {
@ -47,23 +48,28 @@ public class CreateGroupTransactionData extends TransactionData {
example = "true"
)
private boolean isOpen;
@Schema(
description = "how many group admins are required to approve group member transactions"
)
private ApprovalThreshold approvalThreshold;
// Constructors
// For JAX-RS
// For JAXB
protected CreateGroupTransactionData() {
super(TransactionType.CREATE_GROUP);
}
public CreateGroupTransactionData(byte[] creatorPublicKey, String owner, String groupName, String description, boolean isOpen, Integer groupId,
BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.CREATE_GROUP, fee, creatorPublicKey, timestamp, reference, signature);
public CreateGroupTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, String owner, String groupName, String description,
boolean isOpen, ApprovalThreshold approvalThreshold, Integer groupId, BigDecimal fee, byte[] signature) {
super(TransactionType.CREATE_GROUP, timestamp, txGroupId, reference, creatorPublicKey, fee, signature);
this.creatorPublicKey = creatorPublicKey;
this.owner = owner;
this.groupName = groupName;
this.description = description;
this.isOpen = isOpen;
this.approvalThreshold = approvalThreshold;
this.groupId = groupId;
}
@ -85,6 +91,10 @@ public class CreateGroupTransactionData extends TransactionData {
return this.isOpen;
}
public ApprovalThreshold getApprovalThreshold() {
return this.approvalThreshold;
}
public Integer getGroupId() {
return this.groupId;
}

View File

@ -8,10 +8,11 @@ import javax.xml.bind.annotation.XmlAccessorType;
import org.qora.data.voting.PollOptionData;
import org.qora.transaction.Transaction;
import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class CreatePollTransactionData extends TransactionData {
@ -24,13 +25,14 @@ public class CreatePollTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected CreatePollTransactionData() {
super(TransactionType.CREATE_POLL);
}
public CreatePollTransactionData(byte[] creatorPublicKey, String owner, String pollName, String description, List<PollOptionData> pollOptions,
BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(Transaction.TransactionType.CREATE_POLL, fee, creatorPublicKey, timestamp, reference, signature);
public CreatePollTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, String owner,
String pollName, String description, List<PollOptionData> pollOptions, BigDecimal fee, byte[] signature) {
super(Transaction.TransactionType.CREATE_POLL, timestamp, txGroupId, reference, creatorPublicKey, fee, signature);
this.owner = owner;
this.pollName = pollName;
@ -38,9 +40,9 @@ public class CreatePollTransactionData extends TransactionData {
this.pollOptions = pollOptions;
}
public CreatePollTransactionData(byte[] creatorPublicKey, String owner, String pollName, String description, List<PollOptionData> pollOptions,
BigDecimal fee, long timestamp, byte[] reference) {
this(creatorPublicKey, owner, pollName, description, pollOptions, fee, timestamp, reference, null);
public CreatePollTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, String owner,
String pollName, String description, List<PollOptionData> pollOptions, BigDecimal fee) {
this(timestamp, txGroupId, reference, creatorPublicKey, owner, pollName, description, pollOptions, fee, null);
}
// Getters/setters

View File

@ -9,7 +9,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class DeployAtTransactionData extends TransactionData {
@ -17,41 +17,42 @@ public class DeployAtTransactionData extends TransactionData {
// Properties
private String name;
private String description;
private String ATType;
private String aTType;
private String tags;
private byte[] creationBytes;
private BigDecimal amount;
private long assetId;
private String ATAddress;
private String aTAddress;
// Constructors
// For JAX-RS
protected DeployAtTransactionData() {
super(TransactionType.DEPLOY_AT);
}
public DeployAtTransactionData(String ATAddress, byte[] creatorPublicKey, String name, String description, String ATType, String tags, byte[] creationBytes,
BigDecimal amount, long assetId, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.DEPLOY_AT, fee, creatorPublicKey, timestamp, reference, signature);
public DeployAtTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, String aTAddress, String name, String description,
String aTType, String tags, byte[] creationBytes, BigDecimal amount, long assetId, BigDecimal fee, byte[] signature) {
super(TransactionType.DEPLOY_AT, timestamp, txGroupId, reference, creatorPublicKey, fee, signature);
this.aTAddress = aTAddress;
this.name = name;
this.description = description;
this.ATType = ATType;
this.aTType = aTType;
this.tags = tags;
this.creationBytes = creationBytes;
this.amount = amount;
this.assetId = assetId;
this.creationBytes = creationBytes;
this.ATAddress = ATAddress;
}
public DeployAtTransactionData(byte[] creatorPublicKey, String name, String description, String ATType, String tags, byte[] creationBytes,
BigDecimal amount, long assetId, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
this(null, creatorPublicKey, name, description, ATType, tags, creationBytes, amount, assetId, fee, timestamp, reference, signature);
public DeployAtTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, String name, String description,
String aTType, String tags, byte[] creationBytes, BigDecimal amount, long assetId, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, creatorPublicKey, null, name, description, aTType, tags, creationBytes, amount, assetId, fee, signature);
}
public DeployAtTransactionData(byte[] creatorPublicKey, String name, String description, String ATType, String tags, byte[] creationBytes,
BigDecimal amount, long assetId, BigDecimal fee, long timestamp, byte[] reference) {
this(null, creatorPublicKey, name, description, ATType, tags, creationBytes, amount, assetId, fee, timestamp, reference, null);
public DeployAtTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, String name, String description,
String aTType, String tags, byte[] creationBytes, BigDecimal amount, long assetId, BigDecimal fee) {
this(timestamp, txGroupId, reference, creatorPublicKey, null, name, description, aTType, tags, creationBytes, amount, assetId, fee, null);
}
// Getters/Setters
@ -64,8 +65,8 @@ public class DeployAtTransactionData extends TransactionData {
return this.description;
}
public String getATType() {
return this.ATType;
public String getAtType() {
return this.aTType;
}
public String getTags() {
@ -84,12 +85,12 @@ public class DeployAtTransactionData extends TransactionData {
return this.assetId;
}
public String getATAddress() {
return this.ATAddress;
public String getAtAddress() {
return this.aTAddress;
}
public void setATAddress(String ATAddress) {
this.ATAddress = ATAddress;
public void setAtAddress(String AtAddress) {
this.aTAddress = AtAddress;
}
}

View File

@ -11,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(
allOf = {
@ -27,29 +27,30 @@ public class GenesisTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected GenesisTransactionData() {
super(TransactionType.GENESIS);
}
public GenesisTransactionData(String recipient, BigDecimal amount, long assetId, long timestamp, byte[] signature) {
// Zero fee
super(TransactionType.GENESIS, BigDecimal.ZERO, GenesisAccount.PUBLIC_KEY, timestamp, null, signature);
public GenesisTransactionData(long timestamp, String recipient, BigDecimal amount, long assetId, byte[] signature) {
// no groupID, Zero fee
super(TransactionType.GENESIS, timestamp, 0, null, GenesisAccount.PUBLIC_KEY, BigDecimal.ZERO, signature);
this.recipient = recipient;
this.amount = amount;
this.assetId = assetId;
}
public GenesisTransactionData(String recipient, BigDecimal amount, long timestamp, byte[] signature) {
this(recipient, amount, Asset.QORA, timestamp, signature);
public GenesisTransactionData(long timestamp, String recipient, BigDecimal amount, byte[] signature) {
this(timestamp, recipient, amount, Asset.QORA, signature);
}
public GenesisTransactionData(String recipient, BigDecimal amount, long assetId, long timestamp) {
this(recipient, amount, assetId, timestamp, null);
public GenesisTransactionData(long timestamp, String recipient, BigDecimal amount, long assetId) {
this(timestamp, recipient, amount, assetId, null);
}
public GenesisTransactionData(String recipient, BigDecimal amount, long timestamp) {
this(recipient, amount, Asset.QORA, timestamp, null);
public GenesisTransactionData(long timestamp, String recipient, BigDecimal amount) {
this(timestamp, recipient, amount, Asset.QORA, null);
}
// Getters/Setters

View File

@ -0,0 +1,95 @@
package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlTransient;
import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(
allOf = {
TransactionData.class
}
)
public class GroupApprovalTransactionData extends TransactionData {
// Properties
@Schema(
description = "admin's public key",
example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP"
)
private byte[] adminPublicKey;
@Schema(
description = "transaction pending approval"
)
private byte[] pendingSignature;
@Schema(
description = "approval decision",
example = "true"
)
private boolean approval;
/** Reference to prior GROUP_APPROVAL transaction, used to rebuild approval status during orphaning. */
// For internal use when orphaning
@XmlTransient
@Schema(
hidden = true
)
private byte[] priorReference;
// Constructors
// For JAXB
protected GroupApprovalTransactionData() {
super(TransactionType.GROUP_APPROVAL);
}
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.adminPublicKey;
}
public GroupApprovalTransactionData(long timestamp, int groupId, byte[] reference, byte[] adminPublicKey, byte[] pendingSignature, boolean approval,
byte[] priorReference, BigDecimal fee, byte[] signature) {
super(TransactionType.GROUP_APPROVAL, timestamp, groupId, reference, adminPublicKey, fee, signature);
this.adminPublicKey = adminPublicKey;
this.pendingSignature = pendingSignature;
this.approval = approval;
this.priorReference = priorReference;
}
/** Constructor typically used after deserialization */
public GroupApprovalTransactionData(long timestamp, int groupId, byte[] reference, byte[] adminPublicKey, byte[] pendingSignature, boolean approval,
BigDecimal fee, byte[] signature) {
this(timestamp, groupId, reference, adminPublicKey, pendingSignature, approval, null, fee, signature);
}
// Getters / setters
public byte[] getAdminPublicKey() {
return this.adminPublicKey;
}
public byte[] getPendingSignature() {
return this.pendingSignature;
}
public boolean getApproval() {
return this.approval;
}
public byte[] getPriorReference() {
return this.priorReference;
}
public void setPriorReference(byte[] priorReference) {
this.priorReference = priorReference;
}
}

View File

@ -11,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(
allOf = {
@ -61,7 +61,7 @@ public class GroupBanTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected GroupBanTransactionData() {
super(TransactionType.GROUP_BAN);
}
@ -70,9 +70,9 @@ public class GroupBanTransactionData extends TransactionData {
this.creatorPublicKey = this.adminPublicKey;
}
public GroupBanTransactionData(byte[] adminPublicKey, int groupId, String member, String reason, int timeToLive, byte[] memberReference,
byte[] adminReference, byte[] joinInviteReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.GROUP_BAN, fee, adminPublicKey, timestamp, reference, signature);
public GroupBanTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] adminPublicKey, int groupId, String member,
String reason, int timeToLive, byte[] memberReference, byte[] adminReference, byte[] joinInviteReference, BigDecimal fee, byte[] signature) {
super(TransactionType.GROUP_BAN, timestamp, txGroupId, reference, adminPublicKey, fee, signature);
this.adminPublicKey = adminPublicKey;
this.groupId = groupId;
@ -85,9 +85,9 @@ public class GroupBanTransactionData extends TransactionData {
}
/** Constructor typically used after deserialization */
public GroupBanTransactionData(byte[] adminPublicKey, int groupId, String offender, String reason, int timeToLive, BigDecimal fee, long timestamp,
byte[] reference, byte[] signature) {
this(adminPublicKey, groupId, offender, reason, timeToLive, null, null, null, fee, timestamp, reference, signature);
public GroupBanTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] adminPublicKey, int groupId, String offender, String reason,
int timeToLive, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, adminPublicKey, groupId, offender, reason, timeToLive, null, null, null, fee, signature);
}
// Getters / setters

View File

@ -11,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class GroupInviteTransactionData extends TransactionData {
@ -33,7 +33,7 @@ public class GroupInviteTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected GroupInviteTransactionData() {
super(TransactionType.GROUP_INVITE);
}
@ -42,8 +42,8 @@ public class GroupInviteTransactionData extends TransactionData {
this.creatorPublicKey = this.adminPublicKey;
}
public GroupInviteTransactionData(byte[] adminPublicKey, int groupId, String invitee, int timeToLive, byte[] joinReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.GROUP_INVITE, fee, adminPublicKey, timestamp, reference, signature);
public GroupInviteTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] adminPublicKey, int groupId, String invitee, int timeToLive, byte[] joinReference, BigDecimal fee, byte[] signature) {
super(TransactionType.GROUP_INVITE, timestamp, txGroupId, reference, adminPublicKey, fee, signature);
this.adminPublicKey = adminPublicKey;
this.groupId = groupId;
@ -53,8 +53,8 @@ public class GroupInviteTransactionData extends TransactionData {
}
/** Constructor typically used after deserialization */
public GroupInviteTransactionData(byte[] adminPublicKey, int groupId, String invitee, int timeToLive, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
this(adminPublicKey, groupId, invitee, timeToLive, null, fee, timestamp, reference, signature);
public GroupInviteTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] adminPublicKey, int groupId, String invitee, int timeToLive, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, adminPublicKey, groupId, invitee, timeToLive, null, fee, signature);
}
// Getters / setters

View File

@ -11,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(
allOf = {
@ -58,7 +58,7 @@ public class GroupKickTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected GroupKickTransactionData() {
super(TransactionType.GROUP_KICK);
}
@ -67,9 +67,9 @@ public class GroupKickTransactionData extends TransactionData {
this.creatorPublicKey = this.adminPublicKey;
}
public GroupKickTransactionData(byte[] adminPublicKey, int groupId, String member, String reason, byte[] memberReference, byte[] adminReference,
byte[] joinReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.GROUP_KICK, fee, adminPublicKey, timestamp, reference, signature);
public GroupKickTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] adminPublicKey, int groupId, String member,
String reason, byte[] memberReference, byte[] adminReference, byte[] joinReference, BigDecimal fee, byte[] signature) {
super(TransactionType.GROUP_KICK, timestamp, txGroupId, reference, adminPublicKey, fee, signature);
this.adminPublicKey = adminPublicKey;
this.groupId = groupId;
@ -81,9 +81,9 @@ public class GroupKickTransactionData extends TransactionData {
}
/** Constructor typically used after deserialization */
public GroupKickTransactionData(byte[] adminPublicKey, int groupId, String member, String reason, BigDecimal fee, long timestamp, byte[] reference,
byte[] signature) {
this(adminPublicKey, groupId, member, reason, null, null, null, fee, timestamp, reference, signature);
public GroupKickTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] adminPublicKey, int groupId, String member, String reason,
BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, adminPublicKey, groupId, member, reason, null, null, null, fee, signature);
}
// Getters / setters

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@ -10,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class IssueAssetTransactionData extends TransactionData {
@ -34,14 +35,18 @@ public class IssueAssetTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected IssueAssetTransactionData() {
super(TransactionType.ISSUE_ASSET);
}
public IssueAssetTransactionData(Long assetId, byte[] issuerPublicKey, String owner, String assetName, String description, long quantity,
boolean isDivisible, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.ISSUE_ASSET, fee, issuerPublicKey, timestamp, reference, signature);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.issuerPublicKey;
}
public IssueAssetTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] issuerPublicKey, Long assetId, String owner,
String assetName, String description, long quantity, boolean isDivisible, BigDecimal fee, byte[] signature) {
super(TransactionType.ISSUE_ASSET, timestamp, txGroupId, reference, issuerPublicKey, fee, signature);
this.assetId = assetId;
this.issuerPublicKey = issuerPublicKey;
@ -52,14 +57,14 @@ public class IssueAssetTransactionData extends TransactionData {
this.isDivisible = isDivisible;
}
public IssueAssetTransactionData(byte[] issuerPublicKey, String owner, String assetName, String description, long quantity, boolean isDivisible,
BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
this(null, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference, signature);
public IssueAssetTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] issuerPublicKey, String owner, String assetName,
String description, long quantity, boolean isDivisible, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, issuerPublicKey, null, owner, assetName, description, quantity, isDivisible, fee, signature);
}
public IssueAssetTransactionData(byte[] issuerPublicKey, String owner, String assetName, String description, long quantity, boolean isDivisible,
BigDecimal fee, long timestamp, byte[] reference) {
this(null, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference, null);
public IssueAssetTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] issuerPublicKey, String owner, String assetName,
String description, long quantity, boolean isDivisible, BigDecimal fee) {
this(timestamp, txGroupId, reference, issuerPublicKey, null, owner, assetName, description, quantity, isDivisible, fee, null);
}
// Getters/Setters

View File

@ -11,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class JoinGroupTransactionData extends TransactionData {
@ -29,7 +29,7 @@ public class JoinGroupTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected JoinGroupTransactionData() {
super(TransactionType.JOIN_GROUP);
}
@ -38,8 +38,8 @@ public class JoinGroupTransactionData extends TransactionData {
this.creatorPublicKey = this.joinerPublicKey;
}
public JoinGroupTransactionData(byte[] joinerPublicKey, int groupId, byte[] inviteReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.JOIN_GROUP, fee, joinerPublicKey, timestamp, reference, signature);
public JoinGroupTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] joinerPublicKey, int groupId, byte[] inviteReference, BigDecimal fee, byte[] signature) {
super(TransactionType.JOIN_GROUP, timestamp, txGroupId, reference, joinerPublicKey, fee, signature);
this.joinerPublicKey = joinerPublicKey;
this.groupId = groupId;
@ -47,8 +47,8 @@ public class JoinGroupTransactionData extends TransactionData {
}
/** Constructor typically used after deserialization */
public JoinGroupTransactionData(byte[] joinerPublicKey, int groupId, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
this(joinerPublicKey, groupId, null, fee, timestamp, reference, signature);
public JoinGroupTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] joinerPublicKey, int groupId, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, joinerPublicKey, groupId, null, fee, signature);
}
// Getters / setters

View File

@ -11,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class LeaveGroupTransactionData extends TransactionData {
@ -34,7 +34,7 @@ public class LeaveGroupTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected LeaveGroupTransactionData() {
super(TransactionType.LEAVE_GROUP);
}
@ -43,8 +43,8 @@ public class LeaveGroupTransactionData extends TransactionData {
this.creatorPublicKey = this.leaverPublicKey;
}
public LeaveGroupTransactionData(byte[] leaverPublicKey, int groupId, byte[] memberReference, byte[] adminReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.LEAVE_GROUP, fee, leaverPublicKey, timestamp, reference, signature);
public LeaveGroupTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] leaverPublicKey, int groupId, byte[] memberReference, byte[] adminReference, BigDecimal fee, byte[] signature) {
super(TransactionType.LEAVE_GROUP, timestamp, txGroupId, reference, leaverPublicKey, fee, signature);
this.leaverPublicKey = leaverPublicKey;
this.groupId = groupId;
@ -53,8 +53,8 @@ public class LeaveGroupTransactionData extends TransactionData {
}
/** Constructor typically used after deserialization */
public LeaveGroupTransactionData(byte[] leaverPublicKey, int groupId, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
this(leaverPublicKey, groupId, null, null, fee, timestamp, reference, signature);
public LeaveGroupTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] leaverPublicKey, int groupId, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, leaverPublicKey, groupId, null, null, fee, signature);
}
// Getters / setters

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@ -10,14 +11,14 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class MessageTransactionData extends TransactionData {
// Properties
private int version;
private byte[] senderPublicKey;
private int version;
private String recipient;
private Long assetId;
private BigDecimal amount;
@ -27,16 +28,21 @@ public class MessageTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected MessageTransactionData() {
super(TransactionType.MESSAGE);
}
public MessageTransactionData(int version, byte[] senderPublicKey, String recipient, Long assetId, BigDecimal amount, byte[] data, boolean isText,
boolean isEncrypted, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.MESSAGE, fee, senderPublicKey, timestamp, reference, signature);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.senderPublicKey;
}
public MessageTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, int version, String recipient, Long assetId,
BigDecimal amount, byte[] data, boolean isText, boolean isEncrypted, BigDecimal fee, byte[] signature) {
super(TransactionType.MESSAGE, timestamp, txGroupId, reference, senderPublicKey, fee, signature);
this.version = version;
this.senderPublicKey = senderPublicKey;
this.version = version;
this.recipient = recipient;
if (assetId != null)
@ -50,21 +56,21 @@ public class MessageTransactionData extends TransactionData {
this.isEncrypted = isEncrypted;
}
public MessageTransactionData(int version, byte[] senderPublicKey, String recipient, Long assetId, BigDecimal amount, byte[] data, boolean isText,
boolean isEncrypted, BigDecimal fee, long timestamp, byte[] reference) {
this(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference, null);
public MessageTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, int version, String recipient, Long assetId,
BigDecimal amount, byte[] data, boolean isText, boolean isEncrypted, BigDecimal fee) {
this(timestamp, txGroupId, reference, senderPublicKey, version, recipient, assetId, amount, data, isText, isEncrypted, fee, null);
}
// Getters/Setters
public int getVersion() {
return this.version;
}
public byte[] getSenderPublicKey() {
return this.senderPublicKey;
}
public int getVersion() {
return this.version;
}
public String getRecipient() {
return this.recipient;
}

View File

@ -3,15 +3,17 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import java.util.List;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import org.qora.data.PaymentData;
import org.qora.transaction.Transaction;
import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class MultiPaymentTransactionData extends TransactionData {
@ -22,19 +24,24 @@ public class MultiPaymentTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected MultiPaymentTransactionData() {
super(TransactionType.MULTI_PAYMENT);
}
public MultiPaymentTransactionData(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(Transaction.TransactionType.MULTI_PAYMENT, fee, senderPublicKey, timestamp, reference, signature);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.senderPublicKey;
}
public MultiPaymentTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, byte[] signature) {
super(Transaction.TransactionType.MULTI_PAYMENT, timestamp, txGroupId, reference, senderPublicKey, fee, signature);
this.senderPublicKey = senderPublicKey;
this.payments = payments;
}
public MultiPaymentTransactionData(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, long timestamp, byte[] reference) {
this(senderPublicKey, payments, fee, timestamp, reference, null);
public MultiPaymentTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee) {
this(timestamp, txGroupId, reference, senderPublicKey, payments, fee, null);
}
// Getters/setters

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@ -10,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema( allOf = { TransactionData.class } )
public class PaymentTransactionData extends TransactionData {
@ -29,22 +30,26 @@ public class PaymentTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected PaymentTransactionData() {
super(TransactionType.PAYMENT);
}
public PaymentTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference,
byte[] signature) {
super(TransactionType.PAYMENT, fee, senderPublicKey, timestamp, reference, signature);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.senderPublicKey;
}
public PaymentTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, String recipient, BigDecimal amount,
BigDecimal fee, byte[] signature) {
super(TransactionType.PAYMENT, timestamp, txGroupId, reference, senderPublicKey, fee, signature);
this.senderPublicKey = senderPublicKey;
this.recipient = recipient;
this.amount = amount;
}
public PaymentTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference) {
this(senderPublicKey, recipient, amount, fee, timestamp, reference, null);
public PaymentTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, String recipient, BigDecimal amount, BigDecimal fee) {
this(timestamp, txGroupId, reference, senderPublicKey, recipient, amount, fee, null);
}
// Getters/Setters

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@ -9,7 +10,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class RegisterNameTransactionData extends TransactionData {
@ -26,14 +27,18 @@ public class RegisterNameTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected RegisterNameTransactionData() {
super(TransactionType.REGISTER_NAME);
}
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);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.registrantPublicKey;
}
public RegisterNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] registrantPublicKey, String owner, String name, String data,
BigDecimal fee, byte[] signature) {
super(TransactionType.REGISTER_NAME, timestamp, txGroupId, reference, registrantPublicKey, fee, signature);
this.registrantPublicKey = registrantPublicKey;
this.owner = owner;
@ -41,8 +46,8 @@ public class RegisterNameTransactionData extends TransactionData {
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);
public RegisterNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] registrantPublicKey, String owner, String name, String data, BigDecimal fee) {
this(timestamp, txGroupId, reference, registrantPublicKey, owner, name, data, fee, null);
}
// Getters / setters

View File

@ -11,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class RemoveGroupAdminTransactionData extends TransactionData {
@ -31,7 +31,7 @@ public class RemoveGroupAdminTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected RemoveGroupAdminTransactionData() {
super(TransactionType.REMOVE_GROUP_ADMIN);
}
@ -40,8 +40,8 @@ public class RemoveGroupAdminTransactionData extends TransactionData {
this.creatorPublicKey = this.ownerPublicKey;
}
public RemoveGroupAdminTransactionData(byte[] ownerPublicKey, int groupId, String admin, byte[] adminReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.REMOVE_GROUP_ADMIN, fee, ownerPublicKey, timestamp, reference, signature);
public RemoveGroupAdminTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, int groupId, String admin, byte[] adminReference, BigDecimal fee, byte[] signature) {
super(TransactionType.REMOVE_GROUP_ADMIN, timestamp, txGroupId, reference, ownerPublicKey, fee, signature);
this.ownerPublicKey = ownerPublicKey;
this.groupId = groupId;
@ -50,8 +50,8 @@ public class RemoveGroupAdminTransactionData extends TransactionData {
}
/** Constructor typically used after deserialization */
public RemoveGroupAdminTransactionData(byte[] ownerPublicKey, int groupId, String admin, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
this(ownerPublicKey, groupId, admin, null, fee, timestamp, reference, signature);
public RemoveGroupAdminTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, int groupId, String admin, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, ownerPublicKey, groupId, admin, null, fee, signature);
}
// Getters / setters

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@ -10,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class SellNameTransactionData extends TransactionData {
@ -29,21 +30,25 @@ public class SellNameTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected SellNameTransactionData() {
super(TransactionType.SELL_NAME);
}
public SellNameTransactionData(byte[] ownerPublicKey, String name, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.SELL_NAME, fee, ownerPublicKey, timestamp, reference, signature);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.ownerPublicKey;
}
public SellNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, String name, BigDecimal amount, BigDecimal fee, byte[] signature) {
super(TransactionType.SELL_NAME, timestamp, txGroupId, reference, ownerPublicKey, fee, signature);
this.ownerPublicKey = ownerPublicKey;
this.name = name;
this.amount = amount;
}
public SellNameTransactionData(byte[] ownerPublicKey, String name, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference) {
this(ownerPublicKey, name, amount, fee, timestamp, reference, null);
public SellNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, String name, BigDecimal amount, BigDecimal fee) {
this(timestamp, txGroupId, reference, ownerPublicKey, name, amount, fee, null);
}
// Getters / setters

View File

@ -0,0 +1,86 @@
package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(
allOf = {
TransactionData.class
}
)
public class SetGroupTransactionData extends TransactionData {
// Properties
@Schema(
description = "account's new default groupID",
example = "true"
)
private int defaultGroupId;
/** Reference to previous defaultGroupId, used during orphaning. */
// For internal use when orphaning
@XmlTransient
@Schema(
hidden = true
)
private Integer previousDefaultGroupId;
// Constructors
// For JAXB
protected SetGroupTransactionData() {
super(TransactionType.SET_GROUP);
}
public SetGroupTransactionData(long timestamp, int groupId, byte[] reference, byte[] creatorPublicKey, int defaultGroupId, Integer previousDefaultGroupId,
BigDecimal fee, byte[] signature) {
super(TransactionType.SET_GROUP, timestamp, groupId, reference, creatorPublicKey, fee, signature);
this.defaultGroupId = defaultGroupId;
this.previousDefaultGroupId = previousDefaultGroupId;
}
/** Constructor typically used after deserialization */
public SetGroupTransactionData(long timestamp, int groupId, byte[] reference, byte[] creatorPublicKey, int defaultGroupId, BigDecimal fee,
byte[] signature) {
this(timestamp, groupId, reference, creatorPublicKey, defaultGroupId, null, fee, signature);
}
// Getters / setters
public int getDefaultGroupId() {
return this.defaultGroupId;
}
public Integer getPreviousDefaultGroupId() {
return this.previousDefaultGroupId;
}
public void setPreviousDefaultGroupId(int previousDefaultGroupId) {
this.previousDefaultGroupId = previousDefaultGroupId;
}
// Re-expose to JAXB
@Override
@XmlElement
public byte[] getCreatorPublicKey() {
return super.getCreatorPublicKey();
}
@Override
@XmlElement
public void setCreatorPublicKey(byte[] creatorPublicKey) {
super.setCreatorPublicKey(creatorPublicKey);
}
}

View File

@ -10,9 +10,6 @@ import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
// XXX are this still needed? see below
// import org.eclipse.persistence.oxm.annotations.XmlClassExtractor;
// import org.qora.api.TransactionClassExtractor;
import org.qora.crypto.Crypto;
import org.qora.transaction.Transaction.TransactionType;
@ -27,8 +24,6 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
* then chances are that class is missing a no-argument constructor!
*/
// XXX is this still in use?
// @XmlClassExtractor(TransactionClassExtractor.class)
@XmlSeeAlso({GenesisTransactionData.class, PaymentTransactionData.class, RegisterNameTransactionData.class, UpdateNameTransactionData.class,
SellNameTransactionData.class, CancelSellNameTransactionData.class, BuyNameTransactionData.class,
CreatePollTransactionData.class, VoteOnPollTransactionData.class, ArbitraryTransactionData.class,
@ -39,9 +34,10 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
AddGroupAdminTransactionData.class, RemoveGroupAdminTransactionData.class,
GroupBanTransactionData.class, CancelGroupBanTransactionData.class,
GroupKickTransactionData.class, GroupInviteTransactionData.class,
JoinGroupTransactionData.class, LeaveGroupTransactionData.class
JoinGroupTransactionData.class, LeaveGroupTransactionData.class,
GroupApprovalTransactionData.class, SetGroupTransactionData.class
})
//All properties to be converted to JSON via JAX-RS
//All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class TransactionData {
@ -59,6 +55,8 @@ public abstract class TransactionData {
protected BigDecimal fee;
@Schema(accessMode = AccessMode.READ_ONLY, description = "signature for transaction's raw bytes, using sender's private key", example = "real_transaction_signature_in_base58")
protected byte[] signature;
@Schema(description = "groupID for this transaction")
protected int txGroupId;
// For JAX-RS use
@Schema(accessMode = AccessMode.READ_ONLY, hidden = true, description = "height of block containing transaction")
@ -66,26 +64,27 @@ public abstract class TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected TransactionData() {
}
// For JAX-RS
// For JAXB
protected TransactionData(TransactionType type) {
this.type = type;
}
public TransactionData(TransactionType type, BigDecimal fee, byte[] creatorPublicKey, long timestamp, byte[] reference, byte[] signature) {
this.fee = fee;
public TransactionData(TransactionType type, long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) {
this.type = type;
this.creatorPublicKey = creatorPublicKey;
this.timestamp = timestamp;
this.txGroupId = txGroupId;
this.reference = reference;
this.creatorPublicKey = creatorPublicKey;
this.fee = fee;
this.signature = signature;
}
public TransactionData(TransactionType type, BigDecimal fee, byte[] creatorPublicKey, long timestamp, byte[] reference) {
this(type, fee, creatorPublicKey, timestamp, reference, null);
public TransactionData(TransactionType type, long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee) {
this(type, timestamp, txGroupId, reference, creatorPublicKey, fee, null);
}
// Getters/setters
@ -94,18 +93,27 @@ public abstract class TransactionData {
return this.type;
}
public byte[] getCreatorPublicKey() {
return this.creatorPublicKey;
}
public long getTimestamp() {
return this.timestamp;
}
public int getTxGroupId() {
return this.txGroupId;
}
public byte[] getReference() {
return this.reference;
}
public byte[] getCreatorPublicKey() {
return this.creatorPublicKey;
}
@XmlTransient
public void setCreatorPublicKey(byte[] creatorPublicKey) {
this.creatorPublicKey = creatorPublicKey;
}
public BigDecimal getFee() {
return this.fee;
}
@ -125,11 +133,6 @@ public abstract class TransactionData {
return Crypto.toAddress(this.creatorPublicKey);
}
@XmlTransient
public void setCreatorPublicKey(byte[] creatorPublicKey) {
this.creatorPublicKey = creatorPublicKey;
}
@XmlTransient
public void setBlockHeight(int blockHeight) {
this.blockHeight = blockHeight;

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@ -9,7 +10,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class TransferAssetTransactionData extends TransactionData {
@ -22,13 +23,18 @@ public class TransferAssetTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected TransferAssetTransactionData() {
super(TransactionType.TRANSFER_ASSET);
}
public TransferAssetTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, long assetId, BigDecimal fee, long timestamp,
byte[] reference, byte[] signature) {
super(TransactionType.TRANSFER_ASSET, fee, senderPublicKey, timestamp, reference, signature);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.senderPublicKey;
}
public TransferAssetTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, String recipient, BigDecimal amount,
long assetId, BigDecimal fee, byte[] signature) {
super(TransactionType.TRANSFER_ASSET, timestamp, txGroupId, reference, senderPublicKey, fee, signature);
this.senderPublicKey = senderPublicKey;
this.recipient = recipient;
@ -36,9 +42,9 @@ public class TransferAssetTransactionData extends TransactionData {
this.assetId = assetId;
}
public TransferAssetTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, long assetId, BigDecimal fee, long timestamp,
byte[] reference) {
this(senderPublicKey, recipient, amount, assetId, fee, timestamp, reference, null);
public TransferAssetTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, String recipient, BigDecimal amount,
long assetId, BigDecimal fee) {
this(timestamp, txGroupId, reference, senderPublicKey, recipient, amount, assetId, fee, null);
}
// Getters/setters

View File

@ -7,35 +7,61 @@ import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlTransient;
import org.qora.group.Group.ApprovalThreshold;
import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
@Schema(
allOf = {
TransactionData.class
}
)
public class UpdateGroupTransactionData extends TransactionData {
// Properties
@Schema(description = "owner's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
@Schema(
description = "owner's public key",
example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP"
)
private byte[] ownerPublicKey;
@Schema(description = "new owner's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v")
@Schema(
description = "new owner's address",
example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v"
)
private String newOwner;
@Schema(description = "which group to update", example = "my-group")
@Schema(
description = "which group to update",
example = "my-group"
)
private int groupId;
@Schema(description = "replacement group description", example = "my group for accounts I like")
@Schema(
description = "replacement group description",
example = "my group for accounts I like"
)
private String newDescription;
@Schema(description = "new group join policy", example = "true")
@Schema(
description = "new group join policy",
example = "true"
)
private boolean newIsOpen;
@Schema(
description = "new group member transaction approval threshold"
)
private ApprovalThreshold newApprovalThreshold;
/** Reference to CREATE_GROUP or UPDATE_GROUP transaction, used to rebuild group during orphaning. */
// For internal use when orphaning
@XmlTransient
@Schema(hidden = true)
@Schema(
hidden = true
)
private byte[] groupReference;
// Constructors
// For JAX-RS
// For JAXB
protected UpdateGroupTransactionData() {
super(TransactionType.UPDATE_GROUP);
}
@ -44,22 +70,23 @@ public class UpdateGroupTransactionData extends TransactionData {
this.creatorPublicKey = this.ownerPublicKey;
}
public UpdateGroupTransactionData(byte[] ownerPublicKey, int groupId, String newOwner, String newDescription, boolean newIsOpen, byte[] groupReference, BigDecimal fee, long timestamp,
byte[] reference, byte[] signature) {
super(TransactionType.UPDATE_GROUP, fee, ownerPublicKey, timestamp, reference, signature);
public UpdateGroupTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, int groupId,
String newOwner, String newDescription, boolean newIsOpen, ApprovalThreshold newApprovalThreshold, byte[] groupReference, BigDecimal fee, byte[] signature) {
super(TransactionType.UPDATE_GROUP, timestamp, txGroupId, reference, ownerPublicKey, fee, signature);
this.ownerPublicKey = ownerPublicKey;
this.newOwner = newOwner;
this.groupId = groupId;
this.newDescription = newDescription;
this.newIsOpen = newIsOpen;
this.newApprovalThreshold = newApprovalThreshold;
this.groupReference = groupReference;
}
/** Constructor typically used after deserialization */
public UpdateGroupTransactionData(byte[] ownerPublicKey, int groupId, String newOwner, String newDescription, boolean newIsOpen, BigDecimal fee, long timestamp, byte[] reference,
byte[] signature) {
this(ownerPublicKey, groupId, newOwner, newDescription, newIsOpen, null, fee, timestamp, reference, signature);
public UpdateGroupTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, int groupId,
String newOwner, String newDescription, boolean newIsOpen, ApprovalThreshold newApprovalThreshold, BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, ownerPublicKey, groupId, newOwner, newDescription, newIsOpen, newApprovalThreshold, null, fee, signature);
}
// Getters / setters
@ -84,6 +111,10 @@ public class UpdateGroupTransactionData extends TransactionData {
return this.newIsOpen;
}
public ApprovalThreshold getNewApprovalThreshold() {
return this.newApprovalThreshold;
}
public byte[] getGroupReference() {
return this.groupReference;
}

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlTransient;
@ -10,7 +11,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class UpdateNameTransactionData extends TransactionData {
@ -31,14 +32,18 @@ public class UpdateNameTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected UpdateNameTransactionData() {
super(TransactionType.UPDATE_NAME);
}
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);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.ownerPublicKey;
}
public UpdateNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, String newOwner, String name, String newData,
byte[] nameReference, BigDecimal fee, byte[] signature) {
super(TransactionType.UPDATE_NAME, timestamp, txGroupId, reference, ownerPublicKey, fee, signature);
this.ownerPublicKey = ownerPublicKey;
this.newOwner = newOwner;
@ -47,14 +52,14 @@ public class UpdateNameTransactionData extends TransactionData {
this.nameReference = nameReference;
}
public UpdateNameTransactionData(byte[] ownerPublicKey, String newOwner, String name, String newData, BigDecimal fee, long timestamp, byte[] reference,
byte[] signature) {
this(ownerPublicKey, newOwner, name, newData, null, fee, timestamp, reference, signature);
public UpdateNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, String newOwner, String name, String newData,
BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, ownerPublicKey, newOwner, name, newData, null, fee, signature);
}
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);
public UpdateNameTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] ownerPublicKey, String newOwner, String name, String newData,
byte[] nameReference, BigDecimal fee) {
this(timestamp, txGroupId, reference, ownerPublicKey, newOwner, name, newData, nameReference, fee, null);
}
// Getters / setters

View File

@ -2,6 +2,7 @@ package org.qora.data.transaction;
import java.math.BigDecimal;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@ -9,7 +10,7 @@ import org.qora.transaction.Transaction.TransactionType;
import io.swagger.v3.oas.annotations.media.Schema;
// All properties to be converted to JSON via JAX-RS
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@Schema(allOf = { TransactionData.class })
public class VoteOnPollTransactionData extends TransactionData {
@ -22,13 +23,18 @@ public class VoteOnPollTransactionData extends TransactionData {
// Constructors
// For JAX-RS
// For JAXB
protected VoteOnPollTransactionData() {
super(TransactionType.VOTE_ON_POLL);
}
public VoteOnPollTransactionData(byte[] voterPublicKey, String pollName, int optionIndex, Integer previousOptionIndex, BigDecimal fee, long timestamp,
byte[] reference, byte[] signature) {
super(TransactionType.VOTE_ON_POLL, fee, voterPublicKey, timestamp, reference, signature);
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.creatorPublicKey = this.voterPublicKey;
}
public VoteOnPollTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] voterPublicKey, String pollName, int optionIndex,
Integer previousOptionIndex, BigDecimal fee, byte[] signature) {
super(TransactionType.VOTE_ON_POLL, timestamp, txGroupId, reference, voterPublicKey, fee, signature);
this.voterPublicKey = voterPublicKey;
this.pollName = pollName;
@ -36,13 +42,13 @@ public class VoteOnPollTransactionData extends TransactionData {
this.previousOptionIndex = previousOptionIndex;
}
public VoteOnPollTransactionData(byte[] voterPublicKey, String pollName, int optionIndex, BigDecimal fee, long timestamp, byte[] reference,
byte[] signature) {
this(voterPublicKey, pollName, optionIndex, null, fee, timestamp, reference, signature);
public VoteOnPollTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] voterPublicKey, String pollName, int optionIndex,
BigDecimal fee, byte[] signature) {
this(timestamp, txGroupId, reference, voterPublicKey, pollName, optionIndex, null, fee, signature);
}
public VoteOnPollTransactionData(byte[] voterPublicKey, String pollName, int optionIndex, BigDecimal fee, long timestamp, byte[] reference) {
this(voterPublicKey, pollName, optionIndex, null, fee, timestamp, reference, null);
public VoteOnPollTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] voterPublicKey, String pollName, int optionIndex, BigDecimal fee) {
this(timestamp, txGroupId, reference, voterPublicKey, pollName, optionIndex, null, fee, null);
}
// Getters / setters

View File

@ -1,6 +1,10 @@
package org.qora.group;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;
import java.util.Arrays;
import java.util.Map;
import org.qora.account.Account;
import org.qora.account.PublicKeyAccount;
@ -28,12 +32,68 @@ import org.qora.repository.Repository;
public class Group {
/** Group-admin quora threshold for approving transactions */
public enum ApprovalThreshold {
// NOTE: value needs to fit into byte
NONE(0, false),
ONE(1, false),
PCT20(20, true),
PCT40(40, true),
PCT60(60, true),
PCT80(80, true),
PCT100(100, true);
public final int value;
public final boolean isPercentage;
private final static Map<Integer, ApprovalThreshold> map = stream(ApprovalThreshold.values()).collect(toMap(threshold -> threshold.value, threshold -> threshold));
ApprovalThreshold(int value, boolean isPercentage) {
this.value = value;
this.isPercentage = isPercentage;
}
public static ApprovalThreshold valueOf(int value) {
return map.get(value);
}
private boolean meetsTheshold(int currentApprovals, int totalAdmins) {
if (!this.isPercentage)
return currentApprovals >= this.value;
return currentApprovals >= (totalAdmins * 100 / this.value);
}
/**
* Returns whether transaction need approval.
*
* @param repository
* @param txGroupId transaction's groupID
* @param signature transaction's signature
* @return true if approval still needed, false if transaction can be included in block
* @throws DataException
*/
public boolean needsApproval(Repository repository, int txGroupId, byte[] signature) throws DataException {
// Fetch total number of admins in group
final int totalAdmins = repository.getGroupRepository().countGroupAdmins(txGroupId);
// Fetch total number of approvals for signature
// NOT simply number of GROUP_APPROVE transactions as some may be rejecting transaction, or changed opinions
final int currentApprovals = repository.getTransactionRepository().countTransactionApprovals(txGroupId, signature);
return !meetsTheshold(currentApprovals, totalAdmins);
}
}
// Properties
private Repository repository;
private GroupRepository groupRepository;
private GroupData groupData;
// Useful constants
public static final int NO_GROUP = -1;
public static final int DEFAULT_GROUP = 0;
public static final int MAX_NAME_SIZE = 32;
public static final int MAX_DESCRIPTION_SIZE = 128;
/** Max size of kick/ban reason */
@ -53,7 +113,7 @@ public class Group {
this.groupData = new GroupData(createGroupTransactionData.getOwner(), createGroupTransactionData.getGroupName(),
createGroupTransactionData.getDescription(), createGroupTransactionData.getTimestamp(), createGroupTransactionData.getIsOpen(),
createGroupTransactionData.getSignature());
createGroupTransactionData.getApprovalThreshold(), createGroupTransactionData.getSignature());
}
/**
@ -253,6 +313,7 @@ public class Group {
this.groupData.setOwner(updateGroupTransactionData.getNewOwner());
this.groupData.setDescription(updateGroupTransactionData.getNewDescription());
this.groupData.setIsOpen(updateGroupTransactionData.getNewIsOpen());
this.groupData.setApprovalThreshold(updateGroupTransactionData.getNewApprovalThreshold());
this.groupData.setUpdated(updateGroupTransactionData.getTimestamp());
// Save updated group data
@ -311,6 +372,7 @@ public class Group {
this.groupData.setOwner(previousCreateGroupTransactionData.getOwner());
this.groupData.setDescription(previousCreateGroupTransactionData.getDescription());
this.groupData.setIsOpen(previousCreateGroupTransactionData.getIsOpen());
this.groupData.setApprovalThreshold(previousCreateGroupTransactionData.getApprovalThreshold());
this.groupData.setUpdated(null);
break;
@ -319,6 +381,7 @@ public class Group {
this.groupData.setOwner(previousUpdateGroupTransactionData.getNewOwner());
this.groupData.setDescription(previousUpdateGroupTransactionData.getNewDescription());
this.groupData.setIsOpen(previousUpdateGroupTransactionData.getNewIsOpen());
this.groupData.setApprovalThreshold(previousUpdateGroupTransactionData.getNewApprovalThreshold());
this.groupData.setUpdated(previousUpdateGroupTransactionData.getTimestamp());
break;

View File

@ -9,11 +9,35 @@ public interface AccountRepository {
// General account
public void create(AccountData accountData) throws DataException;
/** Returns all general information about account, e.g. public key, last reference, default group ID. */
public AccountData getAccount(String address) throws DataException;
public void save(AccountData accountData) throws DataException;
/** Returns account's last reference or null if not set or account not found. */
public byte[] getLastReference(String address) throws DataException;
/** Returns account's default groupID or null if account not found. */
public Integer getDefaultGroupId(String address) throws DataException;
/**
* Ensures at least minimal account info in repository.
* <p>
* Saves account address, and public key if present.
*/
public void ensureAccount(AccountData accountData) throws DataException;
/**
* Saves account's last reference, and public key if present, in repository.
* <p>
* Note: ignores other fields like default group ID.
*/
public void setLastReference(AccountData accountData) throws DataException;
/**
* Saves account's default groupID, and public key if present, in repository.
* <p>
* Note: ignores other fields like last reference.
*/
public void setDefaultGroupId(AccountData accountData) throws DataException;
public void delete(String address) throws DataException;

View File

@ -3,6 +3,7 @@ package org.qora.repository;
import java.util.List;
import org.qora.api.resource.TransactionsResource.ConfirmationStatus;
import org.qora.data.transaction.GroupApprovalTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.transaction.Transaction.TransactionType;
@ -46,6 +47,20 @@ public interface TransactionRepository {
public List<TransactionData> getAssetTransactions(int assetId, ConfirmationStatus confirmationStatus, Integer limit, Integer offset, Boolean reverse)
throws DataException;
/** Returns number of approvals for transaction with given signature. */
public int countTransactionApprovals(int txGroupId, byte[] signature) throws DataException;
/**
* Returns list of latest approval decisions per admin for given pending transaction signature.
*
* @param signature
* @param adminPublicKey
* restrict results to decision by this admin, pass null for all admins' results
* @return
* @throws DataException
*/
public List<GroupApprovalTransactionData> getLatestApprovals(byte[] pendingSignature, byte[] adminPublicKey) throws DataException;
/**
* Returns whether transaction is confirmed or not.
*

View File

@ -22,7 +22,48 @@ public class HSQLDBAccountRepository implements AccountRepository {
// General account
@Override
public void create(AccountData accountData) throws DataException {
public AccountData getAccount(String address) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT reference, public_key, default_group_id FROM Accounts WHERE account = ?", address)) {
if (resultSet == null)
return null;
byte[] reference = resultSet.getBytes(1);
byte[] publicKey = resultSet.getBytes(2);
int defaultGroupId = resultSet.getInt(3);
return new AccountData(address, reference, publicKey, defaultGroupId);
} catch (SQLException e) {
throw new DataException("Unable to fetch account info from repository", e);
}
}
@Override
public byte[] getLastReference(String address) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT reference FROM Accounts WHERE account = ?", address)) {
if (resultSet == null)
return null;
return resultSet.getBytes(1);
} catch (SQLException e) {
throw new DataException("Unable to fetch account's last reference from repository", e);
}
}
@Override
public Integer getDefaultGroupId(String address) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT default_group_id FROM Accounts WHERE account = ?", address)) {
if (resultSet == null)
return null;
// Column is NOT NULL so this should never implicitly convert to 0
return resultSet.getInt(1);
} catch (SQLException e) {
throw new DataException("Unable to fetch account's default groupID from repository", e);
}
}
@Override
public void ensureAccount(AccountData accountData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
saveHelper.bind("account", accountData.getAddress());
@ -34,35 +75,41 @@ public class HSQLDBAccountRepository implements AccountRepository {
try {
saveHelper.execute(this.repository);
} catch (SQLException e) {
throw new DataException("Unable to create account in repository", e);
throw new DataException("Unable to ensure minimal account in repository", e);
}
}
@Override
public AccountData getAccount(String address) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT reference, public_key FROM Accounts WHERE account = ?", address)) {
if (resultSet == null)
return null;
byte[] reference = resultSet.getBytes(1);
byte[] publicKey = resultSet.getBytes(2);
return new AccountData(address, reference, publicKey);
} catch (SQLException e) {
throw new DataException("Unable to fetch account info from repository", e);
}
}
@Override
public void save(AccountData accountData) throws DataException {
public void setLastReference(AccountData accountData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
saveHelper.bind("account", accountData.getAddress()).bind("reference", accountData.getReference()).bind("public_key", accountData.getPublicKey());
saveHelper.bind("account", accountData.getAddress()).bind("reference", accountData.getReference());
byte[] publicKey = accountData.getPublicKey();
if (publicKey != null)
saveHelper.bind("public_key", publicKey);
try {
saveHelper.execute(this.repository);
} catch (SQLException e) {
throw new DataException("Unable to save account info into repository", e);
throw new DataException("Unable to save account's last reference into repository", e);
}
}
@Override
public void setDefaultGroupId(AccountData accountData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
saveHelper.bind("account", accountData.getAddress()).bind("default_group_id", accountData.getDefaultGroupId());
byte[] publicKey = accountData.getPublicKey();
if (publicKey != null)
saveHelper.bind("public_key", publicKey);
try {
saveHelper.execute(this.repository);
} catch (SQLException e) {
throw new DataException("Unable to save account's default group ID into repository", e);
}
}

View File

@ -5,8 +5,13 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class HSQLDBDatabaseUpdates {
private static final Logger LOGGER = LogManager.getLogger(HSQLDBDatabaseUpdates.class);
/**
* Apply any incremental changes to database schema.
*
@ -526,6 +531,33 @@ public class HSQLDBDatabaseUpdates {
stmt.execute("ALTER TABLE PeersTEMP RENAME TO Peers");
break;
case 33:
// Add groupID to all transactions - groupID 0 is default, which means groupless/no-group
stmt.execute("ALTER TABLE Transactions ADD COLUMN tx_group_id GroupID NOT NULL DEFAULT 0");
stmt.execute("CREATE INDEX TransactionGroupIndex ON Transactions (tx_group_id)");
// Adding approval to group-based transactions
// Default approval threshold is 100% for existing groups but probably of no effect in production
stmt.execute("ALTER TABLE Groups ADD COLUMN approval_threshold TINYINT NOT NULL DEFAULT 100 BEFORE reference");
stmt.execute("ALTER TABLE CreateGroupTransactions ADD COLUMN approval_threshold TINYINT NOT NULL DEFAULT 100 BEFORE group_id");
stmt.execute("ALTER TABLE UpdateGroupTransactions ADD COLUMN new_approval_threshold TINYINT NOT NULL DEFAULT 100 BEFORE group_reference");
// Approval transactions themselves
// "pending_signature" contains signature of pending transaction requiring approval
// "prior_reference" contains signature of previous approval transaction for orphaning purposes
stmt.execute("CREATE TABLE GroupApprovalTransactions (signature Signature, admin QoraPublicKey NOT NULL, pending_signature Signature NOT NULL, approval BOOLEAN NOT NULL, "
+ "prior_reference Signature, PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
// Accounts have a default groupID to be used if transaction's txGroupId is 0
stmt.execute("ALTER TABLE Accounts add default_group_id GroupID NOT NULL DEFAULT 0");
break;
case 34:
// SET_GROUP transaction support
stmt.execute("CREATE TABLE SetGroupTransactions (signature Signature, default_group_id GroupID NOT NULL, previous_default_group_id GroupID, "
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
break;
default:
// nothing to do
return false;
@ -533,6 +565,7 @@ public class HSQLDBDatabaseUpdates {
}
// database was updated
LOGGER.info(String.format("HSQLDB repository updated to version %d", databaseVersion + 1));
return true;
}

View File

@ -13,6 +13,7 @@ import org.qora.data.group.GroupData;
import org.qora.data.group.GroupInviteData;
import org.qora.data.group.GroupJoinRequestData;
import org.qora.data.group.GroupMemberData;
import org.qora.group.Group.ApprovalThreshold;
import org.qora.repository.DataException;
import org.qora.repository.GroupRepository;
@ -29,7 +30,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
@Override
public GroupData fromGroupId(int groupId) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT group_name, owner, description, created, updated, reference, is_open FROM Groups WHERE group_id = ?", groupId)) {
.checkedExecute("SELECT group_name, owner, description, created, updated, reference, is_open, approval_threshold FROM Groups WHERE group_id = ?", groupId)) {
if (resultSet == null)
return null;
@ -45,7 +46,9 @@ public class HSQLDBGroupRepository implements GroupRepository {
byte[] reference = resultSet.getBytes(6);
boolean isOpen = resultSet.getBoolean(7);
return new GroupData(groupId, owner, groupName, description, created, updated, isOpen, reference);
ApprovalThreshold approvalThreshold = ApprovalThreshold.valueOf(resultSet.getInt(8));
return new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, reference);
} catch (SQLException e) {
throw new DataException("Unable to fetch group info from repository", e);
}
@ -54,7 +57,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
@Override
public GroupData fromGroupName(String groupName) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT group_id, owner, description, created, updated, reference, is_open FROM Groups WHERE group_name = ?", groupName)) {
.checkedExecute("SELECT group_id, owner, description, created, updated, reference, is_open, approval_threshold FROM Groups WHERE group_name = ?", groupName)) {
if (resultSet == null)
return null;
@ -70,7 +73,9 @@ public class HSQLDBGroupRepository implements GroupRepository {
byte[] reference = resultSet.getBytes(6);
boolean isOpen = resultSet.getBoolean(7);
return new GroupData(groupId, owner, groupName, description, created, updated, isOpen, reference);
ApprovalThreshold approvalThreshold = ApprovalThreshold.valueOf(resultSet.getInt(8));
return new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, reference);
} catch (SQLException e) {
throw new DataException("Unable to fetch group info from repository", e);
}
@ -96,7 +101,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
@Override
public List<GroupData> getAllGroups(Integer limit, Integer offset, Boolean reverse) throws DataException {
String sql = "SELECT group_id, owner, group_name, description, created, updated, reference, is_open FROM Groups ORDER BY group_name";
String sql = "SELECT group_id, owner, group_name, description, created, updated, reference, is_open, approval_threshold FROM Groups ORDER BY group_name";
if (reverse != null && reverse)
sql += " DESC";
sql += HSQLDBRepository.limitOffsetSql(limit, offset);
@ -121,7 +126,9 @@ public class HSQLDBGroupRepository implements GroupRepository {
byte[] reference = resultSet.getBytes(7);
boolean isOpen = resultSet.getBoolean(8);
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, reference));
ApprovalThreshold approvalThreshold = ApprovalThreshold.valueOf(resultSet.getInt(9));
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, reference));
} while (resultSet.next());
return groups;
@ -132,7 +139,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
@Override
public List<GroupData> getGroupsByOwner(String owner, Integer limit, Integer offset, Boolean reverse) throws DataException {
String sql = "SELECT group_id, group_name, description, created, updated, reference, is_open FROM Groups WHERE owner = ? ORDER BY group_name";
String sql = "SELECT group_id, group_name, description, created, updated, reference, is_open, approval_threshold FROM Groups WHERE owner = ? ORDER BY group_name";
if (reverse != null && reverse)
sql += " DESC";
sql += HSQLDBRepository.limitOffsetSql(limit, offset);
@ -156,7 +163,9 @@ public class HSQLDBGroupRepository implements GroupRepository {
byte[] reference = resultSet.getBytes(6);
boolean isOpen = resultSet.getBoolean(7);
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, reference));
ApprovalThreshold approvalThreshold = ApprovalThreshold.valueOf(resultSet.getInt(8));
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, reference));
} while (resultSet.next());
return groups;
@ -167,7 +176,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
@Override
public List<GroupData> getGroupsWithMember(String member, Integer limit, Integer offset, Boolean reverse) throws DataException {
String sql = "SELECT group_id, owner, group_name, description, created, updated, reference, is_open FROM Groups JOIN GroupMembers USING (group_id) WHERE address = ? ORDER BY group_name";
String sql = "SELECT group_id, owner, group_name, description, created, updated, reference, is_open, approval_threshold FROM Groups JOIN GroupMembers USING (group_id) WHERE address = ? ORDER BY group_name";
if (reverse != null && reverse)
sql += " DESC";
sql += HSQLDBRepository.limitOffsetSql(limit, offset);
@ -192,7 +201,9 @@ public class HSQLDBGroupRepository implements GroupRepository {
byte[] reference = resultSet.getBytes(7);
boolean isOpen = resultSet.getBoolean(8);
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, reference));
ApprovalThreshold approvalThreshold = ApprovalThreshold.valueOf(resultSet.getInt(9));
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, reference));
} while (resultSet.next());
return groups;
@ -211,7 +222,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
saveHelper.bind("group_id", groupData.getGroupId()).bind("owner", groupData.getOwner()).bind("group_name", groupData.getGroupName())
.bind("description", groupData.getDescription()).bind("created", new Timestamp(groupData.getCreated())).bind("updated", updatedTimestamp)
.bind("reference", groupData.getReference()).bind("is_open", groupData.getIsOpen());
.bind("reference", groupData.getReference()).bind("is_open", groupData.getIsOpen()).bind("approval_threshold", groupData.getApprovalThreshold().value);
try {
saveHelper.execute(this.repository);

View File

@ -7,8 +7,8 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.TimeZone;
import org.apache.logging.log4j.LogManager;
@ -36,13 +36,13 @@ public class HSQLDBRepository implements Repository {
public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
protected Connection connection;
protected List<Savepoint> savepoints;
protected Deque<Savepoint> savepoints;
protected boolean debugState = false;
// NB: no visibility modifier so only callable from within same package
HSQLDBRepository(Connection connection) {
this.connection = connection;
this.savepoints = new ArrayList<>();
this.savepoints = new ArrayDeque<>(3);
}
@Override
@ -116,7 +116,7 @@ public class HSQLDBRepository implements Repository {
public void setSavepoint() throws DataException {
try {
Savepoint savepoint = this.connection.setSavepoint();
this.savepoints.add(savepoint);
this.savepoints.push(savepoint);
} catch (SQLException e) {
throw new DataException("savepoint error", e);
}
@ -127,8 +127,7 @@ public class HSQLDBRepository implements Repository {
if (this.savepoints.isEmpty())
throw new DataException("no savepoint to rollback");
Savepoint savepoint = this.savepoints.get(0);
this.savepoints.remove(0);
Savepoint savepoint = this.savepoints.pop();
try {
this.connection.rollback(savepoint);

View File

@ -16,7 +16,7 @@ public class HSQLDBAddGroupAdminTransactionRepository extends HSQLDBTransactionR
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT group_id, address FROM AddGroupAdminTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
@ -24,7 +24,7 @@ public class HSQLDBAddGroupAdminTransactionRepository extends HSQLDBTransactionR
int groupId = resultSet.getInt(1);
String member = resultSet.getString(2);
return new AddGroupAdminTransactionData(creatorPublicKey, groupId, member, fee, timestamp, reference, signature);
return new AddGroupAdminTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, member, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch add group admin transaction from repository", e);
}

View File

@ -19,21 +19,20 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT sender, version, service, data_hash from ArbitraryTransactions WHERE signature = ?",
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT version, service, data_hash from ArbitraryTransactions WHERE signature = ?",
signature)) {
if (resultSet == null)
return null;
byte[] senderPublicKey = resultSet.getBytes(1);
int version = resultSet.getInt(2);
int service = resultSet.getInt(3);
byte[] dataHash = resultSet.getBytes(4);
int version = resultSet.getInt(1);
int service = resultSet.getInt(2);
byte[] dataHash = resultSet.getBytes(3);
List<PaymentData> payments = this.getPaymentsFromSignature(signature);
return new ArbitraryTransactionData(version, senderPublicKey, service, dataHash, DataType.DATA_HASH, payments, fee, timestamp, reference,
signature);
return new ArbitraryTransactionData(timestamp, txGroupId, reference, creatorPublicKey, version, service, dataHash, DataType.DATA_HASH, payments,
fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch arbitrary transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBAtTransactionRepository extends HSQLDBTransactionRepository {
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT AT_address, recipient, amount, asset_id, message FROM ATTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
@ -37,7 +37,7 @@ public class HSQLDBAtTransactionRepository extends HSQLDBTransactionRepository {
if (resultSet.wasNull())
message = null;
return new ATTransactionData(atAddress, recipient, amount, assetId, message, fee, timestamp, reference, signature);
return new ATTransactionData(timestamp, txGroupId, reference, atAddress, recipient, amount, assetId, message, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch AT transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBBuyNameTransactionRepository extends HSQLDBTransactionReposit
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] buyerPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT name, amount, seller, name_reference FROM BuyNameTransactions WHERE signature = ?",
signature)) {
if (resultSet == null)
@ -27,7 +27,7 @@ public class HSQLDBBuyNameTransactionRepository extends HSQLDBTransactionReposit
String seller = resultSet.getString(3);
byte[] nameReference = resultSet.getBytes(4);
return new BuyNameTransactionData(buyerPublicKey, name, amount, seller, nameReference, fee, timestamp, reference, signature);
return new BuyNameTransactionData(timestamp, txGroupId, reference, creatorPublicKey, name, amount, seller, nameReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch buy name transaction from repository", e);
}

View File

@ -16,14 +16,14 @@ public class HSQLDBCancelAssetOrderTransactionRepository extends HSQLDBTransacti
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT asset_order_id FROM CancelAssetOrderTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
byte[] assetOrderId = resultSet.getBytes(1);
return new CancelAssetOrderTransactionData(creatorPublicKey, assetOrderId, fee, timestamp, reference, signature);
return new CancelAssetOrderTransactionData(timestamp, txGroupId, reference, creatorPublicKey, assetOrderId, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch cancel order transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBCancelGroupBanTransactionRepository extends HSQLDBTransaction
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT group_id, address, ban_reference FROM CancelGroupBanTransactions WHERE signature = ?",
signature)) {
if (resultSet == null)
@ -26,7 +26,7 @@ public class HSQLDBCancelGroupBanTransactionRepository extends HSQLDBTransaction
String member = resultSet.getString(2);
byte[] banReference = resultSet.getBytes(3);
return new CancelGroupBanTransactionData(creatorPublicKey, groupId, member, banReference, fee, timestamp, reference, signature);
return new CancelGroupBanTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, member, banReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch group unban transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBCancelGroupInviteTransactionRepository extends HSQLDBTransact
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT group_id, invitee, invite_reference FROM CancelGroupInviteTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
@ -26,7 +26,7 @@ public class HSQLDBCancelGroupInviteTransactionRepository extends HSQLDBTransact
String invitee = resultSet.getString(2);
byte[] inviteReference = resultSet.getBytes(3);
return new CancelGroupInviteTransactionData(creatorPublicKey, groupId, invitee, inviteReference, fee, timestamp, reference, signature);
return new CancelGroupInviteTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, invitee, inviteReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch cancel group invite transaction from repository", e);
}

View File

@ -16,14 +16,14 @@ public class HSQLDBCancelSellNameTransactionRepository extends HSQLDBTransaction
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT name FROM CancelSellNameTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
String name = resultSet.getString(1);
return new CancelSellNameTransactionData(ownerPublicKey, name, fee, timestamp, reference, signature);
return new CancelSellNameTransactionData(timestamp, txGroupId, reference, creatorPublicKey, name, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch cancel sell name transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBCreateAssetOrderTransactionRepository extends HSQLDBTransacti
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT have_asset_id, amount, want_asset_id, price FROM CreateAssetOrderTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
@ -27,7 +27,7 @@ public class HSQLDBCreateAssetOrderTransactionRepository extends HSQLDBTransacti
long wantAssetId = resultSet.getLong(3);
BigDecimal price = resultSet.getBigDecimal(4);
return new CreateAssetOrderTransactionData(creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, timestamp, reference, signature);
return new CreateAssetOrderTransactionData(timestamp, txGroupId, reference, creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch create order transaction from repository", e);
}

View File

@ -6,6 +6,7 @@ import java.sql.SQLException;
import org.qora.data.transaction.CreateGroupTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.group.Group.ApprovalThreshold;
import org.qora.repository.DataException;
import org.qora.repository.hsqldb.HSQLDBRepository;
import org.qora.repository.hsqldb.HSQLDBSaver;
@ -16,9 +17,9 @@ public class HSQLDBCreateGroupTransactionRepository extends HSQLDBTransactionRep
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT owner, group_name, description, is_open, group_id FROM CreateGroupTransactions WHERE signature = ?", signature)) {
.checkedExecute("SELECT owner, group_name, description, is_open, approval_threshold, group_id FROM CreateGroupTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
@ -27,11 +28,14 @@ public class HSQLDBCreateGroupTransactionRepository extends HSQLDBTransactionRep
String description = resultSet.getString(3);
boolean isOpen = resultSet.getBoolean(4);
Integer groupId = resultSet.getInt(5);
ApprovalThreshold approvalThreshold = ApprovalThreshold.valueOf(resultSet.getInt(5));
Integer groupId = resultSet.getInt(6);
if (resultSet.wasNull())
groupId = null;
return new CreateGroupTransactionData(creatorPublicKey, owner, groupName, description, isOpen, groupId, fee, timestamp, reference, signature);
return new CreateGroupTransactionData(timestamp, txGroupId, reference, creatorPublicKey, owner, groupName, description, isOpen, approvalThreshold,
groupId, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch create group transaction from repository", e);
}
@ -46,6 +50,7 @@ public class HSQLDBCreateGroupTransactionRepository extends HSQLDBTransactionRep
saveHelper.bind("signature", createGroupTransactionData.getSignature()).bind("creator", createGroupTransactionData.getCreatorPublicKey())
.bind("owner", createGroupTransactionData.getOwner()).bind("group_name", createGroupTransactionData.getGroupName())
.bind("description", createGroupTransactionData.getDescription()).bind("is_open", createGroupTransactionData.getIsOpen())
.bind("approval_threshold", createGroupTransactionData.getApprovalThreshold().value)
.bind("group_id", createGroupTransactionData.getGroupId());
try {

View File

@ -19,7 +19,7 @@ public class HSQLDBCreatePollTransactionRepository extends HSQLDBTransactionRepo
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT owner, poll_name, description FROM CreatePollTransactions WHERE signature = ?",
signature)) {
if (resultSet == null)
@ -43,7 +43,7 @@ public class HSQLDBCreatePollTransactionRepository extends HSQLDBTransactionRepo
pollOptions.add(new PollOptionData(optionName));
} while (optionsResultSet.next());
return new CreatePollTransactionData(creatorPublicKey, owner, pollName, description, pollOptions, fee, timestamp, reference, signature);
return new CreatePollTransactionData(timestamp, txGroupId, reference, creatorPublicKey, owner, pollName, description, pollOptions, fee, signature);
}
} catch (SQLException e) {
throw new DataException("Unable to fetch create poll transaction from repository", e);

View File

@ -16,7 +16,7 @@ public class HSQLDBDeployAtTransactionRepository extends HSQLDBTransactionReposi
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute(
"SELECT AT_name, description, AT_type, AT_tags, creation_bytes, amount, asset_id, AT_address FROM DeployATTransactions WHERE signature = ?",
signature)) {
@ -25,19 +25,19 @@ public class HSQLDBDeployAtTransactionRepository extends HSQLDBTransactionReposi
String name = resultSet.getString(1);
String description = resultSet.getString(2);
String ATType = resultSet.getString(3);
String atType = resultSet.getString(3);
String tags = resultSet.getString(4);
byte[] creationBytes = resultSet.getBytes(5);
BigDecimal amount = resultSet.getBigDecimal(6).setScale(8);
long assetId = resultSet.getLong(7);
// Special null-checking for AT address
String ATAddress = resultSet.getString(8);
String atAddress = resultSet.getString(8);
if (resultSet.wasNull())
ATAddress = null;
atAddress = null;
return new DeployAtTransactionData(ATAddress, creatorPublicKey, name, description, ATType, tags, creationBytes, amount, assetId, fee, timestamp,
reference, signature);
return new DeployAtTransactionData(timestamp, txGroupId, reference, creatorPublicKey, atAddress, name, description, atType, tags, creationBytes, amount,
assetId, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch deploy AT transaction from repository", e);
}
@ -51,9 +51,9 @@ public class HSQLDBDeployAtTransactionRepository extends HSQLDBTransactionReposi
saveHelper.bind("signature", deployATTransactionData.getSignature()).bind("creator", deployATTransactionData.getCreatorPublicKey())
.bind("AT_name", deployATTransactionData.getName()).bind("description", deployATTransactionData.getDescription())
.bind("AT_type", deployATTransactionData.getATType()).bind("AT_tags", deployATTransactionData.getTags())
.bind("AT_type", deployATTransactionData.getAtType()).bind("AT_tags", deployATTransactionData.getTags())
.bind("creation_bytes", deployATTransactionData.getCreationBytes()).bind("amount", deployATTransactionData.getAmount())
.bind("asset_id", deployATTransactionData.getAssetId()).bind("AT_address", deployATTransactionData.getATAddress());
.bind("asset_id", deployATTransactionData.getAssetId()).bind("AT_address", deployATTransactionData.getAtAddress());
try {
saveHelper.execute(this.repository);

View File

@ -16,7 +16,7 @@ public class HSQLDBGenesisTransactionRepository extends HSQLDBTransactionReposit
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT recipient, amount, asset_id FROM GenesisTransactions WHERE signature = ?",
signature)) {
if (resultSet == null)
@ -26,7 +26,7 @@ public class HSQLDBGenesisTransactionRepository extends HSQLDBTransactionReposit
BigDecimal amount = resultSet.getBigDecimal(2).setScale(8);
long assetId = resultSet.getLong(3);
return new GenesisTransactionData(recipient, amount, assetId, timestamp, signature);
return new GenesisTransactionData(timestamp, recipient, amount, assetId, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch genesis transaction from repository", e);
}

View File

@ -0,0 +1,53 @@
package org.qora.repository.hsqldb.transaction;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.qora.data.transaction.GroupApprovalTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.hsqldb.HSQLDBRepository;
import org.qora.repository.hsqldb.HSQLDBSaver;
public class HSQLDBGroupApprovalTransactionRepository extends HSQLDBTransactionRepository {
public HSQLDBGroupApprovalTransactionRepository(HSQLDBRepository repository) {
this.repository = repository;
}
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT pending_signature, approval, prior_reference FROM GroupApprovalTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
byte[] pendingSignature = resultSet.getBytes(1);
boolean approval = resultSet.getBoolean(2);
byte[] priorReference = resultSet.getBytes(3);
return new GroupApprovalTransactionData(timestamp, txGroupId, reference, creatorPublicKey, pendingSignature, approval, priorReference, fee,
signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch group approval transaction from repository", e);
}
}
@Override
public void save(TransactionData transactionData) throws DataException {
GroupApprovalTransactionData groupApprovalTransactionData = (GroupApprovalTransactionData) transactionData;
HSQLDBSaver saveHelper = new HSQLDBSaver("GroupApprovalTransactions");
saveHelper.bind("signature", groupApprovalTransactionData.getSignature()).bind("admin", groupApprovalTransactionData.getAdminPublicKey())
.bind("pending_signature", groupApprovalTransactionData.getPendingSignature()).bind("approval", groupApprovalTransactionData.getApproval())
.bind("prior_reference", groupApprovalTransactionData.getPriorReference());
try {
saveHelper.execute(this.repository);
} catch (SQLException e) {
throw new DataException("Unable to save group approval transaction into repository", e);
}
}
}

View File

@ -16,7 +16,7 @@ public class HSQLDBGroupBanTransactionRepository extends HSQLDBTransactionReposi
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute(
"SELECT group_id, address, reason, time_to_live, member_reference, admin_reference, join_invite_reference FROM GroupBanTransactions WHERE signature = ?",
signature)) {
@ -31,8 +31,8 @@ public class HSQLDBGroupBanTransactionRepository extends HSQLDBTransactionReposi
byte[] adminReference = resultSet.getBytes(6);
byte[] joinInviteReference = resultSet.getBytes(7);
return new GroupBanTransactionData(creatorPublicKey, groupId, offender, reason, timeToLive, memberReference, adminReference, joinInviteReference,
fee, timestamp, reference, signature);
return new GroupBanTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, offender, reason, timeToLive,
memberReference, adminReference, joinInviteReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch group ban transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBGroupInviteTransactionRepository extends HSQLDBTransactionRep
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT group_id, invitee, time_to_live, join_reference FROM GroupInviteTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
@ -27,7 +27,7 @@ public class HSQLDBGroupInviteTransactionRepository extends HSQLDBTransactionRep
int timeToLive = resultSet.getInt(3);
byte[] joinReference = resultSet.getBytes(4);
return new GroupInviteTransactionData(creatorPublicKey, groupId, invitee, timeToLive, joinReference, fee, timestamp, reference, signature);
return new GroupInviteTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, invitee, timeToLive, joinReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch group invite transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBGroupKickTransactionRepository extends HSQLDBTransactionRepos
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute(
"SELECT group_id, address, reason, member_reference, admin_reference, join_reference FROM GroupKickTransactions WHERE signature = ?",
signature)) {
@ -30,8 +30,8 @@ public class HSQLDBGroupKickTransactionRepository extends HSQLDBTransactionRepos
byte[] adminReference = resultSet.getBytes(5);
byte[] joinReference = resultSet.getBytes(6);
return new GroupKickTransactionData(creatorPublicKey, groupId, member, reason, memberReference, adminReference, joinReference, fee, timestamp,
reference, signature);
return new GroupKickTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, member, reason, memberReference, adminReference,
joinReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch group kick transaction from repository", e);
}

View File

@ -16,26 +16,25 @@ public class HSQLDBIssueAssetTransactionRepository extends HSQLDBTransactionRepo
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute(
"SELECT issuer, owner, asset_name, description, quantity, is_divisible, asset_id FROM IssueAssetTransactions WHERE signature = ?", signature)) {
"SELECT owner, asset_name, description, quantity, is_divisible, asset_id FROM IssueAssetTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
byte[] issuerPublicKey = resultSet.getBytes(1);
String owner = resultSet.getString(2);
String assetName = resultSet.getString(3);
String description = resultSet.getString(4);
long quantity = resultSet.getLong(5);
boolean isDivisible = resultSet.getBoolean(6);
String owner = resultSet.getString(1);
String assetName = resultSet.getString(2);
String description = resultSet.getString(3);
long quantity = resultSet.getLong(4);
boolean isDivisible = resultSet.getBoolean(5);
// Special null-checking for asset ID
Long assetId = resultSet.getLong(7);
Long assetId = resultSet.getLong(6);
if (resultSet.wasNull())
assetId = null;
return new IssueAssetTransactionData(assetId, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference,
signature);
return new IssueAssetTransactionData(timestamp, txGroupId, reference, creatorPublicKey, assetId, owner, assetName, description, quantity, isDivisible,
fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch issue asset transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBJoinGroupTransactionRepository extends HSQLDBTransactionRepos
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT group_id, invite_reference FROM JoinGroupTransactions WHERE signature = ?",
signature)) {
if (resultSet == null)
@ -25,7 +25,7 @@ public class HSQLDBJoinGroupTransactionRepository extends HSQLDBTransactionRepos
int groupId = resultSet.getInt(1);
byte[] inviteReference = resultSet.getBytes(2);
return new JoinGroupTransactionData(creatorPublicKey, groupId, inviteReference, fee, timestamp, reference, signature);
return new JoinGroupTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, inviteReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch join group transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBLeaveGroupTransactionRepository extends HSQLDBTransactionRepo
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT group_id, member_reference, admin_reference FROM LeaveGroupTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
@ -26,7 +26,7 @@ public class HSQLDBLeaveGroupTransactionRepository extends HSQLDBTransactionRepo
byte[] memberReference = resultSet.getBytes(2);
byte[] adminReference = resultSet.getBytes(3);
return new LeaveGroupTransactionData(creatorPublicKey, groupId, memberReference, adminReference, fee, timestamp, reference, signature);
return new LeaveGroupTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, memberReference, adminReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch leave group transaction from repository", e);
}

View File

@ -16,28 +16,27 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute(
"SELECT version, sender, recipient, is_text, is_encrypted, amount, asset_id, data FROM MessageTransactions WHERE signature = ?", signature)) {
"SELECT version, recipient, is_text, is_encrypted, amount, asset_id, data FROM MessageTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
int version = resultSet.getInt(1);
byte[] senderPublicKey = resultSet.getBytes(2);
String recipient = resultSet.getString(3);
boolean isText = resultSet.getBoolean(4);
boolean isEncrypted = resultSet.getBoolean(5);
BigDecimal amount = resultSet.getBigDecimal(6);
String recipient = resultSet.getString(2);
boolean isText = resultSet.getBoolean(3);
boolean isEncrypted = resultSet.getBoolean(4);
BigDecimal amount = resultSet.getBigDecimal(5);
// Special null-checking for asset ID
Long assetId = resultSet.getLong(7);
Long assetId = resultSet.getLong(6);
if (resultSet.wasNull())
assetId = null;
byte[] data = resultSet.getBytes(8);
byte[] data = resultSet.getBytes(7);
return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference,
signature);
return new MessageTransactionData(timestamp, txGroupId, reference, creatorPublicKey, version, recipient, assetId, amount, data, isText, isEncrypted,
fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch message transaction from repository", e);
}

View File

@ -18,16 +18,14 @@ public class HSQLDBMultiPaymentTransactionRepository extends HSQLDBTransactionRe
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT sender from MultiPaymentTransactions WHERE signature = ?", signature)) {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT TRUE from MultiPaymentTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
byte[] senderPublicKey = resultSet.getBytes(1);
List<PaymentData> payments = this.getPaymentsFromSignature(signature);
return new MultiPaymentTransactionData(senderPublicKey, payments, fee, timestamp, reference, signature);
return new MultiPaymentTransactionData(timestamp, txGroupId, reference, creatorPublicKey, payments, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch multi-payment transaction from repository", e);
}

View File

@ -16,16 +16,15 @@ public class HSQLDBPaymentTransactionRepository extends HSQLDBTransactionReposit
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT sender, recipient, amount FROM PaymentTransactions WHERE signature = ?", signature)) {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT recipient, amount FROM PaymentTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
byte[] senderPublicKey = resultSet.getBytes(1);
String recipient = resultSet.getString(2);
BigDecimal amount = resultSet.getBigDecimal(3);
String recipient = resultSet.getString(1);
BigDecimal amount = resultSet.getBigDecimal(2);
return new PaymentTransactionData(senderPublicKey, recipient, amount, fee, timestamp, reference, signature);
return new PaymentTransactionData(timestamp, txGroupId, reference, creatorPublicKey, recipient, amount, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch payment transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBRegisterNameTransactionRepository extends HSQLDBTransactionRe
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] registrantPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT owner, name, data FROM RegisterNameTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
@ -25,7 +25,7 @@ public class HSQLDBRegisterNameTransactionRepository extends HSQLDBTransactionRe
String name = resultSet.getString(2);
String data = resultSet.getString(3);
return new RegisterNameTransactionData(registrantPublicKey, owner, name, data, fee, timestamp, reference, signature);
return new RegisterNameTransactionData(timestamp, txGroupId, reference, creatorPublicKey, owner, name, data, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch register name transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBRemoveGroupAdminTransactionRepository extends HSQLDBTransacti
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT group_id, admin, admin_reference FROM RemoveGroupAdminTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
@ -26,7 +26,7 @@ public class HSQLDBRemoveGroupAdminTransactionRepository extends HSQLDBTransacti
String admin = resultSet.getString(2);
byte[] adminReference = resultSet.getBytes(3);
return new RemoveGroupAdminTransactionData(creatorPublicKey, groupId, admin, adminReference, fee, timestamp, reference, signature);
return new RemoveGroupAdminTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, admin, adminReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch remove group admin transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBSellNameTransactionRepository extends HSQLDBTransactionReposi
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT name, amount FROM SellNameTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
@ -24,7 +24,7 @@ public class HSQLDBSellNameTransactionRepository extends HSQLDBTransactionReposi
String name = resultSet.getString(1);
BigDecimal amount = resultSet.getBigDecimal(2);
return new SellNameTransactionData(ownerPublicKey, name, amount, fee, timestamp, reference, signature);
return new SellNameTransactionData(timestamp, txGroupId, reference, creatorPublicKey, name, amount, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch sell name transaction from repository", e);
}

View File

@ -0,0 +1,52 @@
package org.qora.repository.hsqldb.transaction;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.qora.data.transaction.SetGroupTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.hsqldb.HSQLDBRepository;
import org.qora.repository.hsqldb.HSQLDBSaver;
public class HSQLDBSetGroupTransactionRepository extends HSQLDBTransactionRepository {
public HSQLDBSetGroupTransactionRepository(HSQLDBRepository repository) {
this.repository = repository;
}
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT default_group_id, previous_default_group_id FROM SetGroupTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
int defaultGroupId = resultSet.getInt(1);
Integer previousDefaultGroupId = resultSet.getInt(2);
if (resultSet.wasNull())
previousDefaultGroupId = null;
return new SetGroupTransactionData(timestamp, txGroupId, reference, creatorPublicKey, defaultGroupId, previousDefaultGroupId, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch set group transaction from repository", e);
}
}
@Override
public void save(TransactionData transactionData) throws DataException {
SetGroupTransactionData setGroupTransactionData = (SetGroupTransactionData) transactionData;
HSQLDBSaver saveHelper = new HSQLDBSaver("SetGroupTransactions");
saveHelper.bind("signature", setGroupTransactionData.getSignature()).bind("default_group_id", setGroupTransactionData.getDefaultGroupId())
.bind("previous_default_group_id", setGroupTransactionData.getPreviousDefaultGroupId());
try {
saveHelper.execute(this.repository);
} catch (SQLException e) {
throw new DataException("Unable to save set group transaction into repository", e);
}
}
}

View File

@ -17,6 +17,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qora.api.resource.TransactionsResource.ConfirmationStatus;
import org.qora.data.PaymentData;
import org.qora.data.transaction.GroupApprovalTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.TransactionRepository;
@ -60,8 +61,8 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
}
try {
subclassInfo.fromBaseMethod = subclassInfo.clazz.getDeclaredMethod("fromBase", byte[].class, byte[].class, byte[].class, long.class,
BigDecimal.class);
// params: long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature
subclassInfo.fromBaseMethod = subclassInfo.clazz.getDeclaredMethod("fromBase", long.class, int.class, byte[].class, byte[].class, BigDecimal.class, byte[].class);
} catch (IllegalArgumentException | SecurityException | NoSuchMethodException e) {
LOGGER.debug(String.format("HSQLDBTransactionRepository subclass's \"fromBase\" method not found for transaction type \"%s\"", txType.name()));
}
@ -110,7 +111,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
@Override
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, tx_group_id FROM Transactions WHERE signature = ?",
signature)) {
if (resultSet == null)
return null;
@ -120,8 +121,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
byte[] creatorPublicKey = resultSet.getBytes(3);
long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
BigDecimal fee = resultSet.getBigDecimal(5).setScale(8);
int txGroupId = resultSet.getInt(6);
TransactionData transactionData = this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
TransactionData transactionData = this.fromBase(type, timestamp, txGroupId, reference, creatorPublicKey, fee, signature);
return maybeIncludeBlockHeight(transactionData);
} catch (SQLException e) {
throw new DataException("Unable to fetch transaction from repository", e);
@ -130,7 +132,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
@Override
public TransactionData fromReference(byte[] reference) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute("SELECT type, signature, creator, creation, fee FROM Transactions WHERE reference = ?",
try (ResultSet resultSet = this.repository.checkedExecute("SELECT type, signature, creator, creation, fee, tx_group_id FROM Transactions WHERE reference = ?",
reference)) {
if (resultSet == null)
return null;
@ -140,8 +142,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
byte[] creatorPublicKey = resultSet.getBytes(3);
long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
BigDecimal fee = resultSet.getBigDecimal(5).setScale(8);
int txGroupId = resultSet.getInt(6);
TransactionData transactionData = this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
TransactionData transactionData = this.fromBase(type, timestamp, txGroupId, reference, creatorPublicKey, fee, signature);
return maybeIncludeBlockHeight(transactionData);
} catch (SQLException e) {
throw new DataException("Unable to fetch transaction from repository", e);
@ -172,14 +175,15 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
}
}
private TransactionData fromBase(TransactionType type, byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee)
private TransactionData fromBase(TransactionType type, long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature)
throws DataException {
HSQLDBTransactionRepository txRepository = repositoryByTxType[type.value];
if (txRepository == null)
throw new DataException("Unsupported transaction type [" + type.name() + "] during fetch from HSQLDB repository");
try {
return (TransactionData) subclassInfos[type.value].fromBaseMethod.invoke(txRepository, signature, reference, creatorPublicKey, timestamp, fee);
// params: long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature
return (TransactionData) subclassInfos[type.value].fromBaseMethod.invoke(txRepository, timestamp, txGroupId, reference, creatorPublicKey, fee, signature);
} catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
throw new DataException("Unsupported transaction type [" + type.name() + "] during fetch from HSQLDB repository");
}
@ -484,6 +488,77 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
}
}
@Override
public int countTransactionApprovals(int txGroupId, byte[] signature) throws DataException {
// Fetch total number of approvals for signature
// NOT simply number of GROUP_APPROVAL transactions as some may be rejecting transaction, or changed opinions
// Also make sure that GROUP_APPROVAL transaction's admin is still an admin of group
// Sub-query SQL to find latest GroupApprovalTransaction relating to passed signature
String latestApprovalSql = "SELECT admin AS creator, MAX(creation) AS creation FROM GroupApprovalTransactions NATURAL JOIN Transactions WHERE pending_signature = ? GROUP BY admin";
String sql = "SELECT COUNT(*) FROM "
+ "(" + latestApprovalSql + ") "
+ "NATURAL JOIN Transactions "
+ "NATURAL JOIN GroupApprovalTransactions "
+ "LEFT OUTER JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature "
+ "LEFT OUTER JOIN Accounts ON Accounts.public_key = GroupApprovalTransactions.admin "
+ "LEFT OUTER JOIN GroupAdmins ON GroupAdmins.admin = Accounts.account "
+ "WHERE approval = TRUE AND GroupAdmins.group_id = ?";
try (ResultSet resultSet = this.repository.checkedExecute(sql, signature, txGroupId)) {
return resultSet.getInt(1);
} catch (SQLException e) {
throw new DataException("Unable to count transaction group-admin approvals from repository", e);
}
}
@Override
public List<GroupApprovalTransactionData> getLatestApprovals(byte[] pendingSignature, byte[] adminPublicKey) throws DataException {
// Fetch latest approvals for signature
// NOT simply number of GROUP_APPROVAL transactions as some may be rejecting transaction, or changed opinions
// Also make sure that GROUP_APPROVAL transaction's admin is still an admin of group
Object[] bindArgs;
// Sub-query SQL to find latest GroupApprovalTransaction relating to passed signature
String latestApprovalSql = "SELECT admin AS creator, MAX(creation) AS creation FROM GroupApprovalTransactions NATURAL JOIN Transactions WHERE pending_signature = ? ";
if (adminPublicKey != null)
latestApprovalSql += "AND admin = ? ";
latestApprovalSql += "GROUP BY admin";
String sql = "SELECT signature FROM "
+ "(" + latestApprovalSql + ") "
+ "NATURAL JOIN Transactions "
+ "NATURAL JOIN GroupApprovalTransactions "
+ "LEFT OUTER JOIN BlockTransactions ON BlockTransactions.transaction_signature = Transactions.signature "
+ "LEFT OUTER JOIN Accounts ON Accounts.public_key = GroupApprovalTransactions.admin "
+ "LEFT OUTER JOIN GroupAdmins ON GroupAdmins.admin = Accounts.account "
+ "WHERE approval = TRUE AND GroupAdmins.group_id = Transactions.tx_group_id";
if (adminPublicKey != null)
bindArgs = new Object[] { pendingSignature, adminPublicKey };
else
bindArgs = new Object[] { pendingSignature };
List<GroupApprovalTransactionData> approvals = new ArrayList<>();
try (ResultSet resultSet = this.repository.checkedExecute(sql, bindArgs)) {
if (resultSet == null)
return approvals;
do {
byte[] signature = resultSet.getBytes(1);
approvals.add((GroupApprovalTransactionData) this.fromSignature(signature));
} while (resultSet.next());
return approvals;
} catch (SQLException e) {
throw new DataException("Unable to fetch latest transaction group-admin approvals from repository", e);
}
}
@Override
public boolean isConfirmed(byte[] signature) throws DataException {
try {
@ -553,7 +628,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
HSQLDBSaver saver = new HSQLDBSaver("Transactions");
saver.bind("signature", transactionData.getSignature()).bind("reference", transactionData.getReference()).bind("type", transactionData.getType().value)
.bind("creator", transactionData.getCreatorPublicKey()).bind("creation", new Timestamp(transactionData.getTimestamp()))
.bind("fee", transactionData.getFee()).bind("milestone_block", null);
.bind("fee", transactionData.getFee()).bind("milestone_block", null).bind("tx_group_id", transactionData.getTxGroupId());
try {
saver.execute(this.repository);
} catch (SQLException e) {

View File

@ -16,18 +16,17 @@ public class HSQLDBTransferAssetTransactionRepository extends HSQLDBTransactionR
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT sender, recipient, asset_id, amount FROM TransferAssetTransactions WHERE signature = ?", signature)) {
.checkedExecute("SELECT recipient, asset_id, amount FROM TransferAssetTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
return null;
byte[] senderPublicKey = resultSet.getBytes(1);
String recipient = resultSet.getString(2);
long assetId = resultSet.getLong(3);
BigDecimal amount = resultSet.getBigDecimal(4);
String recipient = resultSet.getString(1);
long assetId = resultSet.getLong(2);
BigDecimal amount = resultSet.getBigDecimal(3);
return new TransferAssetTransactionData(senderPublicKey, recipient, amount, assetId, fee, timestamp, reference, signature);
return new TransferAssetTransactionData(timestamp, txGroupId, reference, creatorPublicKey, recipient, amount, assetId, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch transfer asset transaction from repository", e);
}

View File

@ -5,6 +5,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import org.qora.data.transaction.UpdateGroupTransactionData;
import org.qora.group.Group.ApprovalThreshold;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.hsqldb.HSQLDBRepository;
@ -16,9 +17,10 @@ public class HSQLDBUpdateGroupTransactionRepository extends HSQLDBTransactionRep
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository.checkedExecute(
"SELECT group_id, new_owner, new_description, new_is_open, group_reference FROM UpdateGroupTransactions WHERE signature = ?", signature)) {
"SELECT group_id, new_owner, new_description, new_is_open, new_approval_threshold, group_reference FROM UpdateGroupTransactions WHERE signature = ?",
signature)) {
if (resultSet == null)
return null;
@ -26,10 +28,11 @@ public class HSQLDBUpdateGroupTransactionRepository extends HSQLDBTransactionRep
String newOwner = resultSet.getString(2);
String newDescription = resultSet.getString(3);
boolean newIsOpen = resultSet.getBoolean(4);
byte[] groupReference = resultSet.getBytes(5);
ApprovalThreshold newApprovalThreshold = ApprovalThreshold.valueOf(resultSet.getInt(5));
byte[] groupReference = resultSet.getBytes(6);
return new UpdateGroupTransactionData(creatorPublicKey, groupId, newOwner, newDescription, newIsOpen, groupReference, fee, timestamp, reference,
signature);
return new UpdateGroupTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, newOwner, newDescription, newIsOpen,
newApprovalThreshold, groupReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch update group transaction from repository", e);
}
@ -44,6 +47,7 @@ public class HSQLDBUpdateGroupTransactionRepository extends HSQLDBTransactionRep
saveHelper.bind("signature", updateGroupTransactionData.getSignature()).bind("owner", updateGroupTransactionData.getOwnerPublicKey())
.bind("group_id", updateGroupTransactionData.getGroupId()).bind("new_owner", updateGroupTransactionData.getNewOwner())
.bind("new_description", updateGroupTransactionData.getNewDescription()).bind("new_is_open", updateGroupTransactionData.getNewIsOpen())
.bind("new_approval_threshold", updateGroupTransactionData.getNewApprovalThreshold().value)
.bind("group_reference", updateGroupTransactionData.getGroupReference());
try {

View File

@ -16,7 +16,7 @@ public class HSQLDBUpdateNameTransactionRepository extends HSQLDBTransactionRepo
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT new_owner, name, new_data, name_reference FROM UpdateNameTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
@ -27,7 +27,7 @@ public class HSQLDBUpdateNameTransactionRepository extends HSQLDBTransactionRepo
String newData = resultSet.getString(3);
byte[] nameReference = resultSet.getBytes(4);
return new UpdateNameTransactionData(ownerPublicKey, newOwner, name, newData, nameReference, fee, timestamp, reference, signature);
return new UpdateNameTransactionData(timestamp, txGroupId, reference, creatorPublicKey, newOwner, name, newData, nameReference, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch update name transaction from repository", e);
}

View File

@ -16,7 +16,7 @@ public class HSQLDBVoteOnPollTransactionRepository extends HSQLDBTransactionRepo
this.repository = repository;
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
try (ResultSet resultSet = this.repository
.checkedExecute("SELECT poll_name, option_index, previous_option_index FROM VoteOnPollTransactions WHERE signature = ?", signature)) {
if (resultSet == null)
@ -30,7 +30,7 @@ public class HSQLDBVoteOnPollTransactionRepository extends HSQLDBTransactionRepo
if (resultSet.wasNull())
previousOptionIndex = null;
return new VoteOnPollTransactionData(creatorPublicKey, pollName, optionIndex, previousOptionIndex, fee, timestamp, reference, signature);
return new VoteOnPollTransactionData(timestamp, txGroupId, reference, creatorPublicKey, pollName, optionIndex, previousOptionIndex, fee, signature);
} catch (SQLException e) {
throw new DataException("Unable to fetch vote on poll transaction from repository", e);
}

View File

@ -26,7 +26,7 @@ public class Settings {
private static Settings instance;
private String userpath = "";
private boolean useBitcoinTestNet = false;
private boolean wipeUnconfirmedOnStart = true;
private boolean wipeUnconfirmedOnStart = false;
private String blockchainConfigPath = "blockchain.json";
/** Maximum number of unconfirmed transactions allowed per account */
private int maxUnconfirmedPerAccount = 100;

View File

@ -84,6 +84,10 @@ public class AddGroupAdminTransaction extends Transaction {
if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != addGroupAdminTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account owner = getOwner();
// Check transaction's public key matches group's current owner

View File

@ -19,7 +19,7 @@ import org.qora.transform.transaction.AtTransactionTransformer;
import com.google.common.primitives.Bytes;
public class ATTransaction extends Transaction {
public class AtTransaction extends Transaction {
// Properties
private ATTransactionData atTransactionData;
@ -29,7 +29,7 @@ public class ATTransaction extends Transaction {
// Constructors
public ATTransaction(Repository repository, TransactionData transactionData) {
public AtTransaction(Repository repository, TransactionData transactionData) {
super(repository, transactionData);
this.atTransactionData = (ATTransactionData) this.transactionData;

View File

@ -84,6 +84,10 @@ public class CancelGroupBanTransaction extends Transaction {
if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != groupUnbanTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account admin = getAdmin();
// Can't unban if not an admin

View File

@ -84,6 +84,10 @@ public class CancelGroupInviteTransaction extends Transaction {
if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != cancelGroupInviteTransactionData.getGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account admin = getAdmin();
// Check admin is actually an admin

View File

@ -74,6 +74,10 @@ public class CreateGroupTransaction extends Transaction {
if (!Crypto.isValidAddress(createGroupTransactionData.getOwner()))
return ValidationResult.INVALID_ADDRESS;
// Check approval threshold is valid
if (createGroupTransactionData.getApprovalThreshold() == null)
return ValidationResult.INVALID_GROUP_APPROVAL_THRESHOLD;
// Check group name size bounds
int groupNameLength = Utf8.encodedLength(createGroupTransactionData.getGroupName());
if (groupNameLength < 1 || groupNameLength > Group.MAX_NAME_SIZE)

View File

@ -86,7 +86,7 @@ public class DeployAtTransaction extends Transaction {
/** Make sure deployATTransactionData has an ATAddress */
private void ensureATAddress() throws DataException {
if (this.deployATTransactionData.getATAddress() != null)
if (this.deployATTransactionData.getAtAddress() != null)
return;
int blockHeight = this.getHeight();
@ -112,7 +112,7 @@ public class DeployAtTransaction extends Transaction {
String atAddress = Crypto.toATAddress(byteBuffer.array());
this.deployATTransactionData.setATAddress(atAddress);
this.deployATTransactionData.setAtAddress(atAddress);
} catch (UnsupportedEncodingException e) {
throw new DataException("Unable to generate AT account from Deploy AT transaction data", e);
}
@ -123,7 +123,7 @@ public class DeployAtTransaction extends Transaction {
public Account getATAccount() throws DataException {
ensureATAddress();
return new Account(this.repository, this.deployATTransactionData.getATAddress());
return new Account(this.repository, this.deployATTransactionData.getAtAddress());
}
// Processing
@ -144,7 +144,7 @@ public class DeployAtTransaction extends Transaction {
return ValidationResult.INVALID_DESCRIPTION_LENGTH;
// Check AT-type size bounds
int ATTypeLength = Utf8.encodedLength(deployATTransactionData.getATType());
int ATTypeLength = Utf8.encodedLength(deployATTransactionData.getAtType());
if (ATTypeLength < 1 || ATTypeLength > MAX_AT_TYPE_SIZE)
return ValidationResult.INVALID_AT_TYPE_LENGTH;
@ -257,7 +257,7 @@ public class DeployAtTransaction extends Transaction {
creator.setLastReference(deployATTransactionData.getReference());
// Delete AT's account (and hence its balance)
this.repository.getAccountRepository().delete(this.deployATTransactionData.getATAddress());
this.repository.getAccountRepository().delete(this.deployATTransactionData.getAtAddress());
}
}

View File

@ -0,0 +1,136 @@
package org.qora.transaction;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.qora.account.Account;
import org.qora.account.PublicKeyAccount;
import org.qora.asset.Asset;
import org.qora.data.transaction.GroupApprovalTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
public class GroupApprovalTransaction extends Transaction {
// Properties
private GroupApprovalTransactionData groupApprovalTransactionData;
// Constructors
public GroupApprovalTransaction(Repository repository, TransactionData transactionData) {
super(repository, transactionData);
this.groupApprovalTransactionData = (GroupApprovalTransactionData) this.transactionData;
}
// More information
@Override
public List<Account> getRecipientAccounts() throws DataException {
return Collections.emptyList();
}
@Override
public boolean isInvolved(Account account) throws DataException {
String address = account.getAddress();
if (address.equals(this.getAdmin().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.getAdmin().getAddress()))
amount = amount.subtract(this.transactionData.getFee());
return amount;
}
// Navigation
public Account getAdmin() throws DataException {
return new PublicKeyAccount(this.repository, this.groupApprovalTransactionData.getAdminPublicKey());
}
// Processing
@Override
public ValidationResult isValid() throws DataException {
// Grab pending transaction's data
TransactionData pendingTransactionData = this.repository.getTransactionRepository().fromSignature(groupApprovalTransactionData.getPendingSignature());
if (pendingTransactionData == null)
return ValidationResult.TRANSACTION_UNKNOWN;
// Check pending transaction is not already in a block
if (this.repository.getTransactionRepository().getHeightFromSignature(groupApprovalTransactionData.getPendingSignature()) != 0)
return ValidationResult.TRANSACTION_ALREADY_CONFIRMED;
// Check pending transaction's groupID matches our transaction's groupID
int effectiveTxGroupId = this.getEffectiveGroupId();
if (effectiveTxGroupId != pendingTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account admin = getAdmin();
// Can't cast approval decision if not an admin
if (!this.repository.getGroupRepository().adminExists(groupApprovalTransactionData.getTxGroupId(), admin.getAddress()))
return ValidationResult.NOT_GROUP_ADMIN;
// Check fee is positive
if (groupApprovalTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
return ValidationResult.NEGATIVE_FEE;
// Check reference
if (!Arrays.equals(admin.getLastReference(), groupApprovalTransactionData.getReference()))
return ValidationResult.INVALID_REFERENCE;
// Check creator has enough funds
if (admin.getConfirmedBalance(Asset.QORA).compareTo(groupApprovalTransactionData.getFee()) < 0)
return ValidationResult.NO_BALANCE;
return ValidationResult.OK;
}
@Override
public void process() throws DataException {
// Find previous approval decision (if any) by this admin for pending transaction
List<GroupApprovalTransactionData> approvals = this.repository.getTransactionRepository().getLatestApprovals(groupApprovalTransactionData.getPendingSignature(), groupApprovalTransactionData.getAdminPublicKey());
if (!approvals.isEmpty())
groupApprovalTransactionData.setPriorReference(approvals.get(0).getSignature());
// Save this transaction with updated prior reference to transaction that can help restore state
this.repository.getTransactionRepository().save(groupApprovalTransactionData);
// Update admin's balance
Account admin = getAdmin();
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).subtract(groupApprovalTransactionData.getFee()));
// Update admin's reference
admin.setLastReference(groupApprovalTransactionData.getSignature());
}
@Override
public void orphan() throws DataException {
// Revert?
// Delete this transaction itself
this.repository.getTransactionRepository().delete(groupApprovalTransactionData);
// Update admin's balance
Account admin = getAdmin();
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).add(groupApprovalTransactionData.getFee()));
// Update admin's reference
admin.setLastReference(groupApprovalTransactionData.getReference());
}
}

View File

@ -84,6 +84,10 @@ public class GroupBanTransaction extends Transaction {
if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != groupBanTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account admin = getAdmin();
// Can't ban if not an admin

View File

@ -88,6 +88,11 @@ public class GroupInviteTransaction extends Transaction {
if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
int effectiveTxGroupId = this.getEffectiveGroupId();
if (effectiveTxGroupId != groupInviteTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account admin = getAdmin();
// Can't invite if not an admin

View File

@ -87,6 +87,10 @@ public class GroupKickTransaction extends Transaction {
if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != groupKickTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account admin = getAdmin();
// Can't kick if not an admin

Some files were not shown because too many files have changed in this diff Show More