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 }