diff --git a/CHANGELOG.md b/CHANGELOG.md index 918352301..6f2c3ea08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 11.0.0 + +- Support for Protocol 9 +- Added `ProtocolVersion.V9` corresponding to Protocol version 9 +- Added `tokens` field to `AccountInfo`, containing list of related protocol level tokens +- Added new account transaction, `TokenUpdate`, used to execute protocol level tokens operations +- Added `TokenOperation` interface for protocol level tokens operations +- Added `TransferTokenOperation` implementing protocol level token transfer +- Added protocol level token transfer example, see `SendTokenTransfer` in `concordium-sdk-examples` +- Added `tokenUpdate` result to `AccountTransactionDetails` +- Added `createPltUpdate` authorization to `AuthorizationsV1` +- Added `RejectReasonTokenUpdateTransactionFailed` and `RejectReasonNotExistentTokenId` transaction reject reasons +- Added `CborMapper` singleton providing Jackson CBOR object mapper +- Fixed having the Lombok library transitive + ## 10.0.1 - Added `getPassiveDelegationInfo` endpoint providing information diff --git a/README.md b/README.md index a1ca0aa01..3b8401669 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ The `ClientV2` must be initialized with a `Connection` which holds information o Connection connection = Connection.newBuilder() .host(${node_host}) .port(${node_port}) + .useTLS(TLSConfig.auto()) // If the node is under HTTPS .build(); ClientV2 client = ClientV2.from(connection); ``` diff --git a/concordium-android-sdk/pom.xml b/concordium-android-sdk/pom.xml index 165f165fc..b527b2e55 100644 --- a/concordium-android-sdk/pom.xml +++ b/concordium-android-sdk/pom.xml @@ -5,11 +5,10 @@ com.concordium.sdk concordium-sdk-base - 11.0.0-SNAPSHOT + 11.0.0 concordium-android-sdk - com.concordium.sdk aar @@ -33,57 +32,54 @@ io.grpc grpc-netty-shaded - 1.40.1 commons-io commons-io - 2.11.0 commons-codec commons-codec - 1.15 org.projectlombok lombok - 1.18.26 io.grpc grpc-protobuf - 1.40.1 io.grpc grpc-okhttp - 1.60.0 io.grpc grpc-stub - 1.60.0 com.fasterxml.jackson.core jackson-core - 2.10.1 + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.dataformat + jackson-dataformat-cbor com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.10.1 com.fasterxml.jackson.datatype jackson-datatype-jdk8 - 2.10.1 org.bitcoinj bitcoinj-core - 0.16.2 com.google.protobuf @@ -94,7 +90,6 @@ org.apache.commons commons-lang3 - 3.14.0 diff --git a/concordium-base b/concordium-base index 6f6d1088c..f69906f2e 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 6f6d1088cd8660abe7d9e4a1274a817032313a57 +Subproject commit f69906f2e386444d85ab99f38951869cedf4be67 diff --git a/concordium-sdk-examples/pom.xml b/concordium-sdk-examples/pom.xml index 4c2a7e40f..0319056e5 100644 --- a/concordium-sdk-examples/pom.xml +++ b/concordium-sdk-examples/pom.xml @@ -23,8 +23,7 @@ com.concordium.sdk concordium-sdk - 11.0.0-SNAPSHOT - compile + 11.0.0 org.projectlombok diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTokenTransfer.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTokenTransfer.java new file mode 100644 index 000000000..979998af5 --- /dev/null +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTokenTransfer.java @@ -0,0 +1,102 @@ +package com.concordium.sdk.examples; + +import com.concordium.sdk.ClientV2; +import com.concordium.sdk.Connection; +import com.concordium.sdk.TLSConfig; +import com.concordium.sdk.crypto.ed25519.ED25519SecretKey; +import com.concordium.sdk.exceptions.ClientInitializationException; +import com.concordium.sdk.requests.AccountQuery; +import com.concordium.sdk.requests.BlockQuery; +import com.concordium.sdk.responses.blockitemstatus.FinalizedBlockItem; +import com.concordium.sdk.transactions.*; +import com.concordium.sdk.transactions.tokens.CborMemo; +import com.concordium.sdk.transactions.tokens.TaggedTokenHolderAccount; +import com.concordium.sdk.transactions.tokens.TokenOperationAmount; +import com.concordium.sdk.transactions.tokens.TransferTokenOperation; +import com.concordium.sdk.types.AccountAddress; +import lombok.var; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +import java.math.BigDecimal; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Optional; +import java.util.concurrent.Callable; + +@Command(name = "SendTokenTransfer", mixinStandardHelpOptions = true) +public class SendTokenTransfer implements Callable { + @Option( + names = {"--endpoint"}, + description = "GRPC interface of the node.", + defaultValue = "https://grpc.devnet-plt-beta.concordium.com:20000") + private String endpoint; + + @Option( + names = {"--timeout"}, + description = "GRPC request timeout in milliseconds.", + defaultValue = "100000") + private int timeout; + + @Override + public Integer call() throws MalformedURLException, ClientInitializationException { + var endpointUrl = new URL(this.endpoint); + + Connection connection = Connection.newBuilder() + .host(endpointUrl.getHost()) + .port(endpointUrl.getPort()) + .useTLS(TLSConfig.auto()) + .build(); + + String tokenSymbol = "TestLists"; + TokenOperationAmount amount = new TokenOperationAmount( + new BigDecimal("0.01"), + 10 + ); + AccountAddress sender = AccountAddress.from("4m9AzH7oeq2LNZpmBu3uW9KJEimevgBMD79PhTMxJeYVmtRdxR"); + AccountAddress receiver = AccountAddress.from("386L81BpBVrm2cDrnjEqpaGcuveC8FgiH5ZBxSVdvto4ydVFLX"); + Expiry expiry = Expiry.createNew().addMinutes(5); + + TransactionSigner signer = TransactionSigner.from( + SignerEntry.from(Index.from(0), Index.from(0), + ED25519SecretKey + .from("ad42d4f5122f4cc758c27b9c776f2d7635f30e55acb8697bb7a97edb3d7f0d88"))); + + var client = ClientV2.from(connection); + var senderInfo = client.getAccountInfo(BlockQuery.BEST, AccountQuery.from(sender)); + var nonce = senderInfo.getNonce(); + var txnHash = client.sendTransaction( + TransactionFactory + .newTokenUpdate() + .sender(sender) + .payload( + TokenUpdate + .builder() + .tokenSymbol(tokenSymbol) + .operation( + TransferTokenOperation + .builder() + .recipient(new TaggedTokenHolderAccount(receiver)) + .amount(amount) + .memo(CborMemo.from("You must see a multi-byte white woman scientist emoji: 👩🏻‍🔬")) + .build() + ) + .build() + ) + .nonce(nonce) + .expiry(expiry) + .signer(signer) + .build() + ); + System.out.println(txnHash); + Optional finalizedBlockItem = client.waitUntilFinalized(txnHash, this.timeout); + System.out.println(finalizedBlockItem); + return 0; + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new SendTokenTransfer()).execute(args); + System.exit(exitCode); + } +} diff --git a/concordium-sdk/pom.xml b/concordium-sdk/pom.xml index 566d90ff5..547765a47 100644 --- a/concordium-sdk/pom.xml +++ b/concordium-sdk/pom.xml @@ -5,12 +5,11 @@ 4.0.0 - com.concordium.sdk + com.concordium.sdk concordium-sdk-base - 11.0.0-SNAPSHOT + 11.0.0 - com.concordium.sdk concordium-sdk concordium-sdk @@ -26,15 +25,12 @@ UTF-8 - 1.8 - 1.8 org.bitcoinj bitcoinj-core - 0.16.2 com.google.protobuf @@ -45,75 +41,70 @@ io.grpc grpc-netty-shaded - 1.40.1 io.grpc grpc-protobuf - 1.40.1 io.grpc grpc-testing - 1.40.1 test io.grpc grpc-stub - 1.40.1 javax.annotation javax.annotation-api - 1.2 junit junit - 4.12 test org.projectlombok lombok - 1.18.26 + provided com.fasterxml.jackson.core - jackson-databind - 2.11.1 + jackson-core - commons-codec - commons-codec - 1.15 + com.fasterxml.jackson.core + jackson-databind - commons-io - commons-io - 2.11.0 + com.fasterxml.jackson.dataformat + jackson-dataformat-cbor com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.13.2 com.fasterxml.jackson.datatype jackson-datatype-jdk8 - 2.13.3 + + + commons-codec + commons-codec + + + commons-io + commons-io org.mockito mockito-core - 3.3.3 test org.apache.commons commons-lang3 - 3.14.0 @@ -359,6 +350,11 @@ maven-compiler-plugin 3.8.0 + + 1.8 + 1.8 + false + maven-surefire-plugin diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2MapperExtensions.java b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2MapperExtensions.java index 2953c7362..069a90c18 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2MapperExtensions.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2MapperExtensions.java @@ -1245,6 +1245,14 @@ static Summary to(BlockItemSummary blockItemSummary) { .chainUpdateDetails(details) .build()); break; + case TOKEN_CREATION: + val tokenCreationDetails = blockItemSummary.getTokenCreation(); + summary.details(Details + .builder() + .type(Type.TOKEN_CREATION) + .tokenCreationDetails(tokenCreationDetails) + .build()); + break; case DETAILS_NOT_SET: throw new IllegalArgumentException("Unrecognized block item summary"); } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/ProtocolVersion.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/ProtocolVersion.java index e26331de0..530a65dfe 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/ProtocolVersion.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/ProtocolVersion.java @@ -6,33 +6,37 @@ public enum ProtocolVersion { V1, /** - * https://github.com/Concordium/concordium-update-proposals/blob/main/updates/P2.txt + * Protocol Version: 2 */ V2, /** - * https://github.com/Concordium/concordium-update-proposals/blob/main/updates/P3.txt + * Protocol Version: 3 */ V3, /** - * https://github.com/Concordium/concordium-update-proposals/blob/main/updates/P4.txt + * Protocol Version: 4 */ V4, /** - * https://github.com/Concordium/concordium-update-proposals/blob/main/updates/P5.txt + * Protocol Version: 5 */ V5, /** - * https://github.com/Concordium/concordium-update-proposals/blob/main/updates/P6.txt + * Protocol Version: 6 */ V6, /** - * https://github.com/Concordium/concordium-update-proposals/blob/main/updates/P7.txt + * Protocol Version: 7 */ V7, /** - * https://github.com/Concordium/concordium-update-proposals/blob/main/updates/P8.txt + * Protocol Version: 8 */ V8, + /** + * Protocol Version: 9 + */ + V9, ; /** @@ -59,6 +63,8 @@ public static ProtocolVersion parse(com.concordium.grpc.v2.ProtocolVersion proto return V7; case PROTOCOL_VERSION_8: return V8; + case PROTOCOL_VERSION_9: + return V9; default: throw new IllegalArgumentException("Unrecognized protocol version " + protocolVersion); } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/AccountInfo.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/AccountInfo.java index 23cb0f8bd..14cb07aa4 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/AccountInfo.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/AccountInfo.java @@ -38,7 +38,7 @@ public final class AccountInfo { */ private final CCDAmount accountAmount; /** - * The available (unencrypted) balance of the account (i.e. that can be transferred + * The available (unencrypted) balance of CCD's of the account (i.e. that can be transferred * or used to pay for transactions). This is the balance ({@link AccountInfo#accountAmount}) * minus the locked amount. * The locked amount is the maximum of the amount in the release schedule ({@link AccountInfo#accountReleaseSchedule}) @@ -98,6 +98,12 @@ public final class AccountInfo { @Singular private final ImmutableList cooldowns; + /** + * The protocol level tokens (PLT) held by the account. + */ + @Singular + private final ImmutableList tokens; + public boolean isBaker() { return !Objects.isNull(bakerInfo); } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/AccountTransactionDetails.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/AccountTransactionDetails.java index c5003edcd..f4370541a 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/AccountTransactionDetails.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/AccountTransactionDetails.java @@ -176,6 +176,12 @@ public class AccountTransactionDetails { */ private final EncryptedAmountsRemovedResult removedFromEncryptedBalance; + /** + * The result of the protocol level token update. + * Present if the transaction was a {@link TokenUpdate}. + */ + private final TokenUpdateResult tokenUpdate; + public static AccountTransactionDetails from(com.concordium.grpc.v2.AccountTransactionDetails tx) { val sender = AccountAddress.from(tx.getSender()); val detailsBuilder = AccountTransactionDetails.builder().sender(sender).cost(CCDAmount.from(tx.getCost())).successful(true); @@ -241,6 +247,9 @@ public static AccountTransactionDetails from(com.concordium.grpc.v2.AccountTrans case DELEGATION_CONFIGURED: detailsBuilder.type(TransactionResultEventType.DELEGATION_CONFIGURED).delegatorConfigured(DelegatorConfigured.from(effects.getDelegationConfigured(), sender)); break; + case TOKEN_UPDATE_EFFECT: + detailsBuilder.type(TransactionResultEventType.TOKEN_UPDATE_EFFECT).tokenUpdate(TokenUpdateResult.builder().effect(effects.getTokenUpdateEffect()).build()); + break; case EFFECT_NOT_SET: throw new IllegalArgumentException("Unrecognized effect."); } @@ -415,6 +424,16 @@ private static void extractRejectReasonError(AccountTransactionDetailsBuilder de case POOL_CLOSED: detailsBuilder.rejectReason(new RejectReasonPoolClosed()); break; + case NON_EXISTENT_TOKEN_ID: + detailsBuilder.rejectReason(RejectReasonNotExistentTokenId.builder() + .tokenId(reason.getNonExistentTokenId()) + .build()); + break; + case TOKEN_UPDATE_TRANSACTION_FAILED: + detailsBuilder.rejectReason(RejectReasonTokenUpdateTransactionFailed.builder() + .tokenModuleRejectReason(reason.getTokenUpdateTransactionFailed()) + .build()); + break; case REASON_NOT_SET: break; } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/ChainUpdateDetails.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/ChainUpdateDetails.java index beeedc654..aba51fa72 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/ChainUpdateDetails.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/ChainUpdateDetails.java @@ -11,15 +11,12 @@ import com.concordium.sdk.transactions.CCDAmount; import com.concordium.sdk.types.AccountAddress; import com.concordium.sdk.types.Timestamp; -import com.fasterxml.jackson.annotation.JsonUnwrapped; import lombok.*; import java.time.Duration; -import java.util.Optional; /** - * Details of the different chain updates that - * may occur on the chain. + * Details of a successful chain update. */ @EqualsAndHashCode(doNotUseGetters = true) @Builder @@ -157,6 +154,12 @@ public class ChainUpdateDetails { */ private final FinalizationCommitteeParameters finalizationCommitteeParametersUpdate; + /** + * Parameters that govern validator suspension were changed. + * This is only non-null if the type is {@link UpdateType#VALIDATOR_SCORE_PARAMETERS} + */ + private final ValidatorScoreParameters validatorScoreParametersUpdate; + public static ChainUpdateDetails from(UpdateDetails update) { val chainUpdateDetailsBuilder = ChainUpdateDetails .builder() @@ -272,6 +275,13 @@ public static ChainUpdateDetails from(UpdateDetails update) { .type(UpdateType.UPDATE_FINALIZATION_COMMITTEE_PARAMETERS) .finalizationCommitteeParametersUpdate(FinalizationCommitteeParameters.from(payload.getFinalizationCommitteeParametersUpdate())); break; + case VALIDATOR_SCORE_PARAMETERS_UPDATE: + chainUpdateDetailsBuilder + .type(UpdateType.VALIDATOR_SCORE_PARAMETERS) + .validatorScoreParametersUpdate(ValidatorScoreParameters.from(payload.getValidatorScoreParametersUpdate())); + break; + case CREATE_PLT_UPDATE: + throw new IllegalStateException("This can't happen. CreatePLT operations are not enqueued, but happen immediately"); case PAYLOAD_NOT_SET: throw new IllegalArgumentException("Unrecognized chain update"); diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Details.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Details.java index 5a7e1b1b1..8d599027d 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Details.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Details.java @@ -1,6 +1,7 @@ package com.concordium.sdk.responses.blockitemsummary; import com.concordium.grpc.v2.UpdateDetails; +import com.concordium.grpc.v2.plt.TokenCreationDetails; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -37,6 +38,12 @@ public class Details { */ private final ChainUpdateDetails chainUpdateDetails; + /** + * Protocol-level token (PLT) creation details. + * This is only present if the type is {@link Type#TOKEN_CREATION} + */ + private final TokenCreationDetails tokenCreationDetails; + public static Details newAccountTransaction(com.concordium.grpc.v2.AccountTransactionDetails accountTransaction) { return Details.builder().type(Type.ACCOUNT_TRANSACTION).accountTransactionDetails(AccountTransactionDetails.from(accountTransaction)).build(); } @@ -48,4 +55,8 @@ public static Details newAcountCreation(com.concordium.grpc.v2.AccountCreationDe public static Details newChainUpdate(UpdateDetails update) { return Details.builder().type(Type.CHAIN_UPDATE).chainUpdateDetails(ChainUpdateDetails.from(update)).build(); } + + public static Details newTokenCreation(TokenCreationDetails tokenCreationDetails) { + return Details.builder().type(Type.TOKEN_CREATION).tokenCreationDetails(tokenCreationDetails).build(); + } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Summary.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Summary.java index 3d81fb684..bfb34eddd 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Summary.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Summary.java @@ -52,6 +52,9 @@ public static Summary from(BlockItemSummaryInBlock summary) { case UPDATE: builder.details(Details.newChainUpdate(outcome.getUpdate())); break; + case TOKEN_CREATION: + builder.details(Details.newTokenCreation(outcome.getTokenCreation())); + break; case DETAILS_NOT_SET: throw new IllegalArgumentException("Details type is not set."); } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Type.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Type.java index 5069a0ae6..b8cbc4137 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Type.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/Type.java @@ -16,5 +16,9 @@ public enum Type { /** * A chain update */ - CHAIN_UPDATE + CHAIN_UPDATE, + /** + * A new protocol-level token (PLT) was created. + */ + TOKEN_CREATION, } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/UpdateType.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/UpdateType.java index fd2466cee..33ec1d5b8 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/UpdateType.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/blockitemsummary/UpdateType.java @@ -132,5 +132,11 @@ public enum UpdateType { * of the finalization committee. * This parameter is only applicable from protocol version 6 and onwards. */ - UPDATE_FINALIZATION_COMMITTEE_PARAMETERS + UPDATE_FINALIZATION_COMMITTEE_PARAMETERS, + /** + * Update to the {@link com.concordium.sdk.responses.chainparameters.ValidatorScoreParameters}, + * parameters that govern validator suspension. + * This parameter is only applicable from protocol version 8 and onwards. + */ + VALIDATOR_SCORE_PARAMETERS, } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/chainparameters/AuthorizationsV1.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/chainparameters/AuthorizationsV1.java index 71b935159..869c28fdb 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/chainparameters/AuthorizationsV1.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/chainparameters/AuthorizationsV1.java @@ -30,6 +30,12 @@ public class AuthorizationsV1 extends AuthorizationsV0 implements Authorizations */ private final AccessStructure timeParameters; + /** + * Keys allowed to create a protocol level token. + * This is present from protocol version 9. + */ + private final AccessStructure createPltUpdate; + public static AuthorizationsV1 from(com.concordium.grpc.v2.AuthorizationsV1 value) { val v0Authorizations = value.getV0(); val keys = v0Authorizations.getKeysList().stream().map(UpdatePublicKey::getValue).map(ByteString::toByteArray).map(ED25519PublicKey::from).collect(Collectors.toSet()); @@ -50,6 +56,11 @@ public static AuthorizationsV1 from(com.concordium.grpc.v2.AuthorizationsV1 valu .keys(keys) .timeParameters(AccessStructure.from(value.getParameterTime())) .cooldownParameters(AccessStructure.from(value.getParameterCooldown())) + .createPltUpdate( + (value.hasCreatePlt()) + ? AccessStructure.from(value.getCreatePlt()) + : null + ) .build(); } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/RejectReasonNotExistentTokenId.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/RejectReasonNotExistentTokenId.java new file mode 100644 index 000000000..f9c0c53ea --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/RejectReasonNotExistentTokenId.java @@ -0,0 +1,22 @@ +package com.concordium.sdk.responses.transactionstatus; + +import com.concordium.grpc.v2.plt.TokenId; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +/** + * The provided identifier does not match a token currently on chain. + * Introduced in protocol version 9. + */ +@ToString +@Getter +@Builder +public class RejectReasonNotExistentTokenId extends RejectReason { + private final TokenId tokenId; + + @Override + public RejectReasonType getType() { + return RejectReasonType.NOT_EXISTENT_TOKEN_ID; + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/RejectReasonTokenUpdateTransactionFailed.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/RejectReasonTokenUpdateTransactionFailed.java new file mode 100644 index 000000000..3333a6560 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/RejectReasonTokenUpdateTransactionFailed.java @@ -0,0 +1,22 @@ +package com.concordium.sdk.responses.transactionstatus; + +import com.concordium.grpc.v2.plt.TokenModuleRejectReason; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +/** + * The token update transaction failed. + * Introduced in protocol version 9. + */ +@ToString +@Getter +@Builder +public class RejectReasonTokenUpdateTransactionFailed extends RejectReason { + private final TokenModuleRejectReason tokenModuleRejectReason; + + @Override + public RejectReasonType getType() { + return RejectReasonType.TOKEN_UPDATE_TRANSACTION_FAILED; + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/RejectReasonType.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/RejectReasonType.java index 977c6432b..94572a1c9 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/RejectReasonType.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/RejectReasonType.java @@ -58,7 +58,10 @@ public enum RejectReasonType { DELEGATION_TARGET_NOT_A_BAKER, STAKE_OVER_MAXIMUM_THRESHOLD_FOR_POOL, POOL_WOULD_BECOME_OVER_DELEGATED, - POOL_CLOSED; + POOL_CLOSED, + NOT_EXISTENT_TOKEN_ID, + TOKEN_UPDATE_TRANSACTION_FAILED, + ; public static RejectReasonType from(com.concordium.grpc.v2.RejectReason reason) { switch (reason.getReasonCase()) { @@ -170,6 +173,10 @@ public static RejectReasonType from(com.concordium.grpc.v2.RejectReason reason) return POOL_WOULD_BECOME_OVER_DELEGATED; case POOL_CLOSED: return POOL_CLOSED; + case NON_EXISTENT_TOKEN_ID: + return NOT_EXISTENT_TOKEN_ID; + case TOKEN_UPDATE_TRANSACTION_FAILED: + return TOKEN_UPDATE_TRANSACTION_FAILED; case REASON_NOT_SET: throw new IllegalArgumentException("No reject reason present."); } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/TokenUpdateResult.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/TokenUpdateResult.java new file mode 100644 index 000000000..658309ccd --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/TokenUpdateResult.java @@ -0,0 +1,16 @@ +package com.concordium.sdk.responses.transactionstatus; + +import com.concordium.grpc.v2.plt.TokenEffect; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public final class TokenUpdateResult implements TransactionResultEvent { + private final TokenEffect effect; + + @Override + public TransactionResultEventType getType() { + return TransactionResultEventType.TOKEN_UPDATE_EFFECT; + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/TransactionResultEventType.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/TransactionResultEventType.java index 9cc98ad97..a4b644f6a 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/TransactionResultEventType.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/transactionstatus/TransactionResultEventType.java @@ -93,6 +93,8 @@ public enum TransactionResultEventType { BAKER_CONFIGURED, @JsonProperty("DelegationConfigured") DELEGATION_CONFIGURED, + @JsonProperty("TokenUpdateEffect") + TOKEN_UPDATE_EFFECT, // An event that occurs when an account send an // encrypted transfer. // The result of such a transaction is a 'NEW_ENCRYPTED_AMOUNT' event for the diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/serializing/CborMapper.java b/concordium-sdk/src/main/java/com/concordium/sdk/serializing/CborMapper.java new file mode 100644 index 000000000..d310f6d0a --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/serializing/CborMapper.java @@ -0,0 +1,40 @@ +package com.concordium.sdk.serializing; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; +import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import lombok.val; + +import java.io.IOException; + +public class CborMapper { + public static ObjectMapper INSTANCE = new CBORMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + // Needed to deserialize Optional Fields + .registerModule(new Jdk8Module()); + + /** + * Writes given data as a value content. + * Using just writeBytes() for this purpose breaks array/object element counter in the generator. + * This method uses reflection. + */ + public static void writeBytesAsValue(CBORGenerator generator, + byte[] data, + int offset, + int len) throws IOException { + try { + val verifyValueWrite = CBORGenerator.class.getDeclaredMethod("_verifyValueWrite", String.class); + verifyValueWrite.setAccessible(true); + verifyValueWrite.invoke(generator, "write bytes as value"); + verifyValueWrite.setAccessible(false); + } catch (Exception e) { + throw new RuntimeException("Can't verify value write with reflection: " + e.getMessage(), e); + } + + generator.writeBytes(data, offset, len); + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TokenUpdate.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TokenUpdate.java new file mode 100644 index 000000000..edc364c4c --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TokenUpdate.java @@ -0,0 +1,68 @@ +package com.concordium.sdk.transactions; + + +import com.concordium.sdk.serializing.CborMapper; +import com.concordium.sdk.transactions.tokens.TokenOperation; +import com.concordium.sdk.types.UInt32; +import com.concordium.sdk.types.UInt64; +import lombok.*; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * A protocol-level token (PLT) transaction payload + * containing the actual operations. + */ +@ToString +@Builder +@Getter +@EqualsAndHashCode(callSuper = true) +public class TokenUpdate extends Payload { + + /** + * Symbol (ID) of the token to execute operations on. + */ + private final String tokenSymbol; + + /** + * Operations to execute. + */ + @Singular + private final List operations; + + @Override + public TransactionType getTransactionType() { + return TransactionType.TOKEN_UPDATE; + } + + @Override + @SneakyThrows + protected byte[] getRawPayloadBytes() { + val symbolBytes = tokenSymbol.getBytes(StandardCharsets.UTF_8); + val operationsBytes = CborMapper + .INSTANCE + .writeValueAsBytes(operations); + + val buffer = ByteBuffer.allocate( + Byte.BYTES + symbolBytes.length + + UInt32.BYTES + operationsBytes.length + ); + + buffer.put((byte) symbolBytes.length); + buffer.put(symbolBytes); + buffer.putInt(operationsBytes.length); + buffer.put(operationsBytes); + + return buffer.array(); + } + + public UInt64 getOperationsBaseCost() { + var total = UInt64.from(0L); + for (TokenOperation operation : operations) { + total = total.plus(operation.getBaseCost()); + } + return total; + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TokenUpdateTransaction.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TokenUpdateTransaction.java new file mode 100644 index 000000000..fa4ad22a8 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TokenUpdateTransaction.java @@ -0,0 +1,59 @@ +package com.concordium.sdk.transactions; + +import com.concordium.sdk.exceptions.TransactionCreationException; +import com.concordium.sdk.types.AccountAddress; +import com.concordium.sdk.types.Nonce; +import lombok.*; + +/** + * A protocol-level token (PLT) transaction. + * It is used to execute operations on a single PLT, + * such as transferring, minting, etc. + */ +@Getter +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TokenUpdateTransaction extends AccountTransaction { + + private TokenUpdateTransaction( + @NonNull final TokenUpdate payload, + @NonNull final AccountAddress sender, + @NonNull final Nonce nonce, + @NonNull final Expiry expiry, + @NonNull final TransactionSigner signer) { + super( + sender, + nonce, + expiry, + signer, + payload, + TransactionTypeCost.TOKEN_UPDATE_BASE_COST.getValue() + .plus(payload.getOperationsBaseCost()) + ); + } + + /** + * Creates new {@link TokenUpdateTransaction}. + * + * @param payload Payload for this Transaction. + * @param sender Sender ({@link AccountAddress}) of this Transaction. + * @param nonce Nonce {@link Nonce} Of the Sender Account. + * @param expiry {@link Expiry} of this transaction. + * @param signer {@link Signer} of this transaction. + * @return Initialized {@link TokenUpdateTransaction} + * @throws TransactionCreationException On failure to create the Transaction from input params. + * Ex when any of the input param is NULL. + */ + @Builder + public static TokenUpdateTransaction from(final TokenUpdate payload, + final AccountAddress sender, + final Nonce nonce, + final Expiry expiry, + final TransactionSigner signer) { + try { + return new TokenUpdateTransaction(payload, sender, nonce, expiry, signer); + } catch (NullPointerException nullPointerException) { + throw TransactionCreationException.from(nullPointerException); + } + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionFactory.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionFactory.java index 606520ef5..1cfaf7dc8 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionFactory.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionFactory.java @@ -215,4 +215,15 @@ public static ConfigureBakerTransaction.ConfigureBakerTransactionBuilder newUpda public static ConfigureDelegationTransaction.ConfigureDelegationTransactionBuilder newConfigureDelegation() { return ConfigureDelegationTransaction.builder(); } + + /** + * Creates a new {@link TokenUpdateTransaction.TokenUpdateTransactionBuilder} for creating a + * {@link TokenUpdateTransaction} to execute protocol-level token (PLT) operations + * such as transferring, minting, etc. + * + * @return the builder for a {@link TokenUpdateTransaction} + */ + public static TokenUpdateTransaction.TokenUpdateTransactionBuilder newTokenUpdate() { + return TokenUpdateTransaction.builder(); + } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionType.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionType.java index fa8c99e58..446e441a4 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionType.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionType.java @@ -5,7 +5,6 @@ /** * Type of Account Transaction. */ -// Types must match https://github.com/Concordium/concordium-base/blob/main/haskell-src/Concordium/Types/Execution.hs public enum TransactionType { DEPLOY_MODULE((byte) 0), INITIALIZE_SMART_CONTRACT_INSTANCE((byte) 1), @@ -27,7 +26,9 @@ public enum TransactionType { ENCRYPTED_TRANSFER_WITH_MEMO((byte) 23), TRANSFER_WITH_SCHEDULE_AND_MEMO((byte) 24), CONFIGURE_BAKER((byte) 25), - CONFIGURE_DELEGATION((byte) 26); + CONFIGURE_DELEGATION((byte) 26), + TOKEN_UPDATE((byte) 27), + ; /** * Number of Bytes used for Serializing {@link TransactionType}. diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionTypeCost.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionTypeCost.java index 2657af300..79eff7ace 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionTypeCost.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/TransactionTypeCost.java @@ -54,7 +54,14 @@ public enum TransactionTypeCost { /** * Base cost for a basic transfer. */ - TRANSFER_BASE_COST(300); + TRANSFER_BASE_COST(300), + + /** + * Base cost for a token update transaction. + */ + TOKEN_UPDATE_BASE_COST(300), + ; + /** * The cost of the transaction */ diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/CborMemo.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/CborMemo.java new file mode 100644 index 000000000..387d4e7ab --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/CborMemo.java @@ -0,0 +1,57 @@ +package com.concordium.sdk.transactions.tokens; + +import com.concordium.sdk.serializing.CborMapper; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.val; + +import java.io.IOException; + +@Getter +@JsonSerialize(using = CborMemo.CborSerializer.class) +public class CborMemo { + + /** + * Memo content to be CBOR-encoded (Jackson), up to 256 bytes total. + */ + private final byte[] content; + + private CborMemo(byte[] content) { + if (content.length > 256) { + throw new IllegalArgumentException("The content can't exceed 256 bytes"); + } + this.content = content; + } + + /** + * @param content which is already CBOR-encoded, up to 256 bytes. + */ + public static CborMemo from(byte[] content) { + return new CborMemo(content); + } + + /** + * @param content to be CBOR-encoded (Jackson), up to 256 bytes total. + */ + @SneakyThrows + public static CborMemo from(Object content) { + return new CborMemo(CborMapper.INSTANCE.writeValueAsBytes(content)); + } + + static class CborSerializer extends JsonSerializer { + + @Override + public void serialize(CborMemo cborMemo, + JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) throws IOException { + val cborGenerator = (CBORGenerator) jsonGenerator; + cborGenerator.writeTag(24); + cborGenerator.writeObject(cborMemo.content); + } + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TaggedTokenHolderAccount.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TaggedTokenHolderAccount.java new file mode 100644 index 000000000..6231757aa --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TaggedTokenHolderAccount.java @@ -0,0 +1,38 @@ +package com.concordium.sdk.transactions.tokens; + +import com.concordium.sdk.types.AccountAddress; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; +import lombok.Getter; +import lombok.val; + +import java.io.IOException; + +@Getter +@JsonSerialize(using = TaggedTokenHolderAccount.CborSerializer.class) +public class TaggedTokenHolderAccount { + + private final byte[] data; + + public TaggedTokenHolderAccount(AccountAddress accountAddress) { + this.data = accountAddress.getBytes(); + } + + static class CborSerializer extends JsonSerializer { + + @Override + public void serialize(TaggedTokenHolderAccount taggedTokenHolderAccount, + JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) throws IOException { + val cborGenerator = (CBORGenerator) jsonGenerator; + cborGenerator.writeTag(40307); + cborGenerator.writeStartObject(taggedTokenHolderAccount, 1); + cborGenerator.writeFieldId(3); + cborGenerator.writeObject(taggedTokenHolderAccount.data); + cborGenerator.writeEndObject(); + } + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TokenOperation.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TokenOperation.java new file mode 100644 index 000000000..3df799625 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TokenOperation.java @@ -0,0 +1,48 @@ +package com.concordium.sdk.transactions.tokens; + +import com.concordium.sdk.transactions.TokenUpdate; +import com.concordium.sdk.types.UInt64; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; +import lombok.val; + +import java.io.IOException; + +/** + * A protocol-level token (PLT) operation used in {@link TokenUpdate}. + */ +@JsonSerialize(using = TokenOperation.CborSerializer.class) +public interface TokenOperation { + + /** + * @return operation type name, e.g. "transfer", "mint", etc. + */ + String getType(); + + /** + * @return A CBOR-serializable (Jackson) operation body. + */ + Object getBody(); + + /** + * @return the base energy cost of this operation. + */ + UInt64 getBaseCost(); + + class CborSerializer extends JsonSerializer { + + @Override + public void serialize(TokenOperation tokenOperation, + JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) throws IOException { + val cborGenerator = (CBORGenerator) jsonGenerator; + cborGenerator.writeStartObject(tokenOperation, 1); + cborGenerator.writeFieldName(tokenOperation.getType()); + cborGenerator.writeObject(tokenOperation.getBody()); + cborGenerator.writeEndObject(); + } + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TokenOperationAmount.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TokenOperationAmount.java new file mode 100644 index 000000000..fb40e2eed --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TokenOperationAmount.java @@ -0,0 +1,88 @@ +package com.concordium.sdk.transactions.tokens; + +import com.concordium.grpc.v2.plt.TokenAmount; +import com.concordium.sdk.types.UInt64; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; +import lombok.Getter; +import lombok.val; + +import java.io.IOException; +import java.math.BigDecimal; + +/** + * An amount for a protocol-level token (PLT) operation. + * It is very important for decimals to match the actual value of the token. + */ +@Getter +@JsonSerialize(using = TokenOperationAmount.CborSerializer.class) +public class TokenOperationAmount { + + /** + * The integer amount. + * For example, "1.5" for a token with 6 decimals is "1500000". + */ + private final UInt64 value; + + /** + * The number of token decimals. + * It is very important for this value to match the actual value of the token. + */ + private final int decimals; + + public TokenOperationAmount(UInt64 value, + int decimals) { + this.value = value; + + if (decimals < 0) { + throw new IllegalArgumentException("The number of decimals can't be negative"); + } else if (decimals > 255) { + throw new IllegalArgumentException("The number of decimals can't exceed 255"); + } + + this.decimals = decimals; + } + + public TokenOperationAmount(BigDecimal decimalValue, + int decimals) { + this( + new UInt64( + decimalValue + .movePointRight(decimals) + .toBigInteger() + ), + decimals + ); + } + + public TokenOperationAmount(TokenAmount tokenAmount) { + this( + UInt64.from(tokenAmount.getValue()), + tokenAmount.getDecimals() + ); + } + + static class CborSerializer extends JsonSerializer { + + @Override + public void serialize(TokenOperationAmount tokenOperationAmount, + JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) throws IOException { + val cborGenerator = (CBORGenerator) jsonGenerator; + + // Write the amount as CBOR "decfrac" (decimal fraction), + // with the exponent matching the token decimals. + // For token module with 6 decimals, + // "1.5" must be encoded as 4([-6, 1500000]) and not as 4([-1, 15]) + // even though the latter is shorter. + cborGenerator.writeTag(4); + cborGenerator.writeObject(new Object[]{ + -tokenOperationAmount.decimals, + tokenOperationAmount.value + }); + } + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TransferTokenOperation.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TransferTokenOperation.java new file mode 100644 index 000000000..f9ee58ba5 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TransferTokenOperation.java @@ -0,0 +1,54 @@ +package com.concordium.sdk.transactions.tokens; + +import com.concordium.sdk.types.UInt64; +import lombok.Builder; +import lombok.Getter; +import lombok.val; + +import java.util.HashMap; +import java.util.Optional; + +@Getter +@Builder +public class TransferTokenOperation implements TokenOperation { + + /** + * Amount to be transferred. + * It very important that the decimals in it match the actual value of the token. + */ + private final TokenOperationAmount amount; + + /** + * Recipient of the transfer. + */ + private final TaggedTokenHolderAccount recipient; + + /** + * Optional memo (message) to be included to the transfer, + * which will be publicly available on the blockchain. + */ + private final CborMemo memo; + + public Optional getMemo() { + return Optional.ofNullable(memo); + } + + @Override + public String getType() { + return "transfer"; + } + + @Override + public UInt64 getBaseCost() { + return UInt64.from(100); + } + + @Override + public Object getBody() { + val body = new HashMap(); + body.put("amount", amount); + body.put("recipient", recipient); + body.put("memo", memo); + return body; + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/types/UInt32.java b/concordium-sdk/src/main/java/com/concordium/sdk/types/UInt32.java index e464f7a4b..95ba3252a 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/types/UInt32.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/types/UInt32.java @@ -1,10 +1,11 @@ package com.concordium.sdk.types; +import com.concordium.sdk.serializing.CborMapper; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.val; @@ -66,25 +67,34 @@ public int compareTo(UInt32 other) { return Integer.compareUnsigned(this.value, other.value); } + public UInt32 plus(UInt32 other) { + return UInt32.from(this.value + other.value); + } + /** * A custom Jackson serializer is provided that ensures that the unsigned value * is the one used when serializing to JSON. */ - static class UInt32Serializer extends StdSerializer { + static class UInt32Serializer extends JsonSerializer { - public UInt32Serializer() { - this(null); - } + @Override + public void serialize(UInt32 uint, + JsonGenerator generator, + SerializerProvider provider) throws IOException { + if (generator instanceof CBORGenerator) { + val cborGenerator = (CBORGenerator) generator; - public UInt32Serializer(Class t) { - super(t); - } + // Write as unsigned 4-byte integer. + val value = ByteBuffer.allocate(1 + Integer.BYTES) + .put((byte) 0x1A) + .put(uint.getBytes()) + .array(); + CborMapper.writeBytesAsValue(cborGenerator, value, 0, value.length); - @Override - public void serialize( - UInt32 uint, JsonGenerator jgen, SerializerProvider provider) - throws IOException, JsonProcessingException { - jgen.writeRawValue(Integer.toUnsignedString(uint.getValue())); + return; + } + + generator.writeRawValue(Integer.toUnsignedString(uint.getValue())); } } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/types/UInt64.java b/concordium-sdk/src/main/java/com/concordium/sdk/types/UInt64.java index 96ef3da90..6997e1168 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/types/UInt64.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/types/UInt64.java @@ -1,12 +1,12 @@ package com.concordium.sdk.types; +import com.concordium.sdk.serializing.CborMapper; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - +import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.val; @@ -74,25 +74,35 @@ public int compareTo(UInt64 other) { return Long.compareUnsigned(this.value, other.value); } + public UInt64 plus(UInt64 other) { + return UInt64.from(this.value + other.value); + } + /** * A custom Jackson serializer is provided that ensures that the unsigned value * is the one used when serializing to JSON. */ - static class UInt64Serializer extends StdSerializer { + static class UInt64Serializer extends JsonSerializer { - public UInt64Serializer() { - this(null); - } + @Override + public void serialize(UInt64 uint, + JsonGenerator generator, + SerializerProvider provider) throws IOException { - public UInt64Serializer(Class t) { - super(t); - } + if (generator instanceof CBORGenerator) { + val cborGenerator = (CBORGenerator) generator; - @Override - public void serialize( - UInt64 uint, JsonGenerator jgen, SerializerProvider provider) - throws IOException, JsonProcessingException { - jgen.writeRawValue(Long.toUnsignedString(uint.getValue())); + // Write as unsigned 8-byte integer. + val value = ByteBuffer.allocate(1 + Long.BYTES) + .put((byte) 0x1B) + .put(uint.getBytes()) + .array(); + CborMapper.writeBytesAsValue(cborGenerator, value, 0, value.length); + + return; + } + + generator.writeRawValue(Long.toUnsignedString(uint.getValue())); } } } diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/tokens/CborMemoTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/tokens/CborMemoTest.java new file mode 100644 index 000000000..c38875a35 --- /dev/null +++ b/concordium-sdk/src/test/java/com/concordium/sdk/tokens/CborMemoTest.java @@ -0,0 +1,68 @@ +package com.concordium.sdk.tokens; + +import com.concordium.sdk.serializing.CborMapper; +import com.concordium.sdk.transactions.tokens.CborMemo; +import lombok.SneakyThrows; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Assert; +import org.junit.Test; + +import java.security.SecureRandom; + +public class CborMemoTest { + + @SneakyThrows + @Test + public void testCborMemoStringSerialization() { + Assert.assertEquals( + "d8184c6b48656c6c6f20776f726c64", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + CborMemo.from("Hello world") + ) + ) + ); + Assert.assertEquals( + "d818584f784dd09dd0b5d0bfd0bbd0bed185d0be20d181d180d0b0d0b1d0bed182d0b0d0bdd0be2c20d0bcd0b8d181d182d0b5d18020d0a0d0b0d0b72dd094d0b2d0b020f09f91a9f09f8fbbe2808df09f94ac", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + CborMemo.from("Неплохо сработано, мистер Раз-Два 👩🏻‍🔬") + ) + ) + ); + } + + @SneakyThrows + @Test + public void testCborMemoRawSerialization() { + Assert.assertEquals( + "d81843010203", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + CborMemo.from(new byte[]{1, 2, 3}) + ) + ) + ); + } + + @SneakyThrows + @Test + public void testCborMemoNullSerialization() { + Assert.assertEquals( + "d81841f6", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + CborMemo.from((Object) null) + ) + ) + ); + } + + @SneakyThrows + @Test( + expected = IllegalArgumentException.class + ) + public void testCborMemoTooBig() { + CborMemo.from(SecureRandom.getSeed(257)); + } +} diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/tokens/TaggedTokenHolderAccountTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/tokens/TaggedTokenHolderAccountTest.java new file mode 100644 index 000000000..8f9a410b6 --- /dev/null +++ b/concordium-sdk/src/test/java/com/concordium/sdk/tokens/TaggedTokenHolderAccountTest.java @@ -0,0 +1,29 @@ +package com.concordium.sdk.tokens; + +import com.concordium.sdk.serializing.CborMapper; +import com.concordium.sdk.transactions.tokens.TaggedTokenHolderAccount; +import com.concordium.sdk.types.AccountAddress; +import lombok.SneakyThrows; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Assert; +import org.junit.Test; + +public class TaggedTokenHolderAccountTest { + + @Test + @SneakyThrows + public void testTaggedTokenHolderAccountSerialization() { + Assert.assertEquals( + "d99d73a10358201515151515151515151515151515151515151515151515151515151515151515", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + new TaggedTokenHolderAccount( + AccountAddress.from( + Hex.decode("1515151515151515151515151515151515151515151515151515151515151515") + ) + ) + ) + ) + ); + } +} diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/tokens/TokenOperationAmountTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/tokens/TokenOperationAmountTest.java new file mode 100644 index 000000000..21c0e75f1 --- /dev/null +++ b/concordium-sdk/src/test/java/com/concordium/sdk/tokens/TokenOperationAmountTest.java @@ -0,0 +1,72 @@ +package com.concordium.sdk.tokens; + +import com.concordium.sdk.serializing.CborMapper; +import com.concordium.sdk.transactions.tokens.TokenOperationAmount; +import com.concordium.sdk.types.UInt64; +import lombok.SneakyThrows; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Assert; +import org.junit.Test; + +public class TokenOperationAmountTest { + + @Test + @SneakyThrows + public void testTokenOperationAmountSerialization() { + Assert.assertEquals( + "c482251b000000000016e360", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + new TokenOperationAmount( + UInt64.from("1500000"), + 6 + ) + ) + ) + ); + Assert.assertEquals( + "c482221b000000000012d687", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + new TokenOperationAmount( + UInt64.from("1234567"), + 3 + ) + ) + ) + ); + Assert.assertEquals( + "c482221b8000000000000000", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + new TokenOperationAmount( + UInt64.from(Long.MIN_VALUE), + 3 + ) + ) + ) + ); + Assert.assertEquals( + "c482221b7fffffffffffffff", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + new TokenOperationAmount( + UInt64.from(Long.MAX_VALUE), + 3 + ) + ) + ) + ); + Assert.assertEquals( + "c482221bffffffffffffffff", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + new TokenOperationAmount( + UInt64.from(-1), + 3 + ) + ) + ) + ); + } +} diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/tokens/TransferTokenOperationTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/tokens/TransferTokenOperationTest.java new file mode 100644 index 000000000..dfb35e3ab --- /dev/null +++ b/concordium-sdk/src/test/java/com/concordium/sdk/tokens/TransferTokenOperationTest.java @@ -0,0 +1,78 @@ +package com.concordium.sdk.tokens; + +import com.concordium.sdk.serializing.CborMapper; +import com.concordium.sdk.transactions.tokens.CborMemo; +import com.concordium.sdk.transactions.tokens.TaggedTokenHolderAccount; +import com.concordium.sdk.transactions.tokens.TokenOperationAmount; +import com.concordium.sdk.transactions.tokens.TransferTokenOperation; +import com.concordium.sdk.types.AccountAddress; +import com.concordium.sdk.types.UInt64; +import lombok.SneakyThrows; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Assert; +import org.junit.Test; + +import java.math.BigDecimal; + +public class TransferTokenOperationTest { + + @Test + @SneakyThrows + public void testTokenTransferOperationSerialization() { + Assert.assertEquals( + "a1687472616e73666572bf66616d6f756e74c482251b000000000016e36069726563697069656e74d99d73a103582021bc8745c81c07ca7f3fb79a8bd161624cb1d5da788baec13f5a5d9eac3a29b7646d656d6fd81848674d79206d656d6fff", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + TransferTokenOperation + .builder() + .amount(new TokenOperationAmount(UInt64.from("1500000"), 6)) + .memo(CborMemo.from("My memo")) + .recipient(new TaggedTokenHolderAccount( + AccountAddress.from( + "3CbvrNVpcHpL7tyT2mhXxQwNWHiPNYEJRgp3CMgEcMyXivms6B" + ) + )) + .build() + ) + ) + ); + } + + @Test + @SneakyThrows + public void testTokenTransferOperationWithoutMemoSerialization() { + Assert.assertEquals( + "a1687472616e73666572bf66616d6f756e74c482251b000000000016e36069726563697069656e74d99d73a103582021bc8745c81c07ca7f3fb79a8bd161624cb1d5da788baec13f5a5d9eac3a29b7ff", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + TransferTokenOperation + .builder() + .amount(new TokenOperationAmount(new BigDecimal("1.5"), 6)) + .recipient(new TaggedTokenHolderAccount( + AccountAddress.from( + "3CbvrNVpcHpL7tyT2mhXxQwNWHiPNYEJRgp3CMgEcMyXivms6B" + ) + )) + .build() + ) + ) + ); + + Assert.assertEquals( + "a1687472616e73666572bf66616d6f756e74c482231b000000000000007b69726563697069656e74d99d73a10358201515151515151515151515151515151515151515151515151515151515151515ff", + Hex.toHexString( + CborMapper.INSTANCE.writeValueAsBytes( + TransferTokenOperation + .builder() + .amount(new TokenOperationAmount(UInt64.from("123"), 4)) + .recipient(new TaggedTokenHolderAccount( + AccountAddress.from( + Hex.decode("1515151515151515151515151515151515151515151515151515151515151515") + ) + )) + .build() + ) + ) + ); + } +} diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/transactions/TokenUpdateTransactionTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/transactions/TokenUpdateTransactionTest.java new file mode 100644 index 000000000..4eb66f8d1 --- /dev/null +++ b/concordium-sdk/src/test/java/com/concordium/sdk/transactions/TokenUpdateTransactionTest.java @@ -0,0 +1,138 @@ +package com.concordium.sdk.transactions; + +import com.concordium.grpc.v2.plt.TokenId; +import com.concordium.sdk.transactions.tokens.CborMemo; +import com.concordium.sdk.transactions.tokens.TaggedTokenHolderAccount; +import com.concordium.sdk.transactions.tokens.TokenOperationAmount; +import com.concordium.sdk.transactions.tokens.TransferTokenOperation; +import com.concordium.sdk.types.AccountAddress; +import com.concordium.sdk.types.Nonce; +import com.concordium.sdk.types.UInt64; +import lombok.SneakyThrows; +import org.junit.Assert; +import org.junit.Test; + +import java.math.BigDecimal; + +public class TokenUpdateTransactionTest { + + @Test + @SneakyThrows + public void testTokenUpdateTransferTransactionWithTextMemo() { + Assert.assertEquals( + "24303123d37c83a071bf861bb5d4490f945e4253234890062d851fd75c4bf25c", + TransactionFactory + .newTokenUpdate() + .sender(AccountAddress.from("3JwD2Wm3nMbsowCwb1iGEpnt47UQgdrtnq2qT6opJc3z2AgCrc")) + .payload( + TokenUpdate + .builder() + .tokenSymbol("TEST") + .operation( + TransferTokenOperation + .builder() + .amount( + new TokenOperationAmount( + new BigDecimal("1.5"), + 2 + ) + ) + .recipient( + new TaggedTokenHolderAccount( + AccountAddress.from("3hYXYEPuGyhFcVRhSk2cVgKBhzVcAryjPskYk4SecpwGnoHhuM") + ) + ) + .memo( + CborMemo.from("Memo for the test") + ) + .build() + ) + .build() + ) + .nonce(Nonce.from(78910)) + .expiry(Expiry.from(123456)) + .signer(TransactionTestHelper.getValidSigner()) + .build() + .getHash() + .asHex() + ); + } + + @Test + @SneakyThrows + public void testTokenUpdateTransferTransactionWithoutMemo() { + Assert.assertEquals( + "76d7e2c3f3e2046eb145783f7bda8c26c216e2f2db3a195f41ffb7640981077a", + TransactionFactory + .newTokenUpdate() + .sender(AccountAddress.from("3JwD2Wm3nMbsowCwb1iGEpnt47UQgdrtnq2qT6opJc3z2AgCrc")) + .payload( + TokenUpdate + .builder() + .tokenSymbol("TEST") + .operation( + TransferTokenOperation + .builder() + .amount( + new TokenOperationAmount( + new BigDecimal("1.5"), + 2 + ) + ) + .recipient( + new TaggedTokenHolderAccount( + AccountAddress.from("3hYXYEPuGyhFcVRhSk2cVgKBhzVcAryjPskYk4SecpwGnoHhuM") + ) + ) + .build() + ) + .build() + ) + .nonce(Nonce.from(78910)) + .expiry(Expiry.from(123456)) + .signer(TransactionTestHelper.getValidSigner()) + .build() + .getHash() + .asHex() + ); + } + + @Test + @SneakyThrows + public void testTokenUpdateTransferTransactionCostWithoutMemo() { + Assert.assertEquals( + UInt64.from("751"), + TransactionFactory + .newTokenUpdate() + .sender(AccountAddress.from("3JwD2Wm3nMbsowCwb1iGEpnt47UQgdrtnq2qT6opJc3z2AgCrc")) + .payload( + TokenUpdate + .builder() + .tokenSymbol("TEST") + .operation( + TransferTokenOperation + .builder() + .amount( + new TokenOperationAmount( + new BigDecimal("1.5"), + 2 + ) + ) + .recipient( + new TaggedTokenHolderAccount( + AccountAddress.from("3hYXYEPuGyhFcVRhSk2cVgKBhzVcAryjPskYk4SecpwGnoHhuM") + ) + ) + .build() + ) + .build() + ) + .nonce(Nonce.from(78910)) + .expiry(Expiry.from(123456)) + .signer(TransactionTestHelper.getValidSigner()) + .build() + .getHeader() + .getMaxEnergyCost() + ); + } +} diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/types/UInt32Test.java b/concordium-sdk/src/test/java/com/concordium/sdk/types/UInt32Test.java index 54e40e3bf..e22e51866 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/types/UInt32Test.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/types/UInt32Test.java @@ -31,12 +31,30 @@ public void testComparable() { assertEquals(-1, UInt32.from("4294967294").compareTo(UInt32.from("4294967295"))); } + @Test + public void testAdding() { + assertEquals( + 5, + UInt32.from(3).plus(UInt32.from(2)).getValue() + ); + // UInt32 max + assertEquals( + -1, + UInt32.from(Integer.MIN_VALUE).plus(UInt32.from(Integer.MAX_VALUE)).getValue() + ); + // Overflow + assertEquals( + 0, + UInt32.from(Integer.MIN_VALUE).plus(UInt32.from(Integer.MIN_VALUE)).getValue() + ); + } + @Test public void testSerializeDeserialize() { - val expected = UInt64.from("123242112"); + val expected = UInt32.from("123242112"); val bytes = expected.getBytes(); - val deserialized = UInt64.from(bytes); + val deserialized = UInt32.from(bytes); assertEquals(expected, deserialized); - assertNotEquals(expected, UInt64.from("42")); + assertNotEquals(expected, UInt32.from("42")); } } diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/types/UInt64Test.java b/concordium-sdk/src/test/java/com/concordium/sdk/types/UInt64Test.java index fdc4a3462..480a5f283 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/types/UInt64Test.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/types/UInt64Test.java @@ -34,4 +34,21 @@ public void testSerializeDeserialize() { assertEquals(expected, deserialized); } + @Test + public void testAdding() { + assertEquals( + 5, + UInt64.from(3).plus(UInt64.from(2)).getValue() + ); + // UInt64 max + assertEquals( + -1L, + UInt64.from(Long.MIN_VALUE).plus(UInt64.from(Long.MAX_VALUE)).getValue() + ); + // Overflow + assertEquals( + 0, + UInt64.from(Long.MIN_VALUE).plus(UInt64.from(Long.MIN_VALUE)).getValue() + ); + } } diff --git a/crypto-jni/Cargo.lock b/crypto-jni/Cargo.lock index b102d247f..92bf96768 100644 --- a/crypto-jni/Cargo.lock +++ b/crypto-jni/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ahash" @@ -79,7 +79,7 @@ dependencies = [ "ark-std", "derivative", "hashbrown 0.13.2", - "itertools", + "itertools 0.10.5", "num-traits", "zeroize", ] @@ -96,7 +96,7 @@ dependencies = [ "ark-std", "derivative", "digest", - "itertools", + "itertools 0.10.5", "num-bigint", "num-traits", "paste", @@ -232,47 +232,25 @@ dependencies = [ [[package]] name = "borsh" -version = "0.9.3" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" dependencies = [ "borsh-derive", - "hashbrown 0.11.2", + "cfg_aliases", ] [[package]] name = "borsh-derive" -version = "0.9.3" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" dependencies = [ - "borsh-derive-internal", - "borsh-schema-derive-internal", + "once_cell", "proc-macro-crate", - "proc-macro2", - "syn 1.0.107", -] - -[[package]] -name = "borsh-derive-internal" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "borsh-schema-derive-internal" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" -dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.104", ] [[package]] @@ -293,19 +271,20 @@ checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "bytecheck" -version = "0.6.9" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" dependencies = [ "bytecheck_derive", "ptr_meta", + "simdutf8", ] [[package]] name = "bytecheck_derive" -version = "0.6.9" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" dependencies = [ "proc-macro2", "quote", @@ -320,9 +299,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" @@ -342,6 +321,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.35" @@ -357,6 +342,22 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -394,7 +395,7 @@ dependencies = [ "rust_decimal", "serde", "serde_json", - "thiserror", + "thiserror 1.0.38", ] [[package]] @@ -403,12 +404,12 @@ version = "4.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.104", ] [[package]] name = "concordium_base" -version = "6.0.0" +version = "8.0.0-alpha.2" dependencies = [ "anyhow", "ark-bls12-381", @@ -419,6 +420,8 @@ dependencies = [ "bs58", "byteorder", "chrono", + "ciborium-io", + "ciborium-ll", "concordium-contracts-common", "concordium_base_derive", "curve25519-dalek", @@ -427,7 +430,7 @@ dependencies = [ "either", "ff 0.13.0", "hex", - "itertools", + "itertools 0.14.0", "leb128", "libc", "nom", @@ -443,17 +446,20 @@ dependencies = [ "sha2", "sha3", "subtle", - "thiserror", + "thiserror 2.0.12", "zeroize", ] [[package]] name = "concordium_base_derive" -version = "1.0.0" +version = "1.1.0-alpha.2" dependencies = [ + "convert_case 0.8.0", + "darling", + "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.104", ] [[package]] @@ -468,6 +474,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -526,6 +541,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.6" @@ -599,7 +620,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.104", ] [[package]] @@ -648,9 +669,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -658,27 +679,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.32", + "syn 2.0.104", ] [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.32", + "syn 2.0.104", ] [[package]] @@ -717,7 +738,7 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case", + "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", @@ -789,7 +810,7 @@ dependencies = [ "hmac", "regex", "sha2", - "thiserror", + "thiserror 1.0.38", ] [[package]] @@ -881,14 +902,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash 0.7.6", -] [[package]] name = "hashbrown" @@ -910,9 +938,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "hermit-abi" @@ -996,12 +1024,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.15.4", "serde", ] @@ -1025,6 +1053,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.5" @@ -1041,7 +1078,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.38", "walkdir", ] @@ -1071,7 +1108,7 @@ dependencies = [ [[package]] name = "key_derivation" -version = "2.1.0" +version = "2.1.1" dependencies = [ "concordium_base", "ed25519-dalek", @@ -1081,7 +1118,7 @@ dependencies = [ "pbkdf2", "serde", "sha2", - "thiserror", + "thiserror 1.0.38", ] [[package]] @@ -1267,9 +1304,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "password-hash" -version = "0.3.2" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", "rand_core 0.6.4", @@ -1284,9 +1321,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest", "hmac", @@ -1324,7 +1361,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.104", ] [[package]] @@ -1374,18 +1411,18 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "0.1.5" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -1412,9 +1449,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -1500,7 +1537,7 @@ checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.38", ] [[package]] @@ -1534,32 +1571,36 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rend" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" dependencies = [ "bytecheck", ] [[package]] name = "rkyv" -version = "0.7.39" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" dependencies = [ + "bitvec", "bytecheck", + "bytes", "hashbrown 0.12.3", "ptr_meta", "rend", "rkyv_derive", "seahash", + "tinyvec", + "uuid", ] [[package]] name = "rkyv_derive" -version = "0.7.39" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" dependencies = [ "proc-macro2", "quote", @@ -1568,14 +1609,12 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.28.0" +version = "1.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe32e8c89834541077a5c5bbe5691aa69324361e27e6aeb3552a737db4a70c8" +checksum = "b203a6425500a03e0919c42d3c47caca51e79f1132046626d2c8871c5092035d" dependencies = [ "arrayvec", "borsh", - "bytecheck", - "byteorder", "bytes", "num-traits", "rand", @@ -1667,7 +1706,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.104", ] [[package]] @@ -1691,7 +1730,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.0.0", + "indexmap 2.10.0", "serde", "serde_json", "serde_with_macros", @@ -1707,7 +1746,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.104", ] [[package]] @@ -1740,6 +1779,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "siphasher" version = "0.3.11" @@ -1758,9 +1803,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -1781,9 +1826,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -1834,7 +1879,16 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.38", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -1848,6 +1902,17 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "time" version = "0.3.28" @@ -1892,12 +1957,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] -name = "toml" -version = "0.5.11" +name = "toml_datetime" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "serde", + "indexmap 2.10.0", + "toml_datetime", + "winnow", ] [[package]] @@ -1912,6 +1985,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.1.10" @@ -1924,6 +2003,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" + [[package]] name = "version_check" version = "0.9.4" @@ -1955,7 +2040,7 @@ dependencies = [ "rust_iso3166", "serde", "serde_json", - "thiserror", + "thiserror 1.0.38", ] [[package]] @@ -2115,6 +2200,15 @@ version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" +[[package]] +name = "winnow" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +dependencies = [ + "memchr", +] + [[package]] name = "wyz" version = "0.5.1" @@ -2141,7 +2235,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.104", ] [[package]] diff --git a/pom.xml b/pom.xml index c39696da8..a6bcb38a4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.concordium.sdk concordium-sdk-base - 11.0.0-SNAPSHOT + 11.0.0 pom https://github.com/Concordium/concordium-java-sdk @@ -35,6 +35,109 @@ concordium-sdk concordium-android-sdk - concordium-sdk-examples + + + + + org.bitcoinj + bitcoinj-core + 0.16.2 + + + com.google.protobuf + protobuf-javalite + + + + + io.grpc + grpc-netty-shaded + 1.73.0 + + + io.grpc + grpc-okhttp + 1.73.0 + + + io.grpc + grpc-protobuf + 1.73.0 + + + io.grpc + grpc-testing + 1.73.0 + test + + + io.grpc + grpc-stub + 1.73.0 + + + javax.annotation + javax.annotation-api + 1.2 + + + junit + junit + 4.12 + test + + + org.projectlombok + lombok + 1.18.26 + + + com.fasterxml.jackson.core + jackson-core + 2.19.1 + + + com.fasterxml.jackson.core + jackson-databind + 2.19.1 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-cbor + 2.19.1 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.19.1 + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + 2.19.1 + + + commons-codec + commons-codec + 1.18.0 + + + commons-io + commons-io + 2.19.0 + + + org.mockito + mockito-core + 3.3.3 + test + + + org.apache.commons + commons-lang3 + 3.17.0 + + +