taler-typescript-core

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

coinsim.ts (4130B)


      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 
     18 function getRandomInt(min, max) {
     19   return Math.floor(Math.random() * (max - min + 1)) + min;
     20 }
     21 
     22 const denoms = [8096, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1];
     23 
     24 // mapping from denomination index to count
     25 const wallet = denoms.map(() => 0);
     26 
     27 const trans_max = 5000;
     28 const trans_min = 4;
     29 
     30 const withdraw_max = 10000;
     31 
     32 const num_transactions = parseInt(process.argv[2]);
     33 
     34 // Refresh or withdraw operations
     35 let ops = 0;
     36 let ops_refresh = 0;
     37 let ops_withdraw = 0;
     38 let ops_spend = 0;
     39 let refresh_output = 0;
     40 
     41 function withdraw(amount, is_refresh) {
     42   while (amount != 0) {
     43     for (let i = 0; i < denoms.length; i++) {
     44       let d = denoms[i];
     45       if (d <= amount) {
     46         amount -= d;
     47         wallet[i]++;
     48         ops++;
     49         if (!is_refresh) {
     50           ops_withdraw++;
     51         } else {
     52           refresh_output++;
     53         }
     54         break;
     55       }
     56     }
     57   }
     58 }
     59 
     60 function spendSmallestFirst(cost) {
     61   while (cost != 0) {
     62     for (let j = 0; j < denoms.length; j++) {
     63       const k = denoms.length - j - 1;
     64       const d = denoms[k];
     65       const w = wallet[k];
     66       if (w == 0) {
     67         continue;
     68       }
     69       if (d <= cost) {
     70         // spend
     71         wallet[k]--;
     72         cost -= d;
     73         ops++;
     74         ops_spend++;
     75         break;
     76       }
     77       // partially spend and then refresh
     78       ops++;
     79       ops_spend++;
     80       let r = d - cost;
     81       ops_refresh++;
     82       wallet[k]--;
     83       withdraw(r, true);
     84       cost = 0;
     85     }
     86   }
     87 }
     88 
     89 function spendLargestFirst(cost) {
     90   while (cost != 0) {
     91     for (let j = 0; j < denoms.length; j++) {
     92       const d = denoms[j];
     93       const w = wallet[j];
     94       if (w == 0) {
     95         continue;
     96       }
     97       if (d <= cost) {
     98         // spend
     99         wallet[j]--;
    100         cost -= d;
    101         ops++;
    102         ops_spend++;
    103         break;
    104       }
    105       // partially spend and then refresh
    106       ops++;
    107       ops_spend++;
    108       let r = d - cost;
    109       ops_refresh++;
    110       wallet[j]--;
    111       withdraw(r, true);
    112       cost = 0;
    113     }
    114   }
    115 }
    116 
    117 function spendHybrid(cost) {
    118     for (let j = 0; j < denoms.length; j++) {
    119       const k = denoms.length - j - 1;
    120       const d = denoms[k];
    121       const w = wallet[k];
    122       if (w == 0) {
    123         continue;
    124       }
    125       if (d < cost) {
    126         continue;
    127       }
    128       // partially spend and then refresh
    129       ops++;
    130       ops_spend++;
    131       let r = d - cost;
    132       ops_refresh++;
    133       wallet[k]--;
    134       withdraw(r, true);
    135       cost = 0;
    136     }
    137 
    138   spendSmallestFirst(cost);
    139 }
    140 
    141 for (let i = 0; i < num_transactions; i++) {
    142   // check existing wallet balance
    143   let balance = 0;
    144   for (let j = 0; j < denoms.length; j++) {
    145     balance += wallet[j] * denoms[j]
    146   }
    147   // choose how much we want to spend
    148   let cost = getRandomInt(trans_min, trans_max);
    149   if (balance < cost) {
    150     // we need to withdraw
    151     let amount = getRandomInt(cost - balance, withdraw_max);
    152     withdraw(amount, false);
    153   }
    154 
    155   // check that we now have enough balance
    156   balance = 0;
    157   for (let j = 0; j < denoms.length; j++) {
    158     balance += wallet[j] * denoms[j]
    159   }
    160 
    161   if (balance < cost) {
    162     throw Error("not enough balance");
    163   }
    164 
    165   // now we spend
    166   spendHybrid(cost);
    167 }
    168 
    169 console.log("total ops", ops / num_transactions);
    170 console.log("spend ops", ops_spend / num_transactions);
    171 console.log("pure withdraw ops", ops_withdraw / num_transactions);
    172 console.log("refresh (multi output) ops", ops_refresh / num_transactions);
    173 console.log("refresh output", refresh_output / ops_refresh);