From a625ad69b0b89ea53df782fe98f18f5ea0ab57cd Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 7 Aug 2020 17:33:02 +0200 Subject: [PATCH 1/7] mod: update lnd, use lndclient repo --- challenger.go | 2 +- go.mod | 27 +++------ go.sum | 103 ++++++++++++++++++++++---------- lsat/client_interceptor.go | 14 ++--- lsat/client_interceptor_test.go | 8 +-- 5 files changed, 89 insertions(+), 65 deletions(-) diff --git a/challenger.go b/challenger.go index 9d8b4bd..066cca2 100644 --- a/challenger.go +++ b/challenger.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/lightninglabs/aperture/mint" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" ) diff --git a/go.mod b/go.mod index 10f3419..55d32e9 100644 --- a/go.mod +++ b/go.mod @@ -4,28 +4,15 @@ go 1.13 require ( github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f - github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d - github.com/coreos/etcd v3.3.17+incompatible - github.com/coreos/go-semver v0.3.0 // indirect - github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect - github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect - github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/btcsuite/btcutil v1.0.2 + github.com/coreos/etcd v3.3.22+incompatible github.com/golang/protobuf v1.3.2 - github.com/google/btree v1.0.0 // indirect - github.com/google/uuid v1.1.1 // indirect - github.com/gorilla/websocket v1.4.1 // indirect - github.com/jonboulle/clockwork v0.1.0 // indirect - github.com/json-iterator/go v1.1.9 // indirect - github.com/lightninglabs/loop v0.3.0-alpha.0.20200103135410-5e00ce62677a - github.com/lightningnetwork/lnd v0.9.0-beta-rc4.0.20200313014957-4cb518c17498 - github.com/lightningnetwork/lnd/cert v1.0.0 - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/soheilhy/cmux v0.1.4 // indirect + github.com/lightninglabs/lndclient v1.0.1-0.20200708223031-76709c25d859 + github.com/lightninglabs/loop v0.6.5-beta + github.com/lightningnetwork/lnd v0.10.3-beta + github.com/lightningnetwork/lnd/cert v1.0.2 github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc // indirect - github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect - go.uber.org/zap v1.14.1 // indirect - golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 + golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 golang.org/x/net v0.0.0-20191112182307-2180aed22343 google.golang.org/grpc v1.25.1 gopkg.in/macaroon.v2 v2.1.0 diff --git a/go.sum b/go.sum index 02a8db5..10783ee 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ 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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -25,13 +27,20 @@ github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 h1:A/EVblehb75cUgXA5 github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.20.1-beta.0.20200513120220-b470eee47728/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.20.1-beta.0.20200515232429-9f0179fd2c46 h1:QyTpiR5nQe94vza2qkvf7Ns8XX2Rjh/vdIhO3RzGj4o= +github.com/btcsuite/btcd v0.20.1-beta.0.20200515232429-9f0179fd2c46/go.mod h1:Yktc19YNjh/Iz2//CX0vfRTS4IJKM/RKO5YZ9Fn+Pgo= 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-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.11.0/go.mod h1:qtPAohN1ioo0pvJt/j7bZM8ANBWlYWVCVFL0kkijs7s= -github.com/btcsuite/btcwallet v0.11.1-0.20200219004649-ae9416ad7623 h1:ZuJRjucNsTmlrbZncsqzD0z3EaXrOobCx2I4lc12R4g= -github.com/btcsuite/btcwallet v0.11.1-0.20200219004649-ae9416ad7623/go.mod h1:1O1uRHMPXHdwA4/od8nqYqrgclVKp+wtfXUAqHmeRvE= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/btcutil/psbt v1.0.2 h1:gCVY3KxdoEVU7Q6TjusPO+GANIwVgr9yTLqM+a6CZr8= +github.com/btcsuite/btcutil/psbt v1.0.2/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ= +github.com/btcsuite/btcwallet v0.11.1-0.20200604005347-6390f167e5f8/go.mod h1:9+AH3V5mcTtNXTKe+fe63fDLKGOwQbZqmvOVUef+JFE= +github.com/btcsuite/btcwallet v0.11.1-0.20200612012534-48addcd5591a h1:AZ1Mf0gd9mgJqrTTIFUc17ep9EKUbQusVAIzJ6X+x3Q= +github.com/btcsuite/btcwallet v0.11.1-0.20200612012534-48addcd5591a/go.mod h1:9+AH3V5mcTtNXTKe+fe63fDLKGOwQbZqmvOVUef+JFE= github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0 h1:KGHMW5sd7yDdDMkCZ/JpP0KltolFsQcB973brBnfj4c= github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU= github.com/btcsuite/btcwallet/wallet/txrules v1.0.0 h1:2VsfS0sBedcM5KmDzRMT3+b6xobqWveZGvjb+jFez5w= @@ -40,13 +49,17 @@ github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0 h1:6DxkcoMnCPY4E9cUDPB5tbuuf github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs= github.com/btcsuite/btcwallet/walletdb v1.0.0 h1:mheT7vCWK5EP6rZzhxsQ7ms9+yX4VE8bwiJctECBeNw= github.com/btcsuite/btcwallet/walletdb v1.0.0/go.mod h1:bZTy9RyYZh9fLnSua+/CD48TJtYJSHjjYcSaszuxCCk= -github.com/btcsuite/btcwallet/walletdb v1.1.0/go.mod h1:bZTy9RyYZh9fLnSua+/CD48TJtYJSHjjYcSaszuxCCk= github.com/btcsuite/btcwallet/walletdb v1.2.0 h1:E0+M4jHOToAvGWZ27ew5AaDAHDi6fUiXkjUJUnoEOD0= github.com/btcsuite/btcwallet/walletdb v1.2.0/go.mod h1:9cwc1Yyg4uvd4ZdfdoMnALji+V9gfWSMfxEdLdR5Vwc= +github.com/btcsuite/btcwallet/walletdb v1.3.1/go.mod h1:9cwc1Yyg4uvd4ZdfdoMnALji+V9gfWSMfxEdLdR5Vwc= +github.com/btcsuite/btcwallet/walletdb v1.3.2/go.mod h1:GZCMPNpUu5KE3ASoVd+k06p/1OW8OwNGCCaNWRto2cQ= +github.com/btcsuite/btcwallet/walletdb v1.3.3 h1:u6e7vRIKBF++cJy+hOHaMGg+88ZTwvpaY27AFvtB668= +github.com/btcsuite/btcwallet/walletdb v1.3.3/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= github.com/btcsuite/btcwallet/wtxmgr v1.0.0 h1:aIHgViEmZmZfe0tQQqF1xyd2qBqFWxX5vZXkkbjtbeA= github.com/btcsuite/btcwallet/wtxmgr v1.0.0/go.mod h1:vc4gBprll6BP0UJ+AIGDaySoc7MdAmZf8kelfNb8CFY= -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/btcwallet/wtxmgr v1.1.1-0.20200604005347-6390f167e5f8/go.mod h1:cJGqxXtqQbmXuL7RlXjIM18x0bGHy1407/85mQCLca4= +github.com/btcsuite/btcwallet/wtxmgr v1.2.0 h1:ZUYPsSv8GjF9KK7lboB2OVHF0uYEcHxgrCfFWqPd9NA= +github.com/btcsuite/btcwallet/wtxmgr v1.2.0/go.mod h1:h8hkcKUE3X7lMPzTUoGnNiw5g7VhGrKEW3KpR2r0VnY= 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/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= @@ -64,8 +77,8 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.17+incompatible h1:f/Z3EoDSx1yjaIjLQGo1diYUlQYSBrrAQ5vP8NjwXwo= -github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.22+incompatible h1:AnRMUyVdVvh1k7lHe61YEd227+CLoNogQuAypztGSK4= +github.com/coreos/etcd v3.3.22+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= @@ -79,6 +92,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -94,6 +109,8 @@ github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -111,8 +128,8 @@ github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= @@ -124,14 +141,15 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.6/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.10.0 h1:yqx/nTDLC6pVrQ8fTaCeeeMJNbmt7HglUpysQATYXV4= -github.com/grpc-ecosystem/grpc-gateway v1.10.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY= +github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc= github.com/jackpal/gateway v1.0.5/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/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -170,32 +188,43 @@ 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/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= -github.com/lightninglabs/loop v0.3.0-alpha.0.20200103135410-5e00ce62677a h1:EiZsid2PZaanlYn5x41hKttdoxnd3rJ34SotnO4bu4Y= -github.com/lightninglabs/loop v0.3.0-alpha.0.20200103135410-5e00ce62677a/go.mod h1:VEp7TFUL7Ehq9mjQVUuNVTjpMIeTx8kOHvQ7PdJpLuA= +github.com/lightninglabs/lndclient v0.0.0-20200618122423-5d815058a719 h1:RzyO65pK78xxSaekERq2V3g32GMBPc0MfxmM8MDMVmQ= +github.com/lightninglabs/lndclient v0.0.0-20200618122423-5d815058a719/go.mod h1:WQU2oE0eJIp7jtBOUnL7CO5/YoWrAqyH23A6M1r31i8= +github.com/lightninglabs/lndclient v1.0.1-0.20200708223031-76709c25d859 h1:MrKZJ35EuWPAXR1tS8KCGL+hcsGwhC2jtL0AqlXXOvY= +github.com/lightninglabs/lndclient v1.0.1-0.20200708223031-76709c25d859/go.mod h1:nKEd6j54LxPYuIe3K/BUwQPus24sbvWTdoweoBcHas0= +github.com/lightninglabs/loop v0.6.5-beta h1:KWAydG16aVI/D7gzFdKhwnprVCtJtbLiS4I/t3gkezA= +github.com/lightninglabs/loop v0.6.5-beta/go.mod h1:Pgj7xTC6T3wbCCBxKyOVWBpvDv9mas4zpvW8jZiwG0U= github.com/lightninglabs/neutrino v0.11.0 h1:lPpYFCtsfJX2W5zI4pWycPmbbBdr7zU+BafYdLoD6k0= github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg= +github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200 h1:j4iZ1XlUAPQmW6oSzMcJGILYsRHNs+4O3Gk+2Ms5Dww= +github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h1:MlZmoKa7CJP3eR1s5yB7Rm5aSyadpKkxqAwLQmog7N0= github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI= -github.com/lightningnetwork/lightning-onion v0.0.0-20191214001659-f34e9dc1651d/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4= github.com/lightningnetwork/lightning-onion v1.0.1 h1:qChGgS5+aPxFeR6JiUsGvanei1bn6WJpYbvosw/1604= github.com/lightningnetwork/lightning-onion v1.0.1/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4= -github.com/lightningnetwork/lnd v0.8.0-beta-rc3.0.20200103000305-22e1f006b194/go.mod h1:WHK90FD3m2n6OyWzondS7ho0Uhtgfp30Nxvj24lQYX4= -github.com/lightningnetwork/lnd v0.9.0-beta-rc4.0.20200313014957-4cb518c17498 h1:v9WzPrzY/BOPfs7D1Lg5IuIxhao3BAJaL8EFFcLTzTY= -github.com/lightningnetwork/lnd v0.9.0-beta-rc4.0.20200313014957-4cb518c17498/go.mod h1:fImtTwhIXK91glN8iArkrGuScc0sNEKpZq43pTvVq3w= -github.com/lightningnetwork/lnd/cert v1.0.0 h1:J0gtf2UNQX2U+/j5cXnX2wIMSTuJuwrXv7m9qJr2wtw= -github.com/lightningnetwork/lnd/cert v1.0.0/go.mod h1:fmtemlSMf5t4hsQmcprSoOykypAPp+9c+0d0iqTScMo= +github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea h1:oCj48NQ8u7Vz+MmzHqt0db6mxcFZo3Ho7M5gCJauY/k= +github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4= +github.com/lightningnetwork/lnd v0.10.0-beta.rc6.0.20200615174244-103c59a4889f/go.mod h1:a2ejAHgjuwQ9YQJBzlMD8ZJHUcyWxXb8HRGe5QlJh9Y= +github.com/lightningnetwork/lnd v0.10.3-beta h1:bS9+zMEH7DHAc8yzEml/xBMCiB0Xo5K7R84hKLvO4ao= +github.com/lightningnetwork/lnd v0.10.3-beta/go.mod h1:4d02pduRVtZwgTJ+EimKJTsEAY0jDwi0SPE9h5aRneM= +github.com/lightningnetwork/lnd/cert v1.0.2 h1:g2rEu+sM2Uyz0bpfuvwri/ks6R/26H5iY1NcGbpDJ+c= +github.com/lightningnetwork/lnd/cert v1.0.2/go.mod h1:fmtemlSMf5t4hsQmcprSoOykypAPp+9c+0d0iqTScMo= +github.com/lightningnetwork/lnd/clock v1.0.1 h1:QQod8+m3KgqHdvVMV+2DRNNZS1GRFir8mHZYA+Z2hFo= +github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg= github.com/lightningnetwork/lnd/queue v1.0.1 h1:jzJKcTy3Nj5lQrooJ3aaw9Lau3I0IwvQR5sqtjdv2R0= github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms= -github.com/lightningnetwork/lnd/queue v1.0.2 h1:Hx43fmTz2pDH4fIYDr57P/M5cB+GEMLzN+eif8576Xo= -github.com/lightningnetwork/lnd/queue v1.0.2/go.mod h1:YTkTVZCxz8tAYreH27EO3s8572ODumWrNdYW2E/YKxg= +github.com/lightningnetwork/lnd/queue v1.0.4 h1:8Dq3vxAFSACPy+pKN88oPFhuCpCoAAChPBwa4BJxH4k= +github.com/lightningnetwork/lnd/queue v1.0.4/go.mod h1:YTkTVZCxz8tAYreH27EO3s8572ODumWrNdYW2E/YKxg= github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qpwh6l+AkQEZwU= github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0= 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-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 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/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -240,10 +269,15 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc h1:yUaosFVTJwnltaHbSNC3i82I92quFs+OFPRl8kNMVwo= github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 h1:tcJ6OjwOMvExLlzrAVZute09ocAGa7KqOON60++Gz4E= @@ -254,6 +288,9 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50 h1:ASw9n1EHMftwnP3Az4XW6e308+gNsrHzmdhd0Olz9Hs= +go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= @@ -268,10 +305,9 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM= -golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0= -golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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= @@ -292,8 +328,7 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343 h1:00ohfJ4K98s3m6BGUoBd8nyfp4Yl0GoIKvw5abItTjI= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -312,14 +347,15 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -335,11 +371,14 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c h1:hrpEMCZ2O7DR5gC1n2AJGVhrwiEjOi35+jxtIuZpTMo= +google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -365,6 +404,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl 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= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -372,5 +412,6 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/lsat/client_interceptor.go b/lsat/client_interceptor.go index 7318500..691ca07 100644 --- a/lsat/client_interceptor.go +++ b/lsat/client_interceptor.go @@ -9,8 +9,8 @@ import ( "time" "github.com/btcsuite/btcutil" - "github.com/lightninglabs/loop/lndclient" - "github.com/lightningnetwork/lnd/lnrpc/routerrpc" + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/zpay32" "google.golang.org/grpc" @@ -400,13 +400,13 @@ func (i *ClientInterceptor) trackPayment(ctx context.Context, token *Token) erro // If the payment was successful, we have all the // information we need and we can return the fully paid // token. - case routerrpc.PaymentState_SUCCEEDED: + case lnrpc.Payment_SUCCEEDED: extractPaymentDetails(token, result) return i.store.StoreToken(token) // The payment is still in transit, we'll give it more // time to complete. - case routerrpc.PaymentState_IN_FLIGHT: + case lnrpc.Payment_IN_FLIGHT: // Any other state means either error or timeout. default: @@ -441,8 +441,6 @@ func isPaymentRequired(err error) bool { // from the payment status and stores them in the token. func extractPaymentDetails(token *Token, status lndclient.PaymentStatus) { token.Preimage = status.Preimage - total := status.Route.TotalAmount - fees := status.Route.TotalFees() - token.AmountPaid = total - fees - token.RoutingFeePaid = fees + token.AmountPaid = status.Value + token.RoutingFeePaid = status.Fee } diff --git a/lsat/client_interceptor_test.go b/lsat/client_interceptor_test.go index a08f16a..cbe32bf 100644 --- a/lsat/client_interceptor_test.go +++ b/lsat/client_interceptor_test.go @@ -9,11 +9,10 @@ import ( "testing" "time" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/test" - "github.com/lightningnetwork/lnd/lnrpc/routerrpc" + "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" - "github.com/lightningnetwork/lnd/routing/route" "google.golang.org/grpc" "google.golang.org/grpc/status" "gopkg.in/macaroon.v2" @@ -160,9 +159,8 @@ var ( // return an error. resetBackend(nil, "") msg.Updates <- lndclient.PaymentStatus{ - State: routerrpc.PaymentState_SUCCEEDED, + State: lnrpc.Payment_SUCCEEDED, Preimage: paidPreimage, - Route: &route.Route{}, } }, expectToken: true, From 3b73ac96cb85b5cddde108a7e0a790d8f7561fe4 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 7 Aug 2020 17:33:03 +0200 Subject: [PATCH 2/7] mod+lsat+test: copy test code to get rid of loop To get rid of the loop dependency, we copy the test code that we rely on and fix some imports. --- go.mod | 18 +- go.sum | 37 +--- internal/test/chainnotifier_mock.go | 160 +++++++++++++++ internal/test/context.go | 256 ++++++++++++++++++++++++ internal/test/invoices_mock.go | 110 +++++++++++ internal/test/keys.go | 17 ++ internal/test/lightning_client_mock.go | 240 ++++++++++++++++++++++ internal/test/lnd_services_mock.go | 263 +++++++++++++++++++++++++ internal/test/log.go | 24 +++ internal/test/router_mock.go | 43 ++++ internal/test/signer_mock.go | 58 ++++++ internal/test/testutils.go | 98 +++++++++ internal/test/timeout.go | 34 ++++ internal/test/versioner_mock.go | 51 +++++ internal/test/walletkit_mock.go | 133 +++++++++++++ lsat/client_interceptor_test.go | 2 +- lsat/header.go | 3 +- 17 files changed, 1515 insertions(+), 32 deletions(-) create mode 100644 internal/test/chainnotifier_mock.go create mode 100644 internal/test/context.go create mode 100644 internal/test/invoices_mock.go create mode 100644 internal/test/keys.go create mode 100644 internal/test/lightning_client_mock.go create mode 100644 internal/test/lnd_services_mock.go create mode 100644 internal/test/log.go create mode 100644 internal/test/router_mock.go create mode 100644 internal/test/signer_mock.go create mode 100644 internal/test/testutils.go create mode 100644 internal/test/timeout.go create mode 100644 internal/test/versioner_mock.go create mode 100644 internal/test/walletkit_mock.go diff --git a/go.mod b/go.mod index 55d32e9..6802171 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,31 @@ module github.com/lightninglabs/aperture go 1.13 require ( + github.com/btcsuite/btcd v0.20.1-beta.0.20200515232429-9f0179fd2c46 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f github.com/btcsuite/btcutil v1.0.2 + github.com/btcsuite/btcwallet/wtxmgr v1.2.0 github.com/coreos/etcd v3.3.22+incompatible + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect + github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/fortytw2/leaktest v1.3.0 + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.3.2 + github.com/google/btree v1.0.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/jonboulle/clockwork v0.2.0 // indirect + github.com/json-iterator/go v1.1.10 // indirect github.com/lightninglabs/lndclient v1.0.1-0.20200708223031-76709c25d859 - github.com/lightninglabs/loop v0.6.5-beta github.com/lightningnetwork/lnd v0.10.3-beta github.com/lightningnetwork/lnd/cert v1.0.2 + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/soheilhy/cmux v0.1.4 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc // indirect + github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect + go.uber.org/zap v1.15.0 // indirect golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 golang.org/x/net v0.0.0-20191112182307-2180aed22343 google.golang.org/grpc v1.25.1 diff --git a/go.sum b/go.sum index 10783ee..819e17a 100644 --- a/go.sum +++ b/go.sum @@ -16,7 +16,6 @@ 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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= @@ -38,7 +37,6 @@ github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2ut github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= github.com/btcsuite/btcutil/psbt v1.0.2 h1:gCVY3KxdoEVU7Q6TjusPO+GANIwVgr9yTLqM+a6CZr8= github.com/btcsuite/btcutil/psbt v1.0.2/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ= -github.com/btcsuite/btcwallet v0.11.1-0.20200604005347-6390f167e5f8/go.mod h1:9+AH3V5mcTtNXTKe+fe63fDLKGOwQbZqmvOVUef+JFE= github.com/btcsuite/btcwallet v0.11.1-0.20200612012534-48addcd5591a h1:AZ1Mf0gd9mgJqrTTIFUc17ep9EKUbQusVAIzJ6X+x3Q= github.com/btcsuite/btcwallet v0.11.1-0.20200612012534-48addcd5591a/go.mod h1:9+AH3V5mcTtNXTKe+fe63fDLKGOwQbZqmvOVUef+JFE= github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0 h1:KGHMW5sd7yDdDMkCZ/JpP0KltolFsQcB973brBnfj4c= @@ -57,7 +55,6 @@ github.com/btcsuite/btcwallet/walletdb v1.3.3 h1:u6e7vRIKBF++cJy+hOHaMGg+88ZTwvp github.com/btcsuite/btcwallet/walletdb v1.3.3/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= github.com/btcsuite/btcwallet/wtxmgr v1.0.0 h1:aIHgViEmZmZfe0tQQqF1xyd2qBqFWxX5vZXkkbjtbeA= github.com/btcsuite/btcwallet/wtxmgr v1.0.0/go.mod h1:vc4gBprll6BP0UJ+AIGDaySoc7MdAmZf8kelfNb8CFY= -github.com/btcsuite/btcwallet/wtxmgr v1.1.1-0.20200604005347-6390f167e5f8/go.mod h1:cJGqxXtqQbmXuL7RlXjIM18x0bGHy1407/85mQCLca4= github.com/btcsuite/btcwallet/wtxmgr v1.2.0 h1:ZUYPsSv8GjF9KK7lboB2OVHF0uYEcHxgrCfFWqPd9NA= github.com/btcsuite/btcwallet/wtxmgr v1.2.0/go.mod h1:h8hkcKUE3X7lMPzTUoGnNiw5g7VhGrKEW3KpR2r0VnY= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= @@ -134,15 +131,14 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.8.6 h1:XvND7+MPP7Jp+JpqSZ7naSl5nVZf6k0LbL1V3EKh0zc= github.com/grpc-ecosystem/grpc-gateway v1.8.6/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY= -github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc= @@ -153,12 +149,12 @@ github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4Fw github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.0 h1:J2SLSdy7HgElq8ekSl2Mxh6vrRNFxqbXGenYH2I02Vs= +github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 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.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c h1:3UvYABOQRhJAApj9MdCN+Ydv841ETSoy6xLzdmmr/9A= github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d h1:hJXjZMxj0SWlMoQkzeZDLi2cmeiWKa7y1B8Rg+qaoEc= @@ -188,12 +184,8 @@ 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/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= -github.com/lightninglabs/lndclient v0.0.0-20200618122423-5d815058a719 h1:RzyO65pK78xxSaekERq2V3g32GMBPc0MfxmM8MDMVmQ= -github.com/lightninglabs/lndclient v0.0.0-20200618122423-5d815058a719/go.mod h1:WQU2oE0eJIp7jtBOUnL7CO5/YoWrAqyH23A6M1r31i8= github.com/lightninglabs/lndclient v1.0.1-0.20200708223031-76709c25d859 h1:MrKZJ35EuWPAXR1tS8KCGL+hcsGwhC2jtL0AqlXXOvY= github.com/lightninglabs/lndclient v1.0.1-0.20200708223031-76709c25d859/go.mod h1:nKEd6j54LxPYuIe3K/BUwQPus24sbvWTdoweoBcHas0= -github.com/lightninglabs/loop v0.6.5-beta h1:KWAydG16aVI/D7gzFdKhwnprVCtJtbLiS4I/t3gkezA= -github.com/lightninglabs/loop v0.6.5-beta/go.mod h1:Pgj7xTC6T3wbCCBxKyOVWBpvDv9mas4zpvW8jZiwG0U= github.com/lightninglabs/neutrino v0.11.0 h1:lPpYFCtsfJX2W5zI4pWycPmbbBdr7zU+BafYdLoD6k0= github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg= github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200 h1:j4iZ1XlUAPQmW6oSzMcJGILYsRHNs+4O3Gk+2Ms5Dww= @@ -201,9 +193,6 @@ github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI= github.com/lightningnetwork/lightning-onion v1.0.1 h1:qChGgS5+aPxFeR6JiUsGvanei1bn6WJpYbvosw/1604= github.com/lightningnetwork/lightning-onion v1.0.1/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4= -github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea h1:oCj48NQ8u7Vz+MmzHqt0db6mxcFZo3Ho7M5gCJauY/k= -github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4= -github.com/lightningnetwork/lnd v0.10.0-beta.rc6.0.20200615174244-103c59a4889f/go.mod h1:a2ejAHgjuwQ9YQJBzlMD8ZJHUcyWxXb8HRGe5QlJh9Y= github.com/lightningnetwork/lnd v0.10.3-beta h1:bS9+zMEH7DHAc8yzEml/xBMCiB0Xo5K7R84hKLvO4ao= github.com/lightningnetwork/lnd v0.10.3-beta/go.mod h1:4d02pduRVtZwgTJ+EimKJTsEAY0jDwi0SPE9h5aRneM= github.com/lightningnetwork/lnd/cert v1.0.2 h1:g2rEu+sM2Uyz0bpfuvwri/ks6R/26H5iY1NcGbpDJ+c= @@ -277,13 +266,11 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc h1:yUaosFVTJwnltaHbSNC3i82I92quFs+OFPRl8kNMVwo= github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 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/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= @@ -297,8 +284,8 @@ go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= 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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -328,7 +315,6 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343 h1:00ohfJ4K98s3m6BGUoBd8nyfp4Yl0GoIKvw5abItTjI= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -347,7 +333,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -371,8 +356,6 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c h1:hrpEMCZ2O7DR5gC1n2AJGVhrwiEjOi35+jxtIuZpTMo= -google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -404,7 +387,6 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl 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= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -412,6 +394,5 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/internal/test/chainnotifier_mock.go b/internal/test/chainnotifier_mock.go new file mode 100644 index 0000000..fa8ff97 --- /dev/null +++ b/internal/test/chainnotifier_mock.go @@ -0,0 +1,160 @@ +package test + +import ( + "bytes" + "sync" + "time" + + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/chainntnfs" + "golang.org/x/net/context" +) + +type mockChainNotifier struct { + sync.Mutex + lnd *LndMockServices + confRegistrations []*ConfRegistration + wg sync.WaitGroup +} + +// SpendRegistration contains registration details. +type SpendRegistration struct { + Outpoint *wire.OutPoint + PkScript []byte + HeightHint int32 +} + +// ConfRegistration contains registration details. +type ConfRegistration struct { + TxID *chainhash.Hash + PkScript []byte + HeightHint int32 + NumConfs int32 + ConfChan chan *chainntnfs.TxConfirmation +} + +func (c *mockChainNotifier) RegisterSpendNtfn(ctx context.Context, + outpoint *wire.OutPoint, pkScript []byte, heightHint int32) ( + chan *chainntnfs.SpendDetail, chan error, error) { + + c.lnd.RegisterSpendChannel <- &SpendRegistration{ + HeightHint: heightHint, + Outpoint: outpoint, + PkScript: pkScript, + } + + spendChan := make(chan *chainntnfs.SpendDetail, 1) + errChan := make(chan error, 1) + + c.wg.Add(1) + go func() { + defer c.wg.Done() + + select { + case m := <-c.lnd.SpendChannel: + select { + case spendChan <- m: + case <-ctx.Done(): + } + case <-ctx.Done(): + } + }() + + return spendChan, errChan, nil +} + +func (c *mockChainNotifier) WaitForFinished() { + c.wg.Wait() +} + +func (c *mockChainNotifier) RegisterBlockEpochNtfn(ctx context.Context) ( + chan int32, chan error, error) { + + blockErrorChan := make(chan error, 1) + blockEpochChan := make(chan int32) + + c.wg.Add(1) + go func() { + defer c.wg.Done() + + // Send initial block height + select { + case blockEpochChan <- c.lnd.Height: + case <-ctx.Done(): + return + } + + for { + select { + case m := <-c.lnd.epochChannel: + select { + case blockEpochChan <- m: + case <-ctx.Done(): + return + } + case <-ctx.Done(): + return + } + } + }() + + return blockEpochChan, blockErrorChan, nil +} + +func (c *mockChainNotifier) RegisterConfirmationsNtfn(ctx context.Context, + txid *chainhash.Hash, pkScript []byte, numConfs, heightHint int32) ( + chan *chainntnfs.TxConfirmation, chan error, error) { + + reg := &ConfRegistration{ + PkScript: pkScript, + TxID: txid, + HeightHint: heightHint, + NumConfs: numConfs, + ConfChan: make(chan *chainntnfs.TxConfirmation, 1), + } + + c.Lock() + c.confRegistrations = append(c.confRegistrations, reg) + c.Unlock() + + errChan := make(chan error, 1) + + c.wg.Add(1) + go func() { + defer c.wg.Done() + + select { + case m := <-c.lnd.ConfChannel: + c.Lock() + for i := 0; i < len(c.confRegistrations); i++ { + r := c.confRegistrations[i] + + // Whichever conf notifier catches the confirmation + // will forward it to all matching subscibers. + if bytes.Equal(m.Tx.TxOut[0].PkScript, r.PkScript) { + // Unregister the "notifier". + c.confRegistrations = append( + c.confRegistrations[:i], c.confRegistrations[i+1:]..., + ) + i-- + + select { + case r.ConfChan <- m: + case <-ctx.Done(): + } + } + } + c.Unlock() + case <-ctx.Done(): + } + }() + + select { + case c.lnd.RegisterConfChannel <- reg: + case <-time.After(Timeout): + return nil, nil, ErrTimeout + } + + return reg.ConfChan, errChan, nil +} diff --git a/internal/test/context.go b/internal/test/context.go new file mode 100644 index 0000000..146573c --- /dev/null +++ b/internal/test/context.go @@ -0,0 +1,256 @@ +package test + +import ( + "bytes" + "crypto/sha256" + "testing" + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/zpay32" +) + +// Context contains shared test context functions. +type Context struct { + T *testing.T + Lnd *LndMockServices + FailedInvoices map[lntypes.Hash]struct{} + PaidInvoices map[string]func(error) +} + +// NewContext instanties a new common test context. +func NewContext(t *testing.T, + lnd *LndMockServices) Context { + + return Context{ + T: t, + Lnd: lnd, + FailedInvoices: make(map[lntypes.Hash]struct{}), + PaidInvoices: make(map[string]func(error)), + } +} + +// ReceiveTx receives and decodes a published tx. +func (ctx *Context) ReceiveTx() *wire.MsgTx { + ctx.T.Helper() + + select { + case tx := <-ctx.Lnd.TxPublishChannel: + return tx + case <-time.After(Timeout): + ctx.T.Fatalf("sweep not published") + return nil + } +} + +// NotifySpend simulates a spend. +func (ctx *Context) NotifySpend(tx *wire.MsgTx, inputIndex uint32) { + ctx.T.Helper() + + txHash := tx.TxHash() + + select { + case ctx.Lnd.SpendChannel <- &chainntnfs.SpendDetail{ + SpendingTx: tx, + SpenderTxHash: &txHash, + SpenderInputIndex: inputIndex, + }: + case <-time.After(Timeout): + ctx.T.Fatalf("htlc spend not consumed") + + } +} + +// NotifyConf simulates a conf. +func (ctx *Context) NotifyConf(tx *wire.MsgTx) { + ctx.T.Helper() + + select { + case ctx.Lnd.ConfChannel <- &chainntnfs.TxConfirmation{ + Tx: tx, + }: + case <-time.After(Timeout): + ctx.T.Fatalf("htlc spend not consumed") + + } +} + +// AssertRegisterSpendNtfn asserts that a register for spend has been received. +func (ctx *Context) AssertRegisterSpendNtfn(script []byte) { + ctx.T.Helper() + + select { + case spendIntent := <-ctx.Lnd.RegisterSpendChannel: + if !bytes.Equal(spendIntent.PkScript, script) { + ctx.T.Fatalf("server not listening for published htlc script") + } + case <-time.After(Timeout): + DumpGoroutines() + ctx.T.Fatalf("spend not subscribed to") + } +} + +// AssertTrackPayment asserts that a call was made to track payment, and +// returns the track payment message so that it can be used to send updates +// to the test. +func (ctx *Context) AssertTrackPayment() TrackPaymentMessage { + ctx.T.Helper() + + var msg TrackPaymentMessage + select { + case msg = <-ctx.Lnd.TrackPaymentChannel: + + case <-time.After(Timeout): + DumpGoroutines() + ctx.T.Fatalf("payment not tracked") + } + + return msg +} + +// AssertRegisterConf asserts that a register for conf has been received. +func (ctx *Context) AssertRegisterConf(expectTxHash bool) *ConfRegistration { + ctx.T.Helper() + + // Expect client to register for conf + var confIntent *ConfRegistration + select { + case confIntent = <-ctx.Lnd.RegisterConfChannel: + switch { + case expectTxHash && confIntent.TxID == nil: + ctx.T.Fatalf("expected tx id for registration") + + case !expectTxHash && confIntent.TxID != nil: + ctx.T.Fatalf("expected script only registration") + } + case <-time.After(Timeout): + ctx.T.Fatalf("htlc confirmed not subscribed to") + } + + return confIntent +} + +// AssertPaid asserts that the expected payment request has been paid. This +// function returns a complete function to signal the final payment result. +func (ctx *Context) AssertPaid( + expectedMemo string) func(error) { + + ctx.T.Helper() + + if done, ok := ctx.PaidInvoices[expectedMemo]; ok { + return done + } + + // Assert that client pays swap invoice. + for { + var swapPayment RouterPaymentChannelMessage + select { + case swapPayment = <-ctx.Lnd.RouterSendPaymentChannel: + case <-time.After(Timeout): + ctx.T.Fatalf("no payment sent for invoice: %v", + expectedMemo) + } + + payReq := ctx.DecodeInvoice(swapPayment.Invoice) + + if _, ok := ctx.PaidInvoices[*payReq.Description]; ok { + ctx.T.Fatalf("duplicate invoice paid: %v", + *payReq.Description) + } + + done := func(result error) { + if result != nil { + swapPayment.Errors <- result + return + } + swapPayment.Updates <- lndclient.PaymentStatus{ + State: lnrpc.Payment_SUCCEEDED, + } + } + + ctx.PaidInvoices[*payReq.Description] = done + + if *payReq.Description == expectedMemo { + return done + } + } +} + +// AssertSettled asserts that an invoice with the given hash is settled. +func (ctx *Context) AssertSettled( + expectedHash lntypes.Hash) lntypes.Preimage { + + ctx.T.Helper() + + select { + case preimage := <-ctx.Lnd.SettleInvoiceChannel: + hash := sha256.Sum256(preimage[:]) + if expectedHash != hash { + ctx.T.Fatalf("server claims with wrong preimage") + } + + return preimage + case <-time.After(Timeout): + } + ctx.T.Fatalf("invoice not settled") + return lntypes.Preimage{} +} + +// AssertFailed asserts that an invoice with the given hash is failed. +func (ctx *Context) AssertFailed(expectedHash lntypes.Hash) { + ctx.T.Helper() + + if _, ok := ctx.FailedInvoices[expectedHash]; ok { + return + } + + for { + select { + case hash := <-ctx.Lnd.FailInvoiceChannel: + ctx.FailedInvoices[expectedHash] = struct{}{} + if expectedHash == hash { + return + } + case <-time.After(Timeout): + ctx.T.Fatalf("invoice not failed") + } + } +} + +// DecodeInvoice decodes a payment request string. +func (ctx *Context) DecodeInvoice(request string) *zpay32.Invoice { + ctx.T.Helper() + + payReq, err := ctx.Lnd.DecodeInvoice(request) + if err != nil { + ctx.T.Fatal(err) + } + return payReq +} + +// GetOutputIndex returns the index in the tx outs of the given script hash. +func (ctx *Context) GetOutputIndex(tx *wire.MsgTx, + script []byte) int { + + for idx, out := range tx.TxOut { + if bytes.Equal(out.PkScript, script) { + return idx + } + } + + ctx.T.Fatal("htlc not present in tx") + return 0 +} + +// NotifyServerHeight notifies the server of the arrival of a new block and +// waits for the notification to be processed by selecting on a +// dedicated test channel. +func (ctx *Context) NotifyServerHeight(height int32) { + if err := ctx.Lnd.NotifyHeight(height); err != nil { + ctx.T.Fatal(err) + } +} diff --git a/internal/test/invoices_mock.go b/internal/test/invoices_mock.go new file mode 100644 index 0000000..fc00c9b --- /dev/null +++ b/internal/test/invoices_mock.go @@ -0,0 +1,110 @@ +package test + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/btcsuite/btcd/btcec" + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/zpay32" +) + +type mockInvoices struct { + lnd *LndMockServices + wg sync.WaitGroup +} + +func (s *mockInvoices) SettleInvoice(ctx context.Context, + preimage lntypes.Preimage) error { + + logger.Infof("Settle invoice %v with preimage %v", preimage.Hash(), + preimage) + + s.lnd.SettleInvoiceChannel <- preimage + + return nil +} + +func (s *mockInvoices) WaitForFinished() { + s.wg.Wait() +} + +func (s *mockInvoices) CancelInvoice(ctx context.Context, + hash lntypes.Hash) error { + + s.lnd.FailInvoiceChannel <- hash + + return nil +} + +func (s *mockInvoices) SubscribeSingleInvoice(ctx context.Context, + hash lntypes.Hash) (<-chan lndclient.InvoiceUpdate, + <-chan error, error) { + + updateChan := make(chan lndclient.InvoiceUpdate, 2) + errChan := make(chan error) + + select { + case s.lnd.SingleInvoiceSubcribeChannel <- &SingleInvoiceSubscription{ + Update: updateChan, + Err: errChan, + Hash: hash, + }: + case <-ctx.Done(): + return nil, nil, ctx.Err() + } + + return updateChan, errChan, nil +} + +func (s *mockInvoices) AddHoldInvoice(ctx context.Context, + in *invoicesrpc.AddInvoiceData) (string, error) { + + s.lnd.lock.Lock() + defer s.lnd.lock.Unlock() + + hash := in.Hash + + // Create and encode the payment request as a bech32 (zpay32) string. + creationDate := time.Now() + + payReq, err := zpay32.NewInvoice( + s.lnd.ChainParams, *hash, creationDate, + zpay32.Description(in.Memo), + zpay32.CLTVExpiry(in.CltvExpiry), + zpay32.Amount(in.Value), + ) + if err != nil { + return "", err + } + + privKey, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + return "", err + } + + payReqString, err := payReq.Encode( + zpay32.MessageSigner{ + SignCompact: func(hash []byte) ([]byte, error) { + // btcec.SignCompact returns a pubkey-recoverable signature + sig, err := btcec.SignCompact( + btcec.S256(), privKey, hash, true, + ) + if err != nil { + return nil, fmt.Errorf("can't sign the hash: %v", err) + } + + return sig, nil + }, + }, + ) + if err != nil { + return "", err + } + + return payReqString, nil +} diff --git a/internal/test/keys.go b/internal/test/keys.go new file mode 100644 index 0000000..aa11cb2 --- /dev/null +++ b/internal/test/keys.go @@ -0,0 +1,17 @@ +package test + +import ( + "github.com/btcsuite/btcd/btcec" +) + +// CreateKey returns a deterministically generated key pair. +func CreateKey(index int32) (*btcec.PrivateKey, *btcec.PublicKey) { + // Avoid all zeros, because it results in an invalid key. + privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), + []byte{0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, byte(index + 1)}) + + return privKey, pubKey +} diff --git a/internal/test/lightning_client_mock.go b/internal/test/lightning_client_mock.go new file mode 100644 index 0000000..61d5895 --- /dev/null +++ b/internal/test/lightning_client_mock.go @@ -0,0 +1,240 @@ +package test + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "sync" + "time" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/zpay32" + "golang.org/x/net/context" +) + +type mockLightningClient struct { + lnd *LndMockServices + wg sync.WaitGroup +} + +// PayInvoice pays an invoice. +func (h *mockLightningClient) PayInvoice(ctx context.Context, invoice string, + maxFee btcutil.Amount, + outgoingChannel *uint64) chan lndclient.PaymentResult { + + done := make(chan lndclient.PaymentResult, 1) + + h.lnd.SendPaymentChannel <- PaymentChannelMessage{ + PaymentRequest: invoice, + Done: done, + } + + return done +} + +func (h *mockLightningClient) WaitForFinished() { + h.wg.Wait() +} + +func (h *mockLightningClient) ConfirmedWalletBalance(ctx context.Context) ( + btcutil.Amount, error) { + + return 1000000, nil +} + +func (h *mockLightningClient) GetInfo(ctx context.Context) (*lndclient.Info, + error) { + + pubKeyBytes, err := hex.DecodeString(h.lnd.NodePubkey) + if err != nil { + return nil, err + } + var pubKey [33]byte + copy(pubKey[:], pubKeyBytes) + return &lndclient.Info{ + BlockHeight: 600, + IdentityPubkey: pubKey, + Uris: []string{h.lnd.NodePubkey + "@127.0.0.1:9735"}, + }, nil +} + +func (h *mockLightningClient) EstimateFeeToP2WSH(ctx context.Context, + amt btcutil.Amount, confTarget int32) (btcutil.Amount, + error) { + + return 3000, nil +} + +func (h *mockLightningClient) AddInvoice(ctx context.Context, + in *invoicesrpc.AddInvoiceData) (lntypes.Hash, string, error) { + + h.lnd.lock.Lock() + defer h.lnd.lock.Unlock() + + var hash lntypes.Hash + switch { + case in.Hash != nil: + hash = *in.Hash + case in.Preimage != nil: + hash = (*in.Preimage).Hash() + default: + if _, err := rand.Read(hash[:]); err != nil { + return lntypes.Hash{}, "", err + } + } + + // Create and encode the payment request as a bech32 (zpay32) string. + creationDate := time.Now() + + payReq, err := zpay32.NewInvoice( + h.lnd.ChainParams, hash, creationDate, + zpay32.Description(in.Memo), + zpay32.CLTVExpiry(in.CltvExpiry), + zpay32.Amount(in.Value), + ) + if err != nil { + return lntypes.Hash{}, "", err + } + + privKey, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + return lntypes.Hash{}, "", err + } + + payReqString, err := payReq.Encode( + zpay32.MessageSigner{ + SignCompact: func(hash []byte) ([]byte, error) { + // btcec.SignCompact returns a pubkey-recoverable signature + sig, err := btcec.SignCompact( + btcec.S256(), privKey, hash, true, + ) + if err != nil { + return nil, fmt.Errorf("can't sign the hash: %v", err) + } + + return sig, nil + }, + }, + ) + if err != nil { + return lntypes.Hash{}, "", err + } + + // Add the invoice we have created to our mock's set of invoices. + h.lnd.Invoices[hash] = &lndclient.Invoice{ + Preimage: nil, + Hash: hash, + PaymentRequest: payReqString, + Amount: in.Value, + CreationDate: creationDate, + State: channeldb.ContractOpen, + IsKeysend: false, + } + + return hash, payReqString, nil +} + +// LookupInvoice looks up an invoice in the mock's set of stored invoices. +// If it is not found, this call will fail. Note that these invoices should +// be settled using settleInvoice to have a preimage, settled state and settled +// date set. +func (h *mockLightningClient) LookupInvoice(_ context.Context, + hash lntypes.Hash) (*lndclient.Invoice, error) { + + h.lnd.lock.Lock() + defer h.lnd.lock.Unlock() + + inv, ok := h.lnd.Invoices[hash] + if !ok { + return nil, fmt.Errorf("invoice: %x not found", hash) + } + + return inv, nil +} + +// ListTransactions returns all known transactions of the backing lnd node. +func (h *mockLightningClient) ListTransactions( + _ context.Context) ([]lndclient.Transaction, error) { + + h.lnd.lock.Lock() + txs := h.lnd.Transactions + h.lnd.lock.Unlock() + + return txs, nil +} + +// ListChannels retrieves all channels of the backing lnd node. +func (h *mockLightningClient) ListChannels(ctx context.Context) ( + []lndclient.ChannelInfo, error) { + + return h.lnd.Channels, nil +} + +// ClosedChannels returns a list of our closed channels. +func (h *mockLightningClient) ClosedChannels(_ context.Context) ([]lndclient.ClosedChannel, + error) { + + return h.lnd.ClosedChannels, nil +} + +// ForwardingHistory returns the mock's set of forwarding events. +func (h *mockLightningClient) ForwardingHistory(_ context.Context, + _ lndclient.ForwardingHistoryRequest) (*lndclient.ForwardingHistoryResponse, + error) { + + return &lndclient.ForwardingHistoryResponse{ + LastIndexOffset: 0, + Events: h.lnd.ForwardingEvents, + }, nil +} + +// ListInvoices returns our mock's invoices. +func (h *mockLightningClient) ListInvoices(_ context.Context, + _ lndclient.ListInvoicesRequest) (*lndclient.ListInvoicesResponse, + error) { + + invoices := make([]lndclient.Invoice, 0, len(h.lnd.Invoices)) + for _, invoice := range h.lnd.Invoices { + invoices = append(invoices, *invoice) + } + + return &lndclient.ListInvoicesResponse{ + Invoices: invoices, + }, nil +} + +// ListPayments makes a paginated call to our list payments endpoint. +func (h *mockLightningClient) ListPayments(_ context.Context, + _ lndclient.ListPaymentsRequest) (*lndclient.ListPaymentsResponse, + error) { + + return &lndclient.ListPaymentsResponse{ + Payments: h.lnd.Payments, + }, nil +} + +// ChannelBackup retrieves the backup for a particular channel. The +// backup is returned as an encrypted chanbackup.Single payload. +func (h *mockLightningClient) ChannelBackup(context.Context, wire.OutPoint) ([]byte, error) { + return nil, nil +} + +// ChannelBackups retrieves backups for all existing pending open and +// open channels. The backups are returned as an encrypted +// chanbackup.Multi payload. +func (h *mockLightningClient) ChannelBackups(ctx context.Context) ([]byte, error) { + return nil, nil +} + +// DecodePaymentRequest decodes a payment request. +func (h *mockLightningClient) DecodePaymentRequest(_ context.Context, + _ string) (*lndclient.PaymentRequest, error) { + + return nil, nil +} diff --git a/internal/test/lnd_services_mock.go b/internal/test/lnd_services_mock.go new file mode 100644 index 0000000..acda454 --- /dev/null +++ b/internal/test/lnd_services_mock.go @@ -0,0 +1,263 @@ +package test + +import ( + "context" + "errors" + "sync" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/zpay32" +) + +var ( + testStartingHeight = int32(600) + testNodePubkey = "03f5374b16f0b1f1b49101de1b9d89e0b460bc57ce9c2f9" + + "132b73dfc76d3704daa" + testSignature = []byte{55, 66, 77, 88, 99} + testSignatureMsg = "test" +) + +// NewMockLnd returns a new instance of LndMockServices that can be used in unit +// tests. +func NewMockLnd() *LndMockServices { + lightningClient := &mockLightningClient{} + walletKit := &mockWalletKit{ + feeEstimates: make(map[int32]chainfee.SatPerKWeight), + } + chainNotifier := &mockChainNotifier{} + signer := &mockSigner{} + invoices := &mockInvoices{} + router := &mockRouter{} + versioner := newMockVersioner() + + lnd := LndMockServices{ + LndServices: lndclient.LndServices{ + WalletKit: walletKit, + Client: lightningClient, + ChainNotifier: chainNotifier, + Signer: signer, + Invoices: invoices, + Router: router, + ChainParams: &chaincfg.TestNet3Params, + Versioner: versioner, + }, + SendPaymentChannel: make(chan PaymentChannelMessage), + ConfChannel: make(chan *chainntnfs.TxConfirmation), + RegisterConfChannel: make(chan *ConfRegistration), + RegisterSpendChannel: make(chan *SpendRegistration), + SpendChannel: make(chan *chainntnfs.SpendDetail), + TxPublishChannel: make(chan *wire.MsgTx), + SendOutputsChannel: make(chan wire.MsgTx), + SettleInvoiceChannel: make(chan lntypes.Preimage), + SingleInvoiceSubcribeChannel: make(chan *SingleInvoiceSubscription), + + RouterSendPaymentChannel: make(chan RouterPaymentChannelMessage), + TrackPaymentChannel: make(chan TrackPaymentMessage), + + SignOutputRawChannel: make(chan SignOutputRawRequest), + + FailInvoiceChannel: make(chan lntypes.Hash, 2), + epochChannel: make(chan int32), + Height: testStartingHeight, + NodePubkey: testNodePubkey, + Signature: testSignature, + SignatureMsg: testSignatureMsg, + Invoices: make(map[lntypes.Hash]*lndclient.Invoice), + } + + lightningClient.lnd = &lnd + chainNotifier.lnd = &lnd + walletKit.lnd = &lnd + invoices.lnd = &lnd + router.lnd = &lnd + signer.lnd = &lnd + + // Also simulate the cached info that is loaded on startup. + info, _ := lightningClient.GetInfo(context.Background()) + version, _ := versioner.GetVersion(context.Background()) + lnd.LndServices.NodeAlias = info.Alias + lnd.LndServices.NodePubkey = info.IdentityPubkey + lnd.LndServices.Version = version + + lnd.WaitForFinished = func() { + chainNotifier.WaitForFinished() + lightningClient.WaitForFinished() + invoices.WaitForFinished() + } + + return &lnd +} + +// PaymentChannelMessage is the data that passed through SendPaymentChannel. +type PaymentChannelMessage struct { + PaymentRequest string + Done chan lndclient.PaymentResult +} + +// TrackPaymentMessage is the data that passed through TrackPaymentChannel. +type TrackPaymentMessage struct { + Hash lntypes.Hash + + Updates chan lndclient.PaymentStatus + Errors chan error +} + +// RouterPaymentChannelMessage is the data that passed through RouterSendPaymentChannel. +type RouterPaymentChannelMessage struct { + lndclient.SendPaymentRequest + + TrackPaymentMessage +} + +// SingleInvoiceSubscription contains the single invoice subscribers +type SingleInvoiceSubscription struct { + Hash lntypes.Hash + Update chan lndclient.InvoiceUpdate + Err chan error +} + +// SignOutputRawRequest contains input data for a tx signing request. +type SignOutputRawRequest struct { + Tx *wire.MsgTx + SignDescriptors []*input.SignDescriptor +} + +// LndMockServices provides a full set of mocked lnd services. +type LndMockServices struct { + lndclient.LndServices + + SendPaymentChannel chan PaymentChannelMessage + SpendChannel chan *chainntnfs.SpendDetail + TxPublishChannel chan *wire.MsgTx + SendOutputsChannel chan wire.MsgTx + SettleInvoiceChannel chan lntypes.Preimage + FailInvoiceChannel chan lntypes.Hash + epochChannel chan int32 + + ConfChannel chan *chainntnfs.TxConfirmation + RegisterConfChannel chan *ConfRegistration + RegisterSpendChannel chan *SpendRegistration + + SingleInvoiceSubcribeChannel chan *SingleInvoiceSubscription + + RouterSendPaymentChannel chan RouterPaymentChannelMessage + TrackPaymentChannel chan TrackPaymentMessage + + SignOutputRawChannel chan SignOutputRawRequest + + Height int32 + NodePubkey string + Signature []byte + SignatureMsg string + + Transactions []lndclient.Transaction + Sweeps []string + + // Invoices is a set of invoices that have been created by the mock, + // keyed by hash string. + Invoices map[lntypes.Hash]*lndclient.Invoice + + Channels []lndclient.ChannelInfo + ClosedChannels []lndclient.ClosedChannel + ForwardingEvents []lndclient.ForwardingEvent + Payments []lndclient.Payment + + WaitForFinished func() + + lock sync.Mutex +} + +// NotifyHeight notifies a new block height. +func (s *LndMockServices) NotifyHeight(height int32) error { + s.Height = height + + select { + case s.epochChannel <- height: + case <-time.After(Timeout): + return ErrTimeout + } + return nil +} + +// AddRelevantTx marks the given transaction as relevant. +func (s *LndMockServices) AddTx(tx *wire.MsgTx) { + s.lock.Lock() + s.Transactions = append(s.Transactions, lndclient.Transaction{ + Tx: tx.Copy(), + }) + s.lock.Unlock() +} + +// IsDone checks whether all channels have been fully emptied. If not this may +// indicate unexpected behaviour of the code under test. +func (s *LndMockServices) IsDone() error { + select { + case <-s.SendPaymentChannel: + return errors.New("SendPaymentChannel not empty") + default: + } + + select { + case <-s.SpendChannel: + return errors.New("SpendChannel not empty") + default: + } + + select { + case <-s.TxPublishChannel: + return errors.New("TxPublishChannel not empty") + default: + } + + select { + case <-s.SendOutputsChannel: + return errors.New("SendOutputsChannel not empty") + default: + } + + select { + case <-s.SettleInvoiceChannel: + return errors.New("SettleInvoiceChannel not empty") + default: + } + + select { + case <-s.ConfChannel: + return errors.New("ConfChannel not empty") + default: + } + + select { + case <-s.RegisterConfChannel: + return errors.New("RegisterConfChannel not empty") + default: + } + + select { + case <-s.RegisterSpendChannel: + return errors.New("RegisterSpendChannel not empty") + default: + } + + return nil +} + +// DecodeInvoice decodes a payment request string. +func (s *LndMockServices) DecodeInvoice(request string) (*zpay32.Invoice, + error) { + + return zpay32.Decode(request, s.ChainParams) +} + +func (s *LndMockServices) SetFeeEstimate(confTarget int32, + feeEstimate chainfee.SatPerKWeight) { + + s.WalletKit.(*mockWalletKit).feeEstimates[confTarget] = feeEstimate +} diff --git a/internal/test/log.go b/internal/test/log.go new file mode 100644 index 0000000..e351129 --- /dev/null +++ b/internal/test/log.go @@ -0,0 +1,24 @@ +package test + +import ( + "os" + + "github.com/btcsuite/btclog" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var ( + backendLog = btclog.NewBackend(logWriter{}) + logger = backendLog.Logger("TEST") +) + +// logWriter implements an io.Writer that outputs to both standard output and +// the write-end pipe of an initialized log rotator. +type logWriter struct{} + +func (logWriter) Write(p []byte) (n int, err error) { + os.Stdout.Write(p) + return len(p), nil +} diff --git a/internal/test/router_mock.go b/internal/test/router_mock.go new file mode 100644 index 0000000..f7fbbaa --- /dev/null +++ b/internal/test/router_mock.go @@ -0,0 +1,43 @@ +package test + +import ( + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/lntypes" + "golang.org/x/net/context" +) + +type mockRouter struct { + lnd *LndMockServices +} + +func (r *mockRouter) SendPayment(ctx context.Context, + request lndclient.SendPaymentRequest) (chan lndclient.PaymentStatus, + chan error, error) { + + statusChan := make(chan lndclient.PaymentStatus) + errorChan := make(chan error) + + r.lnd.RouterSendPaymentChannel <- RouterPaymentChannelMessage{ + SendPaymentRequest: request, + TrackPaymentMessage: TrackPaymentMessage{ + Updates: statusChan, + Errors: errorChan, + }, + } + + return statusChan, errorChan, nil +} + +func (r *mockRouter) TrackPayment(ctx context.Context, + hash lntypes.Hash) (chan lndclient.PaymentStatus, chan error, error) { + + statusChan := make(chan lndclient.PaymentStatus) + errorChan := make(chan error) + r.lnd.TrackPaymentChannel <- TrackPaymentMessage{ + Hash: hash, + Updates: statusChan, + Errors: errorChan, + } + + return statusChan, errorChan, nil +} diff --git a/internal/test/signer_mock.go b/internal/test/signer_mock.go new file mode 100644 index 0000000..7c3b768 --- /dev/null +++ b/internal/test/signer_mock.go @@ -0,0 +1,58 @@ +package test + +import ( + "bytes" + "context" + "fmt" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" +) + +type mockSigner struct { + lnd *LndMockServices +} + +func (s *mockSigner) SignOutputRaw(ctx context.Context, tx *wire.MsgTx, + signDescriptors []*input.SignDescriptor) ([][]byte, error) { + + s.lnd.SignOutputRawChannel <- SignOutputRawRequest{ + Tx: tx, + SignDescriptors: signDescriptors, + } + + rawSigs := [][]byte{{1, 2, 3}} + + return rawSigs, nil +} + +func (s *mockSigner) ComputeInputScript(ctx context.Context, tx *wire.MsgTx, + signDescriptors []*input.SignDescriptor) ([]*input.Script, error) { + + return nil, fmt.Errorf("unimplemented") +} + +func (s *mockSigner) SignMessage(ctx context.Context, msg []byte, + locator keychain.KeyLocator) ([]byte, error) { + + return s.lnd.Signature, nil +} + +func (s *mockSigner) VerifyMessage(ctx context.Context, msg, sig []byte, + pubkey [33]byte) (bool, error) { + + // Make the mock somewhat functional by asserting that the message and + // signature is what we expect from the mock parameters. + mockAssertion := bytes.Equal(msg, []byte(s.lnd.SignatureMsg)) && + bytes.Equal(sig, s.lnd.Signature) + + return mockAssertion, nil +} + +func (s *mockSigner) DeriveSharedKey(context.Context, *btcec.PublicKey, + *keychain.KeyLocator) ([32]byte, error) { + + return [32]byte{4, 5, 6}, nil +} diff --git a/internal/test/testutils.go b/internal/test/testutils.go new file mode 100644 index 0000000..0f441d2 --- /dev/null +++ b/internal/test/testutils.go @@ -0,0 +1,98 @@ +package test + +import ( + "errors" + "fmt" + "os" + "runtime/pprof" + "testing" + "time" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/zpay32" +) + +var ( + // Timeout is the default timeout when tests wait for something to + // happen. + Timeout = time.Second * 5 + + // ErrTimeout is returned on timeout. + ErrTimeout = errors.New("test timeout") + + testTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC) +) + +// GetDestAddr deterministically generates a sweep address for testing. +func GetDestAddr(t *testing.T, nr byte) btcutil.Address { + destAddr, err := btcutil.NewAddressScriptHash([]byte{nr}, + &chaincfg.MainNetParams) + if err != nil { + t.Fatal(err) + } + + return destAddr +} + +// EncodePayReq encodes a zpay32 invoice with a fixed key. +func EncodePayReq(payReq *zpay32.Invoice) (string, error) { + privKey, _ := CreateKey(5) + reqString, err := payReq.Encode( + zpay32.MessageSigner{ + SignCompact: func(hash []byte) ([]byte, error) { + // btcec.SignCompact returns a + // pubkey-recoverable signature + sig, err := btcec.SignCompact( + btcec.S256(), + privKey, + payReq.PaymentHash[:], + true, + ) + if err != nil { + return nil, fmt.Errorf( + "can't sign the hash: %v", err) + } + + return sig, nil + }, + }, + ) + if err != nil { + return "", err + } + + return reqString, nil +} + +// GetInvoice creates a testnet payment request with the given parameters. +func GetInvoice(hash lntypes.Hash, amt btcutil.Amount, memo string) ( + string, error) { + + req, err := zpay32.NewInvoice( + &chaincfg.TestNet3Params, hash, testTime, + zpay32.Description(memo), + zpay32.Amount(lnwire.NewMSatFromSatoshis(amt)), + ) + if err != nil { + return "", err + } + + reqString, err := EncodePayReq(req) + if err != nil { + return "", err + } + + return reqString, nil +} + +// DumpGoroutines dumps all currently running goroutines. +func DumpGoroutines() { + err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) + if err != nil { + panic(err) + } +} diff --git a/internal/test/timeout.go b/internal/test/timeout.go new file mode 100644 index 0000000..52d4762 --- /dev/null +++ b/internal/test/timeout.go @@ -0,0 +1,34 @@ +package test + +import ( + "os" + "runtime/pprof" + "testing" + "time" + + "github.com/fortytw2/leaktest" +) + +// Guard implements a test level timeout. +func Guard(t *testing.T) func() { + done := make(chan struct{}) + go func() { + select { + case <-time.After(5 * time.Second): + err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) + if err != nil { + panic(err) + } + + panic("test timeout") + case <-done: + } + }() + + fn := leaktest.Check(t) + + return func() { + close(done) + fn() + } +} diff --git a/internal/test/versioner_mock.go b/internal/test/versioner_mock.go new file mode 100644 index 0000000..7722e03 --- /dev/null +++ b/internal/test/versioner_mock.go @@ -0,0 +1,51 @@ +package test + +import ( + "context" + + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/lnrpc/verrpc" +) + +const ( + defaultMockCommit = "v0.99.9-beta" + defaultMockCommitHash = "0000000000000000000000000000000000000000" + defaultMockVersion = "v0.99.9-beta" + defaultMockAppMajor = 0 + defaultMockAppMinor = 99 + defaultMockAppPatch = 9 + defaultMockAppPrerelease = "beta" + defaultMockAppGoVersion = "go1.99.9" +) + +var ( + defaultMockBuildTags = []string{ + "signrpc", "walletrpc", "chainrpc", "invoicesrpc", + } +) + +type mockVersioner struct { + version *verrpc.Version +} + +var _ lndclient.VersionerClient = (*mockVersioner)(nil) + +func newMockVersioner() *mockVersioner { + return &mockVersioner{ + version: &verrpc.Version{ + Commit: defaultMockCommit, + CommitHash: defaultMockCommitHash, + Version: defaultMockVersion, + AppMajor: defaultMockAppMajor, + AppMinor: defaultMockAppMinor, + AppPatch: defaultMockAppPatch, + AppPreRelease: defaultMockAppPrerelease, + BuildTags: defaultMockBuildTags, + GoVersion: defaultMockAppGoVersion, + }, + } +} + +func (v *mockVersioner) GetVersion(_ context.Context) (*verrpc.Version, error) { + return v.version, nil +} diff --git a/internal/test/walletkit_mock.go b/internal/test/walletkit_mock.go new file mode 100644 index 0000000..45f2e09 --- /dev/null +++ b/internal/test/walletkit_mock.go @@ -0,0 +1,133 @@ +package test + +import ( + "context" + "errors" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/wtxmgr" + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" +) + +type mockWalletKit struct { + lnd *LndMockServices + keyIndex int32 + feeEstimates map[int32]chainfee.SatPerKWeight +} + +var _ lndclient.WalletKitClient = (*mockWalletKit)(nil) + +func (m *mockWalletKit) ListUnspent(ctx context.Context, minConfs, + maxConfs int32) ([]*lnwallet.Utxo, error) { + + return nil, nil +} + +func (m *mockWalletKit) LeaseOutput(ctx context.Context, lockID wtxmgr.LockID, + op wire.OutPoint) (time.Time, error) { + + return time.Now(), nil +} + +func (m *mockWalletKit) ReleaseOutput(ctx context.Context, + lockID wtxmgr.LockID, op wire.OutPoint) error { + + return nil +} + +func (m *mockWalletKit) DeriveNextKey(ctx context.Context, family int32) ( + *keychain.KeyDescriptor, error) { + + index := m.keyIndex + + _, pubKey := CreateKey(index) + m.keyIndex++ + + return &keychain.KeyDescriptor{ + KeyLocator: keychain.KeyLocator{ + Family: keychain.KeyFamily(family), + Index: uint32(index), + }, + PubKey: pubKey, + }, nil +} + +func (m *mockWalletKit) DeriveKey(ctx context.Context, in *keychain.KeyLocator) ( + *keychain.KeyDescriptor, error) { + + _, pubKey := CreateKey(int32(in.Index)) + + return &keychain.KeyDescriptor{ + KeyLocator: *in, + PubKey: pubKey, + }, nil +} + +func (m *mockWalletKit) NextAddr(ctx context.Context) (btcutil.Address, error) { + addr, err := btcutil.NewAddressWitnessPubKeyHash( + make([]byte, 20), &chaincfg.TestNet3Params, + ) + if err != nil { + return nil, err + } + return addr, nil +} + +func (m *mockWalletKit) PublishTransaction(ctx context.Context, tx *wire.MsgTx) error { + m.lnd.AddTx(tx) + m.lnd.TxPublishChannel <- tx + return nil +} + +func (m *mockWalletKit) SendOutputs(ctx context.Context, outputs []*wire.TxOut, + feeRate chainfee.SatPerKWeight) (*wire.MsgTx, error) { + + var inputTxHash chainhash.Hash + + tx := wire.MsgTx{} + tx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: wire.OutPoint{ + Hash: inputTxHash, + Index: 0, + }, + }) + + for _, out := range outputs { + tx.AddTxOut(&wire.TxOut{ + PkScript: out.PkScript, + Value: out.Value, + }) + } + + m.lnd.AddTx(&tx) + m.lnd.SendOutputsChannel <- tx + + return &tx, nil +} + +func (m *mockWalletKit) EstimateFee(ctx context.Context, confTarget int32) ( + chainfee.SatPerKWeight, error) { + + if confTarget <= 1 { + return 0, errors.New("conf target must be greater than 1") + } + + feeEstimate, ok := m.feeEstimates[confTarget] + if !ok { + return 10000, nil + } + + return feeEstimate, nil +} + +// ListSweeps returns a list of the sweep transaction ids known to our node. +func (m *mockWalletKit) ListSweeps(_ context.Context) ([]string, error) { + return m.lnd.Sweeps, nil +} diff --git a/lsat/client_interceptor_test.go b/lsat/client_interceptor_test.go index cbe32bf..a8d6251 100644 --- a/lsat/client_interceptor_test.go +++ b/lsat/client_interceptor_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" + "github.com/lightninglabs/aperture/internal/test" "github.com/lightninglabs/lndclient" - "github.com/lightninglabs/loop/test" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" "google.golang.org/grpc" diff --git a/lsat/header.go b/lsat/header.go index e770c06..177c2cc 100644 --- a/lsat/header.go +++ b/lsat/header.go @@ -8,7 +8,6 @@ import ( "net/http" "regexp" - "github.com/lightninglabs/loop/lsat" "github.com/lightningnetwork/lnd/lntypes" "gopkg.in/macaroon.v2" ) @@ -111,7 +110,7 @@ func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, erro return nil, lntypes.Preimage{}, fmt.Errorf("unable to "+ "unmarshal macaroon: %v", err) } - preimageHex, ok := lsat.HasCaveat(mac, lsat.PreimageKey) + preimageHex, ok := HasCaveat(mac, PreimageKey) if !ok { return nil, lntypes.Preimage{}, errors.New("preimage caveat " + "not found") From 96e69a979cdc52456f036fe131805702371e0f24 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 7 Aug 2020 17:33:05 +0200 Subject: [PATCH 3/7] challenger+auth: implement invoice checker --- aperture.go | 2 +- auth/authenticator.go | 23 +++- auth/authenticator_test.go | 22 ++- auth/interface.go | 20 +++ auth/mock_test.go | 15 +++ challenger.go | 268 ++++++++++++++++++++++++++++++++++++- 6 files changed, 339 insertions(+), 11 deletions(-) diff --git a/aperture.go b/aperture.go index f50faa2..52885d3 100644 --- a/aperture.go +++ b/aperture.go @@ -390,7 +390,7 @@ func createProxy(cfg *config, genInvoiceReq InvoiceRequestGenerator, Secrets: newSecretStore(etcdClient), ServiceLimiter: newStaticServiceLimiter(cfg.Services), }) - authenticator := auth.NewLsatAuthenticator(minter) + authenticator := auth.NewLsatAuthenticator(minter, challenger) return proxy.New( authenticator, cfg.Services, cfg.ServeStatic, cfg.StaticRoot, ) diff --git a/auth/authenticator.go b/auth/authenticator.go index ce568ea..d43c17a 100644 --- a/auth/authenticator.go +++ b/auth/authenticator.go @@ -8,12 +8,14 @@ import ( "github.com/lightninglabs/aperture/lsat" "github.com/lightninglabs/aperture/mint" + "github.com/lightningnetwork/lnd/lnrpc" ) // LsatAuthenticator is an authenticator that uses the LSAT protocol to // authenticate requests. type LsatAuthenticator struct { - minter Minter + minter Minter + checker InvoiceChecker } // A compile time flag to ensure the LsatAuthenticator satisfies the @@ -22,8 +24,13 @@ var _ Authenticator = (*LsatAuthenticator)(nil) // NewLsatAuthenticator creates a new authenticator that authenticates requests // based on LSAT tokens. -func NewLsatAuthenticator(minter Minter) *LsatAuthenticator { - return &LsatAuthenticator{minter: minter} +func NewLsatAuthenticator(minter Minter, + checker InvoiceChecker) *LsatAuthenticator { + + return &LsatAuthenticator{ + minter: minter, + checker: checker, + } } // Accept returns whether or not the header successfully authenticates the user @@ -51,6 +58,16 @@ func (l *LsatAuthenticator) Accept(header *http.Header, serviceName string) bool return false } + // Make sure the backend has the invoice recorded as settled. + err = l.checker.VerifyInvoiceStatus( + preimage.Hash(), lnrpc.Invoice_SETTLED, + DefaultInvoiceLookupTimeout, + ) + if err != nil { + log.Debugf("Deny: Invoice status mismatch: %v", err) + return false + } + return true } diff --git a/auth/authenticator_test.go b/auth/authenticator_test.go index 2810cfb..efd3629 100644 --- a/auth/authenticator_test.go +++ b/auth/authenticator_test.go @@ -3,6 +3,7 @@ package auth_test import ( "encoding/base64" "encoding/hex" + "fmt" "net/http" "testing" @@ -44,9 +45,10 @@ func TestLsatAuthenticator(t *testing.T) { testMacBytes, ) headerTests = []struct { - id string - header *http.Header - result bool + id string + header *http.Header + checkErr error + result bool }{ { id: "empty header", @@ -124,11 +126,23 @@ func TestLsatAuthenticator(t *testing.T) { }, result: true, }, + { + id: "valid macaroon header, wrong invoice state", + header: &http.Header{ + lsat.HeaderMacaroon: []string{ + testMacHex, + }, + }, + checkErr: fmt.Errorf("nope"), + result: false, + }, } ) - a := auth.NewLsatAuthenticator(&mockMint{}) + c := &mockChecker{} + a := auth.NewLsatAuthenticator(&mockMint{}, c) for _, testCase := range headerTests { + c.err = testCase.checkErr result := a.Accept(testCase.header, "test") if result != testCase.result { t.Fatalf("test case %s failed. got %v expected %v", diff --git a/auth/interface.go b/auth/interface.go index 70129a5..08551ab 100644 --- a/auth/interface.go +++ b/auth/interface.go @@ -3,12 +3,21 @@ package auth import ( "context" "net/http" + "time" "github.com/lightninglabs/aperture/lsat" "github.com/lightninglabs/aperture/mint" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntypes" "gopkg.in/macaroon.v2" ) +const ( + // DefaultInvoiceLookupTimeout is the default maximum time we wait for + // an invoice update to arrive. + DefaultInvoiceLookupTimeout = 3 * time.Second +) + // Authenticator is the generic interface for validating client headers and // returning new challenge headers. type Authenticator interface { @@ -30,3 +39,14 @@ type Minter interface { // VerifyLSAT attempts to verify an LSAT with the given parameters. VerifyLSAT(context.Context, *mint.VerificationParams) error } + +// InvoiceChecker is an entity that is able to check the status of an invoice, +// particularly whether it's been paid or not. +type InvoiceChecker interface { + // VerifyInvoiceStatus checks that an invoice identified by a payment + // hash has the desired status. To make sure we don't fail while the + // invoice update is still on its way, we try several times until either + // the desired status is set or the given timeout is reached. + VerifyInvoiceStatus(lntypes.Hash, lnrpc.Invoice_InvoiceState, + time.Duration) error +} diff --git a/auth/mock_test.go b/auth/mock_test.go index 1b0a806..c2f92b1 100644 --- a/auth/mock_test.go +++ b/auth/mock_test.go @@ -2,10 +2,13 @@ package auth_test import ( "context" + "time" "github.com/lightninglabs/aperture/auth" "github.com/lightninglabs/aperture/lsat" "github.com/lightninglabs/aperture/mint" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntypes" "gopkg.in/macaroon.v2" ) @@ -23,3 +26,15 @@ func (m *mockMint) MintLSAT(_ context.Context, func (m *mockMint) VerifyLSAT(_ context.Context, p *mint.VerificationParams) error { return nil } + +type mockChecker struct { + err error +} + +var _ auth.InvoiceChecker = (*mockChecker)(nil) + +func (m *mockChecker) VerifyInvoiceStatus(lntypes.Hash, + lnrpc.Invoice_InvoiceState, time.Duration) error { + + return m.err +} diff --git a/challenger.go b/challenger.go index 066cca2..f182062 100644 --- a/challenger.go +++ b/challenger.go @@ -3,7 +3,13 @@ package aperture import ( "context" "fmt" + "io" + "math" + "strings" + "sync" + "time" + "github.com/lightninglabs/aperture/auth" "github.com/lightninglabs/aperture/mint" "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lnrpc" @@ -19,14 +25,23 @@ type InvoiceRequestGenerator func(price int64) (*lnrpc.Invoice, error) type LndChallenger struct { client lnrpc.LightningClient genInvoiceReq InvoiceRequestGenerator + + invoiceStates map[lntypes.Hash]lnrpc.Invoice_InvoiceState + invoicesMtx *sync.Mutex + invoicesCancel func() + invoicesCond *sync.Cond + + quit chan struct{} + wg sync.WaitGroup } // A compile time flag to ensure the LndChallenger satisfies the -// mint.Challenger interface. +// mint.Challenger and auth.InvoiceChecker interface. var _ mint.Challenger = (*LndChallenger)(nil) +var _ auth.InvoiceChecker = (*LndChallenger)(nil) const ( - // invoiceMacaroonName is the name of the read-only macaroon belonging + // invoiceMacaroonName is the name of the invoice macaroon belonging // to the target lnd node. invoiceMacaroonName = "invoice.macaroon" ) @@ -47,16 +62,162 @@ func NewLndChallenger(cfg *authConfig, genInvoiceReq InvoiceRequestGenerator) ( if err != nil { return nil, err } + + invoicesMtx := &sync.Mutex{} return &LndChallenger{ client: client, genInvoiceReq: genInvoiceReq, + invoiceStates: make(map[lntypes.Hash]lnrpc.Invoice_InvoiceState), + invoicesMtx: invoicesMtx, + invoicesCond: sync.NewCond(invoicesMtx), + quit: make(chan struct{}), }, nil } +// Start starts the challenger's main work which is to keep track of all +// invoices and their states. For that the backing lnd node is queried for all +// invoices on startup and the a subscription to all subsequent invoice updates +// is created. +func (l *LndChallenger) Start() error { + // These are the default values for the subscription. In case there are + // no invoices yet, this will instruct lnd to just send us all updates. + // If there are existing invoices, these indices will be updated to + // reflect the latest known invoices. + addIndex := uint64(0) + settleIndex := uint64(0) + + // Get a list of all existing invoices on startup and add them to our + // cache. We need to keep track of all invoices, even quite old ones to + // make sure tokens are valid. But to save space we only keep track of + // an invoice's state. + invoiceResp, err := l.client.ListInvoices( + context.Background(), &lnrpc.ListInvoiceRequest{ + NumMaxInvoices: math.MaxUint64, + }, + ) + if err != nil { + return err + } + + // Advance our indices to the latest known one so we'll only receive + // updates for new invoices and/or newly settled invoices. + l.invoicesMtx.Lock() + for _, invoice := range invoiceResp.Invoices { + if invoice.AddIndex > addIndex { + addIndex = invoice.AddIndex + } + if invoice.SettleIndex > settleIndex { + settleIndex = invoice.SettleIndex + } + hash, err := lntypes.MakeHash(invoice.RHash) + if err != nil { + l.invoicesMtx.Unlock() + return fmt.Errorf("error parsing invoice hash: %v", err) + } + + // Don't track the state of canceled or expired invoices. + if invoiceIrrelevant(invoice) { + continue + } + l.invoiceStates[hash] = invoice.State + } + l.invoicesMtx.Unlock() + + // We need to be able to cancel any subscription we make. + ctxc, cancel := context.WithCancel(context.Background()) + l.invoicesCancel = cancel + + subscriptionResp, err := l.client.SubscribeInvoices( + ctxc, &lnrpc.InvoiceSubscription{ + AddIndex: addIndex, + SettleIndex: settleIndex, + }, + ) + if err != nil { + cancel() + return err + } + + l.wg.Add(1) + go func() { + defer l.wg.Done() + defer cancel() + + l.readInvoiceStream(subscriptionResp) + }() + + return nil +} + +// readInvoiceStream reads the invoice update messages sent on the stream until +// the stream is aborted or the challenger is shutting down. +func (l *LndChallenger) readInvoiceStream( + stream lnrpc.Lightning_SubscribeInvoicesClient) { + + for { + // In case we receive the shutdown signal right after receiving + // an update, we can exit early. + select { + case <-l.quit: + return + default: + } + + // Wait for an update to arrive. This will block until either a + // message receives, an error occurs or the underlying context + // is canceled (which will also result in an error). + invoice, err := stream.Recv() + switch { + + case err == io.EOF: + return + + case err != nil && strings.Contains( + err.Error(), context.Canceled.Error(), + ): + + return + + case err != nil: + log.Errorf("Received error from invoice subscription: "+ + "%v", err) + return + + default: + } + + hash, err := lntypes.MakeHash(invoice.RHash) + if err != nil { + log.Errorf("Error parsing invoice hash: %v", err) + return + } + + l.invoicesMtx.Lock() + if invoiceIrrelevant(invoice) { + // Don't keep the state of canceled or expired invoices. + delete(l.invoiceStates, hash) + } else { + l.invoiceStates[hash] = invoice.State + } + + // Before releasing the lock, notify our conditions that listen + // for updates on the invoice state. + l.invoicesCond.Broadcast() + l.invoicesMtx.Unlock() + } +} + +// Stop shuts down the challenger. +func (l *LndChallenger) Stop() { + l.invoicesCancel() + close(l.quit) + l.wg.Wait() +} + // NewChallenge creates a new LSAT payment challenge, returning a payment // request (invoice) and the corresponding payment hash. // -// NOTE: This is part of the Challenger interface. +// NOTE: This is part of the mint.Challenger interface. func (l *LndChallenger) NewChallenge(price int64) (string, lntypes.Hash, error) { // Obtain a new invoice from lnd first. We need to know the payment hash // so we can add it as a caveat to the macaroon. @@ -79,3 +240,104 @@ func (l *LndChallenger) NewChallenge(price int64) (string, lntypes.Hash, error) return response.PaymentRequest, paymentHash, nil } + +// VerifyInvoiceStatus checks that an invoice identified by a payment +// hash has the desired status. To make sure we don't fail while the +// invoice update is still on its way, we try several times until either +// the desired status is set or the given timeout is reached. +// +// NOTE: This is part of the auth.InvoiceChecker interface. +func (l *LndChallenger) VerifyInvoiceStatus(hash lntypes.Hash, + state lnrpc.Invoice_InvoiceState, timeout time.Duration) error { + + // Prevent the challenger to be shut down while we're still waiting for + // status updates. + l.wg.Add(1) + defer l.wg.Done() + + var ( + condWg sync.WaitGroup + doneChan = make(chan struct{}) + timeoutReached bool + hasInvoice bool + invoiceState lnrpc.Invoice_InvoiceState + ) + + // First of all, spawn a goroutine that will signal us on timeout. + // Otherwise if a client subscribes to an update on an invoice that + // never arrives, and there is no other activity, it would block + // forever in the condition. + condWg.Add(1) + go func() { + defer condWg.Done() + + select { + case <-doneChan: + case <-time.After(timeout): + case <-l.quit: + } + + l.invoicesCond.L.Lock() + timeoutReached = true + l.invoicesCond.Broadcast() + l.invoicesCond.L.Unlock() + }() + + // Now create the main goroutine that blocks until an update is received + // on the condition. + condWg.Add(1) + go func() { + defer condWg.Done() + l.invoicesCond.L.Lock() + + // Block here until our condition is met or the allowed time is + // up. The Wait() will return whenever a signal is broadcast. + invoiceState, hasInvoice = l.invoiceStates[hash] + for !(hasInvoice && invoiceState == state) && !timeoutReached { + l.invoicesCond.Wait() + + // The Wait() above has re-acquired the lock so we can + // safely access the states map. + invoiceState, hasInvoice = l.invoiceStates[hash] + } + + // We're now done. + l.invoicesCond.L.Unlock() + close(doneChan) + }() + + // Wait until we're either done or timed out. + condWg.Wait() + + // Interpret the result so we can return a more descriptive error than + // just "failed". + switch { + case !hasInvoice: + return fmt.Errorf("no active or settled invoice found for "+ + "hash=%v", hash) + + case invoiceState != state: + return fmt.Errorf("invoice status not correct before timeout, "+ + "hash=%v, status=%v", hash, invoiceState) + + default: + return nil + } +} + +// invoiceIrrelevant returns true if an invoice is nil, canceled or non-settled +// and expired. +func invoiceIrrelevant(invoice *lnrpc.Invoice) bool { + if invoice == nil || invoice.State == lnrpc.Invoice_CANCELED { + return true + } + + creation := time.Unix(invoice.CreationDate, 0) + expiration := creation.Add(time.Duration(invoice.Expiry) * time.Second) + expired := time.Now().After(expiration) + + notSettled := invoice.State == lnrpc.Invoice_OPEN || + invoice.State == lnrpc.Invoice_ACCEPTED + + return expired && notSettled +} From 475bfb9675225c55c72591e1a1e88c39d4113462 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 7 Aug 2020 17:33:06 +0200 Subject: [PATCH 4/7] aperture: create and start/stop challenger --- aperture.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/aperture.go b/aperture.go index 52885d3..037d311 100644 --- a/aperture.go +++ b/aperture.go @@ -95,14 +95,26 @@ func start() error { return fmt.Errorf("unable to connect to etcd: %v", err) } - // Create the proxy and connect it to lnd. + // Create our challenger that uses our backing lnd node to create + // invoices and check their settlement status. genInvoiceReq := func(price int64) (*lnrpc.Invoice, error) { return &lnrpc.Invoice{ Memo: "LSAT", Value: price, }, nil } - servicesProxy, err := createProxy(cfg, genInvoiceReq, etcdClient) + challenger, err := NewLndChallenger(cfg.Authenticator, genInvoiceReq) + if err != nil { + return err + } + err = challenger.Start() + if err != nil { + return err + } + defer challenger.Stop() + + // Create the proxy and connect it to lnd. + servicesProxy, err := createProxy(cfg, challenger, etcdClient) if err != nil { return err } @@ -378,13 +390,9 @@ func initTorListener(cfg *config, etcd *clientv3.Client) (*tor.Controller, error } // createProxy creates the proxy with all the services it needs. -func createProxy(cfg *config, genInvoiceReq InvoiceRequestGenerator, +func createProxy(cfg *config, challenger *LndChallenger, etcdClient *clientv3.Client) (*proxy.Proxy, error) { - challenger, err := NewLndChallenger(cfg.Authenticator, genInvoiceReq) - if err != nil { - return nil, err - } minter := mint.New(&mint.Config{ Challenger: challenger, Secrets: newSecretStore(etcdClient), From 97cf351b19a28236f365968f13acb1fc71861b31 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 7 Aug 2020 17:33:08 +0200 Subject: [PATCH 5/7] aperture: interrupt signals --- aperture.go | 70 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/aperture.go b/aperture.go index 037d311..43e6999 100644 --- a/aperture.go +++ b/aperture.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "path/filepath" + "sync" "time" "github.com/coreos/etcd/clientv3" @@ -17,6 +18,7 @@ import ( "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/cert" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/tor" "golang.org/x/crypto/acme/autocert" "golang.org/x/net/http2" @@ -63,16 +65,16 @@ var ( // Main is the true entrypoint of Kirin. func Main() { // TODO: Prevent from running twice. - err := start() + err := run() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } -// start sets up the proxy server and runs it. This function blocks until a +// run sets up the proxy server and runs it. This function blocks until a // shutdown signal is received. -func start() error { +func run() error { // First, parse configuration file and set up logging. configFile := filepath.Join(apertureDataDir, defaultConfigFilename) cfg, err := getConfig(configFile) @@ -150,17 +152,22 @@ func start() error { } } - // The ListenAndServeTLS below will block until shut down or an error - // occurs. So we can just defer a cleanup function here that will close - // everything on shutdown. - defer cleanup(etcdClient, httpsServer) - - // Finally start the server. + // Finally run the server. + var ( + wg sync.WaitGroup + quit = make(chan struct{}) + ) log.Infof("Starting the server, listening on %s.", cfg.ListenAddr) errChan := make(chan error) + wg.Add(1) go func() { - errChan <- serveFn() + defer wg.Done() + + select { + case errChan <- serveFn(): + case <-quit: + } }() // If we need to listen over Tor as well, we'll set up the onion @@ -170,6 +177,7 @@ func start() error { // will only be reached through the onion services, which already // provide encryption, so running this additional HTTP server should be // relatively safe. + var torHTTPServer *http.Server if cfg.Tor != nil && (cfg.Tor.V2 || cfg.Tor.V3) { torController, err := initTorListener(cfg, etcdClient) if err != nil { @@ -179,17 +187,51 @@ func start() error { _ = torController.Stop() }() - httpServer := &http.Server{ + torHTTPServer = &http.Server{ Addr: fmt.Sprintf("localhost:%d", cfg.Tor.ListenPort), Handler: h2c.NewHandler(handler, &http2.Server{}), } + wg.Add(1) go func() { - errChan <- httpServer.ListenAndServe() + defer wg.Done() + + select { + case errChan <- torHTTPServer.ListenAndServe(): + case <-quit: + } }() - defer httpServer.Close() } - return <-errChan + // Now that we've started everything, intercept any interrupt signals + // and wait for any of them to arrive. + signal.Intercept() + + var returnErr error + select { + case <-signal.ShutdownChannel(): + log.Infof("Received interrupt signal, shutting down aperture.") + + case err := <-errChan: + log.Errorf("Error while running aperture: %v", err) + returnErr = err + } + + // Shut down our client and server connections now. This should cause + // the first goroutine to quit. + cleanup(etcdClient, httpsServer) + + // If we started a tor server as well, shut it down now too to cause the + // second goroutine to quit. + if torHTTPServer != nil { + _ = torHTTPServer.Close() + } + + // Now we wait for the goroutines to exit before we return. The defers + // will take care of the rest of our started resources. + close(quit) + wg.Wait() + + return returnErr } // fileExists reports whether the named file or directory exists. From a5f2f8368505e7eb23d2b6c16994cd1a62306709 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 7 Aug 2020 17:33:09 +0200 Subject: [PATCH 6/7] aperture: use lnd client through interface --- challenger.go | 20 +++++++++++++++++++- go.mod | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/challenger.go b/challenger.go index f182062..ca01c62 100644 --- a/challenger.go +++ b/challenger.go @@ -14,16 +14,34 @@ import ( "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" + "google.golang.org/grpc" ) // InvoiceRequestGenerator is a function type that returns a new request for the // lnrpc.AddInvoice call. type InvoiceRequestGenerator func(price int64) (*lnrpc.Invoice, error) +// InvoiceClient is an interface that only implements part of a full lnd client, +// namely the part around the invoices we need for the challenger to work. +type InvoiceClient interface { + // ListInvoices returns a paginated list of all invoices known to lnd. + ListInvoices(ctx context.Context, in *lnrpc.ListInvoiceRequest, + opts ...grpc.CallOption) (*lnrpc.ListInvoiceResponse, error) + + // SubscribeInvoices subscribes to updates on invoices. + SubscribeInvoices(ctx context.Context, in *lnrpc.InvoiceSubscription, + opts ...grpc.CallOption) ( + lnrpc.Lightning_SubscribeInvoicesClient, error) + + // AddInvoice adds a new invoice to lnd. + AddInvoice(ctx context.Context, in *lnrpc.Invoice, + opts ...grpc.CallOption) (*lnrpc.AddInvoiceResponse, error) +} + // LndChallenger is a challenger that uses an lnd backend to create new LSAT // payment challenges. type LndChallenger struct { - client lnrpc.LightningClient + client InvoiceClient genInvoiceReq InvoiceRequestGenerator invoiceStates map[lntypes.Hash]lnrpc.Invoice_InvoiceState diff --git a/go.mod b/go.mod index 6802171..aa38767 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/lightningnetwork/lnd/cert v1.0.2 github.com/modern-go/reflect2 v1.0.1 // indirect github.com/soheilhy/cmux v0.1.4 // indirect + github.com/stretchr/testify v1.5.1 github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect go.uber.org/zap v1.15.0 // indirect From 494ab47a6058cf7052eb776fc3e5b4b9ac97e383 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 7 Aug 2020 17:33:10 +0200 Subject: [PATCH 7/7] challenger: add unit test --- challenger_test.go | 215 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 challenger_test.go diff --git a/challenger_test.go b/challenger_test.go new file mode 100644 index 0000000..fa43db3 --- /dev/null +++ b/challenger_test.go @@ -0,0 +1,215 @@ +package aperture + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +var ( + defaultTimeout = 20 * time.Millisecond +) + +type invoiceStreamMock struct { + lnrpc.Lightning_SubscribeInvoicesClient + + updateChan chan *lnrpc.Invoice + quit chan struct{} +} + +func (i *invoiceStreamMock) Recv() (*lnrpc.Invoice, error) { + select { + case msg := <-i.updateChan: + return msg, nil + + case <-i.quit: + return nil, context.Canceled + } +} + +type mockInvoiceClient struct { + invoices []*lnrpc.Invoice + updateChan chan *lnrpc.Invoice + quit chan struct{} + + lastAddIndex uint64 +} + +// ListInvoices returns a paginated list of all invoices known to lnd. +func (m *mockInvoiceClient) ListInvoices(_ context.Context, + _ *lnrpc.ListInvoiceRequest, + _ ...grpc.CallOption) (*lnrpc.ListInvoiceResponse, error) { + + return &lnrpc.ListInvoiceResponse{ + Invoices: m.invoices, + }, nil +} + +// SubscribeInvoices subscribes to updates on invoices. +func (m *mockInvoiceClient) SubscribeInvoices(_ context.Context, + in *lnrpc.InvoiceSubscription, _ ...grpc.CallOption) ( + lnrpc.Lightning_SubscribeInvoicesClient, error) { + + m.lastAddIndex = in.AddIndex + + return &invoiceStreamMock{ + updateChan: m.updateChan, + quit: m.quit, + }, nil +} + +// AddInvoice adds a new invoice to lnd. +func (m *mockInvoiceClient) AddInvoice(_ context.Context, in *lnrpc.Invoice, + _ ...grpc.CallOption) (*lnrpc.AddInvoiceResponse, error) { + + m.invoices = append(m.invoices, in) + + return &lnrpc.AddInvoiceResponse{ + RHash: in.RHash, + PaymentRequest: in.PaymentRequest, + AddIndex: uint64(len(m.invoices) - 1), + }, nil +} + +func (m *mockInvoiceClient) stop() { + close(m.quit) +} + +func newChallenger() (*LndChallenger, *mockInvoiceClient) { + mockClient := &mockInvoiceClient{ + updateChan: make(chan *lnrpc.Invoice), + quit: make(chan struct{}), + } + genInvoiceReq := func(price int64) (*lnrpc.Invoice, error) { + return newInvoice(lntypes.ZeroHash, 99, lnrpc.Invoice_OPEN), + nil + } + invoicesMtx := &sync.Mutex{} + return &LndChallenger{ + client: mockClient, + genInvoiceReq: genInvoiceReq, + invoiceStates: make(map[lntypes.Hash]lnrpc.Invoice_InvoiceState), + quit: make(chan struct{}), + invoicesMtx: invoicesMtx, + invoicesCond: sync.NewCond(invoicesMtx), + }, mockClient +} + +func newInvoice(hash lntypes.Hash, addIndex uint64, + state lnrpc.Invoice_InvoiceState) *lnrpc.Invoice { + + return &lnrpc.Invoice{ + PaymentRequest: "foo", + RHash: hash[:], + AddIndex: addIndex, + State: state, + CreationDate: time.Now().Unix(), + Expiry: 10, + } +} + +func TestLndChallenger(t *testing.T) { + t.Parallel() + + // First of all, test that the NewLndChallenger doesn't allow a nil + // invoice generator function. + _, err := NewLndChallenger(nil, nil) + require.Error(t, err) + + // Now mock the lnd backend and create a challenger instance that we can + // test. + c, invoiceMock := newChallenger() + + // Creating a new challenge should add an invoice to the lnd backend. + req, hash, err := c.NewChallenge(1337) + require.NoError(t, err) + require.Equal(t, "foo", req) + require.Equal(t, lntypes.ZeroHash, hash) + require.Equal(t, 1, len(invoiceMock.invoices)) + require.Equal(t, uint64(0), invoiceMock.lastAddIndex) + + // Now we already have an invoice in our lnd mock. When starting the + // challenger, we should have that invoice in the cache and a + // subscription that only starts at our faked addIndex. + err = c.Start() + require.NoError(t, err) + require.Equal(t, 1, len(c.invoiceStates)) + require.Equal(t, lnrpc.Invoice_OPEN, c.invoiceStates[lntypes.ZeroHash]) + require.Equal(t, uint64(99), invoiceMock.lastAddIndex) + require.NoError(t, c.VerifyInvoiceStatus( + lntypes.ZeroHash, lnrpc.Invoice_OPEN, defaultTimeout, + )) + require.Error(t, c.VerifyInvoiceStatus( + lntypes.ZeroHash, lnrpc.Invoice_SETTLED, defaultTimeout, + )) + + // Next, let's send an update for a new invoice and make sure it's added + // to the map. + hash = lntypes.Hash{77, 88, 99} + invoiceMock.updateChan <- newInvoice(hash, 123, lnrpc.Invoice_SETTLED) + require.NoError(t, c.VerifyInvoiceStatus( + hash, lnrpc.Invoice_SETTLED, defaultTimeout, + )) + require.Error(t, c.VerifyInvoiceStatus( + hash, lnrpc.Invoice_OPEN, defaultTimeout, + )) + + // Finally, create a bunch of invoices but only settle the first 5 of + // them. All others should get a failed invoice state after the timeout. + var ( + numInvoices = 20 + errors = make([]error, numInvoices) + wg sync.WaitGroup + ) + for i := 0; i < numInvoices; i++ { + hash := lntypes.Hash{77, 88, 99, byte(i)} + invoiceMock.updateChan <- newInvoice( + hash, 1000+uint64(i), lnrpc.Invoice_OPEN, + ) + + // The verification will block for a certain time. But we want + // all checks to happen automatically to simulate many parallel + // requests. So we spawn a goroutine for each invoice check. + wg.Add(1) + go func(errIdx int, hash lntypes.Hash) { + defer wg.Done() + + errors[errIdx] = c.VerifyInvoiceStatus( + hash, lnrpc.Invoice_SETTLED, defaultTimeout, + ) + }(i, hash) + } + + // With all 20 goroutines spinning and waiting for updates, we settle + // the first 5 invoices. + for i := 0; i < 5; i++ { + hash := lntypes.Hash{77, 88, 99, byte(i)} + invoiceMock.updateChan <- newInvoice( + hash, 1000+uint64(i), lnrpc.Invoice_SETTLED, + ) + } + + // Now wait for all checks to finish, then check that the last 15 + // invoices timed out. + wg.Wait() + for i := 0; i < numInvoices; i++ { + if i < 5 { + require.NoError(t, errors[i]) + } else { + require.Error(t, errors[i]) + require.Contains( + t, errors[i].Error(), + "invoice status not correct before timeout", + ) + } + } + + invoiceMock.stop() + c.Stop() +}