Skip to content

Commit 7ab9d75

Browse files
authored
Merge pull request #293 from Concordium/release/9.5
Release/9.5
2 parents cedff58 + 0a3526b commit 7ab9d75

File tree

13 files changed

+870
-183
lines changed

13 files changed

+870
-183
lines changed

.github/workflows/pipeline.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ name: Build, lint and typecheck examples
33
on:
44
# Triggers the workflow on push or pull request events but only for the main branch
55
push:
6-
branches: [main, release**]
6+
branches: [main, release**, feature**]
77
pull_request:
8-
branches: [main, release**]
8+
branches: [main, release**, feature**]
99
# Don't run on draft PR's, see: https://github.com/orgs/community/discussions/25722#discussioncomment-3248917
1010
types: [opened, synchronize, reopened, ready_for_review]
1111
# Allows us to run the workflow manually from the Actions tab

packages/common/CHANGELOG.md

Lines changed: 153 additions & 138 deletions
Large diffs are not rendered by default.

packages/common/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@concordium/common-sdk",
3-
"version": "9.4.0",
3+
"version": "9.5.0",
44
"license": "Apache-2.0",
55
"engines": {
66
"node": ">=14.16.0"

packages/common/src/GRPCClient.ts

Lines changed: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ export class ConcordiumGRPCClient {
308308
*
309309
* @param transaction the transaction to send to the node
310310
* @param signature the signatures on the signing digest of the transaction
311-
* @returns The transaction hash as a byte array
311+
* @returns The transaction hash as a hex-encoded string
312312
*/
313313
async sendAccountTransaction(
314314
transaction: v1.AccountTransaction,
@@ -1503,6 +1503,126 @@ export class ConcordiumGRPCClient {
15031503
);
15041504
}
15051505

1506+
/**
1507+
* Get the projected earliest time at which a particular baker will be required to bake a block.
1508+
*
1509+
* If the baker is not a baker for the current reward period, this returns a timestamp at the
1510+
* start of the next reward period. If the baker is a baker for the current reward period, the
1511+
* earliest win time is projected from the current round forward, assuming that each round after
1512+
* the last finalized round will take the minimum block time. (If blocks take longer, or timeouts
1513+
* occur, the actual time may be later, and the reported time in subsequent queries may reflect
1514+
* this.) At the end of an epoch (or if the baker is not projected to bake before the end of the
1515+
* epoch) the earliest win time for a (current) baker will be projected as the start of the next
1516+
* epoch. This is because the seed for the leader election is updated at the epoch boundary, and
1517+
* so the winners cannot be predicted beyond that. Note that in some circumstances the returned
1518+
* timestamp can be in the past, especially at the end of an epoch.
1519+
*
1520+
* @throws an `UNAVAILABLE` RPC error if the current consensus version is 0 (prior to protocol version 6), as the endpoint is only supported from consensus version 1 (from protocol version 6).
1521+
*
1522+
* @param {v1.BakerId} baker - The baker that should be queried for.
1523+
*
1524+
* @returns {v1.Timestamp} The projected earliest time at which a particular baker will be required to bake a block, as a unix timestamp in milliseconds.
1525+
*/
1526+
async getBakerEarliestWinTime(baker: v1.BakerId): Promise<v1.Timestamp> {
1527+
const bakerId = {
1528+
value: baker,
1529+
};
1530+
const winTime = await this.client.getBakerEarliestWinTime(bakerId)
1531+
.response;
1532+
return winTime.value;
1533+
}
1534+
1535+
/**
1536+
* For a non-genesis block, this returns the quorum certificate, a timeout
1537+
* certificate (if present) and epoch finalization entry (if present).
1538+
*
1539+
* @throws an `UNIMPLEMENTED` RPC error if the endpoint is not enabled by the node.
1540+
* @throws an `INVALID_ARGUMENT` if the block being pointed to is not a product of ConcordiumBFT
1541+
*
1542+
* @param blockHash optional block hash to get the cryptographic parameters at, otherwise retrieves from last finalized block.
1543+
*
1544+
* @returns the requested block certificates.
1545+
*/
1546+
async getBlockCertificates(
1547+
blockHash?: HexString
1548+
): Promise<v1.BlockCertificates> {
1549+
const blockHashInput = getBlockHashInput(blockHash);
1550+
const blockCertificates = await this.client.getBlockCertificates(
1551+
blockHashInput
1552+
).response;
1553+
return translate.blockCertificates(blockCertificates);
1554+
}
1555+
1556+
/**
1557+
* Get all bakers in the reward period of a block.
1558+
* This endpoint is only supported for protocol version 6 and onwards.
1559+
*
1560+
* @throws an `IllegalArgument` RPC error if the protocol does not support the endpoint.
1561+
*
1562+
* @param blockHash optional block hash to get the cryptographic parameters at, otherwise retrieves from last finalized block.
1563+
*
1564+
* @returns All bakers in the reward period of a block
1565+
*/
1566+
getBakersRewardPeriod(
1567+
blockHash?: HexString
1568+
): AsyncIterable<v1.BakerRewardPeriodInfo> {
1569+
const blockHashInput = getBlockHashInput(blockHash);
1570+
const bakersRewardPeriod =
1571+
this.client.getBakersRewardPeriod(blockHashInput).responses;
1572+
return mapStream(bakersRewardPeriod, translate.bakerRewardPeriodInfo);
1573+
}
1574+
1575+
/**
1576+
* Get the list of bakers that won the lottery in a particular historical epoch (i.e. the
1577+
* last finalized block is in a later epoch). This lists the winners for each round in the
1578+
* epoch, starting from the round after the last block in the previous epoch, running to
1579+
* the round before the first block in the next epoch. It also indicates if a block in each
1580+
* round was included in the finalized chain.
1581+
*
1582+
* The following error cases are possible:
1583+
* @throws a `NOT_FOUND` RPC error if the query specifies an unknown block.
1584+
* @throws an `UNAVAILABLE` RPC error if the query is for an epoch that is not finalized in the current genesis index, or is for a future genesis index.
1585+
* @throws an `INVALID_ARGUMENT` RPC error if the query is for an epoch that is not finalized for a past genesis index.
1586+
* @throws an `INVALID_ARGUMENT` RPC error if the query is for a genesis index at consensus version 0.
1587+
* @throws an `INVALID_ARGUMENT` RPC error if the input `EpochRequest` is malformed.
1588+
* @throws an `UNAVAILABLE` RPC error if the endpoint is disabled on the node.
1589+
*
1590+
* @param {HexString | v1.RelativeEpochRequest } epochRequest - Consists of either a hex-encoded block hash or a relative epoch request consisting of a genesis index and an epoch. If none is passed, it queries the last finalized block.
1591+
*
1592+
* @returns {v1.WinningBaker} A stream of winning bakers for a given epoch.
1593+
*/
1594+
getWinningBakersEpoch(
1595+
epochRequest?: HexString | v1.RelativeEpochRequest
1596+
): AsyncIterable<v1.WinningBaker> {
1597+
const req = getEpochRequest(epochRequest);
1598+
const winningBakers = this.client.getWinningBakersEpoch(req).responses;
1599+
1600+
return mapStream(winningBakers, translate.winningBaker);
1601+
}
1602+
1603+
/**
1604+
* Get the block hash of the first finalized block in a specified epoch.
1605+
*
1606+
* The following error cases are possible:
1607+
* @throws - a `NOT_FOUND` RPC error if the query specifies an unknown block.
1608+
* @throws - an `UNAVAILABLE` RPC error if the query is for an epoch that is not finalized in the current genesis index, or is for a future genesis index.
1609+
* @throws - an `INVALID_ARGUMENT` RPC error if the query is for an epoch with no finalized blocks for a past genesis index.
1610+
* @throws - an `INVALID_ARGUMENT` RPC error if the input `EpochRequest` is malformed.
1611+
* @throws - an `UNAVAILABLE` RPC error if the endpoint is disabled on the node.
1612+
*
1613+
* @param {HexString | v1.RelativeEpochRequest } epochRequest - Consists of either a hex-encoded block hash or a relative epoch request consisting of a genesis index and an epoch. If none is passed, it queries the last finalized block.
1614+
*
1615+
* @returns {HexString} The block hash as a hex encoded string.
1616+
*/
1617+
async getFirstBlockEpoch(
1618+
epochRequest?: HexString | v1.RelativeEpochRequest
1619+
): Promise<HexString> {
1620+
const req = getEpochRequest(epochRequest);
1621+
const blockHash = await this.client.getFirstBlockEpoch(req).response;
1622+
1623+
return translate.unwrapValToHex(blockHash);
1624+
}
1625+
15061626
private async getConsensusHeight() {
15071627
return (await this.getConsensusStatus()).lastFinalizedBlockHeight;
15081628
}
@@ -1602,6 +1722,32 @@ export function getInvokerInput(
16021722
}
16031723
}
16041724

1725+
function getEpochRequest(
1726+
epochRequest?: HexString | v1.RelativeEpochRequest
1727+
): v2.EpochRequest {
1728+
if (
1729+
typeof epochRequest === 'string' ||
1730+
typeof epochRequest === 'undefined'
1731+
) {
1732+
return {
1733+
epochRequestInput: {
1734+
oneofKind: 'blockHash',
1735+
blockHash: getBlockHashInput(epochRequest),
1736+
},
1737+
};
1738+
} else {
1739+
return {
1740+
epochRequestInput: {
1741+
oneofKind: 'relativeEpoch',
1742+
relativeEpoch: {
1743+
genesisIndex: { value: epochRequest.genesisIndex },
1744+
epoch: { value: epochRequest.epoch },
1745+
},
1746+
},
1747+
};
1748+
}
1749+
}
1750+
16051751
function assertValidIp(ip: v1.IpAddressString): void {
16061752
if (!isValidIp(ip)) {
16071753
throw new Error('The input was not a valid ip: ' + ip);

packages/common/src/GRPCTypeTranslation.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ function transPaydayStatus(
343343
lotteryPower: status.lotteryPower,
344344
bakerEquityCapital: unwrap(status.bakerEquityCapital?.value),
345345
delegatedCapital: unwrap(status.delegatedCapital?.value),
346+
commissionRates: trCommissionRates(status.commissionRates),
346347
};
347348
}
348349

@@ -2737,6 +2738,95 @@ export function blockFinalizationSummary(
27372738
}
27382739
}
27392740

2741+
export function blockCertificates(
2742+
certs: v2.BlockCertificates
2743+
): v1.BlockCertificates {
2744+
return {
2745+
...(certs.quorumCertificate !== undefined && {
2746+
quorumCertificate: quorumCertificate(certs.quorumCertificate),
2747+
}),
2748+
...(certs.timeoutCertificate !== undefined && {
2749+
timeoutCertificate: timeoutCertificate(certs.timeoutCertificate),
2750+
}),
2751+
...(certs.epochFinalizationEntry !== undefined && {
2752+
epochFinalizationEntry: epochFinalizationEntry(
2753+
certs.epochFinalizationEntry
2754+
),
2755+
}),
2756+
};
2757+
}
2758+
2759+
export function quorumCertificate(
2760+
cert: v2.QuorumCertificate
2761+
): v1.QuorumCertificate {
2762+
return {
2763+
blockHash: unwrapValToHex(cert.blockHash),
2764+
round: unwrap(cert.round?.value),
2765+
epoch: unwrap(cert.epoch?.value),
2766+
aggregateSignature: unwrapValToHex(cert.aggregateSignature),
2767+
signatories: cert.signatories.map((x) => unwrap(x.value)),
2768+
};
2769+
}
2770+
2771+
export function timeoutCertificate(
2772+
cert: v2.TimeoutCertificate
2773+
): v1.TimeoutCertificate {
2774+
return {
2775+
round: unwrap(cert.round?.value),
2776+
minEpoch: unwrap(cert.minEpoch?.value),
2777+
qcRoundsFirstEpoch: cert.qcRoundsFirstEpoch.map(finalizerRound),
2778+
qcRoundsSecondEpoch: cert.qcRoundsSecondEpoch.map(finalizerRound),
2779+
aggregateSignature: unwrapValToHex(cert.aggregateSignature),
2780+
};
2781+
}
2782+
2783+
export function epochFinalizationEntry(
2784+
cert: v2.EpochFinalizationEntry
2785+
): v1.EpochFinalizationEntry {
2786+
return {
2787+
finalizedQc: quorumCertificate(unwrap(cert.finalizedQc)),
2788+
successorQc: quorumCertificate(unwrap(cert.successorQc)),
2789+
successorProof: unwrapValToHex(cert.successorProof),
2790+
};
2791+
}
2792+
2793+
export function finalizerRound(round: v2.FinalizerRound): v1.FinalizerRound {
2794+
return {
2795+
round: unwrap(round.round?.value),
2796+
finalizers: round.finalizers.map((x) => x.value),
2797+
};
2798+
}
2799+
2800+
export function bakerRewardPeriodInfo(
2801+
bakerRewardPeriod: v2.BakerRewardPeriodInfo
2802+
): v1.BakerRewardPeriodInfo {
2803+
return {
2804+
baker: bakerInfo(unwrap(bakerRewardPeriod.baker)),
2805+
effectiveStake: unwrap(bakerRewardPeriod.effectiveStake?.value),
2806+
commissionRates: trCommissionRates(bakerRewardPeriod.commissionRates),
2807+
equityCapital: unwrap(bakerRewardPeriod.equityCapital?.value),
2808+
delegatedCapital: unwrap(bakerRewardPeriod.equityCapital?.value),
2809+
isFinalizer: bakerRewardPeriod.isFinalizer,
2810+
};
2811+
}
2812+
2813+
export function bakerInfo(bakerInfo: v2.BakerInfo): v1.BakerInfo {
2814+
return {
2815+
bakerId: unwrap(bakerInfo.bakerId?.value),
2816+
electionKey: unwrapValToHex(bakerInfo.electionKey),
2817+
signatureKey: unwrapValToHex(bakerInfo.signatureKey),
2818+
aggregationKey: unwrapValToHex(bakerInfo.aggregationKey),
2819+
};
2820+
}
2821+
2822+
export function winningBaker(winningBaker: v2.WinningBaker): v1.WinningBaker {
2823+
return {
2824+
round: unwrap(winningBaker.round?.value),
2825+
winner: unwrap(winningBaker.winner?.value),
2826+
present: winningBaker.present,
2827+
};
2828+
}
2829+
27402830
// ---------------------------- //
27412831
// --- V1 => V2 translation --- //
27422832
// ---------------------------- //

0 commit comments

Comments
 (0)