From 00656f67247336078fd96cc1d69344840f847b75 Mon Sep 17 00:00:00 2001 From: catbref Date: Mon, 18 Feb 2019 19:00:37 +0000 Subject: [PATCH] Interim safety commit due to large number of changes! log4j2.properties now has debugging entries removed. log4j2-test.properties (not in repo) takes priority so using that in development instead. Unconfirmed transactions no longer wiped on start-up by default - see Settings Reworking of {Public,Private,Genesis}Accounts as it seemed possible to silently lose public key in repository. The use of AccountData didn't work and so field-specific repository calls have been made instead (e.g. setLastReference) that try to opportunistically store public key too, if available (i.e. caller is PublicKeyAccount subclass, or better). Added API call GET /addresses/{address} to return general account info in one go. (Essentially the AccountData object as fetched from repository). Initial work on adding default groupID to accounts, along with corresponding SET_GROUP transaction type. In additional, added blockchain-wide default groupID and flag to allow/disallow no-group/groupless transactions. Initial work on group-admin approval of transactions tied to a specific group via txGroupId. More work needed on transaction's "effective txGroupId"! API call /transactions/pending to list transactions pending group-admin approval. However, this needs more work (see effective txGroupId above) and potentially offloading to HSQLDB repository if possible. Minor CIYAM AT renames to help static reflection initializers. Block.orphan() no longer adds orphaned transactions back to unconfirmed pile as they are themselves deleted during Transaction.orphan(). Maybe the answer is to NOT delete them during Transaction.orphan() but to add them to unconfirmed pile at that point? Very old transactions leftover from major resync would simply expire, whereas recently transactions leftover from minor resync could still make it into a new block on synced chain fork. Changes/tidying/improvements to block generator regarding removing invalid transactions and dealing with transactions pending group approval. Approval threshold added to groups. Mass refactoring of transaction-related classes to unify constructors, particularly field ordering, to fall in line with raw transaction layout. e.g. constructors now reflect that raw transactions mostly start with type, timestamp, txGroupId, publicKey, reference e.g. JAXB afterUnmarshal methods added where needed and corresponding nasty code in Transaction subclass constructors ripped out. e.g. TransactionTransformer subclasses contain less duplicated code. Fixed bug with repository save points thanks to swapping to Deque. Some fixes to do with missing transaction types being passed to JAXB TransactionData subclass constructors. Ripped out obsolete toJSON in TransactionTransformers as this is all nicely taken care of by Swagger/OpenAPI (thanks @Kc) --- log4j2.properties | 27 +- src/main/java/org/qora/account/Account.java | 62 +++-- .../org/qora/account/PrivateKeyAccount.java | 8 +- .../org/qora/account/PublicKeyAccount.java | 16 +- .../qora/api/resource/AddressesResource.java | 25 ++ .../org/qora/api/resource/GroupsResource.java | 90 +++++++ .../api/resource/TransactionsResource.java | 52 ++++ src/main/java/org/qora/at/AT.java | 6 +- src/main/java/org/qora/at/BlockchainAPI.java | 3 +- src/main/java/org/qora/at/QoraATAPI.java | 37 +-- src/main/java/org/qora/block/Block.java | 11 +- src/main/java/org/qora/block/BlockChain.java | 32 +++ .../java/org/qora/block/BlockGenerator.java | 25 +- .../java/org/qora/block/GenesisBlock.java | 4 +- .../org/qora/controller/Synchronizer.java | 2 +- .../qora/data/account/AccountBalanceData.java | 7 +- .../org/qora/data/account/AccountData.java | 25 +- .../java/org/qora/data/group/GroupData.java | 18 +- .../data/transaction/ATTransactionData.java | 22 +- .../AddGroupAdminTransactionData.java | 8 +- .../transaction/ArbitraryTransactionData.java | 44 ++-- .../transaction/BuyNameTransactionData.java | 31 ++- .../CancelAssetOrderTransactionData.java | 12 +- .../CancelGroupBanTransactionData.java | 12 +- .../CancelGroupInviteTransactionData.java | 12 +- .../CancelSellNameTransactionData.java | 17 +- .../CreateAssetOrderTransactionData.java | 16 +- .../CreateGroupTransactionData.java | 20 +- .../CreatePollTransactionData.java | 18 +- .../transaction/DeployAtTransactionData.java | 43 +-- .../transaction/GenesisTransactionData.java | 23 +- .../GroupApprovalTransactionData.java | 95 +++++++ .../transaction/GroupBanTransactionData.java | 16 +- .../GroupInviteTransactionData.java | 12 +- .../transaction/GroupKickTransactionData.java | 16 +- .../IssueAssetTransactionData.java | 27 +- .../transaction/JoinGroupTransactionData.java | 12 +- .../LeaveGroupTransactionData.java | 12 +- .../transaction/MessageTransactionData.java | 34 ++- .../MultiPaymentTransactionData.java | 19 +- .../transaction/PaymentTransactionData.java | 19 +- .../RegisterNameTransactionData.java | 19 +- .../RemoveGroupAdminTransactionData.java | 12 +- .../transaction/SellNameTransactionData.java | 17 +- .../transaction/SetGroupTransactionData.java | 86 ++++++ .../data/transaction/TransactionData.java | 49 ++-- .../TransferAssetTransactionData.java | 22 +- .../UpdateGroupTransactionData.java | 61 +++-- .../UpdateNameTransactionData.java | 27 +- .../VoteOnPollTransactionData.java | 26 +- src/main/java/org/qora/group/Group.java | 65 ++++- .../qora/repository/AccountRepository.java | 30 ++- .../repository/TransactionRepository.java | 15 ++ .../hsqldb/HSQLDBAccountRepository.java | 87 ++++-- .../hsqldb/HSQLDBDatabaseUpdates.java | 33 +++ .../hsqldb/HSQLDBGroupRepository.java | 33 ++- .../repository/hsqldb/HSQLDBRepository.java | 13 +- ...LDBAddGroupAdminTransactionRepository.java | 4 +- .../HSQLDBArbitraryTransactionRepository.java | 15 +- .../HSQLDBAtTransactionRepository.java | 4 +- .../HSQLDBBuyNameTransactionRepository.java | 4 +- ...CancelAssetOrderTransactionRepository.java | 4 +- ...DBCancelGroupBanTransactionRepository.java | 4 +- ...ancelGroupInviteTransactionRepository.java | 4 +- ...DBCancelSellNameTransactionRepository.java | 4 +- ...CreateAssetOrderTransactionRepository.java | 4 +- ...SQLDBCreateGroupTransactionRepository.java | 13 +- ...HSQLDBCreatePollTransactionRepository.java | 4 +- .../HSQLDBDeployAtTransactionRepository.java | 16 +- .../HSQLDBGenesisTransactionRepository.java | 4 +- ...LDBGroupApprovalTransactionRepository.java | 53 ++++ .../HSQLDBGroupBanTransactionRepository.java | 6 +- ...SQLDBGroupInviteTransactionRepository.java | 4 +- .../HSQLDBGroupKickTransactionRepository.java | 6 +- ...HSQLDBIssueAssetTransactionRepository.java | 21 +- .../HSQLDBJoinGroupTransactionRepository.java | 4 +- ...HSQLDBLeaveGroupTransactionRepository.java | 4 +- .../HSQLDBMessageTransactionRepository.java | 21 +- ...QLDBMultiPaymentTransactionRepository.java | 8 +- .../HSQLDBPaymentTransactionRepository.java | 11 +- ...QLDBRegisterNameTransactionRepository.java | 4 +- ...RemoveGroupAdminTransactionRepository.java | 4 +- .../HSQLDBSellNameTransactionRepository.java | 4 +- .../HSQLDBSetGroupTransactionRepository.java | 52 ++++ .../HSQLDBTransactionRepository.java | 93 ++++++- ...LDBTransferAssetTransactionRepository.java | 13 +- ...SQLDBUpdateGroupTransactionRepository.java | 14 +- ...HSQLDBUpdateNameTransactionRepository.java | 4 +- ...HSQLDBVoteOnPollTransactionRepository.java | 4 +- src/main/java/org/qora/settings/Settings.java | 2 +- .../transaction/AddGroupAdminTransaction.java | 4 + ...{ATTransaction.java => AtTransaction.java} | 4 +- .../CancelGroupBanTransaction.java | 4 + .../CancelGroupInviteTransaction.java | 4 + .../transaction/CreateGroupTransaction.java | 4 + .../qora/transaction/DeployAtTransaction.java | 10 +- .../transaction/GroupApprovalTransaction.java | 136 ++++++++++ .../qora/transaction/GroupBanTransaction.java | 4 + .../transaction/GroupInviteTransaction.java | 5 + .../transaction/GroupKickTransaction.java | 4 + .../transaction/LeaveGroupTransaction.java | 4 + .../RemoveGroupAdminTransaction.java | 4 + .../qora/transaction/SetGroupTransaction.java | 135 ++++++++++ .../org/qora/transaction/Transaction.java | 247 +++++++++++++----- .../transaction/UpdateGroupTransaction.java | 8 + .../java/org/qora/transform/Transformer.java | 1 + .../transform/block/BlockTransformer.java | 87 ------ .../AddGroupAdminTransactionTransformer.java | 44 +--- .../ArbitraryTransactionTransformer.java | 69 ++--- .../transaction/AtTransactionTransformer.java | 78 +----- .../BuyNameTransactionTransformer.java | 50 +--- ...ancelAssetOrderTransactionTransformer.java | 45 +--- .../CancelGroupBanTransactionTransformer.java | 44 +--- ...ncelGroupInviteTransactionTransformer.java | 44 +--- .../CancelSellNameTransactionTransformer.java | 45 +--- ...reateAssetOrderTransactionTransformer.java | 49 +--- .../CreateGroupTransactionTransformer.java | 59 ++--- .../CreatePollTransactionTransformer.java | 56 +--- .../DeployAtTransactionTransformer.java | 60 ++--- .../GenesisTransactionTransformer.java | 36 +-- .../GroupApprovalTransactionTransformer.java | 90 +++++++ .../GroupBanTransactionTransformer.java | 50 +--- .../GroupInviteTransactionTransformer.java | 46 +--- .../GroupKickTransactionTransformer.java | 48 +--- .../IssueAssetTransactionTransformer.java | 48 +--- .../JoinGroupTransactionTransformer.java | 42 +-- .../LeaveGroupTransactionTransformer.java | 42 +-- .../MessageTransactionTransformer.java | 68 ++--- .../MultiPaymentTransactionTransformer.java | 49 +--- .../PaymentTransactionTransformer.java | 45 +--- .../RegisterNameTransactionTransformer.java | 49 +--- ...emoveGroupAdminTransactionTransformer.java | 44 +--- .../SellNameTransactionTransformer.java | 47 +--- .../SetGroupTransactionTransformer.java | 85 ++++++ .../transaction/TransactionTransformer.java | 65 +++-- .../TransferAssetTransactionTransformer.java | 50 +--- .../UpdateGroupTransactionTransformer.java | 58 ++-- .../UpdateNameTransactionTransformer.java | 49 +--- .../VoteOnPollTransactionTransformer.java | 45 +--- src/main/java/org/qora/v1feeder.java | 5 +- src/test/java/org/qora/test/ATTests.java | 5 +- src/test/java/org/qora/test/SaveTests.java | 5 +- .../java/org/qora/test/TransactionTests.java | 59 +++-- 143 files changed, 2591 insertions(+), 1848 deletions(-) create mode 100644 src/main/java/org/qora/data/transaction/GroupApprovalTransactionData.java create mode 100644 src/main/java/org/qora/data/transaction/SetGroupTransactionData.java create mode 100644 src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBGroupApprovalTransactionRepository.java create mode 100644 src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBSetGroupTransactionRepository.java rename src/main/java/org/qora/transaction/{ATTransaction.java => AtTransaction.java} (98%) create mode 100644 src/main/java/org/qora/transaction/GroupApprovalTransaction.java create mode 100644 src/main/java/org/qora/transaction/SetGroupTransaction.java create mode 100644 src/main/java/org/qora/transform/transaction/GroupApprovalTransactionTransformer.java create mode 100644 src/main/java/org/qora/transform/transaction/SetGroupTransactionTransformer.java 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);