func storeDepositInfo(c appengine.Context, r *http.Request, participant string) (err error) { // Important: checking (and invalidating) the nonce must be the first thing we do! err = checkNonce(c, r.FormValue("nonce")) if CfgRequireValidNonce && err != nil { return fmt.Errorf("Error in checkNonce: %v", err) } m := bitwrk.DepositAddressMessage{} m.FromValues(r.Form) if m.Participant != participant { return fmt.Errorf("Participant must be %#v", participant) } if m.Signer != CfgTrustedAccount { return fmt.Errorf("Signer must be %#v", CfgTrustedAccount) } // Bitcoin addresses must have the right network id if err := checkBitcoinAddress(m.DepositAddress); err != nil { return err } // Verify that the message was indeed signed by the trusted account if CfgRequireValidSignature { if err := m.VerifyWith(CfgTrustedAccount); err != nil { return err } } f := func(c appengine.Context) error { dao := db.NewGaeAccountingDao(c, true) if account, err := dao.GetAccount(participant); err != nil { return err } else { if account.DepositInfo != "" { c.Infof("Replacing old deposit info: %v", account.DepositInfo) } v := url.Values{} m.ToValues(v) account.DepositInfo = v.Encode() account.LastDepositInfo = time.Now() account.DepositAddressRequest = "" c.Infof("New deposit info: %v", account.DepositInfo) if err := dao.SaveAccount(&account); err != nil { return err } } return dao.Flush() } if err := datastore.RunInTransaction(c, f, &datastore.TransactionOptions{XG: true}); err != nil { // Transaction failed c.Errorf("Transaction failed: %v", err) return err } return }
func requestDepositAddress(c appengine.Context, r *http.Request, participant string) (err error) { // Important: checking (and invalidating) the nonce must be the first thing we do! err = checkNonce(c, r.FormValue("nonce")) if CfgRequireValidNonce && err != nil { return fmt.Errorf("Error in checkNonce: %v", err) } m := bitwrk.DepositAddressRequest{} m.FromValues(r.Form) if m.Participant != participant { return fmt.Errorf("Participant must be %#v", participant) } if m.Signer != CfgTrustedAccount && m.Signer != participant { return fmt.Errorf("Signer must be participant or %#v", CfgTrustedAccount) } // Verify that the request was indeed signed correctly if CfgRequireValidSignature { if err := m.VerifyWith(m.Signer); err != nil { return fmt.Errorf("After verifying %#v against %v: %v", m, m.Signer, err) } } f := func(c appengine.Context) error { dao := db.NewGaeAccountingDao(c, true) if account, err := dao.GetAccount(participant); err != nil { return err } else if account.DepositAddressRequest != "" { return fmt.Errorf("There is a pending address request already.") } else if account.LastDepositInfo.Add(24 * time.Hour).After(time.Now()) { return fmt.Errorf("Next deposit address can be requested %v", account.LastDepositInfo.Add(24*time.Hour)) } else { v := url.Values{} m.ToValues(v) account.DepositAddressRequest = v.Encode() c.Infof("New deposit address request: %v", account.DepositAddressRequest) if err := dao.SaveAccount(&account); err != nil { return err } } return dao.Flush() } if err := datastore.RunInTransaction(c, f, &datastore.TransactionOptions{XG: true}); err != nil { // Transaction failed c.Errorf("Transaction failed: %v", err) return err } return }
func createDeposit(c appengine.Context, depositType, depositAccount, depositAmount, depositNonce, depositUid, depositRef, depositSig string) (err error) { // Important: checking (and invalidating) the nonce must be the first thing we do! err = checkNonce(c, depositNonce) if CfgRequireValidNonce && err != nil { return fmt.Errorf("Error in checkNonce: %v", err) } // Bitcoin addresses must have the right network id err = checkBitcoinAddress(depositAccount) if err != nil { return } deposit, err := bitwrk.ParseDeposit(depositType, depositAccount, depositAmount, depositNonce, depositUid, depositRef, depositSig) if err != nil { return fmt.Errorf("Error in ParseDeposit: %v", err) } if CfgRequireValidSignature { err = deposit.Verify(CfgTrustedAccount) if err != nil { return } } f := func(c appengine.Context) error { dao := db.NewGaeAccountingDao(c, true) if err := deposit.Place(depositUid, dao); err != nil { return err } return dao.Flush() } if err := datastore.RunInTransaction(c, f, &datastore.TransactionOptions{XG: true}); err != nil { // Transaction failed return err } _ = deposit c.Infof("Deposit: %#v", deposit) return }
// Handler function for /ledger/<accountMovementKey> func handleAccountMovement(w http.ResponseWriter, r *http.Request) { movementKey := r.URL.Path[8:] if r.Method == "GET" { acceptable := []string{"text/html", "application/json"} contentType := goautoneg.Negotiate(r.Header.Get("Accept"), acceptable) if contentType == "" { http.Error(w, fmt.Sprintf("No accepted content type found. Supported: %v", acceptable), http.StatusNotAcceptable) return } c := appengine.NewContext(r) dao := db.NewGaeAccountingDao(c, false) var err error movement, err := dao.GetMovement(movementKey) if err == bitwrk.ErrNoSuchObject { http.Error(w, "No such ledger entry", http.StatusNotFound) return } else if err != nil { http.Error(w, "Error retrieving ledger entry", http.StatusInternalServerError) c.Errorf("Error getting account movement %v: %v", movementKey, err) return } w.Header().Set("Content-Type", contentType) if contentType == "application/json" { err = renderAccountMovementJson(w, &movement) } else { err = renderAccountMovementHtml(w, &movement) } if err != nil { http.Error(w, "Error rendering ledger entry", http.StatusInternalServerError) c.Errorf("Error rendering account movement %v as %v: %v", r.URL, contentType, err) } } else { w.WriteHeader(http.StatusMethodNotAllowed) } }
// Handler function for /deposit/<uid> func handleRenderDeposit(w http.ResponseWriter, r *http.Request) { uid := r.URL.Path[9:] if r.Method != "GET" { http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed) return } acceptable := []string{"text/html", "application/json"} contentType := goautoneg.Negotiate(r.Header.Get("Accept"), acceptable) if contentType == "" { http.Error(w, fmt.Sprintf("No accepted content type found. Supported: %v", acceptable), http.StatusNotAcceptable) return } c := appengine.NewContext(r) dao := db.NewGaeAccountingDao(c, false) deposit, err := dao.GetDeposit(uid) if err != nil { http.Error(w, "Deposit not found: "+uid, http.StatusNotFound) c.Warningf("Non-existing deposit queried: '%v'", uid) return } w.Header().Set("Content-Type", contentType) if contentType == "application/json" { err = renderDepositJson(w, deposit) } else { err = renderDepositHtml(w, uid, deposit) } if err != nil { c.Errorf("Error rendering %v as %v: %v", r.URL, contentType, err) } }
// Handler function for /account/<accountId> func handleAccount(w http.ResponseWriter, r *http.Request) { accountId := r.URL.Path[9:] if r.Method == "GET" { acceptable := []string{"text/html", "application/json"} contentType := goautoneg.Negotiate(r.Header.Get("Accept"), acceptable) if contentType == "" { http.Error(w, fmt.Sprintf("No accepted content type found. Supported: %v", acceptable), http.StatusNotAcceptable) return } if err := checkBitcoinAddress(accountId); err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } c := appengine.NewContext(r) dao := db.NewGaeAccountingDao(c, false) var err error account, err := dao.GetAccount(accountId) if err != nil { http.Error(w, "Error retrieving account", http.StatusInternalServerError) c.Errorf("Error getting account %v: %v", accountId, err) return } w.Header().Set("Content-Type", contentType) if contentType == "application/json" { err = renderAccountJson(w, &account) } else { devmode := r.FormValue("developermode") != "" err = renderAccountHtml(w, &account, devmode) } if err != nil { http.Error(w, "Error rendering account", http.StatusInternalServerError) c.Errorf("Error rendering %v as %v: %v", r.URL, contentType, err) } } else if r.Method == "POST" { c := appengine.NewContext(r) c.Infof("Got POST for account: %v", accountId) action := r.FormValue("action") if action == "storedepositinfo" { if err := storeDepositInfo(c, r, accountId); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } else { http.Redirect(w, r, r.RequestURI, http.StatusSeeOther) } } else if action == "requestdepositaddress" { if err := requestDepositAddress(c, r, accountId); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } else { http.Redirect(w, r, r.RequestURI, http.StatusSeeOther) } } else { http.Error(w, "invalid action: "+action, http.StatusInternalServerError) } } else { http.Error(w, "Method not allowed: "+r.Method, http.StatusMethodNotAllowed) } }