Example #1
0
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
}
Example #2
0
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
}
Example #3
0
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
}
Example #4
0
// 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)
	}
}
Example #5
0
// 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)
	}
}
Example #6
0
// 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)
	}
}