github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/emitter/emitter.go (about) 1 package emitter 2 3 import ( 4 "errors" 5 "fmt" 6 "math/rand" 7 "os" 8 "strings" 9 "sync" 10 "time" 11 12 "github.com/unicornultrafoundation/go-helios/emitter/ancestor" 13 "github.com/unicornultrafoundation/go-helios/hash" 14 "github.com/unicornultrafoundation/go-helios/native/idx" 15 "github.com/unicornultrafoundation/go-helios/native/pos" 16 "github.com/unicornultrafoundation/go-helios/utils/piecefunc" 17 "github.com/unicornultrafoundation/go-u2u/core/types" 18 19 "github.com/unicornultrafoundation/go-u2u/gossip/emitter/originatedtxs" 20 "github.com/unicornultrafoundation/go-u2u/logger" 21 "github.com/unicornultrafoundation/go-u2u/native" 22 "github.com/unicornultrafoundation/go-u2u/tracing" 23 "github.com/unicornultrafoundation/go-u2u/utils/errlock" 24 "github.com/unicornultrafoundation/go-u2u/utils/rate" 25 ) 26 27 const ( 28 SenderCountBufferSize = 20000 29 PayloadIndexerSize = 5000 30 ) 31 32 type Emitter struct { 33 config Config 34 35 world World 36 37 syncStatus syncStatus 38 39 prevIdleTime time.Time 40 prevEmittedAtTime time.Time 41 prevEmittedAtBlock idx.Block 42 originatedTxs *originatedtxs.Buffer 43 pendingGas uint64 44 45 // note: track validators and epoch internally to avoid referring to 46 // validators of a future epoch inside OnEventConnected of last epoch event 47 validators *pos.Validators 48 epoch idx.Epoch 49 50 // challenges is deadlines when each validator should emit an event 51 challenges map[idx.ValidatorID]time.Time 52 // offlineValidators is a map of validators which are likely to be offline 53 // This map may be different on different instances 54 offlineValidators map[idx.ValidatorID]bool 55 expectedEmitIntervals map[idx.ValidatorID]time.Duration 56 stakeRatio map[idx.ValidatorID]uint64 57 58 prevRecheckedChallenges time.Time 59 60 quorumIndexer *ancestor.QuorumIndexer 61 fcIndexer *ancestor.FCIndexer 62 payloadIndexer *ancestor.PayloadIndexer 63 64 intervals EmitIntervals 65 globalConfirmingInterval time.Duration 66 67 done chan struct{} 68 wg sync.WaitGroup 69 70 maxParents idx.Event 71 72 cache struct { 73 sortedTxs *types.TransactionsByPriceAndNonce 74 poolTime time.Time 75 poolBlock idx.Block 76 poolCount int 77 } 78 79 emittedEventFile *os.File 80 emittedBvsFile *os.File 81 emittedEvFile *os.File 82 busyRate *rate.Gauge 83 84 switchToFCIndexer bool 85 validatorVersions map[idx.ValidatorID]uint64 86 87 logger.Periodic 88 } 89 90 // NewEmitter creation. 91 func NewEmitter( 92 config Config, 93 world World, 94 ) *Emitter { 95 // Randomize event time to decrease chance of 2 parallel instances emitting event at the same time 96 // It increases the chance of detecting parallel instances 97 r := rand.New(rand.NewSource(time.Now().UnixNano())) 98 config.EmitIntervals = config.EmitIntervals.RandomizeEmitTime(r) 99 100 return &Emitter{ 101 config: config, 102 world: world, 103 originatedTxs: originatedtxs.New(SenderCountBufferSize), 104 intervals: config.EmitIntervals, 105 Periodic: logger.Periodic{Instance: logger.New()}, 106 validatorVersions: make(map[idx.ValidatorID]uint64), 107 } 108 } 109 110 // init emitter without starting events emission 111 func (em *Emitter) init() { 112 em.syncStatus.startup = time.Now() 113 em.syncStatus.lastConnected = time.Now() 114 em.syncStatus.p2pSynced = time.Now() 115 validators, epoch := em.world.GetEpochValidators() 116 em.OnNewEpoch(validators, epoch) 117 118 if len(em.config.PrevEmittedEventFile.Path) != 0 { 119 em.emittedEventFile = openPrevActionFile(em.config.PrevEmittedEventFile.Path, em.config.PrevEmittedEventFile.SyncMode) 120 } 121 if len(em.config.PrevBlockVotesFile.Path) != 0 { 122 em.emittedBvsFile = openPrevActionFile(em.config.PrevBlockVotesFile.Path, em.config.PrevBlockVotesFile.SyncMode) 123 } 124 if len(em.config.PrevEpochVoteFile.Path) != 0 { 125 em.emittedEvFile = openPrevActionFile(em.config.PrevEpochVoteFile.Path, em.config.PrevEpochVoteFile.SyncMode) 126 } 127 em.busyRate = rate.NewGauge() 128 } 129 130 // Start starts event emission. 131 func (em *Emitter) Start() { 132 if em.config.Validator.ID == 0 { 133 // short circuit if not a validator 134 return 135 } 136 if em.done != nil { 137 return 138 } 139 em.init() 140 em.done = make(chan struct{}) 141 142 done := em.done 143 if em.config.EmitIntervals.Min == 0 { 144 return 145 } 146 em.wg.Add(1) 147 go func() { 148 defer em.wg.Done() 149 tick := 11 * time.Millisecond 150 timer := time.NewTimer(tick) 151 defer timer.Stop() 152 for { 153 select { 154 case <-timer.C: 155 em.tick() 156 case <-done: 157 return 158 } 159 timer.Reset(tick) 160 } 161 }() 162 } 163 164 // Stop stops event emission. 165 func (em *Emitter) Stop() { 166 if em.done == nil { 167 return 168 } 169 170 close(em.done) 171 em.done = nil 172 em.wg.Wait() 173 em.busyRate.Stop() 174 } 175 176 func (em *Emitter) tick() { 177 // track synced time 178 if em.world.PeersNum() == 0 { 179 // connected time ~= last time when it's true that "not connected yet" 180 em.syncStatus.lastConnected = time.Now() 181 } 182 if !em.world.IsSynced() { 183 // synced time ~= last time when it's true that "not synced yet" 184 em.syncStatus.p2pSynced = time.Now() 185 } 186 if em.idle() { 187 em.busyRate.Mark(0) 188 } else { 189 em.busyRate.Mark(1) 190 } 191 if em.world.IsBusy() { 192 return 193 } 194 195 em.recheckChallenges() 196 em.recheckIdleTime() 197 if time.Since(em.prevEmittedAtTime) >= em.intervals.Min { 198 _, _ = em.EmitEvent() 199 } 200 } 201 202 func (em *Emitter) getSortedTxs() *types.TransactionsByPriceAndNonce { 203 // Short circuit if pool wasn't updated since the cache was built 204 poolCount := em.world.TxPool.Count() 205 if em.cache.sortedTxs != nil && 206 em.cache.poolBlock == em.world.GetLatestBlockIndex() && 207 em.cache.poolCount == poolCount && 208 time.Since(em.cache.poolTime) < em.config.TxsCacheInvalidation { 209 return em.cache.sortedTxs.Copy() 210 } 211 // Build the cache 212 pendingTxs, err := em.world.TxPool.Pending(true) 213 if err != nil { 214 em.Log.Error("Tx pool transactions fetching error", "err", err) 215 return nil 216 } 217 for from, txs := range pendingTxs { 218 // Filter the excessive transactions from each sender 219 if len(txs) > em.config.MaxTxsPerAddress { 220 pendingTxs[from] = txs[:em.config.MaxTxsPerAddress] 221 } 222 } 223 sortedTxs := types.NewTransactionsByPriceAndNonce(em.world.TxSigner, pendingTxs, em.world.GetRules().Economy.MinGasPrice) 224 em.cache.sortedTxs = sortedTxs 225 em.cache.poolCount = poolCount 226 em.cache.poolBlock = em.world.GetLatestBlockIndex() 227 em.cache.poolTime = time.Now() 228 return sortedTxs.Copy() 229 } 230 231 func (em *Emitter) EmitEvent() (*native.EventPayload, error) { 232 if em.config.Validator.ID == 0 { 233 // short circuit if not a validator 234 return nil, nil 235 } 236 sortedTxs := em.getSortedTxs() 237 238 if em.world.IsBusy() { 239 return nil, nil 240 } 241 em.world.Lock() 242 defer em.world.Unlock() 243 244 e, err := em.createEvent(sortedTxs) 245 if e == nil || err != nil { 246 return nil, err 247 } 248 em.syncStatus.prevLocalEmittedID = e.ID() 249 250 err = em.world.Process(e) 251 if err != nil { 252 em.Log.Error("Self-event connection failed", "err", err.Error()) 253 return nil, err 254 } 255 // write event ID to avoid doublesigning in future after a crash 256 em.writeLastEmittedEventID(e.ID()) 257 if e.EpochVote().Epoch != 0 { 258 em.writeLastEmittedEpochVote(e.EpochVote().Epoch) 259 } 260 if len(e.BlockVotes().Votes) != 0 { 261 em.writeLastEmittedBlockVotes(e.BlockVotes().LastBlock()) 262 } 263 // broadcast the event 264 em.world.Broadcast(e) 265 266 em.prevEmittedAtTime = time.Now() // record time after connecting, to add the event processing time" 267 em.prevEmittedAtBlock = em.world.GetLatestBlockIndex() 268 269 // metrics 270 if tracing.Enabled() { 271 for _, t := range e.Txs() { 272 span := tracing.CheckTx(t.Hash(), "Emitter.EmitEvent()") 273 defer span.Finish() 274 } 275 } 276 277 return e, nil 278 } 279 280 func (em *Emitter) loadPrevEmitTime() time.Time { 281 prevEventID := em.world.GetLastEvent(em.epoch, em.config.Validator.ID) 282 if prevEventID == nil { 283 return em.prevEmittedAtTime 284 } 285 prevEvent := em.world.GetEvent(*prevEventID) 286 if prevEvent == nil { 287 return em.prevEmittedAtTime 288 } 289 return prevEvent.CreationTime().Time() 290 } 291 292 // createEvent is not safe for concurrent use. 293 func (em *Emitter) createEvent(sortedTxs *types.TransactionsByPriceAndNonce) (*native.EventPayload, error) { 294 if !em.isValidator() { 295 return nil, nil 296 } 297 298 if synced := em.logSyncStatus(em.isSyncedToEmit()); !synced { 299 // I'm reindexing my old events, so don't create events until connect all the existing self-events 300 return nil, nil 301 } 302 303 var ( 304 selfParentSeq idx.Event 305 selfParentTime native.Timestamp 306 parents hash.Events 307 maxLamport idx.Lamport 308 ) 309 310 // Find parents 311 selfParent, parents, ok := em.chooseParents(em.epoch, em.config.Validator.ID) 312 if !ok { 313 return nil, nil 314 } 315 prevEmitted := em.readLastEmittedEventID() 316 if prevEmitted != nil && prevEmitted.Epoch() >= em.epoch { 317 if selfParent == nil || *selfParent != *prevEmitted { 318 errlock.Permanent(errors.New("local database is corrupted, which may lead to a double sign")) 319 } 320 } 321 322 // Set parent-dependent fields 323 parentHeaders := make(native.Events, len(parents)) 324 for i, p := range parents { 325 parent := em.world.GetEvent(p) 326 if parent == nil { 327 em.Log.Crit("Emitter: head not found", "mutEvent", p.String()) 328 } 329 parentHeaders[i] = parent 330 if parentHeaders[i].Creator() == em.config.Validator.ID && i != 0 { 331 // there are 2 heads from me, i.e. due to a fork, chooseParents could have found multiple self-parents 332 em.Periodic.Error(5*time.Second, "I've created a fork, events emitting isn't allowed", "creator", em.config.Validator.ID) 333 return nil, nil 334 } 335 maxLamport = idx.MaxLamport(maxLamport, parent.Lamport()) 336 } 337 338 selfParentSeq = 0 339 selfParentTime = 0 340 var selfParentHeader *native.Event 341 if selfParent != nil { 342 selfParentHeader = parentHeaders[0] 343 selfParentSeq = selfParentHeader.Seq() 344 selfParentTime = selfParentHeader.CreationTime() 345 } 346 347 version := uint8(0) 348 if em.world.GetRules().Upgrades.Llr { 349 version = 1 350 } 351 352 mutEvent := &native.MutableEventPayload{} 353 mutEvent.SetVersion(version) 354 mutEvent.SetEpoch(em.epoch) 355 mutEvent.SetSeq(selfParentSeq + 1) 356 mutEvent.SetCreator(em.config.Validator.ID) 357 358 mutEvent.SetParents(parents) 359 mutEvent.SetLamport(maxLamport + 1) 360 mutEvent.SetCreationTime(native.MaxTimestamp(native.Timestamp(time.Now().UnixNano()), selfParentTime+1)) 361 362 // add LLR votes 363 em.addLlrEpochVote(mutEvent) 364 em.addLlrBlockVotes(mutEvent) 365 366 // node version 367 if mutEvent.Seq() <= 1 && len(em.config.VersionToPublish) > 0 { 368 version := []byte("v-" + em.config.VersionToPublish) 369 if uint32(len(version)) <= em.world.GetRules().Dag.MaxExtraData { 370 mutEvent.SetExtra(version) 371 } 372 } 373 374 // set consensus fields 375 var metric ancestor.Metric 376 err := em.world.Build(mutEvent, func() { 377 if em.fcIndexer != nil { 378 pastMe := em.fcIndexer.ValidatorsPastMe() 379 metric = (ancestor.Metric(pastMe) * piecefunc.DecimalUnit) / ancestor.Metric(em.validators.TotalWeight()) 380 if pastMe < em.validators.Quorum() { 381 metric /= 15 382 } 383 if metric < 0.03*piecefunc.DecimalUnit { 384 metric = 0.03 * piecefunc.DecimalUnit 385 } 386 metric = overheadAdjustedEventMetricF(em.validators.Len(), uint64(em.busyRate.Rate1()*piecefunc.DecimalUnit), metric) 387 metric = kickStartMetric(metric/2, mutEvent.Seq()) // adjust emission interval for FC 388 } else if em.quorumIndexer != nil { 389 metric = eventMetric(em.quorumIndexer.GetMetricOf(hash.Events{mutEvent.ID()}), mutEvent.Seq()) 390 metric = overheadAdjustedEventMetricF(em.validators.Len(), uint64(em.busyRate.Rate1()*piecefunc.DecimalUnit), metric) 391 } 392 }) 393 if err != nil { 394 if err == ErrNotEnoughGasPower { 395 em.Periodic.Warn(time.Second, "Not enough gas power to emit event. Too small stake?", 396 "stake%", 100*float64(em.validators.Get(em.config.Validator.ID))/float64(em.validators.TotalWeight())) 397 } else { 398 em.Log.Warn("Dropped event while emitting", "err", err) 399 } 400 return nil, nil 401 } 402 403 // Pre-check if event should be emitted 404 // It is checked in advance to avoid adding transactions just to immediately drop the event later 405 if !em.isAllowedToEmit(mutEvent, true, metric, selfParentHeader) { 406 return nil, nil 407 } 408 409 // Add txs 410 em.addTxs(mutEvent, sortedTxs) 411 412 // Check if event should be emitted 413 // Check only if no txs were added, since check in a case with added txs was performed above 414 if mutEvent.Txs().Len() == 0 { 415 if !em.isAllowedToEmit(mutEvent, mutEvent.Txs().Len() != 0, metric, selfParentHeader) { 416 return nil, nil 417 } 418 } 419 420 // calc Payload hash 421 mutEvent.SetPayloadHash(native.CalcPayloadHash(mutEvent)) 422 423 // sign 424 bSig, err := em.world.Signer.Sign(em.config.Validator.PubKey, mutEvent.HashToSign().Bytes()) 425 if err != nil { 426 em.Periodic.Error(time.Second, "Failed to sign event", "err", err) 427 return nil, err 428 } 429 var sig native.Signature 430 copy(sig[:], bSig) 431 mutEvent.SetSig(sig) 432 433 // build clean event 434 event := mutEvent.Build() 435 436 // check 437 if err := em.world.Check(event, parentHeaders); err != nil { 438 em.Periodic.Error(time.Second, "Emitted incorrect event", "err", err) 439 return nil, err 440 } 441 442 // set mutEvent name for debug 443 em.nameEventForDebug(event) 444 445 return event, nil 446 } 447 448 func (em *Emitter) idle() bool { 449 return em.originatedTxs.Empty() 450 } 451 452 func (em *Emitter) isValidator() bool { 453 return em.config.Validator.ID != 0 && em.validators.Exists(em.config.Validator.ID) 454 } 455 456 func (em *Emitter) nameEventForDebug(e *native.EventPayload) { 457 name := []rune(hash.GetNodeName(e.Creator())) 458 if len(name) < 1 { 459 return 460 } 461 462 name = name[len(name)-1:] 463 hash.SetEventName(e.ID(), fmt.Sprintf("%s%03d", 464 strings.ToLower(string(name)), 465 e.Seq())) 466 }