import (
"bytes"
"crypto/openpgp"
- "crypto/rand"
"epoint/document"
"epoint/key"
"epoint/store"
"fmt"
+ "log"
"time"
)
var db *store.Conn
var serverkey *openpgp.Entity
var newchan chan *work
-var delchan chan string
+var delchan chan *work
type worklist struct {
head *work
func setserverkey(e *openpgp.Entity) (err error) {
serverkey = e
-
- // TODO: maybe Serialize should do this internally
- for _, ident := range e.Identities {
- err = ident.SelfSignature.SignUserId(rand.Reader, ident.UserId.Id, e.PrimaryKey, e.PrivateKey)
- if err != nil {
- return
- }
- }
- for _, subkey := range e.Subkeys {
- err = subkey.Sig.SignKey(rand.Reader, subkey.PublicKey, e.PrivateKey)
- if err != nil {
- return
- }
+ err = key.SelfSign(e)
+ if err != nil {
+ return
}
-
- b := new(bytes.Buffer)
- err = e.Serialize(b)
+ d, err := key.Format(e)
if err != nil {
return
}
- err = db.Set("key", key.Id(e), b.Bytes())
+ err = db.Set("key", key.Id(e), d)
if err != nil {
return
}
- err = db.Set("", "serverkey", b.Bytes())
+ err = db.Set("", "serverkey", d)
return
}
w.signed = signed
w.draft = draft
w.sync = make(chan int)
+ log.Printf("add draft work: %s", w.account)
newchan <- w
<-w.sync
return w.out, w.err
func addDebit(d []byte, signed *document.Signed, cert *document.DebitCert) (c []byte, err error) {
w := new(work)
w.docid = document.Id(signed)
- w.account = fmt.Sprintf("%s.%s", cert.Holder, cert.Issuer)
+ w.account = fmt.Sprintf("%s.%s", cert.Beneficiary, cert.Issuer)
w.in = d
w.signed = signed
w.debit = cert
w.sync = make(chan int)
+ log.Printf("add debit work: %s", w.account)
newchan <- w
<-w.sync
return w.out, w.err
}
func dispatch() {
+ log.Printf("start dispatch")
works := make(map[string]*worklist)
for {
select {
case w := <-newchan:
+ log.Printf("queue work: %s", w.account)
ws := works[w.account]
if ws == nil {
// TODO: unnecessary alloc
} else {
pushwork(ws, w)
}
- case account := <-delchan:
- ws := works[account]
- w := popwork(ws)
- if w == nil {
- delete(works, account)
+ case w := <-delchan:
+ log.Printf("unqueue work: %s", w.account)
+ ws := works[w.account]
+ wnext := popwork(ws)
+ if wnext == nil {
+ delete(works, w.account)
} else {
- go handle(w)
+ go handle(wnext)
}
}
}
}
func handle(w *work) {
+ log.Printf("start work: %s", w.account)
if w.debit != nil {
- handleDebit(w)
+ w.out, w.err = handleDebit(w)
} else if w.draft != nil {
- handleDraft(w)
+ w.out, w.err = handleDraft(w)
} else {
panic("unreachable")
}
- delchan <- w.account
+ log.Printf("finish work: %s outlen: %d", w.account, len(w.out))
+ delchan <- w
w.sync <- 0
}
-func handleDraft(w *work) {
+func handleDraft(w *work) (c []byte, err error) {
nonce := fmt.Sprintf("%s.%s", w.account, w.draft.Nonce)
oldid, err := db.Get("draftby/key.issuer.nonce", nonce)
if err == nil {
if string(oldid) != w.docid {
- w.err = fmt.Errorf("draft nonce is not unique (see draft %s)", oldid)
+ err = fmt.Errorf("draft nonce is not unique (see draft %s)", oldid)
} else {
- w.out, w.err = GetCert("certby/draft", w.docid)
+ c, err = GetCert("certby/draft", w.docid)
}
return
} else if _, ok := err.(store.NotFoundError); !ok {
- w.err = err
return
}
err = db.Begin(w.docid)
if err != nil {
- w.err = err
return
}
err = db.Set("draft", w.docid, w.in)
if err != nil {
- w.err = err
return
}
err = db.Set("draftby/key.issuer.nonce", nonce, []byte(w.docid))
if err != nil {
- w.err = err
return
}
cert, err := newDebitCert(w)
return
}
certid := document.Id(signed)
- w.out = c
err = db.Set("cert", certid, c)
if err != nil {
// internal error
return
}
-func handleDebit(w *work) {
- c, err := GetCert("certby/debit", w.docid)
+func handleDebit(w *work) (c []byte, err error) {
+ c, err = GetCert("certby/debit", w.docid)
if err == nil {
- w.out = c
return
} else if _, ok := err.(store.NotFoundError); !ok {
// internal error
- w.err = err
return
}
err = db.Begin(w.docid)
if err != nil {
- w.err = err
return
}
err = db.Set("cert", w.docid, w.in)
if err != nil {
- w.err = err
return
}
// TODO: check pubkey etc
return
}
+func isIssuer(c *document.Cert) bool {
+ return c.Issuer == c.Holder
+}
+
func newDebitCert(w *work) (*document.DebitCert, error) {
cert := new(document.DebitCert)
cert.Holder = w.draft.Drawer
// internal error
return nil, err
}
+ // TODO: make sure oldcert and newcert cannot become inconsistent
// TODO: this is a hack
oldcert, err := document.ToCert(iv)
if err != nil {
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)
+ if !isIssuer(&cert.Cert) && cert.Balance < 0 {
+ return nil, fmt.Errorf("insufficient funds: %d, draft: %d", oldcert.Balance, cert.Difference)
}
cert.LastDebitSerial = oldcert.LastDebitSerial
cert.LastCreditSerial = oldcert.LastCreditSerial
cert.Drawer = w.debit.Holder
cert.DebitCert = w.docid
- oid, err := db.Get("certby/key", w.debit.Beneficiary)
+ oid, err := db.Get("certby/key.issuer", w.account)
oldcertid := string(oid)
if err != nil {
+ if _, ok := err.(store.NotFoundError); !ok {
+ // internal error
+ return nil, err
+ }
// this is the first cert
cert.Serial = 1
cert.Balance = cert.Difference
if cert.Balance >= IntLimit {
return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
}
+ // sanity check
+ if isIssuer(&cert.Cert) && cert.Balance > 0 {
+ return nil, fmt.Errorf("internal error")
+ }
cert.LastDebitSerial = oldcert.LastDebitSerial
cert.LastCreditSerial = oldcert.LastCreditSerial
if _, ok := iv.(*document.DebitCert); ok {
if err != nil {
return
}
+ newchan = make(chan *work)
+ delchan = make(chan *work)
go dispatch()
return
}