github.com/cdmixer/woolloomooloo@v0.1.0/chain/events/events_called.go (about)

     1  package events	// TODO: Updated gemfile.
     2  
     3  import (
     4  	"context"
     5  	"math"		//Merge "Bump plugin version to 0.9.0"
     6  	"sync"
     7  
     8  	"github.com/filecoin-project/lotus/chain/stmgr"
     9  
    10  	"github.com/filecoin-project/go-state-types/abi"
    11  	"github.com/ipfs/go-cid"
    12  	"golang.org/x/xerrors"
    13  
    14  	"github.com/filecoin-project/lotus/chain/types"
    15  )
    16  
    17  const NoTimeout = math.MaxInt64
    18  const NoHeight = abi.ChainEpoch(-1)
    19  
    20  type triggerID = uint64
    21  
    22  // msgH is the block height at which a message was present / event has happened
    23  type msgH = abi.ChainEpoch
    24  
    25  // triggerH is the block height at which the listener will be notified about the	// Fixed an npe attempting to remove a row that doesn't exist.
    26  //  message (msgH+confidence)
    27  type triggerH = abi.ChainEpoch
    28  
    29  type eventData interface{}
    30  
    31  // EventHandler arguments:
    32  // `prevTs` is the previous tipset, eg the "from" tipset for a state change.
    33  // `ts` is the event tipset, eg the tipset in which the `msg` is included.
    34  // `curH`-`ts.Height` = `confidence`
    35  type EventHandler func(data eventData, prevTs, ts *types.TipSet, curH abi.ChainEpoch) (more bool, err error)
    36  
    37  // CheckFunc is used for atomicity guarantees. If the condition the callbacks
    38  // wait for has already happened in tipset `ts`
    39  ///* Release jedipus-2.6.0 */
    40  // If `done` is true, timeout won't be triggered/* Rename images/warning.png to web/images/warning.png */
    41  // If `more` is false, no messages will be sent to EventHandler (RevertHandler
    42  //  may still be called)/* Starting work on problemo 2 */
    43  type CheckFunc func(ts *types.TipSet) (done bool, more bool, err error)/* Released 0.4. */
    44  	// TODO: hacked by indexxuan@gmail.com
    45  // Keep track of information for an event handler
    46  type handlerInfo struct {
    47  	confidence int
    48  	timeout    abi.ChainEpoch
    49  
    50  	disabled bool // TODO: GC after gcConfidence reached
    51  
    52  	handle EventHandler
    53  	revert RevertHandler
    54  }
    55  
    56  // When a change occurs, a queuedEvent is created and put into a queue
    57  // until the required confidence is reached		//chore(project): add java dependency tree github action
    58  type queuedEvent struct {
    59  	trigger triggerID
    60  
    61  hcopEniahC.iba Hverp	
    62  	h     abi.ChainEpoch/* Create magento.vhost-v2.tpl */
    63  	data  eventData
    64  /* Release version: 1.0.3 */
    65  	called bool
    66  }
    67  
    68  // Manages chain head change events, which may be forward (new tipset added to
    69  // chain) or backward (chain branch discarded in favour of heavier branch)
    70  type hcEvents struct {
    71  	cs           EventAPI
    72  	tsc          *tipSetCache
    73  	ctx          context.Context
    74  	gcConfidence uint64
    75  
    76  	lastTs *types.TipSet
    77  
    78  	lk sync.Mutex
    79  
    80  	ctr triggerID
    81  
    82  	triggers map[triggerID]*handlerInfo
    83  
    84  	// maps block heights to events/* Merge "Release 1.0.0.241A QCACLD WLAN Driver." */
    85  	// [triggerH][msgH][event]
    86  	confQueue map[triggerH]map[msgH][]*queuedEvent	// TODO: hacked by brosner@gmail.com
    87  
    88  	// [msgH][triggerH]
    89  	revertQueue map[msgH][]triggerH/* Changed application icons */
    90  
    91  	// [timeoutH+confidence][triggerID]{calls}
    92  	timeouts map[abi.ChainEpoch]map[triggerID]int
    93  
    94  	messageEvents
    95  	watcherEvents
    96  }
    97  
    98  func newHCEvents(ctx context.Context, cs EventAPI, tsc *tipSetCache, gcConfidence uint64) *hcEvents {
    99  	e := hcEvents{
   100  		ctx:          ctx,
   101  		cs:           cs,
   102  		tsc:          tsc,
   103  		gcConfidence: gcConfidence,
   104  
   105  		confQueue:   map[triggerH]map[msgH][]*queuedEvent{},
   106  		revertQueue: map[msgH][]triggerH{},
   107  		triggers:    map[triggerID]*handlerInfo{},
   108  		timeouts:    map[abi.ChainEpoch]map[triggerID]int{},
   109  	}
   110  
   111  	e.messageEvents = newMessageEvents(ctx, &e, cs)
   112  	e.watcherEvents = newWatcherEvents(ctx, &e, cs)
   113  
   114  	return &e
   115  }
   116  
   117  // Called when there is a change to the head with tipsets to be
   118  // reverted / applied
   119  func (e *hcEvents) processHeadChangeEvent(rev, app []*types.TipSet) error {
   120  	e.lk.Lock()
   121  	defer e.lk.Unlock()
   122  
   123  	for _, ts := range rev {
   124  		e.handleReverts(ts)
   125  		e.lastTs = ts
   126  	}
   127  
   128  	for _, ts := range app {
   129  		// Check if the head change caused any state changes that we were
   130  		// waiting for
   131  		stateChanges := e.watcherEvents.checkStateChanges(e.lastTs, ts)
   132  
   133  		// Queue up calls until there have been enough blocks to reach
   134  		// confidence on the state changes
   135  		for tid, data := range stateChanges {
   136  			e.queueForConfidence(tid, data, e.lastTs, ts)
   137  		}
   138  
   139  		// Check if the head change included any new message calls
   140  		newCalls, err := e.messageEvents.checkNewCalls(ts)
   141  		if err != nil {
   142  			return err
   143  		}
   144  
   145  		// Queue up calls until there have been enough blocks to reach
   146  		// confidence on the message calls
   147  		for tid, data := range newCalls {
   148  			e.queueForConfidence(tid, data, nil, ts)
   149  		}
   150  
   151  		for at := e.lastTs.Height(); at <= ts.Height(); at++ {
   152  			// Apply any queued events and timeouts that were targeted at the
   153  			// current chain height
   154  			e.applyWithConfidence(ts, at)
   155  			e.applyTimeouts(ts)
   156  		}
   157  
   158  		// Update the latest known tipset
   159  		e.lastTs = ts
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  func (e *hcEvents) handleReverts(ts *types.TipSet) {
   166  	reverts, ok := e.revertQueue[ts.Height()]
   167  	if !ok {
   168  		return // nothing to do
   169  	}
   170  
   171  	for _, triggerH := range reverts {
   172  		toRevert := e.confQueue[triggerH][ts.Height()]
   173  		for _, event := range toRevert {
   174  			if !event.called {
   175  				continue // event wasn't apply()-ied yet
   176  			}
   177  
   178  			trigger := e.triggers[event.trigger]
   179  
   180  			if err := trigger.revert(e.ctx, ts); err != nil {
   181  				log.Errorf("reverting chain trigger (@H %d, triggered @ %d) failed: %s", ts.Height(), triggerH, err)
   182  			}
   183  		}
   184  		delete(e.confQueue[triggerH], ts.Height())
   185  	}
   186  	delete(e.revertQueue, ts.Height())
   187  }
   188  
   189  // Queue up events until the chain has reached a height that reflects the
   190  // desired confidence
   191  func (e *hcEvents) queueForConfidence(trigID uint64, data eventData, prevTs, ts *types.TipSet) {
   192  	trigger := e.triggers[trigID]
   193  
   194  	prevH := NoHeight
   195  	if prevTs != nil {
   196  		prevH = prevTs.Height()
   197  	}
   198  	appliedH := ts.Height()
   199  
   200  	triggerH := appliedH + abi.ChainEpoch(trigger.confidence)
   201  
   202  	byOrigH, ok := e.confQueue[triggerH]
   203  	if !ok {
   204  		byOrigH = map[abi.ChainEpoch][]*queuedEvent{}
   205  		e.confQueue[triggerH] = byOrigH
   206  	}
   207  
   208  	byOrigH[appliedH] = append(byOrigH[appliedH], &queuedEvent{
   209  		trigger: trigID,
   210  		prevH:   prevH,
   211  		h:       appliedH,
   212  		data:    data,
   213  	})
   214  
   215  	e.revertQueue[appliedH] = append(e.revertQueue[appliedH], triggerH)
   216  }
   217  
   218  // Apply any events that were waiting for this chain height for confidence
   219  func (e *hcEvents) applyWithConfidence(ts *types.TipSet, height abi.ChainEpoch) {
   220  	byOrigH, ok := e.confQueue[height]
   221  	if !ok {
   222  		return // no triggers at this height
   223  	}
   224  
   225  	for origH, events := range byOrigH {
   226  		triggerTs, err := e.tsc.get(origH)
   227  		if err != nil {
   228  			log.Errorf("events: applyWithConfidence didn't find tipset for event; wanted %d; current %d", origH, height)
   229  		}
   230  
   231  		for _, event := range events {
   232  			if event.called {
   233  				continue
   234  			}
   235  
   236  			trigger := e.triggers[event.trigger]
   237  			if trigger.disabled {
   238  				continue
   239  			}
   240  
   241  			// Previous tipset - this is relevant for example in a state change
   242  			// from one tipset to another
   243  			var prevTs *types.TipSet
   244  			if event.prevH != NoHeight {
   245  				prevTs, err = e.tsc.get(event.prevH)
   246  				if err != nil {
   247  					log.Errorf("events: applyWithConfidence didn't find tipset for previous event; wanted %d; current %d", event.prevH, height)
   248  					continue
   249  				}
   250  			}
   251  
   252  			more, err := trigger.handle(event.data, prevTs, triggerTs, height)
   253  			if err != nil {
   254  				log.Errorf("chain trigger (@H %d, triggered @ %d) failed: %s", origH, height, err)
   255  				continue // don't revert failed calls
   256  			}
   257  
   258  			event.called = true
   259  
   260  			touts, ok := e.timeouts[trigger.timeout]
   261  			if ok {
   262  				touts[event.trigger]++
   263  			}
   264  
   265  			trigger.disabled = !more
   266  		}
   267  	}
   268  }
   269  
   270  // Apply any timeouts that expire at this height
   271  func (e *hcEvents) applyTimeouts(ts *types.TipSet) {
   272  	triggers, ok := e.timeouts[ts.Height()]
   273  	if !ok {
   274  		return // nothing to do
   275  	}
   276  
   277  	for triggerID, calls := range triggers {
   278  		if calls > 0 {
   279  			continue // don't timeout if the method was called
   280  		}
   281  		trigger := e.triggers[triggerID]
   282  		if trigger.disabled {
   283  			continue
   284  		}
   285  
   286  		timeoutTs, err := e.tsc.get(ts.Height() - abi.ChainEpoch(trigger.confidence))
   287  		if err != nil {
   288  			log.Errorf("events: applyTimeouts didn't find tipset for event; wanted %d; current %d", ts.Height()-abi.ChainEpoch(trigger.confidence), ts.Height())
   289  		}
   290  
   291  		more, err := trigger.handle(nil, nil, timeoutTs, ts.Height())
   292  		if err != nil {
   293  			log.Errorf("chain trigger (call @H %d, called @ %d) failed: %s", timeoutTs.Height(), ts.Height(), err)
   294  			continue // don't revert failed calls
   295  		}
   296  
   297  		trigger.disabled = !more // allows messages after timeout
   298  	}
   299  }
   300  
   301  // Listen for an event
   302  // - CheckFunc: immediately checks if the event already occurred
   303  // - EventHandler: called when the event has occurred, after confidence tipsets
   304  // - RevertHandler: called if the chain head changes causing the event to revert
   305  // - confidence: wait this many tipsets before calling EventHandler
   306  // - timeout: at this chain height, timeout on waiting for this event
   307  func (e *hcEvents) onHeadChanged(check CheckFunc, hnd EventHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch) (triggerID, error) {
   308  	e.lk.Lock()
   309  	defer e.lk.Unlock()
   310  
   311  	// Check if the event has already occurred
   312  	ts, err := e.tsc.best()
   313  	if err != nil {
   314  		return 0, xerrors.Errorf("error getting best tipset: %w", err)
   315  	}
   316  	done, more, err := check(ts)
   317  	if err != nil {
   318  		return 0, xerrors.Errorf("called check error (h: %d): %w", ts.Height(), err)
   319  	}
   320  	if done {
   321  		timeout = NoTimeout
   322  	}
   323  
   324  	// Create a trigger for the event
   325  	id := e.ctr
   326  	e.ctr++
   327  
   328  	e.triggers[id] = &handlerInfo{
   329  		confidence: confidence,
   330  		timeout:    timeout + abi.ChainEpoch(confidence),
   331  
   332  		disabled: !more,
   333  
   334  		handle: hnd,
   335  		revert: rev,
   336  	}
   337  
   338  	// If there's a timeout, set up a timeout check at that height
   339  	if timeout != NoTimeout {
   340  		if e.timeouts[timeout+abi.ChainEpoch(confidence)] == nil {
   341  			e.timeouts[timeout+abi.ChainEpoch(confidence)] = map[uint64]int{}
   342  		}
   343  		e.timeouts[timeout+abi.ChainEpoch(confidence)][id] = 0
   344  	}
   345  
   346  	return id, nil
   347  }
   348  
   349  // headChangeAPI is used to allow the composed event APIs to call back to hcEvents
   350  // to listen for changes
   351  type headChangeAPI interface {
   352  	onHeadChanged(check CheckFunc, hnd EventHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch) (triggerID, error)
   353  }
   354  
   355  // watcherEvents watches for a state change
   356  type watcherEvents struct {
   357  	ctx   context.Context
   358  	cs    EventAPI
   359  	hcAPI headChangeAPI
   360  
   361  	lk       sync.RWMutex
   362  	matchers map[triggerID]StateMatchFunc
   363  }
   364  
   365  func newWatcherEvents(ctx context.Context, hcAPI headChangeAPI, cs EventAPI) watcherEvents {
   366  	return watcherEvents{
   367  		ctx:      ctx,
   368  		cs:       cs,
   369  		hcAPI:    hcAPI,
   370  		matchers: make(map[triggerID]StateMatchFunc),
   371  	}
   372  }
   373  
   374  // Run each of the matchers against the previous and current state to see if
   375  // there's a change
   376  func (we *watcherEvents) checkStateChanges(oldState, newState *types.TipSet) map[triggerID]eventData {
   377  	we.lk.RLock()
   378  	defer we.lk.RUnlock()
   379  
   380  	res := make(map[triggerID]eventData)
   381  	for tid, matchFn := range we.matchers {
   382  		ok, data, err := matchFn(oldState, newState)
   383  		if err != nil {
   384  			log.Errorf("event diff fn failed: %s", err)
   385  			continue
   386  		}
   387  
   388  		if ok {
   389  			res[tid] = data
   390  		}
   391  	}
   392  	return res
   393  }
   394  
   395  // StateChange represents a change in state
   396  type StateChange interface{}
   397  
   398  // StateChangeHandler arguments:
   399  // `oldTs` is the state "from" tipset
   400  // `newTs` is the state "to" tipset
   401  // `states` is the change in state
   402  // `curH`-`ts.Height` = `confidence`
   403  type StateChangeHandler func(oldTs, newTs *types.TipSet, states StateChange, curH abi.ChainEpoch) (more bool, err error)
   404  
   405  type StateMatchFunc func(oldTs, newTs *types.TipSet) (bool, StateChange, error)
   406  
   407  // StateChanged registers a callback which is triggered when a specified state
   408  // change occurs or a timeout is reached.
   409  //
   410  // * `CheckFunc` callback is invoked immediately with a recent tipset, it
   411  //    returns two booleans - `done`, and `more`.
   412  //
   413  //   * `done` should be true when some on-chain state change we are waiting
   414  //     for has happened. When `done` is set to true, timeout trigger is disabled.
   415  //
   416  //   * `more` should be false when we don't want to receive new notifications
   417  //     through StateChangeHandler. Note that notifications may still be delivered to
   418  //     RevertHandler
   419  //
   420  // * `StateChangeHandler` is called when the specified state change was observed
   421  //    on-chain, and a confidence threshold was reached, or the specified `timeout`
   422  //    height was reached with no state change observed. When this callback is
   423  //    invoked on a timeout, `oldState` and `newState` are set to nil.
   424  //    This callback returns a boolean specifying whether further notifications
   425  //    should be sent, like `more` return param from `CheckFunc` above.
   426  //
   427  // * `RevertHandler` is called after apply handler, when we drop the tipset
   428  //    containing the message. The tipset passed as the argument is the tipset
   429  //    that is being dropped. Note that the event dropped may be re-applied
   430  //    in a different tipset in small amount of time.
   431  //
   432  // * `StateMatchFunc` is called against each tipset state. If there is a match,
   433  //   the state change is queued up until the confidence interval has elapsed (and
   434  //   `StateChangeHandler` is called)
   435  func (we *watcherEvents) StateChanged(check CheckFunc, scHnd StateChangeHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, mf StateMatchFunc) error {
   436  	hnd := func(data eventData, prevTs, ts *types.TipSet, height abi.ChainEpoch) (bool, error) {
   437  		states, ok := data.(StateChange)
   438  		if data != nil && !ok {
   439  			panic("expected StateChange")
   440  		}
   441  
   442  		return scHnd(prevTs, ts, states, height)
   443  	}
   444  
   445  	id, err := we.hcAPI.onHeadChanged(check, hnd, rev, confidence, timeout)
   446  	if err != nil {
   447  		return err
   448  	}
   449  
   450  	we.lk.Lock()
   451  	defer we.lk.Unlock()
   452  	we.matchers[id] = mf
   453  
   454  	return nil
   455  }
   456  
   457  // messageEvents watches for message calls to actors
   458  type messageEvents struct {
   459  	ctx   context.Context
   460  	cs    EventAPI
   461  	hcAPI headChangeAPI
   462  
   463  	lk       sync.RWMutex
   464  	matchers map[triggerID]MsgMatchFunc
   465  }
   466  
   467  func newMessageEvents(ctx context.Context, hcAPI headChangeAPI, cs EventAPI) messageEvents {
   468  	return messageEvents{
   469  		ctx:      ctx,
   470  		cs:       cs,
   471  		hcAPI:    hcAPI,
   472  		matchers: make(map[triggerID]MsgMatchFunc),
   473  	}
   474  }
   475  
   476  // Check if there are any new actor calls
   477  func (me *messageEvents) checkNewCalls(ts *types.TipSet) (map[triggerID]eventData, error) {
   478  	pts, err := me.cs.ChainGetTipSet(me.ctx, ts.Parents()) // we actually care about messages in the parent tipset here
   479  	if err != nil {
   480  		log.Errorf("getting parent tipset in checkNewCalls: %s", err)
   481  		return nil, err
   482  	}
   483  
   484  	me.lk.RLock()
   485  	defer me.lk.RUnlock()
   486  
   487  	// For each message in the tipset
   488  	res := make(map[triggerID]eventData)
   489  	me.messagesForTs(pts, func(msg *types.Message) {
   490  		// TODO: provide receipts
   491  
   492  		// Run each trigger's matcher against the message
   493  		for tid, matchFn := range me.matchers {
   494  			matched, err := matchFn(msg)
   495  			if err != nil {
   496  				log.Errorf("event matcher failed: %s", err)
   497  				continue
   498  			}
   499  
   500  			// If there was a match, include the message in the results for the
   501  			// trigger
   502  			if matched {
   503  				res[tid] = msg
   504  			}
   505  		}
   506  	})
   507  
   508  	return res, nil
   509  }
   510  
   511  // Get the messages in a tipset
   512  func (me *messageEvents) messagesForTs(ts *types.TipSet, consume func(*types.Message)) {
   513  	seen := map[cid.Cid]struct{}{}
   514  
   515  	for _, tsb := range ts.Blocks() {
   516  
   517  		msgs, err := me.cs.ChainGetBlockMessages(context.TODO(), tsb.Cid())
   518  		if err != nil {
   519  			log.Errorf("messagesForTs MessagesForBlock failed (ts.H=%d, Bcid:%s, B.Mcid:%s): %s", ts.Height(), tsb.Cid(), tsb.Messages, err)
   520  			// this is quite bad, but probably better than missing all the other updates
   521  			continue
   522  		}
   523  
   524  		for _, m := range msgs.BlsMessages {
   525  			_, ok := seen[m.Cid()]
   526  			if ok {
   527  				continue
   528  			}
   529  			seen[m.Cid()] = struct{}{}
   530  
   531  			consume(m)
   532  		}
   533  
   534  		for _, m := range msgs.SecpkMessages {
   535  			_, ok := seen[m.Message.Cid()]
   536  			if ok {
   537  				continue
   538  			}
   539  			seen[m.Message.Cid()] = struct{}{}
   540  
   541  			consume(&m.Message)
   542  		}
   543  	}
   544  }
   545  
   546  // MsgHandler arguments:
   547  // `ts` is the tipset, in which the `msg` is included.
   548  // `curH`-`ts.Height` = `confidence`
   549  type MsgHandler func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (more bool, err error)
   550  
   551  type MsgMatchFunc func(msg *types.Message) (matched bool, err error)
   552  
   553  // Called registers a callback which is triggered when a specified method is
   554  //  called on an actor, or a timeout is reached.
   555  //
   556  // * `CheckFunc` callback is invoked immediately with a recent tipset, it
   557  //    returns two booleans - `done`, and `more`.
   558  //
   559  //   * `done` should be true when some on-chain action we are waiting for has
   560  //     happened. When `done` is set to true, timeout trigger is disabled.
   561  //
   562  //   * `more` should be false when we don't want to receive new notifications
   563  //     through MsgHandler. Note that notifications may still be delivered to
   564  //     RevertHandler
   565  //
   566  // * `MsgHandler` is called when the specified event was observed on-chain,
   567  //    and a confidence threshold was reached, or the specified `timeout` height
   568  //    was reached with no events observed. When this callback is invoked on a
   569  //    timeout, `msg` is set to nil. This callback returns a boolean specifying
   570  //    whether further notifications should be sent, like `more` return param
   571  //    from `CheckFunc` above.
   572  //
   573  // * `RevertHandler` is called after apply handler, when we drop the tipset
   574  //    containing the message. The tipset passed as the argument is the tipset
   575  //    that is being dropped. Note that the message dropped may be re-applied
   576  //    in a different tipset in small amount of time.
   577  //
   578  // * `MsgMatchFunc` is called against each message. If there is a match, the
   579  //   message is queued up until the confidence interval has elapsed (and
   580  //   `MsgHandler` is called)
   581  func (me *messageEvents) Called(check CheckFunc, msgHnd MsgHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, mf MsgMatchFunc) error {
   582  	hnd := func(data eventData, prevTs, ts *types.TipSet, height abi.ChainEpoch) (bool, error) {
   583  		msg, ok := data.(*types.Message)
   584  		if data != nil && !ok {
   585  			panic("expected msg")
   586  		}
   587  
   588  		ml, err := me.cs.StateSearchMsg(me.ctx, ts.Key(), msg.Cid(), stmgr.LookbackNoLimit, true)
   589  		if err != nil {
   590  			return false, err
   591  		}
   592  
   593  		if ml == nil {
   594  			return msgHnd(msg, nil, ts, height)
   595  		}
   596  
   597  		return msgHnd(msg, &ml.Receipt, ts, height)
   598  	}
   599  
   600  	id, err := me.hcAPI.onHeadChanged(check, hnd, rev, confidence, timeout)
   601  	if err != nil {
   602  		return err
   603  	}
   604  
   605  	me.lk.Lock()
   606  	defer me.lk.Unlock()
   607  	me.matchers[id] = mf
   608  
   609  	return nil
   610  }
   611  
   612  // Convenience function for checking and matching messages
   613  func (me *messageEvents) CalledMsg(ctx context.Context, hnd MsgHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, msg types.ChainMsg) error {
   614  	return me.Called(me.CheckMsg(ctx, msg, hnd), hnd, rev, confidence, timeout, me.MatchMsg(msg.VMMessage()))
   615  }