From ea20251d0793f66132f51181796069336fb770d8 Mon Sep 17 00:00:00 2001 From: Chakib Benziane Date: Wed, 3 Apr 2019 20:18:51 +0200 Subject: [PATCH] use lnd grpc (later sphinx), wip polling --- .dockerignore | 3 + api/handlers.go | 17 ++--- btc/price.go | 77 +++++++++++++++++++ config/vars.go | 19 +++++ docker-compose.yml | 20 ++++- go.mod | 8 +- go.sum | 163 +++++++++++++++++++++++++++++++++++++++- ln/api.go | 68 ++++++++++++----- ln/invoice.go | 51 ++++++++++++- lndrpc/commands.go | 99 ++++++++++++++++++++++++ lndrpc/grpc.go | 108 ++++++++++++++++++++++++++ main.go | 3 - storage/upload_ctrl.go | 2 +- storage/upload_model.go | 4 +- utils/http.go | 22 ++++++ web/src/Pay.vue | 6 +- 16 files changed, 622 insertions(+), 48 deletions(-) create mode 100644 .dockerignore create mode 100644 btc/price.go create mode 100644 lndrpc/commands.go create mode 100644 lndrpc/grpc.go create mode 100644 utils/http.go diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6107594 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +web +bit4sat +file-storage diff --git a/api/handlers.go b/api/handlers.go index 9e65b8c..e1a6ca5 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -38,14 +38,13 @@ func sessionHandler(c *gin.Context) { return } - if invoice.Id != "" { - c.JSON(http.StatusOK, gin.H{ - "uploadId": lastUp, - "status": upStatus, - "invoice": invoice, - }) - return - } + // if invoice is not empty + c.JSON(http.StatusOK, gin.H{ + "uploadId": lastUp, + "status": upStatus, + "invoice": invoice, + }) + return } @@ -72,7 +71,7 @@ func invoiceCbHandler(c *gin.Context) { // get upload id related to invoice var uploadId string - invoiceUploadKey := fmt.Sprintf("invoice_%s_upload", invoice.Id) + invoiceUploadKey := fmt.Sprintf("invoice_%s_upload", invoice.RHash) err := db.DB.Redis.Do(radix.FlatCmd(&uploadId, "GET", invoiceUploadKey)) if err != nil { diff --git a/btc/price.go b/btc/price.go new file mode 100644 index 0000000..7bfabd6 --- /dev/null +++ b/btc/price.go @@ -0,0 +1,77 @@ +package btc + +import ( + "encoding/json" + "errors" + "fmt" + "log" + "math" + "net/http" + "net/url" + + "git.sp4ke.com/sp4ke/bit4sat/config" +) + +const ( + BitcoinAverageAPI = "https://apiv2.bitcoinaverage.com" + BTCMsatRatio = float64(100000000000) +) + +var FixedRates = map[string]float64{ + "BTC": 1, +} + +func ToMsat(currency string, amount float64) (int64, error) { + rate, err := getRate(currency) + if err != nil { + return -1, err + } + + return int64(math.Round((amount / rate) * BTCMsatRatio)), nil +} + +func getRate(currency string) (float64, error) { + pairName := "BTC" + currency + + reqParams := url.Values{} + reqParams.Set("crypto", "BTC") + reqParams.Set("fiat", currency) + + reqUri := fmt.Sprintf("%s/indices/global/ticker/short?%s", + BitcoinAverageAPI, reqParams.Encode()) + + // TODO: remove on prod and use normal client + // Used for connectivity problems + proxyUrl, err := url.Parse(config.HttpProxy) + if err != nil { + log.Fatal(err) + } + + client := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyURL(proxyUrl), + }, + } + + resp, err := client.Get(reqUri) + if err != nil { + return -1, err + } + + var data map[string]interface{} + + dec := json.NewDecoder(resp.Body) + err = dec.Decode(&data) + if err != nil { + log.Fatal(err) + } + + pair := data[pairName] + + price, ok := pair.(map[string]interface{})["last"].(float64) + if !ok { + return -1, errors.New("bitcoinaverage parsing result") + } + + return price, nil +} diff --git a/config/vars.go b/config/vars.go index 900b81d..5ed3402 100644 --- a/config/vars.go +++ b/config/vars.go @@ -19,6 +19,14 @@ var ( SessionSecret, LnChargeApi, LnChargeToken, + LndGrpcHost, + LndGrpcPort, + LndCertPath, + LndAdminMacaroonPath, + LockMacaroonIp, + + HttpProxy, + SqlDbHost, SqlDbUser, SqlDbPass string @@ -45,5 +53,16 @@ func init() { flag.StringVar(&LnChargeApi, "ln-charge-api", "", "ln charge api endpoint") flag.StringVar(&LnChargeToken, "ln-charge-token", "", "ln charge api token") + // LND + flag.StringVar(&LndGrpcHost, "lnd-grpc-host", "", "lnd host address") + flag.StringVar(&LndGrpcPort, "lnd-grpc-port", "", "lnd port address") + flag.StringVar(&LndCertPath, "lnd-cert-path", "", "lnd tls cert path") + flag.StringVar(&LndAdminMacaroonPath, "lnd-macaroon-admin-path", "", "admin macaroon path") + flag.StringVar(&LockMacaroonIp, "lock-macaroon-ip", "", "lock macaroon to ip address") + + flag.StringVar(&HttpProxy, "http-proxy", "", "http proxy for clients") + + //log.Printf("locking macaroon to ip %s", LockMacaroonIp) + flag.Parse() } diff --git a/docker-compose.yml b/docker-compose.yml index ad16e5d..5af3a94 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,13 +9,18 @@ volumes: #maria-conf: services: - app: + api: image: sp4ke/bit4sat container_name: bit4sat-api - build: ./docker + build: + context: . + dockerfile: ./docker/Dockerfile + environment: - GO111MODULE=on - API_HOST=bit4sat-api + - LND_GRPC_HOST=lnd + - LND_GRPC_PORT=10009 - BIT4SAT_STORAGE_PATH=/storage - GOPATH=/go - SQL_DB_HOST=postgres @@ -24,6 +29,10 @@ services: - LN_CHARGE_API=ln-charge-test:9112 - LN_CHARGE_TOKEN=3emU3Fy8VasHCzMaMXHSVJYpQSqH3yXQj8N5cQFBbq3botrudJuR7zQkBBmFSbAmgXs9GD4j4U3J4R2sMfgqPo8q - SESSION_SECRET=Ai7fCy36UE5cb9wcmdAxxRXwYyQDsDMr6rYocA6Eava7pdiB29EusLbb9sTYWS1e + - GRPC_SSL_CIPHER_SUITES="HIGH+ECDSA" + + # Used in case of ssl problems + - HTTP_PROXY=http://tinyproxy:8888 #deploy: #replicas: 1 @@ -33,8 +42,8 @@ services: volumes: - $PWD:/src - #- gocache:/go - - /fastData/go:/go + - gocache:/go + #- /fastData/go:/go #- ./db-storage:/sqlite - file-storage:/storage @@ -42,6 +51,7 @@ services: networks: - btc-test-overlay + - btc-overlay - default #maria: @@ -80,3 +90,5 @@ services: networks: btc-test-overlay: external: true + btc-overlay: + external: true diff --git a/go.mod b/go.mod index d376abd..07e4237 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.12 require ( github.com/gin-contrib/cors v0.0.0-20190301062745-f9e10995c85a - github.com/gin-contrib/secure v0.0.0-20190301062601-f9a5befa6106 github.com/gin-contrib/sessions v0.0.0-20190226023029-1532893d996f github.com/gin-gonic/gin v1.3.0 github.com/go-sql-driver/mysql v1.4.1 // indirect @@ -12,8 +11,9 @@ require ( github.com/gorilla/websocket v1.4.0 github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.0.0 + github.com/lightningnetwork/lnd v0.5.2-beta github.com/mattn/go-isatty v0.0.7 // indirect - github.com/mattn/go-sqlite3 v1.10.0 + github.com/mattn/go-sqlite3 v1.10.0 // indirect github.com/mediocregopher/radix/v3 v3.2.3 github.com/namsral/flag v1.7.4-pre github.com/segmentio/ksuid v1.0.2 @@ -22,5 +22,9 @@ require ( github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 // indirect golang.org/x/net v0.0.0-20190322120337-addf6b3196f6 golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc // indirect + golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 // indirect google.golang.org/appengine v1.5.0 // indirect + google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 // indirect + google.golang.org/grpc v1.19.1 + gopkg.in/macaroon.v2 v2.1.0 ) diff --git a/go.sum b/go.sum index 911c8a4..adcd59a 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,59 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +git.schwanenlied.me/yawning/bsaes.git v0.0.0-20180720073208-c0276d75487e h1:F2x1bq7RaNCIuqYpswggh1+c1JmwdnkHNC9wy1KDip0= +git.schwanenlied.me/yawning/bsaes.git v0.0.0-20180720073208-c0276d75487e/go.mod h1:BWqTsj8PgcPriQJGl7el20J/7TuT1d/hSyFDXMEpoEo= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/NebulousLabs/fastrand v0.0.0-20180208210444-3cf7173006a0 h1:g/ETZwHx5wN2fqKWS3gCUrEU7dLko+DvVs3hakQCfyE= +github.com/NebulousLabs/fastrand v0.0.0-20180208210444-3cf7173006a0/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ= +github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 h1:MG93+PZYs9PyEsj/n5/haQu2gK0h4tUtSy9ejtMwWa0= +github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82/go.mod h1:GbuBk21JqF+driLX3XtJYNZjGa45YDoa9IqCTzNSfEc= +github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2 h1:2be4ykKKov3M1yISM2E8gnGXZ/N2SsPawfnGiXxaYEU= +github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04= github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI= +github.com/btcsuite/btcd v0.0.0-20180823030728-d81d8877b8f3/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= +github.com/btcsuite/btcd v0.0.0-20180824064422-ed77733ec07dfc8a513741138419b8d9d3de9d2d h1:JyAoNOSAG3StMRTiI8M5ReA70DEWoTGb/tuaxYdFWGU= +github.com/btcsuite/btcd v0.0.0-20180824064422-ed77733ec07dfc8a513741138419b8d9d3de9d2d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190112041146-bf1e1be93589 h1:9A5pe5iQS+ll6R1EVLFv/y92IjrymihwITCU81aCIBQ= +github.com/btcsuite/btcutil v0.0.0-20190112041146-bf1e1be93589/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcwallet v0.0.0-20180904010540-284e2e0e696e33d5be388f7f3d9a26db703e0c06/go.mod h1:/d7QHZsfUAruXuBhyPITqoYOmJ+nq35qPsJjz/aSpCg= +github.com/btcsuite/btcwallet v0.0.0-20190123033236-ba03278a64bc h1:E7lDde/zAxAfvF750wMP0pUIAzF+wtwO2jQRy++q60U= +github.com/btcsuite/btcwallet v0.0.0-20190123033236-ba03278a64bc/go.mod h1:+u1ftn+QOb9qHKwsLf7rBOr0PHCo9CGA7U1WFq7VLA4= +github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941 h1:kij1x2aL7VE6gtx8KMIt8PGPgI5GV9LgtHFG5KaEMPY= +github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:QcFA8DZHtuIAdYKCq/BzELOaznRsCvwf4zTPmaYwaig= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8 h1:nOsAWScwueMVk/VLm/dvQQD7DuanyvAUb6B3P3eT274= +github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v0.0.0-20180223184059-7ee3ded59d4835e10f3e7d0f7603c42aa5e83820 h1:W1bWzjKRrqKEpWlFsJ6Yef9Q4LUhdfJmS6sQrQj5L6c= +github.com/coreos/bbolt v0.0.0-20180223184059-7ee3ded59d4835e10f3e7d0f7603c42aa5e83820/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.0.0 h1:QgmxFbprE29UG4oL88tGiiL/7VuiBl5xCcz+wJcJhc0= +github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gin-contrib/cors v0.0.0-20190301062745-f9e10995c85a h1:zBycVvXa03SIX+jdMv8wGu9TMDMWdN8EhaR1FoeKHNo= github.com/gin-contrib/cors v0.0.0-20190301062745-f9e10995c85a/go.mod h1:pL2kNE+DgDU+eQ+dary5bX0Z6LPP8nR6Mqs1iejILw4= -github.com/gin-contrib/secure v0.0.0-20190301062601-f9a5befa6106 h1:EvzHcDe2j4sFRLhCJR6sE1cwmt9LtrMMDdejcdjQ1Dg= -github.com/gin-contrib/secure v0.0.0-20190301062601-f9a5befa6106/go.mod h1:l2I5UUSmHxQp81CF1EmB6RIo22QgZx7yIa8bwgFwq3k= github.com/gin-contrib/sessions v0.0.0-20190226023029-1532893d996f h1:f8TGHcU6cxOhMwW6YQhpRe+zlr05qNjVmdcK1qigr5I= github.com/gin-contrib/sessions v0.0.0-20190226023029-1532893d996f/go.mod h1:8Xd9k6zfW7ekJjy3wJrgbgB2KWvP+GWywe6PanyMVwI= github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY= @@ -16,15 +61,24 @@ github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NB github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs= github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20180821051752-b27b920f9e71/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= @@ -34,11 +88,41 @@ github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9R github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/grpc-gateway v0.0.0-20170724004829-f2862b476edc h1:3NXdOHZ1YlN6SGP3FPbn4k73O2MeEp065abehRwGFxI= +github.com/grpc-ecosystem/grpc-gateway v0.0.0-20170724004829-f2862b476edc/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jackpal/gateway v1.0.4 h1:LS5EHkLuQ6jzaHwULi0vL+JO0mU/n4yUtK8oUjHHOlM= +github.com/jackpal/gateway v1.0.4/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= +github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad h1:heFfj7z0pGsNCekUlsFhO2jstxO4b5iQ665LjwM5mDc= +github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v0.0.0-20170926144705-f88afde2fa19 h1:k9/LaykApavRKKlaWkunBd48Um+vMxnUNNsIjS7OJn8= +github.com/jessevdk/go-flags v0.0.0-20170926144705-f88afde2fa19/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/juju/clock v0.0.0-20180808021310-bab88fc67299 h1:K9nBHQ3UNqg/HhZkQnGG2AE4YxDyNmGS9FFT2gGegLQ= +github.com/juju/clock v0.0.0-20180808021310-bab88fc67299/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20180524022052-584905176618 h1:MK144iBQF9hTSwBW/9eJm034bVoG30IshVm688T2hi8= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/retry v0.0.0-20180821225755-9058e192b216 h1:/eQL7EJQKFHByJe3DeE8Z36yqManj9UY5zppDoQi4FU= +github.com/juju/retry v0.0.0-20180821225755-9058e192b216/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073 h1:WQM1NildKThwdP7qWrNAFGzp4ijNLw8RlgENkaI4MJs= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= +github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d h1:irPlN9z5VCe6BTsqVsxheCZH99OFSmqSVyTigW4mEoY= +github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk= +github.com/juju/version v0.0.0-20180108022336-b64dbd566305 h1:lQxPJ1URr2fjsKnJRt/BxiIxjLt9IKGvS+0injMHbag= +github.com/juju/version v0.0.0-20180108022336-b64dbd566305/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U= github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec h1:n1NeQ3SgUHyISrjFFoO5dR748Is8dBL9qpaTNfphQrs= +github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -46,6 +130,18 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lightninglabs/gozmq v0.0.0-20180324010646-462a8a753885 h1:fTLuPUkaKIIV0+gA1IxiBDvDxtF8tzpSF6N6NfFGmsU= +github.com/lightninglabs/gozmq v0.0.0-20180324010646-462a8a753885/go.mod h1:KUh15naRlx/TmUMFS/p4JJrCrE6F7RGF7rsnvuu45E4= +github.com/lightninglabs/neutrino v0.0.0-20181017011010-4d6069299130/go.mod h1:KJq43Fu9ceitbJsSXMILcT4mGDNI/crKmPIkDOZXFyM= +github.com/lightninglabs/neutrino v0.0.0-20190115022559-351f5f06c6af h1:JzoYbWqwPb+PARU4LTtlohetdNa6/ocyQ0xidZQw4Hg= +github.com/lightninglabs/neutrino v0.0.0-20190115022559-351f5f06c6af/go.mod h1:aR+E6cs+FTaIwIa/WLyvNsB8FZg8TiP3r0Led+4Q4gI= +github.com/lightningnetwork/lightning-onion v0.0.0-20180605012408-ac4d9da8f1d6 h1:ONLGrYJVQdbtP6CE/ff1KNWZtygRGEh12RzonTiCzPs= +github.com/lightningnetwork/lightning-onion v0.0.0-20180605012408-ac4d9da8f1d6/go.mod h1:8EgEt4a/NUOVQd+3kk6n9aZCJ1Ssj96Pb6lCrci+6oc= +github.com/lightningnetwork/lnd v0.5.2-beta h1:AecJ2HFtQgktVPgpxViP0/LEFAN73MsWGjtSviZgMU0= +github.com/lightningnetwork/lnd v0.5.2-beta/go.mod h1:tkYuDSrt0DPsObEYGBOgycuRzy1bWm89KOFxVaAOBHI= +github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= +github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= +github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= @@ -58,15 +154,23 @@ github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go. github.com/mediocregopher/radix/v3 v3.2.3 h1:TbcGCZdo9zfPYPgevsqRn+OjvCyfOK6TzuXhqzWdCt0= github.com/mediocregopher/radix/v3 v3.2.3/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc= +github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8 h1:PRMAcldsl4mXKJeRNB/KVNz6TlbS6hk2Rs42PqgU3Ws= +github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/namsral/flag v1.7.4-pre h1:b2ScHhoCUkbsq0d2C15Mv+VU8bl8hAXV8arnWiOHNZs= github.com/namsral/flag v1.7.4-pre/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/segmentio/ksuid v1.0.2 h1:9yBfKyw4ECGTdALaF09Snw3sLJmYIX6AbPJrAy6MrDc= github.com/segmentio/ksuid v1.0.2/go.mod h1:BXuJDr2byAiHuQaQtSKoXh1J0YmUDurywOXgB2w+OSU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -75,20 +179,43 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= +github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 h1:tcJ6OjwOMvExLlzrAVZute09ocAGa7KqOON60++Gz4E= +github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY= github.com/ugorji/go v1.1.2 h1:JON3E2/GPW2iDNGoSAusl1KDf5TRQ8k8q7Tp097pZGs= github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2 h1:EICbibRW4JNKMcY+LsWmuwob+CRS1BmdRdjphAm9mH4= github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= +github.com/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +go.etcd.io/bbolt v1.3.0 h1:oY10fI923Q5pVCVt1GBTZMn8LHo5M+RCInFpeMnV4QI= +go.etcd.io/bbolt v1.3.0/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180821023952-922f4815f713/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190322120337-addf6b3196f6 h1:78jEq2G3J16aXneH23HSnTQQTCwMHoyO8VEiUH+bpPM= golang.org/x/net v0.0.0-20190322120337-addf6b3196f6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180821140842-3b58ed4ad339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -96,14 +223,44 @@ golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc h1:4gbWbmmPFp4ySWICouJl6emP0 golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181127195345-31ac5d88444a/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8= +google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM= +google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v1 v1.0.0 h1:n+7XfCyygBFb8sEjg6692xjC6Us50TFRO54+xYUEwjE= +gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/macaroon-bakery.v2 v2.0.1 h1:0N1TlEdfLP4HXNCg7MQUMp5XwvOoxk+oe9Owr2cpvsc= +gopkg.in/macaroon-bakery.v2 v2.0.1/go.mod h1:B4/T17l+ZWGwxFSZQmlBwp25x+og7OkhETfr3S9MbIA= +gopkg.in/macaroon.v2 v2.0.0/go.mod h1:+I6LnTMkm/uV5ew/0nsulNjL16SK4+C8yDmRUzHR17I= +gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI= +gopkg.in/macaroon.v2 v2.1.0/go.mod h1:OUb+TQP/OP0WOerC2Jp/3CwhIKyIa9kQjuc7H24e6/o= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/ln/api.go b/ln/api.go index 19f75a6..a19fbd7 100644 --- a/ln/api.go +++ b/ln/api.go @@ -5,13 +5,14 @@ import ( "encoding/json" "fmt" "log" - "net" "net/http" "net/url" "strconv" - "time" + "git.sp4ke.com/sp4ke/bit4sat/btc" "git.sp4ke.com/sp4ke/bit4sat/config" + "git.sp4ke.com/sp4ke/bit4sat/lndrpc" + "git.sp4ke.com/sp4ke/bit4sat/utils" "github.com/gin-gonic/gin" ) @@ -50,7 +51,11 @@ func CheckInvoice(id string) (*Invoice, error) { return &invoice, err } +// This will watch on the pubsub for paid invoices func PollPaidInvoice(invoiceId string, invoiceChan chan<- *Invoice, errorChan chan<- error) { +} + +func PollPaidInvoiceTMP(invoiceId string, invoiceChan chan<- *Invoice, errorChan chan<- error) { invoice := Invoice{} @@ -111,7 +116,47 @@ func PollPaidInvoice(invoiceId string, invoiceChan chan<- *Invoice, errorChan ch log.Printf("quit polling %s", invoiceId) } -func NewInvoiceForUpload(amount float32, curr Currency, uploadId string) (*Invoice, error) { +func NewInvoiceForUpload(amount float64, curr Currency, uploadId string) (*Invoice, error) { + var err error + var satValue int64 + + description := fmt.Sprintf("bit4sat upload: %s", uploadId) + + if curr == CurMSat { + log.Println("cur is msat") + // Convert to MSAT, meaning values under 1000 MSAT are ignored + satValue = int64(amount / 1000) + + // Other supported currecies + } + + if curr != CurSat { + log.Println("cur is custom") + + // Get Sat satValue for this invoice + msatVal, err := btc.ToMsat(CurrencyString[curr], amount) + if err != nil { + return nil, err + } + + satValue = msatVal / 1000 + } else { + satValue = int64(amount) + } + + lndInvoice, err := lndrpc.AddInvoiceSat(description, satValue) + if err != nil { + return nil, err + } + + invoice := InvoiceFromLndIn(lndInvoice) + invoice.QuotedCurrency = CurrencyString[curr] + invoice.QuotedAmount = amount + + return invoice, nil +} + +func NewInvoiceForUploadTMP(amount float32, curr Currency, uploadId string) (*Invoice, error) { webhookUrl := fmt.Sprintf("http://%s:%s/%s", config.ApiHost, config.ApiPort, config.ChargeCallbackEndpoint) @@ -164,21 +209,6 @@ func getUrl(endpoint string) string { } -func newClient() *http.Client { - netTransport := &http.Transport{ - Dial: (&net.Dialer{ - Timeout: 5 * time.Second, - }).Dial, - TLSHandshakeTimeout: 5 * time.Second, - } - c := &http.Client{ - Timeout: time.Second * 10, - Transport: netTransport, - } - - return c -} - func init() { - Client = newClient() + Client = utils.NewHttpClient() } diff --git a/ln/invoice.go b/ln/invoice.go index 8245f3b..8422498 100644 --- a/ln/invoice.go +++ b/ln/invoice.go @@ -1,10 +1,13 @@ package ln import ( + "encoding/hex" "encoding/json" "fmt" "strconv" "time" + + "github.com/lightningnetwork/lnd/lnrpc" ) type Currency int @@ -17,6 +20,18 @@ const ( CurEur ) +const ( + Paid int = iota + UnPaid + Expired +) + +var InvoiceStatus = map[int]string{ + UnPaid: "unpaid", + Paid: "paid", + Expired: "expired", +} + const ( InfoEndpoint = "info" InvoiceEndpoint = "invoice" @@ -71,18 +86,18 @@ func (t timestamp) String() string { } type Invoice struct { - Id string `json:"id"` + AddIndex uint64 `json:"-"` Description string `json:"description"` - Msatoshi string `json:"msatoshi"` + Msatoshi float64 `json:"msatoshi"` Payreq string `json:"payreq"` RHash string `json:"rhash"` Status string `json:"status"` - QuotedCurrency string `json:"quoted_currency"` - QuotedAmount string `json:"quoted_amount"` PaidAt timestamp `json:"paid_at"` CreatedAt timestamp `json:"created_at"` ExpiresAt timestamp `json:"expires_at"` Expired bool `json:"-"` + QuotedCurrency string `json:"quoted_currency"` + QuotedAmount float64 `json:"quoted_amount"` } func (i Invoice) MarshalBinary() ([]byte, error) { @@ -92,3 +107,31 @@ func (i Invoice) MarshalBinary() ([]byte, error) { func (i *Invoice) UnmarshalBinary(b []byte) error { return json.Unmarshal(b, &i) } + +func InvoiceFromLndIn(lndInvoice *lnrpc.Invoice) *Invoice { + + invoice := Invoice{ + AddIndex: lndInvoice.AddIndex, + Description: lndInvoice.GetMemo(), + Msatoshi: float64(lndInvoice.Value * 1000), + Payreq: lndInvoice.PaymentRequest, + RHash: hex.EncodeToString(lndInvoice.RHash), + CreatedAt: timestamp(time.Unix(lndInvoice.CreationDate, 0)), + PaidAt: timestamp(time.Unix(lndInvoice.SettleDate, 0)), + Status: InvoiceStatus[UnPaid], + } + + invoice.ExpiresAt = timestamp(time.Time(invoice.CreatedAt).Add(time.Second * time.Duration(lndInvoice.Expiry))) + + // Calculate status + if lndInvoice.Settled { + invoice.Status = InvoiceStatus[Paid] + } + + if time.Now().After(time.Time(invoice.ExpiresAt)) { + invoice.Status = InvoiceStatus[Expired] + invoice.Expired = true + } + + return &invoice +} diff --git a/lndrpc/commands.go b/lndrpc/commands.go new file mode 100644 index 0000000..d72c36c --- /dev/null +++ b/lndrpc/commands.go @@ -0,0 +1,99 @@ +package lndrpc + +import ( + "log" + + "golang.org/x/net/context" + + lnrpc "github.com/lightningnetwork/lnd/lnrpc" +) + +func GetInfo() error { + log.Println("get info") + ctxb := context.Background() + client, cleanUp := getClient() + defer cleanUp() + + req := &lnrpc.GetInfoRequest{} + resp, err := client.GetInfo(ctxb, req) + if err != nil { + return err + } + + log.Println(resp) + + return nil +} + +func lookupInvoiceRhash(rhash []byte) (*lnrpc.Invoice, error) { + ctxb := context.Background() + client, cleanUp := getClient() + defer cleanUp() + + req := &lnrpc.PaymentHash{ + RHash: rhash, + } + + // Get back the invoice + createdInvoice, err := client.LookupInvoice(ctxb, req) + if err != nil { + return nil, err + } + + return createdInvoice, nil +} + +func lookupInvoiceRhashStr(rhash string) (*lnrpc.Invoice, error) { + + ctxb := context.Background() + client, cleanUp := getClient() + defer cleanUp() + + req := &lnrpc.PaymentHash{ + RHashStr: rhash, + } + + // Get back the invoice + createdInvoice, err := client.LookupInvoice(ctxb, req) + if err != nil { + return nil, err + } + + return createdInvoice, nil + +} + +func AddInvoiceSat(desc string, satVal int64) (*lnrpc.Invoice, error) { + ctxb := context.Background() + client, cleanUp := getClient() + defer cleanUp() + + invoice := &lnrpc.Invoice{ + Memo: desc, + + // Value in satoshis + Value: satVal, + } + + resp, err := client.AddInvoice(ctxb, invoice) + if err != nil { + return nil, err + } + + return lookupInvoiceRhash(resp.RHash) +} + +// Blocks until the invoice is paid or expired +func WatchInvoice(addIndex uint64) (*lnrpc.Invoice, error) { + ctxb := context.Background() + client, cleanUp := getClient() + defer cleanUp() + + subscription := &lnrpc.InvoiceSubscription{} + + subscriptionClient, err := client.SubscribeInvoices(ctxb, subscription) + if err != nil { + //return nil, err + } + +} diff --git a/lndrpc/grpc.go b/lndrpc/grpc.go new file mode 100644 index 0000000..b02e069 --- /dev/null +++ b/lndrpc/grpc.go @@ -0,0 +1,108 @@ +// Grpc interface to lnd +package lndrpc + +import ( + "fmt" + "io/ioutil" + "log" + + "github.com/lightningnetwork/lnd/lncfg" + "github.com/lightningnetwork/lnd/macaroons" + macaroon "gopkg.in/macaroon.v2" + + "git.sp4ke.com/sp4ke/bit4sat/config" + "github.com/lightningnetwork/lnd/lnrpc" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +func fatal(err error) { + log.Fatal(err) +} + +func getClient() (lnrpc.LightningClient, func()) { + conn := getClientConn() + + cleanUp := func() { + conn.Close() + } + + return lnrpc.NewLightningClient(conn), cleanUp +} + +func getClientConn() *grpc.ClientConn { + + // Load the specified TLS certificate and build transport credentials + // with it. + creds, err := credentials.NewClientTLSFromFile(config.LndCertPath, "") + if err != nil { + fatal(err) + } + + // Create a dial options array. + opts := []grpc.DialOption{ + grpc.WithTransportCredentials(creds), + } + + // Load the specified macaroon file. + macBytes, err := ioutil.ReadFile(config.LndAdminMacaroonPath) + if err != nil { + fatal(fmt.Errorf("unable to read macaroon path (check "+ + "the network setting!): %v", err)) + } + + mac := &macaroon.Macaroon{} + if err = mac.UnmarshalBinary(macBytes); err != nil { + fatal(fmt.Errorf("unable to decode macaroon: %v", err)) + } + + macConstraints := []macaroons.Constraint{ + // We add a time-based constraint to prevent replay of the + // macaroon. It's good for 60 seconds by default to make up for + // any discrepancy between client and server clocks, but leaking + // the macaroon before it becomes invalid makes it possible for + // an attacker to reuse the macaroon. In addition, the validity + // time of the macaroon is extended by the time the server clock + // is behind the client clock, or shortened by the time the + // server clock is ahead of the client clock (or invalid + // altogether if, in the latter case, this time is more than 60 + // seconds). + // TODO(aakselrod): add better anti-replay protection. + macaroons.TimeoutConstraint(60), + + // Lock macaroon down to a specific IP address. + //macaroons.IPLockConstraint(config.LockMacaroonIp), + macaroons.IPLockConstraint(""), + + // ... Add more constraints if needed. + } + + // Apply constraints to the macaroon. + constrainedMac, err := macaroons.AddConstraints(mac, macConstraints...) + if err != nil { + fatal(err) + } + + // Now we append the macaroon credentials to the dial options. + cred := macaroons.NewMacaroonCredential(constrainedMac) + opts = append(opts, grpc.WithPerRPCCredentials(cred)) + + // We need to use a custom dialer so we can also connect to unix sockets + // and not just TCP addresses. + + opts = append( + opts, grpc.WithDialer( + lncfg.ClientAddressDialer(config.LndGrpcPort), + ), + ) + conn, err := grpc.Dial(fmt.Sprintf("%s:%s", config.LndGrpcHost, config.LndGrpcPort), opts...) + if err != nil { + fatal(fmt.Errorf("unable to connect to RPC server: %v", err)) + } + + return conn +} + +func init() { + log.Println("init grpc") +} diff --git a/main.go b/main.go index 1384fc2..d96fa83 100644 --- a/main.go +++ b/main.go @@ -10,8 +10,5 @@ func main() { defer db.DB.Sql.Close() api := api.NewAPI() - //invoiceWatcher := watchers.NewInvoiceWatcher() - //go invoiceWatcher.Run() api.Run() - } diff --git a/storage/upload_ctrl.go b/storage/upload_ctrl.go index da3c79c..69ad986 100644 --- a/storage/upload_ctrl.go +++ b/storage/upload_ctrl.go @@ -224,7 +224,7 @@ func (ctrl UploadCtrl) PollStatus(c *gin.Context) { log.Println("starting go routine") // If waiting payment, wait until invoice is paid - go ln.PollPaidInvoice(invoice.Id, invoiceChan, errorChan) + go ln.PollPaidInvoice(invoice.RHash, invoiceChan, errorChan) // Block until payment done or error log.Println("blocking") diff --git a/storage/upload_model.go b/storage/upload_model.go index faa66fb..09eb2cf 100644 --- a/storage/upload_model.go +++ b/storage/upload_model.go @@ -222,7 +222,7 @@ func GetUploadInvoice(uploadId string) (*ln.Invoice, error) { func GetUploadInvoiceId(uploadId string) (string, error) { invoice, err := GetUploadInvoice(uploadId) - return invoice.Id, err + return invoice.RHash, err } func SetUploadStatus(id string, status UpStatus) error { @@ -275,7 +275,7 @@ func SetUploadInvoice(uploadId string, invoice *ln.Invoice) error { } // Set inverse relation - invoiceUploadKey := fmt.Sprintf("invoice_%s_upload", invoice.Id) + invoiceUploadKey := fmt.Sprintf("invoice_%s_upload", invoice.RHash) return DB.Redis.Do(radix.FlatCmd(nil, "SET", invoiceUploadKey, uploadId)) } diff --git a/utils/http.go b/utils/http.go new file mode 100644 index 0000000..4c40e46 --- /dev/null +++ b/utils/http.go @@ -0,0 +1,22 @@ +package utils + +import ( + "net" + "net/http" + "time" +) + +func NewHttpClient() *http.Client { + netTransport := &http.Transport{ + Dial: (&net.Dialer{ + Timeout: 5 * time.Second, + }).Dial, + TLSHandshakeTimeout: 5 * time.Second, + } + c := &http.Client{ + Timeout: time.Second * 10, + Transport: netTransport, + } + + return c +} diff --git a/web/src/Pay.vue b/web/src/Pay.vue index 1e95bd2..19c3493 100644 --- a/web/src/Pay.vue +++ b/web/src/Pay.vue @@ -61,7 +61,11 @@ export default { }, showTimer: function(){ - return this.invoice.status != 'paid'; + if (this.invoice.status !== undefined) { + return this.invoice.status != 'paid'; + } + + return false; } }, watch: {