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 }