github.com/bigzoro/my_simplechain@v0.0.0-20240315012955-8ad0a2a29bb9/consensus/hotstuff/legals.go (about) 1 package hotstuff 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "sort" 8 9 "github.com/bigzoro/my_simplechain/common" 10 bls "github.com/bigzoro/my_simplechain/consensus/hotstuff/bls12-381" 11 hots "github.com/bigzoro/my_simplechain/consensus/hotstuff/common" 12 "github.com/bigzoro/my_simplechain/ethdb" 13 lru "github.com/hashicorp/golang-lru" 14 "golang.org/x/crypto/sha3" 15 ) 16 17 var ( 18 // errKnownID is returned when the Legal tries to add a new replica with a duplicate ID. 19 errKnownID = errors.New("known ID") 20 21 // errKnownPublicKey is returned when the Legal tries to add a new replica with a duplicate public key. 22 errKnownPublicKey = errors.New("known public key") 23 24 // replicaSnapshotKeyPrefix + snapshot hash -> replica information snapshot 25 replicaSnapshotKeyPrefix = []byte("hotstuff-") 26 27 // HeadSnapshotKey = []byte("hotstuff-head") 28 29 inmemorySnapshots = 128 30 ) 31 32 type rotation interface { 33 Leader(uint64, hots.IDSet) hots.ID 34 } 35 36 type basicRotation struct{} 37 38 func (br basicRotation) Leader(view uint64, set hots.IDSet) hots.ID { 39 if len(set) == 0 { 40 return hots.ID{} 41 } 42 43 return set[uint64(view)%uint64(len(set))] 44 } 45 46 // snapshot is the state of the replicas at a given point in time. 47 type snapshot struct { 48 // An ordered ID slice ensures that the records of each replica are consistent. 49 Idset hots.IDSet `json:"idset"` 50 Pubkeys map[hots.ID]*bls.PublicKey `json:"pubkeys"` 51 } 52 53 func NewSnapshot(ids []hots.ID, pks []*bls.PublicKey) *snapshot { 54 idset := make(hots.IDSet, 0, len(ids)) 55 pubkeys := make(map[hots.ID]*bls.PublicKey) 56 57 for i := 0; i < len(ids) && i < len(pks); i++ { 58 idset = append(idset, ids[i]) 59 pubkeys[ids[i]] = pks[i] 60 } 61 sort.Sort(idset) 62 return &snapshot{ 63 Idset: ids, 64 Pubkeys: pubkeys, 65 } 66 } 67 68 // Count returns the total number of participating replicas. 69 func (s *snapshot) Count() int { 70 return len(s.Idset) 71 } 72 73 // Threshold returns the minimum number of consensus nodes required to achieve 74 // aggregation criteria. 75 func (s *snapshot) Threshold() int { 76 return len(s.Idset) - (len(s.Idset)-1)/3 77 } 78 79 // PublicKey returns the public key corresponding to the signature ID. 80 func (s *snapshot) PublicKey(id hots.ID) (*bls.PublicKey, bool) { 81 key, exist := s.Pubkeys[id] 82 return key, exist 83 } 84 85 // ForEach calls f for each ID in the quorum. 86 func (s *snapshot) ForEach(f func(hots.ID, *bls.PublicKey)) { 87 for id, key := range s.Pubkeys { 88 f(id, key) 89 } 90 } 91 92 // RangeWhile calls f for each ID in the quorum until f returns false 93 func (s *snapshot) RangeWhile(f func(hots.ID, *bls.PublicKey) bool) { 94 for id, key := range s.Pubkeys { 95 if !f(id, key) { 96 return 97 } 98 } 99 } 100 101 // Leader returns the ID of leader for a given view 102 func (s *snapshot) Leader(view uint64, rotate rotation) hots.ID { 103 return rotate.Leader(view, s.Idset) 104 } 105 106 func (s *snapshot) set(id hots.ID, pk *bls.PublicKey) { 107 if _, ok := s.Pubkeys[id]; !ok { 108 s.Idset = append(s.Idset, id) 109 sort.Sort(s.Idset) 110 } 111 s.Pubkeys[id] = pk 112 } 113 114 func (s *snapshot) remove(id hots.ID) { 115 if _, ok := s.Pubkeys[id]; ok { 116 for i := range s.Idset { 117 if bytes.Equal(s.Idset[i].Bytes(), id.Bytes()) { 118 s.Idset = append(s.Idset[:i], s.Idset[i+1:]...) 119 break 120 } 121 } 122 } 123 delete(s.Pubkeys, id) 124 } 125 126 func (s *snapshot) Hash() (hash common.Hash) { 127 hasher := sha3.NewLegacyKeccak256() 128 for i := range s.Idset { 129 hasher.Write(s.Idset[i].Bytes()) 130 hasher.Write(s.Pubkeys[s.Idset[i]].ToBytes()) 131 } 132 hasher.Sum(hash[:0]) 133 return 134 } 135 136 func (s *snapshot) clone() *snapshot { 137 idset := make(hots.IDSet, len(s.Idset)) 138 copy(idset, s.Idset) 139 140 pubkeys := make(map[hots.ID]*bls.PublicKey) 141 for id, pk := range s.Pubkeys { 142 pubkeys[id] = pk 143 } 144 return &snapshot{ 145 Idset: idset, 146 Pubkeys: pubkeys, 147 } 148 } 149 150 func (s *snapshot) Store(db ethdb.Database) error { 151 blob, err := json.Marshal(s) 152 if err != nil { 153 return err 154 } 155 return db.Put(append(replicaSnapshotKeyPrefix, s.Hash().Bytes()...), blob) 156 } 157 158 func loadSnapshot(db ethdb.Database, hash common.Hash) (*snapshot, error) { 159 raw, err := db.Get(append(replicaSnapshotKeyPrefix, hash.Bytes()...)) 160 if err != nil { 161 return nil, err 162 } 163 var snap snapshot 164 if err := json.Unmarshal(raw, &snap); err != nil { 165 return nil, err 166 } 167 return &snap, nil 168 } 169 170 // Legal tracks the valid Hotstuff relicas and records their public keys and IDs 171 type Legal struct { 172 rot rotation 173 174 recent *lru.ARCCache 175 176 db ethdb.Database 177 } 178 179 func NewLegal(db ethdb.Database) *Legal { 180 recent, _ := lru.NewARC(inmemorySnapshots) 181 return &Legal{ 182 db: db, 183 rot: basicRotation{}, 184 recent: recent, 185 } 186 } 187 188 func (lg *Legal) snapshot(hash common.Hash) (s *snapshot, err error) { 189 if val, ok := lg.recent.Get(hash); ok { 190 return val.(*snapshot), nil 191 } 192 193 s, err = loadSnapshot(lg.db, hash) 194 if err != nil { 195 return nil, err 196 } 197 lg.recent.Add(hash, s) 198 return s, nil 199 } 200 201 func (lg *Legal) store(snap *snapshot) error { 202 if err := snap.Store(lg.db); err != nil { 203 return err 204 } 205 lg.recent.Add(snap.Hash(), snap) 206 return nil 207 }