code reorganization
[epoint] / logic / logic.go
diff --git a/logic/logic.go b/logic/logic.go
deleted file mode 100644 (file)
index 602f458..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-package logic
-
-// main transfer logic
-
-import (
-       "bytes"
-       "crypto/openpgp"
-       "epoint/document"
-       "epoint/dsakey"
-       "epoint/store"
-       "fmt"
-       "time"
-)
-
-// TODO: do in docs?
-const IntLimit = 1e15
-
-var db *store.Conn
-
-func StoreSk(sk *openpgp.Entity) (err error) {
-       // TODO: initkey should save serverkey in db
-       b := new(bytes.Buffer)
-       err = sk.Serialize(b)
-       if err != nil {
-               return
-       }
-       return db.Set("key", fmt.Sprintf("%X", sk.PrimaryKey.Fingerprint), b.Bytes())
-}
-
-func GetKeys(fpr string) (es openpgp.EntityList, err error) {
-       b, err := db.Get("key", fpr)
-       if err != nil {
-               return
-       }
-       es, err = openpgp.ReadKeyRing(bytes.NewBuffer(b))
-       if err != nil {
-               // internal error: pubkey cannot be parsed
-               return
-       }
-       return
-}
-
-func AddKeys(d []byte) (err error) {
-       entities, err := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
-       if err != nil {
-               return
-       }
-       // TODO: allow multiple key uploads at once?
-       if len(entities) > 100 {
-               err = fmt.Errorf("expected at most 100 keys; got %d", len(entities))
-               return
-       }
-       for _, e := range entities {
-               // TODO: various checks..
-               isIssuer, issuer, denom, err1 := dsakey.CheckEntity(e)
-               err = err1
-               if err != nil {
-                       // TODO..
-                       continue
-               }
-               if !isIssuer {
-                       es, err := GetKeys(issuer)
-                       if err != nil {
-                               // TODO..
-                               continue
-                       }
-                       ok, _, den, err := dsakey.CheckEntity(es[0])
-                       if !ok || err != nil || den != denom {
-                               // TODO..
-                               continue
-                       }
-               }
-               b := new(bytes.Buffer)
-               err = e.Serialize(b)
-               if err != nil {
-                       return
-               }
-               fpr := fmt.Sprintf("%X", e.PrimaryKey.Fingerprint)
-               err = db.Set("key", fpr, b.Bytes())
-               if err != nil {
-                       return
-               }
-               err = db.Append("keysby/64", fpr[len(fpr)-16:], []byte(fpr))
-               if err != nil {
-                       return
-               }
-               err = db.Append("keysby/32", fpr[len(fpr)-8:], []byte(fpr))
-               if err != nil {
-                       return
-               }
-       }
-       return
-}
-
-func CertByDraft(draftid string) (d []byte, err error) {
-       certid, err := db.Get("certby/draft", draftid)
-       if err != nil {
-               // TODO: we have the draft but the cert is not ready
-               return
-       }
-       d, err = db.Get("cert", string(certid))
-       if err != nil {
-               // shouldn't happen, cert is not available
-               return
-       }
-       return
-}
-
-func CertByDebitCert(debitid string) (d []byte, err error) {
-       creditid, err := db.Get("certby/debit", debitid)
-       if err != nil {
-               // TODO: we have the debit cert but the credit cert is not ready
-               return
-       }
-       d, err = db.Get("cert", string(creditid))
-       if err != nil {
-               // shouldn't happen, cert is not available
-               return
-       }
-       return
-}
-
-// parse clear signed draft and verify it
-func ParseDraft(d []byte) (draft *document.Draft, draftid string, err error) {
-       iv, signed, err := document.Parse(d)
-       if err != nil {
-               return
-       }
-       draft, ok := iv.(*document.Draft)
-       if !ok {
-               err = fmt.Errorf("ParseDraft: expected a draft docuent")
-               return
-       }
-       draftid = document.Id(signed)
-
-       k, err := db.Get("key", draft.Drawer)
-       if err != nil {
-               return
-       }
-       kr, err := openpgp.ReadKeyRing(bytes.NewBuffer(k))
-       if err != nil {
-               // internal error: pubkey cannot be parsed
-               return
-       }
-       err = document.Verify(signed, kr)
-       if err != nil {
-               return
-       }
-       _, issuer, denom, err := dsakey.CheckEntity(kr[0])
-       if err != nil {
-               return
-       }
-       k, err = db.Get("key", draft.Beneficiary)
-       if err != nil {
-               return
-       }
-       kr, err = openpgp.ReadKeyRing(bytes.NewBuffer(k))
-       if err != nil {
-               // internal error: pubkey cannot be parsed
-               return
-       }
-       _, issuer2, denom2, err := dsakey.CheckEntity(kr[0])
-       if err != nil {
-               return
-       }
-       if draft.Issuer != issuer ||
-               draft.Issuer != issuer2 ||
-               draft.Denomination != denom ||
-               draft.Denomination != denom2 {
-               err = fmt.Errorf("Issuer or denomination mismatch")
-               return
-       }
-
-       // TODO: do various format checks (AuthorizedBy check etc)
-       if draft.Amount <= 0 || draft.Amount >= IntLimit {
-               err = fmt.Errorf("draft amount is invalid: %d", draft.Amount)
-               return
-       }
-       return
-}
-
-func ParseDebitCert(d []byte) (cert *document.DebitCert, certid string, err error) {
-       iv, signed, err := document.Parse(d)
-       if err != nil {
-               return
-       }
-       cert, ok := iv.(*document.DebitCert)
-       if !ok {
-               err = fmt.Errorf("ParseDebitCert: expected a debit docuent")
-               return
-       }
-
-       k, err := db.Get("key", cert.AuthorizedBy)
-       if err != nil {
-               return
-       }
-       // TODO: keep our key at hand
-       kr, err := openpgp.ReadKeyRing(bytes.NewBuffer(k))
-       if err != nil {
-               // internal error: pubkey cannot be parsed
-               return
-       }
-       // must clean up to make sure the hash is ok
-       err = document.Verify(signed, kr)
-       if err != nil {
-               return
-       }
-
-       certid = document.Id(signed)
-       return
-}
-
-func NewDebitCert(draftid string, draft *document.Draft) (*document.DebitCert, error) {
-       cert := new(document.DebitCert)
-       cert.Holder = draft.Drawer
-       cert.Date = time.Seconds()
-       cert.Denomination = "epoint"
-       cert.Issuer = draft.Issuer
-       cert.AuthorizedBy = draft.AuthorizedBy
-       cert.Difference = -draft.Amount
-       cert.Draft = draftid
-       cert.Beneficiary = draft.Beneficiary
-
-       oid, err := db.Get("certby/key", draft.Drawer)
-       oldcertid := string(oid)
-       if err != nil {
-               // first cert: drawer is issuer
-               if draft.Drawer != draft.Issuer {
-                       return nil, fmt.Errorf("drawer must be the issuer when drawing an empty account")
-               }
-               cert.Serial = 1
-               cert.Balance = cert.Difference
-               cert.LastDebitSerial = 0
-               cert.LastCreditSerial = 0
-       } else {
-               d, err := db.Get("cert", oldcertid)
-               if err != nil {
-                       return nil, err
-               }
-               iv, _, err := document.Parse(d)
-               if err != nil {
-                       // internal error
-                       return nil, err
-               }
-               // TODO: this is a hack
-               oldcert, err := document.ToCert(iv)
-               if err != nil {
-                       // internal error
-                       return nil, err
-               }
-               // TODO: sanity checks? oldcert.Holder == draft.Drawer
-               cert.Serial = oldcert.Serial + 1
-               cert.Balance = oldcert.Balance + cert.Difference
-               if cert.Balance <= -IntLimit {
-                       return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
-               }
-               if oldcert.Balance > 0 && cert.Balance < 0 {
-                       return nil, fmt.Errorf("insufficient funds: %d", oldcert.Balance)
-               }
-               cert.LastDebitSerial = oldcert.LastDebitSerial
-               cert.LastCreditSerial = oldcert.LastCreditSerial
-               if _,ok := iv.(*document.DebitCert); ok {
-                       cert.LastDebitSerial = oldcert.Serial
-               } else {
-                       cert.LastCreditSerial = oldcert.Serial
-               }
-               cert.LastCert = &oldcertid
-       }
-       return cert, nil
-}
-
-func NewCreditCert(draftid string, draft *document.Draft, dcertid string, dcert *document.DebitCert) (*document.CreditCert, error) {
-       cert := new(document.CreditCert)
-       // TODO: get from old cert instead?
-       cert.Holder = dcert.Beneficiary
-       cert.Date = time.Seconds()
-       // TODO: get these from the cert holder pubkey
-       cert.Denomination = "epoint"
-       cert.Issuer = draft.Issuer
-       cert.AuthorizedBy = dcert.AuthorizedBy // TODO: draft vs dcert vs serverside decision
-       cert.Difference = -dcert.Difference
-       cert.Draft = draftid
-       cert.Drawer = dcert.Holder
-       cert.DebitCert = dcertid
-
-       oid, err := db.Get("certby/key", dcert.Beneficiary)
-       oldcertid := string(oid)
-       if err != nil {
-               // this is the first cert
-               cert.Serial = 1
-               cert.Balance = cert.Difference
-               cert.LastDebitSerial = 0
-               cert.LastCreditSerial = 0
-       } else {
-               d, err := db.Get("cert", oldcertid)
-               if err != nil {
-                       // internal error
-                       return nil, err
-               }
-               iv, _, err := document.Parse(d)
-               if err != nil {
-                       // internal error
-                       return nil, err
-               }
-               // TODO: this is a hack
-               oldcert, err := document.ToCert(iv)
-               if err != nil {
-                       // internal error
-                       return nil, err
-               }
-               cert.Serial = oldcert.Serial + 1
-               cert.Balance = oldcert.Balance + cert.Difference
-               if cert.Balance >= IntLimit {
-                       return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
-               }
-               cert.LastDebitSerial = oldcert.LastDebitSerial
-               cert.LastCreditSerial = oldcert.LastCreditSerial
-               if _,ok := iv.(*document.DebitCert); ok {
-                       cert.LastDebitSerial = oldcert.Serial
-               } else {
-                       cert.LastCreditSerial = oldcert.Serial
-               }
-               cert.LastCert = &oldcertid
-       }
-       return cert, nil
-}
-
-func EvalDraft(d []byte, sk *openpgp.Entity) (r []byte, err error) {
-       draft, draftid, err := ParseDraft(d)
-       if err != nil {
-               return
-       }
-       _, err = db.Get("draft", draftid)
-       if err == nil {
-               // found
-               // TODO: certby/draft might not be ready even if draft is there
-               return CertByDraft(draftid)
-       }
-       // if draft is ok we save it
-       err = db.Set("draft", draftid, d)
-       if err != nil {
-               // internal error
-               return
-       }
-       // TODO: db.Insert: fails if key exists
-       s := fmt.Sprintf("%s.%s", draft.Drawer, draft.Nonce)
-       _, err = db.Get("draftby/key.nonce", s)
-       if err == nil {
-               err = fmt.Errorf("draft nonce is not unique")
-               return
-       }
-       err = db.Set("draftby/key.nonce", s, d)
-       if err != nil {
-               // internal error
-               return
-       }
-
-       // debit cert
-       cert, err := NewDebitCert(draftid, draft)
-       if err != nil {
-               return
-       }
-       r, signed, err := document.Format(cert, sk)
-       certid := document.Id(signed)
-       err = db.Set("cert", certid, r)
-       if err != nil {
-               // internal error
-               return
-       }
-       err = db.Set("certby/draft", draftid, []byte(certid))
-       if err != nil {
-               // internal error
-               return
-       }
-       err = db.Set("certby/key", cert.Holder, []byte(certid))
-       if err != nil {
-               // internal error
-               return
-       }
-       // TODO: append?
-       err = db.Set("certby/key.serial", fmt.Sprintf("%s.%09d", cert.Holder, cert.Serial), []byte(certid))
-       if err != nil {
-               // internal error
-               return
-       }
-       return
-}
-
-func EvalDebitCert(d []byte, sk *openpgp.Entity) (r []byte, err error) {
-       dcert, dcertid, err := ParseDebitCert(d)
-       if err != nil {
-               return
-       }
-       r, err = CertByDebitCert(dcertid)
-       if err == nil {
-               // found
-               return
-       }
-       // TODO: we only need the draft to know the issuer (+beneficiary)
-       // it should be in the pubkey
-       d, err = db.Get("draft", dcert.Draft)
-       if err != nil {
-               // internal error
-               return
-       }
-       iv, _, err := document.Parse(d)
-       if err != nil {
-               // internal error
-               return
-       }
-       draft, ok := iv.(*document.Draft)
-       if !ok {
-               // internal error
-               err = fmt.Errorf("EvalDebitCert: expected draft from internal db")
-               return
-       }
-
-       // credit side
-       // TODO: check pubkey etc
-       cert, err := NewCreditCert(dcert.Draft, draft, dcertid, dcert)
-       if err != nil {
-               // internal error
-               return
-       }
-       r, signed, err := document.Format(cert, sk)
-       if err != nil {
-               // internal error
-               return
-       }
-       certid := document.Id(signed)
-       err = db.Set("cert", certid, r)
-       if err != nil {
-               // internal error
-               return
-       }
-       err = db.Set("certby/debit", dcertid, []byte(certid))
-       if err != nil {
-               // internal error
-               return
-       }
-       err = db.Set("certby/key", cert.Holder, []byte(certid))
-       if err != nil {
-               // internal error
-               return
-       }
-       // TODO: append?
-       err = db.Set("certby/key.serial", fmt.Sprintf("%s.%09d", cert.Holder, cert.Serial), []byte(certid))
-       if err != nil {
-               // internal error
-               return
-       }
-       return
-}
-
-func Init(rootdir string) (err error) {
-       db, err = store.Open(rootdir)
-       if err != nil {
-               return
-       }
-       err = db.Ensure("key")
-       if err != nil {
-               return
-       }
-       err = db.Ensure("cert")
-       if err != nil {
-               return
-       }
-       err = db.Ensure("draft")
-       if err != nil {
-               return
-       }
-       err = db.Ensure("certby/draft")
-       if err != nil {
-               return
-       }
-       err = db.Ensure("certby/debit")
-       if err != nil {
-               return
-       }
-       err = db.Ensure("certby/key")
-       if err != nil {
-               return
-       }
-       err = db.Ensure("certby/key.serial")
-       if err != nil {
-               return
-       }
-       err = db.Ensure("draftby/key.nonce")
-       if err != nil {
-               return
-       }
-       err = db.Ensure("keysby/64")
-       if err != nil {
-               return
-       }
-       err = db.Ensure("keysby/32")
-       if err != nil {
-               return
-       }
-       return
-}