billstclair / trubanc

Trubanc is an anonymous, digitally-signed vault and trading system.

This URL has Read+Write access

trubanc / todo.txt
100644 282 lines (178 sloc) 14.83 kb
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
If we can't redeem a coupon spend, or cancel a regular spend from the outbox,
there should be some way to remove that item from the outbox. It's a bug
that this happens at all, but there needs to be a way to clean up.
 
======================================================================
 
If inbox has bank fees, the balance could go negative. Don't allow processinbox.
This happens if the user rejects the initial spend of usage tokens.
 
======================================================================
 
Test for existence of cURL, bcmath, and OpenSSL libraries.
 
======================================================================
 
safeidx($array, $idx) doesn't warn if $idx is not in array. Sprinkle liberally.
Or just put @ before suspect accessors.
 
Ship with clientdb and serverdb directories inside of dbs.
 
Backup mechanism. Real-time logging and replication.
 
Client command to change passphrase. Server request to change name
associated with account.
 
The current, not original, asset issuer should be allowed to change the storage fee.
 
======================================================================
 
$t->BATCHFEES command, to iterate over accounts, collecting storage fees.
 
  (<id>,batchfees,<assetid>,<start>,<timelimit>,<frequency>)
 
Returns:
 
  (<bankid>,@batchfees,<amount>,<start2>,
    (<id>,batchfees,<assetid>,<start>,<timelimit>,<frequency>))
 
<assetid> asset for which to collect fees.
<start> user ID at which to start processing
<timelimit> maximum seconds to process. Prevents web form timeout.
<frequency> how often the issuer runs this process. Balances newer than
              (now - <frequency>) won't be processed.
<start2> user ID at which to continue processing
 
This exposes that fsdb->contents() doesn't scale. If there are
millions of accounts, the /account directory has millions of
first-level subdirs. That's a lot of RAM to list. Need to split up
abcdefgh... into ab/cd/efgh.... That turns 2**20 accounts into 2**16
dirs of 2**4 accounts each. Use new fsdb->iterator() function to
return an iterator:
  iterator->next() - returns next item
  iterator->init(start) - next next() will return start
  iterator->peek() - returns next item, but doesn't advance
 
Fees get credited to the issuer's $t->STORAGEFEE accumulators. We need
a $t->BANKFEE accumulator for each account that accumulates the
charges, per assetid. These look like part of the inbox for the
getinbox and processinbox operations, but are separate so we don't
need to find them while batching fees. They have non-integer amounts,
which will require some tweaking to work properly.
 
The server must remember, for each issuer and asset, when the last
batchfees was run, so that we don't double-charge. This is a bigger
problem for inbox items. Since they can sit there for a long time, and
accepting one charges the recipient, we somehow have to know that
batchfees has charged (the sender) for one of them. This needs to be a
special negative bank spend, that debits the recipient if he accepts,
or the spender if rejected.
 
======================================================================
 
Sync command in user interface. Click link. Sync balances, outbox, and inbox with server.
  It would be better to do this automagically when something appears
  wrong, and I'm doing it in some places, but the user needs to be
  able to do it explicitly.
 
Real sync command that doesn't just overwrite the client, but notifies
the server when the client has a more recent message for a balance or
outbox item. Tools for the bank to rectify outstanding notifications
 
Accept only the [url,number] form for coupons. Give a reasonable error
message if the user misses copying part of the coupon (e.g. the
opening or closing square bracket).
 
Transaction fees. Fixed as amount and asset. Or charge rent on space,
e.g. 10% / year, in usage tokens, of your total file count.
 
======================================================================
 
Enable Bank to take a cut of storage fees. Configured in settings.php.
 
  // The cut of storage fees taken by the bank, in percent. Default: 0.
  // And the ID of the account to pay. Default: the bank itself.
  $storagefeecut = 10;
  $storagefeeid = "18864f9f1e8df66c15f76513df0fde9064ceeb2c";
 
======================================================================
 
Lisp wrapper around the OpenSSL command line.
  If this works, do a whole server in Lisp.
 
Currently, transaction fees go only to bank. Could give asset issuer a cut.
Currently, storage fees go only to asset issuer. Could give bank a cut.
Currently, transaction fees are paid by spender. Could charge recipient, instead or as well.
 
Time zone preference for client.
 
History features:
    Additional notes
    Sort
    Filter
    Export/Import
    "Show raw history" link
    Show source and target acct on transfer spends.
 
URLs for adding a contact and for filling the spend page with info for a sale.
 
Nicknames for asset types.
 
Show/Hide checkboxes on sub-account names. Move hidden sub-account
names to bottom of list, one line apiece.
 
If you paste an old inbox entry into somebody's inbox, it will look
real, and give them those assets when processed. Bank needs to protect
against inbox tampering in its file system by storing a message that
encodes the real inbox entries.
 
If the customer can prove he has a higher balance than the bank in an asset, add a balance-disagreement record to that balance entry. The customer's usable balance will be what the bank agrees with, and he can continue to make transactions, but the amount in disagreement will remain in disagreement until the bank officers and the asset issuer decide whether they're going to honor the customer's balance. If they do, they'll add a balance-disagreement-resolution to the user's inbox.
 
A way to transfer contacts between client DBs, likely as a big URL.
 
Encrypt message contents with recipients public key, so bank can't read them.
 
Allow adding non-registered IDs to contact list in web client.
 
Use titles on asset names in the UI to distinguish different assets with the same name. Need to print ID and owner. Probably need to mark certain assets as known in the "Assets" screen.
 
Optimize server traffic.
 
Stand-alone non-HTTP server:
  http://www.van-steenbeek.net/?q=php_pcntl_fork
 
Canonicalize the bank URL at the bank. That way you don't get the spelling of the first user to use it in a client.
 
Email notification. Info must be encrypted in the server database. And it's obviously optional. User specifies message to send and options about transaction details, e.g. amount, asset, spender, bank url.
 
Wrap functions around code shared by do_spend, do_processinbox, and do_asset
 
Finish two-phase commit for spend and inboxhash.
 
To cancel a spend, do a negative spend of the same amount with a note of the outbox timestamp of the original spend. There are locking issues with removing the recipient's inbox entry. May have to do it with another inbox entry that cancels the first one. Then both will be quietly removed by the next getinbox.
 
A real transaction system for disk writes, like Patrick has done for Loom. The locking works, if all goes well in disk writes. But it doesn't if there's an unexpected error, e.g. the problems I had with creating subdirs in a SAFE-MODE environment. We get in an inconsistent state. This should be just a layer on top of fsdb.php.
 
-----
 
Remember the last successful spend or processinbox message and its result, and resend the result if the message is resent. This can heal responses that are lost in transit.
 
Credit the bank for usage tokens paid for files. Some of this may be already done, but that code needs reviewing.
 
-----
 
Encrypt the client<->server messages. SSL could do this, but why bother, when we can easily do it ourselves? Why pay for an SSL certificate and the SSL connection overhead, when you don't have to?
 
  sent: (<id>,getsessionkey,<bankid>,<reqid>)
  rcvd: (<bankid>,sessionkey,<id>,<pubkey encrypted: [<sessionid>,<sessionkey>]>)
 
  sent: [<sessionid>,<sessionkey encrypted: message>]
  rcvd: [<sessionid>,<sessionkey encrypted: response>]
           or
        [<sessionid>,unknown]
 
  sent: [<sessionid>,
         <sessionkey encrypted: (<id>,closesession,<bankid>,<sessionid>)>]
  rcvd: [<sessionid>,
         <sessionkey encrypted:
           (<bankid>,@closesession,(<id>,closesession,<bankid>,<sessionid>))>]
 
Unfortunately, the PHP OpenSSL library doesn't give sufficient access to OpenSSL's encryption functions, so we'll have to rely on mcrypt:
 
http://us3.php.net/manual/en/function.mcrypt-module-open.php
 
http://www.ietf.org/rfc/rfc4344.txt recommends aes256-ctr.
 
Unfortunately, PHP's mcrypt library doesn't provide CTR. CBC or CFB will have to do. The openssl on my machine supports aes-256-cbc and aes-256-cfb, so either will do.
 
rfc4344 also recommends periodic rekeying. Can do that by closing the session and starting a new one. Client needs to keep track of the number of messages or number of bytes sent, per session, and have a rekey period.
 
-----
 
Bank Counterfeiting
 
As it is now, the bank can counterfeit assets. This will put the bank out of balance, but who's to know, unless they hire an auditing firm to audit the database? This means that issuers have to trust the banks they issue for, or they could end up with more liabilities than they promised. One way around this is to have the issuer approve every transaction. Actually, approve it twice, once when the spend happens, and again when the spend|accept or spend|reject happens.
 
The issuer would sign an approval of the new balance, or spend, without the customer <id>, just the <time#>. Otherwise, the issuer would know identities of all the holders of his currency, which many customers are not likely to want. Letting the bank have that information is bad enough, if what you really want is totally anonymous digital cash.
 
The bank would supply the issuer with:
 
  (<bankid>,debit|request,<issuerid>,<time#>,<assetid>,<amount>).
  (<bankid>,debit,<issuerid>,<time2#>,<assetid>,<amount2>).
  (<bankid>,credit,<issuerid>,<time#>,<assetid>,<amount3>)
 
This means that we're spending from the previously approved <time2#> balance, and asking for approval of the spend and the new <time#> balance. <amount2> - <amount> = <amount3>, basically breaking up the <time2#> balance entry into a spend and the <time#> balance entry.
 
The issuer removes the <time2#> balance|approved item from his database, adds the <tim#> balance|approved and debit|approved entries, and sends back:
 
  (<issuerid>,debit|approved,<bankid>,<time#>,<assetid>,<amount>).
  (<issuerid>,balance|approved,<bankid>,<time#>,<assetid>,<amount3>)
 
Or, if the issuer doesn't find the <time2#> balance in his database:
 
  (<issuerid>,debit|rejected,(<bankid>,debit|request,<issuerid>,<time#>,<assetid>,<amount>))
 
If the recipient accepts this spend, the bank sends to the issuer:
 
  (<bankid>,debit|accepted,(<issuerid>,debit|approved,<bankid>,<time#>,<assetid>,<amount>)).
  (<bankid>,debit,<issuerid>,<time4#>,<assetid>,<amount4>).
  (<bankid>,credit,<issuerid>,<time3#>,<assetid>,<amount5>)
 
Here <amount> + <amount4> = <amount5>, joining the <time4#> balance with the <time#> debit approval to create the <time3#> balance.
 
The issuer removes the <time#> debit|approved item and the <time4#> balance|approved item from his database, adds the <time3#> balance|approved item, and sends back:
 
  (<issuerid>,balance|approved,<bankid>,<time3#>,<assetid>,<amount5>)
 
Or, if the issuer doesn't find the <time#> debit|approved item or the <time4#> balance|approved item in his database:
 
  (<issuerid>,credit|rejected,(<bankid>,credit|request,<issuerid>,<time3#>,<assetid>,<amount>))
 
If the recipient rejects the spend, and the spender acknowledges the rejection, the bank sends to the issuer:
 
  (<bankid>,debit|rejected,(<issuerid>,debit|approved,<bankid>,<time#>,<assetid>,<amount>)).
  (<bankid>,debit,<issuerid>,<time#>,<assetid>,<amount3>)
  (<bankid>,credit,<issuerid>,<time5#>,<assetid>,<amount2>)
 
<amount> + <amount3> = <amount2>.
 
The issuer removes the <time#> debit|approved item and the <time#> balance|approved item from his database, adds the <time5#> balance|approved item, and sends back:
 
  (<issuerid>,@debit|rejected,(<bankid>,debit|rejected,(<issuerid>,debit|approved,<bankid>,<time#>,<assetid>,<amount>))).
  (<issuerid>,balance|approved,<bankid>,<time5#>,<assetid>,<amount2>)
 
The bank sends the issuer's balance|approved items back to the customer with successful response to a spend or processinbox request:
 
  (<bankid>,@balance,(<id>,balance,<bankid>,<time#>,<assetid>,<amount3>)).
    (<bankid>,@balance|approved,(<issuerid>,balance|approved,<bankid>,<time#>,<assetid>,<amount3>))
 
Or maybe the customer doesn't need to care about that, leaving the issuer approval between the bank and the issuer. The customer's balances are guaranteed, but if the bank counterfeits, the currency will inflate, making his balances worth less than the backing commodity that the issuer promised.
 
Now we've tranformed the bank counterfeiting problem into an issuer reliability problem. We're depending on the issuer to approve balance changes. If he doesn't, the customer is stuck with a bunch of worthless assets. Many issuers won't be in the position to create a reliable server to approve all balance transformations. They'll have to trust that the bank won't counterfeit their assets.
 
If the issuer trusts the bank, his assets can have value beyond his disappearance. If he doesn't, then if his server disappears, none of his assets can ever be traded again. So I don't know if I'll ever implement this. We'll just have to trust banks not to counterfeit, encourage them to get audited by independent auditing firms, and let the market put out of business banks that aren't honest or don't get properly audited.
 
-----
 
Remove zero-balance files and credit a usage token. This requires a new message that captures all the files for, e.g., an account, so the customer can't claim a non-zero balance later on for the now missing file.
 
spend|cancel to take back a spend before the other guy has accepted or rejected it.
 
Support fees other than usage tokens.
 
Error codes on $this->failmsg()
 
$t->SPEND
---------
 
Right now, spends to the bank are rejected right away.
 
Alternatives:
 
1) Accept spends to the bank right away, with no inbox or
outbox. Credit the bank's accounts.
 
2) Reject spends to the bank, but keep the proferred tokens. This might anger some, but possibly no more than losing tokens when another customer rejects your spend.
 
 
Checks and Certificates
-----------------------
 
Have to keep external spend receipt until expiration date. Check or certificate costs a usage token, which is refunded on expiration. Thank you, Patrick!