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  }