github.com/braveheart12/insolar-09-08-19@v0.8.7/network/fakepulsar/fakepulsar.go (about)

     1  /*
     2   * The Clear BSD License
     3   *
     4   * Copyright (c) 2019 Insolar Technologies
     5   *
     6   * All rights reserved.
     7   *
     8   * Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:
     9   *
    10   *  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    11   *  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    12   *  Neither the name of Insolar Technologies nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    13   *
    14   * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    15   *
    16   */
    17  
    18  package fakepulsar
    19  
    20  import (
    21  	"context"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/insolar/insolar/core"
    26  	"github.com/insolar/insolar/instrumentation/inslogger"
    27  	"github.com/insolar/insolar/log"
    28  	"github.com/insolar/insolar/network"
    29  )
    30  
    31  // Fakepulsar needed when the network starts and can't receive a real pulse.
    32  
    33  // onPulse is a callbaback for pulse recv.
    34  // type callbackOnPulse func(ctx context.Context, pulse core.Pulse)
    35  
    36  // FakePulsar is a struct which uses at void network state.
    37  type FakePulsar struct {
    38  	onPulse network.PulseHandler
    39  	stop    chan bool
    40  	mutex   sync.RWMutex
    41  	running bool
    42  
    43  	firstPulseTime     time.Time
    44  	pulseDuration      time.Duration
    45  	pulseNumberDelta   core.PulseNumber
    46  	currentPulseNumber core.PulseNumber
    47  }
    48  
    49  // NewFakePulsar creates and returns a new FakePulsar.
    50  func NewFakePulsar(callback network.PulseHandler, pulseDuration time.Duration) *FakePulsar {
    51  	return &FakePulsar{
    52  		onPulse: callback,
    53  		stop:    make(chan bool),
    54  		running: false,
    55  
    56  		pulseDuration:    pulseDuration,
    57  		pulseNumberDelta: core.PulseNumber(pulseDuration.Seconds()),
    58  	}
    59  }
    60  
    61  // Start starts sending a fake pulse.
    62  func (fp *FakePulsar) Start(ctx context.Context, firstPulseTime time.Time) {
    63  	fp.mutex.Lock()
    64  	defer fp.mutex.Unlock()
    65  
    66  	logger := inslogger.FromContext(ctx)
    67  
    68  	fp.running = true
    69  	fp.firstPulseTime = firstPulseTime
    70  
    71  	pulseInfo := fp.getPulseInfo()
    72  
    73  	fp.currentPulseNumber = pulseInfo.currentPulseNumber
    74  
    75  	logger.Infof(
    76  		"Fake pulsar is going to start, currentPulse: %d, next pulse scheduled for: %s",
    77  		pulseInfo.currentPulseNumber,
    78  		time.Now().Add(pulseInfo.nextPulseAfter),
    79  	)
    80  
    81  	time.AfterFunc(pulseInfo.nextPulseAfter, func() {
    82  		fp.pulse(ctx)
    83  		for {
    84  			pulseInfo := fp.getPulseInfo()
    85  
    86  			logger.Debug("Pulse scheduled for: ", time.Now().Add(pulseInfo.nextPulseAfter))
    87  
    88  			select {
    89  			case <-time.After(pulseInfo.nextPulseAfter):
    90  				fp.pulse(ctx)
    91  			case <-fp.stop:
    92  				return
    93  			}
    94  		}
    95  	})
    96  }
    97  
    98  func (fp *FakePulsar) getPulseInfo() pulseInfo {
    99  	return calculatePulseInfo(time.Now(), fp.firstPulseTime, fp.pulseDuration)
   100  }
   101  
   102  func (fp *FakePulsar) pulse(ctx context.Context) {
   103  	fp.mutex.Lock()
   104  	defer fp.mutex.Unlock()
   105  
   106  	if !fp.running {
   107  		return
   108  	}
   109  
   110  	fp.currentPulseNumber += fp.pulseNumberDelta
   111  	go fp.onPulse.HandlePulse(ctx, *fp.newPulse())
   112  }
   113  
   114  // Stop sending a fake pulse.
   115  func (fp *FakePulsar) Stop(ctx context.Context) {
   116  	fp.mutex.Lock()
   117  	defer fp.mutex.Unlock()
   118  
   119  	inslogger.FromContext(ctx).Info("Fake pulsar going to stop")
   120  
   121  	if fp.running {
   122  		fp.stop <- true
   123  		close(fp.stop)
   124  		fp.running = false
   125  	}
   126  
   127  	inslogger.FromContext(ctx).Info("Fake pulsar stopped")
   128  }
   129  
   130  func (fp *FakePulsar) newPulse() *core.Pulse {
   131  	return &core.Pulse{
   132  		PulseTimestamp:   time.Now().Unix(),
   133  		PrevPulseNumber:  core.PulseNumber(fp.currentPulseNumber - fp.pulseNumberDelta),
   134  		PulseNumber:      core.PulseNumber(fp.currentPulseNumber),
   135  		NextPulseNumber:  core.PulseNumber(fp.currentPulseNumber + fp.pulseNumberDelta),
   136  		EpochPulseNumber: -1,
   137  		Entropy:          core.Entropy{},
   138  	}
   139  }
   140  
   141  type pulseInfo struct {
   142  	currentPulseNumber core.PulseNumber
   143  	nextPulseAfter     time.Duration
   144  }
   145  
   146  func calculatePulseInfo(targetTime, firstPulseTime time.Time, pulseDuration time.Duration) pulseInfo {
   147  	if firstPulseTime.After(targetTime) {
   148  		log.Warn("First pulse time `%s` is after then targetTime `%s`", firstPulseTime, targetTime)
   149  
   150  		return pulseInfo{
   151  			currentPulseNumber: core.PulseNumber(0),
   152  			nextPulseAfter:     firstPulseTime.Sub(targetTime),
   153  		}
   154  	}
   155  
   156  	timeSinceFirstPulse := targetTime.Sub(firstPulseTime)
   157  
   158  	passedPulses := int64(timeSinceFirstPulse) / int64(pulseDuration)
   159  	currentPulseNumber := core.PulseNumber(passedPulses)
   160  
   161  	passedPulsesDuration := time.Duration(int64(pulseDuration) * passedPulses)
   162  	nextPulseAfter := pulseDuration - (timeSinceFirstPulse - passedPulsesDuration)
   163  
   164  	return pulseInfo{
   165  		currentPulseNumber: currentPulseNumber,
   166  		nextPulseAfter:     nextPulseAfter,
   167  	}
   168  }