Skip to content

Commit a2ea789

Browse files
committed
init commit
Signed-off-by: David Liu <[email protected]>
1 parent 62f5192 commit a2ea789

21 files changed

+4509
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ fabric-lib-go is a home for go code that is common across fabric repositories.
44

55
It contains:
66
- health check logic that is used by fabric and fabric-ca
7+
- protobuf utilities for fabric core or API developer

protoutil/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# protoutil
2+
3+
This originates as a copy of github.com/hyperledger/fabric/protoutil
4+
5+
## Prepare
6+
to use `counterfeiter`
7+
- install by running `go install github.com/maxbrunsfeld/counterfeiter/v6@latest` outside of go module

protoutil/blockutils.go

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package protoutil
8+
9+
import (
10+
"bytes"
11+
"crypto/sha256"
12+
"encoding/asn1"
13+
"encoding/base64"
14+
"fmt"
15+
"github.com/hyperledger/fabric-lib-go/protoutil/common/util"
16+
"math/big"
17+
18+
"github.com/hyperledger/fabric-protos-go-apiv2/common"
19+
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
20+
"github.com/pkg/errors"
21+
"google.golang.org/protobuf/proto"
22+
)
23+
24+
// NewBlock constructs a block with no data and no metadata.
25+
func NewBlock(seqNum uint64, previousHash []byte) *common.Block {
26+
block := &common.Block{}
27+
block.Header = &common.BlockHeader{}
28+
block.Header.Number = seqNum
29+
block.Header.PreviousHash = previousHash
30+
block.Header.DataHash = []byte{}
31+
block.Data = &common.BlockData{}
32+
33+
var metadataContents [][]byte
34+
for i := 0; i < len(common.BlockMetadataIndex_name); i++ {
35+
metadataContents = append(metadataContents, []byte{})
36+
}
37+
block.Metadata = &common.BlockMetadata{Metadata: metadataContents}
38+
39+
return block
40+
}
41+
42+
type asn1Header struct {
43+
Number *big.Int
44+
PreviousHash []byte
45+
DataHash []byte
46+
}
47+
48+
func BlockHeaderBytes(b *common.BlockHeader) []byte {
49+
asn1Header := asn1Header{
50+
PreviousHash: b.PreviousHash,
51+
DataHash: b.DataHash,
52+
Number: new(big.Int).SetUint64(b.Number),
53+
}
54+
result, err := asn1.Marshal(asn1Header)
55+
if err != nil {
56+
// Errors should only arise for types which cannot be encoded, since the
57+
// BlockHeader type is known a-priori to contain only encodable types, an
58+
// error here is fatal and should not be propagated
59+
panic(err)
60+
}
61+
return result
62+
}
63+
64+
func BlockHeaderHash(b *common.BlockHeader) []byte {
65+
sum := sha256.Sum256(BlockHeaderBytes(b))
66+
return sum[:]
67+
}
68+
69+
func BlockDataHash(b *common.BlockData) ([]byte, error) {
70+
if err := VerifyTransactionsAreWellFormed(b); err != nil {
71+
return nil, err
72+
}
73+
return ComputeBlockDataHash(b), nil
74+
}
75+
76+
func ComputeBlockDataHash(b *common.BlockData) []byte {
77+
sum := sha256.Sum256(bytes.Join(b.Data, nil))
78+
return sum[:]
79+
}
80+
81+
// GetChannelIDFromBlockBytes returns channel ID given byte array which represents
82+
// the block
83+
func GetChannelIDFromBlockBytes(bytes []byte) (string, error) {
84+
block, err := UnmarshalBlock(bytes)
85+
if err != nil {
86+
return "", err
87+
}
88+
89+
return GetChannelIDFromBlock(block)
90+
}
91+
92+
// GetChannelIDFromBlock returns channel ID in the block
93+
func GetChannelIDFromBlock(block *common.Block) (string, error) {
94+
if block == nil || block.Data == nil || block.Data.Data == nil || len(block.Data.Data) == 0 {
95+
return "", errors.New("failed to retrieve channel id - block is empty")
96+
}
97+
var err error
98+
envelope, err := GetEnvelopeFromBlock(block.Data.Data[0])
99+
if err != nil {
100+
return "", err
101+
}
102+
payload, err := UnmarshalPayload(envelope.Payload)
103+
if err != nil {
104+
return "", err
105+
}
106+
107+
if payload.Header == nil {
108+
return "", errors.New("failed to retrieve channel id - payload header is empty")
109+
}
110+
chdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
111+
if err != nil {
112+
return "", err
113+
}
114+
115+
return chdr.ChannelId, nil
116+
}
117+
118+
// GetMetadataFromBlock retrieves metadata at the specified index.
119+
func GetMetadataFromBlock(block *common.Block, index common.BlockMetadataIndex) (*common.Metadata, error) {
120+
if block.Metadata == nil {
121+
return nil, errors.New("no metadata in block")
122+
}
123+
124+
if len(block.Metadata.Metadata) <= int(index) {
125+
return nil, errors.Errorf("no metadata at index [%s]", index)
126+
}
127+
128+
md := &common.Metadata{}
129+
err := proto.Unmarshal(block.Metadata.Metadata[index], md)
130+
if err != nil {
131+
return nil, errors.Wrapf(err, "error unmarshalling metadata at index [%s]", index)
132+
}
133+
return md, nil
134+
}
135+
136+
// GetMetadataFromBlockOrPanic retrieves metadata at the specified index, or
137+
// panics on error
138+
func GetMetadataFromBlockOrPanic(block *common.Block, index common.BlockMetadataIndex) *common.Metadata {
139+
md, err := GetMetadataFromBlock(block, index)
140+
if err != nil {
141+
panic(err)
142+
}
143+
return md
144+
}
145+
146+
// GetConsenterMetadataFromBlock attempts to retrieve consenter metadata from the value
147+
// stored in block metadata at index SIGNATURES (first field). If no consenter metadata
148+
// is found there, it falls back to index ORDERER (third field).
149+
func GetConsenterMetadataFromBlock(block *common.Block) (*common.Metadata, error) {
150+
m, err := GetMetadataFromBlock(block, common.BlockMetadataIndex_SIGNATURES)
151+
if err != nil {
152+
return nil, errors.WithMessage(err, "failed to retrieve metadata")
153+
}
154+
155+
obm := &common.OrdererBlockMetadata{}
156+
err = proto.Unmarshal(m.Value, obm)
157+
if err != nil {
158+
return nil, errors.Wrap(err, "failed to unmarshal orderer block metadata")
159+
}
160+
161+
res := &common.Metadata{}
162+
err = proto.Unmarshal(obm.ConsenterMetadata, res)
163+
if err != nil {
164+
return nil, errors.Wrap(err, "failed to unmarshal consenter metadata")
165+
}
166+
167+
return res, nil
168+
}
169+
170+
// GetLastConfigIndexFromBlock retrieves the index of the last config block as
171+
// encoded in the block metadata
172+
func GetLastConfigIndexFromBlock(block *common.Block) (uint64, error) {
173+
m, err := GetMetadataFromBlock(block, common.BlockMetadataIndex_SIGNATURES)
174+
if err != nil {
175+
return 0, errors.WithMessage(err, "failed to retrieve metadata")
176+
}
177+
178+
obm := &common.OrdererBlockMetadata{}
179+
err = proto.Unmarshal(m.Value, obm)
180+
if err != nil {
181+
return 0, errors.Wrap(err, "failed to unmarshal orderer block metadata")
182+
}
183+
return obm.LastConfig.Index, nil
184+
}
185+
186+
// GetLastConfigIndexFromBlockOrPanic retrieves the index of the last config
187+
// block as encoded in the block metadata, or panics on error
188+
func GetLastConfigIndexFromBlockOrPanic(block *common.Block) uint64 {
189+
index, err := GetLastConfigIndexFromBlock(block)
190+
if err != nil {
191+
panic(err)
192+
}
193+
return index
194+
}
195+
196+
// CopyBlockMetadata copies metadata from one block into another
197+
func CopyBlockMetadata(src *common.Block, dst *common.Block) {
198+
dst.Metadata = src.Metadata
199+
// Once copied initialize with rest of the
200+
// required metadata positions.
201+
InitBlockMetadata(dst)
202+
}
203+
204+
// InitBlockMetadata initializes metadata structure
205+
func InitBlockMetadata(block *common.Block) {
206+
if block.Metadata == nil {
207+
block.Metadata = &common.BlockMetadata{Metadata: [][]byte{{}, {}, {}, {}, {}}}
208+
} else if len(block.Metadata.Metadata) < int(common.BlockMetadataIndex_COMMIT_HASH+1) {
209+
for i := len(block.Metadata.Metadata); i <= int(common.BlockMetadataIndex_COMMIT_HASH); i++ {
210+
block.Metadata.Metadata = append(block.Metadata.Metadata, []byte{})
211+
}
212+
}
213+
}
214+
215+
type VerifierBuilder func(block *common.Block) BlockVerifierFunc
216+
217+
type BlockVerifierFunc func(header *common.BlockHeader, metadata *common.BlockMetadata) error
218+
219+
//go:generate counterfeiter -o mocks/policy.go --fake-name Policy . policy
220+
type policy interface { // copied from common.policies to avoid circular import.
221+
// EvaluateSignedData takes a set of SignedData and evaluates whether
222+
// 1) the signatures are valid over the related message
223+
// 2) the signing identities satisfy the policy
224+
EvaluateSignedData(signatureSet []*SignedData) error
225+
}
226+
227+
func BlockSignatureVerifier(bftEnabled bool, consenters []*common.Consenter, policy policy) BlockVerifierFunc {
228+
return func(header *common.BlockHeader, metadata *common.BlockMetadata) error {
229+
if len(metadata.GetMetadata()) < int(common.BlockMetadataIndex_SIGNATURES)+1 {
230+
return errors.Errorf("no signatures in block metadata")
231+
}
232+
233+
md := &common.Metadata{}
234+
if err := proto.Unmarshal(metadata.Metadata[common.BlockMetadataIndex_SIGNATURES], md); err != nil {
235+
return errors.Wrapf(err, "error unmarshalling signatures from metadata: %v", err)
236+
}
237+
238+
var signatureSet []*SignedData
239+
for _, metadataSignature := range md.Signatures {
240+
var signerIdentity []byte
241+
var signedPayload []byte
242+
// if the SignatureHeader is empty and the IdentifierHeader is present, then the consenter expects us to fetch its identity by its numeric identifier
243+
if bftEnabled && len(metadataSignature.GetSignatureHeader()) == 0 && len(metadataSignature.GetIdentifierHeader()) > 0 {
244+
identifierHeader, err := UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)
245+
if err != nil {
246+
return fmt.Errorf("failed unmarshalling identifier header for block %d: %v", header.GetNumber(), err)
247+
}
248+
identifier := identifierHeader.GetIdentifier()
249+
signerIdentity = searchConsenterIdentityByID(consenters, identifier)
250+
if len(signerIdentity) == 0 {
251+
// The identifier is not within the consenter set
252+
continue
253+
}
254+
signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.IdentifierHeader, BlockHeaderBytes(header))
255+
} else {
256+
signatureHeader, err := UnmarshalSignatureHeader(metadataSignature.GetSignatureHeader())
257+
if err != nil {
258+
return fmt.Errorf("failed unmarshalling signature header for block %d: %v", header.GetNumber(), err)
259+
}
260+
261+
signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.SignatureHeader, BlockHeaderBytes(header))
262+
263+
signerIdentity = signatureHeader.Creator
264+
}
265+
266+
signatureSet = append(
267+
signatureSet,
268+
&SignedData{
269+
Identity: signerIdentity,
270+
Data: signedPayload,
271+
Signature: metadataSignature.Signature,
272+
},
273+
)
274+
}
275+
276+
return policy.EvaluateSignedData(signatureSet)
277+
}
278+
}
279+
280+
func searchConsenterIdentityByID(consenters []*common.Consenter, identifier uint32) []byte {
281+
for _, consenter := range consenters {
282+
if consenter.Id == identifier {
283+
return MarshalOrPanic(&msp.SerializedIdentity{
284+
Mspid: consenter.MspId,
285+
IdBytes: consenter.Identity,
286+
})
287+
}
288+
}
289+
return nil
290+
}
291+
292+
func VerifyTransactionsAreWellFormed(bd *common.BlockData) error {
293+
if bd == nil || bd.Data == nil || len(bd.Data) == 0 {
294+
return fmt.Errorf("empty block")
295+
}
296+
297+
// If we have a single transaction, and the block is a config block, then no need to check
298+
// well formed-ness, because there cannot be another transaction in the original block.
299+
if HasConfigTx(bd) {
300+
return nil
301+
}
302+
303+
for i, rawTx := range bd.Data {
304+
env := &common.Envelope{}
305+
if err := proto.Unmarshal(rawTx, env); err != nil {
306+
return fmt.Errorf("transaction %d is invalid: %v", i, err)
307+
}
308+
309+
if len(env.Payload) == 0 {
310+
return fmt.Errorf("transaction %d has no payload", i)
311+
}
312+
313+
if len(env.Signature) == 0 {
314+
return fmt.Errorf("transaction %d has no signature", i)
315+
}
316+
317+
expected, err := proto.Marshal(env)
318+
if err != nil {
319+
return fmt.Errorf("failed re-marshaling envelope: %v", err)
320+
}
321+
322+
if len(expected) < len(rawTx) {
323+
return fmt.Errorf("transaction %d has %d trailing bytes", i, len(rawTx)-len(expected))
324+
}
325+
if !bytes.Equal(expected, rawTx) {
326+
return fmt.Errorf("transaction %d (%s) does not match its raw form (%s)", i,
327+
base64.StdEncoding.EncodeToString(expected), base64.StdEncoding.EncodeToString(rawTx))
328+
}
329+
}
330+
331+
return nil
332+
}

0 commit comments

Comments
 (0)