18 // transaction: either draft or debit cert
26 signed *document.Signed
28 debit *document.DebitCert
33 var serverkey *openpgp.Entity
34 var newchan chan *work
35 var delchan chan string
37 type worklist struct {
42 func pushwork(ws *worklist, w *work) {
52 func popwork(ws *worklist) *work {
64 func storekey() (err error) {
65 b := new(bytes.Buffer)
66 err = serverkey.Serialize(b)
70 err = db.Set("key", key.Id(serverkey), b.Bytes())
74 err = db.Set("", "serverkey", b.Bytes())
78 func GetKeys(fpr string) (es openpgp.EntityList, err error) {
79 b, err := db.Get("key", fpr)
83 es, err = openpgp.ReadKeyRing(bytes.NewBuffer(b))
85 // internal error: pubkey cannot be parsed
91 func AddKeys(d []byte) (err error) {
92 entities, err := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
96 // TODO: allow multiple key uploads at once?
97 if len(entities) > 100 {
98 err = fmt.Errorf("expected at most 100 keys; got %d", len(entities))
101 for _, e := range entities {
102 // TODO: various checks..
103 // TODO: collect errors instead of aborting addkeys
104 isIssuer, issuer, denom, err1 := key.Check(e)
110 es, err1 := GetKeys(issuer)
115 ok, _, den, err1 := key.Check(es[0])
121 if !ok || den != denom {
122 err = fmt.Errorf("Issuer key check failed")
126 b := new(bytes.Buffer)
132 err = db.Insert("key", fpr, b.Bytes())
136 err = db.Append("keysby/64", fpr[len(fpr)-16:], []byte(fpr))
140 err = db.Append("keysby/32", fpr[len(fpr)-8:], []byte(fpr))
148 // Get cert through the named store
149 func GetCert(name, id string) (d []byte, err error) {
150 certid, err := db.Get(name, id)
155 d, err = db.Get("cert", string(certid))
157 // internal error: cert is not available
163 func EvalDraft(d []byte) (c []byte, err error) {
164 iv, signed, err := document.Parse(d)
168 draft, ok := iv.(*document.Draft)
170 err = fmt.Errorf("EvalDraft: expected a draft document")
173 k, err := db.Get("key", draft.Drawer)
178 kr, err := openpgp.ReadKeyRing(bytes.NewBuffer(k))
180 // internal error: pubkey cannot be parsed
183 err = document.Verify(signed, kr)
187 // _, issuer, denom, err := key.Check(kr[0])
191 // TODO: do various format checks (AuthorizedBy check etc)
192 if draft.Amount <= 0 || draft.Amount >= IntLimit {
193 err = fmt.Errorf("draft amount is invalid: %d", draft.Amount)
196 k, err = db.Get("key", draft.Beneficiary)
200 kr, err = openpgp.ReadKeyRing(bytes.NewBuffer(k))
202 // internal error: pubkey cannot be parsed
205 return addDraft(d, signed, draft)
208 func EvalDebitCert(d []byte) (c []byte, err error) {
209 iv, signed, err := document.Parse(d)
213 cert, ok := iv.(*document.DebitCert)
215 err = fmt.Errorf("ParseDebitCert: expected a debit docuent")
219 kr := openpgp.EntityList{serverkey}
220 if key.Id(serverkey) != cert.AuthorizedBy {
223 k, err = db.Get("key", cert.AuthorizedBy)
227 kr, err = openpgp.ReadKeyRing(bytes.NewBuffer(k))
229 // internal error: pubkey cannot be parsed
233 // must clean up to make sure the hash is ok
234 err = document.Verify(signed, kr)
238 return addDebit(d, signed, cert)
241 func addDraft(d []byte, signed *document.Signed, draft *document.Draft) (c []byte, err error) {
243 w.docid = document.Id(signed)
244 w.account = fmt.Sprintf("%s.%s", draft.Drawer, draft.Issuer)
248 w.sync = make(chan int)
254 func addDebit(d []byte, signed *document.Signed, cert *document.DebitCert) (c []byte, err error) {
256 w.docid = document.Id(signed)
257 w.account = fmt.Sprintf("%s.%s", cert.Holder, cert.Issuer)
261 w.sync = make(chan int)
268 works := make(map[string]*worklist)
272 ws := works[w.account]
274 // TODO: unnecessary alloc
275 works[w.account] = new(worklist)
280 case account := <-delchan:
284 delete(works, account)
292 func handle(w *work) {
295 } else if w.draft != nil {
304 func handleDraft(w *work) {
305 nonce := fmt.Sprintf("%s.%s", w.account, w.draft.Nonce)
306 oldid, err := db.Get("draftby/key.issuer.nonce", nonce)
308 if string(oldid) != w.docid {
309 w.err = fmt.Errorf("draft nonce is not unique (see draft %s)", oldid)
311 w.out, w.err = GetCert("certby/draft", w.docid)
314 } else if _, ok := err.(store.NotFoundError); !ok {
319 err = db.Begin(w.docid)
324 err = db.Set("draft", w.docid, w.in)
329 err = db.Set("draftby/key.issuer.nonce", nonce, []byte(w.docid))
334 cert, err := newDebitCert(w)
336 // probably client error
340 c, signed, err := document.Format(cert, serverkey)
344 certid := document.Id(signed)
346 err = db.Set("cert", certid, c)
351 err = db.Set("certby/draft", w.docid, []byte(certid))
357 err = db.Set("certby/key.issuer.serial", fmt.Sprintf("%s.%09d", w.account, cert.Serial), []byte(certid))
362 err = db.Set("certby/key.issuer", w.account, []byte(certid))
367 // TODO: run journal cleanup in case of client errors
368 err = db.End(w.docid)
372 func handleDebit(w *work) {
373 c, err := GetCert("certby/debit", w.docid)
377 } else if _, ok := err.(store.NotFoundError); !ok {
382 err = db.Begin(w.docid)
387 err = db.Set("cert", w.docid, w.in)
392 // TODO: check pubkey etc
393 cert, err := newCreditCert(w)
398 c, signed, err := document.Format(cert, serverkey)
403 certid := document.Id(signed)
404 err = db.Set("cert", certid, c)
409 err = db.Set("certby/debit", w.docid, []byte(certid))
415 err = db.Set("certby/key.issuer.serial", fmt.Sprintf("%s.%09d", w.account, cert.Serial), []byte(certid))
420 err = db.Set("certby/key.issuer", w.account, []byte(certid))
429 func newDebitCert(w *work) (*document.DebitCert, error) {
430 cert := new(document.DebitCert)
431 cert.Holder = w.draft.Drawer
432 cert.Date = time.Now().Unix()
433 cert.Denomination = "epoint"
434 cert.Issuer = w.draft.Issuer
435 cert.AuthorizedBy = w.draft.AuthorizedBy
436 cert.Difference = -w.draft.Amount
438 cert.Beneficiary = w.draft.Beneficiary
440 oid, err := db.Get("certby/key.issuer", w.account)
441 oldcertid := string(oid)
443 // first cert: drawer is issuer
444 if w.draft.Drawer != w.draft.Issuer {
445 return nil, fmt.Errorf("drawer must be the issuer when drawing an empty account (%s != %s)", w.draft.Drawer, w.draft.Issuer)
448 cert.Balance = cert.Difference
449 cert.LastDebitSerial = 0
450 cert.LastCreditSerial = 0
452 d, err := db.Get("cert", oldcertid)
456 iv, _, err := document.Parse(d)
461 // TODO: this is a hack
462 oldcert, err := document.ToCert(iv)
467 // TODO: sanity checks? oldcert.Holder == draft.Drawer
468 cert.Serial = oldcert.Serial + 1
469 cert.Balance = oldcert.Balance + cert.Difference
470 if cert.Balance <= -IntLimit {
471 return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
473 if oldcert.Balance > 0 && cert.Balance < 0 {
474 return nil, fmt.Errorf("insufficient funds: %d", oldcert.Balance)
476 cert.LastDebitSerial = oldcert.LastDebitSerial
477 cert.LastCreditSerial = oldcert.LastCreditSerial
478 if _, ok := iv.(*document.DebitCert); ok {
479 cert.LastDebitSerial = oldcert.Serial
481 cert.LastCreditSerial = oldcert.Serial
483 cert.LastCert = &oldcertid
488 func newCreditCert(w *work) (*document.CreditCert, error) {
489 cert := new(document.CreditCert)
490 // TODO: get from old cert instead?
491 cert.Holder = w.debit.Beneficiary
492 cert.Date = time.Now().Unix()
493 // TODO: get these from the cert holder pubkey
494 cert.Denomination = "epoint"
495 cert.Issuer = w.debit.Issuer
496 cert.AuthorizedBy = w.debit.AuthorizedBy // TODO: draft vs dcert vs serverside decision
497 cert.Difference = -w.debit.Difference
498 cert.Draft = w.debit.Draft
499 cert.Drawer = w.debit.Holder
500 cert.DebitCert = w.docid
502 oid, err := db.Get("certby/key", w.debit.Beneficiary)
503 oldcertid := string(oid)
505 // this is the first cert
507 cert.Balance = cert.Difference
508 cert.LastDebitSerial = 0
509 cert.LastCreditSerial = 0
511 d, err := db.Get("cert", oldcertid)
516 iv, _, err := document.Parse(d)
521 // TODO: this is a hack
522 oldcert, err := document.ToCert(iv)
527 cert.Serial = oldcert.Serial + 1
528 cert.Balance = oldcert.Balance + cert.Difference
529 if cert.Balance >= IntLimit {
530 return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
532 cert.LastDebitSerial = oldcert.LastDebitSerial
533 cert.LastCreditSerial = oldcert.LastCreditSerial
534 if _, ok := iv.(*document.DebitCert); ok {
535 cert.LastDebitSerial = oldcert.Serial
537 cert.LastCreditSerial = oldcert.Serial
539 cert.LastCert = &oldcertid
544 func Init(rootdir string, sk *openpgp.Entity) (err error) {
545 db, err = store.Open(rootdir)
549 err = db.Ensure("key")
553 err = db.Ensure("cert")
557 err = db.Ensure("draft")
561 err = db.Ensure("certby/draft")
565 err = db.Ensure("certby/debit")
569 err = db.Ensure("certby/key.issuer")
573 err = db.Ensure("certby/key.issuer.serial")
577 err = db.Ensure("draftby/key.issuer.nonce")
581 err = db.Ensure("keysby/64")
585 err = db.Ensure("keysby/32")