225 lines
6.1 KiB
TypeScript
225 lines
6.1 KiB
TypeScript
/*
|
|
This file is part of GNU Taler
|
|
(C) 2023 Taler Systems S.A.
|
|
|
|
GNU Taler is free software; you can redistribute it and/or modify it under the
|
|
terms of the GNU General Public License as published by the Free Software
|
|
Foundation; either version 3, or (at your option) any later version.
|
|
|
|
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
*/
|
|
|
|
/**
|
|
* Imports.
|
|
*/
|
|
import {
|
|
AbsoluteTime,
|
|
ContractTermsUtil,
|
|
decodeCrock,
|
|
Duration,
|
|
encodeCrock,
|
|
getRandomBytes,
|
|
hash,
|
|
j2s,
|
|
PeerContractTerms,
|
|
TalerError,
|
|
TalerPreciseTimestamp,
|
|
} from "@gnu-taler/taler-util";
|
|
import {
|
|
checkReserve,
|
|
CryptoDispatcher,
|
|
downloadExchangeInfo,
|
|
EncryptContractRequest,
|
|
findDenomOrThrow,
|
|
SpendCoinDetails,
|
|
SynchronousCryptoWorkerFactoryPlain,
|
|
topupReserveWithDemobank,
|
|
Wallet,
|
|
withdrawCoin,
|
|
} from "@gnu-taler/taler-wallet-core";
|
|
import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
|
|
import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
|
|
|
|
/**
|
|
* Test the exchange's purse API.
|
|
*/
|
|
export async function runExchangePurseTest(t: GlobalTestState) {
|
|
// Set up test environment
|
|
|
|
const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(t);
|
|
|
|
const http = harnessHttpLib;
|
|
const cryptoDisp = new CryptoDispatcher(
|
|
new SynchronousCryptoWorkerFactoryPlain(),
|
|
);
|
|
const cryptoApi = cryptoDisp.cryptoApi;
|
|
|
|
try {
|
|
// Withdraw digital cash into the wallet.
|
|
|
|
const exchangeInfo = await downloadExchangeInfo(exchange.baseUrl, http);
|
|
|
|
const reserveKeyPair = await cryptoApi.createEddsaKeypair({});
|
|
|
|
let reserveUrl = new URL(
|
|
`reserves/${reserveKeyPair.pub}`,
|
|
exchange.baseUrl,
|
|
);
|
|
reserveUrl.searchParams.set("timeout_ms", "30000");
|
|
const longpollReq = http.fetch(reserveUrl.href, {
|
|
method: "GET",
|
|
});
|
|
|
|
await topupReserveWithDemobank({
|
|
amount: "TESTKUDOS:10",
|
|
http,
|
|
reservePub: reserveKeyPair.pub,
|
|
bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl,
|
|
exchangeInfo,
|
|
});
|
|
|
|
console.log("waiting for longpoll request");
|
|
const resp = await longpollReq;
|
|
console.log(`got response, status ${resp.status}`);
|
|
|
|
console.log(exchangeInfo);
|
|
|
|
await checkReserve(http, exchange.baseUrl, reserveKeyPair.pub);
|
|
|
|
const d1 = findDenomOrThrow(exchangeInfo, "TESTKUDOS:8", {
|
|
denomselAllowLate: Wallet.defaultConfig.testing.denomselAllowLate,
|
|
});
|
|
|
|
const coin = await withdrawCoin({
|
|
http,
|
|
cryptoApi,
|
|
reserveKeyPair: {
|
|
reservePriv: reserveKeyPair.priv,
|
|
reservePub: reserveKeyPair.pub,
|
|
},
|
|
denom: d1,
|
|
exchangeBaseUrl: exchange.baseUrl,
|
|
});
|
|
|
|
const amount = "TESTKUDOS:5";
|
|
const purseFee = "TESTKUDOS:0";
|
|
|
|
const mergeTimestamp = TalerPreciseTimestamp.now();
|
|
|
|
const contractTerms: PeerContractTerms = {
|
|
amount,
|
|
summary: "Hello",
|
|
purse_expiration: AbsoluteTime.toProtocolTimestamp(
|
|
AbsoluteTime.addDuration(
|
|
AbsoluteTime.now(),
|
|
Duration.fromSpec({ minutes: 1 }),
|
|
),
|
|
),
|
|
};
|
|
|
|
const mergeReservePair = await cryptoApi.createEddsaKeypair({});
|
|
const pursePair = await cryptoApi.createEddsaKeypair({});
|
|
const mergePair = await cryptoApi.createEddsaKeypair({});
|
|
const contractPair = await cryptoApi.createEddsaKeypair({});
|
|
const contractEncNonce = encodeCrock(getRandomBytes(24));
|
|
|
|
const pursePub = pursePair.pub;
|
|
|
|
const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
|
|
|
|
const purseSigResp = await cryptoApi.signPurseCreation({
|
|
hContractTerms,
|
|
mergePub: mergePair.pub,
|
|
minAge: 0,
|
|
purseAmount: amount,
|
|
purseExpiration: contractTerms.purse_expiration,
|
|
pursePriv: pursePair.priv,
|
|
});
|
|
|
|
const coinSpend: SpendCoinDetails = {
|
|
ageCommitmentProof: undefined,
|
|
coinPriv: coin.coinPriv,
|
|
coinPub: coin.coinPub,
|
|
contribution: amount,
|
|
denomPubHash: coin.denomPubHash,
|
|
denomSig: coin.denomSig,
|
|
};
|
|
|
|
const depositSigsResp = await cryptoApi.signPurseDeposits({
|
|
exchangeBaseUrl: exchange.baseUrl,
|
|
pursePub: pursePair.pub,
|
|
coins: [coinSpend],
|
|
});
|
|
|
|
const encryptContractRequest: EncryptContractRequest = {
|
|
contractTerms: contractTerms,
|
|
mergePriv: mergePair.priv,
|
|
pursePriv: pursePair.priv,
|
|
pursePub: pursePair.pub,
|
|
contractPriv: contractPair.priv,
|
|
contractPub: contractPair.pub,
|
|
nonce: contractEncNonce,
|
|
};
|
|
|
|
const econtractResp = await cryptoApi.encryptContractForMerge(
|
|
encryptContractRequest,
|
|
);
|
|
|
|
const econtractHash = encodeCrock(
|
|
hash(decodeCrock(econtractResp.econtract.econtract)),
|
|
);
|
|
|
|
const createPurseUrl = new URL(
|
|
`purses/${pursePair.pub}/create`,
|
|
exchange.baseUrl,
|
|
);
|
|
|
|
const reqBody = {
|
|
amount: amount,
|
|
merge_pub: mergePair.pub,
|
|
purse_sig: purseSigResp.sig,
|
|
h_contract_terms: hContractTerms,
|
|
purse_expiration: contractTerms.purse_expiration,
|
|
deposits: depositSigsResp.deposits,
|
|
min_age: 0,
|
|
econtract: econtractResp.econtract,
|
|
};
|
|
|
|
const httpResp = await http.fetch(createPurseUrl.href, {
|
|
method: "POST",
|
|
body: reqBody,
|
|
});
|
|
|
|
const respBody = await httpResp.json();
|
|
|
|
console.log("status", httpResp.status);
|
|
|
|
console.log(j2s(respBody));
|
|
|
|
const mergeUrl = new URL(`purses/${pursePub}/merge`, exchange.baseUrl);
|
|
mergeUrl.searchParams.set("timeout_ms", "300");
|
|
const statusResp = await http.fetch(mergeUrl.href, {});
|
|
|
|
const statusRespBody = await statusResp.json();
|
|
|
|
console.log(j2s(statusRespBody));
|
|
|
|
t.assertTrue(statusRespBody.merge_timestamp === undefined);
|
|
} catch (e) {
|
|
if (e instanceof TalerError) {
|
|
console.log(e);
|
|
console.log(j2s(e.errorDetail));
|
|
} else {
|
|
console.log(e);
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
runExchangePurseTest.suites = ["wallet"];
|