-
Notifications
You must be signed in to change notification settings - Fork 1
/
bank.js
151 lines (130 loc) · 4.71 KB
/
bank.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
"use strict";
const blindSignatures = require('blind-signatures');
const { Coin, COIN_RIS_LENGTH, IDENT_STR, BANK_STR, NUM_COINS_REQUIRED } = require('./coin.js');
const utils = require('./utils.js');
// This class represents a bank issuing DigiCash-lite coins.
class Bank {
constructor() {
this.key = blindSignatures.keyGeneration({ b: 2048 });
this.ledger = {};
this.coinDB = {}; // tracks previously redeemed coins
}
// Returns the modulus used for digital signatures.
get n() {
return this.key.keyPair.n.toString();
}
// Returns the e value used for digital signatures.
get e() {
return this.key.keyPair.e.toString();
}
// Prints out the balances for all of the bank's customers.
showBalances() {
console.log(JSON.stringify(this.ledger));
}
// Initializes a client's account with 0 value.
registerClient(client) {
this.ledger[client.name] = 0;
}
// Updates the ledger to account for money submitted directly to the bank.
deposit({account, amount}) {
if (this.ledger[account] === undefined) {
throw new Error(`${account} is not a registered customer of the bank`);
}
this.ledger[account] += amount;
}
// Updates the ledger to account for money withdrawn directly from the bank.
withdraw({account, amount}) {
if (this.ledger[account] === undefined) {
throw new Error(`${account} is not a registered customer of the bank`);
}
if (this.ledger[account] < amount) {
throw new Error("Insufficient funds");
}
this.ledger[account] -= amount;
}
// Returns the balance for the specified account.
balance(account) {
if (this.ledger[account] === undefined) {
throw new Error(`${account} is not a registered customer of the bank`);
}
return this.ledger[account];
}
// Transfers money between 2 of the bank's customers.
transfer({from, to, amount}) {
if (this.ledger[from] === undefined) {
throw new Error(`${from} is not a registered customer of the bank`);
}
if (this.ledger[to] === undefined) {
throw new Error(`${to} is not a registered customer of the bank`);
}
let fromBalance = this.ledger[from];
if (fromBalance < amount) {
throw new Error(`${from} does not have sufficient funds`);
}
this.ledger[from] = fromBalance - amount;
this.ledger[to] += amount;
}
// Verifies that a bank customer has sufficient funds for a transaction.
verifyFunds({account, amount}) {
if (this.ledger[account] === undefined) {
throw new Error(`${account} is not a registered customer of the bank`);
}
let balance = this.ledger[account];
return balance >= amount;
}
// This method represents the bank's side of the exchange when a user buys a coin.
sellCoin(account, amount, coinBlindedHashes, response) {
let selected = utils.randInt(NUM_COINS_REQUIRED - 1); // returns a randomly selected coin.
let cbf = response(selected); // returns blinding factors and the matching coins of all the coins except selected.
let identity;
for(let i in cbf[1]){
let x = cbf[1][i];
if(!x.verifyUnblinded(cbf[0][i])){
throw new Error("Blind factors dont match");
}
for(let j = 0; j < NUM_COINS_REQUIRED; j++){
identity = utils.decryptOTP({key: cbf[1][i].leftIdent[j], ciphertext: cbf[1][i].rightIdent[j], returnType: "string"});
if(identity !== ("IDENT:" + account)){
throw new Error("Identity invalid");
}
}
}
let sig = blindSignatures.sign({blinded: coinBlindedHashes[selected], key: this.key});
this.withdraw({account, amount});
return sig;
}
// Adds a coin to a user's bank account.
redeemCoin({account, coin, ris}) {
blindSignatures.verify({
unblinded: coin.signature,
N: this.n,
E: this.e,
message: coin.toString()
});
if(!(coin.guid in this.coinDB)){
console.log(`Coin #${coin.guid} has been redeemed. Have a nice day.`)
this.coinDB[coin.guid] = ris;
let balance = this.ledger[account];
this.ledger[account] = balance + coin.amount;
}
else{
console.log(`Coin ${coin.guid} previously spent. Determining cheater.`)
for(let i = 0; i < ris.length ; i++){
let identStr = utils.decryptOTP({
key: ris[i],
ciphertext: this.coinDB[coin.guid][i],
returnType: 'string'
});
if(identStr.startsWith(IDENT_STR)){
let cheater = identStr.split(':')[1];
console.log(`${cheater} double spent coin ${coin.guid}.`);
console.log(`Sorry, but coin ${coin.guid} cannot be accepted.`);
return;
}
}
console.log("The merchant tried to redeem the coin twice");
console.log(`Sorry, but coin ${coin.guid} cannot be accepted.`);
}
}
}
exports.Bank = Bank;