fix balance check in server logic
[epoint] / pkg / server / server.go
1 package server
2
3 // main transfer logic
4
5 import (
6         "bytes"
7         "crypto/openpgp"
8         "epoint/document"
9         "epoint/key"
10         "epoint/store"
11         "fmt"
12         "log"
13         "time"
14 )
15
16 // TODO: do in docs?
17 const IntLimit = 1e15
18
19 // transaction: either draft or debit cert
20 type work struct {
21         next    *work
22         docid   string
23         account string
24         in      []byte
25         out     []byte
26         err     error
27         signed  *document.Signed
28         draft   *document.Draft
29         debit   *document.DebitCert
30         sync    chan int
31 }
32
33 var db *store.Conn
34 var serverkey *openpgp.Entity
35 var newchan chan *work
36 var delchan chan *work
37
38 type worklist struct {
39         head *work
40         tail *work
41 }
42
43 func pushwork(ws *worklist, w *work) {
44         if ws.tail == nil {
45                 ws.head = w
46                 ws.tail = w
47         } else {
48                 ws.tail.next = w
49                 ws.tail = w
50         }
51 }
52
53 func popwork(ws *worklist) *work {
54         w := ws.head
55         if w == nil {
56                 return nil
57         }
58         ws.head = w.next
59         if ws.head == nil {
60                 ws.tail = nil
61         }
62         return w
63 }
64
65 func setserverkey(e *openpgp.Entity) (err error) {
66         serverkey = e
67         err = key.SelfSign(e)
68         if err != nil {
69                 return
70         }
71         d, err := key.Format(e)
72         if err != nil {
73                 return
74         }
75         err = db.Set("key", key.Id(e), d)
76         if err != nil {
77                 return
78         }
79         err = db.Set("", "serverkey", d)
80         return
81 }
82
83 func GetKeys(fpr string) (es openpgp.EntityList, err error) {
84         b, err := db.Get("key", fpr)
85         if err != nil {
86                 return
87         }
88         es, err = openpgp.ReadKeyRing(bytes.NewBuffer(b))
89         if err != nil {
90                 // internal error: pubkey cannot be parsed
91                 return
92         }
93         return
94 }
95
96 func AddKeys(d []byte) (err error) {
97         entities, err := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
98         if err != nil {
99                 return
100         }
101         // TODO: allow multiple key uploads at once?
102         if len(entities) > 100 {
103                 err = fmt.Errorf("expected at most 100 keys; got %d", len(entities))
104                 return
105         }
106         for _, e := range entities {
107                 // TODO: various checks..
108                 // TODO: collect errors instead of aborting addkeys
109                 isIssuer, issuer, denom, err1 := key.Check(e)
110                 err = err1
111                 if err != nil {
112                         return
113                 }
114                 if !isIssuer {
115                         es, err1 := GetKeys(issuer)
116                         err = err1
117                         if err != nil {
118                                 return
119                         }
120                         ok, _, den, err1 := key.Check(es[0])
121                         err = err1
122                         if err != nil {
123                                 // internal error
124                                 return
125                         }
126                         if !ok || den != denom {
127                                 err = fmt.Errorf("Issuer key check failed")
128                                 return
129                         }
130                 }
131                 b := new(bytes.Buffer)
132                 err = e.Serialize(b)
133                 if err != nil {
134                         return
135                 }
136                 fpr := key.Id(e)
137                 err = db.Insert("key", fpr, b.Bytes())
138                 if err != nil {
139                         return
140                 }
141                 err = db.Append("keysby/64", fpr[len(fpr)-16:], []byte(fpr))
142                 if err != nil {
143                         return
144                 }
145                 err = db.Append("keysby/32", fpr[len(fpr)-8:], []byte(fpr))
146                 if err != nil {
147                         return
148                 }
149         }
150         return
151 }
152
153 // Get cert through the named store
154 func GetCert(name, id string) (d []byte, err error) {
155         certid, err := db.Get(name, id)
156         if err != nil {
157                 // ok if notfound
158                 return
159         }
160         d, err = db.Get("cert", string(certid))
161         if err != nil {
162                 // internal error: cert is not available
163                 return
164         }
165         return
166 }
167
168 func EvalDraft(d []byte) (c []byte, err error) {
169         iv, signed, err := document.Parse(d)
170         if err != nil {
171                 return
172         }
173         draft, ok := iv.(*document.Draft)
174         if !ok {
175                 err = fmt.Errorf("EvalDraft: expected a draft document")
176                 return
177         }
178         k, err := db.Get("key", draft.Drawer)
179         if err != nil {
180                 return
181         }
182         // TODO: key.Parse
183         kr, err := openpgp.ReadKeyRing(bytes.NewBuffer(k))
184         if err != nil {
185                 // internal error: pubkey cannot be parsed
186                 return
187         }
188         err = document.Verify(signed, kr)
189         if err != nil {
190                 return
191         }
192         //      _, issuer, denom, err := key.Check(kr[0])
193         //      if err != nil {
194         //              return
195         //      }
196         // TODO: do various format checks (AuthorizedBy check etc)
197         if draft.Amount <= 0 || draft.Amount >= IntLimit {
198                 err = fmt.Errorf("draft amount is invalid: %d", draft.Amount)
199                 return
200         }
201         k, err = db.Get("key", draft.Beneficiary)
202         if err != nil {
203                 return
204         }
205         kr, err = openpgp.ReadKeyRing(bytes.NewBuffer(k))
206         if err != nil {
207                 // internal error: pubkey cannot be parsed
208                 return
209         }
210         return addDraft(d, signed, draft)
211 }
212
213 func EvalDebitCert(d []byte) (c []byte, err error) {
214         iv, signed, err := document.Parse(d)
215         if err != nil {
216                 return
217         }
218         cert, ok := iv.(*document.DebitCert)
219         if !ok {
220                 err = fmt.Errorf("ParseDebitCert: expected a debit docuent")
221                 return
222         }
223
224         kr := openpgp.EntityList{serverkey}
225         if key.Id(serverkey) != cert.AuthorizedBy {
226                 // TODO: ...
227                 k := []byte(nil)
228                 k, err = db.Get("key", cert.AuthorizedBy)
229                 if err != nil {
230                         return
231                 }
232                 kr, err = openpgp.ReadKeyRing(bytes.NewBuffer(k))
233                 if err != nil {
234                         // internal error: pubkey cannot be parsed
235                         return
236                 }
237         }
238         // must clean up to make sure the hash is ok
239         err = document.Verify(signed, kr)
240         if err != nil {
241                 return
242         }
243         return addDebit(d, signed, cert)
244 }
245
246 func addDraft(d []byte, signed *document.Signed, draft *document.Draft) (c []byte, err error) {
247         w := new(work)
248         w.docid = document.Id(signed)
249         w.account = fmt.Sprintf("%s.%s", draft.Drawer, draft.Issuer)
250         w.in = d
251         w.signed = signed
252         w.draft = draft
253         w.sync = make(chan int)
254         log.Printf("add draft work: %s", w.account)
255         newchan <- w
256         <-w.sync
257         return w.out, w.err
258 }
259
260 func addDebit(d []byte, signed *document.Signed, cert *document.DebitCert) (c []byte, err error) {
261         w := new(work)
262         w.docid = document.Id(signed)
263         w.account = fmt.Sprintf("%s.%s", cert.Beneficiary, cert.Issuer)
264         w.in = d
265         w.signed = signed
266         w.debit = cert
267         w.sync = make(chan int)
268         log.Printf("add debit work: %s", w.account)
269         newchan <- w
270         <-w.sync
271         return w.out, w.err
272 }
273
274 func dispatch() {
275         log.Printf("start dispatch")
276         works := make(map[string]*worklist)
277         for {
278                 select {
279                 case w := <-newchan:
280                         log.Printf("queue work: %s", w.account)
281                         ws := works[w.account]
282                         if ws == nil {
283                                 // TODO: unnecessary alloc
284                                 works[w.account] = new(worklist)
285                                 go handle(w)
286                         } else {
287                                 pushwork(ws, w)
288                         }
289                 case w := <-delchan:
290                         log.Printf("unqueue work: %s", w.account)
291                         ws := works[w.account]
292                         wnext := popwork(ws)
293                         if wnext == nil {
294                                 delete(works, w.account)
295                         } else {
296                                 go handle(wnext)
297                         }
298                 }
299         }
300 }
301
302 func handle(w *work) {
303         log.Printf("start work: %s", w.account)
304         if w.debit != nil {
305                 w.out, w.err = handleDebit(w)
306         } else if w.draft != nil {
307                 w.out, w.err = handleDraft(w)
308         } else {
309                 panic("unreachable")
310         }
311         log.Printf("finish work: %s outlen: %d", w.account, len(w.out))
312         delchan <- w
313         w.sync <- 0
314 }
315
316 func handleDraft(w *work) (c []byte, err error) {
317         nonce := fmt.Sprintf("%s.%s", w.account, w.draft.Nonce)
318         oldid, err := db.Get("draftby/key.issuer.nonce", nonce)
319         if err == nil {
320                 if string(oldid) != w.docid {
321                         err = fmt.Errorf("draft nonce is not unique (see draft %s)", oldid)
322                 } else {
323                         c, err = GetCert("certby/draft", w.docid)
324                 }
325                 return
326         } else if _, ok := err.(store.NotFoundError); !ok {
327                 return
328         }
329
330         err = db.Begin(w.docid)
331         if err != nil {
332                 return
333         }
334         err = db.Set("draft", w.docid, w.in)
335         if err != nil {
336                 return
337         }
338         err = db.Set("draftby/key.issuer.nonce", nonce, []byte(w.docid))
339         if err != nil {
340                 return
341         }
342         cert, err := newDebitCert(w)
343         if err != nil {
344                 // probably client error
345                 db.End(w.docid)
346                 return
347         }
348         c, signed, err := document.Format(cert, serverkey)
349         if err != nil {
350                 return
351         }
352         certid := document.Id(signed)
353         err = db.Set("cert", certid, c)
354         if err != nil {
355                 // internal error
356                 return
357         }
358         err = db.Set("certby/draft", w.docid, []byte(certid))
359         if err != nil {
360                 // internal error
361                 return
362         }
363         // TODO: append?
364         err = db.Set("certby/key.issuer.serial", fmt.Sprintf("%s.%09d", w.account, cert.Serial), []byte(certid))
365         if err != nil {
366                 // internal error
367                 return
368         }
369         err = db.Set("certby/key.issuer", w.account, []byte(certid))
370         if err != nil {
371                 // internal error
372                 return
373         }
374         // TODO: run journal cleanup in case of client errors
375         err = db.End(w.docid)
376         return
377 }
378
379 func handleDebit(w *work) (c []byte, err error) {
380         c, err = GetCert("certby/debit", w.docid)
381         if err == nil {
382                 return
383         } else if _, ok := err.(store.NotFoundError); !ok {
384                 // internal error
385                 return
386         }
387         err = db.Begin(w.docid)
388         if err != nil {
389                 return
390         }
391         err = db.Set("cert", w.docid, w.in)
392         if err != nil {
393                 return
394         }
395         // TODO: check pubkey etc
396         cert, err := newCreditCert(w)
397         if err != nil {
398                 // internal error
399                 return
400         }
401         c, signed, err := document.Format(cert, serverkey)
402         if err != nil {
403                 // internal error
404                 return
405         }
406         certid := document.Id(signed)
407         err = db.Set("cert", certid, c)
408         if err != nil {
409                 // internal error
410                 return
411         }
412         err = db.Set("certby/debit", w.docid, []byte(certid))
413         if err != nil {
414                 // internal error
415                 return
416         }
417         // TODO: append?
418         err = db.Set("certby/key.issuer.serial", fmt.Sprintf("%s.%09d", w.account, cert.Serial), []byte(certid))
419         if err != nil {
420                 // internal error
421                 return
422         }
423         err = db.Set("certby/key.issuer", w.account, []byte(certid))
424         if err != nil {
425                 // internal error
426                 return
427         }
428         db.End(w.docid)
429         return
430 }
431
432 func isIssuer(c *document.Cert) bool {
433         return c.Issuer == c.Holder
434 }
435
436 func newDebitCert(w *work) (*document.DebitCert, error) {
437         cert := new(document.DebitCert)
438         cert.Holder = w.draft.Drawer
439         cert.Date = time.Now().Unix()
440         cert.Denomination = "epoint"
441         cert.Issuer = w.draft.Issuer
442         cert.AuthorizedBy = w.draft.AuthorizedBy
443         cert.Difference = -w.draft.Amount
444         cert.Draft = w.docid
445         cert.Beneficiary = w.draft.Beneficiary
446
447         oid, err := db.Get("certby/key.issuer", w.account)
448         oldcertid := string(oid)
449         if err != nil {
450                 // first cert: drawer is issuer
451                 if w.draft.Drawer != w.draft.Issuer {
452                         return nil, fmt.Errorf("drawer must be the issuer when drawing an empty account (%s != %s)", w.draft.Drawer, w.draft.Issuer)
453                 }
454                 cert.Serial = 1
455                 cert.Balance = cert.Difference
456                 cert.LastDebitSerial = 0
457                 cert.LastCreditSerial = 0
458         } else {
459                 d, err := db.Get("cert", oldcertid)
460                 if err != nil {
461                         return nil, err
462                 }
463                 iv, _, err := document.Parse(d)
464                 if err != nil {
465                         // internal error
466                         return nil, err
467                 }
468                 // TODO: make sure oldcert and newcert cannot become inconsistent
469                 // TODO: this is a hack
470                 oldcert, err := document.ToCert(iv)
471                 if err != nil {
472                         // internal error
473                         return nil, err
474                 }
475                 // TODO: sanity checks? oldcert.Holder == draft.Drawer
476                 cert.Serial = oldcert.Serial + 1
477                 cert.Balance = oldcert.Balance + cert.Difference
478                 if cert.Balance <= -IntLimit {
479                         return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
480                 }
481                 if !isIssuer(&cert.Cert) && cert.Balance < 0 {
482                         return nil, fmt.Errorf("insufficient funds: %d, draft: %d", oldcert.Balance, cert.Difference)
483                 }
484                 cert.LastDebitSerial = oldcert.LastDebitSerial
485                 cert.LastCreditSerial = oldcert.LastCreditSerial
486                 if _, ok := iv.(*document.DebitCert); ok {
487                         cert.LastDebitSerial = oldcert.Serial
488                 } else {
489                         cert.LastCreditSerial = oldcert.Serial
490                 }
491                 cert.LastCert = &oldcertid
492         }
493         return cert, nil
494 }
495
496 func newCreditCert(w *work) (*document.CreditCert, error) {
497         cert := new(document.CreditCert)
498         // TODO: get from old cert instead?
499         cert.Holder = w.debit.Beneficiary
500         cert.Date = time.Now().Unix()
501         // TODO: get these from the cert holder pubkey
502         cert.Denomination = "epoint"
503         cert.Issuer = w.debit.Issuer
504         cert.AuthorizedBy = w.debit.AuthorizedBy // TODO: draft vs dcert vs serverside decision
505         cert.Difference = -w.debit.Difference
506         cert.Draft = w.debit.Draft
507         cert.Drawer = w.debit.Holder
508         cert.DebitCert = w.docid
509
510         oid, err := db.Get("certby/key.issuer", w.account)
511         oldcertid := string(oid)
512         if err != nil {
513                 if _, ok := err.(store.NotFoundError); !ok {
514                         // internal error
515                         return nil, err
516                 }
517                 // this is the first cert
518                 cert.Serial = 1
519                 cert.Balance = cert.Difference
520                 cert.LastDebitSerial = 0
521                 cert.LastCreditSerial = 0
522         } else {
523                 d, err := db.Get("cert", oldcertid)
524                 if err != nil {
525                         // internal error
526                         return nil, err
527                 }
528                 iv, _, err := document.Parse(d)
529                 if err != nil {
530                         // internal error
531                         return nil, err
532                 }
533                 // TODO: this is a hack
534                 oldcert, err := document.ToCert(iv)
535                 if err != nil {
536                         // internal error
537                         return nil, err
538                 }
539                 cert.Serial = oldcert.Serial + 1
540                 cert.Balance = oldcert.Balance + cert.Difference
541                 if cert.Balance >= IntLimit {
542                         return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
543                 }
544                 // sanity check
545                 if isIssuer(&cert.Cert) && cert.Balance > 0 {
546                         return nil, fmt.Errorf("internal error")
547                 }
548                 cert.LastDebitSerial = oldcert.LastDebitSerial
549                 cert.LastCreditSerial = oldcert.LastCreditSerial
550                 if _, ok := iv.(*document.DebitCert); ok {
551                         cert.LastDebitSerial = oldcert.Serial
552                 } else {
553                         cert.LastCreditSerial = oldcert.Serial
554                 }
555                 cert.LastCert = &oldcertid
556         }
557         return cert, nil
558 }
559
560 func Init(rootdir string, sk *openpgp.Entity) (err error) {
561         db, err = store.Open(rootdir)
562         if err != nil {
563                 return
564         }
565         err = db.Ensure("key")
566         if err != nil {
567                 return
568         }
569         err = db.Ensure("cert")
570         if err != nil {
571                 return
572         }
573         err = db.Ensure("draft")
574         if err != nil {
575                 return
576         }
577         err = db.Ensure("certby/draft")
578         if err != nil {
579                 return
580         }
581         err = db.Ensure("certby/debit")
582         if err != nil {
583                 return
584         }
585         err = db.Ensure("certby/key.issuer")
586         if err != nil {
587                 return
588         }
589         err = db.Ensure("certby/key.issuer.serial")
590         if err != nil {
591                 return
592         }
593         err = db.Ensure("draftby/key.issuer.nonce")
594         if err != nil {
595                 return
596         }
597         err = db.Ensure("keysby/64")
598         if err != nil {
599                 return
600         }
601         err = db.Ensure("keysby/32")
602         if err != nil {
603                 return
604         }
605         err = setserverkey(sk)
606         if err != nil {
607                 return
608         }
609         newchan = make(chan *work)
610         delchan = make(chan *work)
611         go dispatch()
612         return
613 }