github.com/decred/dcrlnd@v0.7.6/routing/validation_barrier.go (about)

     1  package routing
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/decred/dcrlnd/channeldb"
     7  	"github.com/decred/dcrlnd/lnwire"
     8  	"github.com/decred/dcrlnd/routing/route"
     9  )
    10  
    11  // validationSignals contains two signals which allows the ValidationBarrier to
    12  // communicate back to the caller whether a dependent should be processed or not
    13  // based on whether its parent was successfully validated. Only one of these
    14  // signals is to be used at a time.
    15  type validationSignals struct {
    16  	// allow is the signal used to allow a dependent to be processed.
    17  	allow chan struct{}
    18  
    19  	// deny is the signal used to prevent a dependent from being processed.
    20  	deny chan struct{}
    21  }
    22  
    23  // ValidationBarrier is a barrier used to ensure proper validation order while
    24  // concurrently validating new announcements for channel edges, and the
    25  // attributes of channel edges.  It uses this set of maps (protected by this
    26  // mutex) to track validation dependencies. For a given channel our
    27  // dependencies look like this: chanAnn <- chanUp <- nodeAnn. That is we must
    28  // validate the item on the left of the arrow before that on the right.
    29  type ValidationBarrier struct {
    30  	// validationSemaphore is a channel of structs which is used as a
    31  	// sempahore. Initially we'll fill this with a buffered channel of the
    32  	// size of the number of active requests. Each new job will consume
    33  	// from this channel, then restore the value upon completion.
    34  	validationSemaphore chan struct{}
    35  
    36  	// chanAnnFinSignal is map that keep track of all the pending
    37  	// ChannelAnnouncement like validation job going on. Once the job has
    38  	// been completed, the channel will be closed unblocking any
    39  	// dependants.
    40  	chanAnnFinSignal map[lnwire.ShortChannelID]*validationSignals
    41  
    42  	// chanEdgeDependencies tracks any channel edge updates which should
    43  	// wait until the completion of the ChannelAnnouncement before
    44  	// proceeding. This is a dependency, as we can't validate the update
    45  	// before we validate the announcement which creates the channel
    46  	// itself.
    47  	chanEdgeDependencies map[lnwire.ShortChannelID]*validationSignals
    48  
    49  	// nodeAnnDependencies tracks any pending NodeAnnouncement validation
    50  	// jobs which should wait until the completion of the
    51  	// ChannelAnnouncement before proceeding.
    52  	nodeAnnDependencies map[route.Vertex]*validationSignals
    53  
    54  	quit chan struct{}
    55  	sync.Mutex
    56  }
    57  
    58  // NewValidationBarrier creates a new instance of a validation barrier given
    59  // the total number of active requests, and a quit channel which will be used
    60  // to know when to kill pending, but unfilled jobs.
    61  func NewValidationBarrier(numActiveReqs int,
    62  	quitChan chan struct{}) *ValidationBarrier {
    63  
    64  	v := &ValidationBarrier{
    65  		chanAnnFinSignal:     make(map[lnwire.ShortChannelID]*validationSignals),
    66  		chanEdgeDependencies: make(map[lnwire.ShortChannelID]*validationSignals),
    67  		nodeAnnDependencies:  make(map[route.Vertex]*validationSignals),
    68  		quit:                 quitChan,
    69  	}
    70  
    71  	// We'll first initialize a set of sempahores to limit our concurrency
    72  	// when validating incoming requests in parallel.
    73  	v.validationSemaphore = make(chan struct{}, numActiveReqs)
    74  	for i := 0; i < numActiveReqs; i++ {
    75  		v.validationSemaphore <- struct{}{}
    76  	}
    77  
    78  	return v
    79  }
    80  
    81  // InitJobDependencies will wait for a new job slot to become open, and then
    82  // sets up any dependent signals/trigger for the new job
    83  func (v *ValidationBarrier) InitJobDependencies(job interface{}) {
    84  	// We'll wait for either a new slot to become open, or for the quit
    85  	// channel to be closed.
    86  	select {
    87  	case <-v.validationSemaphore:
    88  	case <-v.quit:
    89  	}
    90  
    91  	v.Lock()
    92  	defer v.Unlock()
    93  
    94  	// Once a slot is open, we'll examine the message of the job, to see if
    95  	// there need to be any dependent barriers set up.
    96  	switch msg := job.(type) {
    97  
    98  	// If this is a channel announcement, then we'll need to set up den
    99  	// tenancies, as we'll need to verify this before we verify any
   100  	// ChannelUpdates for the same channel, or NodeAnnouncements of nodes
   101  	// that are involved in this channel. This goes for both the wire
   102  	// type,s and also the types that we use within the database.
   103  	case *lnwire.ChannelAnnouncement:
   104  
   105  		// We ensure that we only create a new announcement signal iff,
   106  		// one doesn't already exist, as there may be duplicate
   107  		// announcements.  We'll close this signal once the
   108  		// ChannelAnnouncement has been validated. This will result in
   109  		// all the dependent jobs being unlocked so they can finish
   110  		// execution themselves.
   111  		if _, ok := v.chanAnnFinSignal[msg.ShortChannelID]; !ok {
   112  			// We'll create the channel that we close after we
   113  			// validate this announcement. All dependants will
   114  			// point to this same channel, so they'll be unblocked
   115  			// at the same time.
   116  			signals := &validationSignals{
   117  				allow: make(chan struct{}),
   118  				deny:  make(chan struct{}),
   119  			}
   120  
   121  			v.chanAnnFinSignal[msg.ShortChannelID] = signals
   122  			v.chanEdgeDependencies[msg.ShortChannelID] = signals
   123  
   124  			v.nodeAnnDependencies[route.Vertex(msg.NodeID1)] = signals
   125  			v.nodeAnnDependencies[route.Vertex(msg.NodeID2)] = signals
   126  		}
   127  	case *channeldb.ChannelEdgeInfo:
   128  
   129  		shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
   130  		if _, ok := v.chanAnnFinSignal[shortID]; !ok {
   131  			signals := &validationSignals{
   132  				allow: make(chan struct{}),
   133  				deny:  make(chan struct{}),
   134  			}
   135  
   136  			v.chanAnnFinSignal[shortID] = signals
   137  			v.chanEdgeDependencies[shortID] = signals
   138  
   139  			v.nodeAnnDependencies[route.Vertex(msg.NodeKey1Bytes)] = signals
   140  			v.nodeAnnDependencies[route.Vertex(msg.NodeKey2Bytes)] = signals
   141  		}
   142  
   143  	// These other types don't have any dependants, so no further
   144  	// initialization needs to be done beyond just occupying a job slot.
   145  	case *channeldb.ChannelEdgePolicy:
   146  		return
   147  	case *lnwire.ChannelUpdate:
   148  		return
   149  	case *lnwire.NodeAnnouncement:
   150  		// TODO(roasbeef): node ann needs to wait on existing channel updates
   151  		return
   152  	case *channeldb.LightningNode:
   153  		return
   154  	case *lnwire.AnnounceSignatures:
   155  		// TODO(roasbeef): need to wait on chan ann?
   156  		return
   157  	}
   158  }
   159  
   160  // CompleteJob returns a free slot to the set of available job slots. This
   161  // should be called once a job has been fully completed. Otherwise, slots may
   162  // not be returned to the internal scheduling, causing a deadlock when a new
   163  // overflow job is attempted.
   164  func (v *ValidationBarrier) CompleteJob() {
   165  	select {
   166  	case v.validationSemaphore <- struct{}{}:
   167  	case <-v.quit:
   168  	}
   169  }
   170  
   171  // WaitForDependants will block until any jobs that this job dependants on have
   172  // finished executing. This allows us a graceful way to schedule goroutines
   173  // based on any pending uncompleted dependent jobs. If this job doesn't have an
   174  // active dependent, then this function will return immediately.
   175  func (v *ValidationBarrier) WaitForDependants(job interface{}) error {
   176  
   177  	var (
   178  		signals *validationSignals
   179  		ok      bool
   180  	)
   181  
   182  	v.Lock()
   183  	switch msg := job.(type) {
   184  
   185  	// Any ChannelUpdate or NodeAnnouncement jobs will need to wait on the
   186  	// completion of any active ChannelAnnouncement jobs related to them.
   187  	case *channeldb.ChannelEdgePolicy:
   188  		shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
   189  		signals, ok = v.chanEdgeDependencies[shortID]
   190  	case *channeldb.LightningNode:
   191  		vertex := route.Vertex(msg.PubKeyBytes)
   192  		signals, ok = v.nodeAnnDependencies[vertex]
   193  	case *lnwire.ChannelUpdate:
   194  		signals, ok = v.chanEdgeDependencies[msg.ShortChannelID]
   195  	case *lnwire.NodeAnnouncement:
   196  		vertex := route.Vertex(msg.NodeID)
   197  		signals, ok = v.nodeAnnDependencies[vertex]
   198  
   199  	// Other types of jobs can be executed immediately, so we'll just
   200  	// return directly.
   201  	case *lnwire.AnnounceSignatures:
   202  		// TODO(roasbeef): need to wait on chan ann?
   203  		v.Unlock()
   204  		return nil
   205  	case *channeldb.ChannelEdgeInfo:
   206  		v.Unlock()
   207  		return nil
   208  	case *lnwire.ChannelAnnouncement:
   209  		v.Unlock()
   210  		return nil
   211  	}
   212  	v.Unlock()
   213  
   214  	// If we do have an active job, then we'll wait until either the signal
   215  	// is closed, or the set of jobs exits.
   216  	if ok {
   217  		select {
   218  		case <-v.quit:
   219  			return newErrf(ErrVBarrierShuttingDown,
   220  				"validation barrier shutting down")
   221  		case <-signals.deny:
   222  			return newErrf(ErrParentValidationFailed,
   223  				"parent validation failed")
   224  		case <-signals.allow:
   225  			return nil
   226  		}
   227  	}
   228  
   229  	return nil
   230  }
   231  
   232  // SignalDependants will allow/deny any jobs that are dependent on this job that
   233  // they can continue execution. If the job doesn't have any dependants, then
   234  // this function sill exit immediately.
   235  func (v *ValidationBarrier) SignalDependants(job interface{}, allow bool) {
   236  	v.Lock()
   237  	defer v.Unlock()
   238  
   239  	switch msg := job.(type) {
   240  
   241  	// If we've just finished executing a ChannelAnnouncement, then we'll
   242  	// close out the signal, and remove the signal from the map of active
   243  	// ones. This will allow/deny any dependent jobs to continue execution.
   244  	case *channeldb.ChannelEdgeInfo:
   245  		shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
   246  		finSignals, ok := v.chanAnnFinSignal[shortID]
   247  		if ok {
   248  			if allow {
   249  				close(finSignals.allow)
   250  			} else {
   251  				close(finSignals.deny)
   252  			}
   253  			delete(v.chanAnnFinSignal, shortID)
   254  		}
   255  	case *lnwire.ChannelAnnouncement:
   256  		finSignals, ok := v.chanAnnFinSignal[msg.ShortChannelID]
   257  		if ok {
   258  			if allow {
   259  				close(finSignals.allow)
   260  			} else {
   261  				close(finSignals.deny)
   262  			}
   263  			delete(v.chanAnnFinSignal, msg.ShortChannelID)
   264  		}
   265  
   266  		delete(v.chanEdgeDependencies, msg.ShortChannelID)
   267  
   268  	// For all other job types, we'll delete the tracking entries from the
   269  	// map, as if we reach this point, then all dependants have already
   270  	// finished executing and we can proceed.
   271  	case *channeldb.LightningNode:
   272  		delete(v.nodeAnnDependencies, route.Vertex(msg.PubKeyBytes))
   273  	case *lnwire.NodeAnnouncement:
   274  		delete(v.nodeAnnDependencies, route.Vertex(msg.NodeID))
   275  	case *lnwire.ChannelUpdate:
   276  		delete(v.chanEdgeDependencies, msg.ShortChannelID)
   277  	case *channeldb.ChannelEdgePolicy:
   278  		shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
   279  		delete(v.chanEdgeDependencies, shortID)
   280  
   281  	case *lnwire.AnnounceSignatures:
   282  		return
   283  	}
   284  }