taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 0cd959846389205711dfab9a871d96d573c494cb
parent 99a259a3ebf14c9130b858b3ccbff222331cdacb
Author: Florian Dold <florian@dold.me>
Date:   Wed, 26 Feb 2025 17:01:22 +0100

clean up some types

Diffstat:
Mpackages/aml-backoffice-ui/src/hooks/officer.ts | 4++--
Mpackages/taler-util/src/http-client/exchange.ts | 79+++++++++++++++++++++++++++++++++++++------------------------------------------
Mpackages/taler-util/src/http-client/officer-account.ts | 26+++++++++++++-------------
Mpackages/taler-util/src/taler-crypto.ts | 147++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mpackages/taler-util/src/taler-signatures.ts | 24++++++++++++++++++++++--
Mpackages/taler-util/src/types-taler-common.ts | 10+++-------
Mpackages/taler-wallet-core/src/crypto/cryptoImplementation.ts | 53++++++++++++++++++++++++-----------------------------
Mpackages/taler-wallet-core/src/withdraw.ts | 31+++++++++++++++++++++++++++----
8 files changed, 213 insertions(+), 161 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/hooks/officer.ts b/packages/aml-backoffice-ui/src/hooks/officer.ts @@ -16,11 +16,11 @@ import { AbsoluteTime, Codec, + EddsaPrivP, LockedAccount, OfficerAccount, OfficerId, OperationOk, - SigningKey, buildCodecForObject, codecForAbsoluteTime, codecForString, @@ -99,7 +99,7 @@ export function useOfficer(): OfficerState { return { id: accountStorage.value.id as OfficerId, - signingKey: decodeCrock(accountStorage.value.strKey) as SigningKey, + signingKey: decodeCrock(accountStorage.value.strKey) as EddsaPrivP, }; }, [accountStorage.value?.id, accountStorage.value?.strKey]); diff --git a/packages/taler-util/src/http-client/exchange.ts b/packages/taler-util/src/http-client/exchange.ts @@ -35,20 +35,13 @@ import { opSuccessFromHttp, opUnknownFailure, } from "../operation.js"; -import { - TalerSignaturePurpose, - amountToBuffer, - buildSigPS, - eddsaSign, - encodeCrock, -} from "../taler-crypto.js"; +import { EddsaPrivP, encodeCrock } from "../taler-crypto.js"; import { AccessToken, AmountString, OfficerAccount, PaginationParams, ReserveAccount, - SigningKey, codecForTalerCommonConfigResponse, } from "../types-taler-common.js"; import { @@ -74,7 +67,14 @@ import { import { CacheEvictor, addPaginationParams, nullEvictor } from "./utils.js"; import { TalerError } from "../errors.js"; -import { AmountJson, Amounts, signAmlDecision } from "../index.js"; +import { + AmountJson, + Amounts, + signKycAuth, + signWalletAccountSetup, + signAmlDecision, + signAmlQuery, +} from "../index.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { AbsoluteTime } from "../time.js"; import { codecForEmptyObject } from "../types-taler-wallet.js"; @@ -614,7 +614,9 @@ export class TalerExchangeHttpClient { const body: WalletKycRequest = { balance, reserve_pub: account.id, - reserve_sig: buildKYCWalletBalanceSignature(account.signingKey, balance), + reserve_sig: encodeCrock( + signWalletAccountSetup(account.signingKey, balance), + ), }; const resp = await this.httpLib.fetch(url.href, { @@ -645,7 +647,7 @@ export class TalerExchangeHttpClient { * */ async checkKycStatus( - signingKey: SigningKey, + signingKey: EddsaPrivP, paytoHash: string, params: { timeout?: number; @@ -664,7 +666,9 @@ export class TalerExchangeHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "Account-Owner-Signature": buildKYCQuerySignature(signingKey), + "Account-Owner-Signature": encodeCrock( + signKycAuth(signingKey), + ), }, }); @@ -834,7 +838,9 @@ export class TalerExchangeHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "Taler-AML-Officer-Signature": buildAMLQuerySignature(auth.signingKey), + "Taler-AML-Officer-Signature": encodeCrock( + signAmlQuery(auth.signingKey), + ), }, }); @@ -876,7 +882,9 @@ export class TalerExchangeHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "Taler-AML-Officer-Signature": buildAMLQuerySignature(auth.signingKey), + "Taler-AML-Officer-Signature": encodeCrock( + signAmlQuery(auth.signingKey), + ), }, }); switch (resp.status) { @@ -924,7 +932,9 @@ export class TalerExchangeHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "Taler-AML-Officer-Signature": buildAMLQuerySignature(auth.signingKey), + "Taler-AML-Officer-Signature": encodeCrock( + signAmlQuery(auth.signingKey), + ), }, }); @@ -959,7 +969,9 @@ export class TalerExchangeHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "Taler-AML-Officer-Signature": buildAMLQuerySignature(auth.signingKey), + "Taler-AML-Officer-Signature": encodeCrock( + signAmlQuery(auth.signingKey), + ), }, }); @@ -996,7 +1008,9 @@ export class TalerExchangeHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { - "Taler-AML-Officer-Signature": buildAMLQuerySignature(auth.signingKey), + "Taler-AML-Officer-Signature": encodeCrock( + signAmlQuery(auth.signingKey), + ), }, body, }); @@ -1038,7 +1052,9 @@ export class TalerExchangeHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "Taler-AML-Officer-Signature": buildAMLQuerySignature(auth.signingKey), + "Taler-AML-Officer-Signature": encodeCrock( + signAmlQuery(auth.signingKey), + ), }, }); @@ -1077,7 +1093,9 @@ export class TalerExchangeHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "Taler-AML-Officer-Signature": buildAMLQuerySignature(auth.signingKey), + "Taler-AML-Officer-Signature": encodeCrock( + signAmlQuery(auth.signingKey), + ), }, }); @@ -1139,26 +1157,3 @@ export class TalerExchangeHttpClient { throw Error("not yet implemented"); } } - -function buildKYCWalletBalanceSignature( - key: SigningKey, - balance: AmountString, -): string { - const sigBlob = buildSigPS(TalerSignaturePurpose.WALLET_ACCOUNT_SETUP) - .put(amountToBuffer(balance)) - .build(); - - return encodeCrock(eddsaSign(sigBlob, key)); -} - -function buildKYCQuerySignature(key: SigningKey): string { - const sigBlob = buildSigPS(TalerSignaturePurpose.KYC_AUTH).build(); - - return encodeCrock(eddsaSign(sigBlob, key)); -} - -function buildAMLQuerySignature(key: SigningKey): string { - const sigBlob = buildSigPS(TalerSignaturePurpose.AML_QUERY).build(); - - return encodeCrock(eddsaSign(sigBlob, key)); -} diff --git a/packages/taler-util/src/http-client/officer-account.ts b/packages/taler-util/src/http-client/officer-account.ts @@ -15,20 +15,20 @@ */ import { - EncryptionNonce, + EddsaPrivP, + EncryptionNonceP, LockedAccount, OfficerAccount, OfficerId, ReserveAccount, ReservePub, - SigningKey, createEddsaKeyPair, decodeCrock, decryptWithDerivedKey, eddsaGetPublic, encodeCrock, encryptWithDerivedKey, - getRandomBytesF, + getRandomBytes, kdf, stringToBytes } from "@gnu-taler/taler-util"; @@ -54,7 +54,7 @@ export async function unlockOfficerAccount( password, ).catch((e) => { throw new UnwrapKeyError(e instanceof Error ? e.message : String(e)); - })) as SigningKey; + })) as EddsaPrivP; const publicKey = eddsaGetPublic(signingKey); @@ -73,14 +73,14 @@ export async function unlockOfficerAccount( */ export async function createNewOfficerAccount( password: string, - extraNonce: EncryptionNonce, + extraNonce: EncryptionNonceP, ): Promise<OfficerAccount & { safe: LockedAccount }> { const { eddsaPriv, eddsaPub } = createEddsaKeyPair(); const key = stringToBytes(password); - const localRnd = getRandomBytesF(24); - const mergedRnd: EncryptionNonce = extraNonce + const localRnd = getRandomBytes(24); + const mergedRnd: EncryptionNonceP = extraNonce ? kdf(24, stringToBytes("aml-officer"), extraNonce, localRnd) : localRnd; @@ -91,7 +91,7 @@ export async function createNewOfficerAccount( password, ); - const signingKey = eddsaPriv as SigningKey; + const signingKey = eddsaPriv as EddsaPrivP; const accountId = encodeCrock(eddsaPub) as OfficerId; const safe = encodeCrock(protectedPrivKey) as LockedAccount; @@ -107,14 +107,14 @@ export async function createNewOfficerAccount( * @returns */ export async function createNewWalletKycAccount( - extraNonce: EncryptionNonce, + extraNonce: EncryptionNonceP, password?: string, ): Promise<ReserveAccount & { safe?: LockedAccount }> { const { eddsaPriv, eddsaPub } = createEddsaKeyPair(); - const mergedRnd: EncryptionNonce = extraNonce && password - ? kdf(24, stringToBytes("aml-officer"), extraNonce, getRandomBytesF(24)) - : getRandomBytesF(24); + const mergedRnd: EncryptionNonceP = extraNonce && password + ? kdf(24, stringToBytes("aml-officer"), extraNonce, getRandomBytes(24)) + : getRandomBytes(24); const protectedPrivKey = password ? await encryptWithDerivedKey( @@ -124,7 +124,7 @@ export async function createNewWalletKycAccount( password, ) : undefined; - const signingKey = eddsaPriv as SigningKey; + const signingKey = eddsaPriv as EddsaPrivP; const accountId = encodeCrock(eddsaPub) as ReservePub; const safe = protectedPrivKey ? encodeCrock(protectedPrivKey) as LockedAccount : undefined; diff --git a/packages/taler-util/src/taler-crypto.ts b/packages/taler-util/src/taler-crypto.ts @@ -38,22 +38,89 @@ import { DenominationPubKey, } from "./types-taler-exchange.js"; -export type Flavor<T, FlavorT extends string> = T & { - _flavor?: `taler.${FlavorT}`; +const isEddsaPubP: unique symbol = Symbol("isEddsaPubP"); +type FlavorEddsaPubP = { + readonly flavor?: typeof isEddsaPubP; + readonly _size?: 32; }; -export type FlavorP<T, FlavorT extends string, S extends number> = T & { - _flavor?: `taler.${FlavorT}`; - _size?: S; +const isEddsaPrivP: unique symbol = Symbol("isEddsaPrivP"); +type FlavorEddsaPrivP = { + readonly flavor?: typeof isEddsaPrivP; + readonly _size?: 32; }; -export function getRandomBytes(n: number): Uint8Array { - return nacl.randomBytes(n); -} +const isEddsaSigP: unique symbol = Symbol("isEddsaSigP"); +type FlavorEddsaSigP = { + readonly flavor?: typeof isEddsaSigP; + readonly _size?: 64; +}; + +const isEdx25519PublicKey: unique symbol = Symbol("isEdx25519PublicKey"); +type FlavorEdx25519PublicKey = { + readonly flavor?: typeof isEdx25519PublicKey; + readonly _size?: 32; +}; + +const isEdx25519PrivateKey: unique symbol = Symbol("isEdx25519PrivateKey"); +type FlavorEdx25519PrivateKey = { + readonly flavor?: typeof isEdx25519PrivateKey; + readonly _size?: 64; +}; + +const isEcdhePrivP: unique symbol = Symbol("isEcdhePrivP"); +type FlavorEcdhePrivP = { + readonly flavor?: typeof isEcdhePrivP; + readonly _size?: 32; +}; + +const isEdx25519Signature: unique symbol = Symbol("isEdx25519Signature"); +type FlavorEdx25519Signature = { + readonly flavor?: typeof isEdx25519Signature; + readonly _size?: 64; +}; + +const isEdx25519PublicKeyEnc: unique symbol = Symbol("isEdx25519PublicKeyEnc"); +type FlavorEdx25519PublicKeyEnc = { + readonly [isEdx25519PublicKeyEnc]?: true; +}; + +const isEdx25519PrivateKeyEnc: unique symbol = Symbol( + "isEdx25519PrivateKeyEnc", +); +type FlavorEdx25519PrivateKeyEnc = { + readonly [isEdx25519PrivateKeyEnc]?: true; +}; + +const isEncryptionNonce: unique symbol = Symbol("isEncryptionNone"); +type FlavorEncryptionNonceP = { + readonly flavor?: typeof isEncryptionNonce; +}; + +type Sized<T> = { readonly _size?: T }; + +export type EddsaPubP = Uint8Array & FlavorEddsaPubP; +export type EddsaPrivP = Uint8Array & FlavorEddsaPrivP; +export type EddsaSigP = Uint8Array & FlavorEddsaSigP; + +export type EcdhePrivP = Uint8Array & FlavorEcdhePrivP; -export function getRandomBytesF<T extends number, N extends string>( - n: T, -): FlavorP<Uint8Array, N, T> { +export type OpaqueData = Uint8Array; +export type Edx25519PublicKey = Uint8Array & FlavorEdx25519PublicKey; +export type Edx25519PrivateKey = Uint8Array & FlavorEdx25519PrivateKey; +export type Edx25519Signature = Uint8Array & FlavorEdx25519Signature; + +export type Edx25519PublicKeyEnc = string & FlavorEdx25519PublicKeyEnc; +export type Edx25519PrivateKeyEnc = string & FlavorEdx25519PrivateKeyEnc; + +export type EncryptionNonceP = Uint8Array & FlavorEncryptionNonceP; + +export type PursePublicKey = EddsaPubP; + +export type ContractPrivateKey = EcdhePrivP; +export type MergePrivateKeyP = Uint8Array & EddsaPrivP; + +export function getRandomBytes<N extends number>(n: N): Uint8Array & Sized<N> { return nacl.randomBytes(n); } @@ -317,8 +384,8 @@ export function keyExchangeEddsaEcdh( } export function keyExchangeEcdhEddsa( - ecdhPriv: Uint8Array & MaterialEcdhePriv, - eddsaPub: Uint8Array & MaterialEddsaPub, + ecdhPriv: Uint8Array & FlavorEcdhePrivP, + eddsaPub: Uint8Array & FlavorEddsaPubP, ): Uint8Array { if (tart) { return tart.keyExchangeEcdhEddsa(ecdhPriv, eddsaPub); @@ -1056,18 +1123,6 @@ export function buildSigPS(purposeNum: number): SignaturePurposeBuilder { return new SignaturePurposeBuilder(purposeNum); } -export type OpaqueData = Flavor<Uint8Array, any>; -export type Edx25519PublicKey = FlavorP<Uint8Array, "Edx25519PublicKey", 32>; -export type Edx25519PrivateKey = FlavorP<Uint8Array, "Edx25519PrivateKey", 64>; -export type Edx25519Signature = FlavorP<Uint8Array, "Edx25519Signature", 64>; - -export type Edx25519PublicKeyEnc = FlavorP<string, "Edx25519PublicKeyEnc", 32>; -export type Edx25519PrivateKeyEnc = FlavorP< - string, - "Edx25519PrivateKeyEnc", - 64 ->; - /** * Convert a big integer to a fixed-size, little-endian array. */ @@ -1436,12 +1491,9 @@ export namespace AgeRestriction { } } -// FIXME: make it a branded type! -export type EncryptionNonce = FlavorP<Uint8Array, "EncryptionNonce", 24>; - async function deriveKey( keySeed: OpaqueData, - nonce: EncryptionNonce, + nonce: EncryptionNonceP, salt: string, ): Promise<Uint8Array> { return kdfKw({ @@ -1453,7 +1505,7 @@ async function deriveKey( } export async function encryptWithDerivedKey( - nonce: EncryptionNonce, + nonce: EncryptionNonceP, keySeed: OpaqueData, plaintext: OpaqueData, salt: string, @@ -1486,44 +1538,15 @@ enum ContractFormatTag { PaymentRequest = 1, } -type MaterialEddsaPub = { - _materialType?: "eddsa-pub"; - _size?: 32; -}; - -type MaterialEddsaPriv = { - _materialType?: "ecdhe-priv"; - _size?: 32; -}; - -type MaterialEcdhePub = { - _materialType?: "ecdhe-pub"; - _size?: 32; -}; - -type MaterialEcdhePriv = { - _materialType?: "ecdhe-priv"; - _size?: 32; -}; - -type PursePublicKey = FlavorP<Uint8Array, "PursePublicKey", 32> & - MaterialEddsaPub; - -type ContractPrivateKey = FlavorP<Uint8Array, "ContractPrivateKey", 32> & - MaterialEcdhePriv; - -type MergePrivateKey = FlavorP<Uint8Array, "MergePrivateKey", 32> & - MaterialEddsaPriv; - const mergeSalt = "p2p-merge-contract"; const depositSalt = "p2p-deposit-contract"; export function encryptContractForMerge( pursePub: PursePublicKey, contractPriv: ContractPrivateKey, - mergePriv: MergePrivateKey, + mergePriv: MergePrivateKeyP, contractTerms: any, - nonce: EncryptionNonce, + nonce: EncryptionNonceP, ): Promise<OpaqueData> { const contractTermsCanon = canonicalJson(contractTerms) + "\0"; const contractTermsBytes = stringToBytes(contractTermsCanon); @@ -1542,7 +1565,7 @@ export function encryptContractForDeposit( pursePub: PursePublicKey, contractPriv: ContractPrivateKey, contractTerms: any, - nonce: EncryptionNonce, + nonce: EncryptionNonceP, ): Promise<OpaqueData> { const contractTermsCanon = canonicalJson(contractTerms) + "\0"; const contractTermsBytes = stringToBytes(contractTermsCanon); diff --git a/packages/taler-util/src/taler-signatures.ts b/packages/taler-util/src/taler-signatures.ts @@ -14,12 +14,15 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { canonicalJson, TalerProtocolTimestamp } from "./index.js"; +import { AmountLike, canonicalJson, TalerProtocolTimestamp } from "./index.js"; import { + amountToBuffer, bufferForUint64, buildSigPS, decodeCrock, + EddsaPrivP, eddsaSign, + EddsaSigP, hash, stringToBytes, TalerSignaturePurpose, @@ -72,8 +75,25 @@ export function signAmlDecision( return eddsaSign(sigBlob, priv); } -export function signAmlQuery(key: Uint8Array): Uint8Array { +export function signAmlQuery(key: Uint8Array): EddsaSigP { const sigBlob = buildSigPS(TalerSignaturePurpose.AML_QUERY).build(); return eddsaSign(sigBlob, key); } + +export function signWalletAccountSetup( + key: EddsaPrivP, + balance: AmountLike, +): EddsaSigP { + const sigBlob = buildSigPS(TalerSignaturePurpose.WALLET_ACCOUNT_SETUP) + .put(amountToBuffer(balance)) + .build(); + + return eddsaSign(sigBlob, key); +} + +export function signKycAuth(key: EddsaPrivP): EddsaSigP { + const sigBlob = buildSigPS(TalerSignaturePurpose.KYC_AUTH).build(); + + return eddsaSign(sigBlob, key); +} diff --git a/packages/taler-util/src/types-taler-common.ts b/packages/taler-util/src/types-taler-common.ts @@ -38,7 +38,7 @@ import { codecForNumber, codecForString, } from "./codec.js"; -import { ReservePub } from "./index.js"; +import { EddsaPrivP, ReservePub } from "./index.js"; import { TalerProtocolDuration, TalerProtocolTimestamp, @@ -481,19 +481,15 @@ declare const opaque_OfficerId: unique symbol; export type OfficerId = string & { [opaque_OfficerId]: true }; declare const opaque_OfficerSigningKey: unique symbol; -/** - * Private key for AML officer - */ -export type SigningKey = Uint8Array & { [opaque_OfficerSigningKey]: true }; export interface OfficerAccount { id: OfficerId; - signingKey: SigningKey; + signingKey: EddsaPrivP; } export interface ReserveAccount { id: ReservePub; - signingKey: SigningKey; + signingKey: EddsaPrivP; } export type PaginationParams = { diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts @@ -86,8 +86,6 @@ import { WireFee, WithdrawalPlanchet, } from "@gnu-taler/taler-util"; -// FIXME: Crypto should not use DB Types! -import { DenominationRecord, timestampProtocolFromDb } from "../db.js"; import { CreateRecoupRefreshReqRequest, CreateRecoupReqRequest, @@ -588,8 +586,18 @@ export interface GlobalFeesValidationRequest { } export interface DenominationValidationRequest { - denom: DenominationRecord; + stampStart: TalerProtocolTimestamp; + stampExpireWithdraw: TalerProtocolTimestamp; + stampExpireDeposit: TalerProtocolTimestamp; + stampExpireLegal: TalerProtocolTimestamp; + value: AmountJson; + feeWithdraw: AmountJson; + feeDeposit: AmountJson; + feeRefresh: AmountJson; + feeRefund: AmountJson; + denomPubHash: string; masterPub: string; + masterSig: string; } export interface PaymentSignatureValidationRequest { @@ -1023,35 +1031,22 @@ export const nativeCryptoR: TalerCryptoInterfaceR = { tci: TalerCryptoInterfaceR, req: DenominationValidationRequest, ): Promise<ValidationResult> { - const { masterPub, denom } = req; - const value: AmountJson = Amounts.parseOrThrow(denom.value); + const value: AmountJson = Amounts.parseOrThrow(req.value); const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY) - .put(decodeCrock(masterPub)) - .put(timestampRoundedToBuffer(timestampProtocolFromDb(denom.stampStart))) - .put( - timestampRoundedToBuffer( - timestampProtocolFromDb(denom.stampExpireWithdraw), - ), - ) - .put( - timestampRoundedToBuffer( - timestampProtocolFromDb(denom.stampExpireDeposit), - ), - ) - .put( - timestampRoundedToBuffer( - timestampProtocolFromDb(denom.stampExpireLegal), - ), - ) + .put(decodeCrock(req.masterPub)) + .put(timestampRoundedToBuffer(req.stampStart)) + .put(timestampRoundedToBuffer(req.stampExpireWithdraw)) + .put(timestampRoundedToBuffer(req.stampExpireDeposit)) + .put(timestampRoundedToBuffer(req.stampExpireLegal)) .put(amountToBuffer(value)) - .put(amountToBuffer(denom.fees.feeWithdraw)) - .put(amountToBuffer(denom.fees.feeDeposit)) - .put(amountToBuffer(denom.fees.feeRefresh)) - .put(amountToBuffer(denom.fees.feeRefund)) - .put(decodeCrock(denom.denomPubHash)) + .put(amountToBuffer(req.feeWithdraw)) + .put(amountToBuffer(req.feeDeposit)) + .put(amountToBuffer(req.feeRefresh)) + .put(amountToBuffer(req.feeRefund)) + .put(decodeCrock(req.denomPubHash)) .build(); - const sig = decodeCrock(denom.masterSig); - const pub = decodeCrock(masterPub); + const sig = decodeCrock(req.masterSig); + const pub = decodeCrock(req.masterSig); const res = eddsaVerify(p, sig, pub); return { valid: res }; }, diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts @@ -150,6 +150,7 @@ import { timestampAbsoluteFromDb, timestampPreciseFromDb, timestampPreciseToDb, + timestampProtocolFromDb, } from "./db.js"; import { selectForcedWithdrawalDenominations, @@ -1805,6 +1806,28 @@ async function processPlanchetVerifyAndStoreCoin( ); } +async function isValidDenomRecord( + wex: WalletExecutionContext, + exchangeMasterPub: string, + d: DenominationRecord, +): Promise<boolean> { + const res = await wex.cryptoApi.isValidDenom({ + masterPub: exchangeMasterPub, + denomPubHash: d.denomPubHash, + feeDeposit: Amounts.parseOrThrow(d.fees.feeDeposit), + feeRefresh: Amounts.parseOrThrow(d.fees.feeRefresh), + feeRefund: Amounts.parseOrThrow(d.fees.feeRefund), + feeWithdraw: Amounts.parseOrThrow(d.fees.feeWithdraw), + masterSig: d.masterSig, + stampExpireDeposit: timestampProtocolFromDb(d.stampExpireDeposit), + stampExpireLegal: timestampProtocolFromDb(d.stampExpireLegal), + stampExpireWithdraw: timestampProtocolFromDb(d.stampExpireWithdraw), + stampStart: timestampProtocolFromDb(d.stampExpireWithdraw), + value: Amounts.parseOrThrow(d.value), + }); + return res.valid; +} + /** * Make sure that denominations that currently can be used for withdrawal * are validated, and the result of validation is stored in the database. @@ -1859,11 +1882,11 @@ export async function updateWithdrawalDenoms( if (wex.ws.config.testing.insecureTrustExchange) { valid = true; } else { - const res = await wex.cryptoApi.isValidDenom({ + valid = await isValidDenomRecord( + wex, + exchangeDetails.masterPublicKey, denom, - masterPub: exchangeDetails.masterPublicKey, - }); - valid = res.valid; + ); } logger.trace(`Done validating ${denom.denomPubHash}`); if (!valid) {