fixed greedy calculation

This commit is contained in:
Sebastian 2023-09-13 11:31:15 -03:00
parent a654c88a58
commit a596066524
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
2 changed files with 118 additions and 26 deletions

View File

@ -17,13 +17,14 @@ import {
AbsoluteTime, AbsoluteTime,
AgeRestriction, AgeRestriction,
AmountJson, AmountJson,
AmountString,
Amounts, Amounts,
DenomKeyType, DenomKeyType,
Duration, Duration,
TransactionAmountMode, TransactionAmountMode,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import test, { ExecutionContext } from "ava"; import test, { ExecutionContext } from "ava";
import { testing_greedySelectPeer } from "./coinSelection.js" import { AvailableDenom, testing_greedySelectPeer } from "./coinSelection.js"
type Tester<T> = { type Tester<T> = {
deep: { deep: {
@ -47,6 +48,7 @@ const inTheDistantFuture = AbsoluteTime.toProtocolTimestamp(
const inThePast = AbsoluteTime.toProtocolTimestamp( const inThePast = AbsoluteTime.toProtocolTimestamp(
AbsoluteTime.subtractDuraction(AbsoluteTime.now(), Duration.fromSpec({ hours: 1 })) AbsoluteTime.subtractDuraction(AbsoluteTime.now(), Duration.fromSpec({ hours: 1 }))
) )
test("should select the coin", (t) => { test("should select the coin", (t) => {
const instructedAmount = Amounts.parseOrThrow("LOCAL:2") const instructedAmount = Amounts.parseOrThrow("LOCAL:2")
const tally = { const tally = {
@ -55,16 +57,114 @@ test("should select the coin", (t) => {
lastDepositFee: Amounts.zeroOfCurrency(instructedAmount.currency), lastDepositFee: Amounts.zeroOfCurrency(instructedAmount.currency),
}; };
const coins = testing_greedySelectPeer( const coins = testing_greedySelectPeer(
// candidates available createCandidates([{
[{ amount: "LOCAL:10",
numAvailable: 5,
depositFee: "LOCAL:0.1",
fromExchange: "http://exchange.localhost/",
}]),
instructedAmount,
tally
);
expect(t, coins).deep.equal({
"hash0;32;http://exchange.localhost/": {
exchangeBaseUrl: "http://exchange.localhost/",
denomPubHash: "hash0",
maxAge: 32,
contributions: [Amounts.parseOrThrow("LOCAL:2")],
}
});
expect(t, tally).deep.equal({
amountAcc: Amounts.parseOrThrow("LOCAL:2"),
depositFeesAcc: Amounts.parseOrThrow("LOCAL:0.1"),
lastDepositFee: Amounts.parseOrThrow("LOCAL:0.1"),
});
});
test("should select 3 coins", (t) => {
const instructedAmount = Amounts.parseOrThrow("LOCAL:20")
const tally = {
amountAcc: Amounts.zeroOfCurrency(instructedAmount.currency),
depositFeesAcc: Amounts.zeroOfCurrency(instructedAmount.currency),
lastDepositFee: Amounts.zeroOfCurrency(instructedAmount.currency),
};
const coins = testing_greedySelectPeer(
createCandidates([{
amount: "LOCAL:10",
numAvailable: 5,
depositFee: "LOCAL:0.1",
fromExchange: "http://exchange.localhost/",
}]),
instructedAmount,
tally
);
expect(t, coins).deep.equal({
"hash0;32;http://exchange.localhost/": {
exchangeBaseUrl: "http://exchange.localhost/",
denomPubHash: "hash0",
maxAge: 32,
contributions: [
Amounts.parseOrThrow("LOCAL:9.9"),
Amounts.parseOrThrow("LOCAL:9.9"),
Amounts.parseOrThrow("LOCAL:0.2")
],
}
});
expect(t, tally).deep.equal({
amountAcc: Amounts.parseOrThrow("LOCAL:20"),
depositFeesAcc: Amounts.parseOrThrow("LOCAL:0.3"),
lastDepositFee: Amounts.parseOrThrow("LOCAL:0.1"),
});
});
test("can't select since the instructed amount is too high", (t) => {
const instructedAmount = Amounts.parseOrThrow("LOCAL:60")
const tally = {
amountAcc: Amounts.zeroOfCurrency(instructedAmount.currency),
depositFeesAcc: Amounts.zeroOfCurrency(instructedAmount.currency),
lastDepositFee: Amounts.zeroOfCurrency(instructedAmount.currency),
};
const coins = testing_greedySelectPeer(
createCandidates([{
amount: "LOCAL:10",
numAvailable: 5,
depositFee: "LOCAL:0.1",
fromExchange: "http://exchange.localhost/",
}]),
instructedAmount,
tally
);
expect(t, coins).deep.equal(undefined);
expect(t, tally).deep.equal({
amountAcc: Amounts.parseOrThrow("LOCAL:49.5"),
depositFeesAcc: Amounts.parseOrThrow("LOCAL:0.5"),
lastDepositFee: Amounts.parseOrThrow("LOCAL:0.1"),
});
});
function createCandidates(ar: {amount: AmountString, depositFee: AmountString, numAvailable: number, fromExchange: string}[]): AvailableDenom[] {
return ar.map((r,idx) => {
return {
"denomPub": { "denomPub": {
"age_mask": 0, "age_mask": 0,
"cipher": DenomKeyType.Rsa, "cipher": DenomKeyType.Rsa,
"rsa_public_key": "PPP" "rsa_public_key": "PPP"
}, },
"denomPubHash": "XXX", "denomPubHash": `hash${idx}`,
"value": "LOCAL:10", "value": r.amount,
"feeDeposit": "LOCAL:0.1", "feeDeposit": r.depositFee,
"feeRefresh": "LOCAL:0", "feeRefresh": "LOCAL:0",
"feeRefund": "LOCAL:0", "feeRefund": "LOCAL:0",
"feeWithdraw": "LOCAL:0", "feeWithdraw": "LOCAL:0",
@ -72,18 +172,10 @@ test("should select the coin", (t) => {
"stampExpireLegal": inTheDistantFuture, "stampExpireLegal": inTheDistantFuture,
"stampExpireWithdraw": inTheDistantFuture, "stampExpireWithdraw": inTheDistantFuture,
"stampStart": inThePast, "stampStart": inThePast,
"exchangeBaseUrl": "http://exchange.localhost/", "exchangeBaseUrl": r.fromExchange,
"numAvailable": 5, "numAvailable": r.numAvailable,
"maxAge": 32 "maxAge": 32,
}],
instructedAmount, tally);
expect(t, coins).deep.equal({
"XXX;32;http://exchange.localhost/": {
exchangeBaseUrl: "http://exchange.localhost/",
denomPubHash: "XXX",
maxAge: 32,
contributions: [Amounts.parseOrThrow("LOCAL:2")],
} }
}); })
}); }

View File

@ -918,19 +918,19 @@ function greedySelectPeer(
instructedAmount, instructedAmount,
tally.amountAcc, tally.amountAcc,
).amount; ).amount;
const coinSpend = Amounts.max( const coinContrib = Amounts.sub(denom.value, denom.feeDeposit).amount
Amounts.min(amountPayRemaining, denom.value),
denom.feeDeposit, const coinSpend = Amounts.min(amountPayRemaining, coinContrib)
);
tally.amountAcc = Amounts.add(tally.amountAcc, coinSpend).amount; tally.amountAcc = Amounts.add(tally.amountAcc, coinSpend).amount;
// Since this is a peer payment, there is no merchant to
// potentially cover the deposit fees.
tally.amountAcc = Amounts.sub(tally.amountAcc, denom.feeDeposit).amount;
tally.depositFeesAcc = Amounts.add( tally.depositFeesAcc = Amounts.add(
tally.depositFeesAcc, tally.depositFeesAcc,
denom.feeDeposit, denom.feeDeposit,
).amount; ).amount;
tally.lastDepositFee = Amounts.parseOrThrow(denom.feeDeposit); tally.lastDepositFee = Amounts.parseOrThrow(denom.feeDeposit);
contributions.push(coinSpend); contributions.push(coinSpend);
} }
if (contributions.length > 0) { if (contributions.length > 0) {