Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/wallet/src/CreateAccount.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
CredentialDeploymentTransaction,
CredentialDeploymentPayload,
CredentialInputNoSeed,
IdentityObjectV1,
getAccountAddress,
Expand Down Expand Up @@ -43,7 +43,7 @@ export function CreateAccount({ identity }: { identity: IdentityObjectV1 }) {
return;
}

const listener = (worker.onmessage = async (e: MessageEvent<CredentialDeploymentTransaction>) => {
const listener = (worker.onmessage = async (e: MessageEvent<CredentialDeploymentPayload>) => {
worker.removeEventListener('message', listener);
const credentialDeploymentTransaction = e.data;
const signingKey = getAccountSigningKey(seedPhrase, credentialDeploymentTransaction.unsignedCdi.ipIdentity);
Expand Down
4 changes: 2 additions & 2 deletions examples/wallet/src/account-worker.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createCredentialTransactionNoSeed } from '@concordium/web-sdk';
import { createCredentialPayloadNoSeed } from '@concordium/web-sdk';

import { AccountWorkerInput } from './types';

self.onmessage = (e: MessageEvent<AccountWorkerInput>) => {
const credentialTransaction = createCredentialTransactionNoSeed(e.data.credentialInput, e.data.expiry);
const credentialTransaction = createCredentialPayloadNoSeed(e.data.credentialInput, e.data.expiry);
self.postMessage(credentialTransaction);
};
4 changes: 2 additions & 2 deletions examples/wallet/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
CcdAmount,
ConcordiumGRPCWebClient,
ConcordiumHdWallet,
CredentialDeploymentTransaction,
CredentialDeploymentPayload,
CryptographicParameters,
IdObjectRequestV1,
IdRecoveryRequest,
Expand Down Expand Up @@ -234,7 +234,7 @@ export function createCredentialDeploymentKeysAndRandomness(
* @returns a promise with the transaction hash of the submitted credential deployment
*/
export async function sendCredentialDeploymentTransaction(
credentialDeployment: CredentialDeploymentTransaction,
credentialDeployment: CredentialDeploymentPayload,
signature: string
) {
const payload = serializeCredentialDeploymentPayload([signature], credentialDeployment);
Expand Down
7 changes: 7 additions & 0 deletions packages/sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
- Renamed `TokenModuleRejectReason` to `EncodedTokenModuleRejectReason`, aligning with the corresponding types for
`TokenModuleEvent`. `TokenModuleRejectReason` now describes the decoded version of `EncodedTokenModuleRejectReason`.
- `parseTokenModuleEvent` (previously `parseModuleEvent`) now returns `TokenModuleEvent | UnknownTokenModuleEvent`
- Rename `CredentialDeploymentTransaction` to `CredentialDeploymentPayload`, and correspondingly
- `createCredentialDeploymentTransaction` -> `createCredentialDeploymentPayload`
- `createCredentialTransaction` -> `createCredentialPayload`
- `createCredentialTransactionNoSeed` -> `createCredentialPayloadNoSeed`

#### GRPC API query response types

Expand All @@ -40,6 +44,9 @@
- Affects `InvokeContractResultSuccess`
- Affects `UpdateContractSummary`
- `ContractVersion` enum has been removed and replaced with `number` where it was used.
- `VerifyKey` uses has now been wrapped in `Upward`, affecting the types
- `CredentialPublicKeys`, bleeding into top-level types `CredentialDeploymentInfo`, `InitialAccountCredential` and
`NormalAccountCredential`

#### `ConcordiumGRPCClient`:

Expand Down
2 changes: 2 additions & 0 deletions packages/sdk/src/accountTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,15 @@ export interface AccountTransactionHandler<
*
* @param payload - The payload to serialize.
* @returns The serialized payload.
* @throws If serializing the type was not possible.
*/
serialize: (payload: PayloadType) => Buffer;

/**
* Deserializes the serialized payload into the payload type.
* @param serializedPayload - The serialized payload to be deserialized.
* @returns The deserialized payload.
* @throws If deserializing the type was not possible.
*/
deserialize: (serializedPayload: Cursor) => PayloadType;

Expand Down
20 changes: 10 additions & 10 deletions packages/sdk/src/grpc/translation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,15 @@ function trCommits(cmm: GRPC.CredentialCommitments): SDK.CredentialDeploymentCom
};
}

function trVerifyKey(verifyKey: GRPC.AccountVerifyKey): SDK.VerifyKey {
if (verifyKey.key.oneofKind === 'ed25519Key') {
return {
schemeId: 'Ed25519',
verifyKey: unwrapToHex(verifyKey.key.ed25519Key),
};
} else {
throw Error('AccountVerifyKey was expected to be of type "ed25519Key", but found' + verifyKey.key.oneofKind);
function trVerifyKey(verifyKey: GRPC.AccountVerifyKey): Upward<SDK.VerifyKey> {
switch (verifyKey.key.oneofKind) {
case 'ed25519Key':
return {
schemeId: 'Ed25519',
verifyKey: unwrapToHex(verifyKey.key.ed25519Key),
};
case undefined:
return null;
}
}

Expand Down Expand Up @@ -1610,9 +1611,8 @@ function trCommissionRange(range: GRPC.InclusiveRangeAmountFraction | undefined)
max: trAmountFraction(range?.max),
};
}
function trUpdatePublicKey(key: GRPC.UpdatePublicKey): SDK.VerifyKey {
function trUpdatePublicKey(key: GRPC.UpdatePublicKey): SDK.UpdatePublicKey {
return {
schemeId: 'Ed25519',
verifyKey: unwrapValToHex(key),
};
}
Expand Down
40 changes: 31 additions & 9 deletions packages/sdk/src/grpc/upward.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,58 @@
import { bail } from '../util.js';

export type Unknown = null;

/**
* Represents types returned by the GRPC API of a Concordium node which are
* possibly unknown to the SDK version. `null` means that the type is unknown.
* possibly unknown to the SDK version. {@linkcode Unknown} means that the type is unknown.
*
* @template T - The type representing the known variants
*
* @example
* // fail on unknown value
* const upwardValue: Upward<string> = ...
* if (upwardValue === null) {
* if (!isKnown(upwardValue)) {
* throw new Error('Uncountered unknown value')
* }
* // the value is known from this point
*
* @example
* // gracefully handle unknown values
* const upwardValue: Upward<string> = ...
* if (upwardValue === null) {
* if (!isKnown(upwardValue)) {
* console.warn('Uncountered unknown value')
* } else {
* // the value is known from this point
* }
*/
export type Upward<T> = T | null;
export type Upward<T> = T | Unknown;

// Recursively remove all occurrences of `null` (or `Unknown`) from a type. Since `null` is only
// used via the Upward<T> sentinel (and never intentionally in other field types),
// this yields a type appropriate for constructing outbound payloads where all
// values must be known.
export type Known<T> = T extends Unknown
? never
: T extends Function
? T
: T extends Map<infer K, infer V>
? Map<Known<K>, Known<V>>
: T extends Set<infer S>
? Set<Known<S>>
: T extends readonly (infer U)[]
? T extends readonly [any, ...any[]]
? { [I in keyof T]: Known<T[I]> }
: Known<U>[]
: T extends object
? { [P in keyof T]: Known<T[P]> }
: T;

/**
* Type guard that checks whether an Upward<T> holds a known value.
*
* @template T - The type representing the known variants
* @param value - The possibly-unknown value returned from gRPC.
* @returns True if value is not null (i.e., is T).
* @param value - The possibly {@linkcode Unknown} value returned from gRPC.
* @returns True if value is not {@linkcode Unknown} (i.e., is T).
*/
export function isKnown<T>(value: Upward<T>): value is T {
return value !== null;
Expand All @@ -39,10 +61,10 @@ export function isKnown<T>(value: Upward<T>): value is T {
/**
* Asserts that an Upward<T> is known, otherwise throws the provided error.
*
* Useful when unknowns should be treated as hard failures.
* Useful when {@linkcode Unknown} values should be treated as hard failures.
*
* @template T - The type representing the known variants
* @param value - The possibly-unknown value returned from gRPC.
* @param value - The possibly {@linkcode Unknown} value returned from gRPC.
* @param error - Error to throw if value is unknown.
* @returns True as a type predicate when value is known.
*/
Expand All @@ -54,7 +76,7 @@ export function assertKnown<T>(value: Upward<T>, error: Error | string): value i
* Returns the known value or throws the provided error when unknown.
*
* @template T - The type representing the known variants
* @param value - The possibly-unknown value returned from gRPC.
* @param value - The possibly {@linkcode Unknown} value returned from gRPC.
* @param error - Error to throw if value is unknown.
* @returns The unwrapped known value of type T.
*/
Expand Down
12 changes: 11 additions & 1 deletion packages/sdk/src/serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Buffer } from 'buffer/index.js';

import { getAccountTransactionHandler } from './accountTransactions.js';
import { calculateEnergyCost } from './energyCost.js';
import { Known, isKnown } from './grpc/upward.js';
import { sha256 } from './hash.js';
import {
encodeWord8,
Expand Down Expand Up @@ -195,9 +196,18 @@ export function serializeAccountTransactionForSubmission(
* @returns the serialization of CredentialDeploymentValues
*/
function serializeCredentialDeploymentValues(credential: CredentialDeploymentValues) {
// Check that we don't attempt to serialize unknown variants
if (Object.values(credential.credentialPublicKeys.keys).some((v) => !isKnown(v)))
throw new Error('Cannot serialize unknown key variants');

const buffers = [];
buffers.push(
serializeMap(credential.credentialPublicKeys.keys, encodeWord8, encodeWord8FromString, serializeVerifyKey)
serializeMap(
credential.credentialPublicKeys.keys as Known<typeof credential.credentialPublicKeys.keys>,
encodeWord8,
encodeWord8FromString,
serializeVerifyKey
)
);

buffers.push(encodeWord8(credential.credentialPublicKeys.threshold));
Expand Down
19 changes: 10 additions & 9 deletions packages/sdk/src/signHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,16 +307,17 @@ export async function verifyMessageSignature(
}

for (const keyIndex of Object.keys(credentialSignature)) {
if (!credentialKeys.keys[Number(keyIndex)]) {
throw new Error('Signature contains signature for non-existing keyIndex');
const key = credentialKeys.keys[Number(keyIndex)];
switch (key) {
case undefined:
throw new Error('Signature contains signature for non-existing keyIndex');
case null:
throw new Error('Found "null" (represents unknown key variants) in credential keys');
default:
break;
}
if (
!(await ed.verifyAsync(
credentialSignature[Number(keyIndex)],
digest,
credentialKeys.keys[Number(keyIndex)].verifyKey
))
) {

if (!(await ed.verifyAsync(credentialSignature[Number(keyIndex)], digest, key.verifyKey))) {
// Incorrect signature;
return false;
}
Expand Down
34 changes: 26 additions & 8 deletions packages/sdk/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @module Common GRPC-Client
*/
import type { Upward } from './grpc/index.js';
import type { Known, Upward } from './grpc/index.js';
import type { Cbor, TokenId } from './plt/index.js';
import type { TokenAccountInfo } from './plt/types.js';
import type * as AccountAddress from './types/AccountAddress.js';
Expand Down Expand Up @@ -515,7 +515,8 @@ interface AuthorizationsCommon {
electionDifficulty: Authorization;
addAnonymityRevoker: Authorization;
addIdentityProvider: Authorization;
keys: VerifyKey[];
/** The authorization keys. */
keys: UpdatePublicKey[];
}

/**
Expand All @@ -539,7 +540,8 @@ export interface AuthorizationsV1 extends AuthorizationsCommon {
export type Authorizations = AuthorizationsV0 | AuthorizationsV1;

export interface KeysWithThreshold {
keys: VerifyKey[];
/** The authorization keys. */
keys: UpdatePublicKey[];
threshold: number;
}

Expand Down Expand Up @@ -797,8 +799,25 @@ export interface VerifyKey {
verifyKey: HexString;
}

/**
* Represents a public key used for chain updates.
*/
export type UpdatePublicKey = {
/** The key in hex format */
verifyKey: HexString;
};

export interface CredentialPublicKeys {
keys: Record<number, VerifyKey>;
/**
* keys for the credential
*
* **Please note**, these can possibly be unknown if the SDK is not fully compatible with the Concordium
* node queried, in which case `null` is returned.
*
* In case this is used as part of a transaction sent to the node, none of the values contained can be `null`,
* as this will cause the transation to fail.
*/
keys: Record<number, Upward<VerifyKey>>;
threshold: number;
}

Expand Down Expand Up @@ -1183,7 +1202,7 @@ export interface AccountInfoUnknown extends AccountInfoCommon {
/**
* This will only ever be `null`, which represents a variant of staking info for the account which is
* unknown to the SDK, for known staking variants this is represented by either {@linkcode AccountInfoBaker}
* or {@linkcode AccountInfoDElegator}.
* or {@linkcode AccountInfoDelegator}.
*
* **Note**: This field is named `accountBaker` to align with the JSON representation produced by the
* corresponding rust SDK.
Expand Down Expand Up @@ -1667,11 +1686,10 @@ interface CdiRandomness {
randomness: CommitmentsRandomness;
}

// TODO Should we rename this, As it is not actually the transaction that is sent to the node. (Note that this would be a breaking change)
export type CredentialDeploymentTransaction = CredentialDeploymentDetails & CdiRandomness;
export type CredentialDeploymentPayload = CredentialDeploymentDetails & CdiRandomness;
/** Internal type used when building credentials */
export type UnsignedCdiWithRandomness = {
unsignedCdi: UnsignedCredentialDeploymentInformation;
unsignedCdi: Known<UnsignedCredentialDeploymentInformation>;
} & CdiRandomness;

export interface CredentialDeploymentInfo extends CredentialDeploymentValues {
Expand Down
4 changes: 2 additions & 2 deletions packages/sdk/src/types/VersionedModuleSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ export async function parseModuleInterface(moduleSource: VersionedModuleSource):
}

/**
* Extract the embedded smart contract schema bytes. Returns `null` if no schema is embedded.
* Extract the embedded smart contract schema bytes. Returns `undefined` if no schema is embedded.
* @param {VersionedModuleSource} moduleSource The smart contract module source.
* @returns {RawModuleSchema | null} The raw module schema if found.
* @returns {RawModuleSchema | undefined} The raw module schema if found.
* @throws If the module source cannot be parsed or contains duplicate schema sections.
*/
export async function getEmbeddedModuleSchema({
Expand Down
12 changes: 9 additions & 3 deletions packages/sdk/src/types/chainUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import type {
MintRate,
TimeoutParameters,
TransactionFeeDistribution,
UpdatePublicKey,
ValidatorScoreParameters,
VerifyKey,
} from '../types.js';
import type * as CcdAmount from './CcdAmount.js';
import type * as Duration from './Duration.js';
Expand Down Expand Up @@ -250,7 +250,7 @@ export enum KeyUpdateEntryStatus {
}

export interface KeyWithStatus {
key: VerifyKey;
key: UpdatePublicKey;
status: KeyUpdateEntryStatus;
}

Expand All @@ -261,7 +261,13 @@ export enum HigherLevelKeyUpdateType {

export interface HigherLevelKeyUpdate {
typeOfUpdate: HigherLevelKeyUpdateType;
updateKeys: VerifyKey[];
/**
* The authorization keys included in the update.
*/
updateKeys: UpdatePublicKey[];
/**
* The key threshold needed to perform the update to higher level keys.
*/
threshold: number;
}

Expand Down
Loading