github.com/bigzoro/my_simplechain@v0.0.0-20240315012955-8ad0a2a29bb9/consensus/hotstuff/navigate.go (about)

     1  package hotstuff
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"sort"
     8  	"sync"
     9  
    10  	bls "github.com/bigzoro/my_simplechain/consensus/hotstuff/bls12-381"
    11  	hots "github.com/bigzoro/my_simplechain/consensus/hotstuff/common"
    12  )
    13  
    14  // An event of a node joined to removed from consensus replicas.
    15  type event struct {
    16  	kind   uint8
    17  	expire uint64
    18  	id     hots.ID
    19  	pubkey *bls.PublicKey
    20  
    21  	sig *bls.AggregateSignature
    22  
    23  	hash []byte
    24  }
    25  
    26  type events []*event
    27  
    28  func (set events) Len() int { return len(set) }
    29  
    30  func (set events) Swap(i, j int) { set[i], set[j] = set[j], set[i] }
    31  
    32  func (set events) Less(i, j int) bool {
    33  	return set[i].expire < set[j].expire
    34  }
    35  
    36  func (ev *event) digest() []byte {
    37  	if len(ev.hash) > 0 {
    38  		return ev.hash
    39  	}
    40  	blknum := make([]byte, 8)
    41  	binary.LittleEndian.PutUint64(blknum, ev.expire)
    42  	texts := [][]byte{{ev.kind}, blknum, ev.id.Bytes()}
    43  	if ev.kind == replicaJoined {
    44  		texts = append(texts, ev.pubkey.ToBytes())
    45  	}
    46  	ev.hash = legacyCypherDigest(texts...).Bytes()
    47  	return ev.hash
    48  }
    49  
    50  func (ev *event) verify(snap *snapshot, number uint64) error {
    51  	if ev.expire <= number {
    52  		// Discard the event if the timeout block height of the event has exceeded the
    53  		// current mined block height.
    54  		return fmt.Errorf("expired event for %d/%d", ev.expire, number)
    55  	}
    56  
    57  	return ev.sig.Verify(snap, ev.digest())
    58  }
    59  
    60  func (ev *event) apply(snap *snapshot) *snapshot {
    61  	clone := snap.clone()
    62  	switch ev.kind {
    63  	case replicaJoined:
    64  		clone.set(ev.id, ev.pubkey)
    65  	case replicaRemoved:
    66  		clone.remove(ev.id)
    67  	}
    68  	return clone
    69  }
    70  
    71  // navigation promotes the replica events to be applied to latest snapshot.
    72  type navigation struct {
    73  	mux    sync.RWMutex
    74  	events map[hots.ID]*event
    75  }
    76  
    77  func newNavigation() *navigation {
    78  	return &navigation{
    79  		events: make(map[hots.ID]*event),
    80  	}
    81  }
    82  
    83  func (nav *navigation) add(ev *event) error {
    84  	nav.mux.Lock()
    85  	defer nav.mux.Unlock()
    86  
    87  	event, ok := nav.events[ev.id]
    88  
    89  	if !ok || (event.kind == ev.kind && ev.expire > event.expire) {
    90  		nav.events[ev.id] = ev
    91  	}
    92  
    93  	if ok && event.kind != ev.kind {
    94  		delete(nav.events, ev.id)
    95  		log.Debug("negating replica event", "kind", ev.kind, "id", ev.id)
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  func (nav *navigation) commit(snap *snapshot) *event {
   102  	nav.mux.Lock()
   103  	defer nav.mux.Unlock()
   104  
   105  	var diffs events
   106  	for id, event := range nav.events {
   107  		pk, ok := snap.Pubkeys[id]
   108  		switch event.kind {
   109  		case replicaJoined:
   110  
   111  			if !ok || !bytes.Equal(pk.ToBytes(), event.pubkey.ToBytes()) {
   112  				diffs = append(diffs, event)
   113  			} else {
   114  				delete(nav.events, id)
   115  				log.Debug("confirmed replica event", "kind", event.kind, "id", event.id)
   116  			}
   117  
   118  		case replicaRemoved:
   119  
   120  			if ok {
   121  				diffs = append(diffs, event)
   122  			} else {
   123  				delete(nav.events, id)
   124  				log.Debug("confirmed replica event", "kind", event.kind, "id", event.id)
   125  			}
   126  
   127  		}
   128  	}
   129  
   130  	if len(diffs) > 0 {
   131  		sort.Sort(diffs)
   132  		return diffs[0]
   133  	}
   134  	return nil
   135  }