From faccccf93f18c24cf6472fa0b4d99ff56a6a3066 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 6 Feb 2019 16:46:00 -0800 Subject: [PATCH 1/6] ticker/ticker: rename ticker -> T In this commit, we rename the previously unexported ticker into T, making it exported along the way. This is nice as we now have access to the actual interface implementation without the need of making further type assertions. --- ticker/ticker.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/ticker/ticker.go b/ticker/ticker.go index 29e0c04b..782cbe5a 100644 --- a/ticker/ticker.go +++ b/ticker/ticker.go @@ -60,10 +60,10 @@ type Ticker interface { Stop() } -// ticker is the production implementation of the resumable Ticker interface. -// This allows various components to toggle their need for tick events, which -// may vary depending on system load. -type ticker struct { +// T is the production implementation of the resumable Ticker interface. This +// allows various components to toggle their need for tick events, which may +// vary depending on system load. +type T struct { // interval is the desired duration between ticks when active. interval time.Duration @@ -73,10 +73,13 @@ type ticker struct { ticker *time.Ticker } +// A compile-time constraint to ensure T satisfies the Ticker interface. +var _ Ticker = (*T)(nil) + // New returns a new ticker that signals with the given interval when not // paused. The ticker starts off inactive. -func New(interval time.Duration) Ticker { - return &ticker{ +func New(interval time.Duration) *T { + return &T{ interval: interval, } } @@ -85,18 +88,18 @@ func New(interval time.Duration) Ticker { // prescribed interval. This method returns nil when the ticker is paused. // // NOTE: Part of the Ticker interface. -func (t *ticker) Ticks() <-chan time.Time { +func (t *T) Ticks() <-chan time.Time { if t.ticker == nil { return nil } return t.ticker.C } -// Resumes starts underlying time.Ticker and causes the ticker to begin +// Resume starts underlying time.Ticker and causes the ticker to begin // delivering scheduled events. // // NOTE: Part of the Ticker interface. -func (t *ticker) Resume() { +func (t *T) Resume() { if t.ticker == nil { t.ticker = time.NewTicker(t.interval) } @@ -106,7 +109,7 @@ func (t *ticker) Resume() { // regular intervals. // // NOTE: Part of the Ticker interface. -func (t *ticker) Pause() { +func (t *T) Pause() { if t.ticker != nil { t.ticker.Stop() t.ticker = nil @@ -118,6 +121,6 @@ func (t *ticker) Pause() { // implementation, this is equivalent to Pause. // // NOTE: Part of the Ticker interface. -func (t *ticker) Stop() { +func (t *T) Stop() { t.Pause() } From abdd01ed9a182dd20dc2a81fcafa756a9e92812f Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 6 Feb 2019 16:48:54 -0800 Subject: [PATCH 2/6] ticker+htlcswitch: rename Mock -> Force --- htlcswitch/link_test.go | 10 +++++----- htlcswitch/mock.go | 4 ++-- htlcswitch/switch_test.go | 2 +- htlcswitch/test_utils.go | 4 ++-- ticker/mock.go | 23 +++++++++++++---------- ticker/ticker.go | 2 +- ticker/ticker_test.go | 2 +- 7 files changed, 25 insertions(+), 22 deletions(-) diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index c0b6e2a1..868b2626 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -1545,7 +1545,7 @@ func newSingleLinkTestHarness(chanAmt, chanReserve btcutil.Amount) ( // Instantiate with a long interval, so that we can precisely control // the firing via force feeding. - bticker := ticker.MockNew(time.Hour) + bticker := ticker.NewForce(time.Hour) aliceCfg := ChannelLinkConfig{ FwrdingPolicy: globalPolicy, Peer: alicePeer, @@ -1568,7 +1568,7 @@ func newSingleLinkTestHarness(chanAmt, chanReserve btcutil.Amount) ( Registry: invoiceRegistry, ChainEvents: &contractcourt.ChainEventSubscription{}, BatchTicker: bticker, - FwdPkgGCTicker: ticker.MockNew(15 * time.Second), + FwdPkgGCTicker: ticker.NewForce(15 * time.Second), // Make the BatchSize and Min/MaxFeeUpdateTimeout large enough // to not trigger commit updates automatically during tests. BatchSize: 10000, @@ -3506,7 +3506,7 @@ func TestChannelLinkShutdownDuringForward(t *testing.T) { // unblocks after nothing has been pulled for two seconds. waitForBobsSwitchToBlock := func() { bobSwitch := n.firstBobChannelLink.cfg.Switch - ticker := bobSwitch.cfg.LogEventTicker.(*ticker.Mock) + ticker := bobSwitch.cfg.LogEventTicker.(*ticker.Force) timeout := time.After(15 * time.Second) for { time.Sleep(50 * time.Millisecond) @@ -3525,7 +3525,7 @@ func TestChannelLinkShutdownDuringForward(t *testing.T) { // Define a helper method that strobes the link's batch ticker, and // unblocks after nothing has been pulled for two seconds. waitForBobsIncomingLinkToBlock := func() { - ticker := n.firstBobChannelLink.cfg.BatchTicker.(*ticker.Mock) + ticker := n.firstBobChannelLink.cfg.BatchTicker.(*ticker.Force) timeout := time.After(15 * time.Second) for { time.Sleep(50 * time.Millisecond) @@ -4060,7 +4060,7 @@ func restartLink(aliceChannel *lnwallet.LightningChannel, aliceSwitch *Switch, // Instantiate with a long interval, so that we can precisely control // the firing via force feeding. - bticker := ticker.MockNew(time.Hour) + bticker := ticker.NewForce(time.Hour) aliceCfg := ChannelLinkConfig{ FwrdingPolicy: globalPolicy, Peer: alicePeer, diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 89913c17..939488ab 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -163,8 +163,8 @@ func initSwitchWithDB(startingHeight uint32, db *channeldb.DB) (*Switch, error) return nil, nil }, Notifier: &mockNotifier{}, - FwdEventTicker: ticker.MockNew(DefaultFwdEventInterval), - LogEventTicker: ticker.MockNew(DefaultLogInterval), + FwdEventTicker: ticker.NewForce(DefaultFwdEventInterval), + LogEventTicker: ticker.NewForce(DefaultLogInterval), NotifyActiveChannel: func(wire.OutPoint) {}, NotifyInactiveChannel: func(wire.OutPoint) {}, } diff --git a/htlcswitch/switch_test.go b/htlcswitch/switch_test.go index 0513bdc2..59b0e4ed 100644 --- a/htlcswitch/switch_test.go +++ b/htlcswitch/switch_test.go @@ -1924,7 +1924,7 @@ func TestMultiHopPaymentForwardingEvents(t *testing.T) { // After sending 5 of the payments, trigger the forwarding ticker, to // make sure the events are properly flushed. - bobTicker, ok := n.bobServer.htlcSwitch.cfg.FwdEventTicker.(*ticker.Mock) + bobTicker, ok := n.bobServer.htlcSwitch.cfg.FwdEventTicker.(*ticker.Force) if !ok { t.Fatalf("mockTicker assertion failed") } diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 58bcd856..86856915 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -1041,8 +1041,8 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer, ChainEvents: &contractcourt.ChainEventSubscription{}, SyncStates: true, BatchSize: 10, - BatchTicker: ticker.MockNew(batchTimeout), - FwdPkgGCTicker: ticker.MockNew(fwdPkgTimeout), + BatchTicker: ticker.NewForce(batchTimeout), + FwdPkgGCTicker: ticker.NewForce(fwdPkgTimeout), MinFeeUpdateTimeout: minFeeUpdateTimeout, MaxFeeUpdateTimeout: maxFeeUpdateTimeout, OnChannelFailure: func(lnwire.ChannelID, lnwire.ShortChannelID, LinkFailureError) {}, diff --git a/ticker/mock.go b/ticker/mock.go index b82d1604..016e3702 100644 --- a/ticker/mock.go +++ b/ticker/mock.go @@ -6,9 +6,9 @@ import ( "time" ) -// Mock implements the Ticker interface, and provides a method of -// force-feeding ticks, even while paused. -type Mock struct { +// Force implements the Ticker interface, and provides a method of force-feeding +// ticks, even while paused. +type Force struct { isActive uint32 // used atomically // Force is used to force-feed a ticks into the ticker. Useful for @@ -22,10 +22,13 @@ type Mock struct { quit chan struct{} } -// MockNew returns a Mock Ticker, used for testing and debugging. It supports +// A compile-time constraint to ensure Force satisfies the Ticker interface. +var _ Ticker = (*Force)(nil) + +// NewForce returns a Force ticker, used for testing and debugging. It supports // the ability to force-feed events that get output by the -func MockNew(interval time.Duration) *Mock { - m := &Mock{ +func NewForce(interval time.Duration) *Force { + m := &Force{ ticker: time.NewTicker(interval).C, Force: make(chan time.Time), skip: make(chan struct{}), @@ -64,7 +67,7 @@ func MockNew(interval time.Duration) *Mock { // time. // // NOTE: Part of the Ticker interface. -func (m *Mock) Ticks() <-chan time.Time { +func (m *Force) Ticks() <-chan time.Time { return m.Force } @@ -72,7 +75,7 @@ func (m *Mock) Ticks() <-chan time.Time { // delivering scheduled events. // // NOTE: Part of the Ticker interface. -func (m *Mock) Resume() { +func (m *Force) Resume() { atomic.StoreUint32(&m.isActive, 1) } @@ -80,7 +83,7 @@ func (m *Mock) Resume() { // regular intervals. // // NOTE: Part of the Ticker interface. -func (m *Mock) Pause() { +func (m *Force) Pause() { atomic.StoreUint32(&m.isActive, 0) // If the ticker fired and read isActive as true, it may still send the @@ -95,7 +98,7 @@ func (m *Mock) Pause() { // regular intervals, and permanently frees up any resources. // // NOTE: Part of the Ticker interface. -func (m *Mock) Stop() { +func (m *Force) Stop() { m.Pause() close(m.quit) m.wg.Wait() diff --git a/ticker/ticker.go b/ticker/ticker.go index 782cbe5a..5cf33ea4 100644 --- a/ticker/ticker.go +++ b/ticker/ticker.go @@ -46,7 +46,7 @@ type Ticker interface { // Pause suspends the underlying ticker, such that Ticks() stops // signaling at regular intervals. After calling Pause, the ticker // should not send any ticks scheduled with the chosen interval. Forced - // ticks are still permissible, as in the case of the Mock Ticker. + // ticks are still permissible, as in the case of the Force Ticker. // // NOTE: It MUST be safe to call Pause at any time, and more than once // successively. diff --git a/ticker/ticker_test.go b/ticker/ticker_test.go index 2abd0543..81da3d7a 100644 --- a/ticker/ticker_test.go +++ b/ticker/ticker_test.go @@ -20,7 +20,7 @@ var tickers = []struct { }, { "mock ticker", - ticker.MockNew(interval), + ticker.NewForce(interval), }, } From 667e57d67d255274aef83325f2ef98b6cd2355e3 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 6 Feb 2019 16:49:41 -0800 Subject: [PATCH 3/6] ticker: rename mock.go -> force.go --- ticker/{mock.go => force.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ticker/{mock.go => force.go} (100%) diff --git a/ticker/mock.go b/ticker/force.go similarity index 100% rename from ticker/mock.go rename to ticker/force.go From f71e400cf0739dae5224de57ff7ad514a8ede7eb Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 6 Feb 2019 16:52:26 -0800 Subject: [PATCH 4/6] ticker: add module support --- ticker/go.mod | 1 + 1 file changed, 1 insertion(+) create mode 100644 ticker/go.mod diff --git a/ticker/go.mod b/ticker/go.mod new file mode 100644 index 00000000..fdab508f --- /dev/null +++ b/ticker/go.mod @@ -0,0 +1 @@ +module github.com/lightningnetwork/lnd/ticker From 9c8b627f804c64ed785ae4e155d4f3535f6e953a Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 6 Feb 2019 17:03:33 -0800 Subject: [PATCH 5/6] queue: add module support --- queue/go.mod | 5 +++++ queue/go.sum | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 queue/go.mod create mode 100644 queue/go.sum diff --git a/queue/go.mod b/queue/go.mod new file mode 100644 index 00000000..62aa0be7 --- /dev/null +++ b/queue/go.mod @@ -0,0 +1,5 @@ +module github.com/lightningnetwork/lnd/queue + +require github.com/lightningnetwork/lnd/ticker v1.0.0 + +replace github.com/lightningnetwork/lnd/ticker v1.0.0 => ../ticker diff --git a/queue/go.sum b/queue/go.sum new file mode 100644 index 00000000..eddb2ea0 --- /dev/null +++ b/queue/go.sum @@ -0,0 +1,2 @@ +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= From 33b6e7c9246d369e3b56543cb21934492560cca4 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 6 Feb 2019 17:18:08 -0800 Subject: [PATCH 6/6] build: update deps to use ticker and queue modules --- go.mod | 6 ++++++ go.sum | 11 ++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index e834b4ad..9032335f 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,8 @@ require ( github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec github.com/lightninglabs/neutrino v0.0.0-20190115022559-351f5f06c6af github.com/lightningnetwork/lightning-onion v0.0.0-20180605012408-ac4d9da8f1d6 + github.com/lightningnetwork/lnd/queue v1.0.0 + github.com/lightningnetwork/lnd/ticker v1.0.0 github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af // indirect @@ -49,3 +51,7 @@ require ( gopkg.in/macaroon.v2 v2.0.0 gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect ) + +replace github.com/lightningnetwork/lnd/ticker => ./ticker + +replace github.com/lightningnetwork/lnd/queue => ./queue diff --git a/go.sum b/go.sum index 8af8f4b2..1094baf4 100644 --- a/go.sum +++ b/go.sum @@ -100,17 +100,18 @@ github.com/lightninglabs/neutrino v0.0.0-20190115022559-351f5f06c6af h1:JzoYbWqw github.com/lightninglabs/neutrino v0.0.0-20190115022559-351f5f06c6af/go.mod h1:aR+E6cs+FTaIwIa/WLyvNsB8FZg8TiP3r0Led+4Q4gI= github.com/lightningnetwork/lightning-onion v0.0.0-20180605012408-ac4d9da8f1d6 h1:ONLGrYJVQdbtP6CE/ff1KNWZtygRGEh12RzonTiCzPs= github.com/lightningnetwork/lightning-onion v0.0.0-20180605012408-ac4d9da8f1d6/go.mod h1:8EgEt4a/NUOVQd+3kk6n9aZCJ1Ssj96Pb6lCrci+6oc= +github.com/lightningnetwork/lnd/queue v1.0.0 h1:eVUxXIzLm1IdLC5eGzs2z3NzgLYRapy44KCvsDZQ/HI= +github.com/lightningnetwork/lnd/queue v1.0.0/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms= +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 h1:b/Op1jKdoE6tzGyjzFx8gc7ZyW3hVFs1jUCQfM/Z2Jo= github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= 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/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -128,19 +129,16 @@ golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnf golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180821023952-922f4815f713/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953 h1:LuZIitY8waaxUfNIdtajyE/YzA/zyf0YxXG27VpLrkg= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180821140842-3b58ed4ad339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU= golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -150,7 +148,6 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2I 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= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181127195345-31ac5d88444a h1:Weemm+oF2juintSvD0c+ZG4lDmCwgYKrM/kPI6gFINY= google.golang.org/genproto v0.0.0-20181127195345-31ac5d88444a/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=