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 }