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

     1  package hotstuff
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"github.com/bigzoro/my_simplechain/common"
    10  	bls "github.com/bigzoro/my_simplechain/consensus/hotstuff/bls12-381"
    11  	"github.com/bigzoro/my_simplechain/core/types"
    12  	"golang.org/x/crypto/sha3"
    13  )
    14  
    15  const (
    16  	// header.extra
    17  	// field|| snapshot hash || signature length || aggregate signature || HotStuff replica event
    18  	// bytes||       32      ||        2         ||         >96         ||  >(61+96)  or >(13+96)
    19  
    20  	snapshotLen = 32 // The fixed length of replicas snapshot
    21  
    22  	signLen = 2
    23  
    24  	minAggsigLen = 96 // The minimum length of the serialized bytes of an aggregate signature.
    25  
    26  	eventKind = 1
    27  
    28  	expiredBlock = 8
    29  
    30  	eventId = 4
    31  
    32  	eventPublicKey = 48
    33  
    34  	// Known replica event type.
    35  	replicaJoined uint8 = 0xf1 + iota
    36  	replicaRemoved
    37  )
    38  
    39  var (
    40  	// HotStuff replica event
    41  	// field|| event kind || expired block number || hotstuff id || public key || aggregate signature
    42  	// bytes||      1     ||          8           ||      4      ||    0/48    ||         >96
    43  	removedEventTopicLen = eventKind + expiredBlock + eventId
    44  
    45  	joinedEventTopicLen = removedEventTopicLen + removedEventTopicLen
    46  
    47  	// errInvalidExtra is returned if the extra format of a header does not comply
    48  	// with the HotStuff type specification
    49  	errInvalidExtra = errors.New("invalid extra field")
    50  )
    51  
    52  // In this HotStuff scheme, the quorum certificate is embedded in the block, and
    53  // extract recovers the aggregate signature from block.extra.
    54  func extract(header *types.Header) (common.Hash, *bls.AggregateSignature, *event, error) {
    55  	if len(header.Extra) <= snapshotLen+signLen {
    56  		return common.Hash{}, nil, nil, errInvalidExtra
    57  	}
    58  
    59  	siglen := binary.LittleEndian.Uint16(header.Extra[snapshotLen : snapshotLen+signLen])
    60  	raw := header.Extra[snapshotLen+signLen:]
    61  	if len(raw) < int(siglen) {
    62  		return common.Hash{}, nil, nil, errInvalidExtra
    63  	}
    64  	sig := new(bls.AggregateSignature)
    65  	if err := sig.FromBytes(raw[:siglen]); err != nil {
    66  		return common.Hash{}, nil, nil, err
    67  	}
    68  
    69  	ev, err := extractReplicaEvent(header, true)
    70  
    71  	return common.BytesToHash(header.Extra[:snapshotLen]), sig, ev, err
    72  }
    73  
    74  func extractSnapshot(header *types.Header, omit bool) (common.Hash, error) {
    75  	if !omit && len(header.Extra) <= snapshotLen {
    76  		return common.Hash{}, errInvalidExtra
    77  	}
    78  	return common.BytesToHash(header.Extra[:snapshotLen]), nil
    79  }
    80  
    81  func extractReplicaEvent(header *types.Header, omit bool) (*event, error) {
    82  	extra := header.Extra
    83  	if !omit && len(header.Extra) <= snapshotLen+signLen {
    84  		return nil, errInvalidExtra
    85  	}
    86  	siglen := binary.LittleEndian.Uint16(header.Extra[snapshotLen : snapshotLen+signLen])
    87  	extra = extra[snapshotLen+signLen+siglen:]
    88  
    89  	if len(extra) == 0 {
    90  		return nil, nil
    91  	}
    92  	ev := new(event)
    93  
    94  	ev.kind = extra[:eventKind][0]
    95  	switch ev.kind {
    96  	case replicaJoined:
    97  		if len(extra) < joinedEventTopicLen+minAggsigLen {
    98  			return nil, fmt.Errorf("joined type event:%w", errInvalidExtra)
    99  		}
   100  
   101  	case replicaRemoved:
   102  		if len(extra) < removedEventTopicLen+minAggsigLen {
   103  			return nil, fmt.Errorf("removed type event:%w", errInvalidExtra)
   104  		}
   105  
   106  	default:
   107  		return nil, fmt.Errorf("unknown event type:%x", ev.kind)
   108  	}
   109  
   110  	extra = extra[eventKind:]
   111  	ev.expire = binary.LittleEndian.Uint64(extra[:expiredBlock])
   112  
   113  	extra = extra[expiredBlock:]
   114  	ev.id.FromBytes(extra[:eventId])
   115  
   116  	extra = extra[eventId:]
   117  	if ev.kind == replicaJoined {
   118  		ev.pubkey = new(bls.PublicKey)
   119  		if err := ev.pubkey.FromBytes(extra[:eventPublicKey]); err != nil {
   120  			return nil, fmt.Errorf("extrat replica event public key:%w", err)
   121  		}
   122  		extra = extra[eventPublicKey:]
   123  	}
   124  
   125  	ev.sig = new(bls.AggregateSignature)
   126  	if err := ev.sig.FromBytes(extra); err != nil {
   127  		return nil, fmt.Errorf("extrat replica event aggregate signature:%w", err)
   128  	}
   129  
   130  	return ev, nil
   131  }
   132  
   133  // // seal serializes the content of an aggregate signature and embeds it into block.extra.
   134  func seal(snapshot common.Hash, sig *bls.AggregateSignature, ev *event) ([]byte, error) {
   135  	sigbytes, err := sig.ToBytes()
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	buffer := bytes.NewBuffer(make([]byte, 0, snapshotLen+signLen+len(sigbytes)))
   141  
   142  	buffer.Write(snapshot.Bytes())
   143  	siglen := make([]byte, signLen)
   144  	binary.LittleEndian.PutUint16(siglen, uint16(len(sigbytes)))
   145  	buffer.Write(siglen)
   146  	buffer.Write(sigbytes)
   147  
   148  	if ev != nil {
   149  		evsigbytes, err := ev.sig.ToBytes()
   150  		if err != nil {
   151  			return nil, err
   152  		}
   153  
   154  		switch ev.kind {
   155  		case replicaJoined:
   156  			buffer.Grow(joinedEventTopicLen + len(evsigbytes))
   157  		case replicaRemoved:
   158  			buffer.Grow(removedEventTopicLen + len(evsigbytes))
   159  		}
   160  		buffer.WriteByte(ev.kind)
   161  		expire := make([]byte, 8)
   162  		binary.LittleEndian.PutUint64(expire, ev.expire)
   163  		buffer.Write(expire)
   164  		buffer.Write(ev.id.Bytes())
   165  		if ev.kind == replicaJoined {
   166  			buffer.Write(ev.pubkey.ToBytes())
   167  		}
   168  		buffer.Write(evsigbytes)
   169  	}
   170  
   171  	return buffer.Bytes(), nil
   172  }
   173  
   174  func legacyQuorumCertDigest(view uint64, block []byte) (hash common.Hash) {
   175  	viewbytes := make([]byte, 8)
   176  	binary.LittleEndian.PutUint64(viewbytes, view)
   177  
   178  	return legacyCypherDigest(viewbytes, block)
   179  }
   180  
   181  func legacyCypherDigest(texts ...[]byte) (hash common.Hash) {
   182  	hasher := sha3.NewLegacyKeccak256()
   183  	for _, text := range texts {
   184  		hasher.Write(text)
   185  	}
   186  	hasher.Sum(hash[:0])
   187  	return
   188  }