Skip to content

Commit

Permalink
Merge pull request #293 from Concordium/release/9.5
Browse files Browse the repository at this point in the history
Release/9.5
  • Loading branch information
soerenbf committed Oct 19, 2023
2 parents cedff58 + 0a3526b commit 7ab9d75
Show file tree
Hide file tree
Showing 13 changed files with 870 additions and 183 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: Build, lint and typecheck examples
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [main, release**]
branches: [main, release**, feature**]
pull_request:
branches: [main, release**]
branches: [main, release**, feature**]
# Don't run on draft PR's, see: https://github.com/orgs/community/discussions/25722#discussioncomment-3248917
types: [opened, synchronize, reopened, ready_for_review]
# Allows us to run the workflow manually from the Actions tab
Expand Down
291 changes: 153 additions & 138 deletions packages/common/CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/common/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@concordium/common-sdk",
"version": "9.4.0",
"version": "9.5.0",
"license": "Apache-2.0",
"engines": {
"node": ">=14.16.0"
Expand Down
148 changes: 147 additions & 1 deletion packages/common/src/GRPCClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ export class ConcordiumGRPCClient {
*
* @param transaction the transaction to send to the node
* @param signature the signatures on the signing digest of the transaction
* @returns The transaction hash as a byte array
* @returns The transaction hash as a hex-encoded string
*/
async sendAccountTransaction(
transaction: v1.AccountTransaction,
Expand Down Expand Up @@ -1503,6 +1503,126 @@ export class ConcordiumGRPCClient {
);
}

/**
* Get the projected earliest time at which a particular baker will be required to bake a block.
*
* If the baker is not a baker for the current reward period, this returns a timestamp at the
* start of the next reward period. 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.) 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. Note that in some circumstances the returned
* timestamp can be in the past, especially at the end of an epoch.
*
* @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).
*
* @param {v1.BakerId} baker - The baker that should be queried for.
*
* @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.
*/
async getBakerEarliestWinTime(baker: v1.BakerId): Promise<v1.Timestamp> {
const bakerId = {
value: baker,
};
const winTime = await this.client.getBakerEarliestWinTime(bakerId)
.response;
return winTime.value;
}

/**
* For a non-genesis block, this returns the quorum certificate, a timeout
* certificate (if present) and epoch finalization entry (if present).
*
* @throws an `UNIMPLEMENTED` RPC error if the endpoint is not enabled by the node.
* @throws an `INVALID_ARGUMENT` if the block being pointed to is not a product of ConcordiumBFT
*
* @param blockHash optional block hash to get the cryptographic parameters at, otherwise retrieves from last finalized block.
*
* @returns the requested block certificates.
*/
async getBlockCertificates(
blockHash?: HexString
): Promise<v1.BlockCertificates> {
const blockHashInput = getBlockHashInput(blockHash);
const blockCertificates = await this.client.getBlockCertificates(
blockHashInput
).response;
return translate.blockCertificates(blockCertificates);
}

/**
* Get all bakers in the reward period of a block.
* This endpoint is only supported for protocol version 6 and onwards.
*
* @throws an `IllegalArgument` RPC error if the protocol does not support the endpoint.
*
* @param blockHash optional block hash to get the cryptographic parameters at, otherwise retrieves from last finalized block.
*
* @returns All bakers in the reward period of a block
*/
getBakersRewardPeriod(
blockHash?: HexString
): AsyncIterable<v1.BakerRewardPeriodInfo> {
const blockHashInput = getBlockHashInput(blockHash);
const bakersRewardPeriod =
this.client.getBakersRewardPeriod(blockHashInput).responses;
return mapStream(bakersRewardPeriod, translate.bakerRewardPeriodInfo);
}

/**
* 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). 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. It also indicates if a block in each
* round was included in the finalized chain.
*
* The following error cases are possible:
* @throws a `NOT_FOUND` RPC error if the query specifies an unknown block.
* @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.
* @throws an `INVALID_ARGUMENT` RPC error if the query is for an epoch that is not finalized for a past genesis index.
* @throws an `INVALID_ARGUMENT` RPC error if the query is for a genesis index at consensus version 0.
* @throws an `INVALID_ARGUMENT` RPC error if the input `EpochRequest` is malformed.
* @throws an `UNAVAILABLE` RPC error if the endpoint is disabled on the node.
*
* @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.
*
* @returns {v1.WinningBaker} A stream of winning bakers for a given epoch.
*/
getWinningBakersEpoch(
epochRequest?: HexString | v1.RelativeEpochRequest
): AsyncIterable<v1.WinningBaker> {
const req = getEpochRequest(epochRequest);
const winningBakers = this.client.getWinningBakersEpoch(req).responses;

return mapStream(winningBakers, translate.winningBaker);
}

/**
* Get the block hash of the first finalized block in a specified epoch.
*
* The following error cases are possible:
* @throws - a `NOT_FOUND` RPC error if the query specifies an unknown block.
* @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.
* @throws - an `INVALID_ARGUMENT` RPC error if the query is for an epoch with no finalized blocks for a past genesis index.
* @throws - an `INVALID_ARGUMENT` RPC error if the input `EpochRequest` is malformed.
* @throws - an `UNAVAILABLE` RPC error if the endpoint is disabled on the node.
*
* @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.
*
* @returns {HexString} The block hash as a hex encoded string.
*/
async getFirstBlockEpoch(
epochRequest?: HexString | v1.RelativeEpochRequest
): Promise<HexString> {
const req = getEpochRequest(epochRequest);
const blockHash = await this.client.getFirstBlockEpoch(req).response;

return translate.unwrapValToHex(blockHash);
}

private async getConsensusHeight() {
return (await this.getConsensusStatus()).lastFinalizedBlockHeight;
}
Expand Down Expand Up @@ -1602,6 +1722,32 @@ export function getInvokerInput(
}
}

function getEpochRequest(
epochRequest?: HexString | v1.RelativeEpochRequest
): v2.EpochRequest {
if (
typeof epochRequest === 'string' ||
typeof epochRequest === 'undefined'
) {
return {
epochRequestInput: {
oneofKind: 'blockHash',
blockHash: getBlockHashInput(epochRequest),
},
};
} else {
return {
epochRequestInput: {
oneofKind: 'relativeEpoch',
relativeEpoch: {
genesisIndex: { value: epochRequest.genesisIndex },
epoch: { value: epochRequest.epoch },
},
},
};
}
}

function assertValidIp(ip: v1.IpAddressString): void {
if (!isValidIp(ip)) {
throw new Error('The input was not a valid ip: ' + ip);
Expand Down
90 changes: 90 additions & 0 deletions packages/common/src/GRPCTypeTranslation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ function transPaydayStatus(
lotteryPower: status.lotteryPower,
bakerEquityCapital: unwrap(status.bakerEquityCapital?.value),
delegatedCapital: unwrap(status.delegatedCapital?.value),
commissionRates: trCommissionRates(status.commissionRates),
};
}

Expand Down Expand Up @@ -2737,6 +2738,95 @@ export function blockFinalizationSummary(
}
}

export function blockCertificates(
certs: v2.BlockCertificates
): v1.BlockCertificates {
return {
...(certs.quorumCertificate !== undefined && {
quorumCertificate: quorumCertificate(certs.quorumCertificate),
}),
...(certs.timeoutCertificate !== undefined && {
timeoutCertificate: timeoutCertificate(certs.timeoutCertificate),
}),
...(certs.epochFinalizationEntry !== undefined && {
epochFinalizationEntry: epochFinalizationEntry(
certs.epochFinalizationEntry
),
}),
};
}

export function quorumCertificate(
cert: v2.QuorumCertificate
): v1.QuorumCertificate {
return {
blockHash: unwrapValToHex(cert.blockHash),
round: unwrap(cert.round?.value),
epoch: unwrap(cert.epoch?.value),
aggregateSignature: unwrapValToHex(cert.aggregateSignature),
signatories: cert.signatories.map((x) => unwrap(x.value)),
};
}

export function timeoutCertificate(
cert: v2.TimeoutCertificate
): v1.TimeoutCertificate {
return {
round: unwrap(cert.round?.value),
minEpoch: unwrap(cert.minEpoch?.value),
qcRoundsFirstEpoch: cert.qcRoundsFirstEpoch.map(finalizerRound),
qcRoundsSecondEpoch: cert.qcRoundsSecondEpoch.map(finalizerRound),
aggregateSignature: unwrapValToHex(cert.aggregateSignature),
};
}

export function epochFinalizationEntry(
cert: v2.EpochFinalizationEntry
): v1.EpochFinalizationEntry {
return {
finalizedQc: quorumCertificate(unwrap(cert.finalizedQc)),
successorQc: quorumCertificate(unwrap(cert.successorQc)),
successorProof: unwrapValToHex(cert.successorProof),
};
}

export function finalizerRound(round: v2.FinalizerRound): v1.FinalizerRound {
return {
round: unwrap(round.round?.value),
finalizers: round.finalizers.map((x) => x.value),
};
}

export function bakerRewardPeriodInfo(
bakerRewardPeriod: v2.BakerRewardPeriodInfo
): v1.BakerRewardPeriodInfo {
return {
baker: bakerInfo(unwrap(bakerRewardPeriod.baker)),
effectiveStake: unwrap(bakerRewardPeriod.effectiveStake?.value),
commissionRates: trCommissionRates(bakerRewardPeriod.commissionRates),
equityCapital: unwrap(bakerRewardPeriod.equityCapital?.value),
delegatedCapital: unwrap(bakerRewardPeriod.equityCapital?.value),
isFinalizer: bakerRewardPeriod.isFinalizer,
};
}

export function bakerInfo(bakerInfo: v2.BakerInfo): v1.BakerInfo {
return {
bakerId: unwrap(bakerInfo.bakerId?.value),
electionKey: unwrapValToHex(bakerInfo.electionKey),
signatureKey: unwrapValToHex(bakerInfo.signatureKey),
aggregationKey: unwrapValToHex(bakerInfo.aggregationKey),
};
}

export function winningBaker(winningBaker: v2.WinningBaker): v1.WinningBaker {
return {
round: unwrap(winningBaker.round?.value),
winner: unwrap(winningBaker.winner?.value),
present: winningBaker.present,
};
}

// ---------------------------- //
// --- V1 => V2 translation --- //
// ---------------------------- //
Expand Down

0 comments on commit 7ab9d75

Please sign in to comment.