taler-typescript-core

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

coinsim.ts (3559B)


      1 /*
      2  This file is part of GNU Taler
      3  (C) 2018 GNUnet e.V.
      4 
      5  GNU Taler is free software; you can redistribute it and/or modify it under the
      6  terms of the GNU General Public License as published by the Free Software
      7  Foundation; either version 3, or (at your option) any later version.
      8 
      9  GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
     10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13  You should have received a copy of the GNU General Public License along with
     14  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 
     17 function getRandomInt(min, max) {
     18   return Math.floor(Math.random() * (max - min + 1)) + min;
     19 }
     20 
     21 const denoms = [8096, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1];
     22 
     23 // mapping from denomination index to count
     24 const wallet = denoms.map(() => 0);
     25 
     26 const trans_max = 5000;
     27 const trans_min = 4;
     28 
     29 const withdraw_max = 10000;
     30 
     31 const num_transactions = parseInt(process.argv[2]);
     32 
     33 // Refresh or withdraw operations
     34 let ops = 0;
     35 let ops_refresh = 0;
     36 let ops_withdraw = 0;
     37 let ops_spend = 0;
     38 let refresh_output = 0;
     39 
     40 function withdraw(amount, is_refresh) {
     41   while (amount != 0) {
     42     for (let i = 0; i < denoms.length; i++) {
     43       let d = denoms[i];
     44       if (d <= amount) {
     45         amount -= d;
     46         wallet[i]++;
     47         ops++;
     48         if (!is_refresh) {
     49           ops_withdraw++;
     50         } else {
     51           refresh_output++;
     52         }
     53         break;
     54       }
     55     }
     56   }
     57 }
     58 
     59 function spendSmallestFirst(cost) {
     60   while (cost != 0) {
     61     for (let j = 0; j < denoms.length; j++) {
     62       const k = denoms.length - j - 1;
     63       const d = denoms[k];
     64       const w = wallet[k];
     65       if (w == 0) {
     66         continue;
     67       }
     68       if (d <= cost) {
     69         // spend
     70         wallet[k]--;
     71         cost -= d;
     72         ops++;
     73         ops_spend++;
     74         break;
     75       }
     76       // partially spend and then refresh
     77       ops++;
     78       ops_spend++;
     79       let r = d - cost;
     80       ops_refresh++;
     81       wallet[k]--;
     82       withdraw(r, true);
     83       cost = 0;
     84     }
     85   }
     86 }
     87 
     88 function spendHybrid(cost) {
     89   for (let j = 0; j < denoms.length; j++) {
     90     const k = denoms.length - j - 1;
     91     const d = denoms[k];
     92     const w = wallet[k];
     93     if (w == 0) {
     94       continue;
     95     }
     96     if (d < cost) {
     97       continue;
     98     }
     99     // partially spend and then refresh
    100     ops++;
    101     ops_spend++;
    102     let r = d - cost;
    103     ops_refresh++;
    104     wallet[k]--;
    105     withdraw(r, true);
    106     cost = 0;
    107   }
    108 
    109   spendSmallestFirst(cost);
    110 }
    111 
    112 for (let i = 0; i < num_transactions; i++) {
    113   // check existing wallet balance
    114   let balance = 0;
    115   for (let j = 0; j < denoms.length; j++) {
    116     balance += wallet[j] * denoms[j];
    117   }
    118   // choose how much we want to spend
    119   let cost = getRandomInt(trans_min, trans_max);
    120   if (balance < cost) {
    121     // we need to withdraw
    122     let amount = getRandomInt(cost - balance, withdraw_max);
    123     withdraw(amount, false);
    124   }
    125 
    126   // check that we now have enough balance
    127   balance = 0;
    128   for (let j = 0; j < denoms.length; j++) {
    129     balance += wallet[j] * denoms[j];
    130   }
    131 
    132   if (balance < cost) {
    133     throw Error("not enough balance");
    134   }
    135 
    136   // now we spend
    137   spendHybrid(cost);
    138 }
    139 
    140 console.log("total ops", ops / num_transactions);
    141 console.log("spend ops", ops_spend / num_transactions);
    142 console.log("pure withdraw ops", ops_withdraw / num_transactions);
    143 console.log("refresh (multi output) ops", ops_refresh / num_transactions);
    144 console.log("refresh output", refresh_output / ops_refresh);