19 // transaction: either draft or debit cert
27 signed *document.Signed
29 debit *document.DebitCert
34 var serverkey *openpgp.Entity
35 var newchan chan *work
36 var delchan chan *work
38 type worklist struct {
43 func pushwork(ws *worklist, w *work) {
53 func popwork(ws *worklist) *work {
65 func setserverkey(e *openpgp.Entity) (err error) {
71 d, err := key.Format(e)
75 err = db.Set("key", key.Id(e), d)
79 err = db.Set("", "serverkey", d)
83 func GetKeys(fpr string) (es openpgp.EntityList, err error) {
84 b, err := db.Get("key", fpr)
88 es, err = openpgp.ReadKeyRing(bytes.NewBuffer(b))
90 // internal error: pubkey cannot be parsed
96 func AddKeys(d []byte) (err error) {
97 entities, err := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
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))
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)
115 es, err1 := GetKeys(issuer)
120 ok, _, den, err1 := key.Check(es[0])
126 if !ok || den != denom {
127 err = fmt.Errorf("Issuer key check failed")
131 b := new(bytes.Buffer)
137 err = db.Insert("key", fpr, b.Bytes())
141 err = db.Append("keysby/64", fpr[len(fpr)-16:], []byte(fpr))
145 err = db.Append("keysby/32", fpr[len(fpr)-8:], []byte(fpr))
153 // Get cert through the named store
154 func GetCert(name, id string) (d []byte, err error) {
155 certid, err := db.Get(name, id)
160 d, err = db.Get("cert", string(certid))
162 // internal error: cert is not available
168 func EvalDraft(d []byte) (c []byte, err error) {
169 iv, signed, err := document.Parse(d)
173 draft, ok := iv.(*document.Draft)
175 err = fmt.Errorf("EvalDraft: expected a draft document")
178 k, err := db.Get("key", draft.Drawer)
183 kr, err := openpgp.ReadKeyRing(bytes.NewBuffer(k))
185 // internal error: pubkey cannot be parsed
188 err = document.Verify(signed, kr)
192 // _, issuer, denom, err := key.Check(kr[0])
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)
201 k, err = db.Get("key", draft.Beneficiary)
205 kr, err = openpgp.ReadKeyRing(bytes.NewBuffer(k))
207 // internal error: pubkey cannot be parsed
210 return addDraft(d, signed, draft)
213 func EvalDebitCert(d []byte) (c []byte, err error) {
214 iv, signed, err := document.Parse(d)
218 cert, ok := iv.(*document.DebitCert)
220 err = fmt.Errorf("ParseDebitCert: expected a debit docuent")
224 kr := openpgp.EntityList{serverkey}
225 if key.Id(serverkey) != cert.AuthorizedBy {
228 k, err = db.Get("key", cert.AuthorizedBy)
232 kr, err = openpgp.ReadKeyRing(bytes.NewBuffer(k))
234 // internal error: pubkey cannot be parsed
238 // must clean up to make sure the hash is ok
239 err = document.Verify(signed, kr)
243 return addDebit(d, signed, cert)
246 func addDraft(d []byte, signed *document.Signed, draft *document.Draft) (c []byte, err error) {
248 w.docid = document.Id(signed)
249 w.account = fmt.Sprintf("%s.%s", draft.Drawer, draft.Issuer)
253 w.sync = make(chan int)
254 log.Printf("add draft work: %s", w.account)
260 func addDebit(d []byte, signed *document.Signed, cert *document.DebitCert) (c []byte, err error) {
262 w.docid = document.Id(signed)
263 w.account = fmt.Sprintf("%s.%s", cert.Beneficiary, cert.Issuer)
267 w.sync = make(chan int)
268 log.Printf("add debit work: %s", w.account)
275 log.Printf("start dispatch")
276 works := make(map[string]*worklist)
280 log.Printf("queue work: %s", w.account)
281 ws := works[w.account]
283 // TODO: unnecessary alloc
284 works[w.account] = new(worklist)
290 log.Printf("unqueue work: %s", w.account)
291 ws := works[w.account]
294 delete(works, w.account)
302 func handle(w *work) {
303 log.Printf("start work: %s", w.account)
305 w.out, w.err = handleDebit(w)
306 } else if w.draft != nil {
307 w.out, w.err = handleDraft(w)
311 log.Printf("finish work: %s outlen: %d", w.account, len(w.out))
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)
320 if string(oldid) != w.docid {
321 err = fmt.Errorf("draft nonce is not unique (see draft %s)", oldid)
323 c, err = GetCert("certby/draft", w.docid)
326 } else if _, ok := err.(store.NotFoundError); !ok {
330 err = db.Begin(w.docid)
334 err = db.Set("draft", w.docid, w.in)
338 err = db.Set("draftby/key.issuer.nonce", nonce, []byte(w.docid))
342 cert, err := newDebitCert(w)
344 // probably client error
348 c, signed, err := document.Format(cert, serverkey)
352 certid := document.Id(signed)
353 err = db.Set("cert", certid, c)
358 err = db.Set("certby/draft", w.docid, []byte(certid))
364 err = db.Set("certby/key.issuer.serial", fmt.Sprintf("%s.%09d", w.account, cert.Serial), []byte(certid))
369 err = db.Set("certby/key.issuer", w.account, []byte(certid))
374 // TODO: run journal cleanup in case of client errors
375 err = db.End(w.docid)
379 func handleDebit(w *work) (c []byte, err error) {
380 c, err = GetCert("certby/debit", w.docid)
383 } else if _, ok := err.(store.NotFoundError); !ok {
387 err = db.Begin(w.docid)
391 err = db.Set("cert", w.docid, w.in)
395 // TODO: check pubkey etc
396 cert, err := newCreditCert(w)
401 c, signed, err := document.Format(cert, serverkey)
406 certid := document.Id(signed)
407 err = db.Set("cert", certid, c)
412 err = db.Set("certby/debit", w.docid, []byte(certid))
418 err = db.Set("certby/key.issuer.serial", fmt.Sprintf("%s.%09d", w.account, cert.Serial), []byte(certid))
423 err = db.Set("certby/key.issuer", w.account, []byte(certid))
432 func isIssuer(c *document.Cert) bool {
433 return c.Issuer == c.Holder
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
445 cert.Beneficiary = w.draft.Beneficiary
447 oid, err := db.Get("certby/key.issuer", w.account)
448 oldcertid := string(oid)
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)
455 cert.Balance = cert.Difference
456 cert.LastDebitSerial = 0
457 cert.LastCreditSerial = 0
459 d, err := db.Get("cert", oldcertid)
463 iv, _, err := document.Parse(d)
468 // TODO: make sure oldcert and newcert cannot become inconsistent
469 // TODO: this is a hack
470 oldcert, err := document.ToCert(iv)
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)
481 if !isIssuer(&cert.Cert) && cert.Balance < 0 {
482 return nil, fmt.Errorf("insufficient funds: %d, draft: %d", oldcert.Balance, cert.Difference)
484 cert.LastDebitSerial = oldcert.LastDebitSerial
485 cert.LastCreditSerial = oldcert.LastCreditSerial
486 if _, ok := iv.(*document.DebitCert); ok {
487 cert.LastDebitSerial = oldcert.Serial
489 cert.LastCreditSerial = oldcert.Serial
491 cert.LastCert = &oldcertid
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
510 oid, err := db.Get("certby/key.issuer", w.account)
511 oldcertid := string(oid)
513 if _, ok := err.(store.NotFoundError); !ok {
517 // this is the first cert
519 cert.Balance = cert.Difference
520 cert.LastDebitSerial = 0
521 cert.LastCreditSerial = 0
523 d, err := db.Get("cert", oldcertid)
528 iv, _, err := document.Parse(d)
533 // TODO: this is a hack
534 oldcert, err := document.ToCert(iv)
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)
545 if isIssuer(&cert.Cert) && cert.Balance > 0 {
546 return nil, fmt.Errorf("internal error")
548 cert.LastDebitSerial = oldcert.LastDebitSerial
549 cert.LastCreditSerial = oldcert.LastCreditSerial
550 if _, ok := iv.(*document.DebitCert); ok {
551 cert.LastDebitSerial = oldcert.Serial
553 cert.LastCreditSerial = oldcert.Serial
555 cert.LastCert = &oldcertid
560 func Init(rootdir string, sk *openpgp.Entity) (err error) {
561 db, err = store.Open(rootdir)
565 err = db.Ensure("key")
569 err = db.Ensure("cert")
573 err = db.Ensure("draft")
577 err = db.Ensure("certby/draft")
581 err = db.Ensure("certby/debit")
585 err = db.Ensure("certby/key.issuer")
589 err = db.Ensure("certby/key.issuer.serial")
593 err = db.Ensure("draftby/key.issuer.nonce")
597 err = db.Ensure("keysby/64")
601 err = db.Ensure("keysby/32")
605 err = setserverkey(sk)
609 newchan = make(chan *work)
610 delchan = make(chan *work)