Skip to content

Commit

Permalink
Merge pull request #283 from Concordium/supportNewEndpoints
Browse files Browse the repository at this point in the history
Support new endpoints
  • Loading branch information
magnusbechwind authored Nov 15, 2023
2 parents b7ed405 + 1268581 commit 48d1f54
Show file tree
Hide file tree
Showing 11 changed files with 474 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog

## Unreleased changes
- Added support for GRPC V2 `GetWinningBakersEpoch` for getting a list of bakers that won the lottery in a particular historical epoch. Only available when querying a node with version at least 6.1.
- Added support for GRPC V2 `GetFirstBlockEpoch` for getting the block hash of the first finalized block in a specified epoch. Only available when querying a node with version at least 6.1.
- Added support for GRPC V2 `GetBakerEarliestWinTime` for getting the projected earliest time at which a particular baker will be required to bake a block. Only available when querying a node woth version at least 6.1.
- Added support for GRPC V2 `GetBakerRewardPeriodInfo` for getting all the bakers in the reward period of a block. Only available when querying a node with version at least 6.1.
- Added support for GRPC V2 `GetBlockCertificates` for retrieving certificates for a block supporting ConcordiumBF, i.e. a node with at least version 6.1.
- Extended `CurrentPaydayStatus` with `CommissionRates` that apply for the current reward period. Requires at least node version 6.1.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.concordium.sdk.examples;

import com.concordium.sdk.ClientV2;
import com.concordium.sdk.Connection;
import com.concordium.sdk.responses.BakerId;
import com.concordium.sdk.types.Timestamp;
import picocli.CommandLine;

import java.net.URL;
import java.util.concurrent.Callable;

/**
* Creates a {@link ClientV2} from the specified connection ("http://localhost:20002" if not specified).
* Retrieves and prints the {@link Timestamp} of the projected earliest time at which baker with id = 1 will be required to bake a block.
*/
@CommandLine.Command(name = "GetBakerEarliestWinTime", mixinStandardHelpOptions = true)
public class GetBakerEarliestWinTime implements Callable<Integer> {
@CommandLine.Option(
names = {"--endpoint"},
description = "GRPC interface of the node.",
defaultValue = "http://localhost:20002")
private String endpoint;

@Override
public Integer call() throws Exception {
URL endpointUrl = new URL(this.endpoint);
Connection connection = Connection.newBuilder()
.host(endpointUrl.getHost())
.port(endpointUrl.getPort())
.build();

ClientV2 client = ClientV2.from(connection);
Timestamp timestamp = client.getBakerEarliestWinTime(BakerId.from(1));
System.out.println(timestamp);
return 0;
}

public static void main(String[] args) {
int exitCode = new CommandLine(new GetBakerEarliestWinTime()).execute(args);
System.exit(exitCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.concordium.sdk.examples;

import com.concordium.sdk.ClientV2;
import com.concordium.sdk.Connection;
import com.concordium.sdk.requests.EpochQuery;
import com.concordium.sdk.responses.Epoch;
import com.concordium.sdk.transactions.Hash;
import picocli.CommandLine;

import java.net.URL;
import java.util.concurrent.Callable;

/**
* Creates a {@link ClientV2} from the specified connection ("http://localhost:20002" if not specified).
* Retrieves and prints the {@link Hash} of the first finalized block for specified Epoch at the specified genesis index (Epoch 2 at genesis index 5 if not specified).
*/
@CommandLine.Command(name = "GetFirstBlockEpoch", mixinStandardHelpOptions = true)
public class GetFirstBlockEpoch implements Callable<Integer> {
@CommandLine.Option(
names = {"--endpoint"},
description = "GRPC interface of the node.",
defaultValue = "http://localhost:20002")
private String endpoint;

@CommandLine.Option(
names = {"--genesisIndex"},
description = "Genesis index to query at",
defaultValue = "5")
private int genesisIndex;

@CommandLine.Option(
names = {"--epoch"},
description = "Epoch index to query",
defaultValue = "5")
private int epoch;

@Override
public Integer call() throws Exception {
URL endpointUrl = new URL(this.endpoint);
Connection connection = Connection.newBuilder()
.host(endpointUrl.getHost())
.port(endpointUrl.getPort())
.build();

ClientV2 client = ClientV2.from(connection);
Hash hash = client.getFirstBlockEpoch(EpochQuery.RELATIVE_EPOCH(genesisIndex, Epoch.from(epoch)));
System.out.println(hash);
return 0;
}

public static void main(String[] args) {
int exitCode = new CommandLine(new GetFirstBlockEpoch()).execute(args);
System.exit(exitCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import java.util.concurrent.Callable;

/**
* Creates a {@link ClientV2} from the specified connection ("http://localhost:20001" if not specified).
* Creates a {@link ClientV2} from the specified connection ("http://localhost:20000" if not specified).
* Retrieves and prints the {@link PeerInfo} of connected Peers.
*/
@CommandLine.Command(name = "GetPeersInfo", mixinStandardHelpOptions = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.concordium.sdk.examples;

import com.concordium.grpc.v2.WinningBaker;
import com.concordium.sdk.ClientV2;
import com.concordium.sdk.Connection;
import com.concordium.sdk.requests.EpochQuery;
import com.concordium.sdk.responses.Epoch;
import picocli.CommandLine;

import java.net.URL;
import java.util.concurrent.Callable;

/**
* Creates a {@link ClientV2} from the specified connection ("http://localhost:20002" if not specified).
* Retrieves and prints the {@link WinningBaker}s for the first finalized block for specified Epoch at the specified genesis index (Epoch 2 at genesis index 5 if not specified).
*/
@CommandLine.Command(name = "GetWinningBakersEpoch", mixinStandardHelpOptions = true)
public class GetWinningBakersEpoch implements Callable<Integer> {
@CommandLine.Option(
names = {"--endpoint"},
description = "GRPC interface of the node.",
defaultValue = "http://localhost:20002")
private String endpoint;

@CommandLine.Option(
names = {"--genesisIndex"},
description = "Genesis index to query at",
defaultValue = "5")
private int genesisIndex;

@CommandLine.Option(
names = {"--epoch"},
description = "Epoch index to query",
defaultValue = "5")
private int epoch;

@Override
public Integer call() throws Exception {
URL endpointUrl = new URL(this.endpoint);
Connection connection = Connection.newBuilder()
.host(endpointUrl.getHost())
.port(endpointUrl.getPort())
.build();

ClientV2 client = ClientV2.from(connection);
client.getWinningBakersEpoch(EpochQuery.RELATIVE_EPOCH(genesisIndex, Epoch.from(epoch)))
.forEach(System.out::println);

return 0;
}

public static void main(String[] args) {
int exitCode = new CommandLine(new GetWinningBakersEpoch()).execute(args);
System.exit(exitCode);
}
}
81 changes: 77 additions & 4 deletions concordium-sdk/src/main/java/com/concordium/sdk/ClientV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.concordium.sdk.exceptions.ClientInitializationException;
import com.concordium.sdk.requests.AccountQuery;
import com.concordium.sdk.requests.BlockQuery;
import com.concordium.sdk.requests.EpochQuery;
import com.concordium.sdk.requests.dumpstart.DumpRequest;
import com.concordium.sdk.requests.smartcontracts.InvokeInstanceRequest;
import com.concordium.sdk.responses.AccountIndex;
Expand Down Expand Up @@ -32,12 +33,14 @@
import com.concordium.sdk.responses.poolstatus.BakerPoolStatus;
import com.concordium.sdk.responses.rewardstatus.RewardsOverview;
import com.concordium.sdk.responses.smartcontracts.InvokeInstanceResult;
import com.concordium.sdk.responses.winningbaker.WinningBaker;
import com.concordium.sdk.transactions.AccountTransaction;
import com.concordium.sdk.transactions.BlockItem;
import com.concordium.sdk.transactions.*;
import com.concordium.sdk.transactions.smartcontracts.WasmModule;
import com.concordium.sdk.types.AccountAddress;
import com.concordium.sdk.types.ContractAddress;
import com.concordium.sdk.types.Timestamp;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
import io.grpc.ManagedChannel;
Expand Down Expand Up @@ -781,11 +784,11 @@ public InvokeInstanceResult invokeInstance(InvokeInstanceRequest request) {
/**
* Get all bakers in the reward period of a block.
* This endpoint is only supported for protocol version 6 and onwards.
* If the protocol does not support the endpoint then an 'UNIMPLEMENTED' exception is thrown
*
* @param input The block to query.
*
* @return {@link ImmutableList} with the {@link BakerRewardPeriodInfo} of all the bakers in the block.
* @throws io.grpc.StatusRuntimeException with {@link io.grpc.Status.Code}:
* <ul><li>{@link io.grpc.Status.Code#UNIMPLEMENTED} if the protocol does not support the endpoint.</ul>
*/
public ImmutableList<BakerRewardPeriodInfo> getBakersRewardPeriod(BlockQuery input) {
val response = this.server().getBakersRewardPeriod(to(input));
Expand All @@ -798,16 +801,86 @@ public ImmutableList<BakerRewardPeriodInfo> getBakersRewardPeriod(BlockQuery inp

/**
* Retrieves the {@link BlockCertificates} for a given block.
* Note that, if the block being pointed to is not a product of ConcordiumBFT, i.e. created before protocol version 6, then a INVALID_ARGUMENT exception will be thrown.
* If the endpoint is not enabled by the node, then an 'UNIMPLEMENTED' exception will be thrown.
*
* @param block The block to query
* @return {@link BlockCertificates} of the block.
* @throws io.grpc.StatusRuntimeException with {@link io.grpc.Status.Code}:<ul>
* <li>{@link io.grpc.Status.Code#UNIMPLEMENTED} if the endpoint is not enabled by the node.
* <li>{@link io.grpc.Status.Code#INVALID_ARGUMENT} if the block being pointed to is not a product of ConcordiumBFT, i.e. created before protocol version 6.
* </ul>
*/
public BlockCertificates getBlockCertificates(BlockQuery block) {
val res = this.server().getBlockCertificates(to(block));
return BlockCertificates.from(res);
}

/**
* Get the projected earliest time at which a particular baker will be required to bake a block. <p>
*
* If the baker is not a baker for the current reward period, this returns a timestamp at the
* start of the next reward period. <p>
* If the baker is a baker for the current reward period, the
* earliest win time is projected from the current round forward, assuming that each round after
* the last finalized round will take the minimum block time. (If blocks take longer, or timeouts
* occur, the actual time may be later, and the reported time in subsequent queries may reflect
* this.) <p>
* At the end of an epoch (or if the baker is not projected to bake before the end of the
* epoch) the earliest win time for a (current) baker will be projected as the start of the next
* epoch. This is because the seed for the leader election is updated at the epoch boundary, and
* so the winners cannot be predicted beyond that. <p>
* Note that in some circumstances the returned timestamp can be in the past, especially at the end of an epoch.
* @param bakerId id of the baker to query.
* @return {@link Timestamp} as described in the method documentation.
* @throws io.grpc.StatusRuntimeException with {@link io.grpc.Status.Code}:
* <ul><li>{@link io.grpc.Status.Code#UNIMPLEMENTED} if the current consensus version is 0, as the endpoint is only supported by consensus version 1.</ul>
*/
public Timestamp getBakerEarliestWinTime(BakerId bakerId) {
val res = this.server().getBakerEarliestWinTime(to(bakerId));
return Timestamp.from(res);
}

/**
* Get the block hash of the first finalized block in a specified epoch.
*
* @param epochQuery {@link EpochQuery} representing the specific epoch to query.
* @return {@link Hash} of the first finalized block in the epoch.
* @throws io.grpc.StatusRuntimeException with {@link io.grpc.Status.Code}: <ul>
* <li> {@link io.grpc.Status#NOT_FOUND} if the query specifies an unknown block.
* <li> {@link io.grpc.Status#UNAVAILABLE} if the query is for an epoch that is not finalized in the current genesis index, or is for a future genesis index.
* <li> {@link io.grpc.Status#INVALID_ARGUMENT} if the query is for an epoch with no finalized blocks for a past genesis index.
* <li> {@link io.grpc.Status#INVALID_ARGUMENT} if the input {@link EpochQuery} is malformed.
* <li> {@link io.grpc.Status#UNIMPLEMENTED} if the endpoint is disabled on the node.
* </ul>
*/
public Hash getFirstBlockEpoch(EpochQuery epochQuery) {
val res = this.server().getFirstBlockEpoch(to(epochQuery));
return Hash.from(res);
}

/**
* Get the list of bakers that won the lottery in a particular historical epoch (i.e. the last finalized block is in a later epoch). <p>
* This lists the winners for each round in the epoch, starting from the round after the last block in the previous epoch, running to the round before the first block in the next epoch. <p>
* It also indicates if a block in each round was included in the finalized chain.
* @param epochQuery {@link EpochQuery} representing the specific epoch to query.
* @return {@link ImmutableList} of bakers that won the lottery in the specified epoch.
* @throws io.grpc.StatusRuntimeException with {@link io.grpc.Status.Code}: <ul>
* <li> {@link io.grpc.Status#NOT_FOUND} if the query specifies an unknown block.
* <li> {@link io.grpc.Status#UNAVAILABLE} if the query is for an epoch that is not finalized in the current genesis index, or is for a future genesis index.
* <li> {@link io.grpc.Status#INVALID_ARGUMENT} if the query is for an epoch that is not finalized for a past genesis index.
* <li> {@link io.grpc.Status#INVALID_ARGUMENT} if the query is for a genesis index at consensus version 0.
* <li> {@link io.grpc.Status#INVALID_ARGUMENT} if the input {@link EpochQuery} is malformed.
* <li> {@link io.grpc.Status#UNIMPLEMENTED} if the endpoint is disabled on the node.
* </ul>
*/
public ImmutableList<WinningBaker> getWinningBakersEpoch(EpochQuery epochQuery) {
val res = this.server().getWinningBakersEpoch(to(epochQuery));
val winners = new ImmutableList.Builder<WinningBaker>();
res.forEachRemaining(
winner -> winners.add(WinningBaker.parse(winner))
);
return winners.build();
}

/**
* Get a {@link QueriesGrpc.QueriesBlockingStub} with a timeout
* The timeout is the one specified in via the {@link Connection} object used to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.concordium.sdk.crypto.vrf.VRFPublicKey;
import com.concordium.sdk.requests.AccountQuery;
import com.concordium.sdk.requests.BlockQuery;
import com.concordium.sdk.requests.EpochQuery;
import com.concordium.sdk.responses.Epoch;
import com.concordium.sdk.responses.Round;
import com.concordium.sdk.responses.TimeoutParameters;
Expand Down Expand Up @@ -1645,4 +1646,22 @@ static com.concordium.grpc.v2.Parameter to(Parameter parameter) {
.setValue(ByteString.copyFrom(parameter.getBytes()))
.build();
}

static EpochRequest to(EpochQuery query) {
switch (query.getType()) {
case BLOCK_HASH:
return EpochRequest.newBuilder()
.setBlockHash(to(query.getBlockHashInput())).build();
case RELATIVE_EPOCH:
return EpochRequest.newBuilder()
.setRelativeEpoch(
EpochRequest.RelativeEpoch.newBuilder()
.setGenesisIndex(GenesisIndex.newBuilder().setValue(query.getGenesisIndex()).build())
.setEpoch(com.concordium.grpc.v2.Epoch.newBuilder().setValue(query.getEpoch().getValue().getValue()).build())
.build()
).build();
default:
throw new IllegalArgumentException("Unexpected EpochQuery");
}
}
}
Loading

0 comments on commit 48d1f54

Please sign in to comment.