· 7 years ago · Jul 23, 2018, 04:22 PM
1/*
2Copyright 2018 Idealnaya rabota LLC
3Licensed under Multy.io license.
4See LICENSE for details
5*/
6package client
7
8import (
9 "context"
10 "crypto/hmac"
11 "crypto/sha512"
12 "encoding/base64"
13 "encoding/json"
14 "errors"
15 "io/ioutil"
16 "math/big"
17 "net/http"
18 "sort"
19 "strconv"
20 "strings"
21 "time"
22
23 "github.com/Multy-io/Multy-back/btc"
24 "github.com/Multy-io/Multy-back/currencies"
25 "github.com/Multy-io/Multy-back/eth"
26 "github.com/Multy-io/Multy-back/store"
27 "github.com/jekabolt/slf"
28
29 btcpb "github.com/Multy-io/Multy-back/node-streamer/btc"
30 ethpb "github.com/Multy-io/Multy-back/node-streamer/eth"
31 "github.com/btcsuite/btcd/rpcclient"
32 "github.com/gin-gonic/gin"
33 mgo "gopkg.in/mgo.v2"
34 "gopkg.in/mgo.v2/bson"
35)
36
37const (
38 msgErrMissingRequestParams = "missing request parametrs"
39 msgErrServerError = "internal server error"
40 msgErrNoWallet = "no such wallet"
41 msgErrWalletNonZeroBalance = "can't delete non zero balance wallet"
42 msgErrWalletIndex = "already existing wallet index"
43 msgErrTxHistory = "not found any transaction history"
44 msgErrAddressIndex = "already existing address index"
45 msgErrMethodNotImplennted = "method is not implemented"
46 msgErrHeaderError = "wrong authorization headers"
47 msgErrRequestBodyError = "missing request body params"
48 msgErrUserNotFound = "user not found in db"
49 msgErrNoTransactionAddress = "zero balance"
50 msgErrNoSpendableOutputs = "no spendable outputs"
51 msgErrRatesError = "internal server error rates"
52 msgErrDecodeWalletIndexErr = "wrong wallet index"
53 msgErrDecodeNetworkIDErr = "wrong network id"
54 msgErrNoSpendableOuts = "no spendable outputs"
55 msgErrDecodeCurIndexErr = "wrong currency index"
56 msgErrDecodenetworkidErr = "wrong networkid index"
57 msgErrAdressBalance = "empty address or 3-rd party server error"
58 msgErrChainIsNotImplemented = "current chain is not implemented"
59 msgErrUserHaveNoTxs = "user have no transactions"
60)
61
62type RestClient struct {
63 middlewareJWT *GinJWTMiddleware
64 userStore store.UserStore
65 rpcClient *rpcclient.Client
66 // // ballance api for test net
67 // apiBTCTest gobcy.API
68 btcConfTestnet BTCApiConf
69 // // ballance api for main net
70 // apiBTCMain gobcy.API
71 btcConfMainnet BTCApiConf
72
73 log slf.StructuredLogger
74
75 donationAddresses []store.DonationInfo
76
77 BTC *btc.BTCConn
78 ETH *eth.ETHConn
79 MultyVerison store.ServerConfig
80 Secretkey string
81}
82
83type BTCApiConf struct {
84 Token, Coin, Chain string
85}
86
87func SetRestHandlers(
88 userDB store.UserStore,
89 r *gin.Engine,
90 donationAddresses []store.DonationInfo,
91 btc *btc.BTCConn,
92 eth *eth.ETHConn,
93 mv store.ServerConfig,
94 secretkey string,
95) (*RestClient, error) {
96 restClient := &RestClient{
97 userStore: userDB,
98 log: slf.WithContext("rest-client"),
99 donationAddresses: donationAddresses,
100 BTC: btc,
101 ETH: eth,
102 MultyVerison: mv,
103 Secretkey: secretkey,
104 }
105 initMiddlewareJWT(restClient)
106
107 r.POST("/auth", restClient.LoginHandler())
108 r.GET("/server/config", restClient.getServerConfig())
109
110 r.GET("/donations", restClient.donations())
111
112 v1 := r.Group("/api/v1")
113 v1.Use(restClient.middlewareJWT.MiddlewareFunc())
114 {
115 v1.POST("/wallet", restClient.addWallet())
116 v1.DELETE("/wallet/:currencyid/:networkid/:walletindex", restClient.deleteWallet())
117 v1.POST("/address", restClient.addAddress())
118 v1.GET("/transaction/feerate/:currencyid/:networkid", restClient.getFeeRate())
119 v1.GET("/outputs/spendable/:currencyid/:networkid/:addr", restClient.getSpendableOutputs())
120 v1.POST("/transaction/send", restClient.sendRawHDTransaction())
121 v1.GET("/wallet/:walletindex/verbose/:currencyid/:networkid", restClient.getWalletVerbose())
122 v1.GET("/wallets/verbose", restClient.getAllWalletsVerbose())
123 v1.GET("/wallets/transactions/:currencyid/:networkid/:walletindex", restClient.getWalletTransactionsHistory())
124 v1.POST("/wallet/name", restClient.changeWalletName())
125 v1.POST("/resync/wallet/:currencyid/:networkid/:walletindex", restClient.resyncWallet())
126 v1.GET("/exchange/changelly/list", restClient.changellyListCurrencies())
127 }
128 return restClient, nil
129}
130
131func initMiddlewareJWT(restClient *RestClient) {
132 restClient.middlewareJWT = &GinJWTMiddleware{
133 Realm: "test zone",
134 Key: []byte(restClient.Secretkey), // config
135 Timeout: time.Hour,
136 MaxRefresh: time.Hour,
137 Authenticator: func(userId, deviceId, pushToken string, deviceType int, c *gin.Context) (store.User, bool) {
138 query := bson.M{"userID": userId}
139
140 user := store.User{}
141
142 err := restClient.userStore.FindUser(query, &user)
143
144 if err != nil || len(user.UserID) == 0 {
145 return user, false
146 }
147 return user, true
148 },
149 Unauthorized: nil,
150 TokenLookup: "header:Authorization",
151
152 TokenHeadName: "Bearer",
153
154 TimeFunc: time.Now,
155 }
156}
157
158type WalletParams struct {
159 CurrencyID int `json:"currencyID"`
160 NetworkID int `json:"networkID"`
161 Address string `json:"address"`
162 AddressIndex int `json:"addressIndex"`
163 WalletIndex int `json:"walletIndex"`
164 WalletName string `json:"walletName"`
165}
166
167type SelectWallet struct {
168 CurrencyID int `json:"currencyID"`
169 NetworkID int `json:"networkID"`
170 WalletIndex int `json:"walletIndex"`
171 Address string `json:"address"`
172 AddressIndex int `json:"addressIndex"`
173}
174
175type EstimationSpeeds struct {
176 VerySlow int
177 Slow int
178 Medium int
179 Fast int
180 VeryFast int
181}
182
183type EstimationSpeedsETH struct {
184 VerySlow string
185 Slow string
186 Medium string
187 Fast string
188 VeryFast string
189}
190
191type Tx struct {
192 Transaction string `json:"transaction"`
193 AllowHighFees bool `json:"allowHighFees"`
194}
195
196type DisplayWallet struct {
197 Chain string `json:"chain"`
198 Adresses []store.Address `json:"addresses"`
199}
200
201type WalletExtended struct {
202 CuurencyID int `bson:"chain"` //cuurencyID
203 WalletIndex int `bson:"walletIndex"` //walletIndex
204 Addresses []AddressEx `bson:"addresses"`
205}
206
207type AddressEx struct { // extended
208 AddressID int `bson:"addressID"` //addressIndex
209 Address string `bson:"address"`
210 Amount int `bson:"amount"` //float64
211}
212
213func getToken(c *gin.Context) (string, error) {
214 authHeader := strings.Split(c.GetHeader("Authorization"), " ")
215 if len(authHeader) < 2 {
216 return "", errors.New(msgErrHeaderError)
217 }
218 return authHeader[1], nil
219}
220
221func createCustomWallet(wp WalletParams, token string, restClient *RestClient, c *gin.Context) error {
222 user := store.User{}
223 query := bson.M{"devices.JWT": token}
224
225 err := restClient.userStore.FindUser(query, &user)
226 if err != nil {
227 restClient.log.Errorf("createCustomWallet: restClient.userStore.FindUser: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
228 err = errors.New(msgErrUserNotFound)
229 return err
230 }
231
232 for _, wallet := range user.Wallets {
233 if wallet.CurrencyID == wp.CurrencyID && wallet.NetworkID == wp.NetworkID && wallet.WalletIndex == wp.WalletIndex {
234 err = errors.New(msgErrWalletIndex)
235 return err
236 }
237 }
238
239 sel := bson.M{"devices.JWT": token}
240 wallet := createWallet(wp.CurrencyID, wp.NetworkID, wp.Address, wp.AddressIndex, wp.WalletIndex, wp.WalletName)
241 update := bson.M{"$push": bson.M{"wallets": wallet}}
242
243 err = restClient.userStore.Update(sel, update)
244 if err != nil {
245 restClient.log.Errorf("addWallet: restClient.userStore.Update: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
246 err := errors.New(msgErrServerError)
247 return err
248 }
249
250 err = AddWatchAndResync(wp.CurrencyID, wp.NetworkID, wp.WalletIndex, wp.AddressIndex, user.UserID, wp.Address, restClient)
251 if err != nil {
252 restClient.log.Errorf("createCustomWallet: AddWatchAndResync: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
253 err := errors.New(msgErrServerError)
254 return err
255 }
256
257 return nil
258}
259
260func changeName(cn ChangeName, token string, restClient *RestClient, c *gin.Context) error {
261 user := store.User{}
262 query := bson.M{"devices.JWT": token}
263
264 if err := restClient.userStore.FindUser(query, &user); err != nil {
265 restClient.log.Errorf("deleteWallet: restClient.userStore.FindUser: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
266 err := errors.New(msgErrUserNotFound)
267 return err
268 }
269 var position int
270
271 for i, wallet := range user.Wallets {
272 if wallet.NetworkID == cn.NetworkID && wallet.WalletIndex == cn.WalletIndex && wallet.CurrencyID == cn.CurrencyID {
273 position = i
274 break
275 }
276 }
277 sel := bson.M{"userID": user.UserID, "wallets.walletIndex": cn.WalletIndex, "wallets.networkID": cn.NetworkID}
278 update := bson.M{
279 "$set": bson.M{
280 "wallets." + strconv.Itoa(position) + ".walletName": cn.WalletName,
281 },
282 }
283 return restClient.userStore.Update(sel, update)
284
285 err := errors.New(msgErrNoWallet)
286 return err
287
288}
289
290func addAddressToWallet(address, token string, currencyID, networkid, walletIndex, addressIndex int, restClient *RestClient, c *gin.Context) error {
291 user := store.User{}
292 query := bson.M{"devices.JWT": token}
293
294 if err := restClient.userStore.FindUser(query, &user); err != nil {
295 // restClient.log.Errorf("deleteWallet: restClient.userStore.FindUser: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
296 return errors.New(msgErrUserNotFound)
297 }
298
299 var position int
300 for i, wallet := range user.Wallets {
301 if wallet.NetworkID == networkid && wallet.CurrencyID == currencyID && wallet.WalletIndex == walletIndex {
302 position = i
303 for _, walletAddress := range wallet.Adresses {
304 if walletAddress.AddressIndex == addressIndex {
305 return errors.New(msgErrAddressIndex)
306 }
307 }
308 }
309 }
310
311 addr := store.Address{
312 Address: address,
313 AddressIndex: addressIndex,
314 LastActionTime: time.Now().Unix(),
315 }
316
317 //TODO: make no possibility to add eth address
318 sel := bson.M{"devices.JWT": token, "wallets.currencyID": currencyID, "wallets.networkID": networkid, "wallets.walletIndex": walletIndex}
319 update := bson.M{"$push": bson.M{"wallets." + strconv.Itoa(position) + ".addresses": addr}}
320 if err := restClient.userStore.Update(sel, update); err != nil {
321 // restClient.log.Errorf("addAddressToWallet: restClient.userStore.Update: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
322 return errors.New(msgErrServerError)
323 }
324
325 return nil
326
327 // return AddWatchAndResync(currencyID, networkid, walletIndex, addressIndex, user.UserID, address, restClient)
328
329}
330
331func AddWatchAndResync(currencyID, networkid, walletIndex, addressIndex int, userid, address string, restClient *RestClient) error {
332
333 err := NewAddressNode(address, userid, currencyID, networkid, walletIndex, addressIndex, restClient)
334 if err != nil {
335 restClient.log.Errorf("AddWatchAndResync: NewAddressWs: currencies.Main: WsBtcMainnetCli.Emit:resync %s\t", err.Error())
336 return err
337 }
338
339 return nil
340}
341
342func NewAddressNode(address, userid string, currencyID, networkID, walletIndex, addressIndex int, restClient *RestClient) error {
343
344 //add new re-sync to map
345 restClient.BTC.Resync.Store(address, true)
346
347 switch currencyID {
348 case currencies.Bitcoin:
349 if networkID == currencies.Main {
350 restClient.BTC.WatchAddressMain <- btcpb.WatchAddress{
351 Address: address,
352 UserID: userid,
353 WalletIndex: int32(walletIndex),
354 AddressIndex: int32(addressIndex),
355 }
356 }
357
358 if networkID == currencies.Test {
359 restClient.BTC.WatchAddressTest <- btcpb.WatchAddress{
360 Address: address,
361 UserID: userid,
362 WalletIndex: int32(walletIndex),
363 AddressIndex: int32(addressIndex),
364 }
365 }
366 case currencies.Ether:
367 if networkID == currencies.ETHMain {
368 restClient.ETH.WatchAddressMain <- ethpb.WatchAddress{
369 Address: address,
370 UserID: userid,
371 WalletIndex: int32(walletIndex),
372 AddressIndex: int32(addressIndex),
373 }
374 }
375
376 if networkID == currencies.ETHTest {
377 restClient.ETH.WatchAddressTest <- ethpb.WatchAddress{
378 Address: address,
379 UserID: userid,
380 WalletIndex: int32(walletIndex),
381 AddressIndex: int32(addressIndex),
382 }
383 }
384 }
385 return nil
386}
387
388func (restClient *RestClient) addWallet() gin.HandlerFunc {
389 return func(c *gin.Context) {
390 token, err := getToken(c)
391 if err != nil {
392 restClient.log.Errorf("addWallet: getToken: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
393 c.JSON(http.StatusBadRequest, gin.H{
394 "code": http.StatusBadRequest,
395 "message": msgErrHeaderError,
396 })
397 return
398 }
399
400 var (
401 code = http.StatusOK
402 message = http.StatusText(http.StatusOK)
403 )
404
405 var wp WalletParams
406
407 err = decodeBody(c, &wp)
408 if err != nil {
409 restClient.log.Errorf("addWallet: decodeBody: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
410 c.JSON(http.StatusBadRequest, gin.H{
411 "code": http.StatusBadRequest,
412 "message": msgErrRequestBodyError,
413 })
414 return
415 }
416
417 err = createCustomWallet(wp, token, restClient, c)
418 if err != nil {
419 c.JSON(http.StatusBadRequest, gin.H{
420 "code": http.StatusBadRequest,
421 "message": err.Error(),
422 })
423 return
424 }
425
426 c.JSON(http.StatusCreated, gin.H{
427 "code": code,
428 "time": time.Now().Unix(),
429 "message": message,
430 })
431 return
432 }
433}
434
435type ChangeName struct {
436 WalletName string `json:"walletname"`
437 CurrencyID int `json:"currencyID"`
438 WalletIndex int `json:"walletIndex"`
439 NetworkID int `json:"networkId"`
440}
441
442func (restClient *RestClient) changeWalletName() gin.HandlerFunc {
443 return func(c *gin.Context) {
444 token, err := getToken(c)
445 if err != nil {
446 c.JSON(http.StatusBadRequest, gin.H{
447 "code": http.StatusBadRequest,
448 "message": msgErrHeaderError,
449 })
450 return
451 }
452
453 var cn ChangeName
454 err = decodeBody(c, &cn)
455 if err != nil {
456 restClient.log.Errorf("changeWalletName: decodeBody: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
457 c.JSON(http.StatusBadRequest, gin.H{
458 "code": http.StatusBadRequest,
459 "message": msgErrRequestBodyError,
460 })
461 return
462 }
463 err = changeName(cn, token, restClient, c)
464 if err != nil {
465 c.JSON(http.StatusBadRequest, gin.H{
466 "code": http.StatusBadRequest,
467 "message": err.Error(),
468 })
469 return
470 }
471
472 c.JSON(http.StatusOK, gin.H{
473 "code": http.StatusOK,
474 "message": http.StatusText(http.StatusOK),
475 })
476
477 }
478}
479
480func (restClient *RestClient) donations() gin.HandlerFunc {
481 return func(c *gin.Context) {
482 donationInfo := []store.Donation{}
483 for _, da := range restClient.donationAddresses {
484 b := checkBTCAddressbalance(da.DonationAddress, currencies.Bitcoin, currencies.Main, restClient)
485 donationInfo = append(donationInfo, store.Donation{
486 FeatureID: da.FeatureCode,
487 Address: da.DonationAddress,
488 Amount: b,
489 Status: 1,
490 })
491 }
492 c.JSON(http.StatusOK, gin.H{
493 "code": http.StatusOK,
494 "message": http.StatusText(http.StatusOK),
495 "donations": donationInfo,
496 })
497 }
498}
499
500func (restClient *RestClient) getServerConfig() gin.HandlerFunc {
501 return func(c *gin.Context) {
502 resp := map[string]interface{}{
503 "stockexchanges": map[string][]string{
504 "poloniex": []string{"usd_btc", "eth_btc", "eth_usd", "btc_usd"},
505 "gdax": []string{"eur_btc", "usd_btc", "eth_btc", "eth_usd", "eth_eur", "btc_usd"},
506 },
507 "servertime": time.Now().UTC().Unix(),
508 "api": "0.01",
509 "android": map[string]int{
510 "soft": 7,
511 "hard": 7,
512 },
513 "version": restClient.MultyVerison,
514 "ios": map[string]int{
515 "soft": 53,
516 "hard": 49,
517 },
518 "donate": restClient.donationAddresses,
519 }
520 c.JSON(http.StatusOK, resp)
521 }
522}
523
524func checkBTCAddressbalance(address string, currencyID, networkid int, restClient *RestClient) int64 {
525 var balance int64
526 spOuts, err := restClient.userStore.GetAddressSpendableOutputs(address, currencyID, networkid)
527 if err != nil {
528 return balance
529 }
530
531 for _, out := range spOuts {
532 balance += out.TxOutAmount
533 }
534 return balance
535}
536
537func getBTCAddressSpendableOutputs(address string, currencyID, networkID int, restClient *RestClient) []store.SpendableOutputs {
538 spOuts, err := restClient.userStore.GetAddressSpendableOutputs(address, currencyID, networkID)
539 if err != nil && err != mgo.ErrNotFound {
540 restClient.log.Errorf("getBTCAddressSpendableOutputs: GetAddressSpendableOutputs: %s\t", err.Error())
541 }
542 return spOuts
543}
544
545func (restClient *RestClient) deleteWallet() gin.HandlerFunc {
546 return func(c *gin.Context) {
547 token, err := getToken(c)
548 if err != nil {
549 c.JSON(http.StatusBadRequest, gin.H{
550 "code": http.StatusBadRequest,
551 "message": msgErrHeaderError,
552 })
553 return
554 }
555
556 walletIndex, err := strconv.Atoi(c.Param("walletindex"))
557 restClient.log.Debugf("getWalletVerbose [%d] \t[walletindexr=%s]", walletIndex, c.Request.RemoteAddr)
558 if err != nil {
559 restClient.log.Errorf("getWalletVerbose: non int wallet index:[%d] %s \t[addr=%s]", walletIndex, err.Error(), c.Request.RemoteAddr)
560 c.JSON(http.StatusBadRequest, gin.H{
561 "code": http.StatusBadRequest,
562 "message": msgErrDecodeWalletIndexErr,
563 })
564 return
565 }
566
567 currencyId, err := strconv.Atoi(c.Param("currencyid"))
568 restClient.log.Debugf("getWalletVerbose [%d] \t[currencyId=%s]", walletIndex, c.Request.RemoteAddr)
569 if err != nil {
570 restClient.log.Errorf("getWalletVerbose: non int currency id:[%d] %s \t[addr=%s]", currencyId, err.Error(), c.Request.RemoteAddr)
571 c.JSON(http.StatusBadRequest, gin.H{
572 "code": http.StatusBadRequest,
573 "message": msgErrDecodeCurIndexErr,
574 })
575 return
576 }
577
578 networkid, err := strconv.Atoi(c.Param("networkid"))
579 restClient.log.Debugf("getWalletVerbose [%d] \t[networkid=%s]", walletIndex, c.Request.RemoteAddr)
580 if err != nil {
581 restClient.log.Errorf("getWalletVerbose: non int networkid index:[%d] %s \t[addr=%s]", networkid, err.Error(), c.Request.RemoteAddr)
582 c.JSON(http.StatusBadRequest, gin.H{
583 "code": http.StatusBadRequest,
584 "message": msgErrDecodenetworkidErr,
585 })
586 return
587 }
588
589 var (
590 code int
591 message string
592 )
593
594 user := store.User{}
595 query := bson.M{"devices.JWT": token}
596 if err := restClient.userStore.FindUser(query, &user); err != nil {
597 restClient.log.Errorf("deleteWallet: restClient.userStore.FindUser: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
598 c.JSON(http.StatusBadRequest, gin.H{
599 "code": http.StatusBadRequest,
600 "message": msgErrUserNotFound,
601 })
602 return
603 }
604 code = http.StatusOK
605 message = http.StatusText(http.StatusOK)
606
607 var totalBalance int64
608
609 switch currencyId {
610 case currencies.Bitcoin:
611
612 if networkid == currencies.Main {
613 for _, wallet := range user.Wallets {
614 if wallet.WalletIndex == walletIndex {
615 for _, address := range wallet.Adresses {
616 totalBalance += checkBTCAddressbalance(address.Address, currencyId, networkid, restClient)
617 }
618 }
619 }
620 }
621 if networkid == currencies.Test {
622 for _, wallet := range user.Wallets {
623 if wallet.WalletIndex == walletIndex {
624 for _, address := range wallet.Adresses {
625 totalBalance += checkBTCAddressbalance(address.Address, currencyId, networkid, restClient)
626 }
627 }
628 }
629 }
630
631 if totalBalance == 0 {
632 err := restClient.userStore.DeleteWallet(user.UserID, walletIndex, currencyId, networkid)
633 if err != nil {
634 restClient.log.Errorf("deleteWallet: restClient.userStore.Update: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
635 c.JSON(http.StatusBadRequest, gin.H{
636 "code": http.StatusInternalServerError,
637 "message": msgErrNoWallet,
638 })
639 return
640 }
641 }
642
643 if totalBalance != 0 {
644 c.JSON(http.StatusBadRequest, gin.H{
645 "code": http.StatusBadRequest,
646 "message": msgErrWalletNonZeroBalance,
647 })
648 return
649 }
650
651 code = http.StatusOK
652 message = http.StatusText(http.StatusOK)
653
654 case currencies.Ether:
655
656 var address string
657 for _, wallet := range user.Wallets {
658 if wallet.WalletIndex == walletIndex {
659 if len(wallet.Adresses) > 0 {
660 address = wallet.Adresses[0].Address
661 }
662 }
663 }
664
665 balance := ðpb.Balance{}
666 if networkid == currencies.ETHMain {
667 balance, err = restClient.ETH.CliMain.EventGetAdressBalance(context.Background(), ðpb.AddressToResync{
668 Address: address,
669 })
670 }
671 if networkid == currencies.ETHTest {
672 restClient.ETH.CliTest.EventGetAdressBalance(context.Background(), ðpb.AddressToResync{
673 Address: address,
674 })
675 }
676
677 if balance.Balance == "0" || balance.Balance == "" {
678 err := restClient.userStore.DeleteWallet(user.UserID, walletIndex, currencyId, networkid)
679 if err != nil {
680 restClient.log.Errorf("deleteWallet: restClient.userStore.Update: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
681 c.JSON(http.StatusBadRequest, gin.H{
682 "code": http.StatusInternalServerError,
683 "message": msgErrNoWallet,
684 })
685 return
686 }
687 }
688
689 if balance.Balance != "0" && balance.Balance != "" {
690 c.JSON(http.StatusBadRequest, gin.H{
691 "code": http.StatusBadRequest,
692 "message": msgErrWalletNonZeroBalance,
693 })
694 return
695 }
696
697 code = http.StatusOK
698 message = http.StatusText(http.StatusOK)
699 default:
700 c.JSON(http.StatusBadRequest, gin.H{
701 "code": http.StatusBadRequest,
702 "message": msgErrChainIsNotImplemented,
703 })
704 return
705 }
706
707 if totalBalance != 0 {
708 c.JSON(http.StatusBadRequest, gin.H{
709 "code": http.StatusBadRequest,
710 "message": msgErrWalletNonZeroBalance,
711 })
712 return
713 }
714 c.JSON(code, gin.H{
715 "code": code,
716 "message": message,
717 })
718 }
719}
720
721func (restClient *RestClient) addAddress() gin.HandlerFunc {
722 return func(c *gin.Context) {
723 token, err := getToken(c)
724 if err != nil {
725 c.JSON(http.StatusBadRequest, gin.H{
726 "code": http.StatusBadRequest,
727 "message": msgErrHeaderError,
728 })
729 return
730 }
731 var sw SelectWallet
732 err = decodeBody(c, &sw)
733 if err != nil {
734 restClient.log.Errorf("addAddress: decodeBody: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
735 }
736
737 err = addAddressToWallet(sw.Address, token, sw.CurrencyID, sw.NetworkID, sw.WalletIndex, sw.AddressIndex, restClient, c)
738 if err != nil {
739 c.JSON(http.StatusBadRequest, gin.H{
740 "code": http.StatusText(http.StatusBadRequest),
741 "message": err.Error(),
742 })
743 return
744 }
745
746 c.JSON(http.StatusCreated, gin.H{
747 "code": http.StatusText(http.StatusCreated),
748 "message": "wallet created",
749 })
750 }
751}
752
753func (restClient *RestClient) getFeeRate() gin.HandlerFunc {
754 return func(c *gin.Context) {
755 var sp EstimationSpeeds
756 currencyID, err := strconv.Atoi(c.Param("currencyid"))
757 if err != nil {
758 restClient.log.Errorf("getWalletVerbose: non int currency id: %s \t[addr=%s]", err.Error(), c.Request.RemoteAddr)
759 c.JSON(http.StatusBadRequest, gin.H{
760 "speeds": sp,
761 "code": http.StatusBadRequest,
762 "message": msgErrDecodeCurIndexErr,
763 })
764 return
765 }
766
767 networkid, err := strconv.Atoi(c.Param("networkid"))
768 restClient.log.Debugf("getWalletVerbose [%d] \t[networkid=%s]", networkid, c.Request.RemoteAddr)
769 if err != nil {
770 restClient.log.Errorf("getWalletVerbose: non int networkid:[%d] %s \t[addr=%s]", networkid, err.Error(), c.Request.RemoteAddr)
771 c.JSON(http.StatusBadRequest, gin.H{
772 "code": http.StatusBadRequest,
773 "message": msgErrDecodenetworkidErr,
774 })
775 return
776 }
777
778 switch currencyID {
779 case currencies.Bitcoin:
780 // restClient.BTC.M.Lock()
781 // mempool := *restClient.BTC.BtcMempool
782 // restClient.BTC.M.Unlock()
783
784 type kv struct {
785 Key string
786 Value int
787 }
788
789 var mp []kv
790 restClient.BTC.BtcMempool.Range(func(k, v interface{}) bool {
791 mp = append(mp, kv{k.(string), v.(int)})
792 return true
793 })
794
795 // var mp []kv
796 // for k, v := range mempool {
797 // mp = append(mp, kv{k, v})
798 // }
799
800 sort.Slice(mp, func(i, j int) bool {
801 return mp[i].Value > mp[j].Value
802 })
803
804 // for _, kv := range ss {
805 // fmt.Printf("%s, %d\n", kv.Key, kv.Value)
806 // }
807
808 // var speeds []string{}
809 var slowestValue, slowValue, mediumValue, fastValue, fastestValue int
810
811 memPoolSize := len(mp)
812
813 if memPoolSize <= 2000 && memPoolSize > 0 {
814 //low rates logic
815
816 fastestPosition := int(memPoolSize / 100 * 5)
817 fastPosition := int(memPoolSize / 100 * 30)
818 mediumPosition := int(memPoolSize / 100 * 50)
819 slowPosition := int(memPoolSize / 100 * 80)
820 //slowestPosition := int(memPoolSize)
821
822 slowestValue = 2
823
824 slowValue = mp[slowPosition].Value
825
826 if slowValue < 2 {
827 slowValue = 2
828 }
829
830 mediumValue = mp[mediumPosition].Value
831 fastValue = mp[fastPosition].Value
832 fastestValue = mp[fastestPosition].Value
833
834 } else if memPoolSize == 0 {
835 slowestValue = 2
836 slowValue = 2
837 mediumValue = 3
838 fastValue = 5
839 fastestValue = 10
840 } else {
841 //high rates logic
842 fastestPosition := 100
843 fastPosition := 500
844 mediumPosition := 2000
845 slowPosition := int(memPoolSize / 100 * 70)
846 slowestPosition := int(memPoolSize / 100 * 90)
847
848 slowestValue = mp[slowestPosition].Value
849
850 if slowestValue < 2 {
851 slowestValue = 2
852 }
853
854 slowValue = mp[slowPosition].Value
855
856 if slowValue < 2 {
857 slowValue = 2
858 }
859
860 mediumValue = mp[mediumPosition].Value
861 fastValue = mp[fastPosition].Value
862 fastestValue = mp[fastestPosition].Value
863
864 }
865
866 if fastValue > fastestValue {
867 fastestValue = fastValue
868 }
869 if mediumValue > fastValue {
870 fastValue = mediumValue
871 }
872 if slowValue > mediumValue {
873 mediumValue = slowValue
874 }
875 if slowestValue > slowValue {
876 slowValue = slowestValue
877 }
878
879 sp = EstimationSpeeds{
880 VerySlow: slowestValue,
881 Slow: slowValue,
882 Medium: mediumValue,
883 Fast: fastValue,
884 VeryFast: fastestValue,
885 }
886
887 restClient.log.Debugf("FeeRates for Bitcoin network id %d is: %v :\n memPoolSize is: %v ", networkid, sp, memPoolSize)
888
889 c.JSON(http.StatusOK, gin.H{
890 "speeds": sp,
891 "code": http.StatusOK,
892 "message": http.StatusText(http.StatusOK),
893 })
894 case currencies.Ether:
895 //TODO: make eth feerate
896 var rate *ethpb.GasPrice
897 var err error
898 switch networkid {
899 case currencies.ETHMain:
900 rate, err = restClient.ETH.CliMain.EventGetGasPrice(context.Background(), ðpb.Empty{})
901 case currencies.ETHTest:
902 rate, err = restClient.ETH.CliTest.EventGetGasPrice(context.Background(), ðpb.Empty{})
903 default:
904 restClient.log.Errorf("getFeeRate:currencies.Ether: no such networkid")
905 }
906
907 if err != nil {
908 restClient.log.Errorf("getFeeRate:currencies.Ether:restClient.ETH.Cli: %v ", err.Error())
909 }
910 speed, _ := strconv.Atoi(rate.GetGas())
911
912 c.JSON(http.StatusOK, gin.H{
913 "speeds": EstimationSpeeds{
914 VerySlow: speed,
915 Slow: speed * 2,
916 Medium: speed * 3,
917 Fast: speed * 4,
918 VeryFast: speed * 5,
919 },
920 "code": http.StatusOK,
921 "message": http.StatusText(http.StatusOK),
922 })
923
924 default:
925
926 }
927
928 }
929}
930
931func avg(arr []store.MempoolRecord) int {
932 total := 0
933 for _, value := range arr {
934 total += value.Category
935 }
936 if total == 0 {
937 return 0
938 }
939 return total / len(arr)
940}
941
942func (restClient *RestClient) getSpendableOutputs() gin.HandlerFunc {
943 return func(c *gin.Context) {
944 token, err := getToken(c)
945 if err != nil {
946 c.JSON(http.StatusBadRequest, gin.H{
947 "code": http.StatusBadRequest,
948 "message": msgErrHeaderError,
949 })
950 return
951 }
952
953 currencyID, err := strconv.Atoi(c.Param("currencyid"))
954 restClient.log.Errorf("getSpendableOutputs [%d] \t[addr=%s]", currencyID, c.Request.RemoteAddr)
955 if err != nil {
956 restClient.log.Errorf("getSpendableOutputs: non int currencyID:[%d] %s \t[addr=%s]", currencyID, err.Error(), c.Request.RemoteAddr)
957 c.JSON(http.StatusBadRequest, gin.H{
958 "code": http.StatusBadRequest,
959 "message": msgErrDecodeCurIndexErr,
960 "outs": 0,
961 })
962 return
963 }
964
965 networkid, err := strconv.Atoi(c.Param("networkid"))
966 restClient.log.Debugf("getSpendableOutputs \t[networkid=%s]", c.Request.RemoteAddr)
967 if err != nil {
968 restClient.log.Errorf("getSpendableOutputs: non int networkid : %s \t[addr=%s]", err.Error(), c.Request.RemoteAddr)
969 c.JSON(http.StatusBadRequest, gin.H{
970 "code": http.StatusBadRequest,
971 "message": msgErrDecodenetworkidErr,
972 })
973 return
974 }
975
976 address := c.Param("addr")
977
978 var (
979 code int
980 message string
981 )
982
983 user := store.User{}
984 query := bson.M{"devices.JWT": token}
985 if err := restClient.userStore.FindUser(query, &user); err != nil {
986 restClient.log.Errorf("getAllWalletsVerbose: restClient.userStore.FindUser: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
987 c.JSON(http.StatusBadRequest, gin.H{
988 "code": http.StatusBadRequest,
989 "message": msgErrUserNotFound,
990 "outs": 0,
991 })
992 return
993 } else {
994 code = http.StatusOK
995 message = http.StatusText(http.StatusOK)
996 }
997
998 spOuts, err := restClient.userStore.GetAddressSpendableOutputs(address, currencyID, networkid)
999 if err != nil {
1000 restClient.log.Errorf("getSpendableOutputs: GetAddressSpendableOutputs:[%d] %s \t[addr=%s]", currencyID, err.Error(), c.Request.RemoteAddr)
1001 }
1002
1003 c.JSON(code, gin.H{
1004 "code": code,
1005 "message": message,
1006 "outs": spOuts,
1007 })
1008 }
1009}
1010
1011type RawHDTx struct {
1012 CurrencyID int `json:"currencyid"`
1013 NetworkID int `json:"networkID"`
1014 Payload `json:"payload"`
1015}
1016
1017type Payload struct {
1018 Address string `json:"address"`
1019 AddressIndex int `json:"addressindex"`
1020 WalletIndex int `json:"walletindex"`
1021 Transaction string `json:"transaction"`
1022 IsHD bool `json:"ishd"`
1023 MultisigFactory bool `json:"multisigfactory"`
1024 WalletName string `json:"walletname"`
1025 Owners []string `json:"owners"`
1026 ConfirmationsNeeded int `json:"confirmationsneeded"`
1027}
1028
1029func (restClient *RestClient) sendRawHDTransaction() gin.HandlerFunc {
1030 return func(c *gin.Context) {
1031
1032 var rawTx RawHDTx
1033 if err := decodeBody(c, &rawTx); err != nil {
1034 c.JSON(http.StatusOK, gin.H{
1035 "code": http.StatusBadRequest,
1036 "message": msgErrRequestBodyError,
1037 })
1038 }
1039
1040 token, err := getToken(c)
1041 if err != nil {
1042 c.JSON(http.StatusBadRequest, gin.H{
1043 "code": http.StatusBadRequest,
1044 "message": msgErrHeaderError,
1045 })
1046 return
1047 }
1048
1049 user := store.User{}
1050 query := bson.M{"devices.JWT": token}
1051 if err := restClient.userStore.FindUser(query, &user); err != nil {
1052 restClient.log.Errorf("sendRawHDTransaction: restClient.userStore.FindUser: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
1053
1054 return
1055 }
1056 code := http.StatusOK
1057 switch rawTx.CurrencyID {
1058 case currencies.Bitcoin:
1059 if rawTx.NetworkID == currencies.Main {
1060 err := NewAddressNode(rawTx.Address, user.UserID, rawTx.CurrencyID, rawTx.NetworkID, rawTx.WalletIndex, rawTx.AddressIndex, restClient)
1061 if err != nil {
1062 c.JSON(http.StatusInternalServerError, gin.H{
1063 "code": http.StatusInternalServerError,
1064 "message": "err: " + err.Error(),
1065 })
1066 return
1067 }
1068
1069 resp, err := restClient.BTC.CliMain.EventSendRawTx(context.Background(), &btcpb.RawTx{
1070 Transaction: rawTx.Transaction,
1071 })
1072
1073 if err != nil {
1074 restClient.log.Errorf("sendRawHDTransaction: restClient.BTC.CliMain.EventSendRawTx: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
1075 code = http.StatusBadRequest
1076 c.JSON(code, gin.H{
1077 "code": code,
1078 "message": err.Error(),
1079 })
1080 return
1081 }
1082
1083 if strings.Contains("err:", resp.GetMessage()) {
1084 restClient.log.Errorf("sendRawHDTransaction: restClient.BTC.CliMain.EventSendRawTx:resp err %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
1085 code = http.StatusBadRequest
1086 c.JSON(code, gin.H{
1087 "code": code,
1088 "message": resp.GetMessage(),
1089 })
1090 return
1091 }
1092
1093 if rawTx.IsHD {
1094 err = addAddressToWallet(rawTx.Address, token, rawTx.CurrencyID, rawTx.NetworkID, rawTx.WalletIndex, rawTx.AddressIndex, restClient, c)
1095 if err != nil {
1096 c.JSON(http.StatusBadRequest, gin.H{
1097 "code": http.StatusBadRequest,
1098 "message": err.Error(),
1099 })
1100 return
1101 }
1102 }
1103
1104 c.JSON(code, gin.H{
1105 "code": code,
1106 "message": resp.Message,
1107 })
1108 return
1109
1110 }
1111 if rawTx.NetworkID == currencies.Test {
1112
1113 err := NewAddressNode(rawTx.Address, user.UserID, rawTx.CurrencyID, rawTx.NetworkID, rawTx.WalletIndex, rawTx.AddressIndex, restClient)
1114 if err != nil {
1115 c.JSON(http.StatusInternalServerError, gin.H{
1116 "code": http.StatusInternalServerError,
1117 "message": "err: " + err.Error(),
1118 })
1119 return
1120 }
1121
1122 resp, err := restClient.BTC.CliTest.EventSendRawTx(context.Background(), &btcpb.RawTx{
1123 Transaction: rawTx.Transaction,
1124 })
1125 if err != nil {
1126 restClient.log.Errorf("sendRawHDTransaction: restClient.BTC.CliMain.EventSendRawTx: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
1127 code = http.StatusBadRequest
1128 c.JSON(code, gin.H{
1129 "code": code,
1130 "message": err.Error(),
1131 })
1132 return
1133 }
1134
1135 if strings.Contains("err:", resp.GetMessage()) {
1136 restClient.log.Errorf("sendRawHDTransaction: restClient.BTC.CliMain.EventSendRawTx:resp err %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
1137 code = http.StatusBadRequest
1138 c.JSON(code, gin.H{
1139 "code": code,
1140 "message": resp.GetMessage(),
1141 })
1142 return
1143 }
1144
1145 if rawTx.IsHD {
1146 err = addAddressToWallet(rawTx.Address, token, rawTx.CurrencyID, rawTx.NetworkID, rawTx.WalletIndex, rawTx.AddressIndex, restClient, c)
1147 if err != nil {
1148 c.JSON(http.StatusBadRequest, gin.H{
1149 "code": http.StatusBadRequest,
1150 "message": err.Error(),
1151 })
1152 return
1153 }
1154 }
1155 flag := 4
1156 for {
1157 ex := restClient.userStore.CheckTx(resp.GetMessage())
1158 if ex {
1159 break
1160 }
1161 flag++
1162 if flag == 4 {
1163 break
1164 }
1165 }
1166
1167 c.JSON(code, gin.H{
1168 "code": code,
1169 "message": resp.Message,
1170 })
1171 return
1172 }
1173 case currencies.Ether:
1174 if rawTx.NetworkID == currencies.ETHMain {
1175 hash, err := restClient.ETH.CliMain.EventSendRawTx(context.Background(), ðpb.RawTx{
1176 Transaction: rawTx.Transaction,
1177 })
1178 if err != nil {
1179 restClient.log.Errorf("sendRawHDTransaction:eth.SendRawTransaction %s", err.Error())
1180 c.JSON(http.StatusInternalServerError, gin.H{
1181 "code": http.StatusInternalServerError,
1182 "message": err.Error(),
1183 })
1184 return
1185 }
1186 // TODO: Make a wallet
1187
1188 c.JSON(http.StatusOK, gin.H{
1189 "code": http.StatusOK,
1190 "message": hash,
1191 })
1192 return
1193 }
1194 if rawTx.NetworkID == currencies.ETHTest {
1195 hash, err := restClient.ETH.CliTest.EventSendRawTx(context.Background(), ðpb.RawTx{
1196 Transaction: rawTx.Transaction,
1197 })
1198 if err != nil {
1199 restClient.log.Errorf("sendRawHDTransaction:eth.SendRawTransaction %s", err.Error())
1200 c.JSON(http.StatusInternalServerError, gin.H{
1201 "code": http.StatusInternalServerError,
1202 "message": err.Error(),
1203 })
1204 return
1205 }
1206
1207 // TODO: Make a wallet
1208
1209 c.JSON(http.StatusOK, gin.H{
1210 "code": http.StatusOK,
1211 "message": hash,
1212 })
1213 return
1214 }
1215
1216 default:
1217 c.JSON(http.StatusBadRequest, gin.H{
1218 "code": http.StatusBadRequest,
1219 "message": msgErrChainIsNotImplemented,
1220 })
1221 }
1222 }
1223}
1224
1225func errHandler(resp string) bool {
1226 return strings.Contains(resp, "err:")
1227}
1228
1229type RawTx struct { // remane RawClientTransaction
1230 Transaction string `json:"transaction"` //HexTransaction
1231}
1232
1233func (restClient *RestClient) getWalletVerbose() gin.HandlerFunc {
1234 return func(c *gin.Context) {
1235 var wv []interface{}
1236 //var wv []WalletVerbose
1237 token, err := getToken(c)
1238 if err != nil {
1239 c.JSON(http.StatusBadRequest, gin.H{
1240 "code": http.StatusBadRequest,
1241 "message": msgErrHeaderError,
1242 })
1243 return
1244 }
1245
1246 walletIndex, err := strconv.Atoi(c.Param("walletindex"))
1247 restClient.log.Debugf("getWalletVerbose [%d] \t[walletindexr=%s]", walletIndex, c.Request.RemoteAddr)
1248 if err != nil {
1249 restClient.log.Errorf("getWalletVerbose: non int wallet index:[%d] %s \t[addr=%s]", walletIndex, err.Error(), c.Request.RemoteAddr)
1250 c.JSON(http.StatusBadRequest, gin.H{
1251 "code": http.StatusBadRequest,
1252 "message": msgErrDecodeWalletIndexErr,
1253 "wallet": wv,
1254 })
1255 return
1256 }
1257
1258 currencyId, err := strconv.Atoi(c.Param("currencyid"))
1259 restClient.log.Debugf("getWalletVerbose [%d] \t[currencyId=%s]", walletIndex, c.Request.RemoteAddr)
1260 if err != nil {
1261 restClient.log.Errorf("getWalletVerbose: non int currency id:[%d] %s \t[addr=%s]", currencyId, err.Error(), c.Request.RemoteAddr)
1262 c.JSON(http.StatusBadRequest, gin.H{
1263 "code": http.StatusBadRequest,
1264 "message": msgErrDecodeCurIndexErr,
1265 })
1266 return
1267 }
1268
1269 networkId, err := strconv.Atoi(c.Param("networkid"))
1270 restClient.log.Debugf("getWalletVerbose [%d] \t[networkID=%s]", networkId, c.Request.RemoteAddr)
1271 if err != nil {
1272 restClient.log.Errorf("getWalletVerbose: non int networkid:[%d] %s \t[addr=%s]", networkId, err.Error(), c.Request.RemoteAddr)
1273 c.JSON(http.StatusBadRequest, gin.H{
1274 "code": http.StatusBadRequest,
1275 "message": msgErrDecodeNetworkIDErr,
1276 "wallet": wv,
1277 })
1278 return
1279 }
1280
1281 var (
1282 code int
1283 message string
1284 )
1285
1286 user := store.User{}
1287 query := bson.M{"devices.JWT": token}
1288 if err := restClient.userStore.FindUser(query, &user); err != nil {
1289 restClient.log.Errorf("getAllWalletsVerbose: restClient.userStore.FindUser: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
1290 }
1291
1292 switch currencyId {
1293 case currencies.Bitcoin:
1294 code = http.StatusOK
1295 message = http.StatusText(http.StatusOK)
1296 var av []AddressVerbose
1297
1298 // fetch wallet with concrete networkid currencyid and wallet index
1299 wallet := store.Wallet{}
1300 for _, w := range user.Wallets {
1301 if w.NetworkID == networkId && w.CurrencyID == currencyId && w.WalletIndex == walletIndex {
1302 wallet = w
1303 break
1304 }
1305 }
1306
1307 if len(wallet.Adresses) == 0 {
1308 restClient.log.Errorf("getAllWalletsVerbose: restClient.userStore.FindUser:\t[addr=%s]", c.Request.RemoteAddr)
1309 c.JSON(code, gin.H{
1310 "code": http.StatusBadRequest,
1311 "message": msgErrUserNotFound,
1312 "wallet": wv,
1313 })
1314 return
1315 }
1316 var pending bool
1317 for _, address := range wallet.Adresses {
1318 spOuts := getBTCAddressSpendableOutputs(address.Address, currencyId, networkId, restClient)
1319 for _, spOut := range spOuts {
1320 if spOut.TxStatus == store.TxStatusAppearedInMempoolIncoming {
1321 pending = true
1322 }
1323 }
1324 // TODO:
1325 _, sync := restClient.BTC.Resync.Load(address.Address)
1326
1327 av = append(av, AddressVerbose{
1328 LastActionTime: address.LastActionTime,
1329 Address: address.Address,
1330 AddressIndex: address.AddressIndex,
1331 Amount: int64(checkBTCAddressbalance(address.Address, currencyId, networkId, restClient)),
1332 SpendableOuts: spOuts,
1333 IsSyncing: sync,
1334 })
1335 }
1336 wv = append(wv, WalletVerbose{
1337 WalletIndex: wallet.WalletIndex,
1338 CurrencyID: wallet.CurrencyID,
1339 NetworkID: wallet.NetworkID,
1340 WalletName: wallet.WalletName,
1341 LastActionTime: wallet.LastActionTime,
1342 DateOfCreation: wallet.DateOfCreation,
1343 VerboseAddress: av,
1344 Pending: pending,
1345 })
1346 av = []AddressVerbose{}
1347
1348 case currencies.Ether:
1349 code = http.StatusOK
1350 message = http.StatusText(http.StatusOK)
1351
1352 var av []ETHAddressVerbose
1353
1354 query := bson.M{"devices.JWT": token}
1355 if err := restClient.userStore.FindUser(query, &user); err != nil {
1356 restClient.log.Errorf("getAllWalletsVerbose: restClient.userStore.FindUser: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
1357 }
1358
1359 // fetch wallet with concrete networkid currencyid and wallet index
1360 wallet := store.Wallet{}
1361 for _, w := range user.Wallets {
1362 if w.NetworkID == networkId && w.CurrencyID == currencyId && w.WalletIndex == walletIndex {
1363 wallet = w
1364 break
1365 }
1366 }
1367
1368 if len(wallet.Adresses) == 0 {
1369 restClient.log.Errorf("getAllWalletsVerbose: len(wallet.Adresses) == 0:\t[addr=%s]", c.Request.RemoteAddr)
1370 c.JSON(code, gin.H{
1371 "code": http.StatusBadRequest,
1372 "message": msgErrNoWallet,
1373 "wallet": wv,
1374 })
1375 return
1376 }
1377
1378 //TODO: make pending
1379 var pending bool
1380 var totalBalance string
1381 var pendingBalance string
1382 // var pendingAmount string
1383 var waletNonce int64
1384 for _, address := range wallet.Adresses {
1385 amount := ðpb.Balance{}
1386 nonce := ðpb.Nonce{}
1387
1388 var err error
1389 adr := ethpb.AddressToResync{
1390 Address: address.Address,
1391 }
1392
1393 switch networkId {
1394 case currencies.ETHTest:
1395 nonce, err = restClient.ETH.CliTest.EventGetAdressNonce(context.Background(), &adr)
1396 amount, err = restClient.ETH.CliTest.EventGetAdressBalance(context.Background(), &adr)
1397 case currencies.ETHMain:
1398 nonce, err = restClient.ETH.CliMain.EventGetAdressNonce(context.Background(), &adr)
1399 amount, err = restClient.ETH.CliMain.EventGetAdressBalance(context.Background(), &adr)
1400 default:
1401 c.JSON(code, gin.H{
1402 "code": http.StatusBadRequest,
1403 "message": msgErrMethodNotImplennted,
1404 "wallet": wv,
1405 })
1406 return
1407 }
1408
1409 if err != nil {
1410 restClient.log.Errorf("EventGetAdressNonce || EventGetAdressBalance: %v", err.Error())
1411 }
1412
1413 totalBalance = amount.GetBalance()
1414 pendingBalance = amount.GetPendingBalance()
1415 restClient.log.Warnf("\n Incoming node balance %v node pending balance %v", totalBalance, pendingBalance)
1416
1417 if totalBalance == pendingBalance {
1418 pendingBalance = "0"
1419 pending = false
1420 }
1421
1422 waletNonce = nonce.GetNonce()
1423 nonceFromDB := int64(0)
1424 userTxs := []store.TransactionETH{}
1425 err = restClient.userStore.GetAllWalletEthTransactions(user.UserID, currencyId, networkId, &userTxs)
1426 for _, tx := range userTxs {
1427 if tx.Status == store.TxStatusAppearedInMempoolIncoming {
1428 if totalBalance != pendingBalance {
1429 address.LastActionTime = time.Now().Unix()
1430 pending = true
1431 }
1432 }
1433 if int64(tx.Nonce) > nonceFromDB && tx.Status == store.TxStatusAppearedInBlockOutcoming || tx.Status == store.TxStatusAppearedInMempoolOutcoming {
1434 nonceFromDB = int64(tx.Nonce) + 1
1435 }
1436 }
1437
1438 if nonce.GetNonce() < nonceFromDB {
1439 restClient.log.Warnf("nonceFromDB")
1440 waletNonce = nonceFromDB + 2
1441 }
1442 restClient.log.Warnf("nonceFromDB n %v ndb %v nonce to user %v", nonce.GetNonce(), nonceFromDB+1, waletNonce)
1443
1444 // Check for transaction status from transaction history in case of that geth take a lot of time to procces address balances in inner
1445 // its a HACK we put a pending flag in case if we have pending txs in out txs history. In this case ballance will be callculated partly from tx hisotry.
1446 // NOT FROM NODE
1447 pendingBalanceBig, _ := new(big.Int).SetString(amount.GetPendingBalance(), 10)
1448 walletHistory := []store.TransactionETH{}
1449 err = restClient.userStore.GetAllWalletEthTransactions(user.UserID, wallet.CurrencyID, wallet.NetworkID, &walletHistory)
1450 for _, tx := range walletHistory {
1451 if tx.WalletIndex == wallet.WalletIndex {
1452 if (tx.Status == store.TxStatusAppearedInMempoolIncoming || tx.Status == store.TxStatusAppearedInMempoolOutcoming) && (tx.From == address.Address || tx.To == address.Address) {
1453 if tx.Status == store.TxStatusAppearedInMempoolIncoming && amount.GetBalance() == amount.GetPendingBalance() {
1454 inTxAmount, _ := new(big.Int).SetString(tx.Amount, 10)
1455 pendingBalanceBig := pendingBalanceBig.Add(pendingBalanceBig, inTxAmount)
1456 pendingBalance = pendingBalanceBig.String()
1457 restClient.log.Warnf("\n Incoming Wallet name %v pending fake balance %v", wallet.WalletName, pendingBalance)
1458 pending = true
1459 }
1460 if tx.Status == store.TxStatusAppearedInMempoolOutcoming && amount.GetBalance() == amount.GetPendingBalance() {
1461 pendingBalanceBigOld := pendingBalanceBig
1462 outTxAmount, _ := new(big.Int).SetString(tx.Amount, 10)
1463 pendingBalanceBig := pendingBalanceBig.Sub(pendingBalanceBig, outTxAmount)
1464 gasLimit := big.NewInt(tx.GasLimit)
1465 gasPrice := big.NewInt(tx.GasPrice)
1466 fee := new(big.Int).Mul(gasLimit, gasPrice)
1467 pendingBalanceBig = pendingBalanceBig.Sub(pendingBalanceBig, fee)
1468 pendingBalance = pendingBalanceBig.String()
1469 restClient.log.Warnf("%v - %v - %v = %v", pendingBalanceBigOld, outTxAmount, fee, pendingBalanceBig)
1470 restClient.log.Warnf("\n Outcoming Wallet name %v pending fake balance %v", wallet.WalletName, pendingBalance)
1471 }
1472 }
1473 }
1474 }
1475
1476 av = append(av, ETHAddressVerbose{
1477 LastActionTime: address.LastActionTime,
1478 Address: address.Address,
1479 AddressIndex: address.AddressIndex,
1480 Amount: totalBalance,
1481 Nonce: nonce.Nonce,
1482 })
1483
1484 }
1485
1486 wv = append(wv, WalletVerboseETH{
1487 WalletIndex: wallet.WalletIndex,
1488 CurrencyID: wallet.CurrencyID,
1489 NetworkID: wallet.NetworkID,
1490 WalletName: wallet.WalletName,
1491 LastActionTime: wallet.LastActionTime,
1492 DateOfCreation: wallet.DateOfCreation,
1493 Nonce: waletNonce,
1494 Balance: totalBalance,
1495 PendingBalance: pendingBalance,
1496 VerboseAddress: av,
1497 Pending: pending,
1498 })
1499
1500 av = []ETHAddressVerbose{}
1501 default:
1502 c.JSON(http.StatusBadRequest, gin.H{
1503 "code": http.StatusBadRequest,
1504 "message": msgErrChainIsNotImplemented,
1505 })
1506 return
1507 }
1508
1509 c.JSON(code, gin.H{
1510 "code": code,
1511 "message": message,
1512 "wallet": wv,
1513 })
1514 }
1515}
1516
1517type WalletVerbose struct {
1518 CurrencyID int `json:"currencyid"`
1519 NetworkID int `json:"networkid"`
1520 WalletIndex int `json:"walletindex"`
1521 WalletName string `json:"walletname"`
1522 LastActionTime int64 `json:"lastactiontime"`
1523 DateOfCreation int64 `json:"dateofcreation"`
1524 VerboseAddress []AddressVerbose `json:"addresses"`
1525 Pending bool `json:"pending"`
1526}
1527
1528type WalletVerboseETH struct {
1529 CurrencyID int `json:"currencyid"`
1530 NetworkID int `json:"networkid"`
1531 WalletIndex int `json:"walletindex"`
1532 WalletName string `json:"walletname"`
1533 LastActionTime int64 `json:"lastactiontime"`
1534 DateOfCreation int64 `json:"dateofcreation"`
1535 Nonce int64 `json:"nonce"`
1536 PendingBalance string `json:"pendingbalance"`
1537 Balance string `json:"balance"`
1538 VerboseAddress []ETHAddressVerbose `json:"addresses"`
1539 Pending bool `json:"pending"`
1540}
1541
1542type AddressVerbose struct {
1543 LastActionTime int64 `json:"lastactiontime"`
1544 Address string `json:"address"`
1545 AddressIndex int `json:"addressindex"`
1546 Amount int64 `json:"amount"`
1547 SpendableOuts []store.SpendableOutputs `json:"spendableoutputs,omitempty"`
1548 Nonce int64 `json:"nonce,omitempty"`
1549 IsSyncing bool `json:"issyncing"`
1550}
1551
1552type ETHAddressVerbose struct {
1553 LastActionTime int64 `json:"lastactiontime"`
1554 Address string `json:"address"`
1555 AddressIndex int `json:"addressindex"`
1556 Amount string `json:"amount"`
1557 SpendableOuts []store.SpendableOutputs `json:"spendableoutputs,omitempty"`
1558 Nonce int64 `json:"nonce,omitempty"`
1559}
1560
1561type StockExchangeRate struct {
1562 ExchangeName string `json:"exchangename"`
1563 FiatEquivalent int `json:"fiatequivalent"`
1564 TotalAmount int `json:"totalamount"`
1565}
1566
1567type TopIndex struct {
1568 CurrencyID int `json:"currencyid"`
1569 NetworkID int `json:"networkid"`
1570 TopIndex int `json:"topindex"`
1571}
1572
1573func findTopIndexes(walletsBTC []store.Wallet) []TopIndex {
1574 top := map[TopIndex]int{} // currency id -> topindex
1575 topIndex := []TopIndex{}
1576 for _, wallet := range walletsBTC {
1577 top[TopIndex{wallet.CurrencyID, wallet.NetworkID, 0}]++
1578 }
1579 for value, maxIndex := range top {
1580 topIndex = append(topIndex, TopIndex{
1581 CurrencyID: value.CurrencyID,
1582 NetworkID: value.NetworkID,
1583 TopIndex: maxIndex,
1584 })
1585 }
1586 return topIndex
1587}
1588
1589func fetchUndeletedWallets(walletsBTC []store.Wallet) []store.Wallet {
1590 //func fetchUndeletedWallets(wallets []store.Wallet) []store.Wallet {
1591 okWalletsBTC := []store.Wallet{}
1592
1593 for _, wallet := range walletsBTC {
1594 if wallet.Status == store.WalletStatusOK {
1595 okWalletsBTC = append(okWalletsBTC, wallet)
1596 }
1597 }
1598
1599 return okWalletsBTC
1600}
1601
1602func (restClient *RestClient) getAllWalletsVerbose() gin.HandlerFunc {
1603 return func(c *gin.Context) {
1604 var wv []interface{}
1605 //var wv []WalletVerbose
1606 token, err := getToken(c)
1607 if err != nil {
1608 c.JSON(http.StatusBadRequest, gin.H{
1609 "code": http.StatusBadRequest,
1610 "message": msgErrHeaderError,
1611 })
1612 return
1613 }
1614 var (
1615 code int
1616 message string
1617 )
1618 user := store.User{}
1619 query := bson.M{"devices.JWT": token}
1620
1621 if err := restClient.userStore.FindUser(query, &user); err != nil {
1622 restClient.log.Errorf("getAllWalletsVerbose: restClient.userStore.FindUser: %s\t[addr=%s]", err.Error(), c.Request.RemoteAddr)
1623 c.JSON(code, gin.H{
1624 "code": http.StatusBadRequest,
1625 "message": msgErrUserNotFound,
1626 "wallets": wv,
1627 })
1628 return
1629 }
1630
1631 topIndexes := findTopIndexes(user.Wallets)
1632
1633 code = http.StatusOK
1634 message = http.StatusText(http.StatusOK)
1635
1636 okWallets := fetchUndeletedWallets(user.Wallets)
1637
1638 userTxs := []store.MultyTX{}
1639
1640 for _, wallet := range okWallets {
1641
1642 switch wallet.CurrencyID {
1643 case currencies.Bitcoin:
1644 var av []AddressVerbose
1645 var pending bool
1646 for _, address := range wallet.Adresses {
1647 spOuts := getBTCAddressSpendableOutputs(address.Address, wallet.CurrencyID, wallet.NetworkID, restClient)
1648
1649 //all user txs
1650 err = restClient.userStore.GetAllWalletTransactions(user.UserID, wallet.CurrencyID, wallet.NetworkID, &userTxs)
1651 if err != nil {
1652 //empty history
1653 }
1654
1655 for _, tx := range userTxs {
1656 if len(tx.TxAddress) > 0 {
1657 if tx.TxAddress[0] == address.Address {
1658 if tx.TxStatus == store.TxStatusAppearedInMempoolIncoming || tx.TxStatus == store.TxStatusAppearedInMempoolOutcoming {
1659 pending = true
1660 }
1661 }
1662 }
1663 }
1664
1665 // restClient.BTC.ResyncM.Lock()
1666 // re := *restClient.BTC.Resync
1667 // restClient.BTC.ResyncM.Unlock()
1668 _, sync := restClient.BTC.Resync.Load(address.Address)
1669
1670 // sync := re[address.Address]
1671
1672 av = append(av, AddressVerbose{
1673 LastActionTime: address.LastActionTime,
1674 Address: address.Address,
1675 AddressIndex: address.AddressIndex,
1676 Amount: int64(checkBTCAddressbalance(address.Address, wallet.CurrencyID, wallet.NetworkID, restClient)),
1677 SpendableOuts: spOuts,
1678 IsSyncing: sync,
1679 })
1680
1681 }
1682
1683 wv = append(wv, WalletVerbose{
1684 WalletIndex: wallet.WalletIndex,
1685 CurrencyID: wallet.CurrencyID,
1686 NetworkID: wallet.NetworkID,
1687 WalletName: wallet.WalletName,
1688 LastActionTime: wallet.LastActionTime,
1689 DateOfCreation: wallet.DateOfCreation,
1690 VerboseAddress: av,
1691 Pending: pending,
1692 })
1693 av = []AddressVerbose{}
1694 userTxs = []store.MultyTX{}
1695 case currencies.Ether:
1696 var av []ETHAddressVerbose
1697 var pending bool
1698 var walletNonce int64
1699
1700 var totalBalance string
1701 var pendingBalance string
1702 // var pendingAmount string
1703
1704 for _, address := range wallet.Adresses {
1705 amount := ðpb.Balance{}
1706 nonce := ðpb.Nonce{}
1707 // blockHeight := ðpb.BlockHeight{}
1708
1709 var err error
1710 adr := ethpb.AddressToResync{
1711 Address: address.Address,
1712 }
1713
1714 switch wallet.NetworkID {
1715 case currencies.ETHTest:
1716 nonce, err = restClient.ETH.CliTest.EventGetAdressNonce(context.Background(), &adr)
1717 amount, err = restClient.ETH.CliTest.EventGetAdressBalance(context.Background(), &adr)
1718 // blockHeight, err = restClient.ETH.CliMain.EventGetBlockHeight(context.Background(), &adr)
1719 case currencies.ETHMain:
1720 nonce, err = restClient.ETH.CliMain.EventGetAdressNonce(context.Background(), &adr)
1721 amount, err = restClient.ETH.CliMain.EventGetAdressBalance(context.Background(), &adr)
1722 // blockHeight, err = restClient.ETH.CliMain.EventGetBlockHeight(context.Background())
1723 default:
1724 c.JSON(code, gin.H{
1725 "code": http.StatusBadRequest,
1726 "message": msgErrMethodNotImplennted,
1727 "wallet": wv,
1728 })
1729 return
1730 }
1731
1732 if err != nil {
1733 restClient.log.Errorf("EventGetAdressNonce || EventGetAdressBalance: %v", err.Error())
1734 }
1735
1736 totalBalance = amount.GetBalance()
1737 pendingBalance = amount.GetPendingBalance()
1738
1739 userTxs := []store.TransactionETH{}
1740 err = restClient.userStore.GetAllWalletEthTransactions(user.UserID, wallet.CurrencyID, wallet.NetworkID, &userTxs)
1741
1742 history := []store.TransactionETH{}
1743 for _, tx := range userTxs {
1744 if tx.WalletIndex == wallet.WalletIndex {
1745 history = append(history, tx)
1746 }
1747 }
1748 restClient.log.Warnf("\n Incoming node balance %v node pending balance %v", totalBalance, pendingBalance)
1749
1750 if totalBalance == pendingBalance {
1751 pendingBalance = "0"
1752 pending = false
1753 }
1754
1755 walletNonce = nonce.GetNonce()
1756 nonceFromDB := int64(0)
1757 for _, tx := range userTxs {
1758 if tx.Status == store.TxStatusAppearedInMempoolIncoming {
1759 if totalBalance != pendingBalance {
1760 restClient.log.Warnf(" perding true Incoming")
1761 pending = true
1762 }
1763 }
1764 if int64(tx.Nonce) > nonceFromDB && tx.Status == store.TxStatusAppearedInBlockOutcoming || tx.Status == store.TxStatusAppearedInMempoolOutcoming {
1765 nonceFromDB = int64(tx.Nonce) + 1
1766 }
1767 }
1768
1769 if nonce.GetNonce() < nonceFromDB {
1770 restClient.log.Warnf("nonceFromDB")
1771 walletNonce = nonceFromDB + 2
1772 }
1773 restClient.log.Warnf("nonceFromDB n %v ndb %v nonce to user %v", nonce.GetNonce(), nonceFromDB+1, walletNonce)
1774
1775 pendingBalanceBig, _ := new(big.Int).SetString(amount.GetPendingBalance(), 10)
1776 walletHistory := []store.TransactionETH{}
1777 err = restClient.userStore.GetAllWalletEthTransactions(user.UserID, wallet.CurrencyID, wallet.NetworkID, &walletHistory)
1778 for _, tx := range walletHistory {
1779 if tx.WalletIndex == wallet.WalletIndex {
1780 if (tx.Status == store.TxStatusAppearedInMempoolIncoming || tx.Status == store.TxStatusAppearedInMempoolOutcoming) && (tx.From == address.Address || tx.To == address.Address) {
1781 if tx.Status == store.TxStatusAppearedInMempoolIncoming && amount.GetBalance() == amount.GetPendingBalance() {
1782 inTxAmount, _ := new(big.Int).SetString(tx.Amount, 10)
1783 pendingBalanceBig := pendingBalanceBig.Add(pendingBalanceBig, inTxAmount)
1784 pendingBalance = pendingBalanceBig.String()
1785 restClient.log.Warnf("\n Incoming Wallet name %v pending fake balance %v", wallet.WalletName, pendingBalance)
1786 pending = true
1787 restClient.log.Warnf(" perding true Incoming fake ")
1788 }
1789 if tx.Status == store.TxStatusAppearedInMempoolOutcoming && amount.GetBalance() == amount.GetPendingBalance() {
1790 pendingBalanceBigOld := pendingBalanceBig
1791 outTxAmount, _ := new(big.Int).SetString(tx.Amount, 10)
1792 pendingBalanceBig := pendingBalanceBig.Sub(pendingBalanceBig, outTxAmount)
1793 gasLimit := big.NewInt(tx.GasLimit)
1794 gasPrice := big.NewInt(tx.GasPrice)
1795 fee := new(big.Int).Mul(gasLimit, gasPrice)
1796 pendingBalanceBig = pendingBalanceBig.Sub(pendingBalanceBig, fee)
1797 pendingBalance = pendingBalanceBig.String()
1798 restClient.log.Warnf("%v - %v - %v = %v", pendingBalanceBigOld, outTxAmount, fee, pendingBalanceBig)
1799 restClient.log.Warnf("\n Outcoming Wallet name %v pending fake balance %v", wallet.WalletName, pendingBalance)
1800 }
1801 }
1802 }
1803 }
1804
1805 av = append(av, ETHAddressVerbose{
1806 LastActionTime: address.LastActionTime,
1807 Address: address.Address,
1808 AddressIndex: address.AddressIndex,
1809 Amount: totalBalance,
1810 Nonce: nonce.Nonce,
1811 })
1812 }
1813 wv = append(wv, WalletVerboseETH{
1814 WalletIndex: wallet.WalletIndex,
1815 CurrencyID: wallet.CurrencyID,
1816 NetworkID: wallet.NetworkID,
1817 Balance: totalBalance,
1818 PendingBalance: pendingBalance,
1819 Nonce: walletNonce,
1820 WalletName: wallet.WalletName,
1821 LastActionTime: wallet.LastActionTime,
1822 DateOfCreation: wallet.DateOfCreation,
1823 VerboseAddress: av,
1824 Pending: pending,
1825 })
1826 av = []ETHAddressVerbose{}
1827 default:
1828
1829 }
1830
1831 }
1832
1833 c.JSON(code, gin.H{
1834 "code": code,
1835 "message": message,
1836 "wallets": wv,
1837 "topindexes": topIndexes,
1838 })
1839
1840 }
1841}
1842
1843func (restClient *RestClient) getWalletTransactionsHistory() gin.HandlerFunc {
1844 return func(c *gin.Context) {
1845 var walletTxs []store.MultyTX
1846 token, err := getToken(c)
1847 if err != nil {
1848 c.JSON(http.StatusBadRequest, gin.H{
1849 "code": http.StatusBadRequest,
1850 "message": msgErrHeaderError,
1851 })
1852 return
1853 }
1854
1855 walletIndex, err := strconv.Atoi(c.Param("walletindex"))
1856 restClient.log.Debugf("getWalletVerbose [%d] \t[walletindexr=%s]", walletIndex, c.Request.RemoteAddr)
1857 if err != nil {
1858 restClient.log.Errorf("getWalletVerbose: non int wallet index:[%d] %s \t[addr=%s]", walletIndex, err.Error(), c.Request.RemoteAddr)
1859 c.JSON(http.StatusBadRequest, gin.H{
1860 "code": http.StatusBadRequest,
1861 "message": msgErrDecodeWalletIndexErr,
1862 "history": walletTxs,
1863 })
1864 return
1865 }
1866
1867 currencyId, err := strconv.Atoi(c.Param("currencyid"))
1868 restClient.log.Debugf("getWalletVerbose [%d] \t[currencyId=%s]", currencyId, c.Request.RemoteAddr)
1869 if err != nil {
1870 restClient.log.Errorf("getWalletVerbose: non int currency id:[%d] %s \t[addr=%s]", currencyId, err.Error(), c.Request.RemoteAddr)
1871 c.JSON(http.StatusBadRequest, gin.H{
1872 "code": http.StatusBadRequest,
1873 "message": msgErrDecodeCurIndexErr,
1874 })
1875 return
1876 }
1877
1878 networkid, err := strconv.Atoi(c.Param("networkid"))
1879 restClient.log.Debugf("getWalletVerbose [%d] \t[networkid=%s]", networkid, c.Request.RemoteAddr)
1880 if err != nil {
1881 restClient.log.Errorf("getWalletVerbose: non int networkid index:[%d] %s \t[addr=%s]", networkid, err.Error(), c.Request.RemoteAddr)
1882 c.JSON(http.StatusBadRequest, gin.H{
1883 "code": http.StatusBadRequest,
1884 "message": msgErrDecodenetworkidErr,
1885 })
1886 return
1887 }
1888
1889 user := store.User{}
1890 sel := bson.M{"devices.JWT": token}
1891 err = restClient.userStore.FindUser(sel, &user)
1892 if err != nil {
1893 c.JSON(http.StatusBadRequest, gin.H{
1894 "code": http.StatusBadRequest,
1895 "message": msgErrUserNotFound,
1896 "history": walletTxs,
1897 })
1898 return
1899 }
1900
1901 switch currencyId {
1902 case currencies.Bitcoin:
1903
1904 var blockHeight int64
1905 switch networkid {
1906 case currencies.Test:
1907 resp, err := restClient.BTC.CliTest.EventGetBlockHeight(context.Background(), &btcpb.Empty{})
1908 if err != nil {
1909 restClient.log.Errorf("getWalletTransactionsHistory: restClient.BTC.CliTest.EventGetBlockHeight %s \t[addr=%s]", err.Error(), c.Request.RemoteAddr)
1910 c.JSON(http.StatusInternalServerError, gin.H{
1911 "code": http.StatusInternalServerError,
1912 "message": http.StatusText(http.StatusInternalServerError),
1913 })
1914 return
1915 }
1916 blockHeight = resp.Height
1917 case currencies.Main:
1918 resp, err := restClient.BTC.CliMain.EventGetBlockHeight(context.Background(), &btcpb.Empty{})
1919 if err != nil {
1920 restClient.log.Errorf("getWalletTransactionsHistory: restClient.BTC.CliTest.EventGetBlockHeight %s \t[addr=%s]", err.Error(), c.Request.RemoteAddr)
1921 c.JSON(http.StatusInternalServerError, gin.H{
1922 "code": http.StatusInternalServerError,
1923 "message": http.StatusText(http.StatusInternalServerError),
1924 })
1925 return
1926 }
1927 blockHeight = resp.Height
1928 }
1929
1930 userTxs := []store.MultyTX{}
1931 err = restClient.userStore.GetAllWalletTransactions(user.UserID, currencyId, networkid, &userTxs)
1932 if err != nil {
1933 c.JSON(http.StatusBadRequest, gin.H{
1934 "code": http.StatusBadRequest,
1935 "message": msgErrTxHistory,
1936 "history": walletTxs,
1937 })
1938 return
1939 }
1940
1941 walletAddresses := []string{}
1942 for _, wallet := range user.Wallets {
1943 if wallet.WalletIndex == walletIndex {
1944 for _, addresses := range wallet.Adresses {
1945 walletAddresses = append(walletAddresses, addresses.Address)
1946 }
1947 }
1948 }
1949
1950 // TODO: fix this logic same wallet to samewallet tx
1951
1952 for _, address := range walletAddresses {
1953 for _, tx := range userTxs {
1954 if len(tx.TxAddress) > 0 {
1955 if tx.TxAddress[0] == address {
1956 // restClient.log.Infof("---------address= %s, txid= %s", address, tx.TxID)
1957 var isTheSameWallet = false
1958 for _, input := range tx.WalletsInput {
1959 if walletIndex == input.WalletIndex {
1960 isTheSameWallet = true
1961 }
1962 }
1963
1964 for _, output := range tx.WalletsOutput {
1965 if walletIndex == output.WalletIndex {
1966 isTheSameWallet = true
1967 }
1968 }
1969
1970 if isTheSameWallet {
1971 walletTxs = append(walletTxs, tx)
1972 }
1973 }
1974 }
1975 }
1976 }
1977
1978 /*
1979 for _, tx := range userTxs {
1980 //New Logic
1981 var isTheSameWallet = false
1982 for _, input := range tx.WalletsInput {
1983 if walletIndex == input.WalletIndex {
1984 isTheSameWallet = true
1985 }
1986 }
1987 for _, output := range tx.WalletsOutput {
1988 if walletIndex == output.WalletIndex {
1989 isTheSameWallet = true
1990 }
1991 }
1992 if isTheSameWallet {
1993 walletTxs = append(walletTxs, tx)
1994 }
1995 }
1996 */
1997
1998 for i := 0; i < len(walletTxs); i++ {
1999 if walletTxs[i].BlockHeight == -1 {
2000 walletTxs[i].Confirmations = 0
2001 } else {
2002 walletTxs[i].Confirmations = int(blockHeight-walletTxs[i].BlockHeight) + 1
2003 }
2004 }
2005
2006 c.JSON(http.StatusOK, gin.H{
2007 "code": http.StatusOK,
2008 "message": http.StatusText(http.StatusOK),
2009 "history": walletTxs,
2010 })
2011 return
2012
2013 case currencies.Ether:
2014 var blockHeight int64
2015
2016 switch networkid {
2017 case currencies.ETHTest:
2018 resp, err := restClient.ETH.CliTest.EventGetBlockHeight(context.Background(), ðpb.Empty{})
2019 if err != nil {
2020 restClient.log.Errorf("getWalletTransactionsHistory: restClient.BTC.CliTest.EventGetBlockHeight %s \t[addr=%s]", err.Error(), c.Request.RemoteAddr)
2021 c.JSON(http.StatusInternalServerError, gin.H{
2022 "code": http.StatusInternalServerError,
2023 "message": http.StatusText(http.StatusInternalServerError),
2024 })
2025 return
2026 }
2027 blockHeight = resp.Height
2028 case currencies.ETHMain:
2029 resp, err := restClient.ETH.CliMain.EventGetBlockHeight(context.Background(), ðpb.Empty{})
2030 if err != nil {
2031 restClient.log.Errorf("getWalletTransactionsHistory: restClient.BTC.CliTest.EventGetBlockHeight %s \t[addr=%s]", err.Error(), c.Request.RemoteAddr)
2032 c.JSON(http.StatusInternalServerError, gin.H{
2033 "code": http.StatusInternalServerError,
2034 "message": http.StatusText(http.StatusInternalServerError),
2035 })
2036 return
2037 }
2038 blockHeight = resp.Height
2039 }
2040 userTxs := []store.TransactionETH{}
2041 err = restClient.userStore.GetAllWalletEthTransactions(user.UserID, currencyId, networkid, &userTxs)
2042 if err != nil {
2043 c.JSON(http.StatusBadRequest, gin.H{
2044 "code": http.StatusBadRequest,
2045 "message": msgErrTxHistory,
2046 "history": walletTxs,
2047 })
2048 return
2049 }
2050
2051 for i := 0; i < len(userTxs); i++ {
2052 if userTxs[i].BlockHeight == -1 {
2053 userTxs[i].Confirmations = 0
2054 } else {
2055 userTxs[i].Confirmations = int(blockHeight-userTxs[i].BlockHeight) + 1
2056 }
2057 }
2058
2059 history := []store.TransactionETH{}
2060 for _, tx := range userTxs {
2061 if tx.WalletIndex == walletIndex {
2062 history = append(history, tx)
2063 }
2064 }
2065
2066 c.JSON(http.StatusOK, gin.H{
2067 "code": http.StatusOK,
2068 "message": http.StatusText(http.StatusOK),
2069 "history": history,
2070 })
2071 return
2072
2073 default:
2074 c.JSON(http.StatusBadRequest, gin.H{
2075 "code": http.StatusBadRequest,
2076 "message": msgErrChainIsNotImplemented,
2077 "history": walletTxs,
2078 })
2079 return
2080 }
2081
2082 }
2083}
2084
2085type TxHistory struct {
2086 TxID string `json:"txid"`
2087 TxHash string `json:"txhash"`
2088 TxOutScript string `json:"txoutscript"`
2089 TxAddress string `json:"address"`
2090 TxStatus int `json:"txstatus"`
2091 TxOutAmount int64 `json:"txoutamount"`
2092 TxOutID int `json:"txoutid"`
2093 WalletIndex int `json:"walletindex"`
2094 BlockTime int64 `json:"blocktime"`
2095 BlockHeight int64 `json:"blockheight"`
2096 TxFee int64 `json:"txfee"`
2097 MempoolTime int64 `json:"mempooltime"`
2098 BtcToUsd float64 `json:"btctousd"`
2099 TxInputs []store.AddresAmount `json:"txinputs"`
2100 TxOutputs []store.AddresAmount `json:"txoutputs"`
2101}
2102
2103func (restClient *RestClient) resyncWallet() gin.HandlerFunc {
2104 return func(c *gin.Context) {
2105 token, err := getToken(c)
2106 if err != nil {
2107 c.JSON(http.StatusBadRequest, gin.H{
2108 "code": http.StatusBadRequest,
2109 "message": msgErrHeaderError,
2110 })
2111 return
2112 }
2113
2114 walletIndex, err := strconv.Atoi(c.Param("walletindex"))
2115 restClient.log.Debugf("getWalletVerbose [%d] \t[walletindexr=%s]", walletIndex, c.Request.RemoteAddr)
2116 if err != nil {
2117 restClient.log.Errorf("resyncWallet: non int wallet index:[%d] %s \t[addr=%s]", walletIndex, err.Error(), c.Request.RemoteAddr)
2118 c.JSON(http.StatusBadRequest, gin.H{
2119 "code": http.StatusBadRequest,
2120 "message": msgErrDecodeWalletIndexErr,
2121 })
2122 return
2123 }
2124
2125 currencyID, err := strconv.Atoi(c.Param("currencyid"))
2126 restClient.log.Debugf("getWalletVerbose [%d] \t[currencyId=%s]", currencyID, c.Request.RemoteAddr)
2127 if err != nil {
2128 restClient.log.Errorf("resyncWallet: non int currency id:[%d] %s \t[addr=%s]", currencyID, err.Error(), c.Request.RemoteAddr)
2129 c.JSON(http.StatusBadRequest, gin.H{
2130 "code": http.StatusBadRequest,
2131 "message": msgErrDecodeCurIndexErr,
2132 })
2133 return
2134 }
2135
2136 networkID, err := strconv.Atoi(c.Param("networkid"))
2137 restClient.log.Debugf("getWalletVerbose [%d] \t[networkid=%s]", networkID, c.Request.RemoteAddr)
2138 if err != nil {
2139 restClient.log.Errorf("resyncWallet: non int networkid index:[%d] %s \t[addr=%s]", networkID, err.Error(), c.Request.RemoteAddr)
2140 c.JSON(http.StatusBadRequest, gin.H{
2141 "code": http.StatusBadRequest,
2142 "message": msgErrDecodenetworkidErr,
2143 })
2144 return
2145 }
2146
2147 user := store.User{}
2148 sel := bson.M{"devices.JWT": token}
2149 err = restClient.userStore.FindUser(sel, &user)
2150 if err != nil {
2151 c.JSON(http.StatusBadRequest, gin.H{
2152 "code": http.StatusBadRequest,
2153 "message": msgErrUserNotFound,
2154 })
2155 return
2156 }
2157
2158 walletToResync := store.Wallet{}
2159 for _, wallet := range user.Wallets {
2160 if wallet.CurrencyID == currencyID && wallet.NetworkID == networkID && wallet.WalletIndex == walletIndex {
2161 walletToResync = wallet
2162 }
2163 }
2164
2165 if len(walletToResync.Adresses) == 0 {
2166 c.JSON(http.StatusBadRequest, gin.H{
2167 "code": http.StatusBadRequest,
2168 "message": msgErrUserHaveNoTxs,
2169 })
2170 return
2171 }
2172
2173 switch currencyID {
2174 case currencies.Bitcoin:
2175 if networkID == currencies.Main {
2176 for _, address := range walletToResync.Adresses {
2177 restClient.BTC.CliMain.EventResyncAddress(context.Background(), &btcpb.AddressToResync{
2178 Address: address.Address,
2179 UserID: user.UserID,
2180 WalletIndex: int32(walletIndex),
2181 AddressIndex: int32(address.AddressIndex),
2182 })
2183 err := restClient.userStore.DeleteHistory(currencyID, networkID, address.Address)
2184 if err != nil {
2185 restClient.log.Errorf("resyncWallet case currencies.Bitcoin:Main: %v", err.Error())
2186 }
2187 }
2188 }
2189
2190 if networkID == currencies.Test {
2191 for _, address := range walletToResync.Adresses {
2192 restClient.BTC.CliTest.EventResyncAddress(context.Background(), &btcpb.AddressToResync{
2193 Address: address.Address,
2194 UserID: user.UserID,
2195 WalletIndex: int32(walletIndex),
2196 AddressIndex: int32(address.AddressIndex),
2197 })
2198 err := restClient.userStore.DeleteHistory(currencyID, networkID, address.Address)
2199 if err != nil {
2200 restClient.log.Errorf("resyncWallet case currencies.Bitcoin:Test: %v", err.Error())
2201 }
2202 }
2203
2204 }
2205 case currencies.Ether:
2206 if networkID == currencies.ETHMain {
2207
2208 }
2209
2210 if networkID == currencies.ETHTest {
2211
2212 }
2213 }
2214
2215 }
2216}
2217
2218func (restClient *RestClient) changellyListCurrencies() gin.HandlerFunc {
2219 return func(c *gin.Context) {
2220 apiUrl := "https://api.changelly.com"
2221 apiKey := "8015e09ba78243ad889db470ec48fed4"
2222 apiSecret := "712bfcf899dd235b0af1d66922d5962e8c85a909635f838688a38b5f12c4d03a"
2223 cr := ChangellyReqest{
2224 JsonRpc: "2.0",
2225 ID: 1,
2226 Method: "getCurrencies",
2227 Params: []string{},
2228 }
2229 bs, err := json.Marshal(cr)
2230 if err != nil {
2231 restClient.log.Errorf("changellyListCurrencies: json.Marshal: %s \t[addr=%s]", err.Error(), c.Request.RemoteAddr)
2232 //
2233 return
2234 }
2235
2236 sign := ComputeHmac512(bs, apiSecret)
2237 req, err := http.NewRequest("GET", apiUrl, nil)
2238 if err != nil {
2239 restClient.log.Errorf("changellyListCurrencies: http.NewRequest: %s \t[addr=%s]", err.Error(), c.Request.RemoteAddr)
2240 //
2241 return
2242 }
2243 req.Header.Set("Content-Type", "application/json")
2244 req.Header.Set("api-key", apiKey)
2245 req.Header.Set("sign", sign)
2246
2247 client := &http.Client{}
2248 resp, err := client.Do(req)
2249 if err != nil {
2250 restClient.log.Errorf("changellyListCurrencies: http.Client.Do: %s \t[addr=%s]", err.Error(), c.Request.RemoteAddr)
2251 //
2252 return
2253 }
2254 defer resp.Body.Close()
2255 body, _ := ioutil.ReadAll(resp.Body)
2256 c.JSON(http.StatusOK, gin.H{
2257 "code": resp.StatusCode,
2258 "message": string(body),
2259 })
2260 }
2261}
2262
2263func ComputeHmac512(message []byte, secret string) string {
2264 key := []byte(secret)
2265 h := hmac.New(sha512.New, key)
2266 h.Write(message)
2267 return base64.StdEncoding.EncodeToString(h.Sum(nil))
2268}
2269
2270type ChangellyReqest struct {
2271 JsonRpc string `json:"jsonrpc"`
2272 ID int `json:"id"`
2273 Method string `json:"method"`
2274 Params []string `json:"params"`
2275}