diff --git a/log4j2.properties b/log4j2.properties index 3188c2ff..ccb88f61 100644 --- a/log4j2.properties +++ b/log4j2.properties @@ -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 diff --git a/src/main/java/org/qora/account/Account.java b/src/main/java/org/qora/account/Account.java index 05d415eb..8a153321 100644 --- a/src/main/java/org/qora/account/Account.java +++ b/src/main/java/org/qora/account/Account.java @@ -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. + *

+ * 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. *

- * NOTE: repository.discardChanges() 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. + *

+ * Caller will need to call repository.saveChanges(). + * + * @param defaultGroupId + * @throws DataException + */ + public void setDefaultGroupId(int defaultGroupId) throws DataException { + AccountData accountData = this.buildAccountData(); + accountData.setDefaultGroupId(defaultGroupId); + this.repository.getAccountRepository().setDefaultGroupId(accountData); } } diff --git a/src/main/java/org/qora/account/PrivateKeyAccount.java b/src/main/java/org/qora/account/PrivateKeyAccount.java index ce896b8c..2d178faf 100644 --- a/src/main/java/org/qora/account/PrivateKeyAccount.java +++ b/src/main/java/org/qora/account/PrivateKeyAccount.java @@ -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() { diff --git a/src/main/java/org/qora/account/PublicKeyAccount.java b/src/main/java/org/qora/account/PublicKeyAccount.java index ae9af10b..fac8dd96 100644 --- a/src/main/java/org/qora/account/PublicKeyAccount.java +++ b/src/main/java/org/qora/account/PublicKeyAccount.java @@ -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) { diff --git a/src/main/java/org/qora/api/resource/AddressesResource.java b/src/main/java/org/qora/api/resource/AddressesResource.java index 041ef063..f3bacb6d 100644 --- a/src/main/java/org/qora/api/resource/AddressesResource.java +++ b/src/main/java/org/qora/api/resource/AddressesResource.java @@ -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( diff --git a/src/main/java/org/qora/api/resource/GroupsResource.java b/src/main/java/org/qora/api/resource/GroupsResource.java index 66d2e7cf..da68de54 100644 --- a/src/main/java/org/qora/api/resource/GroupsResource.java +++ b/src/main/java/org/qora/api/resource/GroupsResource.java @@ -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); + } + } + } \ No newline at end of file diff --git a/src/main/java/org/qora/api/resource/TransactionsResource.java b/src/main/java/org/qora/api/resource/TransactionsResource.java index 2fc00393..d81217e1 100644 --- a/src/main/java/org/qora/api/resource/TransactionsResource.java +++ b/src/main/java/org/qora/api/resource/TransactionsResource.java @@ -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 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 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, diff --git a/src/main/java/org/qora/at/AT.java b/src/main/java/org/qora/at/AT.java index 06706074..e8ec2425 100644 --- a/src/main/java/org/qora/at/AT.java +++ b/src/main/java/org/qora/at/AT.java @@ -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 run(long blockTimestamp) throws DataException { + public List run(long blockTimestamp) throws DataException { String atAddress = this.atData.getATAddress(); QoraATAPI api = new QoraATAPI(repository, this.atData, blockTimestamp); diff --git a/src/main/java/org/qora/at/BlockchainAPI.java b/src/main/java/org/qora/at/BlockchainAPI.java index f1c2a0dc..4a863902 100644 --- a/src/main/java/org/qora/at/BlockchainAPI.java +++ b/src/main/java/org/qora/at/BlockchainAPI.java @@ -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); diff --git a/src/main/java/org/qora/at/QoraATAPI.java b/src/main/java/org/qora/at/QoraATAPI.java index 2b124877..d650e5dd 100644 --- a/src/main/java/org/qora/at/QoraATAPI.java +++ b/src/main/java/org/qora/at/QoraATAPI.java @@ -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 transactions; + List transactions; // Constructors public QoraATAPI(Repository repository, ATData atData, long blockTimestamp) { this.repository = repository; this.atData = atData; - this.transactions = new ArrayList(); + this.transactions = new ArrayList(); this.blockTimestamp = blockTimestamp; } // Methods specific to Qora AT processing, not inherited - public List getTransactions() { + public List 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); diff --git a/src/main/java/org/qora/block/Block.java b/src/main/java/org/qora/block/Block.java index b5f8ecc4..9fcb18fa 100644 --- a/src/main/java/org/qora/block/Block.java +++ b/src/main/java/org/qora/block/Block.java @@ -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. *

* Checks block's transactions by testing their validity then processing them.
- * Hence calls repository.discardChanges() 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 allATTransactions = new ArrayList(); + List allATTransactions = new ArrayList(); this.ourAtStates = new ArrayList(); 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 atTransactions = at.run(this.blockData.getTimestamp()); + List 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()); } diff --git a/src/main/java/org/qora/block/BlockChain.java b/src/main/java/org/qora/block/BlockChain.java index 4835cad3..4fee23fe 100644 --- a/src/main/java/org/qora/block/BlockChain.java +++ b/src/main/java/org/qora/block/BlockChain.java @@ -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> 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; } diff --git a/src/main/java/org/qora/block/BlockGenerator.java b/src/main/java/org/qora/block/BlockGenerator.java index 00871c35..d210a3ad 100644 --- a/src/main/java/org/qora/block/BlockGenerator.java +++ b/src/main/java/org/qora/block/BlockGenerator.java @@ -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 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 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. diff --git a/src/main/java/org/qora/block/GenesisBlock.java b/src/main/java/org/qora/block/GenesisBlock.java index 208b2e83..8cff5d8c 100644 --- a/src/main/java/org/qora/block/GenesisBlock.java +++ b/src/main/java/org/qora/block/GenesisBlock.java @@ -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)); } } diff --git a/src/main/java/org/qora/controller/Synchronizer.java b/src/main/java/org/qora/controller/Synchronizer.java index 7504d8c9..b8acc483 100644 --- a/src/main/java/org/qora/controller/Synchronizer.java +++ b/src/main/java/org/qora/controller/Synchronizer.java @@ -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; } } diff --git a/src/main/java/org/qora/data/account/AccountBalanceData.java b/src/main/java/org/qora/data/account/AccountBalanceData.java index c582b559..1f1ed3e4 100644 --- a/src/main/java/org/qora/data/account/AccountBalanceData.java +++ b/src/main/java/org/qora/data/account/AccountBalanceData.java @@ -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) { diff --git a/src/main/java/org/qora/data/account/AccountData.java b/src/main/java/org/qora/data/account/AccountData.java index 269c0b3b..15b5fbb3 100644 --- a/src/main/java/org/qora/data/account/AccountData.java +++ b/src/main/java/org/qora/data/account/AccountData.java @@ -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 diff --git a/src/main/java/org/qora/data/group/GroupData.java b/src/main/java/org/qora/data/group/GroupData.java index 9b6350bc..09a487c0 100644 --- a/src/main/java/org/qora/data/group/GroupData.java +++ b/src/main/java/org/qora/data/group/GroupData.java @@ -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; + } + } diff --git a/src/main/java/org/qora/data/transaction/ATTransactionData.java b/src/main/java/org/qora/data/transaction/ATTransactionData.java index 19367ec5..ee409669 100644 --- a/src/main/java/org/qora/data/transaction/ATTransactionData.java +++ b/src/main/java/org/qora/data/transaction/ATTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/AddGroupAdminTransactionData.java b/src/main/java/org/qora/data/transaction/AddGroupAdminTransactionData.java index 876e2b48..e29bf2fc 100644 --- a/src/main/java/org/qora/data/transaction/AddGroupAdminTransactionData.java +++ b/src/main/java/org/qora/data/transaction/AddGroupAdminTransactionData.java @@ -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; diff --git a/src/main/java/org/qora/data/transaction/ArbitraryTransactionData.java b/src/main/java/org/qora/data/transaction/ArbitraryTransactionData.java index f7582d53..c2d42384 100644 --- a/src/main/java/org/qora/data/transaction/ArbitraryTransactionData.java +++ b/src/main/java/org/qora/data/transaction/ArbitraryTransactionData.java @@ -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 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 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 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 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; } diff --git a/src/main/java/org/qora/data/transaction/BuyNameTransactionData.java b/src/main/java/org/qora/data/transaction/BuyNameTransactionData.java index 27bde460..8f98253c 100644 --- a/src/main/java/org/qora/data/transaction/BuyNameTransactionData.java +++ b/src/main/java/org/qora/data/transaction/BuyNameTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/CancelAssetOrderTransactionData.java b/src/main/java/org/qora/data/transaction/CancelAssetOrderTransactionData.java index 1d1021ef..514114f4 100644 --- a/src/main/java/org/qora/data/transaction/CancelAssetOrderTransactionData.java +++ b/src/main/java/org/qora/data/transaction/CancelAssetOrderTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/CancelGroupBanTransactionData.java b/src/main/java/org/qora/data/transaction/CancelGroupBanTransactionData.java index 3ce5afd7..f20d8a56 100644 --- a/src/main/java/org/qora/data/transaction/CancelGroupBanTransactionData.java +++ b/src/main/java/org/qora/data/transaction/CancelGroupBanTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/CancelGroupInviteTransactionData.java b/src/main/java/org/qora/data/transaction/CancelGroupInviteTransactionData.java index 646f3664..35dbaca0 100644 --- a/src/main/java/org/qora/data/transaction/CancelGroupInviteTransactionData.java +++ b/src/main/java/org/qora/data/transaction/CancelGroupInviteTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/CancelSellNameTransactionData.java b/src/main/java/org/qora/data/transaction/CancelSellNameTransactionData.java index e55da8c9..3372c298 100644 --- a/src/main/java/org/qora/data/transaction/CancelSellNameTransactionData.java +++ b/src/main/java/org/qora/data/transaction/CancelSellNameTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/CreateAssetOrderTransactionData.java b/src/main/java/org/qora/data/transaction/CreateAssetOrderTransactionData.java index ef232e9b..5b9e148f 100644 --- a/src/main/java/org/qora/data/transaction/CreateAssetOrderTransactionData.java +++ b/src/main/java/org/qora/data/transaction/CreateAssetOrderTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/CreateGroupTransactionData.java b/src/main/java/org/qora/data/transaction/CreateGroupTransactionData.java index 0a4e3a91..a07fc0ee 100644 --- a/src/main/java/org/qora/data/transaction/CreateGroupTransactionData.java +++ b/src/main/java/org/qora/data/transaction/CreateGroupTransactionData.java @@ -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; } diff --git a/src/main/java/org/qora/data/transaction/CreatePollTransactionData.java b/src/main/java/org/qora/data/transaction/CreatePollTransactionData.java index 9d0cd2f9..69fe05af 100644 --- a/src/main/java/org/qora/data/transaction/CreatePollTransactionData.java +++ b/src/main/java/org/qora/data/transaction/CreatePollTransactionData.java @@ -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 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 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 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 pollOptions, BigDecimal fee) { + this(timestamp, txGroupId, reference, creatorPublicKey, owner, pollName, description, pollOptions, fee, null); } // Getters/setters diff --git a/src/main/java/org/qora/data/transaction/DeployAtTransactionData.java b/src/main/java/org/qora/data/transaction/DeployAtTransactionData.java index ebda409c..04e3f681 100644 --- a/src/main/java/org/qora/data/transaction/DeployAtTransactionData.java +++ b/src/main/java/org/qora/data/transaction/DeployAtTransactionData.java @@ -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; } } diff --git a/src/main/java/org/qora/data/transaction/GenesisTransactionData.java b/src/main/java/org/qora/data/transaction/GenesisTransactionData.java index 3a65c5b1..3a1725a0 100644 --- a/src/main/java/org/qora/data/transaction/GenesisTransactionData.java +++ b/src/main/java/org/qora/data/transaction/GenesisTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/GroupApprovalTransactionData.java b/src/main/java/org/qora/data/transaction/GroupApprovalTransactionData.java new file mode 100644 index 00000000..384a4143 --- /dev/null +++ b/src/main/java/org/qora/data/transaction/GroupApprovalTransactionData.java @@ -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; + } + +} diff --git a/src/main/java/org/qora/data/transaction/GroupBanTransactionData.java b/src/main/java/org/qora/data/transaction/GroupBanTransactionData.java index c8a2fd33..d6710b12 100644 --- a/src/main/java/org/qora/data/transaction/GroupBanTransactionData.java +++ b/src/main/java/org/qora/data/transaction/GroupBanTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/GroupInviteTransactionData.java b/src/main/java/org/qora/data/transaction/GroupInviteTransactionData.java index 9f6bfde9..ef9bdbda 100644 --- a/src/main/java/org/qora/data/transaction/GroupInviteTransactionData.java +++ b/src/main/java/org/qora/data/transaction/GroupInviteTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/GroupKickTransactionData.java b/src/main/java/org/qora/data/transaction/GroupKickTransactionData.java index ae4087af..9aa44119 100644 --- a/src/main/java/org/qora/data/transaction/GroupKickTransactionData.java +++ b/src/main/java/org/qora/data/transaction/GroupKickTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/IssueAssetTransactionData.java b/src/main/java/org/qora/data/transaction/IssueAssetTransactionData.java index 0dc95174..7e57a665 100644 --- a/src/main/java/org/qora/data/transaction/IssueAssetTransactionData.java +++ b/src/main/java/org/qora/data/transaction/IssueAssetTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/JoinGroupTransactionData.java b/src/main/java/org/qora/data/transaction/JoinGroupTransactionData.java index 3e04484b..6059b090 100644 --- a/src/main/java/org/qora/data/transaction/JoinGroupTransactionData.java +++ b/src/main/java/org/qora/data/transaction/JoinGroupTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/LeaveGroupTransactionData.java b/src/main/java/org/qora/data/transaction/LeaveGroupTransactionData.java index 7c2dfa5a..34b1c601 100644 --- a/src/main/java/org/qora/data/transaction/LeaveGroupTransactionData.java +++ b/src/main/java/org/qora/data/transaction/LeaveGroupTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/MessageTransactionData.java b/src/main/java/org/qora/data/transaction/MessageTransactionData.java index 604ad1fe..a48a4584 100644 --- a/src/main/java/org/qora/data/transaction/MessageTransactionData.java +++ b/src/main/java/org/qora/data/transaction/MessageTransactionData.java @@ -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; } diff --git a/src/main/java/org/qora/data/transaction/MultiPaymentTransactionData.java b/src/main/java/org/qora/data/transaction/MultiPaymentTransactionData.java index f63f52c7..d524eefc 100644 --- a/src/main/java/org/qora/data/transaction/MultiPaymentTransactionData.java +++ b/src/main/java/org/qora/data/transaction/MultiPaymentTransactionData.java @@ -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 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 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 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 payments, BigDecimal fee) { + this(timestamp, txGroupId, reference, senderPublicKey, payments, fee, null); } // Getters/setters diff --git a/src/main/java/org/qora/data/transaction/PaymentTransactionData.java b/src/main/java/org/qora/data/transaction/PaymentTransactionData.java index c650ae82..cfd08a79 100644 --- a/src/main/java/org/qora/data/transaction/PaymentTransactionData.java +++ b/src/main/java/org/qora/data/transaction/PaymentTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/RegisterNameTransactionData.java b/src/main/java/org/qora/data/transaction/RegisterNameTransactionData.java index dcd4cd46..4582fdbc 100644 --- a/src/main/java/org/qora/data/transaction/RegisterNameTransactionData.java +++ b/src/main/java/org/qora/data/transaction/RegisterNameTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/RemoveGroupAdminTransactionData.java b/src/main/java/org/qora/data/transaction/RemoveGroupAdminTransactionData.java index d254cf55..40639d9c 100644 --- a/src/main/java/org/qora/data/transaction/RemoveGroupAdminTransactionData.java +++ b/src/main/java/org/qora/data/transaction/RemoveGroupAdminTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/SellNameTransactionData.java b/src/main/java/org/qora/data/transaction/SellNameTransactionData.java index e7f9cef7..94f495a9 100644 --- a/src/main/java/org/qora/data/transaction/SellNameTransactionData.java +++ b/src/main/java/org/qora/data/transaction/SellNameTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/SetGroupTransactionData.java b/src/main/java/org/qora/data/transaction/SetGroupTransactionData.java new file mode 100644 index 00000000..42fb5e2a --- /dev/null +++ b/src/main/java/org/qora/data/transaction/SetGroupTransactionData.java @@ -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); + } + +} diff --git a/src/main/java/org/qora/data/transaction/TransactionData.java b/src/main/java/org/qora/data/transaction/TransactionData.java index 37666ecf..4869ce38 100644 --- a/src/main/java/org/qora/data/transaction/TransactionData.java +++ b/src/main/java/org/qora/data/transaction/TransactionData.java @@ -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; diff --git a/src/main/java/org/qora/data/transaction/TransferAssetTransactionData.java b/src/main/java/org/qora/data/transaction/TransferAssetTransactionData.java index 465b9895..9ee5b7b0 100644 --- a/src/main/java/org/qora/data/transaction/TransferAssetTransactionData.java +++ b/src/main/java/org/qora/data/transaction/TransferAssetTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/UpdateGroupTransactionData.java b/src/main/java/org/qora/data/transaction/UpdateGroupTransactionData.java index 7ff6502b..d101702e 100644 --- a/src/main/java/org/qora/data/transaction/UpdateGroupTransactionData.java +++ b/src/main/java/org/qora/data/transaction/UpdateGroupTransactionData.java @@ -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; } diff --git a/src/main/java/org/qora/data/transaction/UpdateNameTransactionData.java b/src/main/java/org/qora/data/transaction/UpdateNameTransactionData.java index f4bb0e06..7abc601b 100644 --- a/src/main/java/org/qora/data/transaction/UpdateNameTransactionData.java +++ b/src/main/java/org/qora/data/transaction/UpdateNameTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/data/transaction/VoteOnPollTransactionData.java b/src/main/java/org/qora/data/transaction/VoteOnPollTransactionData.java index 1c9f9075..124d7c07 100644 --- a/src/main/java/org/qora/data/transaction/VoteOnPollTransactionData.java +++ b/src/main/java/org/qora/data/transaction/VoteOnPollTransactionData.java @@ -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 diff --git a/src/main/java/org/qora/group/Group.java b/src/main/java/org/qora/group/Group.java index 7efdddd8..869b9daa 100644 --- a/src/main/java/org/qora/group/Group.java +++ b/src/main/java/org/qora/group/Group.java @@ -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 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; diff --git a/src/main/java/org/qora/repository/AccountRepository.java b/src/main/java/org/qora/repository/AccountRepository.java index dde2738d..eac15023 100644 --- a/src/main/java/org/qora/repository/AccountRepository.java +++ b/src/main/java/org/qora/repository/AccountRepository.java @@ -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. + *

+ * 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. + *

+ * 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. + *

+ * Note: ignores other fields like last reference. + */ + public void setDefaultGroupId(AccountData accountData) throws DataException; public void delete(String address) throws DataException; diff --git a/src/main/java/org/qora/repository/TransactionRepository.java b/src/main/java/org/qora/repository/TransactionRepository.java index 026340ac..74dbc383 100644 --- a/src/main/java/org/qora/repository/TransactionRepository.java +++ b/src/main/java/org/qora/repository/TransactionRepository.java @@ -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 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 getLatestApprovals(byte[] pendingSignature, byte[] adminPublicKey) throws DataException; + /** * Returns whether transaction is confirmed or not. * diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java index 9eaf6726..a432b0aa 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java @@ -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); } } diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java index 444abe83..8bd51b07 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -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; } diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBGroupRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBGroupRepository.java index e4795234..39ff765b 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBGroupRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBGroupRepository.java @@ -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 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 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 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); diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBRepository.java index efc6e1e1..455a53d6 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBRepository.java @@ -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 savepoints; + protected Deque 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); diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBAddGroupAdminTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBAddGroupAdminTransactionRepository.java index 5388c184..11a7b511 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBAddGroupAdminTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBAddGroupAdminTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBArbitraryTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBArbitraryTransactionRepository.java index 8027a286..2148238a 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBArbitraryTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBArbitraryTransactionRepository.java @@ -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 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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBAtTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBAtTransactionRepository.java index 8e8431ab..3b4f378f 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBAtTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBAtTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java index 62bb1c07..6c9f3f6b 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelAssetOrderTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelAssetOrderTransactionRepository.java index 22a1c254..e035b936 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelAssetOrderTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelAssetOrderTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelGroupBanTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelGroupBanTransactionRepository.java index 605eab7b..6f16fadb 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelGroupBanTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelGroupBanTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelGroupInviteTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelGroupInviteTransactionRepository.java index a35a58c5..382c4a4e 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelGroupInviteTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelGroupInviteTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelSellNameTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelSellNameTransactionRepository.java index 3fbf9008..6ff43a01 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelSellNameTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCancelSellNameTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java index 0ccbf2a7..1dc663a8 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateGroupTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateGroupTransactionRepository.java index 1d8b3c83..098d4dd1 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateGroupTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateGroupTransactionRepository.java @@ -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 { diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreatePollTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreatePollTransactionRepository.java index 838a1801..50c63547 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreatePollTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreatePollTransactionRepository.java @@ -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); diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBDeployAtTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBDeployAtTransactionRepository.java index bcc4784e..fb011631 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBDeployAtTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBDeployAtTransactionRepository.java @@ -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); diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java index c8b11183..130af39b 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupApprovalTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupApprovalTransactionRepository.java new file mode 100644 index 00000000..351efac1 --- /dev/null +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupApprovalTransactionRepository.java @@ -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); + } + } + +} diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupBanTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupBanTransactionRepository.java index fe3806cf..37825ee2 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupBanTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupBanTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupInviteTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupInviteTransactionRepository.java index d1fbe37c..b2a3a9a4 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupInviteTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupInviteTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupKickTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupKickTransactionRepository.java index 4b9c32e3..dbe194de 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupKickTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupKickTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBIssueAssetTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBIssueAssetTransactionRepository.java index 270c2d6d..cd5ffb8d 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBIssueAssetTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBIssueAssetTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBJoinGroupTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBJoinGroupTransactionRepository.java index b54bef75..98dce60d 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBJoinGroupTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBJoinGroupTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBLeaveGroupTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBLeaveGroupTransactionRepository.java index 872d119e..470fb6a0 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBLeaveGroupTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBLeaveGroupTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java index edad7e46..af71837b 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java index 20b4a38d..b7e62f8f 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java @@ -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 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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java index 026d63cf..6307f833 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java index fb6813be..261aa1ca 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRemoveGroupAdminTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRemoveGroupAdminTransactionRepository.java index ca69996b..a01d6e38 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRemoveGroupAdminTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRemoveGroupAdminTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java index e76ed907..f02c6f67 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBSetGroupTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBSetGroupTransactionRepository.java new file mode 100644 index 00000000..92dba478 --- /dev/null +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBSetGroupTransactionRepository.java @@ -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); + } + } + +} diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java index e61ea4e2..84a1af77 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java @@ -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 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 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) { diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java index 21317700..c11a503d 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBUpdateGroupTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBUpdateGroupTransactionRepository.java index 3efb14c5..47e25373 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBUpdateGroupTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBUpdateGroupTransactionRepository.java @@ -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 { diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java index be088143..788dc97f 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBVoteOnPollTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBVoteOnPollTransactionRepository.java index 7dd9c3f8..077f845c 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBVoteOnPollTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBVoteOnPollTransactionRepository.java @@ -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); } diff --git a/src/main/java/org/qora/settings/Settings.java b/src/main/java/org/qora/settings/Settings.java index 463c4790..54a92fd8 100644 --- a/src/main/java/org/qora/settings/Settings.java +++ b/src/main/java/org/qora/settings/Settings.java @@ -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; diff --git a/src/main/java/org/qora/transaction/AddGroupAdminTransaction.java b/src/main/java/org/qora/transaction/AddGroupAdminTransaction.java index 4b703581..7ecc2ee1 100644 --- a/src/main/java/org/qora/transaction/AddGroupAdminTransaction.java +++ b/src/main/java/org/qora/transaction/AddGroupAdminTransaction.java @@ -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 diff --git a/src/main/java/org/qora/transaction/ATTransaction.java b/src/main/java/org/qora/transaction/AtTransaction.java similarity index 98% rename from src/main/java/org/qora/transaction/ATTransaction.java rename to src/main/java/org/qora/transaction/AtTransaction.java index 92ce728c..3babce38 100644 --- a/src/main/java/org/qora/transaction/ATTransaction.java +++ b/src/main/java/org/qora/transaction/AtTransaction.java @@ -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; diff --git a/src/main/java/org/qora/transaction/CancelGroupBanTransaction.java b/src/main/java/org/qora/transaction/CancelGroupBanTransaction.java index 79ccf456..92b18853 100644 --- a/src/main/java/org/qora/transaction/CancelGroupBanTransaction.java +++ b/src/main/java/org/qora/transaction/CancelGroupBanTransaction.java @@ -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 diff --git a/src/main/java/org/qora/transaction/CancelGroupInviteTransaction.java b/src/main/java/org/qora/transaction/CancelGroupInviteTransaction.java index d4cbe20b..cd725f82 100644 --- a/src/main/java/org/qora/transaction/CancelGroupInviteTransaction.java +++ b/src/main/java/org/qora/transaction/CancelGroupInviteTransaction.java @@ -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 diff --git a/src/main/java/org/qora/transaction/CreateGroupTransaction.java b/src/main/java/org/qora/transaction/CreateGroupTransaction.java index 5671469e..5bb29987 100644 --- a/src/main/java/org/qora/transaction/CreateGroupTransaction.java +++ b/src/main/java/org/qora/transaction/CreateGroupTransaction.java @@ -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) diff --git a/src/main/java/org/qora/transaction/DeployAtTransaction.java b/src/main/java/org/qora/transaction/DeployAtTransaction.java index d420bb9e..d30c8ccd 100644 --- a/src/main/java/org/qora/transaction/DeployAtTransaction.java +++ b/src/main/java/org/qora/transaction/DeployAtTransaction.java @@ -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()); } } diff --git a/src/main/java/org/qora/transaction/GroupApprovalTransaction.java b/src/main/java/org/qora/transaction/GroupApprovalTransaction.java new file mode 100644 index 00000000..03677b1d --- /dev/null +++ b/src/main/java/org/qora/transaction/GroupApprovalTransaction.java @@ -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 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 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()); + } + +} diff --git a/src/main/java/org/qora/transaction/GroupBanTransaction.java b/src/main/java/org/qora/transaction/GroupBanTransaction.java index 12bb4883..3aa1e21a 100644 --- a/src/main/java/org/qora/transaction/GroupBanTransaction.java +++ b/src/main/java/org/qora/transaction/GroupBanTransaction.java @@ -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 diff --git a/src/main/java/org/qora/transaction/GroupInviteTransaction.java b/src/main/java/org/qora/transaction/GroupInviteTransaction.java index 47019cdc..6278ecb9 100644 --- a/src/main/java/org/qora/transaction/GroupInviteTransaction.java +++ b/src/main/java/org/qora/transaction/GroupInviteTransaction.java @@ -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 diff --git a/src/main/java/org/qora/transaction/GroupKickTransaction.java b/src/main/java/org/qora/transaction/GroupKickTransaction.java index 5063656e..4d632e19 100644 --- a/src/main/java/org/qora/transaction/GroupKickTransaction.java +++ b/src/main/java/org/qora/transaction/GroupKickTransaction.java @@ -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 diff --git a/src/main/java/org/qora/transaction/LeaveGroupTransaction.java b/src/main/java/org/qora/transaction/LeaveGroupTransaction.java index 6f79e42c..f25d4bd9 100644 --- a/src/main/java/org/qora/transaction/LeaveGroupTransaction.java +++ b/src/main/java/org/qora/transaction/LeaveGroupTransaction.java @@ -72,6 +72,10 @@ public class LeaveGroupTransaction extends Transaction { if (groupData == null) return ValidationResult.GROUP_DOES_NOT_EXIST; + // Check transaction's groupID matches group's ID + if (groupData.getGroupId() != leaveGroupTransactionData.getTxGroupId()) + return ValidationResult.GROUP_ID_MISMATCH; + Account leaver = getLeaver(); // Can't leave if group owner diff --git a/src/main/java/org/qora/transaction/RemoveGroupAdminTransaction.java b/src/main/java/org/qora/transaction/RemoveGroupAdminTransaction.java index 412c1969..40dec872 100644 --- a/src/main/java/org/qora/transaction/RemoveGroupAdminTransaction.java +++ b/src/main/java/org/qora/transaction/RemoveGroupAdminTransaction.java @@ -84,6 +84,10 @@ public class RemoveGroupAdminTransaction extends Transaction { if (groupData == null) return ValidationResult.GROUP_DOES_NOT_EXIST; + // Check transaction's groupID matches group's ID + if (groupData.getGroupId() != removeGroupAdminTransactionData.getTxGroupId()) + return ValidationResult.GROUP_ID_MISMATCH; + Account owner = getOwner(); // Check transaction's public key matches group's current owner diff --git a/src/main/java/org/qora/transaction/SetGroupTransaction.java b/src/main/java/org/qora/transaction/SetGroupTransaction.java new file mode 100644 index 00000000..4f3402af --- /dev/null +++ b/src/main/java/org/qora/transaction/SetGroupTransaction.java @@ -0,0 +1,135 @@ +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.asset.Asset; +import org.qora.data.transaction.SetGroupTransactionData; +import org.qora.data.group.GroupData; +import org.qora.data.transaction.TransactionData; +import org.qora.group.Group; +import org.qora.repository.DataException; +import org.qora.repository.Repository; + +public class SetGroupTransaction extends Transaction { + + // Properties + private SetGroupTransactionData setGroupTransactionData; + + // Constructors + + public SetGroupTransaction(Repository repository, TransactionData transactionData) { + super(repository, transactionData); + + this.setGroupTransactionData = (SetGroupTransactionData) this.transactionData; + } + + // More information + + @Override + public List getRecipientAccounts() throws DataException { + return Collections.emptyList(); + } + + @Override + public boolean isInvolved(Account account) throws DataException { + String address = account.getAddress(); + + if (address.equals(this.getCreator().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.getCreator().getAddress())) + amount = amount.subtract(this.transactionData.getFee()); + + return amount; + } + + // Navigation + + // Processing + + @Override + public ValidationResult isValid() throws DataException { + GroupData groupData = this.repository.getGroupRepository().fromGroupId(setGroupTransactionData.getDefaultGroupId()); + + // Check group exists + if (groupData == null) + return ValidationResult.GROUP_DOES_NOT_EXIST; + + Account creator = getCreator(); + + // Must be member of group + if (!this.repository.getGroupRepository().memberExists(setGroupTransactionData.getDefaultGroupId(), creator.getAddress())) + return ValidationResult.NOT_GROUP_MEMBER; + + // Check fee is positive + if (setGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) + return ValidationResult.NEGATIVE_FEE; + + // Check reference + if (!Arrays.equals(creator.getLastReference(), setGroupTransactionData.getReference())) + return ValidationResult.INVALID_REFERENCE; + + // Check creator has enough funds + if (creator.getConfirmedBalance(Asset.QORA).compareTo(setGroupTransactionData.getFee()) < 0) + return ValidationResult.NO_BALANCE; + + return ValidationResult.OK; + } + + @Override + public void process() throws DataException { + Account creator = getCreator(); + + Integer previousDefaultGroupId = this.repository.getAccountRepository().getDefaultGroupId(creator.getAddress()); + if (previousDefaultGroupId == null) + previousDefaultGroupId = Group.DEFAULT_GROUP; + + setGroupTransactionData.setPreviousDefaultGroupId(previousDefaultGroupId); + + // Save this transaction with account's previous defaultGroupId value + this.repository.getTransactionRepository().save(setGroupTransactionData); + + // Set account's new default groupID + creator.setDefaultGroupId(setGroupTransactionData.getDefaultGroupId()); + + // Update creator's balance + creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(setGroupTransactionData.getFee())); + + // Update admin's reference + creator.setLastReference(setGroupTransactionData.getSignature()); + } + + @Override + public void orphan() throws DataException { + // Revert + Account creator = getCreator(); + + Integer previousDefaultGroupId = setGroupTransactionData.getPreviousDefaultGroupId(); + if (previousDefaultGroupId == null) + previousDefaultGroupId = Group.DEFAULT_GROUP; + + creator.setDefaultGroupId(previousDefaultGroupId); + + // Delete this transaction itself + this.repository.getTransactionRepository().delete(setGroupTransactionData); + + // Update creator's balance + creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(setGroupTransactionData.getFee())); + + // Update admin's reference + creator.setLastReference(setGroupTransactionData.getReference()); + } + +} diff --git a/src/main/java/org/qora/transaction/Transaction.java b/src/main/java/org/qora/transaction/Transaction.java index 4e5754ac..6c78185a 100644 --- a/src/main/java/org/qora/transaction/Transaction.java +++ b/src/main/java/org/qora/transaction/Transaction.java @@ -17,12 +17,13 @@ import org.qora.account.PublicKeyAccount; import org.qora.block.BlockChain; import org.qora.data.block.BlockData; import org.qora.data.transaction.TransactionData; +import org.qora.group.Group; import org.qora.repository.DataException; +import org.qora.repository.GroupRepository; import org.qora.repository.Repository; import org.qora.settings.Settings; import org.qora.transform.TransformationException; import org.qora.transform.transaction.TransactionTransformer; -import org.qora.utils.Base58; import org.qora.utils.NTP; import static java.util.Arrays.stream; @@ -36,40 +37,43 @@ public abstract class Transaction { // Transaction types public enum TransactionType { // NOTE: must be contiguous or reflection fails - GENESIS(1), - PAYMENT(2), - REGISTER_NAME(3), - UPDATE_NAME(4), - SELL_NAME(5), - CANCEL_SELL_NAME(6), - BUY_NAME(7), - CREATE_POLL(8), - VOTE_ON_POLL(9), - ARBITRARY(10), - ISSUE_ASSET(11), - TRANSFER_ASSET(12), - CREATE_ASSET_ORDER(13), - CANCEL_ASSET_ORDER(14), - MULTI_PAYMENT(15), - DEPLOY_AT(16), - MESSAGE(17), - DELEGATION(18), - SUPERNODE(19), - AIRDROP(20), - AT(21), - CREATE_GROUP(22), - UPDATE_GROUP(23), - ADD_GROUP_ADMIN(24), - REMOVE_GROUP_ADMIN(25), - GROUP_BAN(26), - CANCEL_GROUP_BAN(27), - GROUP_KICK(28), - GROUP_INVITE(29), - CANCEL_GROUP_INVITE(30), - JOIN_GROUP(31), - LEAVE_GROUP(32); + GENESIS(1, true), + PAYMENT(2, false), + REGISTER_NAME(3, false), + UPDATE_NAME(4, false), + SELL_NAME(5, false), + CANCEL_SELL_NAME(6, false), + BUY_NAME(7, false), + CREATE_POLL(8, false), + VOTE_ON_POLL(9, false), + ARBITRARY(10, false), + ISSUE_ASSET(11, false), + TRANSFER_ASSET(12, false), + CREATE_ASSET_ORDER(13, false), + CANCEL_ASSET_ORDER(14, false), + MULTI_PAYMENT(15, false), + DEPLOY_AT(16, false), + MESSAGE(17, false), + DELEGATION(18, false), + SUPERNODE(19, false), + AIRDROP(20, true), + AT(21, true), + CREATE_GROUP(22, false), + UPDATE_GROUP(23, true), + ADD_GROUP_ADMIN(24, true), + REMOVE_GROUP_ADMIN(25, true), + GROUP_BAN(26, true), + CANCEL_GROUP_BAN(27, true), + GROUP_KICK(28, true), + GROUP_INVITE(29, true), + CANCEL_GROUP_INVITE(30, true), + JOIN_GROUP(31, true), + LEAVE_GROUP(32, true), + GROUP_APPROVAL(33, true), + SET_GROUP(34, true); public final int value; + public final boolean skipsApproval; public final String valueString; public final String className; public final Class clazz; @@ -77,8 +81,9 @@ public abstract class Transaction { private final static Map map = stream(TransactionType.values()).collect(toMap(type -> type.value, type -> type)); - TransactionType(int value) { + TransactionType(int value, boolean skipsApproval) { this.value = value; + this.skipsApproval = skipsApproval; this.valueString = String.valueOf(value); String[] classNameParts = this.name().toLowerCase().split("_"); @@ -173,6 +178,11 @@ public abstract class Transaction { BAN_UNKNOWN(59), BANNED_FROM_GROUP(60), JOIN_REQUEST_EXISTS(61), + INVALID_GROUP_APPROVAL_THRESHOLD(62), + GROUP_ID_MISMATCH(63), + INVALID_GROUP_ID(64), + TRANSACTION_UNKNOWN(65), + TRANSACTION_ALREADY_CONFIRMED(66), NOT_YET_RELEASED(1000); public final int value; @@ -443,8 +453,7 @@ public abstract class Transaction { * Returns whether transaction can be added to unconfirmed transactions. *

* NOTE: temporarily updates creator's lastReference to that from - * unconfirmed transactions, and hence calls repository.discardChanges() - * before exit. + * unconfirmed transactions, and hence uses a repository savepoint. *

* This is not done normally because we don't want unconfirmed transactions affecting validity of transactions already included in a block. * @@ -462,11 +471,16 @@ public abstract class Transaction { if (this.transactionData.getTimestamp() > maxTimestamp) return ValidationResult.TIMESTAMP_TOO_NEW; + repository.setSavepoint(); try { PublicKeyAccount creator = this.getCreator(); if (creator == null) return ValidationResult.MISSING_CREATOR; + // Check transaction's txGroupId + if (!this.isValidTxGroupId()) + return ValidationResult.INVALID_GROUP_ID; + creator.setLastReference(creator.getUnconfirmedLastReference()); ValidationResult result = this.isValid(); @@ -476,10 +490,35 @@ public abstract class Transaction { return result; } finally { - repository.discardChanges(); + repository.rollbackToSavepoint(); } } + private boolean isValidTxGroupId() throws DataException { + // Does this transaction type bypass approval? + if (this.transactionData.getType().skipsApproval) + return true; + + int txGroupId = this.getEffectiveGroupId(); + if (txGroupId == Group.NO_GROUP) + return true; + + Group group = new Group(repository, txGroupId); + if (group.getGroupData() == null) { + // Group no longer exists? Possibly due to blockchain orphaning undoing group creation? + return false; + } + + GroupRepository groupRepository = this.repository.getGroupRepository(); + + // Is transaction's creator is group member? + PublicKeyAccount creator = this.getCreator(); + if (groupRepository.memberExists(txGroupId, creator.getAddress())) + return true; + + return false; + } + private int countUnconfirmedByCreator(PublicKeyAccount creator) throws DataException { List unconfirmedTransactions = repository.getTransactionRepository().getUnconfirmedTransactions(); @@ -499,8 +538,7 @@ public abstract class Transaction { * Returns sorted, unconfirmed transactions, deleting invalid. *

* NOTE: temporarily updates accounts' lastReference to that from - * unconfirmed transactions, and hence calls repository.discardChanges() - * before exit. + * unconfirmed transactions, hence uses a repository savepoint. * * @return sorted unconfirmed transactions * @throws DataException @@ -508,42 +546,64 @@ public abstract class Transaction { public static List getUnconfirmedTransactions(Repository repository) throws DataException { BlockData latestBlockData = repository.getBlockRepository().getLastBlock(); + List unconfirmedTransactions = repository.getTransactionRepository().getUnconfirmedTransactions(); + + unconfirmedTransactions.sort(getDataComparator()); + + repository.setSavepoint(); + try { + for (int i = 0; i < unconfirmedTransactions.size(); ++i) { + TransactionData transactionData = unconfirmedTransactions.get(i); + + if (!isStillValidUnconfirmed(repository, transactionData, latestBlockData.getTimestamp())) { + unconfirmedTransactions.remove(i); + --i; + continue; + } + } + } finally { + // Throw away temporary updates to account lastReference + repository.rollbackToSavepoint(); + } + + return unconfirmedTransactions; + } + + public static List getInvalidTransactions(Repository repository) throws DataException { + BlockData latestBlockData = repository.getBlockRepository().getLastBlock(); + List unconfirmedTransactions = repository.getTransactionRepository().getUnconfirmedTransactions(); List invalidTransactions = new ArrayList<>(); unconfirmedTransactions.sort(getDataComparator()); - for (int i = 0; i < unconfirmedTransactions.size(); ++i) { - TransactionData transactionData = unconfirmedTransactions.get(i); + repository.setSavepoint(); + try { + for (int i = 0; i < unconfirmedTransactions.size(); ++i) { + TransactionData transactionData = unconfirmedTransactions.get(i); - if (!isStillValidUnconfirmed(repository, transactionData, latestBlockData.getTimestamp())) { - invalidTransactions.add(transactionData); + if (!isStillValidUnconfirmed(repository, transactionData, latestBlockData.getTimestamp())) { + invalidTransactions.add(transactionData); - unconfirmedTransactions.remove(i); - --i; - continue; + unconfirmedTransactions.remove(i); + --i; + continue; + } } + } finally { + // Throw away temporary updates to account lastReference + repository.rollbackToSavepoint(); } - // Throw away temporary updates to account lastReference - repository.discardChanges(); - - // 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(); - - return unconfirmedTransactions; + return invalidTransactions; } /** * Returns whether transaction is still a valid unconfirmed transaction. *

* NOTE: temporarily updates creator's lastReference to that from - * unconfirmed transactions, and hence caller should invoke - * repository.discardChanges(). + * unconfirmed transactions, and hence caller should use a repository + * savepoint or invoke repository.discardChanges(). * * @return true if transaction can be added to unconfirmed transactions, false otherwise * @throws DataException @@ -567,6 +627,75 @@ public abstract class Transaction { return true; } + /** + * Returns transaction's effective groupID, using default values where necessary. + */ + public int getEffectiveGroupId() throws DataException { + int txGroupId = this.transactionData.getTxGroupId(); + + // If transaction's groupID is NO_GROUP then group-ness doesn't apply + if (txGroupId == Group.NO_GROUP) { + if (BlockChain.getInstance().getGrouplessAllowed()) + return txGroupId; + else + txGroupId = Group.DEFAULT_GROUP; + } + + // If transaction's groupID is not DEFAULT_GROUP then no further processing required + if (txGroupId != Group.DEFAULT_GROUP) + return txGroupId; + + // Try using account's default groupID + PublicKeyAccount creator = this.getCreator(); + txGroupId = creator.getDefaultGroupId(); + + // If transaction's groupID is NO_GROUP then group-ness doesn't apply + if (txGroupId == Group.NO_GROUP) { + if (BlockChain.getInstance().getGrouplessAllowed()) + return txGroupId; + else + txGroupId = Group.DEFAULT_GROUP; + } + + // If txGroupId now not DEFAULT_GROUP then no further processing required + if (txGroupId != Group.DEFAULT_GROUP) + return txGroupId; + + // Still zero? Use blockchain default + return BlockChain.getInstance().getDefaultGroupId(); + } + + /** + * Returns whether transaction still requires group-admin approval. + * + * @throws DataException + */ + public boolean needsGroupApproval() throws DataException { + // Does this transaction type bypass approval? + if (this.transactionData.getType().skipsApproval) + return false; + + int txGroupId = this.getEffectiveGroupId(); + + if (txGroupId == Group.NO_GROUP) + return false; + + Group group = new Group(repository, txGroupId); + if (group.getGroupData() == null) { + // Group no longer exists? Possibly due to blockchain orphaning undoing group creation? + return true; + } + + GroupRepository groupRepository = this.repository.getGroupRepository(); + + // If transaction's creator is group admin then auto-approve + PublicKeyAccount creator = this.getCreator(); + if (groupRepository.adminExists(txGroupId, creator.getAddress())) + return false; + + return group.getGroupData().getApprovalThreshold().needsApproval(repository, txGroupId, this.transactionData.getSignature()); + } + /** * Returns whether transaction can be added to the blockchain. *

diff --git a/src/main/java/org/qora/transaction/UpdateGroupTransaction.java b/src/main/java/org/qora/transaction/UpdateGroupTransaction.java index ddd9b685..eea0fe6d 100644 --- a/src/main/java/org/qora/transaction/UpdateGroupTransaction.java +++ b/src/main/java/org/qora/transaction/UpdateGroupTransaction.java @@ -80,6 +80,10 @@ public class UpdateGroupTransaction extends Transaction { if (!Crypto.isValidAddress(updateGroupTransactionData.getNewOwner())) return ValidationResult.INVALID_ADDRESS; + // Check new approval threshold is valid + if (updateGroupTransactionData.getNewApprovalThreshold() == null) + return ValidationResult.INVALID_GROUP_APPROVAL_THRESHOLD; + // Check new description size bounds int newDescriptionLength = Utf8.encodedLength(updateGroupTransactionData.getNewDescription()); if (newDescriptionLength < 1 || newDescriptionLength > Group.MAX_DESCRIPTION_SIZE) @@ -91,6 +95,10 @@ public class UpdateGroupTransaction extends Transaction { if (groupData == null) return ValidationResult.GROUP_DOES_NOT_EXIST; + // Check transaction's groupID matches group's ID + if (groupData.getGroupId() != updateGroupTransactionData.getTxGroupId()) + return ValidationResult.GROUP_ID_MISMATCH; + Account owner = getOwner(); // Check transaction's public key matches group's current owner diff --git a/src/main/java/org/qora/transform/Transformer.java b/src/main/java/org/qora/transform/Transformer.java index 6df49338..bc125d68 100644 --- a/src/main/java/org/qora/transform/Transformer.java +++ b/src/main/java/org/qora/transform/Transformer.java @@ -3,6 +3,7 @@ package org.qora.transform; public abstract class Transformer { public static final int BOOLEAN_LENGTH = 1; + public static final int BYTE_LENGTH = 1; public static final int INT_LENGTH = 4; public static final int LONG_LENGTH = 8; public static final int BIG_DECIMAL_LENGTH = 8; diff --git a/src/main/java/org/qora/transform/block/BlockTransformer.java b/src/main/java/org/qora/transform/block/BlockTransformer.java index 748f9368..5cb29e0a 100644 --- a/src/main/java/org/qora/transform/block/BlockTransformer.java +++ b/src/main/java/org/qora/transform/block/BlockTransformer.java @@ -8,17 +8,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; import org.qora.account.PublicKeyAccount; -import org.qora.asset.Order; import org.qora.block.Block; -import org.qora.data.asset.TradeData; import org.qora.data.at.ATStateData; import org.qora.data.block.BlockData; import org.qora.data.transaction.TransactionData; import org.qora.repository.DataException; -import org.qora.transaction.CreateAssetOrderTransaction; import org.qora.transaction.Transaction; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; @@ -28,7 +23,6 @@ import org.qora.utils.Base58; import org.qora.utils.Serialization; import org.qora.utils.Triple; -import com.google.common.hash.HashCode; import com.google.common.primitives.Bytes; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; @@ -283,87 +277,6 @@ public class BlockTransformer extends Transformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(Block block) throws TransformationException { - BlockData blockData = block.getBlockData(); - - JSONObject json = new JSONObject(); - - json.put("version", blockData.getVersion()); - json.put("timestamp", blockData.getTimestamp()); - json.put("generatingBalance", blockData.getGeneratingBalance()); - json.put("generator", PublicKeyAccount.getAddress(blockData.getGeneratorPublicKey())); - json.put("generatorPublicKey", Base58.encode(blockData.getGeneratorPublicKey())); - json.put("fee", blockData.getTotalFees().toPlainString()); - json.put("transactionsSignature", Base58.encode(blockData.getTransactionsSignature())); - json.put("generatorSignature", Base58.encode(blockData.getGeneratorSignature())); - json.put("signature", Base58.encode(blockData.getSignature())); - - if (blockData.getReference() != null) - json.put("reference", Base58.encode(blockData.getReference())); - - json.put("height", blockData.getHeight()); - - // Add transaction info - JSONArray transactionsJson = new JSONArray(); - - // XXX this should be moved out to API as it requires repository access - boolean tradesHappened = false; - - try { - for (Transaction transaction : block.getTransactions()) { - transactionsJson.add(TransactionTransformer.toJSON(transaction.getTransactionData())); - - // If this is an asset CreateOrderTransaction then check to see if any trades happened - if (transaction.getTransactionData().getType() == Transaction.TransactionType.CREATE_ASSET_ORDER) { - CreateAssetOrderTransaction orderTransaction = (CreateAssetOrderTransaction) transaction; - Order order = orderTransaction.getOrder(); - List trades = order.getTrades(); - - if (trades.stream().anyMatch(tradeData -> Arrays.equals(tradeData.getInitiator(), order.getOrderData().getOrderId()))) { - tradesHappened = true; - // No need to check any further - break; - } - } - } - } catch (DataException e) { - throw new TransformationException("Unable to transform block into JSON", e); - } - json.put("transactions", transactionsJson); - - // Add asset trade activity flag - json.put("assetTrades", tradesHappened); - - // Add CIYAM AT info (if any) - if (blockData.getATCount() > 0) { - JSONArray atsJson = new JSONArray(); - - try { - for (ATStateData atStateData : block.getATStates()) { - JSONObject atJson = new JSONObject(); - - atJson.put("AT", atStateData.getATAddress()); - atJson.put("stateHash", HashCode.fromBytes(atStateData.getStateHash()).toString()); - - if (blockData.getVersion() >= 4) - atJson.put("fees", atStateData.getFees().toPlainString()); - - atsJson.add(atJson); - } - } catch (DataException e) { - throw new TransformationException("Unable to transform block into JSON", e); - } - - json.put("ATs", atsJson); - - if (blockData.getVersion() >= 2) - json.put("atFees", blockData.getATFees()); - } - - return json; - } - public static byte[] getBytesForGeneratorSignature(BlockData blockData) throws TransformationException { byte[] generatorSignature = Arrays.copyOf(blockData.getReference(), GENERATOR_SIGNATURE_LENGTH); PublicKeyAccount generator = new PublicKeyAccount(null, blockData.getGeneratorPublicKey()); diff --git a/src/main/java/org/qora/transform/transaction/AddGroupAdminTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/AddGroupAdminTransactionTransformer.java index 69460da3..a2595684 100644 --- a/src/main/java/org/qora/transform/transaction/AddGroupAdminTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/AddGroupAdminTransactionTransformer.java @@ -5,26 +5,22 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.AddGroupAdminTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class AddGroupAdminTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int OWNER_LENGTH = PUBLIC_KEY_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH; private static final int MEMBER_LENGTH = ADDRESS_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + OWNER_LENGTH + GROUPID_LENGTH + MEMBER_LENGTH; + private static final int EXTRAS_LENGTH = GROUPID_LENGTH + MEMBER_LENGTH; protected static final TransactionLayout layout; @@ -32,6 +28,7 @@ public class AddGroupAdminTransactionTransformer extends TransactionTransformer layout = new TransactionLayout(); layout.add("txType: " + TransactionType.ADD_GROUP_ADMIN.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("group owner's public key", TransformationType.PUBLIC_KEY); layout.add("group ID", TransformationType.INT); @@ -43,6 +40,10 @@ public class AddGroupAdminTransactionTransformer extends TransactionTransformer public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -57,11 +58,11 @@ public class AddGroupAdminTransactionTransformer extends TransactionTransformer byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new AddGroupAdminTransactionData(ownerPublicKey, groupId, member, fee, timestamp, reference, signature); + return new AddGroupAdminTransactionData(timestamp, txGroupId, reference, ownerPublicKey, groupId, member, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -70,12 +71,10 @@ public class AddGroupAdminTransactionTransformer extends TransactionTransformer ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(addGroupAdminTransactionData.getType().value)); - bytes.write(Longs.toByteArray(addGroupAdminTransactionData.getTimestamp())); - bytes.write(addGroupAdminTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(addGroupAdminTransactionData.getCreatorPublicKey()); bytes.write(Ints.toByteArray(addGroupAdminTransactionData.getGroupId())); + Serialization.serializeAddress(bytes, addGroupAdminTransactionData.getMember()); Serialization.serializeBigDecimal(bytes, addGroupAdminTransactionData.getFee()); @@ -89,25 +88,4 @@ public class AddGroupAdminTransactionTransformer extends TransactionTransformer } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - AddGroupAdminTransactionData addGroupAdminTransactionData = (AddGroupAdminTransactionData) transactionData; - - byte[] ownerPublicKey = addGroupAdminTransactionData.getOwnerPublicKey(); - - json.put("owner", PublicKeyAccount.getAddress(ownerPublicKey)); - json.put("ownerPublicKey", HashCode.fromBytes(ownerPublicKey).toString()); - - json.put("groupId", addGroupAdminTransactionData.getGroupId()); - json.put("member", addGroupAdminTransactionData.getMember()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/ArbitraryTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/ArbitraryTransactionTransformer.java index e592cb6d..42876a44 100644 --- a/src/main/java/org/qora/transform/transaction/ArbitraryTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/ArbitraryTransactionTransformer.java @@ -8,9 +8,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; import org.qora.block.BlockChain; import org.qora.data.PaymentData; import org.qora.data.transaction.ArbitraryTransactionData; @@ -20,23 +17,17 @@ import org.qora.transaction.ArbitraryTransaction; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.PaymentTransformer; import org.qora.transform.TransformationException; -import org.qora.utils.Base58; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class ArbitraryTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int SENDER_LENGTH = PUBLIC_KEY_LENGTH; private static final int SERVICE_LENGTH = INT_LENGTH; private static final int DATA_SIZE_LENGTH = INT_LENGTH; - private static final int PAYMENTS_COUNT_LENGTH = INT_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH_V1 = BASE_TYPELESS_LENGTH + SENDER_LENGTH + SERVICE_LENGTH + DATA_SIZE_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH_V3 = BASE_TYPELESS_LENGTH + SENDER_LENGTH + PAYMENTS_COUNT_LENGTH + SERVICE_LENGTH + DATA_SIZE_LENGTH; + private static final int EXTRAS_LENGTH = SERVICE_LENGTH + DATA_SIZE_LENGTH; protected static final TransactionLayout layout; @@ -44,6 +35,7 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.ARBITRARY.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("sender's public key", TransformationType.PUBLIC_KEY); layout.add("number of payments", TransformationType.INT); @@ -64,6 +56,10 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer { int version = ArbitraryTransaction.getVersionByTimestamp(timestamp); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -93,17 +89,20 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new ArbitraryTransactionData(version, senderPublicKey, service, data, DataType.RAW_DATA, payments, fee, timestamp, reference, signature); + return new ArbitraryTransactionData(timestamp, txGroupId, reference, senderPublicKey, version, service, data, DataType.RAW_DATA, payments, fee, + signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { ArbitraryTransactionData arbitraryTransactionData = (ArbitraryTransactionData) transactionData; - if (arbitraryTransactionData.getVersion() == 1) - return TYPE_LENGTH + TYPELESS_DATALESS_LENGTH_V1 + arbitraryTransactionData.getData().length; - else - return TYPE_LENGTH + TYPELESS_DATALESS_LENGTH_V3 + arbitraryTransactionData.getData().length - + arbitraryTransactionData.getPayments().size() * PaymentTransformer.getDataLength(); + int length = getBaseLength(transactionData) + EXTRAS_LENGTH; + + // V3+ transactions have optional payments + if (arbitraryTransactionData.getVersion() >= 3) + length += arbitraryTransactionData.getData().length + arbitraryTransactionData.getPayments().size() * PaymentTransformer.getDataLength(); + + return length; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -112,11 +111,7 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(arbitraryTransactionData.getType().value)); - bytes.write(Longs.toByteArray(arbitraryTransactionData.getTimestamp())); - bytes.write(arbitraryTransactionData.getReference()); - - bytes.write(arbitraryTransactionData.getSenderPublicKey()); + transformCommonBytes(transactionData, bytes); if (arbitraryTransactionData.getVersion() != 1) { List payments = arbitraryTransactionData.getPayments(); @@ -170,36 +165,4 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer { return Arrays.copyOfRange(bytes, v1Start, bytes.length); } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - ArbitraryTransactionData arbitraryTransactionData = (ArbitraryTransactionData) transactionData; - - byte[] senderPublicKey = arbitraryTransactionData.getSenderPublicKey(); - - json.put("version", arbitraryTransactionData.getVersion()); - json.put("sender", PublicKeyAccount.getAddress(senderPublicKey)); - json.put("senderPublicKey", HashCode.fromBytes(senderPublicKey).toString()); - - json.put("service", arbitraryTransactionData.getService()); - json.put("data", Base58.encode(arbitraryTransactionData.getData())); - - if (arbitraryTransactionData.getVersion() != 1) { - List payments = arbitraryTransactionData.getPayments(); - JSONArray paymentsJson = new JSONArray(); - - for (PaymentData paymentData : payments) - paymentsJson.add(PaymentTransformer.toJSON(paymentData)); - - json.put("payments", paymentsJson); - } - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/AtTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/AtTransactionTransformer.java index 3500f670..5598c815 100644 --- a/src/main/java/org/qora/transform/transaction/AtTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/AtTransactionTransformer.java @@ -1,94 +1,26 @@ package org.qora.transform.transaction; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.data.transaction.ATTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transform.TransformationException; -import org.qora.utils.Serialization; - -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class AtTransactionTransformer extends TransactionTransformer { + protected static final TransactionLayout layout = null; + // Property lengths - private static final int SENDER_LENGTH = ADDRESS_LENGTH; - private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; - private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; - private static final int ASSET_ID_LENGTH = LONG_LENGTH; - private static final int DATA_SIZE_LENGTH = INT_LENGTH; - - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + SENDER_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH + ASSET_ID_LENGTH - + DATA_SIZE_LENGTH; - public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { throw new TransformationException("Serialized AT Transactions should not exist!"); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - ATTransactionData atTransactionData = (ATTransactionData) transactionData; - - return TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + atTransactionData.getMessage().length; + // AT Transactions aren't serialized so don't take up any space in the block. + return 0; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { - try { - ATTransactionData atTransactionData = (ATTransactionData) transactionData; - - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - - bytes.write(Ints.toByteArray(atTransactionData.getType().value)); - bytes.write(Longs.toByteArray(atTransactionData.getTimestamp())); - bytes.write(atTransactionData.getReference()); - - Serialization.serializeAddress(bytes, atTransactionData.getATAddress()); - - Serialization.serializeAddress(bytes, atTransactionData.getRecipient()); - - // Only emit amount if greater than zero (safer than checking assetId) - if (atTransactionData.getAmount().compareTo(BigDecimal.ZERO) > 0) { - Serialization.serializeBigDecimal(bytes, atTransactionData.getAmount()); - bytes.write(Longs.toByteArray(atTransactionData.getAssetId())); - } - - byte[] message = atTransactionData.getMessage(); - if (message.length > 0) { - bytes.write(Ints.toByteArray(message.length)); - bytes.write(message); - } else { - bytes.write(Ints.toByteArray(0)); - } - - Serialization.serializeBigDecimal(bytes, atTransactionData.getFee()); - - if (atTransactionData.getSignature() != null) - bytes.write(atTransactionData.getSignature()); - - return bytes.toByteArray(); - } catch (IOException | ClassCastException e) { - throw new TransformationException(e); - } - } - - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - ATTransactionData atTransactionData = (ATTransactionData) transactionData; - - json.put("sender", atTransactionData.getATAddress()); - - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; + throw new TransformationException("Serialized AT Transactions should not exist!"); } } diff --git a/src/main/java/org/qora/transform/transaction/BuyNameTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/BuyNameTransactionTransformer.java index c27b6c50..8c895dbc 100644 --- a/src/main/java/org/qora/transform/transaction/BuyNameTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/BuyNameTransactionTransformer.java @@ -5,8 +5,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.BuyNameTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.naming.Name; @@ -15,19 +14,15 @@ import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class BuyNameTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int BUYER_LENGTH = PUBLIC_KEY_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; private static final int SELLER_LENGTH = ADDRESS_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + BUYER_LENGTH + NAME_SIZE_LENGTH + AMOUNT_LENGTH + SELLER_LENGTH; + private static final int EXTRAS_LENGTH = NAME_SIZE_LENGTH + AMOUNT_LENGTH + SELLER_LENGTH; protected static final TransactionLayout layout; @@ -35,6 +30,7 @@ public class BuyNameTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.BUY_NAME.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("buyer's public key", TransformationType.PUBLIC_KEY); layout.add("name length", TransformationType.INT); @@ -48,6 +44,10 @@ public class BuyNameTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -64,15 +64,13 @@ public class BuyNameTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new BuyNameTransactionData(buyerPublicKey, name, amount, seller, fee, timestamp, reference, signature); + return new BuyNameTransactionData(timestamp, txGroupId, reference, buyerPublicKey, name, amount, seller, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { BuyNameTransactionData buyNameTransactionData = (BuyNameTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(buyNameTransactionData.getName()); - - return dataLength; + return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(buyNameTransactionData.getName()); } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -81,13 +79,12 @@ public class BuyNameTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(buyNameTransactionData.getType().value)); - bytes.write(Longs.toByteArray(buyNameTransactionData.getTimestamp())); - bytes.write(buyNameTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(buyNameTransactionData.getBuyerPublicKey()); Serialization.serializeSizedString(bytes, buyNameTransactionData.getName()); + Serialization.serializeBigDecimal(bytes, buyNameTransactionData.getAmount()); + Serialization.serializeAddress(bytes, buyNameTransactionData.getSeller()); Serialization.serializeBigDecimal(bytes, buyNameTransactionData.getFee()); @@ -101,27 +98,4 @@ public class BuyNameTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - BuyNameTransactionData buyNameTransactionData = (BuyNameTransactionData) transactionData; - - byte[] buyerPublicKey = buyNameTransactionData.getBuyerPublicKey(); - - json.put("buyer", PublicKeyAccount.getAddress(buyerPublicKey)); - json.put("buyerPublicKey", HashCode.fromBytes(buyerPublicKey).toString()); - - json.put("name", buyNameTransactionData.getName()); - json.put("amount", buyNameTransactionData.getAmount().toPlainString()); - - json.put("seller", buyNameTransactionData.getSeller()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/CancelAssetOrderTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/CancelAssetOrderTransactionTransformer.java index 0bdab13d..b653941a 100644 --- a/src/main/java/org/qora/transform/transaction/CancelAssetOrderTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/CancelAssetOrderTransactionTransformer.java @@ -5,26 +5,19 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.CancelAssetOrderTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; -import org.qora.utils.Base58; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; - public class CancelAssetOrderTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int CREATOR_LENGTH = PUBLIC_KEY_LENGTH; private static final int ORDER_ID_LENGTH = SIGNATURE_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + CREATOR_LENGTH + ORDER_ID_LENGTH; + private static final int EXTRAS_LENGTH = ORDER_ID_LENGTH; protected static final TransactionLayout layout; @@ -32,6 +25,7 @@ public class CancelAssetOrderTransactionTransformer extends TransactionTransform layout = new TransactionLayout(); layout.add("txType: " + TransactionType.CANCEL_ASSET_ORDER.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("order creator's public key", TransformationType.PUBLIC_KEY); layout.add("order ID to cancel", TransformationType.SIGNATURE); @@ -42,6 +36,10 @@ public class CancelAssetOrderTransactionTransformer extends TransactionTransform public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -55,11 +53,11 @@ public class CancelAssetOrderTransactionTransformer extends TransactionTransform byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new CancelAssetOrderTransactionData(creatorPublicKey, orderId, fee, timestamp, reference, signature); + return new CancelAssetOrderTransactionData(timestamp, txGroupId, reference, creatorPublicKey, orderId, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -68,11 +66,8 @@ public class CancelAssetOrderTransactionTransformer extends TransactionTransform ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(cancelOrderTransactionData.getType().value)); - bytes.write(Longs.toByteArray(cancelOrderTransactionData.getTimestamp())); - bytes.write(cancelOrderTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(cancelOrderTransactionData.getCreatorPublicKey()); bytes.write(cancelOrderTransactionData.getOrderId()); Serialization.serializeBigDecimal(bytes, cancelOrderTransactionData.getFee()); @@ -86,24 +81,4 @@ public class CancelAssetOrderTransactionTransformer extends TransactionTransform } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - CancelAssetOrderTransactionData cancelOrderTransactionData = (CancelAssetOrderTransactionData) transactionData; - - byte[] creatorPublicKey = cancelOrderTransactionData.getCreatorPublicKey(); - - json.put("creator", PublicKeyAccount.getAddress(creatorPublicKey)); - json.put("creatorPublicKey", HashCode.fromBytes(creatorPublicKey).toString()); - - json.put("order", Base58.encode(cancelOrderTransactionData.getOrderId())); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/CancelGroupBanTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/CancelGroupBanTransactionTransformer.java index 07ec79f1..4a2317fa 100644 --- a/src/main/java/org/qora/transform/transaction/CancelGroupBanTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/CancelGroupBanTransactionTransformer.java @@ -5,26 +5,22 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.CancelGroupBanTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class CancelGroupBanTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int ADMIN_LENGTH = PUBLIC_KEY_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH; private static final int MEMBER_LENGTH = ADDRESS_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + ADMIN_LENGTH + GROUPID_LENGTH + MEMBER_LENGTH; + private static final int EXTRAS_LENGTH = GROUPID_LENGTH + MEMBER_LENGTH; protected static final TransactionLayout layout; @@ -32,6 +28,7 @@ public class CancelGroupBanTransactionTransformer extends TransactionTransformer layout = new TransactionLayout(); layout.add("txType: " + TransactionType.CANCEL_GROUP_BAN.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("group admin's public key", TransformationType.PUBLIC_KEY); layout.add("group ID", TransformationType.INT); @@ -43,6 +40,10 @@ public class CancelGroupBanTransactionTransformer extends TransactionTransformer public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -57,11 +58,11 @@ public class CancelGroupBanTransactionTransformer extends TransactionTransformer byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new CancelGroupBanTransactionData(adminPublicKey, groupId, member, fee, timestamp, reference, signature); + return new CancelGroupBanTransactionData(timestamp, txGroupId, reference, adminPublicKey, groupId, member, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -70,12 +71,10 @@ public class CancelGroupBanTransactionTransformer extends TransactionTransformer ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(groupUnbanTransactionData.getType().value)); - bytes.write(Longs.toByteArray(groupUnbanTransactionData.getTimestamp())); - bytes.write(groupUnbanTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(groupUnbanTransactionData.getCreatorPublicKey()); bytes.write(Ints.toByteArray(groupUnbanTransactionData.getGroupId())); + Serialization.serializeAddress(bytes, groupUnbanTransactionData.getMember()); Serialization.serializeBigDecimal(bytes, groupUnbanTransactionData.getFee()); @@ -89,25 +88,4 @@ public class CancelGroupBanTransactionTransformer extends TransactionTransformer } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - CancelGroupBanTransactionData groupUnbanTransactionData = (CancelGroupBanTransactionData) transactionData; - - byte[] adminPublicKey = groupUnbanTransactionData.getAdminPublicKey(); - - json.put("admin", PublicKeyAccount.getAddress(adminPublicKey)); - json.put("adminPublicKey", HashCode.fromBytes(adminPublicKey).toString()); - - json.put("groupId", groupUnbanTransactionData.getGroupId()); - json.put("member", groupUnbanTransactionData.getMember()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/CancelGroupInviteTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/CancelGroupInviteTransactionTransformer.java index 1a0f6a77..6c702d7d 100644 --- a/src/main/java/org/qora/transform/transaction/CancelGroupInviteTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/CancelGroupInviteTransactionTransformer.java @@ -5,26 +5,22 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.CancelGroupInviteTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class CancelGroupInviteTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int ADMIN_LENGTH = PUBLIC_KEY_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH; private static final int INVITEE_LENGTH = ADDRESS_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + ADMIN_LENGTH + GROUPID_LENGTH + INVITEE_LENGTH; + private static final int EXTRAS_LENGTH = GROUPID_LENGTH + INVITEE_LENGTH; protected static final TransactionLayout layout; @@ -32,6 +28,7 @@ public class CancelGroupInviteTransactionTransformer extends TransactionTransfor layout = new TransactionLayout(); layout.add("txType: " + TransactionType.CANCEL_GROUP_INVITE.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("group admin's public key", TransformationType.PUBLIC_KEY); layout.add("group ID", TransformationType.INT); @@ -43,6 +40,10 @@ public class CancelGroupInviteTransactionTransformer extends TransactionTransfor public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -57,11 +58,11 @@ public class CancelGroupInviteTransactionTransformer extends TransactionTransfor byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new CancelGroupInviteTransactionData(adminPublicKey, groupId, invitee, fee, timestamp, reference, signature); + return new CancelGroupInviteTransactionData(timestamp, txGroupId, reference, adminPublicKey, groupId, invitee, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -70,12 +71,10 @@ public class CancelGroupInviteTransactionTransformer extends TransactionTransfor ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(cancelGroupInviteTransactionData.getType().value)); - bytes.write(Longs.toByteArray(cancelGroupInviteTransactionData.getTimestamp())); - bytes.write(cancelGroupInviteTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(cancelGroupInviteTransactionData.getCreatorPublicKey()); bytes.write(Ints.toByteArray(cancelGroupInviteTransactionData.getGroupId())); + Serialization.serializeAddress(bytes, cancelGroupInviteTransactionData.getInvitee()); Serialization.serializeBigDecimal(bytes, cancelGroupInviteTransactionData.getFee()); @@ -89,25 +88,4 @@ public class CancelGroupInviteTransactionTransformer extends TransactionTransfor } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - CancelGroupInviteTransactionData cancelGroupInviteTransactionData = (CancelGroupInviteTransactionData) transactionData; - - byte[] adminPublicKey = cancelGroupInviteTransactionData.getAdminPublicKey(); - - json.put("admin", PublicKeyAccount.getAddress(adminPublicKey)); - json.put("adminPublicKey", HashCode.fromBytes(adminPublicKey).toString()); - - json.put("groupId", cancelGroupInviteTransactionData.getGroupId()); - json.put("invitee", cancelGroupInviteTransactionData.getInvitee()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } \ No newline at end of file diff --git a/src/main/java/org/qora/transform/transaction/CancelSellNameTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/CancelSellNameTransactionTransformer.java index 5e4f1d83..15fa07cc 100644 --- a/src/main/java/org/qora/transform/transaction/CancelSellNameTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/CancelSellNameTransactionTransformer.java @@ -5,8 +5,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.CancelSellNameTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.naming.Name; @@ -15,17 +14,13 @@ import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class CancelSellNameTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int OWNER_LENGTH = PUBLIC_KEY_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH; + private static final int EXTRAS_LENGTH = NAME_SIZE_LENGTH; protected static final TransactionLayout layout; @@ -33,6 +28,7 @@ public class CancelSellNameTransactionTransformer extends TransactionTransformer layout = new TransactionLayout(); layout.add("txType: " + TransactionType.CANCEL_SELL_NAME.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("name owner's public key", TransformationType.PUBLIC_KEY); layout.add("name length", TransformationType.INT); @@ -44,6 +40,10 @@ public class CancelSellNameTransactionTransformer extends TransactionTransformer public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -56,15 +56,13 @@ public class CancelSellNameTransactionTransformer extends TransactionTransformer byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new CancelSellNameTransactionData(ownerPublicKey, name, fee, timestamp, reference, signature); + return new CancelSellNameTransactionData(timestamp, txGroupId, reference, ownerPublicKey, name, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { CancelSellNameTransactionData cancelSellNameTransactionData = (CancelSellNameTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(cancelSellNameTransactionData.getName()); - - return dataLength; + return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(cancelSellNameTransactionData.getName()); } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -73,11 +71,8 @@ public class CancelSellNameTransactionTransformer extends TransactionTransformer ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(cancelSellNameTransactionData.getType().value)); - bytes.write(Longs.toByteArray(cancelSellNameTransactionData.getTimestamp())); - bytes.write(cancelSellNameTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(cancelSellNameTransactionData.getOwnerPublicKey()); Serialization.serializeSizedString(bytes, cancelSellNameTransactionData.getName()); Serialization.serializeBigDecimal(bytes, cancelSellNameTransactionData.getFee()); @@ -91,24 +86,4 @@ public class CancelSellNameTransactionTransformer extends TransactionTransformer } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - CancelSellNameTransactionData cancelSellNameTransactionData = (CancelSellNameTransactionData) transactionData; - - byte[] ownerPublicKey = cancelSellNameTransactionData.getOwnerPublicKey(); - - json.put("owner", PublicKeyAccount.getAddress(ownerPublicKey)); - json.put("ownerPublicKey", HashCode.fromBytes(ownerPublicKey).toString()); - - json.put("name", cancelSellNameTransactionData.getName()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/CreateAssetOrderTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/CreateAssetOrderTransactionTransformer.java index bfbb2d28..2cb28685 100644 --- a/src/main/java/org/qora/transform/transaction/CreateAssetOrderTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/CreateAssetOrderTransactionTransformer.java @@ -5,8 +5,6 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; import org.qora.block.BlockChain; import org.qora.data.transaction.CreateAssetOrderTransactionData; import org.qora.data.transaction.TransactionData; @@ -14,18 +12,16 @@ import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; public class CreateAssetOrderTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int CREATOR_LENGTH = PUBLIC_KEY_LENGTH; private static final int ASSET_ID_LENGTH = LONG_LENGTH; private static final int AMOUNT_LENGTH = 12; // Not standard BIG_DECIMAL_LENGTH - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + CREATOR_LENGTH + (ASSET_ID_LENGTH + AMOUNT_LENGTH) * 2; + private static final int EXTRAS_LENGTH = (ASSET_ID_LENGTH + AMOUNT_LENGTH) * 2; protected static final TransactionLayout layout; @@ -33,6 +29,7 @@ public class CreateAssetOrderTransactionTransformer extends TransactionTransform layout = new TransactionLayout(); layout.add("txType: " + TransactionType.CREATE_ASSET_ORDER.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("order creator's public key", TransformationType.PUBLIC_KEY); layout.add("ID of asset of offer", TransformationType.LONG); @@ -46,6 +43,10 @@ public class CreateAssetOrderTransactionTransformer extends TransactionTransform public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -64,11 +65,11 @@ public class CreateAssetOrderTransactionTransformer extends TransactionTransform byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - 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); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -77,14 +78,14 @@ public class CreateAssetOrderTransactionTransformer extends TransactionTransform ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(createOrderTransactionData.getType().value)); - bytes.write(Longs.toByteArray(createOrderTransactionData.getTimestamp())); - bytes.write(createOrderTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(createOrderTransactionData.getCreatorPublicKey()); bytes.write(Longs.toByteArray(createOrderTransactionData.getHaveAssetId())); + bytes.write(Longs.toByteArray(createOrderTransactionData.getWantAssetId())); + Serialization.serializeBigDecimal(bytes, createOrderTransactionData.getAmount(), AMOUNT_LENGTH); + Serialization.serializeBigDecimal(bytes, createOrderTransactionData.getPrice(), AMOUNT_LENGTH); Serialization.serializeBigDecimal(bytes, createOrderTransactionData.getFee()); @@ -135,30 +136,4 @@ public class CreateAssetOrderTransactionTransformer extends TransactionTransform } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - CreateAssetOrderTransactionData createOrderTransactionData = (CreateAssetOrderTransactionData) transactionData; - - byte[] creatorPublicKey = createOrderTransactionData.getCreatorPublicKey(); - - json.put("creator", PublicKeyAccount.getAddress(creatorPublicKey)); - json.put("creatorPublicKey", HashCode.fromBytes(creatorPublicKey).toString()); - - JSONObject order = new JSONObject(); - order.put("have", createOrderTransactionData.getHaveAssetId()); - order.put("want", createOrderTransactionData.getWantAssetId()); - order.put("amount", createOrderTransactionData.getAmount().toPlainString()); - order.put("price", createOrderTransactionData.getPrice().toPlainString()); - - json.put("order", order); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/CreateGroupTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/CreateGroupTransactionTransformer.java index 122e8c74..e3dd313b 100644 --- a/src/main/java/org/qora/transform/transaction/CreateGroupTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/CreateGroupTransactionTransformer.java @@ -5,31 +5,27 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.CreateGroupTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.group.Group; +import org.qora.group.Group.ApprovalThreshold; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class CreateGroupTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int CREATOR_LENGTH = PUBLIC_KEY_LENGTH; private static final int OWNER_LENGTH = ADDRESS_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; private static final int DESCRIPTION_SIZE_LENGTH = INT_LENGTH; private static final int IS_OPEN_LENGTH = BOOLEAN_LENGTH; + private static final int APPROVAL_THRESHOLD_LENGTH = BYTE_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + CREATOR_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH - + IS_OPEN_LENGTH; + private static final int EXTRAS_LENGTH = OWNER_LENGTH + NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH + IS_OPEN_LENGTH + APPROVAL_THRESHOLD_LENGTH; protected static final TransactionLayout layout; @@ -37,6 +33,7 @@ public class CreateGroupTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.CREATE_GROUP.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("group creator's public key", TransformationType.PUBLIC_KEY); layout.add("group's name length", TransformationType.INT); @@ -44,6 +41,7 @@ public class CreateGroupTransactionTransformer extends TransactionTransformer { layout.add("group's description length", TransformationType.INT); layout.add("group's description", TransformationType.STRING); layout.add("is group \"open\"?", TransformationType.BOOLEAN); + layout.add("group transaction approval threshold", TransformationType.BYTE); layout.add("fee", TransformationType.AMOUNT); layout.add("signature", TransformationType.SIGNATURE); } @@ -51,6 +49,10 @@ public class CreateGroupTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -64,21 +66,22 @@ public class CreateGroupTransactionTransformer extends TransactionTransformer { boolean isOpen = byteBuffer.get() != 0; + ApprovalThreshold approvalThreshold = ApprovalThreshold.valueOf(byteBuffer.get()); + BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new CreateGroupTransactionData(creatorPublicKey, owner, groupName, description, isOpen, null, fee, timestamp, reference, signature); + return new CreateGroupTransactionData(timestamp, txGroupId, reference, creatorPublicKey, owner, groupName, description, isOpen, approvalThreshold, null, + fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { CreateGroupTransactionData createGroupTransactionData = (CreateGroupTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(createGroupTransactionData.getGroupName()) + return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(createGroupTransactionData.getGroupName()) + Utf8.encodedLength(createGroupTransactionData.getDescription()); - - return dataLength; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -87,17 +90,18 @@ public class CreateGroupTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(createGroupTransactionData.getType().value)); - bytes.write(Longs.toByteArray(createGroupTransactionData.getTimestamp())); - bytes.write(createGroupTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(createGroupTransactionData.getCreatorPublicKey()); Serialization.serializeAddress(bytes, createGroupTransactionData.getOwner()); + Serialization.serializeSizedString(bytes, createGroupTransactionData.getGroupName()); + Serialization.serializeSizedString(bytes, createGroupTransactionData.getDescription()); bytes.write((byte) (createGroupTransactionData.getIsOpen() ? 1 : 0)); + bytes.write((byte) createGroupTransactionData.getApprovalThreshold().value); + Serialization.serializeBigDecimal(bytes, createGroupTransactionData.getFee()); if (createGroupTransactionData.getSignature() != null) @@ -109,27 +113,4 @@ public class CreateGroupTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - CreateGroupTransactionData createGroupTransactionData = (CreateGroupTransactionData) transactionData; - - byte[] creatorPublicKey = createGroupTransactionData.getCreatorPublicKey(); - - json.put("creator", PublicKeyAccount.getAddress(creatorPublicKey)); - json.put("creatorPublicKey", HashCode.fromBytes(creatorPublicKey).toString()); - - json.put("owner", createGroupTransactionData.getOwner()); - json.put("groupName", createGroupTransactionData.getGroupName()); - json.put("description", createGroupTransactionData.getDescription()); - json.put("isOpen", createGroupTransactionData.getIsOpen()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/CreatePollTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/CreatePollTransactionTransformer.java index ee7b6aa0..9175ad64 100644 --- a/src/main/java/org/qora/transform/transaction/CreatePollTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/CreatePollTransactionTransformer.java @@ -7,9 +7,6 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; import org.qora.block.BlockChain; import org.qora.data.transaction.CreatePollTransactionData; import org.qora.data.transaction.TransactionData; @@ -20,21 +17,17 @@ import org.qora.utils.Serialization; import org.qora.voting.Poll; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class CreatePollTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int CREATOR_LENGTH = PUBLIC_KEY_LENGTH; private static final int OWNER_LENGTH = ADDRESS_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; private static final int DESCRIPTION_SIZE_LENGTH = INT_LENGTH; private static final int OPTIONS_SIZE_LENGTH = INT_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + CREATOR_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH - + OPTIONS_SIZE_LENGTH; + private static final int EXTRAS_LENGTH = OWNER_LENGTH + NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH + OPTIONS_SIZE_LENGTH; protected static final TransactionLayout layout; @@ -42,6 +35,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.CREATE_POLL.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("poll creator's public key", TransformationType.PUBLIC_KEY); layout.add("poll name length", TransformationType.INT); @@ -58,6 +52,10 @@ public class CreatePollTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -92,13 +90,13 @@ public class CreatePollTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - 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); } public static int getDataLength(TransactionData transactionData) throws TransformationException { CreatePollTransactionData createPollTransactionData = (CreatePollTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(createPollTransactionData.getPollName()) + int dataLength = getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(createPollTransactionData.getPollName()) + Utf8.encodedLength(createPollTransactionData.getDescription()); // Add lengths for each poll options @@ -120,16 +118,12 @@ public class CreatePollTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(createPollTransactionData.getType().value)); - - bytes.write(Longs.toByteArray(createPollTransactionData.getTimestamp())); - - bytes.write(createPollTransactionData.getReference()); - - bytes.write(createPollTransactionData.getCreatorPublicKey()); + transformCommonBytes(transactionData, bytes); Serialization.serializeAddress(bytes, createPollTransactionData.getOwner()); + Serialization.serializeSizedString(bytes, createPollTransactionData.getPollName()); + Serialization.serializeSizedString(bytes, createPollTransactionData.getDescription()); List pollOptions = createPollTransactionData.getPollOptions(); @@ -178,32 +172,4 @@ public class CreatePollTransactionTransformer extends TransactionTransformer { return bytes; } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - CreatePollTransactionData createPollTransactionData = (CreatePollTransactionData) transactionData; - - byte[] creatorPublicKey = createPollTransactionData.getCreatorPublicKey(); - - json.put("creator", PublicKeyAccount.getAddress(creatorPublicKey)); - json.put("creatorPublicKey", HashCode.fromBytes(creatorPublicKey).toString()); - - json.put("owner", createPollTransactionData.getOwner()); - json.put("name", createPollTransactionData.getPollName()); - json.put("description", createPollTransactionData.getDescription()); - - JSONArray options = new JSONArray(); - for (PollOptionData optionData : createPollTransactionData.getPollOptions()) - options.add(optionData.getOptionName()); - - json.put("options", options); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/DeployAtTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/DeployAtTransactionTransformer.java index 9317d8e2..6621aec9 100644 --- a/src/main/java/org/qora/transform/transaction/DeployAtTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/DeployAtTransactionTransformer.java @@ -5,8 +5,6 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; import org.qora.asset.Asset; import org.qora.block.BlockChain; import org.qora.data.transaction.DeployAtTransactionData; @@ -17,14 +15,12 @@ import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; public class DeployAtTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int CREATOR_LENGTH = PUBLIC_KEY_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; private static final int DESCRIPTION_SIZE_LENGTH = INT_LENGTH; private static final int AT_TYPE_SIZE_LENGTH = INT_LENGTH; @@ -33,9 +29,8 @@ public class DeployAtTransactionTransformer extends TransactionTransformer { private static final int AMOUNT_LENGTH = LONG_LENGTH; private static final int ASSET_ID_LENGTH = LONG_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + CREATOR_LENGTH + NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH + AT_TYPE_SIZE_LENGTH - + TAGS_SIZE_LENGTH + CREATION_BYTES_SIZE_LENGTH + AMOUNT_LENGTH; - private static final int V4_TYPELESS_LENGTH = TYPELESS_LENGTH + ASSET_ID_LENGTH; + private static final int EXTRAS_LENGTH = NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH + AT_TYPE_SIZE_LENGTH + TAGS_SIZE_LENGTH + CREATION_BYTES_SIZE_LENGTH + + AMOUNT_LENGTH; protected static final TransactionLayout layout; @@ -43,6 +38,7 @@ public class DeployAtTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.DEPLOY_AT.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("AT creator's public key", TransformationType.PUBLIC_KEY); layout.add("AT name length", TransformationType.INT); @@ -64,6 +60,10 @@ public class DeployAtTransactionTransformer extends TransactionTransformer { int version = DeployAtTransaction.getVersionByTimestamp(timestamp); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -95,24 +95,23 @@ public class DeployAtTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new DeployAtTransactionData(creatorPublicKey, name, description, ATType, tags, creationBytes, amount, assetId, fee, timestamp, reference, - signature); + return new DeployAtTransactionData(timestamp, txGroupId, reference, creatorPublicKey, name, description, ATType, tags, creationBytes, amount, assetId, + fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { DeployAtTransactionData deployATTransactionData = (DeployAtTransactionData) transactionData; - int dataLength = TYPE_LENGTH; + int dataLength = getBaseLength(transactionData) + EXTRAS_LENGTH; int version = DeployAtTransaction.getVersionByTimestamp(transactionData.getTimestamp()); + // V4+ have assetId too if (version >= 4) - dataLength += V4_TYPELESS_LENGTH; - else - dataLength += TYPELESS_LENGTH; + dataLength += ASSET_ID_LENGTH; dataLength += Utf8.encodedLength(deployATTransactionData.getName()) + Utf8.encodedLength(deployATTransactionData.getDescription()) - + Utf8.encodedLength(deployATTransactionData.getATType()) + Utf8.encodedLength(deployATTransactionData.getTags()) + + Utf8.encodedLength(deployATTransactionData.getAtType()) + Utf8.encodedLength(deployATTransactionData.getTags()) + deployATTransactionData.getCreationBytes().length; return dataLength; @@ -126,17 +125,13 @@ public class DeployAtTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(deployATTransactionData.getType().value)); - bytes.write(Longs.toByteArray(deployATTransactionData.getTimestamp())); - bytes.write(deployATTransactionData.getReference()); - - bytes.write(deployATTransactionData.getCreatorPublicKey()); + transformCommonBytes(transactionData, bytes); Serialization.serializeSizedString(bytes, deployATTransactionData.getName()); Serialization.serializeSizedString(bytes, deployATTransactionData.getDescription()); - Serialization.serializeSizedString(bytes, deployATTransactionData.getATType()); + Serialization.serializeSizedString(bytes, deployATTransactionData.getAtType()); Serialization.serializeSizedString(bytes, deployATTransactionData.getTags()); @@ -207,29 +202,4 @@ public class DeployAtTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - DeployAtTransactionData deployATTransactionData = (DeployAtTransactionData) transactionData; - - byte[] creatorPublicKey = deployATTransactionData.getCreatorPublicKey(); - - json.put("creator", PublicKeyAccount.getAddress(creatorPublicKey)); - json.put("creatorPublicKey", HashCode.fromBytes(creatorPublicKey).toString()); - json.put("name", deployATTransactionData.getName()); - json.put("description", deployATTransactionData.getDescription()); - json.put("atType", deployATTransactionData.getATType()); - json.put("tags", deployATTransactionData.getTags()); - json.put("creationBytes", HashCode.fromBytes(deployATTransactionData.getCreationBytes()).toString()); - json.put("amount", deployATTransactionData.getAmount().toPlainString()); - json.put("assetId", deployATTransactionData.getAssetId()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/GenesisTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/GenesisTransactionTransformer.java index 0448046b..23e7eb60 100644 --- a/src/main/java/org/qora/transform/transaction/GenesisTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/GenesisTransactionTransformer.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; import org.qora.asset.Asset; import org.qora.block.BlockChain; import org.qora.data.transaction.GenesisTransactionData; @@ -19,15 +18,13 @@ import com.google.common.primitives.Longs; public class GenesisTransactionTransformer extends TransactionTransformer { + // Note that Genesis transactions don't require reference, fee or signature + // Property lengths private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; private static final int AMOUNT_LENGTH = LONG_LENGTH; private static final int ASSET_ID_LENGTH = LONG_LENGTH; - // Note that Genesis transactions don't require reference, fee or signature: - private static final int TYPELESS_LENGTH_V1 = TIMESTAMP_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH; - private static final int TYPELESS_LENGTH_V4 = TIMESTAMP_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH + ASSET_ID_LENGTH; - protected static final TransactionLayout layout; static { @@ -37,7 +34,6 @@ public class GenesisTransactionTransformer extends TransactionTransformer { layout.add("recipient", TransformationType.ADDRESS); layout.add("amount", TransformationType.AMOUNT); layout.add("asset ID", TransformationType.LONG); - layout.add("fee", TransformationType.AMOUNT); layout.add("signature", TransformationType.SIGNATURE); } @@ -52,14 +48,15 @@ public class GenesisTransactionTransformer extends TransactionTransformer { if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) assetId = byteBuffer.getLong(); - return new GenesisTransactionData(recipient, amount, assetId, timestamp); + return new GenesisTransactionData(timestamp, recipient, amount, assetId); } public static int getDataLength(TransactionData transactionData) throws TransformationException { if (transactionData.getTimestamp() < BlockChain.getInstance().getQoraV2Timestamp()) - return TYPE_LENGTH + TYPELESS_LENGTH_V1; - else - return TYPE_LENGTH + TYPELESS_LENGTH_V4; + return TYPE_LENGTH + TIMESTAMP_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH; + + // Qora V2+ + return TYPE_LENGTH + TIMESTAMP_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH + ASSET_ID_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -69,9 +66,11 @@ public class GenesisTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); bytes.write(Ints.toByteArray(genesisTransactionData.getType().value)); + bytes.write(Longs.toByteArray(genesisTransactionData.getTimestamp())); Serialization.serializeAddress(bytes, genesisTransactionData.getRecipient()); + Serialization.serializeBigDecimal(bytes, genesisTransactionData.getAmount()); if (genesisTransactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp()) @@ -83,21 +82,4 @@ public class GenesisTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - GenesisTransactionData genesisTransactionData = (GenesisTransactionData) transactionData; - - json.put("recipient", genesisTransactionData.getRecipient()); - json.put("amount", genesisTransactionData.getAmount().toPlainString()); - json.put("assetId", genesisTransactionData.getAssetId()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/GroupApprovalTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/GroupApprovalTransactionTransformer.java new file mode 100644 index 00000000..84040e53 --- /dev/null +++ b/src/main/java/org/qora/transform/transaction/GroupApprovalTransactionTransformer.java @@ -0,0 +1,90 @@ +package org.qora.transform.transaction; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.nio.ByteBuffer; + +import org.qora.block.BlockChain; +import org.qora.data.transaction.GroupApprovalTransactionData; +import org.qora.data.transaction.TransactionData; +import org.qora.transaction.Transaction.TransactionType; +import org.qora.transform.TransformationException; +import org.qora.utils.Serialization; + +public class GroupApprovalTransactionTransformer extends TransactionTransformer { + + // Property lengths + private static final int PENDING_SIGNATURE_LENGTH = SIGNATURE_LENGTH; + private static final int APPROVAL_LENGTH = BOOLEAN_LENGTH; + + private static final int EXTRAS_LENGTH = PENDING_SIGNATURE_LENGTH + APPROVAL_LENGTH; + + protected static final TransactionLayout layout; + + static { + layout = new TransactionLayout(); + layout.add("txType: " + TransactionType.GROUP_INVITE.valueString, TransformationType.INT); + layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); + layout.add("reference", TransformationType.SIGNATURE); + layout.add("group admin's public key", TransformationType.PUBLIC_KEY); + layout.add("pending transaction's signature", TransformationType.SIGNATURE); + layout.add("approval decision", TransformationType.BOOLEAN); + layout.add("fee", TransformationType.AMOUNT); + layout.add("signature", TransformationType.SIGNATURE); + } + + public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { + long timestamp = byteBuffer.getLong(); + + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + + byte[] reference = new byte[REFERENCE_LENGTH]; + byteBuffer.get(reference); + + byte[] adminPublicKey = Serialization.deserializePublicKey(byteBuffer); + + byte[] pendingSignature = new byte[SIGNATURE_LENGTH]; + byteBuffer.get(pendingSignature); + + boolean approval = byteBuffer.get() != 0; + + BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + + byte[] signature = new byte[SIGNATURE_LENGTH]; + byteBuffer.get(signature); + + return new GroupApprovalTransactionData(timestamp, txGroupId, reference, adminPublicKey, pendingSignature, approval, null, fee, signature); + } + + public static int getDataLength(TransactionData transactionData) throws TransformationException { + return getBaseLength(transactionData) + EXTRAS_LENGTH; + } + + public static byte[] toBytes(TransactionData transactionData) throws TransformationException { + try { + GroupApprovalTransactionData groupApprovalTransactionData = (GroupApprovalTransactionData) transactionData; + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + transformCommonBytes(transactionData, bytes); + + bytes.write(groupApprovalTransactionData.getPendingSignature()); + + bytes.write((byte) (groupApprovalTransactionData.getApproval() ? 1 : 0)); + + Serialization.serializeBigDecimal(bytes, groupApprovalTransactionData.getFee()); + + if (groupApprovalTransactionData.getSignature() != null) + bytes.write(groupApprovalTransactionData.getSignature()); + + return bytes.toByteArray(); + } catch (IOException | ClassCastException e) { + throw new TransformationException(e); + } + } + +} diff --git a/src/main/java/org/qora/transform/transaction/GroupBanTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/GroupBanTransactionTransformer.java index 2e0a0c7e..b7cf1920 100644 --- a/src/main/java/org/qora/transform/transaction/GroupBanTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/GroupBanTransactionTransformer.java @@ -5,8 +5,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.GroupBanTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.group.Group; @@ -15,20 +14,17 @@ import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class GroupBanTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int ADMIN_LENGTH = PUBLIC_KEY_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH; private static final int MEMBER_LENGTH = ADDRESS_LENGTH; private static final int REASON_SIZE_LENGTH = INT_LENGTH; private static final int TTL_LENGTH = INT_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + ADMIN_LENGTH + GROUPID_LENGTH + MEMBER_LENGTH + REASON_SIZE_LENGTH + TTL_LENGTH; + private static final int EXTRAS_LENGTH = GROUPID_LENGTH + MEMBER_LENGTH + REASON_SIZE_LENGTH + TTL_LENGTH; protected static final TransactionLayout layout; @@ -36,6 +32,7 @@ public class GroupBanTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.GROUP_BAN.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("group admin's public key", TransformationType.PUBLIC_KEY); layout.add("group ID", TransformationType.INT); @@ -50,6 +47,10 @@ public class GroupBanTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -68,15 +69,13 @@ public class GroupBanTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new GroupBanTransactionData(adminPublicKey, groupId, offender, reason, timeToLive, fee, timestamp, reference, signature); + return new GroupBanTransactionData(timestamp, txGroupId, reference, adminPublicKey, groupId, offender, reason, timeToLive, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { GroupBanTransactionData groupBanTransactionData = (GroupBanTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(groupBanTransactionData.getReason()); - - return dataLength; + return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(groupBanTransactionData.getReason()); } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -85,14 +84,14 @@ public class GroupBanTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(groupBanTransactionData.getType().value)); - bytes.write(Longs.toByteArray(groupBanTransactionData.getTimestamp())); - bytes.write(groupBanTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(groupBanTransactionData.getCreatorPublicKey()); bytes.write(Ints.toByteArray(groupBanTransactionData.getGroupId())); + Serialization.serializeAddress(bytes, groupBanTransactionData.getOffender()); + Serialization.serializeSizedString(bytes, groupBanTransactionData.getReason()); + bytes.write(Ints.toByteArray(groupBanTransactionData.getTimeToLive())); Serialization.serializeBigDecimal(bytes, groupBanTransactionData.getFee()); @@ -106,27 +105,4 @@ public class GroupBanTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - GroupBanTransactionData groupBanTransactionData = (GroupBanTransactionData) transactionData; - - byte[] adminPublicKey = groupBanTransactionData.getAdminPublicKey(); - - json.put("admin", PublicKeyAccount.getAddress(adminPublicKey)); - json.put("adminPublicKey", HashCode.fromBytes(adminPublicKey).toString()); - - json.put("groupId", groupBanTransactionData.getGroupId()); - json.put("offender", groupBanTransactionData.getOffender()); - json.put("reason", groupBanTransactionData.getReason()); - json.put("timeToLive", groupBanTransactionData.getTimeToLive()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/GroupInviteTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/GroupInviteTransactionTransformer.java index 98c2df43..87677553 100644 --- a/src/main/java/org/qora/transform/transaction/GroupInviteTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/GroupInviteTransactionTransformer.java @@ -5,27 +5,23 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.GroupInviteTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class GroupInviteTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int ADMIN_LENGTH = PUBLIC_KEY_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH; private static final int INVITEE_LENGTH = ADDRESS_LENGTH; private static final int TTL_LENGTH = INT_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + ADMIN_LENGTH + GROUPID_LENGTH + INVITEE_LENGTH + TTL_LENGTH; + private static final int EXTRAS_LENGTH = GROUPID_LENGTH + INVITEE_LENGTH + TTL_LENGTH; protected static final TransactionLayout layout; @@ -33,6 +29,7 @@ public class GroupInviteTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.GROUP_INVITE.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("group admin's public key", TransformationType.PUBLIC_KEY); layout.add("group ID", TransformationType.INT); @@ -45,6 +42,10 @@ public class GroupInviteTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -61,11 +62,11 @@ public class GroupInviteTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new GroupInviteTransactionData(adminPublicKey, groupId, invitee, timeToLive, fee, timestamp, reference, signature); + return new GroupInviteTransactionData(timestamp, txGroupId, reference, adminPublicKey, groupId, invitee, timeToLive, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -74,13 +75,12 @@ public class GroupInviteTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(groupInviteTransactionData.getType().value)); - bytes.write(Longs.toByteArray(groupInviteTransactionData.getTimestamp())); - bytes.write(groupInviteTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(groupInviteTransactionData.getCreatorPublicKey()); bytes.write(Ints.toByteArray(groupInviteTransactionData.getGroupId())); + Serialization.serializeAddress(bytes, groupInviteTransactionData.getInvitee()); + bytes.write(Ints.toByteArray(groupInviteTransactionData.getTimeToLive())); Serialization.serializeBigDecimal(bytes, groupInviteTransactionData.getFee()); @@ -94,26 +94,4 @@ public class GroupInviteTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - GroupInviteTransactionData groupInviteTransactionData = (GroupInviteTransactionData) transactionData; - - byte[] adminPublicKey = groupInviteTransactionData.getAdminPublicKey(); - - json.put("admin", PublicKeyAccount.getAddress(adminPublicKey)); - json.put("adminPublicKey", HashCode.fromBytes(adminPublicKey).toString()); - - json.put("groupId", groupInviteTransactionData.getGroupId()); - json.put("invitee", groupInviteTransactionData.getInvitee()); - json.put("timeToLive", groupInviteTransactionData.getTimeToLive()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/GroupKickTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/GroupKickTransactionTransformer.java index a2dd9bfe..db6241c6 100644 --- a/src/main/java/org/qora/transform/transaction/GroupKickTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/GroupKickTransactionTransformer.java @@ -5,8 +5,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.GroupKickTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.group.Group; @@ -15,19 +14,16 @@ import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class GroupKickTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int ADMIN_LENGTH = PUBLIC_KEY_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH; private static final int MEMBER_LENGTH = ADDRESS_LENGTH; private static final int REASON_SIZE_LENGTH = INT_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + ADMIN_LENGTH + GROUPID_LENGTH + MEMBER_LENGTH + REASON_SIZE_LENGTH; + private static final int EXTRAS_LENGTH = GROUPID_LENGTH + MEMBER_LENGTH + REASON_SIZE_LENGTH; protected static final TransactionLayout layout; @@ -35,6 +31,7 @@ public class GroupKickTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.GROUP_KICK.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("group admin's public key", TransformationType.PUBLIC_KEY); layout.add("group ID", TransformationType.INT); @@ -48,6 +45,10 @@ public class GroupKickTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -64,15 +65,13 @@ public class GroupKickTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new GroupKickTransactionData(adminPublicKey, groupId, member, reason, fee, timestamp, reference, signature); + return new GroupKickTransactionData(timestamp, txGroupId, reference, adminPublicKey, groupId, member, reason, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { GroupKickTransactionData groupKickTransactionData = (GroupKickTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(groupKickTransactionData.getReason()); - - return dataLength; + return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(groupKickTransactionData.getReason()); } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -81,13 +80,12 @@ public class GroupKickTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(groupKickTransactionData.getType().value)); - bytes.write(Longs.toByteArray(groupKickTransactionData.getTimestamp())); - bytes.write(groupKickTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(groupKickTransactionData.getCreatorPublicKey()); bytes.write(Ints.toByteArray(groupKickTransactionData.getGroupId())); + Serialization.serializeAddress(bytes, groupKickTransactionData.getMember()); + Serialization.serializeSizedString(bytes, groupKickTransactionData.getReason()); Serialization.serializeBigDecimal(bytes, groupKickTransactionData.getFee()); @@ -101,26 +99,4 @@ public class GroupKickTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - GroupKickTransactionData groupKickTransactionData = (GroupKickTransactionData) transactionData; - - byte[] adminPublicKey = groupKickTransactionData.getAdminPublicKey(); - - json.put("admin", PublicKeyAccount.getAddress(adminPublicKey)); - json.put("adminPublicKey", HashCode.fromBytes(adminPublicKey).toString()); - - json.put("groupId", groupKickTransactionData.getGroupId()); - json.put("member", groupKickTransactionData.getMember()); - json.put("reason", groupKickTransactionData.getReason()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/IssueAssetTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/IssueAssetTransactionTransformer.java index 6da7819f..49644147 100644 --- a/src/main/java/org/qora/transform/transaction/IssueAssetTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/IssueAssetTransactionTransformer.java @@ -6,8 +6,6 @@ import java.math.BigDecimal; import java.nio.ByteBuffer; import java.util.Arrays; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; import org.qora.block.BlockChain; import org.qora.data.transaction.IssueAssetTransactionData; import org.qora.data.transaction.TransactionData; @@ -17,14 +15,11 @@ import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; -import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; public class IssueAssetTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int ISSUER_LENGTH = PUBLIC_KEY_LENGTH; private static final int OWNER_LENGTH = ADDRESS_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; private static final int DESCRIPTION_SIZE_LENGTH = INT_LENGTH; @@ -32,8 +27,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { private static final int IS_DIVISIBLE_LENGTH = BOOLEAN_LENGTH; private static final int ASSET_REFERENCE_LENGTH = REFERENCE_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + ISSUER_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH - + QUANTITY_LENGTH + IS_DIVISIBLE_LENGTH; + private static final int EXTRAS_LENGTH = OWNER_LENGTH + NAME_SIZE_LENGTH + DESCRIPTION_SIZE_LENGTH + QUANTITY_LENGTH + IS_DIVISIBLE_LENGTH; protected static final TransactionLayout layout; @@ -41,6 +35,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.ISSUE_ASSET.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("asset issuer's public key", TransformationType.PUBLIC_KEY); layout.add("asset owner", TransformationType.ADDRESS); @@ -57,6 +52,10 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -82,13 +81,14 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new IssueAssetTransactionData(issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference, signature); + return new IssueAssetTransactionData(timestamp, txGroupId, reference, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, + signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { IssueAssetTransactionData issueAssetTransactionData = (IssueAssetTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_LENGTH + Utf8.encodedLength(issueAssetTransactionData.getAssetName()) + int dataLength = getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(issueAssetTransactionData.getAssetName()) + Utf8.encodedLength(issueAssetTransactionData.getDescription()); // In v1, IssueAssetTransaction uses Asset.toBytes which also serializes reference. @@ -104,15 +104,12 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(issueAssetTransactionData.getType().value)); - bytes.write(Longs.toByteArray(issueAssetTransactionData.getTimestamp())); - bytes.write(issueAssetTransactionData.getReference()); - - bytes.write(issueAssetTransactionData.getIssuerPublicKey()); + transformCommonBytes(transactionData, bytes); Serialization.serializeAddress(bytes, issueAssetTransactionData.getOwner()); Serialization.serializeSizedString(bytes, issueAssetTransactionData.getAssetName()); + Serialization.serializeSizedString(bytes, issueAssetTransactionData.getDescription()); bytes.write(Longs.toByteArray(issueAssetTransactionData.getQuantity())); @@ -161,27 +158,4 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { return bytes; } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - IssueAssetTransactionData issueAssetTransactionData = (IssueAssetTransactionData) transactionData; - - byte[] issuerPublicKey = issueAssetTransactionData.getIssuerPublicKey(); - - json.put("issuer", PublicKeyAccount.getAddress(issuerPublicKey)); - json.put("issuerPublicKey", HashCode.fromBytes(issuerPublicKey).toString()); - json.put("owner", issueAssetTransactionData.getOwner()); - json.put("assetName", issueAssetTransactionData.getAssetName()); - json.put("description", issueAssetTransactionData.getDescription()); - json.put("quantity", issueAssetTransactionData.getQuantity()); - json.put("isDivisible", issueAssetTransactionData.getIsDivisible()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/JoinGroupTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/JoinGroupTransactionTransformer.java index 26bab418..5728eadc 100644 --- a/src/main/java/org/qora/transform/transaction/JoinGroupTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/JoinGroupTransactionTransformer.java @@ -5,25 +5,21 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.JoinGroupTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class JoinGroupTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int JOINER_LENGTH = PUBLIC_KEY_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + JOINER_LENGTH + GROUPID_LENGTH; + private static final int EXTRAS_LENGTH = GROUPID_LENGTH; protected static final TransactionLayout layout; @@ -31,6 +27,7 @@ public class JoinGroupTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.JOIN_GROUP.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("joiner's public key", TransformationType.PUBLIC_KEY); layout.add("group ID", TransformationType.INT); @@ -41,6 +38,10 @@ public class JoinGroupTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -53,11 +54,11 @@ public class JoinGroupTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new JoinGroupTransactionData(joinerPublicKey, groupId, fee, timestamp, reference, signature); + return new JoinGroupTransactionData(timestamp, txGroupId, reference, joinerPublicKey, groupId, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -66,11 +67,8 @@ public class JoinGroupTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(joinGroupTransactionData.getType().value)); - bytes.write(Longs.toByteArray(joinGroupTransactionData.getTimestamp())); - bytes.write(joinGroupTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(joinGroupTransactionData.getCreatorPublicKey()); bytes.write(Ints.toByteArray(joinGroupTransactionData.getGroupId())); Serialization.serializeBigDecimal(bytes, joinGroupTransactionData.getFee()); @@ -84,24 +82,4 @@ public class JoinGroupTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - JoinGroupTransactionData joinGroupTransactionData = (JoinGroupTransactionData) transactionData; - - byte[] joinerPublicKey = joinGroupTransactionData.getJoinerPublicKey(); - - json.put("joiner", PublicKeyAccount.getAddress(joinerPublicKey)); - json.put("joinerPublicKey", HashCode.fromBytes(joinerPublicKey).toString()); - - json.put("groupId", joinGroupTransactionData.getGroupId()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/LeaveGroupTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/LeaveGroupTransactionTransformer.java index 77c4634b..3bee73f0 100644 --- a/src/main/java/org/qora/transform/transaction/LeaveGroupTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/LeaveGroupTransactionTransformer.java @@ -5,25 +5,21 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.LeaveGroupTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class LeaveGroupTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int JOINER_LENGTH = PUBLIC_KEY_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + JOINER_LENGTH + GROUPID_LENGTH; + private static final int EXTRAS_LENGTH = GROUPID_LENGTH; protected static final TransactionLayout layout; @@ -31,6 +27,7 @@ public class LeaveGroupTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.LEAVE_GROUP.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("leaver's public key", TransformationType.PUBLIC_KEY); layout.add("group ID", TransformationType.INT); @@ -41,6 +38,10 @@ public class LeaveGroupTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -53,11 +54,11 @@ public class LeaveGroupTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new LeaveGroupTransactionData(leaverPublicKey, groupId, fee, timestamp, reference, signature); + return new LeaveGroupTransactionData(timestamp, txGroupId, reference, leaverPublicKey, groupId, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -66,11 +67,8 @@ public class LeaveGroupTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(leaveGroupTransactionData.getType().value)); - bytes.write(Longs.toByteArray(leaveGroupTransactionData.getTimestamp())); - bytes.write(leaveGroupTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(leaveGroupTransactionData.getCreatorPublicKey()); bytes.write(Ints.toByteArray(leaveGroupTransactionData.getGroupId())); Serialization.serializeBigDecimal(bytes, leaveGroupTransactionData.getFee()); @@ -84,24 +82,4 @@ public class LeaveGroupTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - LeaveGroupTransactionData leaveGroupTransactionData = (LeaveGroupTransactionData) transactionData; - - byte[] leaverPublicKey = leaveGroupTransactionData.getLeaverPublicKey(); - - json.put("leaver", PublicKeyAccount.getAddress(leaverPublicKey)); - json.put("leaverPublicKey", HashCode.fromBytes(leaverPublicKey).toString()); - - json.put("groupId", leaveGroupTransactionData.getGroupId()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/MessageTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/MessageTransactionTransformer.java index 7f6d06ca..d8f43e2f 100644 --- a/src/main/java/org/qora/transform/transaction/MessageTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/MessageTransactionTransformer.java @@ -4,11 +4,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; import org.qora.asset.Asset; +import org.qora.block.BlockChain; import org.qora.data.transaction.MessageTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.MessageTransaction; @@ -16,14 +14,12 @@ import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; public class MessageTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int SENDER_LENGTH = PUBLIC_KEY_LENGTH; private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; private static final int ASSET_ID_LENGTH = LONG_LENGTH; @@ -31,10 +27,7 @@ public class MessageTransactionTransformer extends TransactionTransformer { private static final int IS_TEXT_LENGTH = BOOLEAN_LENGTH; private static final int IS_ENCRYPTED_LENGTH = BOOLEAN_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH_V1 = BASE_TYPELESS_LENGTH + SENDER_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH + DATA_SIZE_LENGTH - + IS_TEXT_LENGTH + IS_ENCRYPTED_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH_V3 = BASE_TYPELESS_LENGTH + SENDER_LENGTH + RECIPIENT_LENGTH + ASSET_ID_LENGTH + AMOUNT_LENGTH - + DATA_SIZE_LENGTH + IS_TEXT_LENGTH + IS_ENCRYPTED_LENGTH; + private static final int EXTRAS_LENGTH = RECIPIENT_LENGTH + AMOUNT_LENGTH + DATA_SIZE_LENGTH + IS_TEXT_LENGTH + IS_ENCRYPTED_LENGTH; protected static final TransactionLayout layout; @@ -42,6 +35,7 @@ public class MessageTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.MESSAGE.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("sender's public key", TransformationType.PUBLIC_KEY); layout.add("recipient", TransformationType.ADDRESS); @@ -60,6 +54,10 @@ public class MessageTransactionTransformer extends TransactionTransformer { int version = MessageTransaction.getVersionByTimestamp(timestamp); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -92,17 +90,20 @@ public class MessageTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference, + return new MessageTransactionData(timestamp, txGroupId, reference, senderPublicKey, version, recipient, assetId, amount, data, isText, isEncrypted, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { MessageTransactionData messageTransactionData = (MessageTransactionData) transactionData; - if (messageTransactionData.getVersion() == 1) - return TYPE_LENGTH + TYPELESS_DATALESS_LENGTH_V1 + messageTransactionData.getData().length; - else - return TYPE_LENGTH + TYPELESS_DATALESS_LENGTH_V3 + messageTransactionData.getData().length; + int dataLength = getBaseLength(transactionData) + EXTRAS_LENGTH; + + // V3+ has assetID for amount + if (messageTransactionData.getVersion() != 1) + dataLength += ASSET_ID_LENGTH; + + return dataLength; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -111,20 +112,21 @@ public class MessageTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(messageTransactionData.getType().value)); - bytes.write(Longs.toByteArray(messageTransactionData.getTimestamp())); - bytes.write(messageTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(messageTransactionData.getSenderPublicKey()); Serialization.serializeAddress(bytes, messageTransactionData.getRecipient()); if (messageTransactionData.getVersion() != 1) bytes.write(Longs.toByteArray(messageTransactionData.getAssetId())); Serialization.serializeBigDecimal(bytes, messageTransactionData.getAmount()); + bytes.write(Ints.toByteArray(messageTransactionData.getData().length)); + bytes.write(messageTransactionData.getData()); + bytes.write((byte) (messageTransactionData.getIsEncrypted() ? 1 : 0)); + bytes.write((byte) (messageTransactionData.getIsText() ? 1 : 0)); Serialization.serializeBigDecimal(bytes, messageTransactionData.getFee()); @@ -138,34 +140,4 @@ public class MessageTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - MessageTransactionData messageTransactionData = (MessageTransactionData) transactionData; - - byte[] senderPublicKey = messageTransactionData.getSenderPublicKey(); - - json.put("version", messageTransactionData.getVersion()); - json.put("sender", PublicKeyAccount.getAddress(senderPublicKey)); - json.put("senderPublicKey", HashCode.fromBytes(senderPublicKey).toString()); - json.put("recipient", messageTransactionData.getRecipient()); - json.put("amount", messageTransactionData.getAmount().toPlainString()); - json.put("assetId", messageTransactionData.getAssetId()); - json.put("isText", messageTransactionData.getIsText()); - json.put("isEncrypted", messageTransactionData.getIsEncrypted()); - - // We can only show plain text as unencoded - if (messageTransactionData.getIsText() && !messageTransactionData.getIsEncrypted()) - json.put("data", new String(messageTransactionData.getData(), Charset.forName("UTF-8"))); - else - json.put("data", HashCode.fromBytes(messageTransactionData.getData()).toString()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/MultiPaymentTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/MultiPaymentTransactionTransformer.java index 3b6c29bb..26ab08b1 100644 --- a/src/main/java/org/qora/transform/transaction/MultiPaymentTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/MultiPaymentTransactionTransformer.java @@ -8,9 +8,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; import org.qora.block.BlockChain; import org.qora.data.PaymentData; import org.qora.data.transaction.MultiPaymentTransactionData; @@ -20,17 +17,14 @@ import org.qora.transform.PaymentTransformer; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class MultiPaymentTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int SENDER_LENGTH = PUBLIC_KEY_LENGTH; private static final int PAYMENTS_COUNT_LENGTH = INT_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + SENDER_LENGTH + PAYMENTS_COUNT_LENGTH; + private static final int EXTRAS_LENGTH = PAYMENTS_COUNT_LENGTH; protected static final TransactionLayout layout; @@ -38,6 +32,7 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.MULTI_PAYMENT.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("sender's public key", TransformationType.PUBLIC_KEY); layout.add("number of payments", TransformationType.INT); @@ -51,6 +46,10 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -67,13 +66,13 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new MultiPaymentTransactionData(senderPublicKey, payments, fee, timestamp, reference, signature); + return new MultiPaymentTransactionData(timestamp, txGroupId, reference, senderPublicKey, payments, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { MultiPaymentTransactionData multiPaymentTransactionData = (MultiPaymentTransactionData) transactionData; - return TYPE_LENGTH + TYPELESS_LENGTH + multiPaymentTransactionData.getPayments().size() * PaymentTransformer.getDataLength(); + return getBaseLength(transactionData) + EXTRAS_LENGTH + multiPaymentTransactionData.getPayments().size() * PaymentTransformer.getDataLength(); } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -82,11 +81,7 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(multiPaymentTransactionData.getType().value)); - bytes.write(Longs.toByteArray(multiPaymentTransactionData.getTimestamp())); - bytes.write(multiPaymentTransactionData.getReference()); - - bytes.write(multiPaymentTransactionData.getSenderPublicKey()); + transformCommonBytes(transactionData, bytes); List payments = multiPaymentTransactionData.getPayments(); bytes.write(Ints.toByteArray(payments.size())); @@ -128,30 +123,4 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer { return Arrays.copyOfRange(bytes, v1Start, bytes.length); } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - MultiPaymentTransactionData multiPaymentTransactionData = (MultiPaymentTransactionData) transactionData; - - byte[] senderPublicKey = multiPaymentTransactionData.getSenderPublicKey(); - - json.put("sender", PublicKeyAccount.getAddress(senderPublicKey)); - json.put("senderPublicKey", HashCode.fromBytes(senderPublicKey).toString()); - - List payments = multiPaymentTransactionData.getPayments(); - JSONArray paymentsJson = new JSONArray(); - - for (PaymentData paymentData : payments) - paymentsJson.add(PaymentTransformer.toJSON(paymentData)); - - json.put("payments", paymentsJson); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/PaymentTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/PaymentTransactionTransformer.java index 636ad910..a3ca39b5 100644 --- a/src/main/java/org/qora/transform/transaction/PaymentTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/PaymentTransactionTransformer.java @@ -5,26 +5,20 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.PaymentTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; - public class PaymentTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int SENDER_LENGTH = PUBLIC_KEY_LENGTH; private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + SENDER_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH; + private static final int EXTRAS_LENGTH = RECIPIENT_LENGTH + AMOUNT_LENGTH; protected static final TransactionLayout layout; @@ -32,6 +26,7 @@ public class PaymentTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.PAYMENT.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("sender's public key", TransformationType.PUBLIC_KEY); layout.add("recipient", TransformationType.ADDRESS); @@ -43,6 +38,10 @@ public class PaymentTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -57,11 +56,11 @@ public class PaymentTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new PaymentTransactionData(senderPublicKey, recipient, amount, fee, timestamp, reference, signature); + return new PaymentTransactionData(timestamp, txGroupId, reference, senderPublicKey, recipient, amount, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -70,12 +69,10 @@ public class PaymentTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(paymentTransactionData.getType().value)); - bytes.write(Longs.toByteArray(paymentTransactionData.getTimestamp())); - bytes.write(paymentTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(paymentTransactionData.getSenderPublicKey()); Serialization.serializeAddress(bytes, paymentTransactionData.getRecipient()); + Serialization.serializeBigDecimal(bytes, paymentTransactionData.getAmount()); Serialization.serializeBigDecimal(bytes, paymentTransactionData.getFee()); @@ -89,24 +86,4 @@ public class PaymentTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - PaymentTransactionData paymentTransactionData = (PaymentTransactionData) transactionData; - - byte[] senderPublicKey = paymentTransactionData.getSenderPublicKey(); - - json.put("sender", PublicKeyAccount.getAddress(senderPublicKey)); - json.put("senderPublicKey", HashCode.fromBytes(senderPublicKey).toString()); - json.put("recipient", paymentTransactionData.getRecipient()); - json.put("amount", paymentTransactionData.getAmount().toPlainString()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/RegisterNameTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/RegisterNameTransactionTransformer.java index 9cdf4c91..39aad278 100644 --- a/src/main/java/org/qora/transform/transaction/RegisterNameTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/RegisterNameTransactionTransformer.java @@ -5,8 +5,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.RegisterNameTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.naming.Name; @@ -15,19 +14,15 @@ import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class RegisterNameTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int REGISTRANT_LENGTH = PUBLIC_KEY_LENGTH; private static final int OWNER_LENGTH = ADDRESS_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; private static final int DATA_SIZE_LENGTH = INT_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + REGISTRANT_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + DATA_SIZE_LENGTH; + private static final int EXTRAS_LENGTH = OWNER_LENGTH + NAME_SIZE_LENGTH + DATA_SIZE_LENGTH; protected static final TransactionLayout layout; @@ -35,6 +30,7 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.REGISTER_NAME.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("name registrant's public key", TransformationType.PUBLIC_KEY); layout.add("name owner", TransformationType.ADDRESS); @@ -49,6 +45,10 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -65,16 +65,14 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new RegisterNameTransactionData(registrantPublicKey, owner, name, data, fee, timestamp, reference, signature); + return new RegisterNameTransactionData(timestamp, txGroupId, reference, registrantPublicKey, owner, name, data, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { RegisterNameTransactionData registerNameTransactionData = (RegisterNameTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(registerNameTransactionData.getName()) + return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(registerNameTransactionData.getName()) + Utf8.encodedLength(registerNameTransactionData.getData()); - - return dataLength; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -83,13 +81,12 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(registerNameTransactionData.getType().value)); - bytes.write(Longs.toByteArray(registerNameTransactionData.getTimestamp())); - bytes.write(registerNameTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(registerNameTransactionData.getRegistrantPublicKey()); Serialization.serializeAddress(bytes, registerNameTransactionData.getOwner()); + Serialization.serializeSizedString(bytes, registerNameTransactionData.getName()); + Serialization.serializeSizedString(bytes, registerNameTransactionData.getData()); Serialization.serializeBigDecimal(bytes, registerNameTransactionData.getFee()); @@ -103,26 +100,4 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - RegisterNameTransactionData registerNameTransactionData = (RegisterNameTransactionData) transactionData; - - byte[] registrantPublicKey = registerNameTransactionData.getRegistrantPublicKey(); - - json.put("registrant", PublicKeyAccount.getAddress(registrantPublicKey)); - json.put("registrantPublicKey", HashCode.fromBytes(registrantPublicKey).toString()); - - json.put("owner", registerNameTransactionData.getOwner()); - json.put("name", registerNameTransactionData.getName()); - json.put("data", registerNameTransactionData.getData()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/RemoveGroupAdminTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/RemoveGroupAdminTransactionTransformer.java index 2d265b89..99fd991f 100644 --- a/src/main/java/org/qora/transform/transaction/RemoveGroupAdminTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/RemoveGroupAdminTransactionTransformer.java @@ -5,26 +5,22 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.RemoveGroupAdminTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class RemoveGroupAdminTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int OWNER_LENGTH = PUBLIC_KEY_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH; private static final int MEMBER_LENGTH = ADDRESS_LENGTH; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + OWNER_LENGTH + GROUPID_LENGTH + MEMBER_LENGTH; + private static final int EXTRAS_LENGTH = GROUPID_LENGTH + MEMBER_LENGTH; protected static final TransactionLayout layout; @@ -32,6 +28,7 @@ public class RemoveGroupAdminTransactionTransformer extends TransactionTransform layout = new TransactionLayout(); layout.add("txType: " + TransactionType.REMOVE_GROUP_ADMIN.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("group owner's public key", TransformationType.PUBLIC_KEY); layout.add("group ID", TransformationType.INT); @@ -43,6 +40,10 @@ public class RemoveGroupAdminTransactionTransformer extends TransactionTransform public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -57,11 +58,11 @@ public class RemoveGroupAdminTransactionTransformer extends TransactionTransform byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new RemoveGroupAdminTransactionData(ownerPublicKey, groupId, admin, fee, timestamp, reference, signature); + return new RemoveGroupAdminTransactionData(timestamp, txGroupId, reference, ownerPublicKey, groupId, admin, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -70,12 +71,10 @@ public class RemoveGroupAdminTransactionTransformer extends TransactionTransform ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(removeGroupAdminTransactionData.getType().value)); - bytes.write(Longs.toByteArray(removeGroupAdminTransactionData.getTimestamp())); - bytes.write(removeGroupAdminTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(removeGroupAdminTransactionData.getCreatorPublicKey()); bytes.write(Ints.toByteArray(removeGroupAdminTransactionData.getGroupId())); + Serialization.serializeAddress(bytes, removeGroupAdminTransactionData.getAdmin()); Serialization.serializeBigDecimal(bytes, removeGroupAdminTransactionData.getFee()); @@ -89,25 +88,4 @@ public class RemoveGroupAdminTransactionTransformer extends TransactionTransform } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - RemoveGroupAdminTransactionData removeGroupAdminTransactionData = (RemoveGroupAdminTransactionData) transactionData; - - byte[] ownerPublicKey = removeGroupAdminTransactionData.getOwnerPublicKey(); - - json.put("owner", PublicKeyAccount.getAddress(ownerPublicKey)); - json.put("ownerPublicKey", HashCode.fromBytes(ownerPublicKey).toString()); - - json.put("groupId", removeGroupAdminTransactionData.getGroupId()); - json.put("admin", removeGroupAdminTransactionData.getAdmin()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/SellNameTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/SellNameTransactionTransformer.java index 66af7b53..63a0663a 100644 --- a/src/main/java/org/qora/transform/transaction/SellNameTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/SellNameTransactionTransformer.java @@ -5,8 +5,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.SellNameTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.naming.Name; @@ -15,18 +14,14 @@ import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class SellNameTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int OWNER_LENGTH = PUBLIC_KEY_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + AMOUNT_LENGTH; + private static final int EXTRAS_LENGTH = NAME_SIZE_LENGTH + AMOUNT_LENGTH; protected static final TransactionLayout layout; @@ -34,6 +29,7 @@ public class SellNameTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.SELL_NAME.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("name owner's public key", TransformationType.PUBLIC_KEY); layout.add("name length", TransformationType.INT); @@ -46,6 +42,10 @@ public class SellNameTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -60,15 +60,13 @@ public class SellNameTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new SellNameTransactionData(ownerPublicKey, name, amount, fee, timestamp, reference, signature); + return new SellNameTransactionData(timestamp, txGroupId, reference, ownerPublicKey, name, amount, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { SellNameTransactionData sellNameTransactionData = (SellNameTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(sellNameTransactionData.getName()); - - return dataLength; + return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(sellNameTransactionData.getName()); } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -77,12 +75,10 @@ public class SellNameTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(sellNameTransactionData.getType().value)); - bytes.write(Longs.toByteArray(sellNameTransactionData.getTimestamp())); - bytes.write(sellNameTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(sellNameTransactionData.getOwnerPublicKey()); Serialization.serializeSizedString(bytes, sellNameTransactionData.getName()); + Serialization.serializeBigDecimal(bytes, sellNameTransactionData.getAmount()); Serialization.serializeBigDecimal(bytes, sellNameTransactionData.getFee()); @@ -96,25 +92,4 @@ public class SellNameTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - SellNameTransactionData sellNameTransactionData = (SellNameTransactionData) transactionData; - - byte[] ownerPublicKey = sellNameTransactionData.getOwnerPublicKey(); - - json.put("owner", PublicKeyAccount.getAddress(ownerPublicKey)); - json.put("ownerPublicKey", HashCode.fromBytes(ownerPublicKey).toString()); - - json.put("name", sellNameTransactionData.getName()); - json.put("amount", sellNameTransactionData.getAmount().toPlainString()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/SetGroupTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/SetGroupTransactionTransformer.java new file mode 100644 index 00000000..1faf1c29 --- /dev/null +++ b/src/main/java/org/qora/transform/transaction/SetGroupTransactionTransformer.java @@ -0,0 +1,85 @@ +package org.qora.transform.transaction; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.nio.ByteBuffer; + +import org.qora.block.BlockChain; +import org.qora.data.transaction.SetGroupTransactionData; +import org.qora.data.transaction.TransactionData; +import org.qora.transaction.Transaction.TransactionType; +import org.qora.transform.TransformationException; +import org.qora.utils.Serialization; + +import com.google.common.primitives.Ints; + +public class SetGroupTransactionTransformer extends TransactionTransformer { + + // Property lengths + private static final int GROUPID_LENGTH = SIGNATURE_LENGTH; + + private static final int EXTRAS_LENGTH = GROUPID_LENGTH; + + protected static final TransactionLayout layout; + + static { + layout = new TransactionLayout(); + layout.add("txType: " + TransactionType.GROUP_INVITE.valueString, TransformationType.INT); + layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); + layout.add("reference", TransformationType.SIGNATURE); + layout.add("account's public key", TransformationType.PUBLIC_KEY); + layout.add("account's new default groupID", TransformationType.INT); + layout.add("fee", TransformationType.AMOUNT); + layout.add("signature", TransformationType.SIGNATURE); + } + + public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { + long timestamp = byteBuffer.getLong(); + + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + + byte[] reference = new byte[REFERENCE_LENGTH]; + byteBuffer.get(reference); + + byte[] creatorPublicKey = Serialization.deserializePublicKey(byteBuffer); + + int defaultGroupId = byteBuffer.getInt(); + + BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + + byte[] signature = new byte[SIGNATURE_LENGTH]; + byteBuffer.get(signature); + + return new SetGroupTransactionData(timestamp, txGroupId, reference, creatorPublicKey, defaultGroupId, fee, signature); + } + + public static int getDataLength(TransactionData transactionData) throws TransformationException { + return getBaseLength(transactionData) + EXTRAS_LENGTH; + } + + public static byte[] toBytes(TransactionData transactionData) throws TransformationException { + try { + SetGroupTransactionData setGroupTransactionData = (SetGroupTransactionData) transactionData; + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + transformCommonBytes(transactionData, bytes); + + bytes.write(Ints.toByteArray(setGroupTransactionData.getDefaultGroupId())); + + Serialization.serializeBigDecimal(bytes, setGroupTransactionData.getFee()); + + if (setGroupTransactionData.getSignature() != null) + bytes.write(setGroupTransactionData.getSignature()); + + return bytes.toByteArray(); + } catch (IOException | ClassCastException e) { + throw new TransformationException(e); + } + } + +} diff --git a/src/main/java/org/qora/transform/transaction/TransactionTransformer.java b/src/main/java/org/qora/transform/transaction/TransactionTransformer.java index 8d079762..147572af 100644 --- a/src/main/java/org/qora/transform/transaction/TransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/TransactionTransformer.java @@ -1,5 +1,7 @@ package org.qora.transform.transaction; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -15,25 +17,26 @@ import javax.xml.bind.annotation.XmlElement; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.json.simple.JSONObject; import org.qora.account.PrivateKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.transform.Transformer; -import org.qora.utils.Base58; import com.google.common.hash.HashCode; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; public abstract class TransactionTransformer extends Transformer { private static final Logger LOGGER = LogManager.getLogger(TransactionTransformer.class); protected static final int TYPE_LENGTH = INT_LENGTH; + protected static final int GROUPID_LENGTH = INT_LENGTH; protected static final int REFERENCE_LENGTH = SIGNATURE_LENGTH; protected static final int FEE_LENGTH = BIG_DECIMAL_LENGTH; - protected static final int BASE_TYPELESS_LENGTH = TIMESTAMP_LENGTH + REFERENCE_LENGTH + FEE_LENGTH + SIGNATURE_LENGTH; /** Description of one component of raw transaction layout */ public enum TransformationType { @@ -47,7 +50,8 @@ public abstract class TransactionTransformer extends Transformer { LONG("long", LONG_LENGTH), STRING("UTF-8 string of variable length", null), DATA("opaque data of variable length", null), - BOOLEAN("0 for false, anything else for true", 1); + BOOLEAN("0 for false, anything else for true", 1), + BYTE("byte", 1); public final String description; public final Integer length; @@ -109,7 +113,6 @@ public abstract class TransactionTransformer extends Transformer { public Method getDataLengthMethod; public Method toBytesMethod; public Method toBytesForSigningImplMethod; - public Method toJSONMethod; } /** Cache of transformer subclass info, keyed by transaction type */ @@ -166,12 +169,6 @@ public abstract class TransactionTransformer extends Transformer { txType.name())); } - try { - subclassInfo.toJSONMethod = subclassInfo.clazz.getDeclaredMethod("toJSON", TransactionData.class); - } catch (IllegalArgumentException | SecurityException | NoSuchMethodException e) { - LOGGER.debug(String.format("TransactionTransformer subclass's \"toJSON\" method not found for transaction type \"%s\"", txType.name())); - } - subclassInfos[txType.value] = subclassInfo; } @@ -218,6 +215,16 @@ public abstract class TransactionTransformer extends Transformer { } } + protected static int getBaseLength(TransactionData transactionData) { + // All transactions have at least txType, timestamp, maybe txGroupId, tx creator's public key and finally signature (on the end) + int baseLength = TYPE_LENGTH + TIMESTAMP_LENGTH + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH; + + if (transactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp()) + baseLength += GROUPID_LENGTH; + + return baseLength; + } + public static int getDataLength(TransactionData transactionData) throws TransformationException { TransactionType type = transactionData.getType(); @@ -280,34 +287,24 @@ public abstract class TransactionTransformer extends Transformer { } } - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - TransactionType type = transactionData.getType(); + protected static void transformCommonBytes(TransactionData transactionData, ByteArrayOutputStream bytes) throws IOException { + boolean v2 = transactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp(); - try { - Method method = subclassInfos[type.value].toJSONMethod; - return (JSONObject) method.invoke(null, transactionData); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - throw new TransformationException("Internal error with transaction type [" + type.value + "] during conversion to JSON"); - } - } + // Transaction type + bytes.write(Ints.toByteArray(transactionData.getType().value)); - @SuppressWarnings("unchecked") - static JSONObject getBaseJSON(TransactionData transaction) { - JSONObject json = new JSONObject(); + // Timestamp + bytes.write(Longs.toByteArray(transactionData.getTimestamp())); - json.put("type", transaction.getType().value); - json.put("fee", transaction.getFee().toPlainString()); - json.put("timestamp", transaction.getTimestamp()); - json.put("signature", Base58.encode(transaction.getSignature())); + if (v2) + // Transaction's groupID + bytes.write(Ints.toByteArray(transactionData.getTxGroupId())); - byte[] reference = transaction.getReference(); - if (reference != null) - json.put("reference", Base58.encode(reference)); + // Reference + bytes.write(transactionData.getReference()); - // XXX Can't do this as it requires database access: - // json.put("confirmations", RepositoryManager.getTransactionRepository.getConfirmations(transaction)); - - return json; + // Creator public key + bytes.write(transactionData.getCreatorPublicKey()); } } diff --git a/src/main/java/org/qora/transform/transaction/TransferAssetTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/TransferAssetTransactionTransformer.java index d6b0d650..720f0ed8 100644 --- a/src/main/java/org/qora/transform/transaction/TransferAssetTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/TransferAssetTransactionTransformer.java @@ -5,27 +5,23 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.TransactionData; import org.qora.data.transaction.TransferAssetTransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -import com.google.common.hash.HashCode; -import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; public class TransferAssetTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int SENDER_LENGTH = PUBLIC_KEY_LENGTH; private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH; private static final int ASSET_ID_LENGTH = LONG_LENGTH; private static final int AMOUNT_LENGTH = 12; - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + SENDER_LENGTH + RECIPIENT_LENGTH + ASSET_ID_LENGTH + AMOUNT_LENGTH; + private static final int EXTRAS_LENGTH = RECIPIENT_LENGTH + ASSET_ID_LENGTH + AMOUNT_LENGTH; protected static final TransactionLayout layout; @@ -33,6 +29,7 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer layout = new TransactionLayout(); layout.add("txType: " + TransactionType.TRANSFER_ASSET.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("asset owner's public key", TransformationType.PUBLIC_KEY); layout.add("asset's new owner", TransformationType.ADDRESS); @@ -44,6 +41,10 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -60,11 +61,11 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new TransferAssetTransactionData(senderPublicKey, recipient, amount, assetId, fee, timestamp, reference, signature); + return new TransferAssetTransactionData(timestamp, txGroupId, reference, senderPublicKey, recipient, amount, assetId, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { - return TYPE_LENGTH + TYPELESS_LENGTH; + return getBaseLength(transactionData) + EXTRAS_LENGTH; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -73,13 +74,12 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(transferAssetTransactionData.getType().value)); - bytes.write(Longs.toByteArray(transferAssetTransactionData.getTimestamp())); - bytes.write(transferAssetTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(transferAssetTransactionData.getSenderPublicKey()); Serialization.serializeAddress(bytes, transferAssetTransactionData.getRecipient()); + bytes.write(Longs.toByteArray(transferAssetTransactionData.getAssetId())); + Serialization.serializeBigDecimal(bytes, transferAssetTransactionData.getAmount(), AMOUNT_LENGTH); Serialization.serializeBigDecimal(bytes, transferAssetTransactionData.getFee()); @@ -93,30 +93,4 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - TransferAssetTransactionData transferAssetTransactionData = (TransferAssetTransactionData) transactionData; - - byte[] senderPublicKey = transferAssetTransactionData.getSenderPublicKey(); - - json.put("sender", PublicKeyAccount.getAddress(senderPublicKey)); - json.put("senderPublicKey", HashCode.fromBytes(senderPublicKey).toString()); - json.put("recipient", transferAssetTransactionData.getRecipient()); - - // For gen1 support: - json.put("asset", transferAssetTransactionData.getAssetId()); - // Gen2 version: - json.put("assetId", transferAssetTransactionData.getAssetId()); - - json.put("amount", transferAssetTransactionData.getAmount().toPlainString()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/UpdateGroupTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/UpdateGroupTransactionTransformer.java index 5d63f331..ab9490e3 100644 --- a/src/main/java/org/qora/transform/transaction/UpdateGroupTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/UpdateGroupTransactionTransformer.java @@ -5,31 +5,29 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.UpdateGroupTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.group.Group; +import org.qora.group.Group.ApprovalThreshold; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class UpdateGroupTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int OWNER_LENGTH = PUBLIC_KEY_LENGTH; private static final int GROUPID_LENGTH = INT_LENGTH; private static final int NEW_OWNER_LENGTH = ADDRESS_LENGTH; private static final int NEW_DESCRIPTION_SIZE_LENGTH = INT_LENGTH; private static final int NEW_IS_OPEN_LENGTH = BOOLEAN_LENGTH; + private static final int NEW_APPROVAL_THRESHOLD_LENGTH = BYTE_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + OWNER_LENGTH + GROUPID_LENGTH + NEW_OWNER_LENGTH + NEW_DESCRIPTION_SIZE_LENGTH - + NEW_IS_OPEN_LENGTH; + private static final int EXTRAS_LENGTH = GROUPID_LENGTH + NEW_OWNER_LENGTH + NEW_DESCRIPTION_SIZE_LENGTH + NEW_IS_OPEN_LENGTH + + NEW_APPROVAL_THRESHOLD_LENGTH; protected static final TransactionLayout layout; @@ -37,6 +35,7 @@ public class UpdateGroupTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.UPDATE_GROUP.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("group owner's public key", TransformationType.PUBLIC_KEY); layout.add("group ID", TransformationType.INT); @@ -44,6 +43,7 @@ public class UpdateGroupTransactionTransformer extends TransactionTransformer { layout.add("group's new description length", TransformationType.INT); layout.add("group's new description", TransformationType.STRING); layout.add("is group \"open\"?", TransformationType.BOOLEAN); + layout.add("new group transaction approval threshold", TransformationType.BYTE); layout.add("fee", TransformationType.AMOUNT); layout.add("signature", TransformationType.SIGNATURE); } @@ -51,6 +51,10 @@ public class UpdateGroupTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -64,20 +68,21 @@ public class UpdateGroupTransactionTransformer extends TransactionTransformer { boolean newIsOpen = byteBuffer.get() != 0; + ApprovalThreshold newApprovalThreshold = ApprovalThreshold.valueOf(byteBuffer.get()); + BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new UpdateGroupTransactionData(ownerPublicKey, groupId, newOwner, newDescription, newIsOpen, fee, timestamp, reference, signature); + return new UpdateGroupTransactionData(timestamp, txGroupId, reference, ownerPublicKey, groupId, newOwner, newDescription, newIsOpen, + newApprovalThreshold, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { UpdateGroupTransactionData updateGroupTransactionData = (UpdateGroupTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(updateGroupTransactionData.getNewDescription()); - - return dataLength; + return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(updateGroupTransactionData.getNewDescription()); } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -86,18 +91,18 @@ public class UpdateGroupTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(updateGroupTransactionData.getType().value)); - bytes.write(Longs.toByteArray(updateGroupTransactionData.getTimestamp())); - bytes.write(updateGroupTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(updateGroupTransactionData.getCreatorPublicKey()); bytes.write(Ints.toByteArray(updateGroupTransactionData.getGroupId())); Serialization.serializeAddress(bytes, updateGroupTransactionData.getNewOwner()); + Serialization.serializeSizedString(bytes, updateGroupTransactionData.getNewDescription()); bytes.write((byte) (updateGroupTransactionData.getNewIsOpen() ? 1 : 0)); + bytes.write((byte) updateGroupTransactionData.getNewApprovalThreshold().value); + Serialization.serializeBigDecimal(bytes, updateGroupTransactionData.getFee()); if (updateGroupTransactionData.getSignature() != null) @@ -109,27 +114,4 @@ public class UpdateGroupTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - UpdateGroupTransactionData updateGroupTransactionData = (UpdateGroupTransactionData) transactionData; - - byte[] ownerPublicKey = updateGroupTransactionData.getOwnerPublicKey(); - - json.put("owner", PublicKeyAccount.getAddress(ownerPublicKey)); - json.put("ownerPublicKey", HashCode.fromBytes(ownerPublicKey).toString()); - - json.put("groupId", updateGroupTransactionData.getGroupId()); - json.put("newOwner", updateGroupTransactionData.getNewOwner()); - json.put("newDescription", updateGroupTransactionData.getNewDescription()); - json.put("newIsOpen", updateGroupTransactionData.getNewIsOpen()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/UpdateNameTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/UpdateNameTransactionTransformer.java index 58beec5e..4db37fae 100644 --- a/src/main/java/org/qora/transform/transaction/UpdateNameTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/UpdateNameTransactionTransformer.java @@ -5,8 +5,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.TransactionData; import org.qora.data.transaction.UpdateNameTransactionData; import org.qora.naming.Name; @@ -15,19 +14,15 @@ import org.qora.transform.TransformationException; import org.qora.utils.Serialization; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class UpdateNameTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int REGISTRANT_LENGTH = PUBLIC_KEY_LENGTH; private static final int OWNER_LENGTH = ADDRESS_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; private static final int DATA_SIZE_LENGTH = INT_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + REGISTRANT_LENGTH + OWNER_LENGTH + NAME_SIZE_LENGTH + DATA_SIZE_LENGTH; + private static final int EXTRAS_LENGTH = OWNER_LENGTH + NAME_SIZE_LENGTH + DATA_SIZE_LENGTH; protected static final TransactionLayout layout; @@ -35,6 +30,7 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.UPDATE_NAME.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("name owner's public key", TransformationType.PUBLIC_KEY); layout.add("name's new owner", TransformationType.ADDRESS); @@ -49,6 +45,10 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -65,16 +65,14 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new UpdateNameTransactionData(ownerPublicKey, newOwner, name, newData, fee, timestamp, reference, signature); + return new UpdateNameTransactionData(timestamp, txGroupId, reference, ownerPublicKey, newOwner, name, newData, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { UpdateNameTransactionData updateNameTransactionData = (UpdateNameTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(updateNameTransactionData.getName()) + return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(updateNameTransactionData.getName()) + Utf8.encodedLength(updateNameTransactionData.getNewData()); - - return dataLength; } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -83,13 +81,12 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(updateNameTransactionData.getType().value)); - bytes.write(Longs.toByteArray(updateNameTransactionData.getTimestamp())); - bytes.write(updateNameTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); - bytes.write(updateNameTransactionData.getOwnerPublicKey()); Serialization.serializeAddress(bytes, updateNameTransactionData.getNewOwner()); + Serialization.serializeSizedString(bytes, updateNameTransactionData.getName()); + Serialization.serializeSizedString(bytes, updateNameTransactionData.getNewData()); Serialization.serializeBigDecimal(bytes, updateNameTransactionData.getFee()); @@ -103,26 +100,4 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - UpdateNameTransactionData updateNameTransactionData = (UpdateNameTransactionData) transactionData; - - byte[] ownerPublicKey = updateNameTransactionData.getOwnerPublicKey(); - - json.put("owner", PublicKeyAccount.getAddress(ownerPublicKey)); - json.put("ownerPublicKey", HashCode.fromBytes(ownerPublicKey).toString()); - - json.put("newOwner", updateNameTransactionData.getNewOwner()); - json.put("name", updateNameTransactionData.getName()); - json.put("newData", updateNameTransactionData.getNewData()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/transform/transaction/VoteOnPollTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/VoteOnPollTransactionTransformer.java index 171f46a7..367d05b4 100644 --- a/src/main/java/org/qora/transform/transaction/VoteOnPollTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/VoteOnPollTransactionTransformer.java @@ -5,8 +5,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; -import org.json.simple.JSONObject; -import org.qora.account.PublicKeyAccount; +import org.qora.block.BlockChain; import org.qora.data.transaction.TransactionData; import org.qora.data.transaction.VoteOnPollTransactionData; import org.qora.transaction.Transaction.TransactionType; @@ -15,9 +14,7 @@ import org.qora.utils.Serialization; import org.qora.voting.Poll; import com.google.common.base.Utf8; -import com.google.common.hash.HashCode; import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; public class VoteOnPollTransactionTransformer extends TransactionTransformer { @@ -25,7 +22,7 @@ public class VoteOnPollTransactionTransformer extends TransactionTransformer { private static final int VOTER_LENGTH = ADDRESS_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; - private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + VOTER_LENGTH + NAME_SIZE_LENGTH; + private static final int EXTRAS_LENGTH = VOTER_LENGTH + NAME_SIZE_LENGTH; protected static final TransactionLayout layout; @@ -33,6 +30,7 @@ public class VoteOnPollTransactionTransformer extends TransactionTransformer { layout = new TransactionLayout(); layout.add("txType: " + TransactionType.VOTE_ON_POLL.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); + layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("voter's public key", TransformationType.PUBLIC_KEY); layout.add("poll name length", TransformationType.INT); @@ -45,6 +43,10 @@ public class VoteOnPollTransactionTransformer extends TransactionTransformer { public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { long timestamp = byteBuffer.getLong(); + int txGroupId = 0; + if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp()) + txGroupId = byteBuffer.getInt(); + byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); @@ -61,15 +63,13 @@ public class VoteOnPollTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new VoteOnPollTransactionData(voterPublicKey, pollName, optionIndex, fee, timestamp, reference, signature); + return new VoteOnPollTransactionData(timestamp, txGroupId, reference, voterPublicKey, pollName, optionIndex, fee, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { VoteOnPollTransactionData voteOnPollTransactionData = (VoteOnPollTransactionData) transactionData; - int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(voteOnPollTransactionData.getPollName()); - - return dataLength; + return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(voteOnPollTransactionData.getPollName()); } public static byte[] toBytes(TransactionData transactionData) throws TransformationException { @@ -78,12 +78,12 @@ public class VoteOnPollTransactionTransformer extends TransactionTransformer { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Ints.toByteArray(voteOnPollTransactionData.getType().value)); - bytes.write(Longs.toByteArray(voteOnPollTransactionData.getTimestamp())); - bytes.write(voteOnPollTransactionData.getReference()); + transformCommonBytes(transactionData, bytes); bytes.write(voteOnPollTransactionData.getVoterPublicKey()); + Serialization.serializeSizedString(bytes, voteOnPollTransactionData.getPollName()); + bytes.write(Ints.toByteArray(voteOnPollTransactionData.getOptionIndex())); Serialization.serializeBigDecimal(bytes, voteOnPollTransactionData.getFee()); @@ -97,25 +97,4 @@ public class VoteOnPollTransactionTransformer extends TransactionTransformer { } } - @SuppressWarnings("unchecked") - public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { - JSONObject json = TransactionTransformer.getBaseJSON(transactionData); - - try { - VoteOnPollTransactionData voteOnPollTransactionData = (VoteOnPollTransactionData) transactionData; - - byte[] voterPublicKey = voteOnPollTransactionData.getVoterPublicKey(); - - json.put("voter", PublicKeyAccount.getAddress(voterPublicKey)); - json.put("voterPublicKey", HashCode.fromBytes(voterPublicKey).toString()); - - json.put("name", voteOnPollTransactionData.getPollName()); - json.put("optionIndex", voteOnPollTransactionData.getOptionIndex()); - } catch (ClassCastException e) { - throw new TransformationException(e); - } - - return json; - } - } diff --git a/src/main/java/org/qora/v1feeder.java b/src/main/java/org/qora/v1feeder.java index 8160669f..77ca458d 100644 --- a/src/main/java/org/qora/v1feeder.java +++ b/src/main/java/org/qora/v1feeder.java @@ -38,6 +38,7 @@ import org.qora.data.at.ATStateData; import org.qora.data.block.BlockData; import org.qora.data.transaction.ATTransactionData; import org.qora.data.transaction.TransactionData; +import org.qora.group.Group; import org.qora.repository.DataException; import org.qora.repository.Repository; import org.qora.repository.RepositoryFactory; @@ -495,13 +496,13 @@ public class v1feeder extends Thread { BigDecimal fee = BigDecimal.ZERO.setScale(8); - TransactionData transactionData = new ATTransactionData(sender, recipient, amount, Asset.QORA, message, fee, timestamp, reference); + TransactionData transactionData = new ATTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender, recipient, amount, Asset.QORA, message, fee); byte[] digest; try { digest = Crypto.digest(AtTransactionTransformer.toBytes(transactionData)); byte[] signature = Bytes.concat(digest, digest); - transactionData = new ATTransactionData(sender, recipient, amount, Asset.QORA, message, fee, timestamp, reference, signature); + transactionData = new ATTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender, recipient, amount, Asset.QORA, message, fee, signature); } catch (TransformationException e) { throw new RuntimeException("Couldn't transform AT Transaction into bytes", e); } diff --git a/src/test/java/org/qora/test/ATTests.java b/src/test/java/org/qora/test/ATTests.java index 7fa6ce63..ce9f09ac 100644 --- a/src/test/java/org/qora/test/ATTests.java +++ b/src/test/java/org/qora/test/ATTests.java @@ -6,6 +6,7 @@ import org.qora.data.at.ATStateData; import org.qora.data.block.BlockData; import org.qora.data.block.BlockTransactionData; import org.qora.data.transaction.DeployAtTransactionData; +import org.qora.group.Group; import org.qora.repository.DataException; import org.qora.repository.Repository; import org.qora.repository.RepositoryManager; @@ -46,8 +47,8 @@ public class ATTests extends Common { byte[] reference = Base58.decode("2D3jX1pEgu6irsQ7QzJb85QP1D9M45dNyP5M9a3WFHndU5ZywF4F5pnUurcbzMnGMcTwpAY6H7DuLw8cUBU66ao1"); byte[] signature = Base58.decode("2dZ4megUyNoYYY7qWmuSd4xw1yUKgPPF97yBbeddh8aKuC8PLpz7Xvf3r6Zjv1zwGrR8fEAHuaztCPD4KQp76KdL"); - DeployAtTransactionData transactionData = new DeployAtTransactionData(creatorPublicKey, name, description, ATType, tags, creationBytes, amount, - Asset.QORA, fee, timestamp, reference, signature); + DeployAtTransactionData transactionData = new DeployAtTransactionData(timestamp, Group.DEFAULT_GROUP, reference, creatorPublicKey, name, description, ATType, + tags, creationBytes, amount, Asset.QORA, fee, signature); try (final Repository repository = RepositoryManager.getRepository()) { repository.getTransactionRepository().save(transactionData); diff --git a/src/test/java/org/qora/test/SaveTests.java b/src/test/java/org/qora/test/SaveTests.java index 22d3eba6..b166a973 100644 --- a/src/test/java/org/qora/test/SaveTests.java +++ b/src/test/java/org/qora/test/SaveTests.java @@ -6,6 +6,7 @@ import java.time.Instant; import org.junit.jupiter.api.Test; import org.qora.account.PublicKeyAccount; import org.qora.data.transaction.PaymentTransactionData; +import org.qora.group.Group; import org.qora.repository.DataException; import org.qora.repository.Repository; import org.qora.repository.RepositoryManager; @@ -22,8 +23,8 @@ public class SaveTests extends Common { byte[] signature = Base58.decode(signature58); PublicKeyAccount sender = new PublicKeyAccount(repository, "Qsender".getBytes()); - PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), "Qrecipient", BigDecimal.valueOf(12345L), - BigDecimal.ONE, Instant.now().getEpochSecond(), reference, signature); + PaymentTransactionData paymentTransactionData = new PaymentTransactionData(Instant.now().getEpochSecond(), Group.DEFAULT_GROUP, reference, + sender.getPublicKey(), "Qrecipient", BigDecimal.valueOf(12345L), BigDecimal.ONE, signature); repository.getTransactionRepository().save(paymentTransactionData); diff --git a/src/test/java/org/qora/test/TransactionTests.java b/src/test/java/org/qora/test/TransactionTests.java index 2b60fb01..211f8a76 100644 --- a/src/test/java/org/qora/test/TransactionTests.java +++ b/src/test/java/org/qora/test/TransactionTests.java @@ -32,6 +32,7 @@ import org.qora.data.transaction.VoteOnPollTransactionData; import org.qora.data.voting.PollData; import org.qora.data.voting.PollOptionData; import org.qora.data.voting.VoteOnPollData; +import org.qora.group.Group; import org.qora.repository.AccountRepository; import org.qora.repository.AssetRepository; import org.qora.repository.DataException; @@ -120,7 +121,7 @@ public class TransactionTests { // Create test generator account generator = new PrivateKeyAccount(repository, generatorSeed); - accountRepository.save(new AccountData(generator.getAddress(), generatorSeed, generator.getPublicKey())); + accountRepository.setLastReference(new AccountData(generator.getAddress(), generatorSeed, generator.getPublicKey(), Group.DEFAULT_GROUP)); accountRepository.save(new AccountBalanceData(generator.getAddress(), Asset.QORA, initialGeneratorBalance)); // Create test sender account @@ -128,7 +129,7 @@ public class TransactionTests { // Mock account reference = senderSeed; - accountRepository.save(new AccountData(sender.getAddress(), reference, sender.getPublicKey())); + accountRepository.setLastReference(new AccountData(sender.getAddress(), reference, sender.getPublicKey(), Group.DEFAULT_GROUP)); // Mock balance accountRepository.save(new AccountBalanceData(sender.getAddress(), Asset.QORA, initialSenderBalance)); @@ -146,7 +147,7 @@ public class TransactionTests { BigDecimal amount = genericPaymentAmount; BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), recipient, amount, fee, timestamp, reference); + PaymentTransactionData paymentTransactionData = new PaymentTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), recipient, amount, fee); Transaction paymentTransaction = new PaymentTransaction(repository, paymentTransactionData); paymentTransaction.sign(sender); @@ -163,8 +164,8 @@ public class TransactionTests { BigDecimal amount = BigDecimal.valueOf(1_000L); BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), recipient.getAddress(), amount, fee, timestamp, - reference); + PaymentTransactionData paymentTransactionData = new PaymentTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), recipient.getAddress(), + amount, fee); Transaction paymentTransaction = new PaymentTransaction(repository, paymentTransactionData); paymentTransaction.sign(sender); @@ -224,8 +225,8 @@ public class TransactionTests { BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - RegisterNameTransactionData registerNameTransactionData = new RegisterNameTransactionData(sender.getPublicKey(), sender.getAddress(), name, data, fee, - timestamp, reference); + RegisterNameTransactionData registerNameTransactionData = new RegisterNameTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), sender.getAddress(), + name, data, fee); Transaction registerNameTransaction = new RegisterNameTransaction(repository, registerNameTransactionData); registerNameTransaction.sign(sender); @@ -280,8 +281,8 @@ public class TransactionTests { BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - UpdateNameTransactionData updateNameTransactionData = new UpdateNameTransactionData(sender.getPublicKey(), newOwner.getAddress(), name, newData, - nameReference, fee, timestamp, reference); + UpdateNameTransactionData updateNameTransactionData = new UpdateNameTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), + newOwner.getAddress(), name, newData, nameReference, fee); Transaction updateNameTransaction = new UpdateNameTransaction(repository, updateNameTransactionData); updateNameTransaction.sign(sender); @@ -326,7 +327,7 @@ public class TransactionTests { BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - SellNameTransactionData sellNameTransactionData = new SellNameTransactionData(sender.getPublicKey(), name, amount, fee, timestamp, reference); + SellNameTransactionData sellNameTransactionData = new SellNameTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), name, amount, fee); Transaction sellNameTransaction = new SellNameTransaction(repository, sellNameTransactionData); sellNameTransaction.sign(sender); @@ -377,7 +378,7 @@ public class TransactionTests { BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - CancelSellNameTransactionData cancelSellNameTransactionData = new CancelSellNameTransactionData(sender.getPublicKey(), name, fee, timestamp, reference); + CancelSellNameTransactionData cancelSellNameTransactionData = new CancelSellNameTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), name, fee); Transaction cancelSellNameTransaction = new CancelSellNameTransaction(repository, cancelSellNameTransactionData); cancelSellNameTransaction.sign(sender); @@ -442,8 +443,8 @@ public class TransactionTests { BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - BuyNameTransactionData buyNameTransactionData = new BuyNameTransactionData(buyer.getPublicKey(), name, originalNameData.getSalePrice(), seller, - nameReference, fee, timestamp, buyersReference); + BuyNameTransactionData buyNameTransactionData = new BuyNameTransactionData(timestamp, Group.DEFAULT_GROUP, buyersReference, buyer.getPublicKey(), + name, originalNameData.getSalePrice(), seller, nameReference, fee); Transaction buyNameTransaction = new BuyNameTransaction(repository, buyNameTransactionData); buyNameTransaction.sign(buyer); @@ -495,8 +496,8 @@ public class TransactionTests { Account recipient = new PublicKeyAccount(repository, recipientSeed); BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - CreatePollTransactionData createPollTransactionData = new CreatePollTransactionData(sender.getPublicKey(), recipient.getAddress(), pollName, - description, pollOptions, fee, timestamp, reference); + CreatePollTransactionData createPollTransactionData = new CreatePollTransactionData(timestamp, Group.DEFAULT_GROUP, reference, + sender.getPublicKey(), recipient.getAddress(), pollName, description, pollOptions, fee); Transaction createPollTransaction = new CreatePollTransaction(repository, createPollTransactionData); createPollTransaction.sign(sender); @@ -549,8 +550,8 @@ public class TransactionTests { for (int optionIndex = 0; optionIndex <= pollOptionsSize; ++optionIndex) { // Make a vote-on-poll transaction - VoteOnPollTransactionData voteOnPollTransactionData = new VoteOnPollTransactionData(sender.getPublicKey(), pollName, optionIndex, fee, timestamp, - reference); + VoteOnPollTransactionData voteOnPollTransactionData = new VoteOnPollTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), pollName, + optionIndex, fee); Transaction voteOnPollTransaction = new VoteOnPollTransaction(repository, voteOnPollTransactionData); voteOnPollTransaction.sign(sender); @@ -621,8 +622,8 @@ public class TransactionTests { BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - IssueAssetTransactionData issueAssetTransactionData = new IssueAssetTransactionData(sender.getPublicKey(), sender.getAddress(), assetName, description, - quantity, isDivisible, fee, timestamp, reference); + IssueAssetTransactionData issueAssetTransactionData = new IssueAssetTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), + sender.getAddress(), assetName, description, quantity, isDivisible, fee); Transaction issueAssetTransaction = new IssueAssetTransaction(repository, issueAssetTransactionData); issueAssetTransaction.sign(sender); @@ -711,8 +712,8 @@ public class TransactionTests { BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - TransferAssetTransactionData transferAssetTransactionData = new TransferAssetTransactionData(sender.getPublicKey(), recipient.getAddress(), amount, - assetId, fee, timestamp, reference); + TransferAssetTransactionData transferAssetTransactionData = new TransferAssetTransactionData(timestamp, Group.DEFAULT_GROUP, reference, + sender.getPublicKey(), recipient.getAddress(), amount, assetId, fee); Transaction transferAssetTransaction = new TransferAssetTransaction(repository, transferAssetTransactionData); transferAssetTransaction.sign(sender); @@ -816,8 +817,8 @@ public class TransactionTests { BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; - CreateAssetOrderTransactionData createOrderTransactionData = new CreateAssetOrderTransactionData(buyer.getPublicKey(), haveAssetId, wantAssetId, amount, price, - fee, timestamp, buyersReference); + CreateAssetOrderTransactionData createOrderTransactionData = new CreateAssetOrderTransactionData(timestamp, Group.DEFAULT_GROUP, buyersReference, buyer.getPublicKey(), haveAssetId, + wantAssetId, amount, price, fee); Transaction createOrderTransaction = new CreateAssetOrderTransaction(this.repository, createOrderTransactionData); createOrderTransaction.sign(buyer); assertTrue(createOrderTransaction.isSignatureValid()); @@ -897,7 +898,7 @@ public class TransactionTests { BigDecimal fee = BigDecimal.ONE; long timestamp = parentBlockData.getTimestamp() + 1_000; byte[] buyersReference = buyer.getLastReference(); - CancelAssetOrderTransactionData cancelOrderTransactionData = new CancelAssetOrderTransactionData(buyer.getPublicKey(), orderId, fee, timestamp, buyersReference); + CancelAssetOrderTransactionData cancelOrderTransactionData = new CancelAssetOrderTransactionData(timestamp, Group.DEFAULT_GROUP, buyersReference, buyer.getPublicKey(), orderId, fee); Transaction cancelOrderTransaction = new CancelAssetOrderTransaction(this.repository, cancelOrderTransactionData); cancelOrderTransaction.sign(buyer); @@ -972,8 +973,8 @@ public class TransactionTests { long timestamp = parentBlockData.getTimestamp() + 1_000; BigDecimal senderPreTradeWantBalance = sender.getConfirmedBalance(wantAssetId); - CreateAssetOrderTransactionData createOrderTransactionData = new CreateAssetOrderTransactionData(sender.getPublicKey(), haveAssetId, wantAssetId, amount, price, - fee, timestamp, reference); + CreateAssetOrderTransactionData createOrderTransactionData = new CreateAssetOrderTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), haveAssetId, + wantAssetId, amount, price, fee); Transaction createOrderTransaction = new CreateAssetOrderTransaction(this.repository, createOrderTransactionData); createOrderTransaction.sign(sender); assertTrue(createOrderTransaction.isSignatureValid()); @@ -1081,7 +1082,7 @@ public class TransactionTests { payments.add(paymentData); } - MultiPaymentTransactionData multiPaymentTransactionData = new MultiPaymentTransactionData(sender.getPublicKey(), payments, fee, timestamp, reference); + MultiPaymentTransactionData multiPaymentTransactionData = new MultiPaymentTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), payments, fee); Transaction multiPaymentTransaction = new MultiPaymentTransaction(repository, multiPaymentTransactionData); multiPaymentTransaction.sign(sender); @@ -1150,8 +1151,8 @@ public class TransactionTests { boolean isText = true; boolean isEncrypted = false; - MessageTransactionData messageTransactionData = new MessageTransactionData(version, sender.getPublicKey(), recipient.getAddress(), Asset.QORA, amount, - data, isText, isEncrypted, fee, timestamp, reference); + MessageTransactionData messageTransactionData = new MessageTransactionData(timestamp, Group.DEFAULT_GROUP, reference, sender.getPublicKey(), version, + recipient.getAddress(), Asset.QORA, amount, data, isText, isEncrypted, fee); Transaction messageTransaction = new MessageTransaction(repository, messageTransactionData); messageTransaction.sign(sender);