diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index 8a2f9e205..b499b69e0 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -12,7 +12,6 @@ - `@concordium/common-sdk/cis4` entrypoint exposes functionality for working with contracts adhering to the [CIS-4](https://proposals.concordium.software/CIS/cis-4.html) standard. - `@concordium/common-sdk/grpc` entrypoint exposes the grpc client for interacting with a nodes GRPCv2 interface. - `@concordium/common-sdk/id` entrypoint exposes functionality for working with ID proofs. - - `@concordium/common-sdk/json-rpc` entrypoint exposes the **(deprecated)** json-rpc client for interacting with a nodes GPRCv1 interface. - `@concordium/common-sdk/schema` entrypoint exposes functionality for working with smart contract schemas, i.e.(de)serializing types using a smart contract schema. - This uses the wasm entrypoint at `@concordium/rust-bindings/dapp`. - `@concordium/common-sdk/types` entrypoint exposes functionality for working with concordium domain types. @@ -23,7 +22,6 @@ - For TypeScript projects the minimum required version of typescript is: - NodeJS: 4.7, `"moduleResolution": "node16" // or "nodenext"` - Bundled applications (webpack, esbuild, rollup, etc...): 5.0, `"moduleResolution": "bundler"` - - The following functions now parse using `json-bigint` meaning that they return bigints instead of numbers _for all numbers no matter size_ - `deserializeContractState` - `deserializeReceiveReturnValue` @@ -56,6 +54,7 @@ Several types have been replaced with a module containing the type itself togeth - `CredentialRegistrationId` is now a module with functions related to credential registration IDs: - To refer to `CredentialRegistrationId` as a type use `CredentialRegistrationId.Type`. - Constructing `new CredentialRegistrationId("")` is now `CredentialRegistrationId.fromHexString("")`. +- Removed `JsonRpcClient` and types and functionality associated solely with this class. - Renamed `AccountSequenceNumber` module to `SequenceNumber`. - Fix type for `TranferredEvent` from `ContractTraceEvent` to only be from contract addresses to account addresses. @@ -70,6 +69,21 @@ Several types have been replaced with a module containing the type itself togeth - `ReceiveName` is now a module with functions related to receive-function names of a smart contract. - `ReturnValue` is now a module with functions related to return values from invoking a smart contract. +### Changes + +- Added version discriminators to types versioned by the protocol version of Concordium nodes: + - `MintDistribution` + - `GasRewards` + - `RewardParameters` + - `ChainParameters` + - `Authorizations` + - `RewardStatus` + - `BlockInfo` + - `ConsensusStatus` + - `AccountBakerDetails` + - `ElectionInfo` +- Added type discriminator to different forms of `AccountInfo`. + ## 9.4.0 ### Added diff --git a/packages/common/src/accountHelpers.ts b/packages/common/src/accountHelpers.ts index 490e87ef0..074aea5fe 100644 --- a/packages/common/src/accountHelpers.ts +++ b/packages/common/src/accountHelpers.ts @@ -4,25 +4,44 @@ import { AccountInfo, AccountInfoBaker, AccountInfoDelegator, + StakePendingChange, + AccountInfoType, + StakePendingChangeType, } from './types.js'; -/** Whether {@link AccountInfo} parameter given is of type {@link AccountInfoDelegator}, i.e. the account is a delegator */ +/** + * Whether {@link AccountInfo} parameter given is of type {@link AccountInfoDelegator}, i.e. the account is a delegator + * + * @deprecated check `type` member instead. + */ export const isDelegatorAccount = ( ai: AccountInfo -): ai is AccountInfoDelegator => - (ai as AccountInfoDelegator).accountDelegation !== undefined; +): ai is AccountInfoDelegator => ai.type === AccountInfoType.Delegator; -/** Whether {@link AccountInfo} parameter given is of type {@link AccountInfoBaker}, i.e. the account is a baker. */ +/** + * Whether {@link AccountInfo} parameter given is of type {@link AccountInfoBaker}, i.e. the account is a baker. + * + * @deprecated check `type` member instead. + */ export const isBakerAccount = (ai: AccountInfo): ai is AccountInfoBaker => - (ai as AccountInfoBaker).accountBaker !== undefined; + ai.type === AccountInfoType.Baker; -/** Whether the pending change given is of type {@link ReduceStakePendingChange} */ +/** + * Whether the pending change given is of type {@link ReduceStakePendingChange} + * + * @deprecated check `change` member instead. + */ export const isReduceStakePendingChange = ( - spc: ReduceStakePendingChange | RemovalPendingChange + spc: StakePendingChange ): spc is ReduceStakePendingChange => - (spc as ReduceStakePendingChange).newStake !== undefined; + spc.change === StakePendingChangeType.ReduceStake; -/** Whether the pending change given is of type {@link RemovalPendingChange} */ +/** + * Whether the pending change given is of type {@link RemovalPendingChange} + * + * @deprecated check `change` member instead. + */ export const isRemovalPendingChange = ( - spc: ReduceStakePendingChange | RemovalPendingChange -): spc is RemovalPendingChange => !isReduceStakePendingChange(spc); + spc: StakePendingChange +): spc is RemovalPendingChange => + spc.change === StakePendingChangeType.RemoveStake; diff --git a/packages/common/src/blockSummaryHelpers.ts b/packages/common/src/blockSummaryHelpers.ts deleted file mode 100644 index 31884d020..000000000 --- a/packages/common/src/blockSummaryHelpers.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { - BlockSummary, - BlockSummaryV0, - BlockSummaryV1, - BlockSummaryV2, - UpdateQueues, - UpdateQueuesV0, - UpdateQueuesV1, - UpdateQueuesV2, - Updates, - UpdatesV0, - UpdatesV1, - UpdatesV2, -} from './types.js'; - -/** Whether {@link UpdateQueues} parameter given is of type {@link UpdateQueuesV0} */ -export const isUpdateQueuesV0 = (uq: UpdateQueues): uq is UpdateQueuesV0 => - (uq as UpdateQueuesV0).bakerStakeThreshold !== undefined; - -/** Whether {@link UpdateQueues} parameter given is of type {@link UpdateQueuesV1} */ -export const isUpdateQueuesV1 = (uq: UpdateQueues): uq is UpdateQueuesV1 => - (uq as UpdateQueuesV1).timeParameters !== undefined; - -/** Whether {@link UpdateQueues} parameter given is of type {@link UpdateQueuesV2} */ -export const isUpdateQueuesV2 = (uq: UpdateQueues): uq is UpdateQueuesV2 => - (uq as UpdateQueuesV2).consensus2TimingParameters !== undefined; - -export const isUpdatesV0 = (u: Updates): u is UpdatesV0 => - isUpdateQueuesV0(u.updateQueues); - -export const isUpdatesV1 = (u: Updates): u is UpdatesV1 => - isUpdateQueuesV1(u.updateQueues); - -export const isUpdatesV2 = (u: Updates): u is UpdatesV2 => - isUpdateQueuesV2(u.updateQueues); - -export const isBlockSummaryV0 = (bs: BlockSummary): bs is BlockSummaryV0 => - bs.protocolVersion === undefined || bs.protocolVersion <= 3n; - -export const isBlockSummaryV1 = (bs: BlockSummary): bs is BlockSummaryV1 => - bs.protocolVersion !== undefined && - bs.protocolVersion > 3n && - bs.protocolVersion <= 5n; - -export const isBlockSummaryV2 = (bs: BlockSummary): bs is BlockSummaryV2 => - bs.protocolVersion !== undefined && bs.protocolVersion > 5n; diff --git a/packages/common/src/grpc/translation.ts b/packages/common/src/grpc/translation.ts index 824b384b2..272c50257 100644 --- a/packages/common/src/grpc/translation.ts +++ b/packages/common/src/grpc/translation.ts @@ -187,7 +187,7 @@ function trTimestamp(timestamp: v2.Timestamp | undefined): Date { function trPendingChange( pendingChange: v2.StakePendingChange | undefined -): v1.StakePendingChangeV1 { +): v1.StakePendingChange { const change = unwrap(pendingChange?.change); if (change.oneofKind === 'reduce') { return { @@ -198,7 +198,7 @@ function trPendingChange( } else if (change.oneofKind === 'remove') { return { effectiveTime: trTimestamp(change.remove), - change: v1.StakePendingChangeType.RemoveStakeV1, + change: v1.StakePendingChangeType.RemoveStake, }; } else { throw Error( @@ -241,24 +241,35 @@ function trOpenStatus( function trBaker(baker: v2.AccountStakingInfo_Baker): v1.AccountBakerDetails { const bakerInfo = baker.bakerInfo; - const bakerPoolInfo: v1.BakerPoolInfo = { - openStatus: trOpenStatus(baker.poolInfo?.openStatus), - metadataUrl: unwrap(baker.poolInfo?.url), - commissionRates: trCommissionRates(baker.poolInfo?.commissionRates), - }; - return { + + const v0: v1.AccountBakerDetails = { + version: 0, restakeEarnings: baker.restakeEarnings, - bakerId: unwrap(baker.bakerInfo?.bakerId?.value), + bakerId: unwrap(bakerInfo?.bakerId?.value), bakerAggregationVerifyKey: unwrapValToHex(bakerInfo?.aggregationKey), bakerElectionVerifyKey: unwrapValToHex(baker.bakerInfo?.electionKey), bakerSignatureVerifyKey: unwrapValToHex(bakerInfo?.signatureKey), - bakerPoolInfo: bakerPoolInfo, stakedAmount: unwrap(baker.stakedAmount?.value), // Set the following value if baker.pendingChange is set to true ...(baker.pendingChange && { pendingChange: trPendingChange(baker.pendingChange), }), }; + + if (baker.poolInfo === undefined) { + return v0; + } + + const bakerPoolInfo: v1.BakerPoolInfo = { + openStatus: trOpenStatus(baker.poolInfo?.openStatus), + metadataUrl: unwrap(baker.poolInfo?.url), + commissionRates: trCommissionRates(baker.poolInfo?.commissionRates), + }; + return { + ...v0, + version: 1, + bakerPoolInfo: bakerPoolInfo, + }; } function trHigherLevelKeysUpdate( @@ -378,6 +389,7 @@ export function accountInfo(acc: v2.AccountInfo): v1.AccountInfo { schedule: unwrap(acc.schedule?.schedules).map(trRelease), }; const accInfoCommon: v1.AccountInfoSimple = { + type: v1.AccountInfoType.Simple, accountAddress: AccountAddress.fromProto(unwrap(acc.address)), accountNonce: SequenceNumber.fromProto(unwrap(acc.sequenceNumber)), accountAmount: unwrap(acc.amount?.value), @@ -392,11 +404,13 @@ export function accountInfo(acc: v2.AccountInfo): v1.AccountInfo { if (acc.stake?.stakingInfo.oneofKind === 'delegator') { return { ...accInfoCommon, + type: v1.AccountInfoType.Delegator, accountDelegation: trDelegator(acc.stake.stakingInfo.delegator), }; } else if (acc.stake?.stakingInfo.oneofKind === 'baker') { return { ...accInfoCommon, + type: v1.AccountInfoType.Baker, accountBaker: trBaker(acc.stake.stakingInfo.baker), }; } else { @@ -428,13 +442,16 @@ function trChainParametersV0(v0: v2.ChainParametersV0): v1.ChainParametersV0 { const commonRewardParameters = translateRewardParametersCommon(v0); return { ...common, + version: 0, level2Keys: trAuthorizationsV0(unwrap(v0.level2Keys)), electionDifficulty: trAmountFraction(v0.electionDifficulty?.value), bakerCooldownEpochs: unwrap(v0.bakerCooldownEpochs?.value), minimumThresholdForBaking: unwrap(v0.minimumThresholdForBaking?.value), rewardParameters: { + version: 0, ...commonRewardParameters, gASRewards: { + version: 0, baker: trAmountFraction(v0.gasRewards?.baker), finalizationProof: trAmountFraction( v0.gasRewards?.finalizationProof @@ -445,6 +462,7 @@ function trChainParametersV0(v0: v2.ChainParametersV0): v1.ChainParametersV0 { chainUpdate: trAmountFraction(v0.gasRewards?.chainUpdate), }, mintDistribution: { + version: 0, bakingReward: trAmountFraction( v0.mintDistribution?.bakingReward ), @@ -464,6 +482,7 @@ function trChainParametersV1( const commonRewardParameters = translateRewardParametersCommon(params); return { ...common, + version: 1, level2Keys: trAuthorizationsV1(unwrap(params.level2Keys)), electionDifficulty: trAmountFraction(params.electionDifficulty?.value), rewardPeriodLength: unwrap( @@ -503,7 +522,9 @@ function trChainParametersV1( leverageBound: unwrap(params.poolParameters?.leverageBound?.value), rewardParameters: { ...commonRewardParameters, + version: 1, gASRewards: { + version: 0, baker: trAmountFraction(params.gasRewards?.baker), finalizationProof: trAmountFraction( params.gasRewards?.finalizationProof @@ -514,6 +535,7 @@ function trChainParametersV1( chainUpdate: trAmountFraction(params.gasRewards?.chainUpdate), }, mintDistribution: { + version: 1, bakingReward: trAmountFraction( params.mintDistribution?.bakingReward ), @@ -533,6 +555,7 @@ function trChainParametersV2( return { ...common, + version: 2, level2Keys: trAuthorizationsV1(unwrap(params.level2Keys)), rewardPeriodLength: unwrap( params.timeParameters?.rewardPeriodLength?.value?.value @@ -571,7 +594,9 @@ function trChainParametersV2( leverageBound: unwrap(params.poolParameters?.leverageBound?.value), rewardParameters: { ...commonRewardParameters, + version: 2, gASRewards: { + version: 1, baker: trAmountFraction(params.gasRewards?.baker), accountCreation: trAmountFraction( params.gasRewards?.accountCreation @@ -579,6 +604,7 @@ function trChainParametersV2( chainUpdate: trAmountFraction(params.gasRewards?.chainUpdate), }, mintDistribution: { + version: 1, bakingReward: trAmountFraction( params.mintDistribution?.bakingReward ), @@ -676,6 +702,7 @@ export function tokenomicsInfo(info: v2.TokenomicsInfo): v1.RewardStatus { case 'v0': { const v0 = info.tokenomics.v0; return { + version: 0, protocolVersion: translateProtocolVersion(v0.protocolVersion), totalAmount: unwrap(v0.totalAmount?.value), totalEncryptedAmount: unwrap(v0.totalEncryptedAmount?.value), @@ -689,6 +716,7 @@ export function tokenomicsInfo(info: v2.TokenomicsInfo): v1.RewardStatus { case 'v1': { const v1 = info.tokenomics.v1; return { + version: 1, protocolVersion: translateProtocolVersion(v1.protocolVersion), totalAmount: unwrap(v1.totalAmount?.value), totalEncryptedAmount: unwrap(v1.totalEncryptedAmount?.value), @@ -767,6 +795,7 @@ export function consensusInfo(ci: v2.ConsensusInfo): v1.ConsensusStatus { if (ci.protocolVersion < v2.ProtocolVersion.PROTOCOL_VERSION_6) { const ci0: v1.ConsensusStatusV0 = { ...common, + version: 0, slotDuration: Duration.fromProto(unwrap(ci.slotDuration)), }; @@ -775,6 +804,7 @@ export function consensusInfo(ci: v2.ConsensusInfo): v1.ConsensusStatus { const ci1: v1.ConsensusStatusV1 = { ...common, + version: 1, concordiumBFTStatus: { currentTimeoutDuration: Duration.fromProto( unwrap(ci.currentTimeoutDuration) @@ -1010,7 +1040,7 @@ function trDelegationEvent( const stakeIncr = event.delegationStakeIncreased; return { tag: v1.TransactionEventTag.DelegationStakeIncreased, - delegatorId: Number(unwrap(stakeIncr.delegatorId?.id?.value)), + delegatorId: unwrap(stakeIncr.delegatorId?.id?.value), newStake: unwrap(stakeIncr.newStake?.value), account, }; @@ -1019,7 +1049,7 @@ function trDelegationEvent( const stakeDecr = event.delegationStakeDecreased; return { tag: v1.TransactionEventTag.DelegationStakeDecreased, - delegatorId: Number(unwrap(stakeDecr.delegatorId?.id?.value)), + delegatorId: unwrap(stakeDecr.delegatorId?.id?.value), newStake: unwrap(stakeDecr.newStake?.value), account, }; @@ -1028,7 +1058,7 @@ function trDelegationEvent( const restake = event.delegationSetRestakeEarnings; return { tag: v1.TransactionEventTag.DelegationSetRestakeEarnings, - delegatorId: Number(unwrap(restake.delegatorId?.id?.value)), + delegatorId: unwrap(restake.delegatorId?.id?.value), restakeEarnings: unwrap(restake.restakeEarnings), account, }; @@ -1037,7 +1067,7 @@ function trDelegationEvent( const target = event.delegationSetDelegationTarget; return { tag: v1.TransactionEventTag.DelegationSetDelegationTarget, - delegatorId: Number(unwrap(target.delegatorId?.id?.value)), + delegatorId: unwrap(target.delegatorId?.id?.value), delegationTarget: trDelegTarget(target.delegationTarget), account, }; @@ -1045,13 +1075,13 @@ function trDelegationEvent( case 'delegationAdded': return { tag: v1.TransactionEventTag.DelegationAdded, - delegatorId: Number(unwrap(event.delegationAdded.id?.value)), + delegatorId: unwrap(event.delegationAdded.id?.value), account, }; case 'delegationRemoved': return { tag: v1.TransactionEventTag.DelegationRemoved, - delegatorId: Number(unwrap(event.delegationRemoved.id?.value)), + delegatorId: unwrap(event.delegationRemoved.id?.value), account, }; default: @@ -1332,6 +1362,7 @@ function trGasRewardsUpdate(gasRewards: v2.GasRewards): v1.GasRewardsV0Update { return { updateType: v1.UpdateType.GasRewards, update: { + version: 0, baker: trAmountFraction(gasRewards.baker), accountCreation: trAmountFraction(gasRewards.accountCreation), chainUpdate: trAmountFraction(gasRewards.accountCreation), @@ -1346,6 +1377,7 @@ function trGasRewardsCpv2Update( return { updateType: v1.UpdateType.GasRewardsCpv2, update: { + version: 1, baker: trAmountFraction(gasRewards.baker), accountCreation: trAmountFraction(gasRewards.accountCreation), chainUpdate: trAmountFraction(gasRewards.accountCreation), @@ -1494,6 +1526,7 @@ function trMintDistributionCpv0Update( return { updateType: v1.UpdateType.MintDistribution, update: { + version: 0, bakingReward: trAmountFraction(mintDist.bakingReward), finalizationReward: trAmountFraction(mintDist.finalizationReward), mintPerSlot: trMintRate(mintDist.mintPerSlot), @@ -1507,6 +1540,7 @@ function trMintDistributionCpv1Update( return { updateType: v1.UpdateType.MintDistribution, update: { + version: 1, bakingReward: trAmountFraction(mintDist.bakingReward), finalizationReward: trAmountFraction(mintDist.finalizationReward), }, @@ -1743,6 +1777,7 @@ function trKeyUpdate(keyUpdate: v2.RootUpdate | v2.Level1Update): v1.KeyUpdate { typeOfUpdate: v1.AuthorizationKeysUpdateType.Level2KeysUpdateV1, updatePayload: { ...trAuthorizationsV0(v0), + version: 1, cooldownParameters: trAccessStructure( update.parameterCooldown ), @@ -1757,6 +1792,7 @@ function trKeyUpdate(keyUpdate: v2.RootUpdate | v2.Level1Update): v1.KeyUpdate { function trAuthorizationsV0(auths: v2.AuthorizationsV0): v1.AuthorizationsV0 { return { + version: 0, keys: auths.keys.map(trUpdatePublicKey), addIdentityProvider: trAccessStructure(auths.addIdentityProvider), addAnonymityRevoker: trAccessStructure(auths.addAnonymityRevoker), @@ -1778,6 +1814,7 @@ function trAuthorizationsV0(auths: v2.AuthorizationsV0): v1.AuthorizationsV0 { function trAuthorizationsV1(auths: v2.AuthorizationsV1): v1.AuthorizationsV1 { return { ...trAuthorizationsV0(unwrap(auths.v0)), + version: 1, cooldownParameters: trAccessStructure(auths.parameterCooldown), timeParameters: trAccessStructure(auths.parameterTime), }; @@ -2357,6 +2394,7 @@ export function blockInfo(blockInfo: v2.BlockInfo): v1.BlockInfo { if (blockInfo.protocolVersion < v2.ProtocolVersion.PROTOCOL_VERSION_6) { const bi0: v1.BlockInfoV0 = { ...common, + version: 0, blockSlot: unwrap(blockInfo.slotNumber?.value), }; @@ -2365,6 +2403,7 @@ export function blockInfo(blockInfo: v2.BlockInfo): v1.BlockInfo { const bi1: v1.BlockInfoV1 = { ...common, + version: 1, round: unwrap(blockInfo.round?.value), epoch: unwrap(blockInfo.epoch?.value), }; @@ -2410,11 +2449,15 @@ export function electionInfo(electionInfo: v2.ElectionInfo): v1.ElectionInfo { if (electionInfo.electionDifficulty === undefined) { // election difficulty removed in protocol version 6. - return common; + return { + ...common, + version: 1, + }; } return { ...common, + version: 0, electionDifficulty: trAmountFraction( electionInfo.electionDifficulty?.value ), diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 782fb1518..608f3742e 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -2,9 +2,9 @@ export * from './pub/types.js'; export * from './pub/util.js'; export * from './pub/wasm.js'; export * from './pub/id.js'; -export * from './pub/json-rpc.js'; export * from './pub/grpc.js'; export * from './pub/cis0.js'; export * from './pub/cis2.js'; export * from './pub/cis4.js'; +export * from './pub/schema.js'; export * from './pub/web3-id.js'; diff --git a/packages/common/src/json-rpc/JsonRpcClient.ts b/packages/common/src/json-rpc/JsonRpcClient.ts deleted file mode 100644 index 62121a5d2..000000000 --- a/packages/common/src/json-rpc/JsonRpcClient.ts +++ /dev/null @@ -1,403 +0,0 @@ -import { Buffer } from 'buffer/index.js'; -import { - AccountInfo, - AccountTransaction, - AccountTransactionSignature, - buildInvoker, - ConcordiumBftStatus, - ConsensusStatus, - ConsensusStatusV0, - ConsensusStatusV1, - ContractAddress, - ContractContext, - CryptographicParameters, - InstanceInfo, - InvokeContractResultV1, - NextAccountNonce, - SignedCredentialDeploymentDetails, - TransactionStatus, - TransactionSummary, - Versioned, -} from '../pub/types.js'; -import * as AccountAddress from '../types/AccountAddress.js'; -import * as BlockHash from '../types/BlockHash.js'; -import * as Parameter from '../types/Parameter.js'; -import Provider, { JsonRpcResponse } from './providers/provider.js'; -import { - serializeAccountTransactionForSubmission, - serializeSignedCredentialDeploymentDetailsForSubmission, -} from '../serialization.js'; -import { CcdAmount } from '../types/ccdAmount.js'; -import { ModuleReference } from '../types/moduleReference.js'; -import { buildJsonResponseReviver, intToStringTransformer } from '../util.js'; -import * as CredentialRegistrationId from '../types/CredentialRegistrationId.js'; - -function transformJsonResponse( - jsonString: string, - reviver?: (this: unknown, key: string, value: unknown) => unknown, - transformer?: (json: string) => string -): JsonRpcResponse { - if (transformer) { - const transformedJson = transformer(jsonString); - return JSON.parse(transformedJson, reviver); - } - - return JSON.parse(jsonString, reviver); -} - -/** - * @deprecated This has been deprecated in favor of the {@link ConcordiumGRPCClient} that uses version 2 of the concordium gRPC API - */ -export class JsonRpcClient { - provider: Provider; - - constructor(provider: Provider) { - this.provider = provider; - } - - async getNextAccountNonce( - accountAddress: AccountAddress.Type - ): Promise { - const response = await this.provider.request('getNextAccountNonce', { - address: accountAddress.address, - }); - - const bigIntPropertyKeys: (keyof NextAccountNonce)[] = ['nonce']; - - const res = transformJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - return res.result; - } - - async getTransactionStatus( - transactionHash: string - ): Promise { - const response = await this.provider.request('getTransactionStatus', { - transactionHash: transactionHash, - }); - - // TODO avoid code duplication with nodejs client - const bigIntPropertyKeys: (keyof TransactionSummary)[] = [ - 'cost', - 'energyCost', - 'index', - ]; - - const res = transformJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - return res.result; - } - - /** - * @param serializedTransaction the transaction serialized as a base64-encoded string. - */ - private async sendRawTransaction( - serializedTransaction: string - ): Promise { - const res = await this.provider.request('sendTransaction', { - transaction: serializedTransaction, - }); - return JSON.parse(res).result || false; - } - - async sendAccountTransaction( - accountTransaction: AccountTransaction, - signatures: AccountTransactionSignature - ): Promise { - const serializedAccountTransaction: Buffer = Buffer.from( - serializeAccountTransactionForSubmission( - accountTransaction, - signatures - ) - ); - return this.sendRawTransaction( - serializedAccountTransaction.toString('base64') - ); - } - - async sendCredentialDeployment( - credentialDetails: SignedCredentialDeploymentDetails - ): Promise { - const serializedDetails = - serializeSignedCredentialDeploymentDetailsForSubmission( - credentialDetails - ); - return this.sendRawTransaction(serializedDetails.toString('base64')); - } - - async getConsensusStatus(): Promise { - const response = await this.provider.request('getConsensusStatus'); - type CS = ConsensusStatusV0 & ConsensusStatusV1; - - // TODO Avoid code duplication with nodejs client - const datePropertyKeys: (keyof CS | keyof ConcordiumBftStatus)[] = [ - 'blockLastReceivedTime', - 'blockLastArrivedTime', - 'genesisTime', - 'currentEraGenesisTime', - 'lastFinalizedTime', - - // v1 - 'triggerBlockTime', - ]; - const bigIntPropertyKeys: (keyof CS | keyof ConcordiumBftStatus)[] = [ - 'epochDuration', - 'slotDuration', - 'bestBlockHeight', - 'lastFinalizedBlockHeight', - 'finalizationCount', - 'blocksVerifiedCount', - 'blocksReceivedCount', - 'protocolVersion', - - // v1 - 'currentTimeoutDuration', - 'currentRound', - 'currentEpoch', - ]; - - const res = transformJsonResponse( - response, - buildJsonResponseReviver(datePropertyKeys, bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - - if (!res.result) { - throw new Error( - 'Nothing was returned when trying to get the consensus status.' - ); - } - - return res.result; - } - - /** - * Retrieve information about a given smart contract instance. - * @param blockHash the block hash to get the smart contact instances at - * @param address the address of the smart contract - * @returns A JSON object with information about the contract instance - */ - async getInstanceInfo( - address: ContractAddress.Type, - blockHash?: BlockHash.Type - ): Promise { - if (!blockHash) { - const consensusStatus = await this.getConsensusStatus(); - blockHash = consensusStatus.lastFinalizedBlock; - } - - const response = await this.provider.request('getInstanceInfo', { - index: address.index, - subindex: address.subindex, - blockHash: BlockHash.toHexString(blockHash), - }); - - const result = JSON.parse(response).result; - - if (!result) { - return undefined; - } - - // TODO: Avoid code duplication with nodejs client - const common = { - amount: new CcdAmount(BigInt(result.amount)), - sourceModule: new ModuleReference(result.sourceModule), - owner: AccountAddress.fromBase58(result.owner), - methods: result.methods, - name: result.name, - }; - - switch (result.version) { - case 1: - return { - version: 1, - ...common, - }; - case undefined: - case 0: - return { - version: 0, - ...common, - model: Buffer.from(result.model, 'hex'), - }; - default: - throw new Error( - 'InstanceInfo had unsupported version: ' + - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (result as any).version - ); - } - } - - /** - * Retrieves the account info for the given account. If the provided block - * hash is in a block prior to the finalization of the account, then the account - * information will not be available. - * A credential registration id can also be provided, instead of an address. In this case - * the node will return the account info of the account, which the corresponding credential - * is (or was) deployed to. - * @param accountAddress base58 account address (or a credential registration id) to get the account info for - * @param blockHash the block hash to get the account info at - * @returns the account info for the provided account address, undefined is the account does not exist - */ - async getAccountInfo( - accountAddress: - | string - | AccountAddress.Type - | CredentialRegistrationId.Type, - blockHash?: BlockHash.Type - ): Promise { - if (!blockHash) { - const consensusStatus = await this.getConsensusStatus(); - blockHash = consensusStatus.lastFinalizedBlock; - } - - let address: string; - if (typeof accountAddress === 'string') { - address = accountAddress; - } else if ('address' in accountAddress) { - address = accountAddress.address; - } else if ('credId' in accountAddress) { - address = accountAddress.credId; - } else { - throw new Error('Invalid accountAddress input'); - } - - const response = await this.provider.request('getAccountInfo', { - blockHash: BlockHash.toHexString(blockHash), - address, - }); - - const datePropertyKeys = ['timestamp', 'effectiveTime']; - const bigIntPropertyKeys = [ - 'accountAmount', - 'accountNonce', - 'accountIndex', - 'startIndex', - 'total', - 'amount', - 'stakedAmount', - 'bakerId', - 'newStake', - 'epoch', - ]; - const res = transformJsonResponse( - response, - buildJsonResponseReviver(datePropertyKeys, bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - - return res.result; - } - - /** - * Retrieves the global cryptographic parameters on the blockchain at - * the provided block. - * @param blockHash the block to get the cryptographic parameters at - * @returns the global cryptographic parameters at the given block, or undefined it the block does not exist. - */ - async getCryptographicParameters( - blockHash?: BlockHash.Type - ): Promise | undefined> { - if (!blockHash) { - const consensusStatus = await this.getConsensusStatus(); - blockHash = consensusStatus.lastFinalizedBlock; - } - - const response = await this.provider.request( - 'getCryptographicParameters', - { - blockHash: BlockHash.toHexString(blockHash), - } - ); - - const res = - transformJsonResponse>(response); - - return res.result; - } - - /** - * Retrieves the source of the given module at - * the provided block. - * @param moduleReference the module's reference, which is the hex encoded hash of the source. - * @param blockHash the block to get the cryptographic parameters at - * @returns the source of the module as raw bytes. - */ - async getModuleSource( - moduleReference: ModuleReference, - blockHash?: BlockHash.Type - ): Promise { - if (!blockHash) { - const consensusStatus = await this.getConsensusStatus(); - blockHash = consensusStatus.lastFinalizedBlock; - } - - const response = await this.provider.request('getModuleSource', { - moduleReference: moduleReference.moduleRef, - blockHash: BlockHash.toHexString(blockHash), - }); - - return Buffer.from(JSON.parse(response).result, 'base64'); - } - - /** - * Invokes a smart contract. - * @param context the collection of details used to invoke the contract. Must include the address of the contract and the method invoked. - * @param blockHash the block hash at which the contract should be invoked at. The contract is invoked in the state at the end of this block. - * @returns If the node was able to invoke, then a object describing the outcome is returned. - * The outcome is determined by the `tag` field, which is either `success` or `failure`. - * The `usedEnergy` field will always be present, and is the amount of NRG was used during the execution. - * If the tag is `success`, then an `events` field is present, and it contains the events that would have been generated. - * If invoking a V1 contract and it produces a return value, it will be present in the `returnValue` field. - * If the tag is `failure`, then a `reason` field is present, and it contains the reason the update would have been rejected. - * If either the block does not exist, or then node fails to parse of any of the inputs, then undefined is returned. - */ - async invokeContract( - contractContext: ContractContext, - blockHash?: BlockHash.Type - ): Promise { - if (!blockHash) { - const consensusStatus = await this.getConsensusStatus(); - blockHash = consensusStatus.lastFinalizedBlock; - } - - const invoker = buildInvoker(contractContext.invoker); - - const context = { - ...contractContext, - invoker, - method: contractContext.method.value, - energy: - contractContext.energy === undefined - ? undefined - : contractContext.energy.value, - amount: - contractContext.amount && contractContext.amount.microCcdAmount, - parameter: - contractContext.parameter === undefined - ? undefined - : Parameter.toHexString(contractContext.parameter), - }; - - const response = await this.provider.request('invokeContract', { - blockHash: BlockHash.toHexString(blockHash), - context, - }); - - const bigIntPropertyKeys = ['usedEnergy', 'index', 'subindex']; - const res = transformJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - - return res.result; - } -} diff --git a/packages/common/src/json-rpc/index.ts b/packages/common/src/json-rpc/index.ts deleted file mode 100644 index 9d659060f..000000000 --- a/packages/common/src/json-rpc/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { HttpProvider } from './providers/httpProvider.js'; -export { JsonRpcClient } from './JsonRpcClient.js'; diff --git a/packages/common/src/json-rpc/providers/httpProvider.ts b/packages/common/src/json-rpc/providers/httpProvider.ts deleted file mode 100644 index 4f7e94abe..000000000 --- a/packages/common/src/json-rpc/providers/httpProvider.ts +++ /dev/null @@ -1,71 +0,0 @@ -import Provider, { JsonRpcRequest } from './provider.js'; -import fetch from 'cross-fetch'; -import JSONBig from 'json-bigint'; -import { v4 as uuidv4 } from 'uuid'; - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export class HttpProvider implements Provider { - request: JsonRpcRequest; - cookie?: string; - - /** - * @param internalFetch Fetch function that performs the request. Defaults to using the cross-fetch package. - */ - constructor( - url: string, - internalFetch: typeof fetch = fetch, - onSetCookie?: (cookie: string) => void, - initialCookie?: string, - autoUpdateCookie = true - ) { - this.cookie = initialCookie; - this.request = async function (method, params?) { - const request = { - method: method, - params: params, - id: uuidv4(), - jsonrpc: '2.0', - }; - - const options = { - method: 'POST', - // Use JSONBig in order ensure bigints are automatically parsed (as numbers) - body: JSONBig.stringify(request), - headers: { - 'Content-Type': 'application/json', - ...(this.cookie && { cookie: this.cookie }), - }, - }; - - const res = await internalFetch(url, options); - if (res.status >= 400) { - const json = await res.json(); - if (json.error) { - throw new Error( - `${json.error.code}: ${json.error.message} (id: ${json.id})` - ); - } else { - throw new Error( - `${res.status}: ${res.statusText} (id: ${json.id})` - ); - } - } - - const setCookieValue = res.headers.get('set-cookie'); - if (setCookieValue) { - onSetCookie?.(setCookieValue); - if (autoUpdateCookie) { - this.updateCookie(setCookieValue); - } - } - - return res.text(); - }; - } - - updateCookie(newCookie: string): void { - this.cookie = newCookie; - } -} diff --git a/packages/common/src/json-rpc/providers/provider.ts b/packages/common/src/json-rpc/providers/provider.ts deleted file mode 100644 index 32974fec7..000000000 --- a/packages/common/src/json-rpc/providers/provider.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Invoker } from '../../types.js'; - -/* eslint-disable @typescript-eslint/no-explicit-any */ -interface JsonRpcResponseBase { - jsonrpc: '2.0'; - id: string | null; -} - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export interface JsonRpcResponseError extends JsonRpcResponseBase { - error: { - code: number; - message: string; - data?: any; - }; - result?: never; -} - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export interface JsonRpcResponseSuccess extends JsonRpcResponseBase { - error?: never; - result: Result; -} - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export type JsonRpcResponse = - | JsonRpcResponseError - | JsonRpcResponseSuccess; - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export type JsonRpcRequest = ( - ...args: - | ['getNextAccountNonce', { address: string }] - | ['getTransactionStatus', { transactionHash: string }] - | ['getConsensusStatus'] - | [ - 'getInstanceInfo', - { blockHash: string; index: bigint; subindex: bigint } - ] - | ['sendTransaction', { transaction: string }] - | ['getAccountInfo', { address: string; blockHash: string }] - | ['getCryptographicParameters', { blockHash: string }] - | ['getModuleSource', { blockHash: string; moduleReference: string }] - | [ - 'invokeContract', - { - blockHash: string; - context: { - contract: { index: bigint; subindex: bigint }; - method: string; - amount?: bigint; - invoker: Invoker; - energy?: bigint; - parameter?: string; - }; - } - ] -) => Promise; - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export default interface Provider { - request: JsonRpcRequest; -} diff --git a/packages/common/src/pub/json-rpc.ts b/packages/common/src/pub/json-rpc.ts deleted file mode 100644 index 65ea46d2f..000000000 --- a/packages/common/src/pub/json-rpc.ts +++ /dev/null @@ -1,2 +0,0 @@ -// The JSON-RPC client -export * from '../json-rpc/index.js'; diff --git a/packages/common/src/pub/types.ts b/packages/common/src/pub/types.ts index 18b365cc8..9074e03ea 100644 --- a/packages/common/src/pub/types.ts +++ b/packages/common/src/pub/types.ts @@ -6,7 +6,6 @@ export { getCredentialDeploymentSignDigest, getCredentialForExistingAccountSignDigest, serializeAccountTransactionForSubmission, - getSignedCredentialDeploymentTransactionHash, serializeAccountTransactionPayload, serializeAccountTransaction, } from '../serialization.js'; @@ -28,8 +27,6 @@ export { deserializeAccountTransaction } from '../deserialization.js'; export * from '../signHelpers.js'; export * from '../versionedTypeHelpers.js'; export * from '../accountHelpers.js'; -export * from '../blockSummaryHelpers.js'; -export * from '../rewardStatusHelpers.js'; export { isHex, streamToList, wasmToSchema, unwrap } from '../util.js'; diff --git a/packages/common/src/pub/util.ts b/packages/common/src/pub/util.ts index 014c2fe26..3bc1944ca 100644 --- a/packages/common/src/pub/util.ts +++ b/packages/common/src/pub/util.ts @@ -1,7 +1,2 @@ // Utility functions needed in web/nodejs packages -export { - buildJsonResponseReviver, - intToStringTransformer, - isValidHash, - stringToInt, -} from '../util.js'; +export { isValidHash, stringToInt } from '../util.js'; diff --git a/packages/common/src/rewardStatusHelpers.ts b/packages/common/src/rewardStatusHelpers.ts deleted file mode 100644 index b82c7ba21..000000000 --- a/packages/common/src/rewardStatusHelpers.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { RewardStatus, RewardStatusV1 } from './types.js'; - -export function isRewardStatusV1(rs: RewardStatus): rs is RewardStatusV1 { - return rs.protocolVersion !== undefined && rs.protocolVersion > 3n; -} diff --git a/packages/common/src/serialization.ts b/packages/common/src/serialization.ts index d2964ec51..c922cf655 100644 --- a/packages/common/src/serialization.ts +++ b/packages/common/src/serialization.ts @@ -23,7 +23,6 @@ import { UnsignedCredentialDeploymentInformation, CredentialDeploymentInfo, CredentialDeploymentDetails, - SignedCredentialDeploymentDetails, } from './types.js'; import { calculateEnergyCost } from './energyCost.js'; import { countSignatures } from './util.js'; @@ -403,50 +402,3 @@ export function getCredentialDeploymentSignDigest( encodeWord64(credentialDeployment.expiry.expiryEpochSeconds), ]); } - -/** - * @deprecated the SignedCredentialDeploymentDetails is only used with JSON-RPC - */ -function serializeSignedCredentialDeploymentDetails( - credentialDetails: SignedCredentialDeploymentDetails -): Buffer { - const serializedBlockItemKind = encodeWord8( - BlockItemKind.CredentialDeploymentKind - ); - const serializedExpiry = encodeWord64( - credentialDetails.expiry.expiryEpochSeconds - ); - const serializedCredentialKind = encodeWord8(1); - const serializedInfo: Buffer = Buffer.from( - serializeCredentialDeploymentInfo(credentialDetails.cdi) - ); - return Buffer.concat([ - serializedBlockItemKind, - serializedExpiry, - serializedCredentialKind, - serializedInfo, - ]); -} - -/** - * @deprecated the SignedCredentialDeploymentDetails is only used with JSON-RPC - */ -export function serializeSignedCredentialDeploymentDetailsForSubmission( - credentialDetails: SignedCredentialDeploymentDetails -): Buffer { - const serializedVersion = encodeWord8(0); - const serializedDetails = - serializeSignedCredentialDeploymentDetails(credentialDetails); - return Buffer.concat([serializedVersion, serializedDetails]); -} - -/** - * @deprecated the SignedCredentialDeploymentDetails is only used with JSON-RPC - */ -export function getSignedCredentialDeploymentTransactionHash( - credentialDetails: SignedCredentialDeploymentDetails -): string { - const serializedDetails = - serializeSignedCredentialDeploymentDetails(credentialDetails); - return sha256([serializedDetails]).toString('hex'); -} diff --git a/packages/common/src/signHelpers.ts b/packages/common/src/signHelpers.ts index cdda9f3b7..dd8c865d7 100644 --- a/packages/common/src/signHelpers.ts +++ b/packages/common/src/signHelpers.ts @@ -3,11 +3,10 @@ import { AccountInfo, AccountTransaction, AccountTransactionSignature, + Base58String, CredentialSignature, HexString, - SimpleAccountKeys, - WalletExportFormat, - WithAccountKeys, + JsonString, } from './types.js'; import { sign, verify } from '@noble/ed25519'; import { Buffer } from 'buffer/index.js'; @@ -15,6 +14,86 @@ import * as AccountAddress from './types/AccountAddress.js'; import { sha256 } from './hash.js'; import { mapRecord } from './util.js'; +export interface KeyPair { + signKey: HexString; + verifyKey: HexString; +} + +export interface CredentialKeys { + keys: Record; + threshold: number; +} + +export interface AccountKeys { + keys: Record; + threshold: number; +} + +export type SimpleAccountKeys = Record>; + +export interface WithAccountKeys { + accountKeys: AccountKeys; +} + +export interface WalletExportFormat { + type: string; + v: number; + environment: string; + value: { + accountKeys: AccountKeys; + address: Base58String; + credentials: Record; + }; +} + +/** + * Parses a wallet export file into a WalletExportFormat. The wallet export + * file is exported from a concordium wallet. + */ +export function parseWallet(walletString: JsonString): WalletExportFormat { + const wallet = JSON.parse(walletString); + console.log(typeof wallet.type); + if (typeof wallet.type !== 'string') { + throw Error( + 'Expected field "type" to be of type "string" but was of type "' + + typeof wallet.type + + '"' + ); + } + if (typeof wallet.v !== 'number') { + throw Error( + 'Expected field "v" to be of type "number" but was of type "' + + typeof wallet.v + + '"' + ); + } + if (typeof wallet.environment !== 'string') { + throw Error( + 'Expected field "environment" to be of type "string" but was of type "' + + typeof wallet.environment + + '"' + ); + } + if (typeof wallet.value.address !== 'string') { + throw Error( + 'Expected field "value.address" to be of type "string" but was of type "' + + typeof wallet.value.address + + '"' + ); + } + if (wallet.value.accountKeys === undefined) { + throw Error( + 'Expected field "value.accountKeys" to be defined, but was not' + ); + } + if (wallet.value.credentials === undefined) { + throw Error( + 'Expected field "value.credentials" to be defined, but was not' + ); + } + return wallet; +} + /** * A structure to use for creating signatures on a given digest. */ diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index ecf7c2706..9933b610e 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -19,13 +19,8 @@ import { CcdAmount } from './types/ccdAmount.js'; import { DataBlob } from './types/DataBlob.js'; import { TransactionExpiry } from './types/transactionExpiry.js'; import { ModuleReference } from './types/moduleReference.js'; -import { RejectReason, RejectReasonV1 } from './types/rejectReason.js'; -import { - ContractTraceEvent, - MemoEvent, - TransactionEvent, - TransferredEvent, -} from './types/transactionEvent.js'; +import { RejectReason } from './types/rejectReason.js'; +import { ContractTraceEvent } from './types/transactionEvent.js'; export * from './types/NodeInfo.js'; export * from './types/PeerInfo.js'; @@ -149,21 +144,6 @@ export type Address = } | AddressAccount; -interface RejectedEventResult { - outcome: 'reject'; - rejectReason: RejectReasonV1; -} - -interface SuccessfulEventResult { - outcome: 'success'; - events: TransactionEvent[]; -} - -export type EventResult = - | SuccessfulEventResult - | TransferWithMemoEventResult - | RejectedEventResult; - export enum TransactionSummaryType { AccountTransaction = 'accountTransaction', CredentialDeploymentTransaction = 'credentialDeploymentTransaction', @@ -194,76 +174,6 @@ export interface BaseTransactionSummary { index: bigint; } -/** - * @deprecated This is type for describing return types from the V1 gRPC client, which has been deprecated - */ -interface GenericTransactionSummary extends BaseTransactionSummary { - type: GenericTransactionSummaryType; - result: EventResult; -} - -/** - * @deprecated This is type for describing return types from the V1 gRPC client, which has been deprecated - */ -interface TransferWithMemoEventResult { - outcome: 'success'; - events: [TransferredEvent, MemoEvent]; -} - -/** - * @deprecated This is type for describing return types from the V1 gRPC client, which has been deprecated - */ -export interface TransferWithMemoTransactionSummary - extends BaseTransactionSummary { - type: TransferWithMemoSummaryType; - result: TransferWithMemoEventResult; -} - -/** - * @deprecated This is helper intented for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type TransactionSummary = - | GenericTransactionSummary - | TransferWithMemoTransactionSummary; - -/** - * @deprecated This is helper for type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export function instanceOfTransferWithMemoTransactionSummary( - object: TransactionSummary -): object is TransferWithMemoTransactionSummary { - return ( - object.type !== undefined && object.type.contents === 'transferWithMemo' - ); -} - -/** - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface TransactionStatus { - status: TransactionStatusEnum; - outcomes?: Record; -} - -/** - * @deprecated This is type describing return types from the V1 gRPC client, which has been deprecated - */ -export interface PartyInfo { - bakerId: bigint; - weight: bigint; - signed: boolean; -} - -/** - * @deprecated This is type describing return types from the V1 gRPC client, which has been deprecated - */ -export interface FinalizationData { - finalizationIndex: bigint; - finalizationDelay: bigint; - finalizationBlockPointer: string; - finalizers: PartyInfo[]; -} - export interface Ratio { numerator: bigint; denominator: bigint; @@ -296,10 +206,13 @@ interface MintDistributionCommon { } export interface MintDistributionV0 extends MintDistributionCommon { + version: 0; mintPerSlot: number; } -export type MintDistributionV1 = MintDistributionCommon; +export interface MintDistributionV1 extends MintDistributionCommon { + version: 1; +} export type MintDistribution = MintDistributionV0 | MintDistributionV1; @@ -315,12 +228,15 @@ export interface GasRewardsCommon { /** Gas rewards properties for protocol version 1-5 ({@link ChainParametersV0} and {@link ChainParametersV1}). */ export interface GasRewardsV0 extends GasRewardsCommon { + version: 0; /** The fractional amount paid for including a finalization proof */ finalizationProof: number; } /** Gas rewards properties from protocol version 6 ({@link ChainParametersV2}). */ -export type GasRewardsV1 = GasRewardsCommon; +export interface GasRewardsV1 extends GasRewardsCommon { + version: 1; +} /** Common reward parameters used across all protocol versions */ export interface RewardParametersCommon { @@ -330,6 +246,7 @@ export interface RewardParametersCommon { /** Reward parameters used from protocol version 1-3 ({@link ChainParametersV0}). */ export interface RewardParametersV0 extends RewardParametersCommon { + version: 0; /** The current mint distribution */ mintDistribution: MintDistributionV0; /** The current gas rewards parameters */ @@ -338,6 +255,7 @@ export interface RewardParametersV0 extends RewardParametersCommon { /** Reward parameters used in protocol versions 4 and 5 ({@link ChainParametersV1}). */ export interface RewardParametersV1 extends RewardParametersCommon { + version: 1; /** The current mint distribution */ mintDistribution: MintDistributionV1; /** The current gas rewards parameters */ @@ -346,6 +264,7 @@ export interface RewardParametersV1 extends RewardParametersCommon { /** Reward parameters used from protocol version 6 ({@link ChainParametersV2}). */ export interface RewardParametersV2 extends RewardParametersCommon { + version: 2; /** The current mint distribution */ mintDistribution: MintDistributionV1; /** The current gas rewards parameters */ @@ -456,8 +375,6 @@ export interface ChainParametersCommon { accountCreationLimit: number; /** The chain foundation account */ foundationAccount: AccountAddress.Type; - /** The chain foundation account index */ - foundationAccountIndex?: bigint; /** Keys allowed to do level1 updates */ level1Keys: KeysWithThreshold; /** Keys allowed to do root updates */ @@ -468,6 +385,7 @@ export interface ChainParametersCommon { export type ChainParametersV0 = ChainParametersCommon & CooldownParametersV0 & PoolParametersV0 & { + version: 0; /** The election difficulty for consensus lottery */ electionDifficulty: number; /** The election difficulty for consensus lottery */ @@ -481,6 +399,7 @@ export type ChainParametersV1 = ChainParametersCommon & CooldownParametersV1 & TimeParametersV1 & PoolParametersV1 & { + version: 1; /** The election difficulty for consensus lottery */ electionDifficulty: number; /** The election difficulty for consensus lottery */ @@ -497,6 +416,7 @@ export type ChainParametersV2 = ChainParametersCommon & FinalizationCommitteeParameters & TimeoutParameters & ConsensusParameters & { + version: 2; /** The election difficulty for consensus lottery */ rewardParameters: RewardParametersV2; /** Keys allowed to do parameter updates */ @@ -539,12 +459,15 @@ interface AuthorizationsCommon { /** * Used from protocol version 1-3 */ -export type AuthorizationsV0 = AuthorizationsCommon; +export interface AuthorizationsV0 extends AuthorizationsCommon { + version: 0; +} /** * Used from protocol version 4 */ export interface AuthorizationsV1 extends AuthorizationsCommon { + version: 1; cooldownParameters: Authorization; timeParameters: Authorization; } @@ -556,177 +479,6 @@ export interface KeysWithThreshold { threshold: number; } -interface KeysCommon { - rootKeys: KeysWithThreshold; - level1Keys: KeysWithThreshold; -} - -/** - * Used from protocol version 1-3 - */ -export interface KeysV0 extends KeysCommon { - level2Keys: AuthorizationsV0; -} - -/** - * Used from protocol version 4 - */ -export interface KeysV1 extends KeysCommon { - level2Keys: AuthorizationsV1; -} - -export type Keys = KeysV0 | KeysV1; - -export interface UpdateQueueQueue { - effectiveTime: Date; - // TODO Update the type of update to a generic update transaction when - // update types have been added. - /** Information about the actual update. */ - update: unknown; -} - -export interface UpdateQueue { - nextSequenceNumber: bigint; - queue: UpdateQueueQueue[]; -} - -interface UpdateQueuesCommon { - microGTUPerEuro: UpdateQueue; - euroPerEnergy: UpdateQueue; - transactionFeeDistribution: UpdateQueue; - foundationAccount: UpdateQueue; - mintDistribution: UpdateQueue; - protocol: UpdateQueue; - gasRewards: UpdateQueue; - addAnonymityRevoker: UpdateQueue; - addIdentityProvider: UpdateQueue; - rootKeys: UpdateQueue; - level1Keys: UpdateQueue; - level2Keys: UpdateQueue; -} - -/** - * Used from protocol version 1-3 - */ -export interface UpdateQueuesV0 extends UpdateQueuesCommon { - electionDifficulty: UpdateQueue; - bakerStakeThreshold: UpdateQueue; -} - -/** - * Used in protocol version 4 and 5 - */ -export interface UpdateQueuesV1 extends UpdateQueuesCommon { - electionDifficulty: UpdateQueue; - cooldownParameters: UpdateQueue; - timeParameters: UpdateQueue; - poolParameters: UpdateQueue; -} - -/** - * Used from protocol version 6 - */ -export interface UpdateQueuesV2 extends UpdateQueuesV1 { - consensus2TimingParameters: UpdateQueue; -} - -export type UpdateQueues = UpdateQueuesV0 | UpdateQueuesV1 | UpdateQueuesV2; - -interface ProtocolUpdate { - message: string; - specificationUrl: string; - specificationHash: string; - specificationAuxiliaryData: string; -} - -interface UpdatesCommon { - protocolUpdate: ProtocolUpdate | undefined; -} - -/** - * Used from protocol version 1-3 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface UpdatesV0 extends UpdatesCommon { - chainParameters: Omit< - ChainParametersV0, - 'level1Keys' | 'level2Keys' | 'rootKeys' - >; - updateQueues: UpdateQueuesV0; - keys: KeysV0; -} - -/** - * Used in protocol version 4 and 5 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface UpdatesV1 extends UpdatesCommon { - chainParameters: Omit< - ChainParametersV1, - 'level1Keys' | 'level2Keys' | 'rootKeys' - >; - updateQueues: UpdateQueuesV1; - keys: KeysV1; -} - -/** - * Used from protocol version 6 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface UpdatesV2 extends UpdatesCommon { - chainParameters: Omit< - ChainParametersV2, - 'level1Keys' | 'level2Keys' | 'rootKeys' - >; - updateQueues: UpdateQueuesV2; - keys: KeysV1; -} - -/** - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type Updates = UpdatesV0 | UpdatesV1 | UpdatesV2; - -/** - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -interface BlockSummaryCommon { - protocolVersion?: bigint; - finalizationData: FinalizationData; - transactionSummaries: TransactionSummary[]; -} - -/** - * Used from protocol version 1-3 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface BlockSummaryV0 extends BlockSummaryCommon { - updates: UpdatesV0; -} - -/** - * Used in protocol version 4 and 5 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface BlockSummaryV1 extends BlockSummaryCommon { - updates: UpdatesV1; - protocolVersion: bigint; -} - -/** - * Used from protocol version 6 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface BlockSummaryV2 extends BlockSummaryCommon { - updates: UpdatesV2; - protocolVersion: bigint; -} - -/** - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type BlockSummary = BlockSummaryV0 | BlockSummaryV1 | BlockSummaryV2; - interface RewardStatusCommon { protocolVersion?: bigint; totalAmount: Amount; @@ -736,9 +488,12 @@ interface RewardStatusCommon { gasAccount: Amount; } -export type RewardStatusV0 = RewardStatusCommon; +export interface RewardStatusV0 extends RewardStatusCommon { + version: 0; +} export interface RewardStatusV1 extends RewardStatusCommon { + version: 1; foundationTransactionRewards: Amount; nextPaydayTime: Date; nextPaydayMintRate: MintRate; @@ -798,12 +553,14 @@ export interface BlockInfoCommon { /** Block info used for protocol version 1-5 */ export interface BlockInfoV0 extends BlockInfoCommon { + version: 0; /** The slot number in which the block was baked. */ blockSlot: bigint; } /** Block info used from protocol version 6 */ export interface BlockInfoV1 extends BlockInfoCommon { + version: 1; /** The block round */ round: Round; /** The block epoch */ @@ -912,6 +669,7 @@ export interface ConsensusStatusCommon { /** Consensus status used for protocol version 1-5 */ export interface ConsensusStatusV0 extends ConsensusStatusCommon { + version: 0; /** (Current) slot duration in milliseconds */ slotDuration: Duration.Type; } @@ -932,6 +690,7 @@ export interface ConcordiumBftStatus { /** Consensus status used from protocol version 6 */ export type ConsensusStatusV1 = ConsensusStatusCommon & { + version: 1; concordiumBFTStatus: ConcordiumBftStatus; }; @@ -976,91 +735,11 @@ export interface VerifyKey { verifyKey: HexString; } -export interface KeyPair { - signKey: HexString; - verifyKey: HexString; -} - export interface CredentialPublicKeys { keys: Record; threshold: number; } -export interface CredentialKeys { - keys: Record; - threshold: number; -} - -export interface AccountKeys { - keys: Record; - threshold: number; -} - -export type SimpleAccountKeys = Record>; - -export interface WithAccountKeys { - accountKeys: AccountKeys; -} - -export interface WalletExportFormat { - type: string; - v: number; - environment: string; - value: { - accountKeys: AccountKeys; - address: Base58String; - credentials: Record; - }; -} - -/** - * Parses a wallet export file into a WalletExportFormat. The wallet export - * file is exported from a concordium wallet. - */ -export function parseWallet(walletString: JsonString): WalletExportFormat { - const wallet = JSON.parse(walletString); - console.log(typeof wallet.type); - if (typeof wallet.type !== 'string') { - throw Error( - 'Expected field "type" to be of type "string" but was of type "' + - typeof wallet.type + - '"' - ); - } - if (typeof wallet.v !== 'number') { - throw Error( - 'Expected field "v" to be of type "number" but was of type "' + - typeof wallet.v + - '"' - ); - } - if (typeof wallet.environment !== 'string') { - throw Error( - 'Expected field "environment" to be of type "string" but was of type "' + - typeof wallet.environment + - '"' - ); - } - if (typeof wallet.value.address !== 'string') { - throw Error( - 'Expected field "value.address" to be of type "string" but was of type "' + - typeof wallet.value.address + - '"' - ); - } - if (wallet.value.accountKeys === undefined) { - throw Error( - 'Expected field "value.accountKeys" to be defined, but was not' - ); - } - if (wallet.value.credentials === undefined) { - throw Error( - 'Expected field "value.credentials" to be defined, but was not' - ); - } - return wallet; -} - export interface ChainArData { encIdCredPubShare: string; } @@ -1110,59 +789,25 @@ export interface InitialAccountCredential { export enum StakePendingChangeType { ReduceStake = 'ReduceStake', - RemoveStakeV0 = 'RemoveBaker', - RemoveStakeV1 = 'RemoveStake', + RemoveStake = 'RemoveStake', } -interface StakePendingChangeV0Common { - epoch: bigint; -} - -interface StakePendingChangeV1Common { +interface StakePendingChangeCommon { effectiveTime: Date; } -interface ReduceStakePendingChangeCommon { - newStake: bigint; -} - -export interface ReduceStakePendingChangeV0 - extends ReduceStakePendingChangeCommon, - StakePendingChangeV0Common { +export interface ReduceStakePendingChange extends StakePendingChangeCommon { change: StakePendingChangeType.ReduceStake; + newStake: bigint; } -export interface ReduceStakePendingChangeV1 - extends ReduceStakePendingChangeCommon, - StakePendingChangeV1Common { - change: StakePendingChangeType.ReduceStake; -} - -export type ReduceStakePendingChange = - | ReduceStakePendingChangeV0 - | ReduceStakePendingChangeV1; - -export interface RemovalPendingChangeV0 extends StakePendingChangeV0Common { - change: StakePendingChangeType.RemoveStakeV0; -} - -export interface RemovalPendingChangeV1 extends StakePendingChangeV1Common { - change: StakePendingChangeType.RemoveStakeV1; +export interface RemovalPendingChange extends StakePendingChangeCommon { + change: StakePendingChangeType.RemoveStake; } -export type RemovalPendingChange = - | RemovalPendingChangeV0 - | RemovalPendingChangeV1; - -export type StakePendingChangeV0 = - | ReduceStakePendingChangeV0 - | RemovalPendingChangeV0; - -export type StakePendingChangeV1 = - | ReduceStakePendingChangeV1 - | RemovalPendingChangeV1; - -export type StakePendingChange = StakePendingChangeV0 | StakePendingChangeV1; +export type StakePendingChange = + | ReduceStakePendingChange + | RemovalPendingChange; export enum OpenStatus { OpenForAll = 0, @@ -1181,8 +826,7 @@ export enum OpenStatusText { export type Amount = bigint; export type BakerId = bigint; -// TODO: Change this to bigint when GrpcV1 is removed. -export type DelegatorId = number; +export type DelegatorId = bigint; export interface BakerPoolInfo { openStatus: OpenStatusText; @@ -1325,9 +969,14 @@ interface AccountBakerDetailsCommon { pendingChange?: StakePendingChange; } -export type AccountBakerDetailsV0 = AccountBakerDetailsCommon; +/** Protocol version 1-3. */ +export interface AccountBakerDetailsV0 extends AccountBakerDetailsCommon { + version: 0; +} +/** Protocol version 4 and later. */ export interface AccountBakerDetailsV1 extends AccountBakerDetailsCommon { + version: 1; bakerPoolInfo: BakerPoolInfo; } @@ -1337,44 +986,43 @@ export interface AccountDelegationDetails { restakeEarnings: boolean; stakedAmount: bigint; delegationTarget: DelegationTarget; - pendingChange?: StakePendingChangeV1; + pendingChange?: StakePendingChange; } export type AccountCredential = Versioned< InitialAccountCredential | NormalAccountCredential >; +export enum AccountInfoType { + Simple = 'simple', + Baker = 'baker', + Delegator = 'delegator', +} + interface AccountInfoCommon { accountAddress: AccountAddress.Type; accountNonce: SequenceNumber.Type; accountAmount: bigint; accountIndex: bigint; - accountThreshold: number; - accountEncryptionKey: string; accountEncryptedAmount: AccountEncryptedAmount; - accountReleaseSchedule: AccountReleaseSchedule; - accountCredentials: Record; } -export type AccountInfoSimple = AccountInfoCommon; - -export interface AccountInfoBakerV0 extends AccountInfoCommon { - accountBaker: AccountBakerDetailsV0; +export interface AccountInfoSimple extends AccountInfoCommon { + type: AccountInfoType.Simple; } -/** Protocol version 4 and later. */ -export interface AccountInfoBakerV1 extends AccountInfoCommon { - accountBaker: AccountBakerDetailsV1; +export interface AccountInfoBaker extends AccountInfoCommon { + type: AccountInfoType.Baker; + accountBaker: AccountBakerDetails; } -export type AccountInfoBaker = AccountInfoBakerV0 | AccountInfoBakerV1; - /** Protocol version 4 and later. */ export interface AccountInfoDelegator extends AccountInfoCommon { + type: AccountInfoType.Delegator; accountDelegation: AccountDelegationDetails; } @@ -1431,11 +1079,14 @@ export interface ElectionInfoCommon { /** Election info used for protocol version 1-5 */ export interface ElectionInfoV0 extends ElectionInfoCommon { + version: 0; electionDifficulty: number; } /** Election info used from protocol version 6 */ -export type ElectionInfoV1 = ElectionInfoCommon; +export interface ElectionInfoV1 extends ElectionInfoCommon { + version: 1; +} /** * Union of different versions of election info across all protocol versions. @@ -1703,74 +1354,6 @@ export interface AccountTransaction { payload: AccountTransactionPayload; } -/** - * @deprecated This type was for serialization code, which has been moved to rust-bindings - */ -export enum ParameterType { - /** Nothing. */ - Unit = 0, - /** Boolean (`true` or `false`). */ - Bool, - /** Unsigned 8-bit integer. */ - U8, - /** Unsigned 16-bit integer. */ - U16, - /** Unsigned 32-bit integer. */ - U32, - /** Unsigned 64-bit integer. */ - U64, - /** Signed 8-bit integer. */ - I8, - /** Signed 16-bit integer. */ - I16, - /** Signed 32-bit integer. */ - I32, - /** Signed 64-bit integer. */ - I64, - /** Token amount in microCCD (10^-6 CCD). */ - Amount, - /** Sender account address. */ - AccountAddress, - /** Address of the contract instance consisting of an index and a subindex. */ - ContractAddress, - /** Unsigned 64-bit integer storing milliseconds since UNIX epoch and representing a timestamp. */ - Timestamp, - /** Unsigned 64-bit integer storing milliseconds and representing a duration. */ - Duration, - /** Tuple. */ - Pair, - /** Variable size list. */ - List, - /** Unordered collection of unique elements. */ - Set, - /** Unordered map from keys to values. */ - Map, - /** Fixed size array. */ - Array, - /** Struct. */ - Struct, - /** Enum. */ - Enum, - /** List of bytes representing a string. */ - String, - /** Unsigned 128-bit integer. */ - U128, - /** Signed 128-bit integer. */ - I128, - /** Name of the contract. */ - ContractName, - /** Receive function name. */ - ReceiveName, - /** LEB128 encoding of an unsigned integer */ - ULeb128, - /** LEB128 encoding of a signed integer */ - ILeb128, - /** Variable size list of bytes */ - ByteList, - /** Fixed size list of bytes */ - ByteArray, -} - export interface InstanceInfoCommon { /** Version of the smart contract module. */ version: number; @@ -1797,12 +1380,6 @@ export interface InstanceInfoV1 extends InstanceInfoCommon { export type InstanceInfo = InstanceInfoV0 | InstanceInfoV1; -export const isInstanceInfoV1 = (info: InstanceInfo): info is InstanceInfoV1 => - info.version === 1; - -export const isInstanceInfoV0 = (info: InstanceInfo): info is InstanceInfoV0 => - info.version === undefined || info.version === 0; - export type CredentialSignature = Record; export type AccountTransactionSignature = Record; @@ -1842,52 +1419,6 @@ export interface ContractContext { energy?: Energy.Type; } -/** - * Format of invoker expected by the node for the invokeContract entrypoint. - * @deprecated This is type used by the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type Invoker = - | { - type: 'AddressContract'; - address: { - index: DigitString; - subindex: DigitString; - }; - } - | { - type: 'AddressAccount'; - address: Base58String; - } - | null; - -/** - * Takes an accountAddress or ContractAddress and transforms it into the specific format used for - * InvokeContract's invoker parameter. - * @deprecated This is helper intented for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export function buildInvoker( - invoker?: AccountAddress.Type | ContractAddress.Type -): Invoker { - if (!invoker) { - return null; - } else if (AccountAddress.isAccountAddress(invoker)) { - return { - type: 'AddressAccount', - address: AccountAddress.toBase58(invoker), - }; - } else if (ContractAddress.isContractAddress(invoker)) { - return { - type: 'AddressContract', - address: { - subindex: invoker.subindex.toString(), - index: invoker.index.toString(), - }, - }; - } else { - throw new Error('Unexpected input to build invoker'); - } -} - export interface InvokeContractSuccessResult { tag: 'success'; usedEnergy: Energy.Type; @@ -1902,26 +1433,10 @@ export interface InvokeContractFailedResult { returnValue?: ReturnValue.Type; } -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface InvokeContractFailedResultV1 { - tag: 'failure'; - usedEnergy: Energy.Type; - reason: RejectReasonV1; -} - export type InvokeContractResult = | InvokeContractSuccessResult | InvokeContractFailedResult; -/** - * @deprecated This is helper intented for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type InvokeContractResultV1 = - | InvokeContractSuccessResult - | InvokeContractFailedResultV1; - export interface CredentialDeploymentDetails { expiry: TransactionExpiry; unsignedCdi: UnsignedCredentialDeploymentInformation; diff --git a/packages/common/src/types/rejectReason.ts b/packages/common/src/types/rejectReason.ts index b89bfd2ee..a54630536 100644 --- a/packages/common/src/types/rejectReason.ts +++ b/packages/common/src/types/rejectReason.ts @@ -1,11 +1,4 @@ -import { - Address, - Base58String, - DigitString, - HexString, - Amount, - BakerId, -} from '../types.js'; +import { Address, Base58String, HexString, Amount, BakerId } from '../types.js'; import type * as ContractAddress from './ContractAddress.js'; /* @@ -155,14 +148,6 @@ export interface BakerIdRejectReason { contents: BakerId; } -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface NumberRejectReason { - tag: BakerIdRejectReasonTag; - contents: number; -} - export interface SimpleRejectReason { tag: SimpleRejectReasonTag; } @@ -175,14 +160,6 @@ export interface InvalidReceiveMethod { }; } -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface InvalidReceiveMethodV1 { - tag: RejectReasonTag.InvalidReceiveMethod; - contents: [HexString, string]; // [moduleRef, receiveName] -} - export interface InvalidInitMethod { tag: RejectReasonTag.InvalidInitMethod; contents: { @@ -191,14 +168,6 @@ export interface InvalidInitMethod { }; } -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface InvalidInitMethodV1 { - tag: RejectReasonTag.InvalidInitMethod; - contents: [HexString, string]; // [moduleRef, initName] -} - export interface AmountTooLarge { tag: RejectReasonTag.AmountTooLarge; contents: { @@ -207,14 +176,6 @@ export interface AmountTooLarge { }; } -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface AmountTooLargeV1 { - tag: RejectReasonTag.AmountTooLarge; - contents: [Address, DigitString]; // [address, amount] -} - export interface InvalidContractAddress { tag: RejectReasonTag.InvalidContractAddress; contents: ContractAddress.Type; @@ -243,13 +204,3 @@ export type RejectReason = | InvalidReceiveMethod | InvalidInitMethod | AmountTooLarge; - -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type RejectReasonV1 = - | RejectReasonCommon - | NumberRejectReason - | InvalidReceiveMethodV1 - | InvalidInitMethodV1 - | AmountTooLargeV1; diff --git a/packages/common/src/util.ts b/packages/common/src/util.ts index 1f8ce9436..7b876f192 100644 --- a/packages/common/src/util.ts +++ b/packages/common/src/util.ts @@ -3,29 +3,8 @@ import { AccountTransactionSignature, HexString, IpAddressString, - ReleaseSchedule, } from './types.js'; -/** - * Replaces a number in a JSON string with the same number as a - * string, i.e. with quotes (") prior to and after the number. This - * is needed as the default JSON parser cannot intepret BigInts - * correctly when they arrive as JSON numbers. - * @param jsonStruct the JSON structure as a string - * @param keys the keys where the number has to be quoted - * @returns the same JSON string where the numbers at the supplied keys are quoted - */ -function intToString(jsonStruct: string, keys: string[]): string { - let result = jsonStruct; - for (const key of keys) { - result = result.replace( - new RegExp(`"${key}":\\s*([0-9]+)`, 'g'), - `"${key}":"$1"` - ); - } - return result; -} - /** * Replaces a string in a JSON string with the same string as a * number, i.e. removing quotes (") prior to and after the string. This @@ -47,55 +26,6 @@ export function stringToInt(jsonStruct: string, keys: string[]): string { return result; } -/** - * A transformer that converts all the values provided as keys to - * string values. - * @param json the json to transform - * @param bigIntPropertyKeys the keys in the json that must be converted to strings - * @returns the transformed json where numbers have been replaced with strings - * @deprecated This is helper intented for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export function intToStringTransformer( - bigIntPropertyKeys: string[] -): (json: string) => string { - return (json: string) => intToString(json, bigIntPropertyKeys); -} - -/** - * Builds a JSON.parse() reviver function used to parse dates and big integers. - * @param datePropertyKeys the JSON keys that must be parsed as dates - * @param bigIntPropertyKeys the JSON keys that must be parsed as big integers - * @returns a reviver function that handles dates and big integers - * @deprecated This is helper intented for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export function buildJsonResponseReviver( - datePropertyKeys: (keyof T)[], - bigIntPropertyKeys: (keyof T)[] -): (key: string, value: any) => any { - return function reviver(key: string, value: any) { - if (datePropertyKeys.includes(key as keyof T)) { - // Note that we reduce the time precision from nano to milliseconds when doing this conversion. - return new Date(value); - } else if (bigIntPropertyKeys.includes(key as keyof T)) { - // Handle the special case where amount is a scheduled amount, - // which has an array structure. - if (key === 'amount' && Array.isArray(value)) { - const result: ReleaseSchedule[] = []; - for (const entry of value) { - const schedule: ReleaseSchedule = { - timestamp: new Date(entry[0]), - amount: BigInt(entry[1]), - }; - result.push(schedule); - } - return result; - } - return value === null ? value : BigInt(value); - } - return value; - }; -} - /** * Checks if the input string is a valid hexadecimal string. * @param str the string to check for hexadecimal diff --git a/packages/common/src/versionedTypeHelpers.ts b/packages/common/src/versionedTypeHelpers.ts index 42cae035d..62efbd81c 100644 --- a/packages/common/src/versionedTypeHelpers.ts +++ b/packages/common/src/versionedTypeHelpers.ts @@ -1,7 +1,4 @@ import { - AccountInfo, - AccountInfoBakerV0, - AccountInfoBakerV1, Authorizations, AuthorizationsV1, BlockInfo, @@ -17,90 +14,120 @@ import { ElectionInfo, ElectionInfoV0, ElectionInfoV1, - Keys, - KeysV0, - KeysV1, - StakePendingChange, - StakePendingChangeV0, - StakePendingChangeV1, + InstanceInfo, + InstanceInfoV0, + InstanceInfoV1, + RewardStatus, + RewardStatusV1, } from './types.js'; -/** Whether {@link AccountInfo} parameter given is of type {@link AccountInfoBakerV0} */ -export const isBakerAccountV0 = (ai: AccountInfo): ai is AccountInfoBakerV0 => - (ai as AccountInfoBakerV1).accountBaker?.bakerPoolInfo === undefined; - -/** Whether {@link AccountInfo} parameter given is of type {@link AccountInfoBakerV1} */ -export const isBakerAccountV1 = (ai: AccountInfo): ai is AccountInfoBakerV1 => - (ai as AccountInfoBakerV1).accountBaker?.bakerPoolInfo !== undefined; - -/** Whether {@link StakePendingChange} parameter given is of type {@link StakePendingChangeV0} */ -export const isStakePendingChangeV0 = ( - spc: StakePendingChange -): spc is StakePendingChangeV0 => - (spc as StakePendingChangeV0).epoch !== undefined; - -/** Whether {@link StakePendingChange} parameter given is of type {@link StakePendingChangeV1} */ -export const isStakePendingChangeV1 = ( - spc: StakePendingChange -): spc is StakePendingChangeV1 => - (spc as StakePendingChangeV1).effectiveTime !== undefined; - -/** Whether {@link Authorizations} parameter given is of type {@link AuthorizationsV1} */ +/** + * Whether {@link Authorizations} parameter given is of type {@link AuthorizationsV1} + * + * @deprecated check the `version` member instead. + */ export const isAuthorizationsV1 = ( as: Authorizations -): as is AuthorizationsV1 => - (as as AuthorizationsV1).timeParameters !== undefined; +): as is AuthorizationsV1 => as.version === 1; -/** Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV0} */ +/** + * Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV0} + * + * @deprecated check the `version` member instead. + */ export const isChainParametersV0 = ( cp: ChainParameters -): cp is ChainParametersV0 => - (cp as ChainParametersV0).minimumThresholdForBaking !== undefined; +): cp is ChainParametersV0 => cp.version === 0; -/** Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV1} */ +/** + * Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV1} + * + * @deprecated check the `version` member instead. + */ export const isChainParametersV1 = ( cp: ChainParameters -): cp is ChainParametersV1 => - (cp as ChainParametersV1).mintPerPayday !== undefined && - !isChainParametersV2(cp); +): cp is ChainParametersV1 => cp.version === 1; -/** Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV2} */ +/** + * Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV2} + * + * @deprecated check the `version` member instead. + */ export const isChainParametersV2 = ( cp: ChainParameters -): cp is ChainParametersV2 => - (cp as ChainParametersV2).maximumFinalizers !== undefined; - -/** Whether {@link Keys} parameter given is of type {@link KeysV0} */ -export const isKeysV0 = (ks: Keys): ks is KeysV0 => - !isAuthorizationsV1(ks.level2Keys); - -/** Whether {@link Keys} parameter given is of type {@link KeysV1} */ -export const isKeysV1 = (ks: Keys): ks is KeysV1 => - isAuthorizationsV1(ks.level2Keys); +): cp is ChainParametersV2 => cp.version === 2; -/** Whether {@link BlockInfo} parameter given is of type {@link BlockInfoV0} */ +/** + * Whether {@link BlockInfo} parameter given is of type {@link BlockInfoV0} + * + * @deprecated check the `version` member instead. + */ export const isBlockInfoV0 = (bi: BlockInfo): bi is BlockInfoV0 => - (bi as BlockInfoV0).blockSlot !== undefined; + bi.version === 0; -/** Whether {@link BlockInfo} parameter given is of type {@link BlockInfoV1} */ +/** + * Whether {@link BlockInfo} parameter given is of type {@link BlockInfoV1} + * + * @deprecated check the `version` member instead. + */ export const isBlockInfoV1 = (bi: BlockInfo): bi is BlockInfoV1 => - (bi as BlockInfoV1).round !== undefined; + bi.version === 1; -/** Whether {@link ConensusStatus} parameter given is of type {@link ConsensusStatusV0} */ +/** + * Whether {@link ConensusStatus} parameter given is of type {@link ConsensusStatusV0} + * + * @deprecated check the `version` member instead. + */ export const isConsensusStatusV0 = ( cs: ConsensusStatus -): cs is ConsensusStatusV0 => (cs as ConsensusStatusV0).slotDuration != null; +): cs is ConsensusStatusV0 => cs.version === 0; -/** Whether {@link ConensusStatus} parameter given is of type {@link ConsensusStatusV1} */ +/** + * Whether {@link ConensusStatus} parameter given is of type {@link ConsensusStatusV1} + * + * @deprecated check the `version` member instead. + */ export const isConsensusStatusV1 = ( cs: ConsensusStatus -): cs is ConsensusStatusV1 => - (cs as ConsensusStatusV1).concordiumBFTStatus !== undefined; +): cs is ConsensusStatusV1 => cs.version === 1; -/** Whether {@link ElectionInfo} parameter given is of type {@link ElectionInfoV0} */ +/** + * Whether {@link ElectionInfo} parameter given is of type {@link ElectionInfoV0} + * + * @deprecated check the `version` member instead. + */ export const isElectionInfoV0 = (ei: ElectionInfo): ei is ElectionInfoV0 => - (ei as ElectionInfoV0).electionDifficulty !== undefined; + ei.version === 0; -/** Whether {@link ElectionInfo} parameter given is of type {@link ElectionInfoV1} */ +/** + * Whether {@link ElectionInfo} parameter given is of type {@link ElectionInfoV1} + * + * @deprecated check the `version` member instead. + */ export const isElectionInfoV1 = (ei: ElectionInfo): ei is ElectionInfoV1 => - !isElectionInfoV0(ei); + ei.version === 1; + +/** + * Whether {@link InstanceInfo} parameter given is of type {@link InstanceInfoV1} + * + * @deprecated check the `version` member instead. + */ +export const isInstanceInfoV1 = (info: InstanceInfo): info is InstanceInfoV1 => + info.version === 1; + +/** + * Whether {@link InstanceInfo} parameter given is of type {@link InstanceInfoV0} + * + * @deprecated check the `version` member instead. + */ +export const isInstanceInfoV0 = (info: InstanceInfo): info is InstanceInfoV0 => + info.version === undefined || info.version === 0; + +/** + * Whether {@link RewardStatus} parameter given is of type {@link RewardStatusV1} + * + * @deprecated check the `version` member instead. + */ +export function isRewardStatusV1(rs: RewardStatus): rs is RewardStatusV1 { + return rs.version === 0; +} diff --git a/packages/common/src/wasm/credentialDeploymentTransactions.ts b/packages/common/src/wasm/credentialDeploymentTransactions.ts index c7ac0e2b3..7c408025d 100644 --- a/packages/common/src/wasm/credentialDeploymentTransactions.ts +++ b/packages/common/src/wasm/credentialDeploymentTransactions.ts @@ -13,7 +13,6 @@ import { IpInfo, ArInfo, IdentityObjectV1, - SignedCredentialDeploymentDetails, Network, CredentialPublicKeys, AttributesKeys, @@ -203,26 +202,6 @@ export type CredentialInput = CredentialInputCommon & { identityIndex: number; }; -/** - * Creates a credential for a new account, using the version 1 algorithm, which uses a seed to generate keys and commitments. - * @deprecated This function outputs the format used by the JSON-RPC client, createCredentialTransaction should be used instead. - */ -export function createCredentialV1( - input: CredentialInput & { expiry: number } -): SignedCredentialDeploymentDetails { - const rawRequest = wasm.createCredentialV1(JSON.stringify(input)); - let info: CredentialDeploymentInfo; - try { - info = JSON.parse(rawRequest); - } catch (e) { - throw new Error(rawRequest); - } - return { - expiry: TransactionExpiry.fromEpochSeconds(BigInt(input.expiry)), - cdi: info, - }; -} - export type CredentialInputNoSeed = CredentialInputCommon & { idCredSec: HexString; prfKey: HexString; diff --git a/packages/common/test/credentialDeployment.test.ts b/packages/common/test/credentialDeployment.test.ts index 15617ea4c..2304dce45 100644 --- a/packages/common/test/credentialDeployment.test.ts +++ b/packages/common/test/credentialDeployment.test.ts @@ -1,7 +1,6 @@ import { createCredentialTransaction, CredentialInput, - createCredentialV1, } from '../src/wasm/credentialDeploymentTransactions.js'; import fs from 'fs'; import { AttributeKey } from '../src/types.js'; @@ -39,36 +38,6 @@ export function createCredentialInput( }; } -test('Test createCredentialV1', () => { - const expiry = Math.floor(Date.now() / 1000) + 720; - const revealedAttributes: AttributeKey[] = ['firstName']; - const output = createCredentialV1({ - ...createCredentialInput(revealedAttributes), - expiry, - }); - - expect(output.cdi.credId).toEqual( - 'b317d3fea7de56f8c96f6e72820c5cd502cc0eef8454016ee548913255897c6b52156cc60df965d3efb3f160eff6ced4' - ); - expect(output.cdi.credentialPublicKeys.keys[0].verifyKey).toEqual( - '29723ec9a0b4ca16d5d548b676a1a0adbecdedc5446894151acb7699293d69b1' - ); - expect(output.cdi.credentialPublicKeys.threshold).toEqual(1); - expect(output.cdi.ipIdentity).toEqual(0); - expect(output.cdi.policy.createdAt).toEqual('202208'); - expect(output.cdi.policy.validTo).toEqual('202308'); - expect(Object.keys(output.cdi.policy.revealedAttributes)).toEqual( - revealedAttributes - ); - expect(output.cdi.revocationThreshold).toEqual(1); - expect(typeof output.cdi.proofs).toEqual('string'); - expect(Object.keys(output.cdi.arData)).toEqual(['1', '2', '3']); - expect(typeof output.cdi.arData[1].encIdCredPubShare).toEqual('string'); - expect(typeof output.cdi.arData[2].encIdCredPubShare).toEqual('string'); - expect(typeof output.cdi.arData[3].encIdCredPubShare).toEqual('string'); - expect(output.expiry.expiryEpochSeconds).toEqual(BigInt(expiry)); -}); - test('Test createCredentialTransaction', () => { const expiry = BigInt(Math.floor(Date.now() / 1000)) + BigInt(720); const revealedAttributes: AttributeKey[] = ['firstName']; diff --git a/packages/common/test/types/blockItemSummary.test.ts b/packages/common/test/types/blockItemSummary.test.ts index 072ba139c..a7e2e7996 100644 --- a/packages/common/test/types/blockItemSummary.test.ts +++ b/packages/common/test/types/blockItemSummary.test.ts @@ -236,14 +236,14 @@ const configureDelegation: BaseAccountTransactionSummary & events: [ { tag: TransactionEventTag.DelegationAdded, - delegatorId: 2499, + delegatorId: 2499n, account: AccountAddress.fromBase58( '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' ), }, { tag: TransactionEventTag.DelegationSetDelegationTarget, - delegatorId: 2499, + delegatorId: 2499n, delegationTarget: { delegateType: DelegationTargetType.Baker, bakerId: 15, @@ -254,7 +254,7 @@ const configureDelegation: BaseAccountTransactionSummary & }, { tag: TransactionEventTag.DelegationSetRestakeEarnings, - delegatorId: 2499, + delegatorId: 2499n, restakeEarnings: true, account: AccountAddress.fromBase58( '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' @@ -262,7 +262,7 @@ const configureDelegation: BaseAccountTransactionSummary & }, { tag: TransactionEventTag.DelegationStakeIncreased, - delegatorId: 2499, + delegatorId: 2499n, newStake: 240000000n, account: AccountAddress.fromBase58( '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' diff --git a/packages/common/test/util.test.ts b/packages/common/test/util.test.ts index c65b0fc7d..de909806b 100644 --- a/packages/common/test/util.test.ts +++ b/packages/common/test/util.test.ts @@ -1,38 +1,6 @@ -import { - intToStringTransformer, - stringToInt, - wasmToSchema, -} from '../src/util.js'; +import { stringToInt, wasmToSchema } from '../src/util.js'; import { readFileSync } from 'fs'; -test('intToStringTransformer transform chosen field, but not others', () => { - const keysToTransform = ['a']; - const input = - '{ "a":90071992547409910, "b":90071992547409911, "aa":90071992547409912}'; - const transformed = intToStringTransformer(keysToTransform)(input); - expect(transformed).toEqual( - '{ "a":"90071992547409910", "b":90071992547409911, "aa":90071992547409912}' - ); -}); - -test('intToStringTransformer transforms multiple fields', () => { - const keysToTransform = ['a', 'b']; - const input = - '{ "a":90071992547409910, "b":90071992547409911, "aa":{"a":12071992547409910,"c":1}}'; - const transformed = intToStringTransformer(keysToTransform)(input); - expect(transformed).toEqual( - '{ "a":"90071992547409910", "b":"90071992547409911", "aa":{"a":"12071992547409910","c":1}}' - ); -}); - -test('intToStringTransformer will not change the string if no keys match', () => { - const keysToTransform = ['d', 'aaa']; - const input = - '{ "a":90071992547409910, "b":90071992547409911, "aa":{"a":12071992547409910,"c":1}}'; - const transformed = intToStringTransformer(keysToTransform)(input); - expect(transformed).toEqual(input); -}); - test('stringToInt transforms chosen field, but not others', () => { const keysToTransform = ['a']; const input = @@ -61,27 +29,6 @@ test('stringToInt will not change the string if no keys match', () => { expect(transformed).toEqual(input); }); -test('stringToInt can inverse intToStringTransformer (with same chosen keys, and no matching number fields)', () => { - const keysToTransform = ['a', 'b']; - const input = - '{ "a":90071992547409910, "b":90071992547409911, "aa":{"a":12071992547409910,"c":"1"}, "d": true}'; - const transformed = stringToInt( - intToStringTransformer(keysToTransform)(input), - keysToTransform - ); - expect(transformed).toEqual(input); -}); - -test('intToStringTransformer is inverse of stringToInt (with same chosen keys, and no matching string fields)', () => { - const keysToTransform = ['a', 'b']; - const input = - '{ "a":"90071992547409910", "b":"90071992547409911", "aa":{"a":"12071992547409910","c":"1"}, "d": true}'; - const transformed = intToStringTransformer(keysToTransform)( - stringToInt(input, keysToTransform) - ); - expect(transformed).toEqual(input); -}); - test('Embedded schema is the same as a seperate schema file', () => { const versionedWasmModule = readFileSync( 'test/resources/icecream-with-schema.wasm' diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json index 7cfbf855c..f92a20b21 100644 --- a/packages/common/tsconfig.json +++ b/packages/common/tsconfig.json @@ -1,5 +1,11 @@ { "extends": "../../tsconfig-base.json", + "compilerOptions": { + "lib": [ + "ES2020", + "DOM" // To get WebAssembly type. + ] + }, "include": [ "src/**/*", "types/**/*", diff --git a/packages/nodejs/CHANGELOG.md b/packages/nodejs/CHANGELOG.md index 41a44aa8d..b7281ea92 100644 --- a/packages/nodejs/CHANGELOG.md +++ b/packages/nodejs/CHANGELOG.md @@ -24,6 +24,7 @@ - NodeJS: 4.7, `"moduleResolution": "node16" // or "nodenext"` - Bundled applications (webpack, esbuild, rollup, etc...): 5.0, `"moduleResolution": "bundler"` +- Removed `ConcordiumNodeClient` and types and functionality associated solely with this class. The API now uses dedicated types instead of language primitives: - Use `AccountAddress` instead of a string with base58 encoding. Use `AccountAddress.fromBase58('')` to construct it. @@ -40,6 +41,7 @@ The API now uses dedicated types instead of language primitives: - Use `Duration` instead of a bigint. Can be constructed using `Duration.fromMillis()`. Several types have been replaced with a module containing the type itself together with functions for constructing and converting the type: + - `AccountAddress` is now a module with functions related to account addresses: - To refer to `AccountAddress` as a type use `AccountAddress.Type`. - Constructing `new AccountAddress("
")` is now `AccountAddress.fromBase58("
")`. diff --git a/packages/nodejs/package.json b/packages/nodejs/package.json index d2986b9e0..157992838 100644 --- a/packages/nodejs/package.json +++ b/packages/nodejs/package.json @@ -33,10 +33,7 @@ ], "devDependencies": { "@noble/ed25519": "^1.7.1", - "@protobuf-ts/grpcweb-transport": "^2.8.2", - "@protobuf-ts/plugin": "^2.9.1", "@types/bs58check": "^2.1.0", - "@types/google-protobuf": "^3.15.3", "@types/jest": "^26.0.23", "@typescript-eslint/eslint-plugin": "^6.7.0", "@typescript-eslint/parser": "^6.7.0", @@ -46,8 +43,6 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", - "grpc-tools": "^1.11.2", - "grpc_tools_node_protoc_ts": "5.3.0", "isomorphic-fetch": "^3.0.0", "jest": "^29.6.2", "lint-staged": "^12.0.2", @@ -61,21 +56,15 @@ "tabWidth": 4 }, "scripts": { - "generate-ts": "yarn run grpc_tools_node_protoc --plugin=protoc-gen-ts=../../node_modules/@protobuf-ts/plugin/bin/protoc-gen-ts --ts_opt 'optimize_code_size,ts_nocheck' --ts_out=src/grpc-api -I ../../deps/concordium-base/concordium-grpc-api ../../deps/concordium-base/concordium-grpc-api/**/*.proto", - "generate": "([ -e \"../../deps/concordium-base/concordium-grpc-api\" ] && yarn generate-ts && node ../../scripts/proto-node-esm-compat.js src/grpc-api) || echo 'Please checkout submodules before building'", "lint": "eslint . --cache --ext .ts,.tsx --max-warnings 0", "lint-fix": "yarn --silent lint --fix; exit 0", "test": "jest", - "build": "yarn clean; mkdir -p src/grpc-api; yarn generate && yarn build-dev", - "build-dev": "tsc -p tsconfig.build.json", - "clean": "rimraf -- lib src/grpc-api tsconfig.build.tsbuildinfo" + "build": "tsc -p tsconfig.build.json", + "clean": "rimraf -- lib tsconfig.build.tsbuildinfo" }, "dependencies": { "@concordium/common-sdk": "9.4.0", "@grpc/grpc-js": "^1.3.4", - "@protobuf-ts/grpc-transport": "^2.8.2", - "@protobuf-ts/runtime": "^2.9.1", - "@protobuf-ts/runtime-rpc": "^2.9.1", - "google-protobuf": "^3.20.1" + "@protobuf-ts/grpc-transport": "^2.8.2" } } diff --git a/packages/nodejs/src/client.ts b/packages/nodejs/src/client.ts deleted file mode 100644 index 378f6f61d..000000000 --- a/packages/nodejs/src/client.ts +++ /dev/null @@ -1,875 +0,0 @@ -import { ChannelCredentials } from '@grpc/grpc-js'; -import { P2PClient } from './grpc-api/concordium_p2p_rpc.client.js'; -import { - AccountAddress, - BlockHash, - BlockHeight, - GetAddressInfoRequest, - GetPoolStatusRequest, - GetModuleSourceRequest, - PeerListResponse, - PeersRequest, - SendTransactionRequest, - TransactionHash, - InvokeContractRequest, -} from './grpc-api/concordium_p2p_rpc.js'; -import { - serializeAccountTransactionForSubmission, - AccountAddress as Address, - CredentialRegistrationId, - AccountBakerDetails, - AccountEncryptedAmount, - AccountInfo, - AccountReleaseSchedule, - AccountTransaction, - AccountTransactionSignature, - ArInfo, - BlockInfo, - BlockSummary, - ConsensusStatus, - ContractAddress, - CredentialDeploymentTransaction, - CryptographicParameters, - ExchangeRate, - FinalizationData, - IpInfo, - KeysWithThreshold, - NextAccountNonce, - PartyInfo, - ReleaseSchedule, - TransactionStatus, - TransactionSummary, - TransferredEvent, - UpdateQueue, - Versioned, - InstanceInfo, - InstanceInfoSerialized, - BakerId, - ChainParametersV0, - ChainParametersV1, - PoolStatus, - BakerPoolStatusDetails, - CurrentPaydayBakerPoolStatus, - KeysMatching, - BakerPoolPendingChangeReduceBakerCapitalDetails, - BakerPoolStatus, - RewardStatusV0, - RewardStatus, - RewardStatusV1, - ReduceStakePendingChangeV0, - PassiveDelegationStatus, - PassiveDelegationStatusDetails, - ContractContext, - InvokeContractResultV1, - CcdAmount, - ModuleReference, - ReduceStakePendingChangeV1, - buildInvoker, - DelegationStakeChangedEvent, - CooldownParametersV1, - TimeParametersV1, - PoolParametersV1, - BlockInfoV0, - BlockInfoV1, - ConsensusStatusV0, - ConsensusStatusV1, - ChainParametersV2, - ConsensusParameters, - TimeoutParameters, - Ratio, - ReceiveName, - ConcordiumBftStatus, - Parameter, - InitName, -} from '@concordium/common-sdk'; -import { - buildJsonResponseReviver, - intToStringTransformer, - isValidHash, - stringToInt, -} from '@concordium/common-sdk/util'; -import { convertJsonResponse, intListToStringList } from './util.js'; -import { serializeCredentialDeploymentTransactionForSubmission } from '@concordium/common-sdk/wasm'; -import { GrpcTransport } from '@protobuf-ts/grpc-transport'; -import { RpcMetadata } from '@protobuf-ts/runtime-rpc'; - -/** - * A concordium-node specific gRPC client wrapper. - * - * @example - * import ConcordiumNodeClient from "..." - * const client = new ConcordiumNodeClient('127.0.0.1', 10000, credentials, metadata, 15000); - * @deprecated This has been succeeded by the new V2 client, check {@link createConcordiumClient} - */ -export default class ConcordiumNodeClient { - client: P2PClient; - - // metadata: Metadata; - - address: string; - - port: number; - - timeout: number; - - /** - * Initialize a gRPC client for a specific concordium node. - * @param address the ip address of the node, e.g. 127.0.0.1 - * @param port the port to use when econnecting to the node - * @param credentials credentials to use to connect to the node - * @param meta metadata to send with requests to node - * @param timeout milliseconds to wait before timing out - * @param options optional options for the P2PClient - */ - constructor( - address: string, - port: number, - credentials: ChannelCredentials, - meta: RpcMetadata, - timeout: number, - options?: Record - ) { - if (timeout < 0 || !Number.isSafeInteger(timeout)) { - throw new Error( - 'The timeout must be a positive integer, but was: ' + timeout - ); - } - - this.address = address; - this.port = port; - this.timeout = timeout; - // this.metadata = metadata; - const grpcTransport = new GrpcTransport({ - host: `${address}:${port}`, - channelCredentials: credentials, - meta, - ...options, - }); - this.client = new P2PClient(grpcTransport); - } - - /** - * Sends a credential deployment transaction, for creating a new account, - * to the node to be put in a block on the chain. - * - * Note that a transaction can still fail even if it was accepted by the node. - * To keep track of the transaction use getTransactionStatus. - * @param credentialDeploymentTransaction the credential deployment transaction to send to the node - * @param signatures the signatures on the hash of the serialized unsigned credential deployment information, in order - * @returns true if the transaction was accepted, otherwise false - */ - async sendCredentialDeploymentTransaction( - credentialDeploymentTransaction: CredentialDeploymentTransaction, - signatures: string[] - ): Promise { - const payload: Buffer = Buffer.from( - serializeCredentialDeploymentTransactionForSubmission( - credentialDeploymentTransaction, - signatures - ) - ); - - const sendTransactionRequest: SendTransactionRequest = { - networkId: 100, - payload, - }; - - const { value } = await this.client.sendTransaction( - sendTransactionRequest - ).response; - return value; - } - - /** - * Serializes and sends an account transaction to the node to be - * put in a block on the chain. - * - * Note that a transaction can still fail even if it was accepted by the node. - * To keep track of the transaction use getTransactionStatus. - * @param accountTransaction the transaction to send to the node - * @param signatures the signatures on the signing digest of the transaction - * @returns true if the transaction was accepted, otherwise false - */ - async sendAccountTransaction( - accountTransaction: AccountTransaction, - signatures: AccountTransactionSignature - ): Promise { - const payload: Buffer = Buffer.from( - serializeAccountTransactionForSubmission( - accountTransaction, - signatures - ) - ); - - const sendTransactionRequest: SendTransactionRequest = { - networkId: 100, - payload, - }; - - const { value } = await this.client.sendTransaction( - sendTransactionRequest - ).response; - return value; - } - - /** - * Retrieves the account info for the given account. If the provided block - * hash is in a block prior to the finalization of the account, then the account - * information will not be available. - * A credential registration id can also be provided, instead of an address. In this case - * the node will return the account info of the account, which the corresponding credential - * is (or was) deployed to. - * @param accountAddress base58 account address (or a credential registration id) to get the account info for - * @param blockHash the block hash to get the account info at - * @returns the account info for the provided account address, undefined is the account does not exist - */ - async getAccountInfo( - accountAddress: string | Address.Type | CredentialRegistrationId.Type, - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - let address: string; - if (typeof accountAddress === 'string') { - address = accountAddress; - } else if ('address' in accountAddress) { - address = accountAddress.address; - } else if ('credId' in accountAddress) { - address = accountAddress.credId; - } else { - throw new Error('Invalid accountAddress input'); - } - - const getAddressInfoRequest: GetAddressInfoRequest = { - address, - blockHash, - }; - - const { value: res } = await this.client.getAccountInfo( - getAddressInfoRequest - ).response; - const datePropertyKeys: ( - | keyof ReleaseSchedule - | keyof ReduceStakePendingChangeV1 - )[] = ['timestamp', 'effectiveTime']; - const bigIntPropertyKeys: ( - | keyof AccountInfo - | keyof AccountEncryptedAmount - | keyof AccountReleaseSchedule - | keyof ReleaseSchedule - | keyof AccountBakerDetails - | keyof ReduceStakePendingChangeV0 - )[] = [ - 'accountAmount', - 'accountNonce', - 'accountIndex', - 'startIndex', - 'total', - 'amount', - 'stakedAmount', - 'bakerId', - 'newStake', - 'epoch', - ]; - return convertJsonResponse( - res, - buildJsonResponseReviver(datePropertyKeys, bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - /** - * Retrieves the next account nonce for the given account. The account nonce is - * used in all account transactions as part of their header. - * @param accountAddress base58 account address to get the next account nonce for - * @returns the next account nonce, and a boolean indicating if the nonce is reliable - */ - async getNextAccountNonce( - accountAddress: Address.Type - ): Promise { - const input: AccountAddress = { - accountAddress: accountAddress.address, - }; - const { value: response } = await this.client.getNextAccountNonce(input) - .response; - const bigIntPropertyKeys: (keyof NextAccountNonce)[] = ['nonce']; - - return convertJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - /** - * Retrieves a status for the given transaction. - * @param transactionHash the transaction to get a status for - * @returns the transaction status for the given transaction, or undefined if the transaction does not exist - */ - async getTransactionStatus( - transactionHash: string - ): Promise { - if (!isValidHash(transactionHash)) { - throw new Error( - 'The input was not a valid hash: ' + transactionHash - ); - } - - const bigIntPropertyKeys: (keyof TransactionSummary)[] = [ - 'cost', - 'energyCost', - 'index', - ]; - - const input: TransactionHash = { transactionHash }; - const { value: response } = await this.client.getTransactionStatus( - input - ).response; - return convertJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - /** - * Retrieves the block summary for a specific block. This contains information - * about finalization, update sequence numbers (their nonce), update queues, - * updateable chain parameters and transaction summaries for any transaction - * in the block. - * @param blockHash the block to get the summary for - * @returns the block summary for the given block, or undefined if the block does not exist - */ - async getBlockSummary( - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const input: BlockHash = { blockHash }; - const { value: response } = await this.client.getBlockSummary(input) - .response; - - const bigIntPropertyKeys: ( - | keyof PartyInfo - | keyof FinalizationData - | keyof TransactionSummary - | keyof (ChainParametersV0 & ChainParametersV1 & ChainParametersV2) - | keyof BlockSummary - | keyof PoolParametersV1 - | keyof CooldownParametersV1 - | keyof TimeParametersV1 - | keyof ExchangeRate - | keyof UpdateQueue - | keyof KeysWithThreshold - | keyof TransferredEvent - | keyof ContractAddress.Type - | keyof DelegationStakeChangedEvent - | keyof ConsensusParameters - | keyof TimeoutParameters - | keyof Ratio - )[] = [ - 'bakerId', - 'newStake', - 'weight', - 'finalizationIndex', - 'finalizationDelay', - 'cost', - 'energyCost', - 'index', - 'numerator', - 'denominator', - 'nextSequenceNumber', - 'amount', - 'index', - 'subindex', - 'protocolVersion', - 'foundationAccountIndex', - - // v0 keys - 'bakerCooldownEpochs', - 'minimumThresholdForBaking', - - // v1 keys - 'rewardPeriodLength', - 'minimumEquityCapital', - 'poolOwnerCooldown', - 'delegatorCooldown', - - // v2 keys - 'timeoutBase', - 'denominator', - 'numerator', - 'minBlockTime', - 'blockEnergyLimit', - ]; - - return convertJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys) - ); - } - - /** - * Retrieves information about a specific block. - * @param blockHash the block to get information about - * @returns the block information for the given block, or undefined if the block does not exist - */ - async getBlockInfo(blockHash: string): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const input: BlockHash = { blockHash }; - const { value: response } = await this.client.getBlockInfo(input) - .response; - - const datePropertyKeys: (keyof BlockInfo)[] = [ - 'blockArriveTime', - 'blockReceiveTime', - 'blockSlotTime', - ]; - const bigIntPropertyKeys: (keyof (BlockInfoV0 & BlockInfoV1))[] = [ - 'blockHeight', - 'blockBaker', - 'blockSlot', - 'transactionEnergyCost', - 'transactionCount', - 'transactionsSize', - ]; - - return convertJsonResponse( - response, - buildJsonResponseReviver(datePropertyKeys, bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - /** - * Retrieves the blocks are the given height. - * @param height the block height as a positive integer - * @returns a string array containing the blocks at the given height, i.e. ['blockHash1', 'blockHash2', ...] - */ - async getBlocksAtHeight(height: bigint): Promise { - if (height <= 0n) { - throw new Error( - 'The block height has to be a positive integer, but it was: ' + - height - ); - } - const input: BlockHeight = { - blockHeight: height.toString(), - fromGenesisIndex: 0, - restrictToGenesisIndex: false, - }; - const { value: response } = await this.client.getBlocksAtHeight(input) - .response; - - if (!response) { - return []; - } - return convertJsonResponse(response); - } - - /** - * Retrieves the consensus status information from the node. Note that the optional - * fields will only be unavailable for a newly started node that has not processed - * enough data yet. - */ - async getConsensusStatus(): Promise { - type CS = ConsensusStatusV0 & ConsensusStatusV1; - const { value: response } = await this.client.getConsensusStatus({}) - .response; - - const datePropertyKeys: (keyof CS | keyof ConcordiumBftStatus)[] = [ - 'blockLastReceivedTime', - 'blockLastArrivedTime', - 'genesisTime', - 'currentEraGenesisTime', - 'lastFinalizedTime', - - //v1 - 'triggerBlockTime', - ]; - const bigIntPropertyKeys: (keyof CS | keyof ConcordiumBftStatus)[] = [ - 'epochDuration', - 'slotDuration', - 'bestBlockHeight', - 'lastFinalizedBlockHeight', - 'finalizationCount', - 'blocksVerifiedCount', - 'blocksReceivedCount', - 'protocolVersion', - - // v1 - 'currentTimeoutDuration', - 'currentRound', - 'currentEpoch', - ]; - - const consensusStatus = convertJsonResponse( - response, - buildJsonResponseReviver(datePropertyKeys, bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - - if (!consensusStatus) { - throw new Error( - 'Nothing was returned when trying to get the consensus status.' - ); - } - - return consensusStatus; - } - - /** - * Retrieves the global cryptographic parameters on the blockchain at - * the provided block. - * @param blockHash the block to get the cryptographic parameters at - * @returns the global cryptographic parameters at the given block, or undefined it the block does not exist. - */ - async getCryptographicParameters( - blockHash: string - ): Promise | undefined> { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const input: BlockHash = { blockHash }; - const { value } = await this.client.getCryptographicParameters(input) - .response; - return convertJsonResponse(value); - } - - /** - * Retrieves a list of the node's peers and connection information related to them. - * @param includeBootstrappers whether or not any bootstrapper nodes should be included in the list - * @returns a list of the node's peers and connection information related to them - */ - async getPeerList( - includeBootstrappers: boolean - ): Promise { - const input: PeersRequest = { - includeBootstrappers, - }; - return this.client.peerList(input).response; - } - - /** - * Retrieves the list of identity providers at the provided blockhash. - * @param blockHash the block to get the identity providers at - * @returns the list of identity providers at the given block - */ - async getIdentityProviders( - blockHash: string - ): Promise { - const input: BlockHash = { blockHash }; - const { value } = await this.client.getIdentityProviders(input) - .response; - return convertJsonResponse(value); - } - - /** - * Retrieves the list of anonymity revokers at the provided blockhash. - * @param blockHash the block to get the anonymity revokers at - * @returns the list of anonymity revokers at the given block - */ - async getAnonymityRevokers( - blockHash: string - ): Promise { - const input: BlockHash = { blockHash }; - const { value } = await this.client.getAnonymityRevokers(input) - .response; - return convertJsonResponse(value); - } - - /** - * Retrieves the addresses of all smart contract instances. - * @param blockHash the block hash to get the smart contact instances at - * @returns a list of contract addresses on the chain, i.e. [{"subindex":0,"index":0},{"subindex":0,"index":1}, ....] - */ - async getInstances( - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - const input: BlockHash = { blockHash }; - const { value } = await this.client.getInstances(input).response; - const bigIntPropertyKeys: (keyof ContractAddress.Type)[] = [ - 'index', - 'subindex', - ]; - - return convertJsonResponse( - value, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - /** - * Retrieve information about a given smart contract instance. - * @param blockHash the block hash to get the smart contact instances at - * @param address the address of the smart contract - * @returns A JSON object with information about the contract instance - */ - async getInstanceInfo( - address: ContractAddress.Type, - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - const input: GetAddressInfoRequest = { - address: `{"subindex":${address.subindex},"index":${address.index}}`, - blockHash, - }; - const { value } = await this.client.getInstanceInfo(input).response; - - const result = convertJsonResponse(value); - if (result !== undefined) { - const common = { - amount: new CcdAmount(BigInt(result.amount)), - sourceModule: new ModuleReference(result.sourceModule), - owner: Address.fromBase58(result.owner), - methods: result.methods.map(ReceiveName.fromStringUnchecked), - name: InitName.fromStringUnchecked(result.name), - }; - - switch (result.version) { - case 1: - return { - version: 1, - ...common, - }; - case undefined: - case 0: - return { - version: 0, - ...common, - model: Buffer.from(result.model, 'hex'), - }; - default: - throw new Error( - 'InstanceInfo had unsupported version: ' + - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (result as any).version - ); - } - } - } - - async getRewardStatus( - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - type DateKey = KeysMatching; - type BigIntKey = KeysMatching; - - const dates: DateKey[] = ['nextPaydayTime']; - const bigInts: BigIntKey[] = [ - 'protocolVersion', - 'gasAccount', - 'totalAmount', - 'totalStakedCapital', - 'bakingRewardAccount', - 'totalEncryptedAmount', - 'finalizationRewardAccount', - 'foundationTransactionRewards', - ]; - - const input: BlockHash = { blockHash }; - const { value } = await this.client.getRewardStatus(input).response; - - return convertJsonResponse( - value, - buildJsonResponseReviver(dates, bigInts), - intToStringTransformer(bigInts) - ); - } - - /** - * Retrieve list of bakers on the network. - * @param blockHash the block hash to get the smart contact instances at - * @returns A JSON list of baker IDs - */ - async getBakerList(blockHash: string): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const input: BlockHash = { blockHash }; - const { value } = await this.client.getBakerList(input).response; - - return convertJsonResponse( - value, - undefined, - intListToStringList - )?.map((v) => BigInt(v)); - } - - /** - * Gets the status of passive delegation. - * @param blockHash the block hash the status at - * @returns The status of passive delegation. - */ - async getPoolStatus( - blockHash: string - ): Promise; - /** - * Gets the status a baker. - * @param blockHash the block hash the status at - * @param bakerId the ID of the baker to get the status for. - * @returns The status of the corresponding baker pool. - */ - async getPoolStatus( - blockHash: string, - bakerId: BakerId - ): Promise; - /** - * Gets the status of either a baker, if a baker ID is supplied, or passive delegation if left undefined. - * @param blockHash the block hash the status at - * @param [bakerId] the ID of the baker to get the status for. If left undefined, the status of passive delegation is returned. - * @returns The status of the corresponding pool. - */ - async getPoolStatus( - blockHash: string, - bakerId?: BakerId - ): Promise; - async getPoolStatus( - blockHash: string, - bakerId?: BakerId - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const input: GetPoolStatusRequest = { - blockHash, - passiveDelegation: bakerId === undefined, - bakerId: bakerId?.toString() ?? '', - }; - const { value } = await this.client.getPoolStatus(input).response; - - type DateKey = KeysMatching< - BakerPoolPendingChangeReduceBakerCapitalDetails, - Date - >; - type BigIntKey = KeysMatching< - BakerPoolStatusDetails & - PassiveDelegationStatusDetails & - CurrentPaydayBakerPoolStatus, - bigint - >; - - const dates: DateKey[] = ['effectiveTime']; - const bigInts: BigIntKey[] = [ - 'bakerId', - 'bakerEquityCapital', - 'delegatedCapital', - 'delegatedCapitalCap', - 'currentPaydayTransactionFeesEarned', - 'currentPaydayDelegatedCapital', - 'blocksBaked', - 'transactionFeesEarned', - 'effectiveStake', - 'allPoolTotalCapital', - ]; - - return convertJsonResponse( - value, - buildJsonResponseReviver(dates, bigInts), - intToStringTransformer(bigInts) - ); - } - - /** - * Retrieves the source of the given module at - * the provided block. - * @param moduleReference the module's reference, which is the hex encoded hash of the source. - * @param blockHash the block to get the cryptographic parameters at - * @returns the source of the module as raw bytes. - */ - async getModuleSource( - moduleReference: ModuleReference, - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - const input: GetModuleSourceRequest = { - blockHash, - moduleRef: moduleReference.moduleRef, - }; - const { value } = await this.client.getModuleSource(input).response; - - if (value.length === 0) { - return undefined; - } - return Buffer.from(value); - } - - /** - * Invokes a smart contract. - * @param context the collection of details used to invoke the contract. Must include the address of the contract and the method invoked. - * @param blockHash the block hash at which the contract should be invoked at. The contract is invoked in the state at the end of this block. - * @returns If the node was able to invoke, then a object describing the outcome is returned. - * The outcome is determined by the `tag` field, which is either `success` or `failure`. - * The `usedEnergy` field will always be present, and is the amount of NRG was used during the execution. - * If the tag is `success`, then an `events` field is present, and it contains the events that would have been generated. - * If invoking a V1 contract and it produces a return value, it will be present in the `returnValue` field. - * If the tag is `failure`, then a `reason` field is present, and it contains the reason the update would have been rejected. - * If either the block does not exist, or then node fails to parse of any of the inputs, then undefined is returned. - */ - async invokeContract( - contractContext: ContractContext, - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - const input: InvokeContractRequest = { - blockHash, - context: stringToInt( - JSON.stringify({ - invoker: buildInvoker(contractContext.invoker), - contract: { - subindex: contractContext.contract.subindex.toString(), - index: contractContext.contract.index.toString(), - }, - amount: - contractContext.amount && - contractContext.amount.microCcdAmount.toString(), - method: contractContext.method, - parameter: - contractContext.parameter === undefined - ? undefined - : Parameter.toHexString(contractContext.parameter), - energy: - contractContext.energy && - Number(contractContext.energy.toString()), - }), - ['index', 'subindex'] - ), - }; - const { value } = await this.client.invokeContract(input).response; - const bigIntPropertyKeys = [ - 'usedEnergy', - 'index', - 'subindex', - 'amount', - ]; - return convertJsonResponse( - value, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } -} diff --git a/packages/nodejs/src/index.ts b/packages/nodejs/src/index.ts index dc78a5a2e..6d45c3035 100644 --- a/packages/nodejs/src/index.ts +++ b/packages/nodejs/src/index.ts @@ -2,7 +2,6 @@ export * from './pub/types.js'; export * from './pub/schema.js'; export * from './pub/wasm.js'; export * from './pub/id.js'; -export * from './pub/client.js'; export * from './pub/grpc.js'; export * from './pub/cis0.js'; export * from './pub/cis2.js'; diff --git a/packages/nodejs/src/pub/client.ts b/packages/nodejs/src/pub/client.ts deleted file mode 100644 index 6233fa85b..000000000 --- a/packages/nodejs/src/pub/client.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as ConcordiumNodeClient } from '../client.js'; diff --git a/packages/nodejs/src/pub/json-rpc.ts b/packages/nodejs/src/pub/json-rpc.ts deleted file mode 100644 index 893cc8bcf..000000000 --- a/packages/nodejs/src/pub/json-rpc.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@concordium/common-sdk/json-rpc'; diff --git a/packages/nodejs/src/util.ts b/packages/nodejs/src/util.ts index 261d40aa7..ff8fa5069 100644 --- a/packages/nodejs/src/util.ts +++ b/packages/nodejs/src/util.ts @@ -1,32 +1,5 @@ import * as fs from 'fs'; -/** - * @deprecated This is a helper function for the v1 gRPC client, which has been deprecated - */ -export function intListToStringList(jsonStruct: string): string { - return jsonStruct.replace(/(\-?[0-9]+)/g, '"$1"'); -} - -/** - * Converts a JsonResponse to type T - * @param reviver JSON reviver function to change types while parsing - * @param transformer a function to transform the JSON string prior to parsing the JSON - * @returns The converted JSON object - * @deprecated This is a helper function for the v1 gRPC client, which has been deprecated - */ -export function convertJsonResponse( - jsonString: string, - reviver?: (this: unknown, key: string, value: unknown) => unknown, - transformer?: (json: string) => string -): T { - if (transformer) { - const transformedJson = transformer(jsonString); - return JSON.parse(transformedJson, reviver); - } - - return JSON.parse(jsonString, reviver); -} - /** * Loads the module as a buffer, given the given filePath. * @param filePath the location of the module diff --git a/packages/nodejs/test/client.test.ts b/packages/nodejs/test/client.test.ts deleted file mode 100644 index e05d25791..000000000 --- a/packages/nodejs/test/client.test.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { - isBlockSummaryV0, - isBlockSummaryV1, - isBlockSummaryV2, - isConsensusStatusV0, - isConsensusStatusV1, -} from '@concordium/common-sdk'; -import { getNodeClient } from './testHelpers.js'; - -/** - * These tests mostly serve the purpose of making sure that the types exposed follow the format returned by the API, - * i.e. we don't change the types to conform to the v2 API, but rather translate the v2 types to the v1 types until we can - * remove the v1 API entirely. - */ - -const client = getNodeClient(); - -// eslint-disable-next-line prefer-const -let CHAIN_GENESIS_BLOCK: string | undefined = undefined; -// eslint-disable-next-line prefer-const -let PV5_BLOCK: string | undefined = undefined; -// eslint-disable-next-line prefer-const -let PV6_BLOCK: string | undefined = undefined; - -// Testnet blocks. -CHAIN_GENESIS_BLOCK = - '4221332d34e1694168c2a0c0b3fd0f273809612cb13d000d5c2e00e85f50f796'; -PV5_BLOCK = '58daebb41ca195442593e10c1a67279bb839a8195c8ea7442ea7116d87114fbb'; - -test.each([CHAIN_GENESIS_BLOCK, PV5_BLOCK, PV6_BLOCK])( - 'blockSummary format as expected', - async (block) => { - if (block === undefined) { - return; - } - - const bs = await client.getBlockSummary(block); - - if (!bs) { - throw new Error('could not find block'); - } - - // BlockSummary - expect(typeof bs.protocolVersion).toEqual('bigint'); - - // UpdateQueues - expect( - typeof bs.updates.updateQueues.foundationAccount.nextSequenceNumber - ).toEqual('bigint'); - expect( - Array.isArray(bs.updates.updateQueues.foundationAccount.queue) - ).toBeTruthy(); - expect( - typeof bs.updates.updateQueues.euroPerEnergy.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.mintDistribution.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.microGTUPerEuro.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.protocol.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.rootKeys.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.gasRewards.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.level1Keys.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.level2Keys.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.addAnonymityRevoker - .nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.addIdentityProvider - .nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.transactionFeeDistribution - .nextSequenceNumber - ).toEqual('bigint'); - - // Keys - expect(bs.updates.keys.rootKeys.keys).toBeDefined(); - expect(bs.updates.keys.rootKeys.threshold).toBeDefined(); - expect(bs.updates.keys.level1Keys.keys).toBeDefined(); - expect(bs.updates.keys.level2Keys.keys).toBeDefined(); - expect( - bs.updates.keys.level2Keys.addAnonymityRevoker.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.poolParameters.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.transactionFeeDistribution.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.addIdentityProvider.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.protocol.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.microGTUPerEuro.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.mintDistribution.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.euroPerEnergy.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.foundationAccount.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.electionDifficulty.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.emergency.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.paramGASRewards.authorizedKeys - ).toBeDefined(); - - // Test format of chain parameters holds - expect( - typeof bs.updates.chainParameters.microGTUPerEuro.numerator - ).toEqual('bigint'); - expect( - typeof bs.updates.chainParameters.microGTUPerEuro.denominator - ).toEqual('bigint'); - expect( - bs.updates.chainParameters.rewardParameters.gASRewards.baker - ).toBeDefined(); - expect( - bs.updates.chainParameters.rewardParameters.gASRewards.chainUpdate - ).toBeDefined(); - expect( - bs.updates.chainParameters.rewardParameters.gASRewards - .accountCreation - ).toBeDefined(); - expect( - bs.updates.chainParameters.rewardParameters.mintDistribution - .bakingReward - ).toBeDefined(); - expect( - bs.updates.chainParameters.rewardParameters.mintDistribution - .finalizationReward - ).toBeDefined(); - expect(bs.updates.chainParameters.euroPerEnergy).toBeDefined(); - expect(bs.updates.chainParameters.foundationAccountIndex).toBeDefined(); - expect(bs.updates.chainParameters.accountCreationLimit).toBeDefined(); - - if (isBlockSummaryV0(bs)) { - expect( - typeof bs.updates.updateQueues.electionDifficulty - .nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.bakerStakeThreshold - .nextSequenceNumber - ).toEqual('bigint'); - - expect( - typeof bs.updates.chainParameters.bakerCooldownEpochs - ).toEqual('bigint'); - } else if (isBlockSummaryV1(bs)) { - expect( - typeof bs.updates.updateQueues.electionDifficulty - .nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.poolParameters.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.timeParameters.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.cooldownParameters - .nextSequenceNumber - ).toEqual('bigint'); - - expect( - bs.updates.keys.level2Keys.cooldownParameters.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.timeParameters.authorizedKeys - ).toBeDefined(); - - expect(bs.updates.chainParameters.electionDifficulty).toBeDefined(); - expect( - bs.updates.chainParameters.rewardParameters.gASRewards - .finalizationProof - ); - expect(bs.updates.chainParameters.mintPerPayday).toBeDefined(); - expect(bs.updates.chainParameters.capitalBound).toBeDefined(); - expect(bs.updates.chainParameters.leverageBound).toBeDefined(); - expect( - bs.updates.chainParameters.finalizationCommissionRange.min - ).toBeDefined(); - } else if (isBlockSummaryV2(bs)) { - expect( - typeof bs.updates.updateQueues.consensus2TimingParameters - .nextSequenceNumber - ).toEqual('bigint'); - - expect( - bs.updates.keys.level2Keys.cooldownParameters.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.timeParameters.authorizedKeys - ).toBeDefined(); - - expect(bs.updates.chainParameters.minimumFinalizers).toBeDefined(); - expect(bs.updates.chainParameters.maximumFinalizers).toBeDefined(); - expect(typeof bs.updates.chainParameters.blockEnergyLimit).toEqual( - 'bigint' - ); - expect(typeof bs.updates.chainParameters.minBlockTime).toEqual( - 'bigint' - ); - expect(typeof bs.updates.chainParameters.timeoutBase).toEqual( - 'bigint' - ); - expect( - typeof bs.updates.chainParameters.timeoutDecrease.numerator - ).toEqual('bigint'); - expect( - typeof bs.updates.chainParameters.timeoutIncrease.denominator - ).toEqual('bigint'); - expect( - bs.updates.chainParameters.finalizerRelativeStakeThreshold - ).toBeDefined(); - } - } -); - -test('consensusStatus format as expected', async () => { - const cs = await client.getConsensusStatus(); - - expect(typeof cs.protocolVersion).toEqual('bigint'); - expect(cs.bestBlock).toBeDefined(); - expect(cs.genesisTime instanceof Date).toBeTruthy(); - expect(cs.genesisBlock).toBeDefined(); - expect(cs.genesisIndex).toBeDefined(); - expect(typeof cs.epochDuration).toEqual('bigint'); - expect(typeof cs.bestBlockHeight).toEqual('bigint'); - expect(typeof cs.finalizationCount).toEqual('bigint'); - expect(cs.lastFinalizedBlock).toBeDefined(); - expect(typeof cs.blocksReceivedCount).toEqual('bigint'); - expect(typeof cs.blocksVerifiedCount).toEqual('bigint'); - expect(cs.blockArriveLatencyEMA).toBeDefined(); - expect(cs.blockLastArrivedTime instanceof Date).toBeTruthy(); - expect(cs.currentEraGenesisTime instanceof Date).toBeTruthy(); - expect(cs.blockArriveLatencyEMSD).toBeDefined(); - expect(cs.currentEraGenesisBlock).toBeDefined(); - expect(cs.transactionsPerBlockEMA).toBeDefined(); - expect(typeof cs.lastFinalizedBlockHeight).toEqual('bigint'); - expect(cs.transactionsPerBlockEMSD).toBeDefined(); - - if (isConsensusStatusV0(cs)) { - expect(typeof cs.slotDuration).toEqual('bigint'); - } else if (isConsensusStatusV1(cs)) { - expect(typeof cs.concordiumBFTStatus.currentTimeoutDuration).toEqual( - 'bigint' - ); - expect(typeof cs.concordiumBFTStatus.currentEpoch).toEqual('bigint'); - expect(typeof cs.concordiumBFTStatus.currentRound).toEqual('bigint'); - expect( - cs.concordiumBFTStatus.triggerBlockTime instanceof Date - ).toBeTruthy(); - } -}); diff --git a/packages/nodejs/test/deserialization.test.ts b/packages/nodejs/test/deserialization.test.ts index 6727643dc..c55f43958 100644 --- a/packages/nodejs/test/deserialization.test.ts +++ b/packages/nodejs/test/deserialization.test.ts @@ -1,8 +1,9 @@ -import { getNodeClient } from './testHelpers.js'; +import { getNodeClientV2 as getNodeClient } from './testHelpers.js'; import { ContractAddress, ContractName, isInstanceInfoV0, + BlockHash, } from '@concordium/common-sdk'; import * as fs from 'fs'; import { deserializeContractState } from '@concordium/common-sdk/schema'; @@ -17,7 +18,7 @@ test.skip('Deserialize state with schema from file (two-step-transfer)', async ( const instanceInfo = await client.getInstanceInfo( contractAddress, - blockHash + BlockHash.fromHexString(blockHash) ); if (!instanceInfo) { throw new Error( diff --git a/packages/nodejs/test/resources/expectedJsons.ts b/packages/nodejs/test/resources/expectedJsons.ts index 6014733e1..f170864ed 100644 --- a/packages/nodejs/test/resources/expectedJsons.ts +++ b/packages/nodejs/test/resources/expectedJsons.ts @@ -450,7 +450,7 @@ export const passiveDelegatorInfoList: DelegatorInfo[] = [ stake: 100000000n, pendingChange: { effectiveTime: new Date('2022-06-28T11:47:37.750Z'), - change: StakePendingChangeType.RemoveStakeV1, + change: StakePendingChangeType.RemoveStake, }, }, ]; @@ -471,6 +471,7 @@ export const passiveDelegatorRewardInfoList: DelegatorRewardPeriodInfo[] = [ ]; export const electionInfoList: ElectionInfoV0 = { + version: 0, electionDifficulty: 0.025, electionNonce: '0bb2121015ddd9026d0c31a8b33499ce6049daf5696fe4e2cd94cff83ad331f2', @@ -763,7 +764,7 @@ export const configureDelegation: DelegationEvent[] = [ account: AccountAddress.fromBase58( '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' ), - delegatorId: 2059, + delegatorId: 2059n, tag: TransactionEventTag.DelegationAdded, }, { @@ -773,14 +774,14 @@ export const configureDelegation: DelegationEvent[] = [ delegationTarget: { delegateType: DelegationTargetType.PassiveDelegation, }, - delegatorId: 2059, + delegatorId: 2059n, tag: TransactionEventTag.DelegationSetDelegationTarget, }, { account: AccountAddress.fromBase58( '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' ), - delegatorId: 2059, + delegatorId: 2059n, restakeEarnings: true, tag: TransactionEventTag.DelegationSetRestakeEarnings, }, @@ -788,7 +789,7 @@ export const configureDelegation: DelegationEvent[] = [ account: AccountAddress.fromBase58( '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' ), - delegatorId: 2059, + delegatorId: 2059n, newStake: 1000000n, tag: TransactionEventTag.DelegationStakeIncreased, }, @@ -1060,7 +1061,7 @@ export const delegationRemovedEvent: DelegationEvent = { account: AccountAddress.fromBase58( '4nvFUvdF3Ki7M6Xc2vHejX7iQW5Gtu7UBu6RaPRZV7LorLToPG' ), - delegatorId: 4002, + delegatorId: 4002n, }; export const transferWithMemoSummary: BaseAccountTransactionSummary & @@ -1159,7 +1160,7 @@ export const delegationStakeDecreasedEvent: DelegationEvent = { account: AccountAddress.fromBase58( '4mAs6xcFw26fb6u8odkJWoe3fAK8bCJ91BwScUc36DFhh3thwD' ), - delegatorId: 57, + delegatorId: 57n, newStake: 10000000n, }; @@ -1538,6 +1539,7 @@ export const bakerAccountInfo: AccountInfo = { '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L' ), accountBaker: { + version: 1, bakerAggregationVerifyKey: 'b18a02de74826e55f6eadc0f31d0d9a6edfb2993d030e65136f1e1256a69ba523acb40fa4d304d0668aa307c19257a0a10726e70149c904e1ef29aedb2679c825997e3f14edd303bf276f2c0b0c5a4c4870fff0c043150be06b715466be564c4', bakerElectionVerifyKey: @@ -1945,6 +1947,7 @@ const level1Keys = { }; export const chainParameters: ChainParametersV1 = { + version: 1, electionDifficulty: 0.025, euroPerEnergy: { numerator: 1n, denominator: 50000n }, microGTUPerEuro: { @@ -1969,17 +1972,24 @@ export const chainParameters: ChainParametersV1 = { capitalBound: 0.1, leverageBound: { numerator: 3n, denominator: 1n }, rewardParameters: { + version: 1, transactionFeeDistribution: { baker: 0.45, gasAccount: 0.45 }, gASRewards: { + version: 0, baker: 0.25, finalizationProof: 0.005, accountCreation: 0.02, chainUpdate: 0.005, }, - mintDistribution: { bakingReward: 0.6, finalizationReward: 0.3 }, + mintDistribution: { + version: 1, + bakingReward: 0.6, + finalizationReward: 0.3, + }, }, level1Keys, level2Keys: { + version: 1, addAnonymityRevoker: { authorizedKeys: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], threshold: 7, @@ -2117,10 +2127,11 @@ export const chainParameters: ChainParametersV1 = { rootKeys, }; -const { cooldownParameters, timeParameters, ...oldLevel2Keys } = +const { cooldownParameters, timeParameters, version, ...oldLevel2Keys } = chainParameters.level2Keys; export const oldChainParameters: ChainParametersV0 = { + version: 0, electionDifficulty: 0.025, euroPerEnergy: { numerator: 1n, denominator: 50000n }, microGTUPerEuro: { numerator: 50000000n, denominator: 1n }, @@ -2131,21 +2142,24 @@ export const oldChainParameters: ChainParametersV0 = { bakerCooldownEpochs: 166n, minimumThresholdForBaking: 15000000000n, rewardParameters: { + version: 0, transactionFeeDistribution: { baker: 0.45, gasAccount: 0.45 }, gASRewards: { + version: 0, baker: 0.25, finalizationProof: 0.005, accountCreation: 0.02, chainUpdate: 0.005, }, mintDistribution: { + version: 0, bakingReward: 0.6, finalizationReward: 0.3, mintPerSlot: 7.555665e-10, }, }, level1Keys, - level2Keys: oldLevel2Keys, + level2Keys: { version: 0, ...oldLevel2Keys }, rootKeys, }; @@ -2249,6 +2263,7 @@ export const blocksAtHeight: BlockHash.Type[] = [ ].map(BlockHash.fromHexString); export const blockInfo: BlockInfoV0 = { + version: 0, blockParent: BlockHash.fromHexString( '28d92ec42dbda119f0b0207d3400b0573fe8baf4b0d3dbe44b86781ad6b655cf' ), diff --git a/packages/nodejs/test/testHelpers.ts b/packages/nodejs/test/testHelpers.ts index 2e4c2f290..072f629e5 100644 --- a/packages/nodejs/test/testHelpers.ts +++ b/packages/nodejs/test/testHelpers.ts @@ -9,39 +9,16 @@ import { } from '../src/wallet/crypto.js'; import { MobileWalletExport } from '../src/wallet/types.js'; import { createConcordiumClient } from '../src/clientV2.js'; -import ConcordiumNodeClient from '../src/client.js'; import { GrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport'; // This makes sure the necessary types are added to `globalThis` import 'isomorphic-fetch'; -import { RpcMetadata } from '@protobuf-ts/runtime-rpc'; export { getModuleBuffer } from '../src/util.js'; const TESTNET_NODE = 'node.testnet.concordium.com'; -const GRPCV1_PORT = 10000; const GRPCV2_PORT = 20000; -/** - * Creates a gRPC v1 client (for nodeJS) to communicate with a local concordium-node - * used for automatic tests. - */ -export function getNodeClient( - address = TESTNET_NODE, - port = GRPCV1_PORT -): ConcordiumNodeClient { - const metadata: RpcMetadata = { - authentication: 'rpcadmin', - }; - return new ConcordiumNodeClient( - address, - port, - credentials.createInsecure(), - metadata, - 15000 - ); -} - /** * Creates a gRPC v2 client (for nodeJS) to communicate with a local concordium-node * used for automatic tests. diff --git a/packages/nodejs/test/util.test.ts b/packages/nodejs/test/util.test.ts deleted file mode 100644 index 16300dcc8..000000000 --- a/packages/nodejs/test/util.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { intListToStringList } from '../src/util.js'; - -test('Correctly converts stringified list of numbers to stringified list of corresponding strings', () => { - // List of ints - let numbers = '[1, 22, 332]'; - let strings = intListToStringList(numbers); - - expect(strings).toEqual('["1", "22", "332"]'); - - // Empty list - numbers = '[]'; - strings = intListToStringList(numbers); - - expect(strings).toEqual('[]'); - - // Single int list - numbers = '[1]'; - strings = intListToStringList(numbers); - - expect(strings).toEqual('["1"]'); - - // negative int list - numbers = '[-1, 21, -32]'; - strings = intListToStringList(numbers); - - expect(strings).toEqual('["-1", "21", "-32"]'); -}); diff --git a/packages/web/CHANGELOG.md b/packages/web/CHANGELOG.md index 1de1e0bdc..b99df82b7 100644 --- a/packages/web/CHANGELOG.md +++ b/packages/web/CHANGELOG.md @@ -12,7 +12,6 @@ - `@concordium/web-sdk/cis4` entrypoint exposes functionality for working with contracts adhering to the [CIS-4](https://proposals.concordium.software/CIS/cis-4.html) standard. - `@concordium/web-sdk/grpc` entrypoint exposes the grpc client for interacting with a nodes GRPCv2 interface. - `@concordium/web-sdk/id` entrypoint exposes functionality for working with ID proofs. - - `@concordium/web-sdk/json-rpc` entrypoint exposes the **(deprecated)** json-rpc client for interacting with a nodes GPRCv1 interface. - `@concordium/web-sdk/schema` entrypoint exposes functionality for working with smart contract schemas, i.e.(de)serializing types using a smart contract schema. - This uses the wasm entrypoint at `@concordium/rust-bindings/dapp`. - `@concordium/web-sdk/types` entrypoint exposes functionality for working with concordium domain types. @@ -24,7 +23,6 @@ - NodeJS: 4.7, `"moduleResolution": "node16" // or "nodenext"` - Bundled applications (webpack, esbuild, rollup, etc...): 5.0, `"moduleResolution": "bundler"` - The API now uses dedicated types instead of language primitives: - Use `AccountAddress` instead of a string with base58 encoding. Use `AccountAddress.fromBase58('')` to construct it. - Use `BlockHash` instead of a string with hex encoding. Use `BlockHash.fromHexString('')` to construct it. @@ -50,6 +48,7 @@ Several types have been replaced with a module containing the type itself togeth - `CredentialRegistrationId` is now a module with functions related to credential registration IDs: - To refer to `CredentialRegistrationId` as a type use `CredentialRegistrationId.Type`. - Constructing `new CredentialRegistrationId("")` is now `CredentialRegistrationId.fromHexString("")`. +- Removed `JsonRpcClient` and types and functionality associated solely with this class. - Renamed `AccountSequenceNumber` module to `SequenceNumber`. - Fix type for `TranferredEvent` from `ContractTraceEvent` to only be from contract addresses to account addresses. diff --git a/packages/web/src/index.ts b/packages/web/src/index.ts index c9fac73ca..caf825c55 100644 --- a/packages/web/src/index.ts +++ b/packages/web/src/index.ts @@ -6,7 +6,6 @@ export * from './pub/schema.js'; export * from './pub/wasm.js'; export * from './pub/id.js'; export * from './pub/grpc.js'; -export * from './pub/json-rpc.js'; export * from './pub/cis0.js'; export * from './pub/cis2.js'; export * from './pub/cis4.js'; diff --git a/packages/web/src/pub/json-rpc.ts b/packages/web/src/pub/json-rpc.ts deleted file mode 100644 index 893cc8bcf..000000000 --- a/packages/web/src/pub/json-rpc.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@concordium/common-sdk/json-rpc'; diff --git a/yarn.lock b/yarn.lock index fe713eed7..508ee956f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1661,12 +1661,7 @@ __metadata: "@grpc/grpc-js": ^1.3.4 "@noble/ed25519": ^1.7.1 "@protobuf-ts/grpc-transport": ^2.8.2 - "@protobuf-ts/grpcweb-transport": ^2.8.2 - "@protobuf-ts/plugin": ^2.9.1 - "@protobuf-ts/runtime": ^2.9.1 - "@protobuf-ts/runtime-rpc": ^2.9.1 "@types/bs58check": ^2.1.0 - "@types/google-protobuf": ^3.15.3 "@types/jest": ^26.0.23 "@typescript-eslint/eslint-plugin": ^6.7.0 "@typescript-eslint/parser": ^6.7.0 @@ -1676,9 +1671,6 @@ __metadata: eslint-config-prettier: ^8.3.0 eslint-plugin-import: ^2.23.4 eslint-plugin-prettier: ^3.4.0 - google-protobuf: ^3.20.1 - grpc-tools: ^1.11.2 - grpc_tools_node_protoc_ts: 5.3.0 isomorphic-fetch: ^3.0.0 jest: ^29.6.2 lint-staged: ^12.0.2 @@ -2684,13 +2676,6 @@ __metadata: languageName: node linkType: hard -"@types/google-protobuf@npm:^3.15.3": - version: 3.15.6 - resolution: "@types/google-protobuf@npm:3.15.6" - checksum: e8f7cd9d263093cd0a72eb10ff0bb8221e28aa8b9f601eddbe19b286932aae96fbc9ccb1f3a744525e7811fa87a6aeefd431858b6e2034f14a2dc68710a86d54 - languageName: node - linkType: hard - "@types/graceful-fs@npm:^4.1.2": version: 4.1.5 resolution: "@types/graceful-fs@npm:4.1.5" @@ -5599,13 +5584,6 @@ __metadata: languageName: node linkType: hard -"google-protobuf@npm:^3.20.1": - version: 3.20.1 - resolution: "google-protobuf@npm:3.20.1" - checksum: a440fbc329bb441b253bc421b01540918f4b66921c47a3bc57d154d2677dc917cb1a29126ff35ddc4d8872d1b5843ef1ece60b6dff506ba64a9f5807962d3f61 - languageName: node - linkType: hard - "graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10"