// CreateOrder create order through exchange server. // mode: POST // url: /api/v1/account/order?coin_pair=[:coin_pair]&type=[:type]&price=[:price]&amt=[:amt] // params: // coin_pair: order coin pair. // type: order type, can be bid or ask. // price: price. // amt: amount. func CreateOrder(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { rlt := &pp.EmptyRes{} for { req, err := makeOrderReq(r) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } a, err := account.GetActive() if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } req.Pubkey = pp.PtrString(a.Pubkey) var res pp.OrderRes if err := sknet.EncryGet(se.GetServAddr(), "/create/order", req, &res); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } sendJSON(w, res) return } sendJSON(w, rlt) } }
// GetUtxos get unspent output of specific address. func GetUtxos(egn engine.Exchange) sknet.HandlerFunc { return func(c *sknet.Context) error { var req pp.GetUtxoReq var rlt *pp.EmptyRes for { if err := c.BindJSON(&req); err != nil { rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) logger.Error(err.Error()) break } coin, err := egn.GetCoin(req.GetCoinType()) if err != nil { rlt = pp.MakeErrRes(err) logger.Error(err.Error()) break } res, err := coin.GetUtxos(req.GetAddresses()) if err != nil { rlt = pp.MakeErrRes(err) logger.Error(err.Error()) break } return c.SendJSON(&res) } return c.Error(rlt) } }
// GetAddrBalance get balance of specific address. func GetAddrBalance(ee engine.Exchange) sknet.HandlerFunc { return func(c *sknet.Context) error { var rlt *pp.EmptyRes for { req := pp.GetAddrBalanceReq{} if err := c.BindJSON(&req); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) break } coin, err := ee.GetCoin(req.GetCoinType()) if err != nil { rlt = pp.MakeErrRes(err) logger.Error(err.Error()) break } addrs := strings.Split(req.GetAddrs(), ",") b, err := coin.GetBalance(addrs) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } res := pp.GetAddrBalanceRes{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), Balance: &b, } return c.SendJSON(&res) } return c.Error(rlt) } }
// GetRawTx get raw tx by txid. // mode: GET // url: /api/v1/rawtx?coin_type=[:coin_type]&txid=[:txid] func GetRawTx(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes for { // get coin type cp := r.FormValue("coin_type") if cp == "" { logger.Error("no coin type") rlt = pp.MakeErrRes(errors.New("no coin type")) break } // get txid txid := r.FormValue("txid") if txid == "" { logger.Error("no txid") rlt = pp.MakeErrRes(errors.New("no txid")) break } req := pp.GetRawTxReq{ CoinType: pp.PtrString(cp), Txid: pp.PtrString(txid), } var res pp.GetRawTxRes if err := sknet.EncryGet(se.GetServAddr(), "/get/rawtx", req, &res); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } sendJSON(w, res) return } sendJSON(w, rlt) } }
func btcWithdraw(rp *ReqParams) (*pp.WithdrawalRes, *pp.EmptyRes) { ee := rp.Values["engine"].(engine.Exchange) acnt := rp.Values["account"].(account.Accounter) amt := rp.Values["amt"].(uint64) ct := rp.Values["cointype"].(string) toAddr := rp.Values["toAddr"].(string) // verify the toAddr if _, err := cipher.BitcoinDecodeBase58Address(toAddr); err != nil { return nil, pp.MakeErrRes(errors.New("invalid bitcoin address")) } var success bool var btcTxRlt *BtcTxResult var err error // decrease balance and check if the balance is sufficient. if err := acnt.DecreaseBalance(ct, amt+ee.GetBtcFee()); err != nil { return nil, pp.MakeErrRes(err) } defer func() { if !success { go func() { if btcTxRlt != nil { ee.PutUtxos(bitcoin.Type, btcTxRlt.UsingUtxos) } acnt.IncreaseBalance(ct, amt+ee.GetBtcFee()) }() } else { //TODO: handle the saving failure. ee.SaveAccount() } }() btcTxRlt, err = createBtcWithdrawTx(ee, amt, toAddr) if err != nil { return nil, pp.MakeErrRes(errors.New("failed to create withdrawal tx")) } rawtx, err := btcTxRlt.Tx.Serialize() if err != nil { return nil, pp.MakeErrRes(errors.New("tx serialize failed")) } newTxid, err := bitcoin.BroadcastTx(hex.EncodeToString(rawtx)) if err != nil { logger.Error(err.Error()) return nil, pp.MakeErrResWithCode(pp.ErrCode_BroadcastTxFail) } success = true if btcTxRlt.ChangeAddr != "" { logger.Debug("change address:%s", btcTxRlt.ChangeAddr) ee.WatchAddress(ct, btcTxRlt.ChangeAddr) } resp := pp.WithdrawalRes{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), NewTxid: &newTxid, } return &resp, nil }
// Withdraw transaction. func Withdraw(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { rlt := &pp.EmptyRes{} for { a, err := account.GetActive() if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } cp := r.FormValue("coin_type") if cp == "" { err := errors.New("coin_type empty") logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } amount := r.FormValue("amount") if amount == "" { rlt = pp.MakeErrRes(errors.New("amount empty")) break } toAddr := r.FormValue("toaddr") if toAddr == "" { rlt = pp.MakeErrRes(errors.New("toaddr empty")) break } amt, err := strconv.ParseUint(amount, 10, 64) if err != nil { rlt = pp.MakeErrRes(err) break } req := pp.WithdrawalReq{ Pubkey: &a.Pubkey, CoinType: &cp, Coins: &amt, OutputAddress: &toAddr, } var res pp.WithdrawalRes if err := sknet.EncryGet(se.GetServAddr(), "/withdrawl", req, &res); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } sendJSON(w, res) return } sendJSON(w, rlt) } }
// GetWalletBalance get local wallet balance. // mode: GET // url: /api/v1/wallet/balance?id=[:id] // params: // id: wallet id. func GetWalletBalance(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes for { id := r.FormValue("id") if id == "" { err := errors.New("id is empty") logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } // get addresses in wallet. addrs, err := wallet.GetAddresses(id) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } if len(addrs) == 0 { res := pp.GetAddrBalanceRes{ Result: pp.MakeResult(pp.ErrCode_NotExits, "wallet have no address"), } sendJSON(w, &res) return } cp := strings.Split(id, "_")[0] // get address balance. req := pp.GetAddrBalanceReq{ CoinType: pp.PtrString(cp), Addrs: pp.PtrString(strings.Join(addrs, ",")), } var res pp.GetAddrBalanceRes if err := sknet.EncryGet(se.GetServAddr(), "/get/address/balance", req, &res); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } sendJSON(w, res) return } sendJSON(w, rlt) } }
// GetCoins get supported coins from exchange server. func GetCoins(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { rlt := &pp.EmptyRes{} for { a, err := account.GetActive() if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } req := pp.GetCoinsReq{ Pubkey: pp.PtrString(a.Pubkey), } var res pp.CoinsRes if err := sknet.EncryGet(se.GetServAddr(), "/get/coins", req, &res); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } sendJSON(w, res) return } sendJSON(w, rlt) } }
// NewAddress create address in wallet. // mode: POST // url: /api/v1/wallet/:id/address?&id=[:id] // params: // id: wallet id. func NewAddress(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes for { // get wallet id wltID := r.FormValue("id") if wltID == "" { rlt = pp.MakeErrRes(errors.New("id is required")) break } addrEntries, err := wallet.NewAddresses(wltID, 1) if err != nil { rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } res := struct { Result *pp.Result Address string `json:"address"` }{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), Address: addrEntries[0].Address, } sendJSON(w, &res) return } sendJSON(w, rlt) } }
// GetRawTx return rawtx of specifc tx. func GetRawTx(egn engine.Exchange) sknet.HandlerFunc { return func(c *sknet.Context) error { var rlt *pp.EmptyRes for { req := pp.GetRawTxReq{} if err := c.BindJSON(&req); err != nil { rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) break } coin, err := egn.GetCoin(req.GetCoinType()) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } rawtx, err := coin.GetRawTx(req.GetTxid()) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) break } res := pp.GetRawTxRes{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), CoinType: req.CoinType, Rawtx: pp.PtrString(rawtx), } return c.SendJSON(&res) } return c.Error(rlt) } }
// GetAddresses get all addresses in wallet. // mode: GET // url: /api/v1/wallet/addresses?id=[:id] // params: // id: wallet id. func GetAddresses(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes for { id := r.FormValue("id") if id == "" { rlt = pp.MakeErrRes(errors.New("id is required")) break } addrs, err := wallet.GetAddresses(id) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) break } res := struct { Result *pp.Result `json:"result"` Addresses []string `json:"addresses"` }{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), Addresses: addrs, } sendJSON(w, &res) return } sendJSON(w, rlt) } }
// CreateWallet api for creating local wallet. // mode: POST // url: /api/v1/wallet?type=[:type]&seed=[:seed] // params: // type: bitcoin or skycoin // seed: wallet seed. func CreateWallet(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { rlt := &pp.EmptyRes{} for { // get coin type cp := r.FormValue("type") // get seed sd := r.FormValue("seed") if sd == "" { rlt = pp.MakeErrRes(errors.New("seed is required")) break } wlt, err := wallet.New(cp, sd) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } // bind the wallet to current account. a, err := account.GetActive() if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } a.WltIDs[cp] = wlt.GetID() // update the account. account.Set(a) res := struct { Result *pp.Result `json:"result"` ID string `json:"id"` }{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), ID: wlt.GetID(), } sendJSON(w, &res) return } sendJSON(w, rlt) } }
// Authorize will decrypt the request, and it's a buildin middleware for skynet. func Authorize(servSeckey string) HandlerFunc { return func(c *Context) error { var ( req pp.EncryptReq rlt *pp.EmptyRes ) c.ServSeckey = servSeckey for { if c.UnmarshalReq(&req) == nil { // validate pubkey. if err := validatePubkey(req.GetPubkey()); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongPubkey) break } pubkey, err := cipher.PubKeyFromHex(req.GetPubkey()) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongPubkey) break } c.Pubkey = pubkey.Hex() seckey, err := cipher.SecKeyFromHex(servSeckey) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } key := cipher.ECDH(pubkey, seckey) data, err := cipher.Chacha20Decrypt(req.GetEncryptdata(), key, req.GetNonce()) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_UnAuthorized) break } ok, err := regexp.MatchString(`^\{.*\}$`, string(data)) if err != nil || !ok { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_UnAuthorized) break } c.Raw = data return c.Next() } rlt = pp.MakeErrRes(errors.New("bad request")) break } return c.Error(rlt) } }
// SignRawTx sign transaction. // mode: POST // url: /api/v1/signr_awtx?coin_type=[:coin_type]&rawtx=[:rawtx] // params: // coin_type: skycoin or bitcoin. // rawtx: raw transaction. func SignRawTx(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes for { // check coin type cp := r.FormValue("coin_type") // get raw tx rawtx := r.FormValue("rawtx") if rawtx == "" { err := errors.New("rawtx is empty") logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } coin, err := se.GetCoin(cp) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } tx, err := coin.SignRawTx(rawtx, getPrivKey(cp)) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } res := struct { Result *pp.Result `json:"result"` Rawtx string `json:"rawtx"` }{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), Rawtx: tx, } sendJSON(w, &res) return } sendJSON(w, rlt) } }
// GetTx get transaction by id. func GetTx(egn engine.Exchange) sknet.HandlerFunc { return func(c *sknet.Context) error { var rlt *pp.EmptyRes for { req := pp.GetTxReq{} if err := c.BindJSON(&req); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) break } coin, err := egn.GetCoin(req.GetCoinType()) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } // brief validate transaction id if !coin.ValidateTxid(req.GetTxid()) { rlt = pp.MakeErrRes(errors.New("invalid transaction id")) break } tx, err := coin.GetTx(req.GetTxid()) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } res := pp.GetTxRes{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), CoinType: req.CoinType, Tx: tx, } return c.SendJSON(&res) } return c.Error(rlt) } }
// process handle the incoming connection, will read request from conn, setup the middle, // and dispatch the request. func process(id int, c net.Conn, engine *Engine) { logger.Debug("[%d] working", id) r := &Request{} w := &Response{c: c} defer func() { // catch panic if r := recover(); r != nil { logger.Critical("%s", r) debug.PrintStack() } c.Close() logger.Debug("[%d] worker done", id) }() var err error var context Context for { r.Reset() context.Reset() context.Resp = w if err = Read(c, r); err != nil { if err.Error() != "EOF" { logger.Error("%v", err) context.Error(pp.MakeErrRes(err)) } return } context.Request = r // check if the path belongs to group. hds, find := engine.findGroupHandlers(r.GetPath()) if find { context.handlers = append(engine.handlers, hds...) } else { if h, ok := engine.handlerFunc[r.GetPath()]; ok { context.handlers = append(engine.handlers, h) } else { logger.Error("no handler for path: %s", r.GetPath()) res := pp.MakeErrResWithCode(pp.ErrCode_ServerError) context.Error(res) return } } if err := context.handlers[0](&context); err != nil { logger.Error(err.Error()) return } } }
// GetUtxos get utxos through exchange server. func GetUtxos(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes for { cp := r.FormValue("coin_type") if cp == "" { logger.Error("coin type empty") rlt = pp.MakeErrRes(errors.New("coin type empty")) break } addrs := r.FormValue("addrs") if addrs == "" { logger.Error("addrs empty") rlt = pp.MakeErrRes(errors.New("addrs empty")) break } addrArray := strings.Split(addrs, ",") for i, addr := range addrArray { addrArray[i] = strings.Trim(addr, " ") } req := pp.GetUtxoReq{ CoinType: pp.PtrString(cp), Addresses: addrArray, } var res pp.GetUtxoRes if err := sknet.EncryGet(se.GetServAddr(), "/get/utxos", req, &res); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } sendJSON(w, res) return } sendJSON(w, rlt) } }
// GetKeys get keys of specific address in wallet. // mode: GET // url: /api/v1/wallet/address/keys?id=[:id]&address=[:address] func GetKeys(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes for { // get wallet id wltID := r.FormValue("id") if wltID == "" { rlt = pp.MakeErrRes(errors.New("no id")) break } // get address addr := r.FormValue("address") if addr == "" { rlt = pp.MakeErrRes(errors.New("no address")) break } p, s, err := wallet.GetKeypair(wltID, addr) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } res := struct { Result *pp.Result Pubkey string `json:"pubkey"` Seckey string `json:"seckey"` }{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), Pubkey: p, Seckey: s, } sendJSON(w, &res) return } sendJSON(w, rlt) } }
// InjectTx broadcast transaction. // mode: POST // url: /api/v1/inject_rawtx?rawtx=[:rawtx]&coin_type=[:coin_type] // params: // rawtx: raw tx that's going to be injected. // coin_type: skycoin or bitcoin. func InjectTx(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes for { // get tx rawtx := r.FormValue("rawtx") if rawtx == "" { err := errors.New("rawtx is empty") logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } // get coin type cp := r.FormValue("coin_type") if cp == "" { logger.Error("empty coin type") rlt = pp.MakeErrRes(errors.New("empty coin type")) break } req := pp.InjectTxnReq{ CoinType: pp.PtrString(cp), Tx: pp.PtrString(rawtx), } var res pp.InjectTxnRes if err := sknet.EncryGet(se.GetServAddr(), "/inject/tx", req, &res); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } sendJSON(w, res) return } sendJSON(w, rlt) } }
// GetDepositAddress get deposit address from exchange server. func GetDepositAddress(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { rlt := &pp.EmptyRes{} for { a, err := account.GetActive() if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } cp := r.FormValue("coin_type") if cp == "" { err := errors.New("coin type empty") logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } req := pp.GetDepositAddrReq{ Pubkey: pp.PtrString(a.Pubkey), CoinType: pp.PtrString(cp), } var res pp.GetDepositAddrRes if err := sknet.EncryGet(se.GetServAddr(), "/create/deposit_address", req, &res); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } sendJSON(w, res) return } sendJSON(w, rlt) } }
// ActiveAccount active the specific account. // mode: PUT // url: /api/v1/account/state?pubkey=[:pubkey] func ActiveAccount(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes for { // get pubkey pk := r.FormValue("pubkey") if pk == "" { logger.Error("pubkey is empty") rlt = pp.MakeErrRes(errors.New("pubkey is empty")) break } // validate the pubkey if _, err := cipher.PubKeyFromHex(pk); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(errors.New("invalid pubkey")) break } // active the account if err := account.SetActive(pk); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } res := struct { Result *pp.Result }{ pp.MakeResultWithCode(pp.ErrCode_Success), } sendJSON(w, &res) return } sendJSON(w, rlt) } }
// InjectTx inject transaction. func InjectTx(egn engine.Exchange) sknet.HandlerFunc { return func(c *sknet.Context) error { var rlt *pp.EmptyRes for { req := pp.InjectTxnReq{} if err := c.BindJSON(&req); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) break } // get coin gateway coin, err := egn.GetCoin(req.GetCoinType()) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } // inject tx. txid, err := coin.InjectTx(req.GetTx()) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } res := pp.InjectTxnRes{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), Txid: pp.PtrString(txid), } return c.SendJSON(&res) } return c.Error(rlt) } }
// UpdateCredit update credit. func UpdateCredit(ee engine.Exchange) sknet.HandlerFunc { return func(c *sknet.Context) error { var rlt *pp.EmptyRes for { req := pp.UpdateCreditReq{} if err := c.BindJSON(&req); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) break } // validate the dst pubkey. dstPubkey := req.GetDst() if err := validatePubkey(dstPubkey); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongPubkey) break } // get account. a, err := ee.GetAccount(dstPubkey) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongPubkey) break } // get coin type. if err := a.SetBalance(req.GetCoinType(), req.GetAmount()); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } ee.SaveAccount() res := pp.UpdateCreditRes{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), } return c.SendJSON(&res) } return c.Error(rlt) } }
// AdminUpdateBalance update balance of specific account // mode: PUT // url: /api/v1/admin/account/balance?dst=[:dst]&coin_type=[:coin_type]&amt=[:amt] // params: // dst: the dst account pubkey, whose balance will be updated. // coin_type: skycoin or bitcoin, the coin you want to credit. // amt: balance that will be updated. func AdminUpdateBalance(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes for { // get dst_pubkey dstPk := r.FormValue("dst") if dstPk == "" { err := errors.New("dst pubkey is empty") logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } // validate the dst_pubkey if _, err := cipher.PubKeyFromHex(dstPk); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(errors.New("invalid dst pubkey")) break } // get coin type. cp := r.FormValue("coin_type") if cp == "" { err := errors.New("coin_type is empty") logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } // get amt amt := r.FormValue("amt") if amt == "" { err := errors.New("amt is empty") logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } amount, err := strconv.ParseUint(amt, 10, 64) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } a, err := account.GetActive() if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } req := pp.UpdateCreditReq{ Pubkey: pp.PtrString(a.Pubkey), CoinType: pp.PtrString(cp), Amount: pp.PtrUint64(amount), Dst: pp.PtrString(dstPk), } res := pp.UpdateCreditRes{} if err := sknet.EncryGet(se.GetServAddr(), "/admin/update/credit", req, &res); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } sendJSON(w, res) return } sendJSON(w, rlt) } }
// Withdraw api for handlering withdraw process. func Withdraw(ee engine.Exchange) sknet.HandlerFunc { return func(c *sknet.Context) error { rlt := &pp.EmptyRes{} for { reqParam, err := getWithdrawReqParams(c, ee) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } cp := reqParam.Values["cointype"].(string) a := reqParam.Values["account"].(account.Accounter) amt := reqParam.Values["amt"].(uint64) outAddr := reqParam.Values["outAddr"].(string) // get handler for creating txIns and txOuts base on the coin type. createTxInOut, err := getTxInOutHandler(cp) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } // create txIns and txOuts. inOutSet, err := createTxInOut(ee, a, amt, outAddr) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } var success bool defer func() { if !success { // if not success, invoke the teardown, for putting back utxos, and reset balance. inOutSet.Teardown() } }() // get coin gateway. coin, err := ee.GetCoin(cp) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } // create raw tx rawtx, err := coin.CreateRawTx(inOutSet.TxIns, inOutSet.TxOuts) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } // sign the tx rawtx, err = coin.SignRawTx(rawtx, getAddrPrivKey(ee, cp)) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } // inject the transaction. txid, err := coin.InjectTx(rawtx) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } success = true resp := pp.WithdrawalRes{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), NewTxid: &txid, } return c.SendJSON(&resp) } return c.Error(rlt) } }
func skyWithdrawl(nodeAddr string, rp *ReqParams) (*pp.WithdrawalRes, *pp.EmptyRes) { ee := rp.Values["engine"].(engine.Exchange) acnt := rp.Values["account"].(account.Accounter) amt := rp.Values["amt"].(uint64) ct := rp.Values["cointype"].(string) toAddr := rp.Values["toAddr"].(string) if err := skycoin.VerifyAmount(amt); err != nil { return nil, pp.MakeErrRes(err) } // verify the toAddr if _, err := cipher.DecodeBase58Address(toAddr); err != nil { return nil, pp.MakeErrRes(errors.New("invalid skycoin address")) } var success bool var skyTxRlt *SkyTxResult var err error if err := acnt.DecreaseBalance(ct, amt); err != nil { return nil, pp.MakeErrRes(err) } defer func() { if !success { go func() { if skyTxRlt != nil { ee.PutUtxos(skycoin.Type, skyTxRlt.UsingUtxos) } acnt.IncreaseBalance(ct, amt) }() } else { //TODO: handle the saving failure. ee.SaveAccount() } }() skyTxRlt, err = createSkyWithdrawTx(ee, amt, toAddr) if err != nil { return nil, pp.MakeErrRes(errors.New("failed to create withdrawal tx")) } rawtx, err := skyTxRlt.Tx.Serialize() if err != nil { return nil, pp.MakeErrRes(errors.New("skycoin tx serialize failed")) } newTxid, err := skycoin.BroadcastTx(nodeAddr, hex.EncodeToString(rawtx)) if err != nil { logger.Error(err.Error()) return nil, pp.MakeErrResWithCode(pp.ErrCode_BroadcastTxFail) } success = true if skyTxRlt.ChangeAddr != "" { logger.Debug("change address:%s", skyTxRlt.ChangeAddr) ee.WatchAddress(ct, skyTxRlt.ChangeAddr) } resp := pp.WithdrawalRes{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), NewTxid: &newTxid, } return &resp, nil }
// CreateRawTx create raw tx base on some utxos. // mode: POST // url: /api/v1/create_rawtx?coin_type=[:coin_type] // request json: // different in bitcoin and skycoin. func CreateRawTx(se Servicer) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var rlt *pp.EmptyRes loop: for { // get coin type cp := r.FormValue("coin_type") // get request body params := rawTxParams{} if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } coin, err := se.GetCoin(cp) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_ServerError) break } var rawtx string switch cp { case bitcoin.Type: outs := make([]bitcoin.TxOut, len(params.TxOuts)) for i, o := range params.TxOuts { outs[i].Addr = o.Addr outs[i].Value = o.Value } rawtx, err = coin.CreateRawTx(params.TxIns, outs) case skycoin.Type: outs := make([]skycoin.TxOut, len(params.TxOuts)) for i, o := range params.TxOuts { addr, err := cipher.DecodeBase58Address(o.Addr) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break loop } outs[i].Address = addr outs[i].Coins = o.Value outs[i].Hours = o.Hours } rawtx, err = coin.CreateRawTx(params.TxIns, outs) } if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrRes(err) break } res := struct { Result *pp.Result `json:"result"` Rawtx string `json:"rawtx"` }{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), Rawtx: rawtx, } sendJSON(w, &res) return } sendJSON(w, rlt) } }
// CreateOrder create specifc order. func CreateOrder(egn engine.Exchange) sknet.HandlerFunc { return func(c *sknet.Context) error { rlt := &pp.EmptyRes{} req := &pp.OrderReq{} for { if err := c.BindJSON(req); err != nil { rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) logger.Error(err.Error()) break } // validate pubkey pubkey := req.GetPubkey() if err := validatePubkey(pubkey); err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongPubkey) break } // get order type op, err := order.TypeFromStr(req.GetType()) if err != nil { rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) logger.Error(err.Error()) break } // find the account acnt, err := egn.GetAccount(pubkey) if err != nil { rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongPubkey) logger.Error(err.Error()) break } cp, bal, err := needBalance(op, req) if err != nil { rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) logger.Error(err.Error()) break } if acnt.GetBalance(cp) < bal { err := fmt.Errorf("%s balance is not sufficient", cp) rlt = pp.MakeErrRes(err) logger.Debug(err.Error()) break } var success bool if op == order.Bid { defer func() { if success { egn.SaveAccount() } else { acnt.IncreaseBalance(cp, bal) } }() // decrease the balance, in case of double use the coins. logger.Info("account:%s decrease %s:%d", acnt.GetID(), cp, bal) if err := acnt.DecreaseBalance(cp, bal); err != nil { rlt = pp.MakeErrRes(err) logger.Error(err.Error()) break } } odr := order.New(pubkey, op, req.GetPrice(), req.GetAmount()) oid, err := egn.AddOrder(req.GetCoinPair(), *odr) if err != nil { logger.Error(err.Error()) rlt = pp.MakeErrResWithCode(pp.ErrCode_WrongRequest) break } success = true logger.Info(fmt.Sprintf("new %s order:%d", op, oid)) res := pp.OrderRes{ Result: pp.MakeResultWithCode(pp.ErrCode_Success), OrderId: &oid, } return c.SendJSON(&res) } return c.Error(rlt) } }