Skip to content

Commit

Permalink
Merge pull request #325 from Concordium/extend-examples
Browse files Browse the repository at this point in the history
Extend examples
  • Loading branch information
magnusbechwind authored Mar 11, 2024
2 parents 799290f + 8e32265 commit 89ff4a1
Show file tree
Hide file tree
Showing 13 changed files with 915 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
- Added utility functions for converting between `CCDAmount` and `Energy`. Present in utility class `Converter`.
- Fixed a bug in `CustomEvent`. Removed unnecessary `tag` field.
- Added `Web3IdProof` class with `getWeb3IdProof` method to create Presentations. (And supporting classes)
- 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.
- Fixed an issue where `ConcordiumHdWallet.fromSeedPhrase` always produced an invalid seed as hex.

## 7.0.0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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<Integer> {

@CommandLine.Parameters(
description = "List of addresses to check",
arity = "2..." )
List<String> addresses;
@Override
public Integer call() throws Exception {
String first = addresses.remove(0);
AccountAddress addr = AccountAddress.from(first);
for (String address : addresses) {

if (!addr.isAliasOf(AccountAddress.from(address))) {
System.out.println(address + " 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package com.concordium.sdk.examples;

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;
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 picocli.CommandLine;

import java.io.File;
import java.io.IOException;
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 of 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<Integer> {

@CommandLine.Option(
names = {"--endpoint"},
description = "GRPC interface of the node.",
defaultValue = "http://localhost:20002")
private String endpoint;

@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<String> tlsPath;

@CommandLine.Option(
names = {"--timeout"},
description = "GRPC request timeout in milliseconds.",
defaultValue = "100000")
private int timeout;

@CommandLine.Option(
names = {"--from"},
description = "Starting time (format 2024-01-22T10:15:30+01:00). Defaults to genesis"
)
private Optional<String> fromString;

@CommandLine.Option(
names = {"--to"},
description = "End time (format 2024-01-22T10:15:30+01:00). Defaults to the time the tool has run"
)
private Optional<String> toString;

@Override
public Integer call() throws IOException, ClientInitializationException {
URL endpointUrl = new URL(this.endpoint);
Connection.ConnectionBuilder connection = Connection.newBuilder()
.host(endpointUrl.getHost())
.port(endpointUrl.getPort())
.timeout(timeout);

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;
if (fromString.isPresent()) {
ZonedDateTime from = ZonedDateTime.parse(fromString.get(), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Optional<BlockInfo> startBlock = client.findAtLowestHeight((clientV2, query) -> {
BlockInfo blockInfo = clientV2.getBlockInfo(query);
ZonedDateTime blockTime = blockInfo.getBlockTime().getZonedDateTime();
if (from.isBefore(blockTime)) {
return Optional.of(blockInfo);
} else {
return Optional.empty();
}

});
if (startBlock.isPresent()) {
start = AbsoluteBlockHeight.from(startBlock.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;
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$-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");
while (blocks.hasNext()) {
BlockIdentifier block = blocks.next();
BlockQuery blockQuery = BlockQuery.HASH(block.getBlockHash());
BlockInfo blockInfo = client.getBlockInfo(blockQuery);
ZonedDateTime blockTime = blockInfo.getBlockTime().getZonedDateTime();
if (endTime.isBefore(blockTime)) {
break;
}

int paydayBlock = 0;
ImmutableList<SpecialOutcome> 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++;
}
}
boolean hasFinalizationData = client.getBlockFinalizationSummary(blockQuery).isPresent();

System.out.printf(format,
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().get().getValue().toString() : "",
blockInfo.getEpoch().isPresent() ? blockInfo.getEpoch().get().getValue().toString() : "",
Objects.isNull(blockInfo.getBlockBaker()) ? "" : blockInfo.getBlockBaker().toString()

);

if (hasFinalizationData) {
finalizationCount++;
}
blockCount++;

}
blocks.drop();
System.out.println("Block count: " + blockCount);
System.out.println("Finalization record count: " + finalizationCount);

return 0;
}

public static void main(String[] args) {
int exitCode = new CommandLine(new BlockStats()).execute(args);
System.exit(exitCode);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.concordium.sdk.examples;

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;
import com.concordium.sdk.responses.blockinfo.BlockInfo;
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 picocli.CommandLine;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
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<Integer> {

@CommandLine.Option(
names = {"--endpoint"},
description = "GRPC interface of the node.",
defaultValue = "http://localhost:20002")
private String endpoint;

@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<String> tlsPath;

@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 = "4AuT5RRmBwcdkLMA6iVjxTDb1FQmxwAh3wHBS22mggWL8xH6s3")
private String account;

@Override
public Integer call() throws MalformedURLException, ClientInitializationException {
URL endpointUrl = new URL(this.endpoint);
Connection.ConnectionBuilder connection = Connection.newBuilder()
.host(endpointUrl.getHost())
.port(endpointUrl.getPort())
.timeout(timeout);

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<FindAccountResponse> response = client.findAccountCreation(AccountAddress.from(account));

boolean isEmpty = !response.isPresent();

if (isEmpty) {
System.out.println("Account not found.");
return 0;
}
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<Summary> 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;
}

public static void main(String[] args) {
int exitCode = new CommandLine(new FindAccount()).execute(args);
System.exit(exitCode);
}
}
Loading

0 comments on commit 89ff4a1

Please sign in to comment.