From 85e7365ca164aff42ffc15d65313f06192eb6fc5 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 5 Feb 2024 16:27:12 +0100 Subject: [PATCH 01/21] Draft of memo transfer --- .../sdk/examples/SendTransferWithMemo.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTransferWithMemo.java diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTransferWithMemo.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTransferWithMemo.java new file mode 100644 index 000000000..789852621 --- /dev/null +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTransferWithMemo.java @@ -0,0 +1,80 @@ +package com.concordium.sdk.examples; + +import com.concordium.sdk.ClientV2; +import com.concordium.sdk.Connection; +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.types.AccountAddress; +import lombok.var; +import picocli.CommandLine; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Optional; +import java.util.concurrent.Callable; + +@CommandLine.Command(name = "SendTransferWithMemo", mixinStandardHelpOptions = true) +public class SendTransferWithMemo implements Callable { + + @CommandLine.Option( + names = {"--endpoint"}, + description = "GRPC interface of the node.", + defaultValue = "http://localhost:20002") + private String endpoint; + + @CommandLine.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()) + .build(); + + AccountAddress sender = AccountAddress.from("3WZE6etUvVp1eyhEtTxqZrQaanTAZnZCHEmZmDyCbCwxnmQuPE"); + AccountAddress receiver = AccountAddress.from("4Lpoq7uTZJLLotMdSVHzqZFcKwS9SfVp9hZkZiV9QBRiAarEE3"); + CCDAmount amount = CCDAmount.fromMicro(1000_000); + Expiry expiry = Expiry.createNew().addMinutes(5); + + + TransactionSigner signer = TransactionSigner.from( + SignerEntry.from(Index.from(0), Index.from(0), + ED25519SecretKey + .from("56f60de843790c308dac2d59a5eec9f6b1649513f827e5a13d7038accfe31784"))); + + var client = ClientV2.from(connection); + var senderInfo = client.getAccountInfo(BlockQuery.BEST, AccountQuery.from(sender)); + var nonce = senderInfo.getAccountNonce(); + var memo = Memo.from(new byte[]{0, 1, 2, 3}); + var txnHash = client.sendTransaction(TransactionFactory.newTransferWithMemo() + .sender(sender) + .receiver(receiver) + .amount(amount) + .nonce(AccountNonce.from(nonce)) + .expiry(expiry) + .signer(signer) + .memo(memo) + .build()); + + System.out.println("Sending transaction with hash: " + txnHash); + Optional finalizedTransaction = client.waitUntilFinalized(txnHash,100000); + finalizedTransaction.ifPresent(finalizedBlockItem -> System.out.println("Transaction finalized in block with hash: " + finalizedBlockItem.getBlockHash())); + + return 0; + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new SendTransferWithMemo()).execute(args); + System.exit(exitCode); + } +} From b63514ea6ccb2a88ea9215db956fc22199f483ea Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 5 Feb 2024 17:12:34 +0100 Subject: [PATCH 02/21] Alias checker --- .../com/concordium/sdk/examples/Aliases.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java new file mode 100644 index 000000000..dfe59e45b --- /dev/null +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java @@ -0,0 +1,37 @@ +package com.concordium.sdk.examples; + +import com.concordium.sdk.types.AccountAddress; +import picocli.CommandLine; + +import java.util.List; +import java.util.concurrent.Callable; + +/** + * Utility to check whether a list of accounts are all aliases of each other. + */ +@CommandLine.Command(name = "Aliases", mixinStandardHelpOptions = true) +public class Aliases implements Callable { + + @CommandLine.Parameters( + description = "List of addresses to check") + List addresses; + @Override + public Integer call() throws Exception { + String first = addresses.remove(0); + AccountAddress addr = AccountAddress.from(first); + for (String a : addresses) { + + if (!addr.isAliasOf(AccountAddress.from(a))) { + System.out.println(a + " is not an alias of " + first); + return 0; + } + } + System.out.println("All addresses are aliases"); + return 0; + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new Aliases()).execute(args); + System.exit(exitCode); + } +} From f6f8192d3538078cd7afc6e21bdd52c4176b5fe8 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 12 Feb 2024 10:09:23 +0100 Subject: [PATCH 03/21] Alias checker enforce 2 or more parameters --- .../src/main/java/com/concordium/sdk/examples/Aliases.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java index dfe59e45b..9e2111fa4 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java @@ -13,7 +13,8 @@ public class Aliases implements Callable { @CommandLine.Parameters( - description = "List of addresses to check") + description = "List of addresses to check", + arity = "2..." ) List addresses; @Override public Integer call() throws Exception { From d52d9846b2b8f1916f90861cd23311f452994fda Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Wed, 14 Feb 2024 13:54:22 +0100 Subject: [PATCH 04/21] FindAccount skeleton --- .../concordium/sdk/examples/FindAccount.java | 64 +++++++++++++++ .../sdk/examples/SendTransferWithMemo.java | 80 ------------------- 2 files changed, 64 insertions(+), 80 deletions(-) create mode 100644 concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java delete mode 100644 concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTransferWithMemo.java diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java new file mode 100644 index 000000000..f56eb07ed --- /dev/null +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java @@ -0,0 +1,64 @@ +package com.concordium.sdk.examples; + +import com.concordium.sdk.ClientV2; +import com.concordium.sdk.Connection; +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.types.AccountAddress; +import jdk.nashorn.internal.codegen.CompilerConstants; +import lombok.var; +import picocli.CommandLine; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Optional; +import java.util.concurrent.Callable; + +/** + * Find out when a given account was created on the chain. + * That is, the block in which the account creation transaction is committed. + */ +@CommandLine.Command(name = "FindAccount", mixinStandardHelpOptions = true) +public class FindAccount implements Callable { + + @CommandLine.Option( + names = {"--endpoint"}, + description = "GRPC interface of the node.", + defaultValue = "http://localhost:20002") + private String endpoint; + + @CommandLine.Option( + names = {"--timeout"}, + description = "GRPC request timeout in milliseconds.", + defaultValue = "100000") + private int timeout; + + @CommandLine.Option( + names = {"-a ", "--account"}, + description = "Account to look for", + defaultValue = "3WZE6etUvVp1eyhEtTxqZrQaanTAZnZCHEmZmDyCbCwxnmQuPE") + private String account; + + @Override + public Integer call() throws MalformedURLException, ClientInitializationException { + var endpointUrl = new URL(this.endpoint); + Connection connection = Connection.newBuilder() + .host(endpointUrl.getHost()) + .port(endpointUrl.getPort()) + .timeout(timeout) + .build(); + var client = ClientV2.from(connection); + + + return 0; + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new FindAccount()).execute(args); + System.exit(exitCode); + } +} diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTransferWithMemo.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTransferWithMemo.java deleted file mode 100644 index 789852621..000000000 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/SendTransferWithMemo.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.concordium.sdk.examples; - -import com.concordium.sdk.ClientV2; -import com.concordium.sdk.Connection; -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.types.AccountAddress; -import lombok.var; -import picocli.CommandLine; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Optional; -import java.util.concurrent.Callable; - -@CommandLine.Command(name = "SendTransferWithMemo", mixinStandardHelpOptions = true) -public class SendTransferWithMemo implements Callable { - - @CommandLine.Option( - names = {"--endpoint"}, - description = "GRPC interface of the node.", - defaultValue = "http://localhost:20002") - private String endpoint; - - @CommandLine.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()) - .build(); - - AccountAddress sender = AccountAddress.from("3WZE6etUvVp1eyhEtTxqZrQaanTAZnZCHEmZmDyCbCwxnmQuPE"); - AccountAddress receiver = AccountAddress.from("4Lpoq7uTZJLLotMdSVHzqZFcKwS9SfVp9hZkZiV9QBRiAarEE3"); - CCDAmount amount = CCDAmount.fromMicro(1000_000); - Expiry expiry = Expiry.createNew().addMinutes(5); - - - TransactionSigner signer = TransactionSigner.from( - SignerEntry.from(Index.from(0), Index.from(0), - ED25519SecretKey - .from("56f60de843790c308dac2d59a5eec9f6b1649513f827e5a13d7038accfe31784"))); - - var client = ClientV2.from(connection); - var senderInfo = client.getAccountInfo(BlockQuery.BEST, AccountQuery.from(sender)); - var nonce = senderInfo.getAccountNonce(); - var memo = Memo.from(new byte[]{0, 1, 2, 3}); - var txnHash = client.sendTransaction(TransactionFactory.newTransferWithMemo() - .sender(sender) - .receiver(receiver) - .amount(amount) - .nonce(AccountNonce.from(nonce)) - .expiry(expiry) - .signer(signer) - .memo(memo) - .build()); - - System.out.println("Sending transaction with hash: " + txnHash); - Optional finalizedTransaction = client.waitUntilFinalized(txnHash,100000); - finalizedTransaction.ifPresent(finalizedBlockItem -> System.out.println("Transaction finalized in block with hash: " + finalizedBlockItem.getBlockHash())); - - return 0; - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new SendTransferWithMemo()).execute(args); - System.exit(exitCode); - } -} From 21cb00c66aa1b1cbe95b94384df220cccb12fd37 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Wed, 14 Feb 2024 16:07:29 +0100 Subject: [PATCH 05/21] Added @Getter to ElectionInfoBaker --- .../concordium/sdk/responses/election/ElectionInfoBaker.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/election/ElectionInfoBaker.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/election/ElectionInfoBaker.java index 3a836b09d..085604e95 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/election/ElectionInfoBaker.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/election/ElectionInfoBaker.java @@ -4,6 +4,7 @@ import com.concordium.sdk.types.AccountAddress; import lombok.Builder; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.ToString; /** @@ -12,6 +13,7 @@ @Builder @ToString @EqualsAndHashCode +@Getter public class ElectionInfoBaker { /** * The ID of the baker. From 2c069dc00a33f8e0a1b0a6d7cb1e733a1ce582c9 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Wed, 14 Feb 2024 16:58:41 +0100 Subject: [PATCH 06/21] Added @Getter to DelegatorRewardPeriodInfo --- .../com/concordium/sdk/responses/DelegatorRewardPeriodInfo.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/DelegatorRewardPeriodInfo.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/DelegatorRewardPeriodInfo.java index 367a5a515..4ce5cb493 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/DelegatorRewardPeriodInfo.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/DelegatorRewardPeriodInfo.java @@ -4,10 +4,12 @@ import com.concordium.sdk.types.AccountAddress; import lombok.Builder; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.ToString; @Builder @ToString +@Getter @EqualsAndHashCode public class DelegatorRewardPeriodInfo { /** From 6b84f5941dee226c9420630e03f552afa88cfe04 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Wed, 14 Feb 2024 17:33:27 +0100 Subject: [PATCH 07/21] BakerStats example --- .../concordium/sdk/examples/BlockStats.java | 74 ++++++++++++ .../examples/contractexample/BakerStats.java | 109 ++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java create mode 100644 concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/contractexample/BakerStats.java diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java new file mode 100644 index 000000000..404210bab --- /dev/null +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java @@ -0,0 +1,74 @@ +package com.concordium.sdk.examples; + +import com.concordium.sdk.ClientV2; +import com.concordium.sdk.Connection; +import com.concordium.sdk.exceptions.ClientInitializationException; +import com.concordium.sdk.requests.BlockQuery; +import com.concordium.sdk.responses.blocksatheight.BlocksAtHeightRequest; +import lombok.var; +import picocli.CommandLine; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.concurrent.Callable; + +/** + * Traverse blocks in a given span and query statistics. + * For each block print + * - Block hash + * - Block slot time + * - Receive time of the block at the given node + * - Arrive time of the block at the given node + * - Difference between receive and slot times + * - Difference between arrive and slot times + * - Number if events associated with payday + * - Whether the block contains a finalization record + * - The number of transactions included in the block + */ +@CommandLine.Command(name = "BlockStats", mixinStandardHelpOptions = true) +public class BlockStats implements Callable { + + @CommandLine.Option( + names = {"--endpoint"}, + description = "GRPC interface of the node.", + defaultValue = "http://localhost:20002") + private String endpoint; + + @CommandLine.Option( + names = {"--timeout"}, + description = "GRPC request timeout in milliseconds.", + defaultValue = "100000") + private int timeout; + + @CommandLine.Option( + names = {"--from"}, + description = "Starting relative block height", + defaultValue = "0" + ) + private long from; + + @CommandLine.Option( + names = {"--to"}, + description = "End absolute block heigh" + ) + private long to; + + @Override + public Integer call() throws MalformedURLException, ClientInitializationException { + var endpointUrl = new URL(this.endpoint); + Connection connection = Connection.newBuilder() + .host(endpointUrl.getHost()) + .port(endpointUrl.getPort()) + .timeout(timeout) + .build(); + var client = ClientV2.from(connection); + + return 0; + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new FindAccount()).execute(args); + System.exit(exitCode); + } +} + diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/contractexample/BakerStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/contractexample/BakerStats.java new file mode 100644 index 000000000..9e6be3e68 --- /dev/null +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/contractexample/BakerStats.java @@ -0,0 +1,109 @@ +package com.concordium.sdk.examples.contractexample; + +import com.concordium.sdk.ClientV2; +import com.concordium.sdk.Connection; +import com.concordium.sdk.exceptions.ClientInitializationException; +import com.concordium.sdk.requests.BlockQuery; +import com.concordium.sdk.responses.BakerId; +import lombok.val; +import lombok.var; +import picocli.CommandLine; + +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.concurrent.Callable; + +/** + * List + * - all bakers (id, own stake, stake from delegators, list of delegators and their stake) + * - total equity + * - total delegated stake + * - total effective stake + */ +@CommandLine.Command(name = "BakerStats", mixinStandardHelpOptions = true) +public class BakerStats implements Callable { + + @CommandLine.Option( + names = {"--endpoint"}, + description = "GRPC interface of the node.", + defaultValue = "http://localhost:20002") + private String endpoint; + + @CommandLine.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()) + .timeout(timeout) + .build(); + var client = ClientV2.from(connection); + + // BigInteger to avoid overflow + BigInteger equity = BigInteger.ZERO; + BigInteger delegated = BigInteger.ZERO; + BigInteger effective = BigInteger.ZERO; + var activeBakers = 0; + + val block = client.getBlockInfo(BlockQuery.LAST_FINAL).getBlockHash(); + val blockQuery = BlockQuery.HASH(block); + val bakers = client.getBakerList(blockQuery); + + StringBuilder sb = new StringBuilder(); + while (bakers.hasNext()) { + BakerId baker = bakers.next(); + val pool = client.getPoolInfo(blockQuery, baker); + val statusOptional = pool.getCurrentPaydayStatus(); + if (statusOptional.isPresent()) { + val status = statusOptional.get(); + long equityVal = status.getBakerEquityCapital().getValue().getValue(); + long delegatedVal = status.getDelegatedCapital().getValue().getValue(); + long effectiveVal = status.getEffectiveStake().getValue().getValue(); + + activeBakers++; + equity = equity.add(BigInteger.valueOf(equityVal)); + delegated = delegated.add(BigInteger.valueOf(delegatedVal)); + effective = effective.add(BigInteger.valueOf(effectiveVal)); + + // Add info about this specific baker + sb.append("Baker ").append(baker).append(": own stake = ").append(equityVal) + .append(" micro CCD, from delegators = ").append(delegatedVal).append(" micro CCD"); + + val delegators = client.getPoolDelegatorsRewardPeriod(blockQuery, baker); + + // Add info about delegators if present + if (delegators.hasNext()) { + sb.append("\n Delegators: \n"); + delegators.forEachRemaining( + d -> sb.append("\t") + .append("Account: ").append(d.getAccount()) + .append(", stake: ").append(d.getStake()) + .append("\n") + ); + } + + sb.append("\n"); + + } + } + sb.append("\nThere are ").append(activeBakers).append(" active bakers\n") + .append("Total effective stake is ").append(effective).append(" micro CCD\n") + .append("Total equity capital is ").append(equity).append(" micro CCD\n") + .append("Total delegated stake is ").append(delegated).append(" micro CCD\n"); + System.out.println(sb); + + return 0; + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new BakerStats()).execute(args); + System.exit(exitCode); + } +} From 195a8afdea4b20334f37880f8725f982459df954 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 19 Feb 2024 15:22:51 +0100 Subject: [PATCH 08/21] Renamed bakers to validators --- concordium-base | 2 +- .../BakerStats.java => ValidatorStats.java} | 30 +++++++++---------- crypto-jni/Cargo.lock | 5 ++-- 3 files changed, 19 insertions(+), 18 deletions(-) rename concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/{contractexample/BakerStats.java => ValidatorStats.java} (79%) diff --git a/concordium-base b/concordium-base index 8024ceed0..23129b486 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 8024ceed057aeccee59f158a64cd108e35f2a5fc +Subproject commit 23129b48624f8ca9fed57b167c88b31e7b7170d8 diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/contractexample/BakerStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java similarity index 79% rename from concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/contractexample/BakerStats.java rename to concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java index 9e6be3e68..c6365ed55 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/contractexample/BakerStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java @@ -1,4 +1,4 @@ -package com.concordium.sdk.examples.contractexample; +package com.concordium.sdk.examples; import com.concordium.sdk.ClientV2; import com.concordium.sdk.Connection; @@ -16,13 +16,13 @@ /** * List - * - all bakers (id, own stake, stake from delegators, list of delegators and their stake) + * - all validators (id, own stake, stake from delegators, list of delegators and their stake) * - total equity * - total delegated stake * - total effective stake */ -@CommandLine.Command(name = "BakerStats", mixinStandardHelpOptions = true) -public class BakerStats implements Callable { +@CommandLine.Command(name = "ValidatorStats", mixinStandardHelpOptions = true) +public class ValidatorStats implements Callable { @CommandLine.Option( names = {"--endpoint"}, @@ -50,16 +50,16 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio BigInteger equity = BigInteger.ZERO; BigInteger delegated = BigInteger.ZERO; BigInteger effective = BigInteger.ZERO; - var activeBakers = 0; + var activeValidators = 0; val block = client.getBlockInfo(BlockQuery.LAST_FINAL).getBlockHash(); val blockQuery = BlockQuery.HASH(block); - val bakers = client.getBakerList(blockQuery); + val validators = client.getBakerList(blockQuery); StringBuilder sb = new StringBuilder(); - while (bakers.hasNext()) { - BakerId baker = bakers.next(); - val pool = client.getPoolInfo(blockQuery, baker); + while (validators.hasNext()) { + BakerId validator = validators.next(); + val pool = client.getPoolInfo(blockQuery, validator); val statusOptional = pool.getCurrentPaydayStatus(); if (statusOptional.isPresent()) { val status = statusOptional.get(); @@ -67,16 +67,16 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio long delegatedVal = status.getDelegatedCapital().getValue().getValue(); long effectiveVal = status.getEffectiveStake().getValue().getValue(); - activeBakers++; + activeValidators++; equity = equity.add(BigInteger.valueOf(equityVal)); delegated = delegated.add(BigInteger.valueOf(delegatedVal)); effective = effective.add(BigInteger.valueOf(effectiveVal)); - // Add info about this specific baker - sb.append("Baker ").append(baker).append(": own stake = ").append(equityVal) + // Add info about this specific validator + sb.append("Validator ").append(validator).append(": own stake = ").append(equityVal) .append(" micro CCD, from delegators = ").append(delegatedVal).append(" micro CCD"); - val delegators = client.getPoolDelegatorsRewardPeriod(blockQuery, baker); + val delegators = client.getPoolDelegatorsRewardPeriod(blockQuery, validator); // Add info about delegators if present if (delegators.hasNext()) { @@ -93,7 +93,7 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio } } - sb.append("\nThere are ").append(activeBakers).append(" active bakers\n") + sb.append("\nThere are ").append(activeValidators).append(" active validators\n") .append("Total effective stake is ").append(effective).append(" micro CCD\n") .append("Total equity capital is ").append(equity).append(" micro CCD\n") .append("Total delegated stake is ").append(delegated).append(" micro CCD\n"); @@ -103,7 +103,7 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio } public static void main(String[] args) { - int exitCode = new CommandLine(new BakerStats()).execute(args); + int exitCode = new CommandLine(new ValidatorStats()).execute(args); System.exit(exitCode); } } diff --git a/crypto-jni/Cargo.lock b/crypto-jni/Cargo.lock index 46aaed340..87f9a9204 100644 --- a/crypto-jni/Cargo.lock +++ b/crypto-jni/Cargo.lock @@ -368,7 +368,7 @@ dependencies = [ [[package]] name = "concordium-contracts-common" -version = "8.1.1" +version = "9.0.0" dependencies = [ "base64", "bs58", @@ -397,7 +397,7 @@ dependencies = [ [[package]] name = "concordium_base" -version = "3.2.0" +version = "4.0.0" dependencies = [ "anyhow", "ark-bls12-381", @@ -1754,6 +1754,7 @@ version = "0.2.0" dependencies = [ "anyhow", "concordium_base", + "ed25519-dalek", "either", "hex", "key_derivation", From a1b631f03c21ca5f09360beb776deb39ac7d4032 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Wed, 21 Feb 2024 15:37:07 +0100 Subject: [PATCH 09/21] Implemented findAtLowestHeight and findAccountCreation utility functions in ClientV2. Added FindAccount example. --- .../concordium/sdk/examples/BlockStats.java | 20 ++- .../concordium/sdk/examples/FindAccount.java | 24 +++- .../java/com/concordium/sdk/ClientV2.java | 121 +++++++++++++++++- .../sdk/ClientV2MapperExtensions.java | 1 + .../com/concordium/sdk/requests/Range.java | 65 ++++++++++ .../sdk/responses/FindAccountResponse.java | 24 ++++ .../sdk/types/AbsoluteBlockHeight.java | 25 ++++ 7 files changed, 276 insertions(+), 4 deletions(-) create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/requests/Range.java create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/responses/FindAccountResponse.java create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/types/AbsoluteBlockHeight.java diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java index 404210bab..b345c3dee 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java @@ -1,10 +1,12 @@ package com.concordium.sdk.examples; +import com.concordium.grpc.v2.AbsoluteBlockHeight; import com.concordium.sdk.ClientV2; import com.concordium.sdk.Connection; import com.concordium.sdk.exceptions.ClientInitializationException; import com.concordium.sdk.requests.BlockQuery; import com.concordium.sdk.responses.blocksatheight.BlocksAtHeightRequest; +import lombok.val; import lombok.var; import picocli.CommandLine; @@ -63,11 +65,27 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio .build(); var client = ClientV2.from(connection); + var height = client.getConsensusInfo().getLastFinalizedBlockHeight()-10; + var max = client.getConsensusInfo().getLastFinalizedBlockHeight(); + // Traverse entire chain starting from genesis block + while (height <= max){ + val start = BlocksAtHeightRequest.newAbsolute(height); + val query = BlockQuery.HEIGHT(start); + val b = client.getBlockInfo(query); + System.out.println(b); + height++; + if (height == max) { + max = client.getConsensusInfo().getLastFinalizedBlockHeight(); + if (height == max) { + break; + } + } + } return 0; } public static void main(String[] args) { - int exitCode = new CommandLine(new FindAccount()).execute(args); + int exitCode = new CommandLine(new BlockStats()).execute(args); System.exit(exitCode); } } diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java index f56eb07ed..71df5d71a 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java @@ -6,10 +6,13 @@ import com.concordium.sdk.exceptions.ClientInitializationException; import com.concordium.sdk.requests.AccountQuery; import com.concordium.sdk.requests.BlockQuery; +import com.concordium.sdk.responses.FindAccountResponse; import com.concordium.sdk.responses.blockitemstatus.FinalizedBlockItem; +import com.concordium.sdk.responses.blockitemsummary.Type; import com.concordium.sdk.transactions.*; import com.concordium.sdk.types.AccountAddress; import jdk.nashorn.internal.codegen.CompilerConstants; +import lombok.val; import lombok.var; import picocli.CommandLine; @@ -28,7 +31,7 @@ public class FindAccount implements Callable { @CommandLine.Option( names = {"--endpoint"}, description = "GRPC interface of the node.", - defaultValue = "http://localhost:20002") + defaultValue = "http://node.testnet.concordium.com:20000") private String endpoint; @CommandLine.Option( @@ -40,7 +43,7 @@ public class FindAccount implements Callable { @CommandLine.Option( names = {"-a ", "--account"}, description = "Account to look for", - defaultValue = "3WZE6etUvVp1eyhEtTxqZrQaanTAZnZCHEmZmDyCbCwxnmQuPE") + defaultValue = "4AuT5RRmBwcdkLMA6iVjxTDb1FQmxwAh3wHBS22mggWL8xH6s3") private String account; @Override @@ -53,6 +56,23 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio .build(); var client = ClientV2.from(connection); + Optional response = client.findAccountCreation(AccountAddress.from(account)); + if (!response.isPresent()) { + System.out.println("Account not found."); + return 0; + } + System.out.println("Account created in block: " + response.get().getBlockHash()); + val blockInfo = client.getBlockInfo(BlockQuery.HASH(response.get().getBlockHash())); + System.out.println("Timestamp of the block: " + blockInfo.getBlockTime()); + val summaries = client.getBlockTransactionEvents(BlockQuery.HASH(response.get().getBlockHash())); + summaries.forEachRemaining(summary -> { + val details = summary.getDetails(); + if (details.getType() == Type.ACCOUNT_CREATION) { + if (details.getAccountCreationDetails().getAddress().isAliasOf(AccountAddress.from(account))) { + System.out.println("Created by transaction hash: " + summary.getTransactionHash()); + } + } + }); return 0; } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java index e149a4682..f7deca545 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java @@ -2,6 +2,7 @@ import com.concordium.grpc.v2.*; import com.concordium.sdk.exceptions.ClientInitializationException; +import com.concordium.sdk.requests.Range; import com.concordium.sdk.requests.AccountQuery; import com.concordium.sdk.requests.BlockQuery; import com.concordium.sdk.requests.EpochQuery; @@ -19,6 +20,7 @@ import com.concordium.sdk.responses.blockitemstatus.BlockItemStatus; import com.concordium.sdk.responses.blockitemstatus.FinalizedBlockItem; import com.concordium.sdk.responses.blockitemsummary.Summary; +import com.concordium.sdk.responses.blocksatheight.BlocksAtHeightRequest; import com.concordium.sdk.responses.blocksummary.FinalizationData; import com.concordium.sdk.responses.blocksummary.specialoutcomes.SpecialOutcome; import com.concordium.sdk.responses.blocksummary.updates.queues.AnonymityRevokerInfo; @@ -39,6 +41,7 @@ import com.concordium.sdk.transactions.BlockItem; import com.concordium.sdk.transactions.*; import com.concordium.sdk.transactions.smartcontracts.WasmModule; +import com.concordium.sdk.types.AbsoluteBlockHeight; import com.concordium.sdk.types.AccountAddress; import com.concordium.sdk.types.ContractAddress; import com.concordium.sdk.types.Nonce; @@ -55,9 +58,9 @@ import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Iterator; -import java.util.Objects; import java.util.Optional; import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; import static com.concordium.sdk.ClientV2MapperExtensions.to; import static com.concordium.sdk.ClientV2MapperExtensions.toTransactionHash; @@ -965,6 +968,122 @@ private Optional getFinalizedTransaction(Hash transactionHas } } + /** + * Find a finalized block with the lowest height that satisfies the given condition. If a block is not found return {@link Optional#empty()}.

+ * + * The provided `test` method should, given a {@link ClientV2} and a {@link BlockQuery}, + * return {@link Optional#of(T)} if the object is found in the block, and {@link Optional#empty()} otherwise. + * It can also throw exceptions which will terminate the search immediately.

+ * + * The precondition for this method is that the `test` method is monotone, i.e. if a block at height `h` satisfies the test then also a block at height `h+1` does. + * If this precondition does not hold then the return value from this method is unspecified.

+ * + * Note, this searches the entire chain. Use {@link ClientV2#findAtLowestHeight(Range, BiFunction)} to only search a given range. + * @param test {@link BiFunction} satisfying the conditions described above. + * @return {@link Optional#of(T)} if the search was successful, {@link Optional#empty()} otherwise. + * @param The type to be returned if the search is successful. + */ + public Optional findAtLowestHeight(BiFunction> test) { + return findAtLowestHeight(Range.newUnbounded(), test); + } + + /** + * Find a finalized block with the lowest height that satisfies the given condition. If a block is not found return {@link Optional#empty()}.

+ * + * The provided `test` method should, given a {@link ClientV2} and a {@link BlockQuery}, + * return {@link Optional#of(T)} if the object is found in the block, and {@link Optional#empty()} otherwise. + * It can also throw exceptions which will terminate the search immediately.

+ * + * The precondition for this method is that the `test` method is monotone, i.e. if a block at height `h` satisfies the test then also a block at height `h+1` does. + * If this precondition does not hold then the return value from this method is unspecified.

+ * + * The search is limited to at most the given range, the upper bound is always at most the last finalized block at the time of the call. + * If the lower bound is not provided it defaults to 0, if the upper bound is not provided it defaults to the last finalized block at the time of the call. + * @param range {@link Range} optionally specifying upper and lower bounds of the search. + * @param test {@link BiFunction} satisfying the conditions described above. + * @return {@link Optional#of(T)} if the search was successful, {@link Optional#empty()} otherwise. + * @param The type to be returned if the search is successful. + */ + public Optional findAtLowestHeight(Range range, BiFunction> test) { + long start = 0; + if (range.getLowerBound().isPresent()) { + start = range.getLowerBound().get().getHeight().getValue(); + } + + long end = this.getConsensusInfo().getLastFinalizedBlockHeight(); + if (range.getUpperBound().isPresent()) { + end = Math.min(end, range.getUpperBound().get().getHeight().getValue()); + } + + if (end < start) { + throw new IllegalArgumentException("Start height must be before end height"); + } + Optional lastFound = Optional.empty(); + while (start < end) { + long mid = start + (end - start) / 2; + Optional ok = test.apply(this, BlockQuery.HEIGHT(BlocksAtHeightRequest.newAbsolute(mid))); + if (ok.isPresent()) { + end = mid; + lastFound = ok; + } else { + start = mid + 1; + } + } + + return lastFound; + } + + /** + * Find a block in which the {@link AccountAddress} was created, if it exists and is finalized. + * The returned {@link FindAccountResponse}, if present, contains the absolute block height, corresponding {@link Hash} and the {@link AccountInfo} at the end of the block. + * The block is the first block in which the account appears.

+ * + * Note that this is not necessarily the initial state of the account since there can be transactions updating it in the same block that it is created.

+ * + * The search is limited to at most the given range, the upper bound is always at most the last finalized block at the time of the call. + * If the lower bound is not provided it defaults to 0, if the upper bound is not provided it defaults to the last finalized block at the time of the call.

+ * + * If the account is not found, {@link Optional#empty()} is returned. + * @param range {@link Range} optionally specifying upper and lower bounds of the search. + * @param address The {@link AccountAddress} to search for. + * @return {@link Optional} containing {@link FindAccountResponse} if the search was successful, {@link Optional#empty()} otherwise. + */ + public Optional findAccountCreation(Range range, AccountAddress address) { + return this.findAtLowestHeight(range, (client, height) -> { + try { + AccountInfo info = client.getAccountInfo(height, AccountQuery.from(address)); + BlockInfo blockInfo = client.getBlockInfo(height); + FindAccountResponse response = FindAccountResponse.builder() + .absoluteBlockHeight(blockInfo.getBlockHeight()) + .accountInfo(info) + .blockHash(blockInfo.getBlockHash()) + .build(); + return Optional.of(response); + } catch (io.grpc.StatusRuntimeException e) { + if (e.getStatus().getCode().equals(Status.Code.NOT_FOUND)) { + return Optional.empty(); + } + throw e; + } + }); + } + + /** + * Find a block in which the {@link AccountAddress} was created, if it exists and is finalized. + * The returned {@link FindAccountResponse}, if present, contains the absolute block height, corresponding {@link Hash} and the {@link AccountInfo} at the end of the block. + * The block is the first block in which the account appears.

+ * + * Note that this is not necessarily the initial state of the account since there can be transactions updating it in the same block that it is created.

+ * + * If the account is not found, {@link Optional#empty()} is returned.

+ * Note, this searches the entire chain. Use {@link ClientV2#findAtLowestHeight(Range, BiFunction)} to only search a given range. + * @param address The {@link AccountAddress} to search for. + * @return {@link Optional} containing {@link FindAccountResponse} if the search was successful, {@link Optional#empty()} otherwise. + */ + public Optional findAccountCreation(AccountAddress address) { + return findAccountCreation(Range.newUnbounded(), address); + } + /** * Get a {@link QueriesGrpc.QueriesBlockingStub} with a timeout * The timeout is the one specified in via the {@link Connection} object used to 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 2c6c3c9af..d6d439ee0 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2MapperExtensions.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2MapperExtensions.java @@ -21,6 +21,7 @@ import com.concordium.grpc.v2.Policy; import com.concordium.grpc.v2.ProtocolVersion; import com.concordium.grpc.v2.ReleaseSchedule; +import com.concordium.grpc.v2.AbsoluteBlockHeight; import com.concordium.grpc.v2.*; import com.concordium.sdk.crypto.bulletproof.BulletproofGenerators; import com.concordium.sdk.crypto.ed25519.ED25519PublicKey; diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/requests/Range.java b/concordium-sdk/src/main/java/com/concordium/sdk/requests/Range.java new file mode 100644 index 000000000..73e4dfdcb --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/requests/Range.java @@ -0,0 +1,65 @@ +package com.concordium.sdk.requests; + +import lombok.*; + +import java.util.Optional; + +/** + * A range parameter for queries in {@link com.concordium.sdk.ClientV2}. + * @param type of the values in the range. + */ +@EqualsAndHashCode +@Builder(access = AccessLevel.PRIVATE) +@ToString +public class Range { + + /** + * Lower bound of the range. If not present indicates unbounded in the lower direction. + */ + private final T lowerBound; + /** + * Upper bound of the range. If not present indicates unbounded in the upper direction. + */ + private final T upperBound; + + /** + * Creates a new {@link Range} unbounded in either direction. + */ + public static Range newUnbounded() { + return Range.builder().build(); + } + + /** + * Creates a new {@link Range} bounded in the lower direction. + */ + public static Range newLowerBounded(T lowerBound) { + return Range.builder() + .lowerBound(lowerBound).build(); + } + + /** + * Creates a new {@link Range} bounded in the upper direction. + */ + public static Range newUpperBounded(T upperBound) { + return Range.builder().upperBound(upperBound).build(); + } + + /** + * Creates a new {@link Range} bounded in both directions. + */ + public static Range newBounded(T lowerBound, T upperBound) { + return Range.builder() + .lowerBound(lowerBound) + .upperBound(upperBound) + .build(); + } + public Optional getLowerBound() { + return Optional.ofNullable(lowerBound); + } + + public Optional getUpperBound() { + return Optional.ofNullable(upperBound); + } + + +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/FindAccountResponse.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/FindAccountResponse.java new file mode 100644 index 000000000..0c500223c --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/FindAccountResponse.java @@ -0,0 +1,24 @@ +package com.concordium.sdk.responses; + +import com.concordium.sdk.requests.Range; +import com.concordium.sdk.responses.accountinfo.AccountInfo; +import com.concordium.sdk.transactions.Hash; +import com.concordium.sdk.types.AccountAddress; +import com.concordium.sdk.types.UInt64; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** + * Response type for {@link com.concordium.sdk.ClientV2#findAccountCreation(Range, AccountAddress)}. + */ +@Getter +@EqualsAndHashCode +@ToString +@Builder +public class FindAccountResponse { + private final UInt64 absoluteBlockHeight; + private final Hash blockHash; + private final AccountInfo accountInfo; +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/types/AbsoluteBlockHeight.java b/concordium-sdk/src/main/java/com/concordium/sdk/types/AbsoluteBlockHeight.java new file mode 100644 index 000000000..446382c1f --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/types/AbsoluteBlockHeight.java @@ -0,0 +1,25 @@ +package com.concordium.sdk.types; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** + * The absolute height of a block. This is the number of ancestors of a block since the genesis block. In particular, the chain genesis block has absolute height 0. + */ +@ToString +@EqualsAndHashCode +@Getter +@AllArgsConstructor +public class AbsoluteBlockHeight { + private UInt64 height; + + public static AbsoluteBlockHeight from(UInt64 height) { + return new AbsoluteBlockHeight(height); + } + public static AbsoluteBlockHeight from(long height) { + return from(UInt64.from(height)); + } + +} From 133ca1a75ccfe3ca1d8ef48edb99fa342d83c129 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Wed, 21 Feb 2024 15:38:59 +0100 Subject: [PATCH 10/21] Updated CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06e12da95..116a7b646 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## Unreleased changes +- Added method `findAtLowestHeight` for finding the earliest block satisfying some condition. +- Added method `findAccountCreation` for finding the block in which an account was created. - Make the `energy` parameter for invoking an instance `Optional`. - Parse the underlying reject reasons into `AccountTransactionDetails`. - Introduced Cis2Client for interfacing with CIS2 compliant smart contracts. From 992c6aa61cc31e39dbd9226264e40b14c1a0f759 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 26 Feb 2024 15:54:54 +0100 Subject: [PATCH 11/21] Added method getBlocksAtHeight and getFinalizedBlocksFrom Updated CHANGELOG.md --- CHANGELOG.md | 2 + .../java/com/concordium/sdk/ClientV2.java | 42 +++++- .../sdk/ClientV2MapperExtensions.java | 34 ++++- .../responses/FinalizedBlockItemIterator.java | 137 ++++++++++++++++++ 4 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 116a7b646..404d5b937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## Unreleased changes +- Added method `getFinalizedBlocksFrom` for retrieving finalized blocks starting from a given height. +- Added method `getBlocksAtHeight` for retrieving live blocks at a given height. - Added method `findAtLowestHeight` for finding the earliest block satisfying some condition. - Added method `findAccountCreation` for finding the block in which an account was created. - Make the `energy` parameter for invoking an instance `Optional`. diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java index f7deca545..b0b127b9e 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java @@ -51,6 +51,8 @@ import io.grpc.ManagedChannel; import io.grpc.Status; import lombok.val; +import lombok.var; +import org.checkerframework.checker.units.qual.C; import java.io.File; import java.io.IOException; @@ -58,6 +60,7 @@ import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Iterator; +import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; @@ -153,6 +156,20 @@ public Iterator getFinalizedBlocks(int timeoutMillis) { return to(grpcOutput, ClientV2MapperExtensions::to); } + /** + * Gets an {@link Iterator} of Finalized Blocks from the time request is made and onwards. + * This can be used to listen for blocks being Finalized.

+ * + * Note, may block indefinitely. Use {@link ClientV2#getFinalizedBlocks(int)} to specify a timeout. + * @return {@link Iterator} + */ + public Iterator getFinalizedBlocks() { + val grpcOutput = this.blockingStub + .getFinalizedBlocks(Empty.newBuilder().build()); + + return to(grpcOutput, ClientV2MapperExtensions::to); + } + /** * Retrieve the information about the given account in the given block. * @@ -1009,7 +1026,7 @@ public Optional findAtLowestHeight(Range range, BiFu if (range.getLowerBound().isPresent()) { start = range.getLowerBound().get().getHeight().getValue(); } - + // TODO rewrite using BlockQuery and just get abs height from `from` and `to` in input long end = this.getConsensusInfo().getLastFinalizedBlockHeight(); if (range.getUpperBound().isPresent()) { end = Math.min(end, range.getUpperBound().get().getHeight().getValue()); @@ -1084,6 +1101,29 @@ public Optional findAccountCreation(AccountAddress address) return findAccountCreation(Range.newUnbounded(), address); } + /** + * Get a {@link ImmutableList}of live blocks at a given height. + * @param height {@link BlocksAtHeightRequest} with the height to query at. + * @return {@link ImmutableList} of {@link Hash} of live blocks at the specified height. + */ + public ImmutableList getBlocksAtHeight(BlocksAtHeightRequest height) { + val output = this.server().getBlocksAtHeight(to(height)).getBlocksList(); + val list = new ImmutableList.Builder(); + output.forEach(hash -> list.add(to(hash))); + return list.build(); + } + + /** + * Get a {@link FinalizedBlockItemIterator} of {@link BlockIdentifier} of finalized blocks starting from a given height. + * This function starts a {@link Thread} that listens for new finalized blocks. + * This {@link Thread} is killed when the {@link FinalizedBlockItemIterator} dropped with {@link FinalizedBlockItemIterator#drop()} + * @param startHeight {@link AbsoluteBlockHeight} to start at. + * @return {@link FinalizedBlockItemIterator} containing {@link BlockIdentifier}s of finalized blocks. + */ + public FinalizedBlockItemIterator getFinalizedBlocksFrom(AbsoluteBlockHeight startHeight) { + return new FinalizedBlockItemIterator(this, startHeight); + } + /** * Get a {@link QueriesGrpc.QueriesBlockingStub} with a timeout * The timeout is the one specified in via the {@link Connection} object used to 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 d6d439ee0..866727240 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2MapperExtensions.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2MapperExtensions.java @@ -146,6 +146,28 @@ static com.concordium.grpc.v2.BlockHashInput to(final BlockQuery input) { return builder.build(); } + static com.concordium.grpc.v2.BlocksAtHeightRequest to(com.concordium.sdk.responses.blocksatheight.BlocksAtHeightRequest height) { + switch (height.getType()) { + case ABSOLUTE: + return BlocksAtHeightRequest.newBuilder() + .setAbsolute(BlocksAtHeightRequest.Absolute.newBuilder() + .setHeight(AbsoluteBlockHeight.newBuilder().setValue(height.getHeight()).build()) + .build()) + .build(); + case RELATIVE: + return BlocksAtHeightRequest.newBuilder() + .setRelative(BlocksAtHeightRequest.Relative.newBuilder() + .setGenesisIndex(GenesisIndex.newBuilder().setValue(height.getGenesisIndex())) + .setHeight(BlockHeight.newBuilder().setValue(height.getHeight())) + .setRestrict(height.isRestrictedToGenesisIndex()) + .build()) + .build(); + default: + throw new IllegalArgumentException("Invalid type"); + } + + } + static com.concordium.grpc.v2.BlockHash to(final Hash blockHash) { return com.concordium.grpc.v2.BlockHash.newBuilder().setValue(to(blockHash.getBytes())).build(); } @@ -874,13 +896,13 @@ static SendBlockItemRequest to(CredentialDeploymentTransaction credentialDeploym TransactionTime time = to(credentialDeploymentTransaction.getExpiry().getValue()); return SendBlockItemRequest.newBuilder() - .setCredentialDeployment( - CredentialDeployment.newBuilder() - .setMessageExpiry(time) - .setRawPayload(ByteString.copyFrom(credentialDeploymentTransaction.getPayloadBytes())) - .build() + .setCredentialDeployment( + CredentialDeployment.newBuilder() + .setMessageExpiry(time) + .setRawPayload(ByteString.copyFrom(credentialDeploymentTransaction.getPayloadBytes())) + .build() ) - .build(); + .build(); } static AccountTransactionSignature to(TransactionSignature signature) { diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java new file mode 100644 index 000000000..7d5b806c8 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java @@ -0,0 +1,137 @@ +package com.concordium.sdk.responses; + +import com.concordium.sdk.ClientV2; +import com.concordium.sdk.responses.blocksatheight.BlocksAtHeightRequest; +import com.concordium.sdk.types.AbsoluteBlockHeight; +import com.concordium.sdk.types.UInt64; +import lombok.val; +import lombok.var; + +import java.util.Iterator; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * An {@link Iterator} for {@link BlockIdentifier}s of finalized blocks. Contains a background task that + * polls for new finalized blocks indefinitely. This task can be stopped by calling {@link FinalizedBlockItemIterator#drop()}. + */ +public class FinalizedBlockItemIterator implements Iterator { + + private final BlockingQueue queue = new LinkedBlockingQueue<>(20); + private final Thread producer; + public FinalizedBlockItemIterator(ClientV2 client, AbsoluteBlockHeight startHeight) { + producer = new Thread(new Producer(queue, client, startHeight)); + producer.start(); + } + + /** + * Interrupts the underlying task polling for finalized blocks. + */ + public void drop() { + producer.interrupt(); + } + @Override + public boolean hasNext() { + return true; + } + + /** + * Get the {@link BlockIdentifier} of the next finalized block. + * This function blocks until a finalized block becomes available. + * Use {@link FinalizedBlockItemIterator#next(long)} to only wait at most a specified duration. + * @return {@link BlockIdentifier} of the next finalized block. + */ + @Override + public BlockIdentifier next() { + try { + return queue.take(); + } catch (InterruptedException e) { + producer.interrupt(); + throw new RuntimeException(e); + } + } + + /** + * Like {@link FinalizedBlockItemIterator#next()} but wait's at most the specified duration. + * @param timeoutMillis amount of milliseconds to wait at most. + * @return {@link BlockIdentifier} of the next finalized block or null if not available in time. + */ + public BlockIdentifier next(long timeoutMillis) { + try { + return queue.poll(timeoutMillis, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + producer.interrupt(); + throw new RuntimeException(e); + } + } + + /** + * Polls for finalized blocks indefinitely. + */ + private static class Producer implements Runnable { + + private final BlockingQueue queue; + private final ClientV2 client; + private final AbsoluteBlockHeight startHeight; + + public Producer(BlockingQueue queue, ClientV2 client, AbsoluteBlockHeight startHeight) { + this.queue = queue; + this.client = client; + this.startHeight = startHeight; + } + + @Override + public void run() { + produce(startHeight); + } + + private void produce(AbsoluteBlockHeight startHeight) { + var finalHeight = client.getConsensusInfo().getLastFinalizedBlockHeight(); + var height = startHeight.getHeight().getValue(); + while (true) { + if (height > finalHeight) { + finalHeight = client.getConsensusInfo().getLastFinalizedBlockHeight(); + if (height > finalHeight) { + break; + } + } else { + tryPutBlockAtHeight(height); + height = height + 1; + } + } + val blockStream = client.getFinalizedBlocks(); + while (!Thread.currentThread().isInterrupted()) { + if (!blockStream.hasNext()) { + continue; + } + val block = blockStream.next(); + // Recover missed blocks + val blockHeight = block.getBlockHeight().getValue(); + while (height < blockHeight) { + tryPutBlockAtHeight(height); + height = height + 1; + } + tryPut(block); + height = height + 1; + } + } + + private void tryPutBlockAtHeight(long height) { + var blocks = client.getBlocksAtHeight(BlocksAtHeightRequest.newAbsolute(height)); + val blockHash = blocks.get(0); + val info = BlockIdentifier.builder().blockHash(blockHash).blockHeight(UInt64.from(height)).build(); + tryPut(info); + } + + private void tryPut(BlockIdentifier info) { + try { + queue.put(info); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + } +} + From aebe92af32137b4a40530daa7622adc4ff1eae8c Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Wed, 28 Feb 2024 14:41:20 +0100 Subject: [PATCH 12/21] Block Stats example --- .../concordium/sdk/examples/BlockStats.java | 125 ++++++++++++++---- 1 file changed, 96 insertions(+), 29 deletions(-) diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java index b345c3dee..dfc620e66 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java @@ -1,31 +1,39 @@ package com.concordium.sdk.examples; -import com.concordium.grpc.v2.AbsoluteBlockHeight; import com.concordium.sdk.ClientV2; import com.concordium.sdk.Connection; import com.concordium.sdk.exceptions.ClientInitializationException; import com.concordium.sdk.requests.BlockQuery; -import com.concordium.sdk.responses.blocksatheight.BlocksAtHeightRequest; +import com.concordium.sdk.responses.blocksummary.specialoutcomes.PaydayAccountReward; +import com.concordium.sdk.responses.blocksummary.specialoutcomes.PaydayFoundationReward; +import com.concordium.sdk.responses.blocksummary.specialoutcomes.PaydayPoolReward; +import com.concordium.sdk.responses.blocksummary.specialoutcomes.SpecialOutcome; +import com.concordium.sdk.types.AbsoluteBlockHeight; import lombok.val; import lombok.var; import picocli.CommandLine; import java.net.MalformedURLException; import java.net.URL; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.Callable; /** * Traverse blocks in a given span and query statistics. * For each block print - * - Block hash - * - Block slot time - * - Receive time of the block at the given node - * - Arrive time of the block at the given node - * - Difference between receive and slot times - * - Difference between arrive and slot times - * - Number if events associated with payday - * - Whether the block contains a finalization record - * - The number of transactions included in the block + * - Block hash + * - Block slot time + * - Receive time of the block at the given node + * - Arrive time of the block at the given node + * - Difference between receive and slot times + * - Difference between arrive and slot times + * - Number if events associated with payday + * - Whether the block contains a finalization record + * - The number of transactions included in the block */ @CommandLine.Command(name = "BlockStats", mixinStandardHelpOptions = true) public class BlockStats implements Callable { @@ -44,16 +52,15 @@ public class BlockStats implements Callable { @CommandLine.Option( names = {"--from"}, - description = "Starting relative block height", - defaultValue = "0" + description = "Starting time (format 2024-01-22T10:15:30+01:00). Defaults to genesis" ) - private long from; + private Optional fromString; @CommandLine.Option( names = {"--to"}, - description = "End absolute block heigh" + description = "End time (format 2024-01-22T10:15:30+01:00). Defaults to the time the tool has run" ) - private long to; + private Optional toString; @Override public Integer call() throws MalformedURLException, ClientInitializationException { @@ -65,22 +72,82 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio .build(); var client = ClientV2.from(connection); - var height = client.getConsensusInfo().getLastFinalizedBlockHeight()-10; - var max = client.getConsensusInfo().getLastFinalizedBlockHeight(); - // Traverse entire chain starting from genesis block - while (height <= max){ - val start = BlocksAtHeightRequest.newAbsolute(height); - val query = BlockQuery.HEIGHT(start); - val b = client.getBlockInfo(query); - System.out.println(b); - height++; - if (height == max) { - max = client.getConsensusInfo().getLastFinalizedBlockHeight(); - if (height == max) { - break; + AbsoluteBlockHeight start; + if (fromString.isPresent()) { + val from = ZonedDateTime.parse(fromString.get(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); + val b = client.findAtLowestHeight((c, query) -> { + val blockInfo = c.getBlockInfo(query); + val blockTime = blockInfo.getBlockTime().getZonedDateTime(); + if (from.isBefore(blockTime)) { + return Optional.of(blockInfo); + } else { + return Optional.empty(); } + + }); + if (b.isPresent()) { + start = AbsoluteBlockHeight.from(b.get().getBlockHeight()); + } else { + throw new IllegalArgumentException("Last finalized block is not after the requested start time."); } + + } else { + start = AbsoluteBlockHeight.from(0); } + + int blockCount = 0; + int finalizationCount = 0; + val blocks = client.getFinalizedBlocksFrom(start); + ZonedDateTime endTime; + endTime = toString.map(s -> ZonedDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)).orElseGet(ZonedDateTime::now); + String fstring = "%1$-64s | %2$-29s | %3$-29s | %4$-29s | %5$-13s | %6$-12s | %7$-14s | %8$-17s | %9$-17s | %10$-30s | %11$-28s | %12$-8s\n"; + System.out.printf(fstring, + "Block hash", "block time", "block receive time", "block arrive time", "receive delta", "arrive" + + " delta", "#payday events", "finalization data", "transaction count", "round", "epoch", "baker id"); + while (blocks.hasNext()) { + val block = blocks.next(); + BlockQuery blockQuery = BlockQuery.HASH(block.getBlockHash()); + val blockInfo = client.getBlockInfo(blockQuery); + val blockTime = blockInfo.getBlockTime().getZonedDateTime(); + if (endTime.isBefore(blockTime)) { + break; + } + + var paydayBlock = 0; + val events = client.getBlockSpecialEvents(blockQuery); + for (SpecialOutcome event: events) { + if (event.getClass().equals(PaydayFoundationReward.class) || event.getClass().equals(PaydayAccountReward.class) || event.getClass().equals(PaydayPoolReward.class)) { + paydayBlock++; + } + } + val hasFinalizationData = client.getBlockFinalizationSummary(blockQuery).isPresent(); + + System.out.printf(fstring, + blockInfo.getBlockHash(), + blockInfo.getBlockTime(), + blockInfo.getBlockReceiveTime(), + blockInfo.getBlockArriveTime(), + ChronoUnit.MILLIS.between(blockInfo.getBlockTime().getZonedDateTime(), blockInfo.getBlockReceiveTime().getZonedDateTime()), + ChronoUnit.MILLIS.between(blockInfo.getBlockTime().getZonedDateTime(), blockInfo.getBlockArriveTime().getZonedDateTime()), + paydayBlock, + hasFinalizationData, + blockInfo.getTransactionCount(), + blockInfo.getRound().isPresent() ? blockInfo.getRound().toString() : "", + blockInfo.getEpoch().isPresent() ? blockInfo.getEpoch().toString() : "", + Objects.isNull(blockInfo.getBlockBaker()) ? "" : blockInfo.getBlockBaker().toString() + + ); + + if (hasFinalizationData) { + finalizationCount++; + } + blockCount++; + + } + System.out.println("Block count: " + blockCount); + System.out.println("Finalization record count: " + finalizationCount); + + return 0; } From 1e9ea09421af910309b0c2bfb9cac041cfa3e8d9 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 4 Mar 2024 10:22:17 +0100 Subject: [PATCH 13/21] Replaced lombok types with explicit types --- .../concordium/sdk/examples/BlockStats.java | 34 +++++++++++-------- .../java/com/concordium/sdk/ClientV2.java | 3 +- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java index dfc620e66..254b272fe 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java @@ -4,11 +4,15 @@ import com.concordium.sdk.Connection; import com.concordium.sdk.exceptions.ClientInitializationException; import com.concordium.sdk.requests.BlockQuery; +import com.concordium.sdk.responses.BlockIdentifier; +import com.concordium.sdk.responses.FinalizedBlockItemIterator; +import com.concordium.sdk.responses.blockinfo.BlockInfo; import com.concordium.sdk.responses.blocksummary.specialoutcomes.PaydayAccountReward; import com.concordium.sdk.responses.blocksummary.specialoutcomes.PaydayFoundationReward; import com.concordium.sdk.responses.blocksummary.specialoutcomes.PaydayPoolReward; import com.concordium.sdk.responses.blocksummary.specialoutcomes.SpecialOutcome; import com.concordium.sdk.types.AbsoluteBlockHeight; +import com.google.common.collect.ImmutableList; import lombok.val; import lombok.var; import picocli.CommandLine; @@ -74,10 +78,10 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio AbsoluteBlockHeight start; if (fromString.isPresent()) { - val from = ZonedDateTime.parse(fromString.get(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); - val b = client.findAtLowestHeight((c, query) -> { - val blockInfo = c.getBlockInfo(query); - val blockTime = blockInfo.getBlockTime().getZonedDateTime(); + ZonedDateTime from = ZonedDateTime.parse(fromString.get(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); + Optional b = client.findAtLowestHeight((c, query) -> { + BlockInfo blockInfo = c.getBlockInfo(query); + ZonedDateTime blockTime = blockInfo.getBlockTime().getZonedDateTime(); if (from.isBefore(blockTime)) { return Optional.of(blockInfo); } else { @@ -97,32 +101,32 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio int blockCount = 0; int finalizationCount = 0; - val blocks = client.getFinalizedBlocksFrom(start); + FinalizedBlockItemIterator blocks = client.getFinalizedBlocksFrom(start); ZonedDateTime endTime; endTime = toString.map(s -> ZonedDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)).orElseGet(ZonedDateTime::now); - String fstring = "%1$-64s | %2$-29s | %3$-29s | %4$-29s | %5$-13s | %6$-12s | %7$-14s | %8$-17s | %9$-17s | %10$-30s | %11$-28s | %12$-8s\n"; - System.out.printf(fstring, + String format = "%1$-64s | %2$-29s | %3$-29s | %4$-29s | %5$-13s | %6$-12s | %7$-14s | %8$-17s | %9$-17s | %10$-30s | %11$-28s | %12$-8s\n"; + System.out.printf(format, "Block hash", "block time", "block receive time", "block arrive time", "receive delta", "arrive" + " delta", "#payday events", "finalization data", "transaction count", "round", "epoch", "baker id"); while (blocks.hasNext()) { - val block = blocks.next(); + BlockIdentifier block = blocks.next(); BlockQuery blockQuery = BlockQuery.HASH(block.getBlockHash()); - val blockInfo = client.getBlockInfo(blockQuery); - val blockTime = blockInfo.getBlockTime().getZonedDateTime(); + BlockInfo blockInfo = client.getBlockInfo(blockQuery); + ZonedDateTime blockTime = blockInfo.getBlockTime().getZonedDateTime(); if (endTime.isBefore(blockTime)) { break; } - var paydayBlock = 0; - val events = client.getBlockSpecialEvents(blockQuery); + int paydayBlock = 0; + ImmutableList events = client.getBlockSpecialEvents(blockQuery); for (SpecialOutcome event: events) { if (event.getClass().equals(PaydayFoundationReward.class) || event.getClass().equals(PaydayAccountReward.class) || event.getClass().equals(PaydayPoolReward.class)) { paydayBlock++; } } - val hasFinalizationData = client.getBlockFinalizationSummary(blockQuery).isPresent(); + boolean hasFinalizationData = client.getBlockFinalizationSummary(blockQuery).isPresent(); - System.out.printf(fstring, + System.out.printf(format, blockInfo.getBlockHash(), blockInfo.getBlockTime(), blockInfo.getBlockReceiveTime(), @@ -144,10 +148,10 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio blockCount++; } + blocks.drop(); System.out.println("Block count: " + blockCount); System.out.println("Finalization record count: " + finalizationCount); - return 0; } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java index b0b127b9e..2f139e47e 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java @@ -1026,7 +1026,6 @@ public Optional findAtLowestHeight(Range range, BiFu if (range.getLowerBound().isPresent()) { start = range.getLowerBound().get().getHeight().getValue(); } - // TODO rewrite using BlockQuery and just get abs height from `from` and `to` in input long end = this.getConsensusInfo().getLastFinalizedBlockHeight(); if (range.getUpperBound().isPresent()) { end = Math.min(end, range.getUpperBound().get().getHeight().getValue()); @@ -1102,7 +1101,7 @@ public Optional findAccountCreation(AccountAddress address) } /** - * Get a {@link ImmutableList}of live blocks at a given height. + * Get a {@link ImmutableList} of live blocks at a given height. * @param height {@link BlocksAtHeightRequest} with the height to query at. * @return {@link ImmutableList} of {@link Hash} of live blocks at the specified height. */ From 74472e3b3cc5c64f0676a4eabccfb7a7d21920c2 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 4 Mar 2024 10:48:13 +0100 Subject: [PATCH 14/21] Cleanup and final documentation --- .../concordium/sdk/examples/FindAccount.java | 12 ++++++---- .../sdk/examples/ValidatorStats.java | 22 ++++++++++++------- .../java/com/concordium/sdk/ClientV2.java | 2 +- .../responses/FinalizedBlockItemIterator.java | 8 ++++++- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java index 71df5d71a..e9cba3997 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java @@ -7,7 +7,10 @@ import com.concordium.sdk.requests.AccountQuery; import com.concordium.sdk.requests.BlockQuery; import com.concordium.sdk.responses.FindAccountResponse; +import com.concordium.sdk.responses.blockinfo.BlockInfo; import com.concordium.sdk.responses.blockitemstatus.FinalizedBlockItem; +import com.concordium.sdk.responses.blockitemsummary.Details; +import com.concordium.sdk.responses.blockitemsummary.Summary; import com.concordium.sdk.responses.blockitemsummary.Type; import com.concordium.sdk.transactions.*; import com.concordium.sdk.types.AccountAddress; @@ -18,6 +21,7 @@ import java.net.MalformedURLException; import java.net.URL; +import java.util.Iterator; import java.util.Optional; import java.util.concurrent.Callable; @@ -31,7 +35,7 @@ public class FindAccount implements Callable { @CommandLine.Option( names = {"--endpoint"}, description = "GRPC interface of the node.", - defaultValue = "http://node.testnet.concordium.com:20000") + defaultValue = "http://localhost:20002") private String endpoint; @CommandLine.Option( @@ -62,11 +66,11 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio return 0; } System.out.println("Account created in block: " + response.get().getBlockHash()); - val blockInfo = client.getBlockInfo(BlockQuery.HASH(response.get().getBlockHash())); + BlockInfo blockInfo = client.getBlockInfo(BlockQuery.HASH(response.get().getBlockHash())); System.out.println("Timestamp of the block: " + blockInfo.getBlockTime()); - val summaries = client.getBlockTransactionEvents(BlockQuery.HASH(response.get().getBlockHash())); + Iterator

summaries = client.getBlockTransactionEvents(BlockQuery.HASH(response.get().getBlockHash())); summaries.forEachRemaining(summary -> { - val details = summary.getDetails(); + Details details = summary.getDetails(); if (details.getType() == Type.ACCOUNT_CREATION) { if (details.getAccountCreationDetails().getAddress().isAliasOf(AccountAddress.from(account))) { System.out.println("Created by transaction hash: " + summary.getTransactionHash()); diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java index c6365ed55..2a8fc7444 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java @@ -5,6 +5,10 @@ import com.concordium.sdk.exceptions.ClientInitializationException; import com.concordium.sdk.requests.BlockQuery; import com.concordium.sdk.responses.BakerId; +import com.concordium.sdk.responses.DelegatorRewardPeriodInfo; +import com.concordium.sdk.responses.poolstatus.BakerPoolStatus; +import com.concordium.sdk.responses.poolstatus.CurrentPaydayStatus; +import com.concordium.sdk.transactions.Hash; import lombok.val; import lombok.var; import picocli.CommandLine; @@ -12,6 +16,8 @@ import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; +import java.util.Iterator; +import java.util.Optional; import java.util.concurrent.Callable; /** @@ -50,19 +56,19 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio BigInteger equity = BigInteger.ZERO; BigInteger delegated = BigInteger.ZERO; BigInteger effective = BigInteger.ZERO; - var activeValidators = 0; + int activeValidators = 0; - val block = client.getBlockInfo(BlockQuery.LAST_FINAL).getBlockHash(); - val blockQuery = BlockQuery.HASH(block); - val validators = client.getBakerList(blockQuery); + Hash block = client.getBlockInfo(BlockQuery.LAST_FINAL).getBlockHash(); + BlockQuery blockQuery = BlockQuery.HASH(block); + Iterator validators = client.getBakerList(blockQuery); StringBuilder sb = new StringBuilder(); while (validators.hasNext()) { BakerId validator = validators.next(); - val pool = client.getPoolInfo(blockQuery, validator); - val statusOptional = pool.getCurrentPaydayStatus(); + BakerPoolStatus pool = client.getPoolInfo(blockQuery, validator); + Optional statusOptional = pool.getCurrentPaydayStatus(); if (statusOptional.isPresent()) { - val status = statusOptional.get(); + CurrentPaydayStatus status = statusOptional.get(); long equityVal = status.getBakerEquityCapital().getValue().getValue(); long delegatedVal = status.getDelegatedCapital().getValue().getValue(); long effectiveVal = status.getEffectiveStake().getValue().getValue(); @@ -76,7 +82,7 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio sb.append("Validator ").append(validator).append(": own stake = ").append(equityVal) .append(" micro CCD, from delegators = ").append(delegatedVal).append(" micro CCD"); - val delegators = client.getPoolDelegatorsRewardPeriod(blockQuery, validator); + Iterator delegators = client.getPoolDelegatorsRewardPeriod(blockQuery, validator); // Add info about delegators if present if (delegators.hasNext()) { diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java index 2f139e47e..b35fa0340 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java @@ -1092,7 +1092,7 @@ public Optional findAccountCreation(Range * * If the account is not found, {@link Optional#empty()} is returned.

- * Note, this searches the entire chain. Use {@link ClientV2#findAtLowestHeight(Range, BiFunction)} to only search a given range. + * Note, this searches the entire chain. Use {@link ClientV2#findAccountCreation(Range, AccountAddress)} to only search a given range. * @param address The {@link AccountAddress} to search for. * @return {@link Optional} containing {@link FindAccountResponse} if the search was successful, {@link Optional#empty()} otherwise. */ diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java index 7d5b806c8..75d7417ad 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java @@ -75,7 +75,13 @@ private static class Producer implements Runnable { private final ClientV2 client; private final AbsoluteBlockHeight startHeight; - public Producer(BlockingQueue queue, ClientV2 client, AbsoluteBlockHeight startHeight) { + /** + * Instantiates a new {@link Producer} polling for finalized blocks using the provided {@link ClientV2}, starting from the provided {@link AbsoluteBlockHeight} and putting them in the provided {@link BlockingQueue}. + * @param queue the {@link BlockingQueue} to put the found blocks in. + * @param client {@link ClientV2} to use for polling. + * @param startHeight {@link AbsoluteBlockHeight} to start from. + */ + Producer(BlockingQueue queue, ClientV2 client, AbsoluteBlockHeight startHeight) { this.queue = queue; this.client = client; this.startHeight = startHeight; From 65d76a715eb38856a74cd93d3633256306f3d963 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 4 Mar 2024 11:28:46 +0100 Subject: [PATCH 15/21] Final cleanup and documentation --- .../src/main/java/com/concordium/sdk/ClientV2.java | 8 +++----- .../sdk/responses/FinalizedBlockItemIterator.java | 3 --- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java index b35fa0340..09e2503a1 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java @@ -141,9 +141,7 @@ public Iterator getBlocks(int timeoutMillis) { } /** - * Gets an {@link Iterator} of Finalized Blocks. - * With Specified Timeout. - * Form the time request is made and onwards. + * Gets an {@link Iterator} of Finalized Blocks with specified timeout from the time request is made and onwards. * This can be used to listen for blocks being Finalized. * * @param timeoutMillis Timeout for the request in Milliseconds. @@ -990,7 +988,7 @@ private Optional getFinalizedTransaction(Hash transactionHas * * The provided `test` method should, given a {@link ClientV2} and a {@link BlockQuery}, * return {@link Optional#of(T)} if the object is found in the block, and {@link Optional#empty()} otherwise. - * It can also throw exceptions which will terminate the search immediately.

+ * It can also throw exceptions which will terminate the search immediately and pass on the exception.

* * The precondition for this method is that the `test` method is monotone, i.e. if a block at height `h` satisfies the test then also a block at height `h+1` does. * If this precondition does not hold then the return value from this method is unspecified.

@@ -1009,7 +1007,7 @@ public Optional findAtLowestHeight(BiFunction + * It can also throw exceptions which will terminate the search immediately and pass on the exception.

* * The precondition for this method is that the `test` method is monotone, i.e. if a block at height `h` satisfies the test then also a block at height `h+1` does. * If this precondition does not hold then the return value from this method is unspecified.

diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java index 75d7417ad..c35fd27a6 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/FinalizedBlockItemIterator.java @@ -108,9 +108,6 @@ private void produce(AbsoluteBlockHeight startHeight) { } val blockStream = client.getFinalizedBlocks(); while (!Thread.currentThread().isInterrupted()) { - if (!blockStream.hasNext()) { - continue; - } val block = blockStream.next(); // Recover missed blocks val blockHeight = block.getBlockHeight().getValue(); From 15cb52b09942989e40c8d397bc824ebeaed32f0d Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 4 Mar 2024 14:01:56 +0100 Subject: [PATCH 16/21] Removed unused imports --- concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java index 09e2503a1..8ba1dae77 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java @@ -51,8 +51,6 @@ import io.grpc.ManagedChannel; import io.grpc.Status; import lombok.val; -import lombok.var; -import org.checkerframework.checker.units.qual.C; import java.io.File; import java.io.IOException; @@ -60,7 +58,6 @@ import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Iterator; -import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; From f7978187330ece9ed43eeb017407a86fe637ae46 Mon Sep 17 00:00:00 2001 From: magnusbechwind <93247743+magnusbechwind@users.noreply.github.com> Date: Wed, 6 Mar 2024 14:25:54 +0100 Subject: [PATCH 17/21] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakob Ørhøj <57264157+orhoj@users.noreply.github.com> --- .../src/main/java/com/concordium/sdk/examples/BlockStats.java | 2 +- .../main/java/com/concordium/sdk/examples/FindAccount.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java index 254b272fe..164b4639b 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java @@ -35,7 +35,7 @@ * - Arrive time of the block at the given node * - Difference between receive and slot times * - Difference between arrive and slot times - * - Number if events associated with payday + * - Number of events associated with payday * - Whether the block contains a finalization record * - The number of transactions included in the block */ diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java index e9cba3997..9cc208d2b 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java @@ -45,7 +45,7 @@ public class FindAccount implements Callable { private int timeout; @CommandLine.Option( - names = {"-a ", "--account"}, + names = {"--a", "--account"}, description = "Account to look for", defaultValue = "4AuT5RRmBwcdkLMA6iVjxTDb1FQmxwAh3wHBS22mggWL8xH6s3") private String account; @@ -61,7 +61,7 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio var client = ClientV2.from(connection); Optional response = client.findAccountCreation(AccountAddress.from(account)); - if (!response.isPresent()) { + if (response.isEmpty()) { System.out.println("Account not found."); return 0; } From 19d06b3b46fa90fe3d3cdd7ac8c89e130629eb50 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Wed, 6 Mar 2024 14:28:22 +0100 Subject: [PATCH 18/21] Applied suggestions from review --- .../com/concordium/sdk/examples/Aliases.java | 6 ++--- .../concordium/sdk/examples/BlockStats.java | 23 +++++++++---------- .../concordium/sdk/examples/FindAccount.java | 18 +++++++++------ .../sdk/examples/ValidatorStats.java | 4 ++-- .../java/com/concordium/sdk/ClientV2.java | 2 +- .../sdk/responses/FindAccountResponse.java | 4 ++-- 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java index 9e2111fa4..9153eea1a 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/Aliases.java @@ -20,10 +20,10 @@ public class Aliases implements Callable { public Integer call() throws Exception { String first = addresses.remove(0); AccountAddress addr = AccountAddress.from(first); - for (String a : addresses) { + for (String address : addresses) { - if (!addr.isAliasOf(AccountAddress.from(a))) { - System.out.println(a + " is not an alias of " + first); + if (!addr.isAliasOf(AccountAddress.from(address))) { + System.out.println(address + " is not an alias of " + first); return 0; } } diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java index 254b272fe..3379ab31a 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java @@ -13,11 +13,10 @@ import com.concordium.sdk.responses.blocksummary.specialoutcomes.SpecialOutcome; import com.concordium.sdk.types.AbsoluteBlockHeight; import com.google.common.collect.ImmutableList; -import lombok.val; import lombok.var; import picocli.CommandLine; -import java.net.MalformedURLException; +import java.io.IOException; import java.net.URL; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -67,20 +66,20 @@ public class BlockStats implements Callable { private Optional toString; @Override - public Integer call() throws MalformedURLException, ClientInitializationException { - var endpointUrl = new URL(this.endpoint); + public Integer call() throws IOException, ClientInitializationException { + URL endpointUrl = new URL(this.endpoint); Connection connection = Connection.newBuilder() .host(endpointUrl.getHost()) .port(endpointUrl.getPort()) .timeout(timeout) .build(); - var client = ClientV2.from(connection); + ClientV2 client = ClientV2.from(connection); AbsoluteBlockHeight start; if (fromString.isPresent()) { ZonedDateTime from = ZonedDateTime.parse(fromString.get(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); - Optional b = client.findAtLowestHeight((c, query) -> { - BlockInfo blockInfo = c.getBlockInfo(query); + Optional startBlock = client.findAtLowestHeight((clientV2, query) -> { + BlockInfo blockInfo = clientV2.getBlockInfo(query); ZonedDateTime blockTime = blockInfo.getBlockTime().getZonedDateTime(); if (from.isBefore(blockTime)) { return Optional.of(blockInfo); @@ -89,8 +88,8 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio } }); - if (b.isPresent()) { - start = AbsoluteBlockHeight.from(b.get().getBlockHeight()); + if (startBlock.isPresent()) { + start = AbsoluteBlockHeight.from(startBlock.get().getBlockHeight()); } else { throw new IllegalArgumentException("Last finalized block is not after the requested start time."); } @@ -104,7 +103,7 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio FinalizedBlockItemIterator blocks = client.getFinalizedBlocksFrom(start); ZonedDateTime endTime; endTime = toString.map(s -> ZonedDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)).orElseGet(ZonedDateTime::now); - String format = "%1$-64s | %2$-29s | %3$-29s | %4$-29s | %5$-13s | %6$-12s | %7$-14s | %8$-17s | %9$-17s | %10$-30s | %11$-28s | %12$-8s\n"; + String format = "%1$-64s | %2$-29s | %3$-29s | %4$-29s | %5$-13s | %6$-12s | %7$-14s | %8$-17s | %9$-17s | %10$-7s | %11$-6s | %12$-8s\n"; System.out.printf(format, "Block hash", "block time", "block receive time", "block arrive time", "receive delta", "arrive" + " delta", "#payday events", "finalization data", "transaction count", "round", "epoch", "baker id"); @@ -136,8 +135,8 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio paydayBlock, hasFinalizationData, blockInfo.getTransactionCount(), - blockInfo.getRound().isPresent() ? blockInfo.getRound().toString() : "", - blockInfo.getEpoch().isPresent() ? blockInfo.getEpoch().toString() : "", + blockInfo.getRound().isPresent() ? blockInfo.getRound().get().getValue().toString() : "", + blockInfo.getEpoch().isPresent() ? blockInfo.getEpoch().get().getValue().toString() : "", Objects.isNull(blockInfo.getBlockBaker()) ? "" : blockInfo.getBlockBaker().toString() ); diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java index e9cba3997..89c1069a7 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java @@ -52,31 +52,35 @@ public class FindAccount implements Callable { @Override public Integer call() throws MalformedURLException, ClientInitializationException { - var endpointUrl = new URL(this.endpoint); + URL endpointUrl = new URL(this.endpoint); Connection connection = Connection.newBuilder() .host(endpointUrl.getHost()) .port(endpointUrl.getPort()) .timeout(timeout) .build(); - var client = ClientV2.from(connection); + ClientV2 client = ClientV2.from(connection); Optional response = client.findAccountCreation(AccountAddress.from(account)); if (!response.isPresent()) { System.out.println("Account not found."); return 0; } - System.out.println("Account created in block: " + response.get().getBlockHash()); - BlockInfo blockInfo = client.getBlockInfo(BlockQuery.HASH(response.get().getBlockHash())); + Hash blockHash = response.get().getBlockHash(); + BlockQuery blockQuery = BlockQuery.HASH(blockHash); + System.out.println("Account created in block: " + blockHash); + BlockInfo blockInfo = client.getBlockInfo(blockQuery); System.out.println("Timestamp of the block: " + blockInfo.getBlockTime()); - Iterator

summaries = client.getBlockTransactionEvents(BlockQuery.HASH(response.get().getBlockHash())); - summaries.forEachRemaining(summary -> { + Iterator summaries = client.getBlockTransactionEvents(blockQuery); + while (summaries.hasNext()) { + Summary summary = summaries.next(); Details details = summary.getDetails(); if (details.getType() == Type.ACCOUNT_CREATION) { if (details.getAccountCreationDetails().getAddress().isAliasOf(AccountAddress.from(account))) { System.out.println("Created by transaction hash: " + summary.getTransactionHash()); + break; } } - }); + } return 0; } diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java index 2a8fc7444..f7bc8f0e3 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java @@ -44,13 +44,13 @@ public class ValidatorStats implements Callable { @Override public Integer call() throws MalformedURLException, ClientInitializationException { - var endpointUrl = new URL(this.endpoint); + URL endpointUrl = new URL(this.endpoint); Connection connection = Connection.newBuilder() .host(endpointUrl.getHost()) .port(endpointUrl.getPort()) .timeout(timeout) .build(); - var client = ClientV2.from(connection); + ClientV2 client = ClientV2.from(connection); // BigInteger to avoid overflow BigInteger equity = BigInteger.ZERO; diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java index 8ba1dae77..e54522e19 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java @@ -1065,7 +1065,7 @@ public Optional findAccountCreation(Range Date: Wed, 6 Mar 2024 14:34:30 +0100 Subject: [PATCH 19/21] bugfix --- .../java/com/concordium/sdk/examples/FindAccount.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java index a29b4add8..79a067ba9 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java @@ -2,21 +2,15 @@ import com.concordium.sdk.ClientV2; import com.concordium.sdk.Connection; -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.FindAccountResponse; import com.concordium.sdk.responses.blockinfo.BlockInfo; -import com.concordium.sdk.responses.blockitemstatus.FinalizedBlockItem; import com.concordium.sdk.responses.blockitemsummary.Details; import com.concordium.sdk.responses.blockitemsummary.Summary; import com.concordium.sdk.responses.blockitemsummary.Type; import com.concordium.sdk.transactions.*; import com.concordium.sdk.types.AccountAddress; -import jdk.nashorn.internal.codegen.CompilerConstants; -import lombok.val; -import lombok.var; import picocli.CommandLine; import java.net.MalformedURLException; @@ -61,7 +55,10 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio ClientV2 client = ClientV2.from(connection); Optional response = client.findAccountCreation(AccountAddress.from(account)); - if (response.isEmpty()) { + + boolean isEmpty = !response.isPresent(); + + if (isEmpty) { System.out.println("Account not found."); return 0; } From 25f223a9767a4230fa73a5ca042f4e1f4107b008 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 11 Mar 2024 10:45:06 +0100 Subject: [PATCH 20/21] possibility for using TLS --- .../concordium/sdk/examples/BlockStats.java | 20 +++++++++++++----- .../concordium/sdk/examples/FindAccount.java | 19 +++++++++++++---- .../sdk/examples/ValidatorStats.java | 21 +++++++++++++------ 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java index 5913135a2..6760754c6 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java @@ -2,6 +2,7 @@ import com.concordium.sdk.ClientV2; import com.concordium.sdk.Connection; +import com.concordium.sdk.TLSConfig; import com.concordium.sdk.exceptions.ClientInitializationException; import com.concordium.sdk.requests.BlockQuery; import com.concordium.sdk.responses.BlockIdentifier; @@ -13,9 +14,9 @@ import com.concordium.sdk.responses.blocksummary.specialoutcomes.SpecialOutcome; import com.concordium.sdk.types.AbsoluteBlockHeight; import com.google.common.collect.ImmutableList; -import lombok.var; import picocli.CommandLine; +import java.io.File; import java.io.IOException; import java.net.URL; import java.time.ZonedDateTime; @@ -47,6 +48,12 @@ public class BlockStats implements Callable { defaultValue = "http://localhost:20002") private String endpoint; + @CommandLine.Option( + names = {"--tls"}, + description = "Path to the server certificate" + ) + private Optional tls; + @CommandLine.Option( names = {"--timeout"}, description = "GRPC request timeout in milliseconds.", @@ -68,12 +75,15 @@ public class BlockStats implements Callable { @Override public Integer call() throws IOException, ClientInitializationException { URL endpointUrl = new URL(this.endpoint); - Connection connection = Connection.newBuilder() + Connection.ConnectionBuilder connection = Connection.newBuilder() .host(endpointUrl.getHost()) .port(endpointUrl.getPort()) - .timeout(timeout) - .build(); - ClientV2 client = ClientV2.from(connection); + .timeout(timeout); + + if (tls.isPresent()) { + connection = connection.useTLS(TLSConfig.from(new File(tls.get()))); + } + ClientV2 client = ClientV2.from(connection.build()); AbsoluteBlockHeight start; if (fromString.isPresent()) { diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java index 79a067ba9..ea0f5ece8 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java @@ -2,6 +2,7 @@ import com.concordium.sdk.ClientV2; import com.concordium.sdk.Connection; +import com.concordium.sdk.TLSConfig; import com.concordium.sdk.exceptions.ClientInitializationException; import com.concordium.sdk.requests.BlockQuery; import com.concordium.sdk.responses.FindAccountResponse; @@ -13,6 +14,7 @@ import com.concordium.sdk.types.AccountAddress; import picocli.CommandLine; +import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.Iterator; @@ -32,6 +34,12 @@ public class FindAccount implements Callable { defaultValue = "http://localhost:20002") private String endpoint; + @CommandLine.Option( + names = {"--tls"}, + description = "Path to the server certificate" + ) + private Optional tls; + @CommandLine.Option( names = {"--timeout"}, description = "GRPC request timeout in milliseconds.", @@ -47,12 +55,15 @@ public class FindAccount implements Callable { @Override public Integer call() throws MalformedURLException, ClientInitializationException { URL endpointUrl = new URL(this.endpoint); - Connection connection = Connection.newBuilder() + Connection.ConnectionBuilder connection = Connection.newBuilder() .host(endpointUrl.getHost()) .port(endpointUrl.getPort()) - .timeout(timeout) - .build(); - ClientV2 client = ClientV2.from(connection); + .timeout(timeout); + + if (tls.isPresent()) { + connection = connection.useTLS(TLSConfig.from(new File(tls.get()))); + } + ClientV2 client = ClientV2.from(connection.build()); Optional response = client.findAccountCreation(AccountAddress.from(account)); diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java index f7bc8f0e3..826155b90 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java @@ -2,6 +2,7 @@ import com.concordium.sdk.ClientV2; import com.concordium.sdk.Connection; +import com.concordium.sdk.TLSConfig; import com.concordium.sdk.exceptions.ClientInitializationException; import com.concordium.sdk.requests.BlockQuery; import com.concordium.sdk.responses.BakerId; @@ -9,10 +10,9 @@ import com.concordium.sdk.responses.poolstatus.BakerPoolStatus; import com.concordium.sdk.responses.poolstatus.CurrentPaydayStatus; import com.concordium.sdk.transactions.Hash; -import lombok.val; -import lombok.var; import picocli.CommandLine; +import java.io.File; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; @@ -36,6 +36,12 @@ public class ValidatorStats implements Callable { defaultValue = "http://localhost:20002") private String endpoint; + @CommandLine.Option( + names = {"--tls"}, + description = "Path to the server certificate" + ) + private Optional tls; + @CommandLine.Option( names = {"--timeout"}, description = "GRPC request timeout in milliseconds.", @@ -45,12 +51,15 @@ public class ValidatorStats implements Callable { @Override public Integer call() throws MalformedURLException, ClientInitializationException { URL endpointUrl = new URL(this.endpoint); - Connection connection = Connection.newBuilder() + Connection.ConnectionBuilder connection = Connection.newBuilder() .host(endpointUrl.getHost()) .port(endpointUrl.getPort()) - .timeout(timeout) - .build(); - ClientV2 client = ClientV2.from(connection); + .timeout(timeout); + + if (tls.isPresent()) { + connection = connection.useTLS(TLSConfig.from(new File(tls.get()))); + } + ClientV2 client = ClientV2.from(connection.build()); // BigInteger to avoid overflow BigInteger equity = BigInteger.ZERO; From 8e32265e5068dfc79a32d775a3f81e86cedae7de Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 11 Mar 2024 10:56:16 +0100 Subject: [PATCH 21/21] more TLS --- .../com/concordium/sdk/examples/BlockStats.java | 17 ++++++++++++++--- .../concordium/sdk/examples/FindAccount.java | 17 ++++++++++++++--- .../concordium/sdk/examples/ValidatorStats.java | 17 ++++++++++++++--- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java index 6760754c6..d962ac340 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/BlockStats.java @@ -50,9 +50,15 @@ public class BlockStats implements Callable { @CommandLine.Option( names = {"--tls"}, + description = "Whether to use TLS. Uses default trust store. Use --tls-path to supply certificate" + ) + private boolean useTLS; + + @CommandLine.Option( + names = {"--tls-path"}, description = "Path to the server certificate" ) - private Optional tls; + private Optional tlsPath; @CommandLine.Option( names = {"--timeout"}, @@ -80,9 +86,14 @@ public Integer call() throws IOException, ClientInitializationException { .port(endpointUrl.getPort()) .timeout(timeout); - if (tls.isPresent()) { - connection = connection.useTLS(TLSConfig.from(new File(tls.get()))); + if (useTLS) { + if (tlsPath.isPresent()) { + connection = connection.useTLS(TLSConfig.from(new File(tlsPath.get()))); + } else { + connection = connection.useTLS(TLSConfig.auto()); + } } + ClientV2 client = ClientV2.from(connection.build()); AbsoluteBlockHeight start; diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java index ea0f5ece8..5b92a2ba2 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/FindAccount.java @@ -36,9 +36,15 @@ public class FindAccount implements Callable { @CommandLine.Option( names = {"--tls"}, + description = "Whether to use TLS. Uses default trust store. Use --tls-path to supply certificate" + ) + private boolean useTLS; + + @CommandLine.Option( + names = {"--tls-path"}, description = "Path to the server certificate" ) - private Optional tls; + private Optional tlsPath; @CommandLine.Option( names = {"--timeout"}, @@ -60,9 +66,14 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio .port(endpointUrl.getPort()) .timeout(timeout); - if (tls.isPresent()) { - connection = connection.useTLS(TLSConfig.from(new File(tls.get()))); + if (useTLS) { + if (tlsPath.isPresent()) { + connection = connection.useTLS(TLSConfig.from(new File(tlsPath.get()))); + } else { + connection = connection.useTLS(TLSConfig.auto()); + } } + ClientV2 client = ClientV2.from(connection.build()); Optional response = client.findAccountCreation(AccountAddress.from(account)); diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java index 826155b90..b4cb3ea4c 100644 --- a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java +++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/ValidatorStats.java @@ -38,9 +38,15 @@ public class ValidatorStats implements Callable { @CommandLine.Option( names = {"--tls"}, + description = "Whether to use TLS. Uses default trust store. Use --tls-path to supply certificate" + ) + private boolean useTLS; + + @CommandLine.Option( + names = {"--tls-path"}, description = "Path to the server certificate" ) - private Optional tls; + private Optional tlsPath; @CommandLine.Option( names = {"--timeout"}, @@ -56,9 +62,14 @@ public Integer call() throws MalformedURLException, ClientInitializationExceptio .port(endpointUrl.getPort()) .timeout(timeout); - if (tls.isPresent()) { - connection = connection.useTLS(TLSConfig.from(new File(tls.get()))); + if (useTLS) { + if (tlsPath.isPresent()) { + connection = connection.useTLS(TLSConfig.from(new File(tlsPath.get()))); + } else { + connection = connection.useTLS(TLSConfig.auto()); + } } + ClientV2 client = ClientV2.from(connection.build()); // BigInteger to avoid overflow