-
Notifications
You must be signed in to change notification settings - Fork 79
Expand file tree
/
Copy pathendorse.go
More file actions
139 lines (114 loc) · 3.68 KB
/
Copy pathendorse.go
File metadata and controls
139 lines (114 loc) · 3.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package transaction
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"io"
"google.golang.org/protobuf/proto"
"github.com/hyperledger/fabric-x-common/api/applicationpb"
"github.com/hyperledger/fabric-x-common/api/msppb"
"github.com/hyperledger/fabric-x-common/msp"
)
// Endorse signs a transaction with the provided identity for all namespaces.
// Returns a cloned transaction with added endorsements. Currently uses threshold ECDSA;
// MSP-based endorsement support is planned.
func Endorse(signer msp.SigningIdentity, txID string, tx *applicationpb.Tx) (*applicationpb.Tx, error) {
if tx == nil {
return nil, errors.New("nil transaction")
}
tx = proto.CloneOf(tx)
// check that tx does not yet carry any endorsements
if tx.Endorsements == nil {
tx.Endorsements = make([]*applicationpb.Endorsements, len(tx.GetNamespaces()))
}
// get signer identity to be attached to the endorsement
signerIdentity, err := identity(signer)
if err != nil {
return nil, err
}
// create signature for each namespace in transaction
for nsIdx := range tx.GetNamespaces() {
// Note that a default msp signer hash the msg before signing.
// For that reason we use the TxNamespace message as ASN1 encoded msg
msg, err := tx.Namespaces[nsIdx].ASN1Marshal(txID)
if err != nil {
return nil, fmt.Errorf("failed asn1 marshal tx: %w", err)
}
sig, err := signer.Sign(msg)
if err != nil {
return nil, fmt.Errorf("failed signing tx: %w", err)
}
// store signature as endorsementWithIdentity
eid := &applicationpb.EndorsementWithIdentity{
Endorsement: sig,
Identity: signerIdentity,
}
// check if there is already an endorsement for this namespace, so we can append the new endorsement
// if not we create an empty endorser set
if tx.Endorsements[nsIdx] == nil {
tx.Endorsements[nsIdx] = &applicationpb.Endorsements{
EndorsementsWithIdentity: []*applicationpb.EndorsementWithIdentity{},
}
}
for _, existing := range tx.Endorsements[nsIdx].EndorsementsWithIdentity {
if existing.GetIdentity().GetMspId() == signerIdentity.GetMspId() {
return nil, fmt.Errorf(
"duplicate endorsement: signer %q already endorsed namespace %d",
signerIdentity.GetMspId(),
nsIdx,
)
}
}
tx.Endorsements[nsIdx].EndorsementsWithIdentity = append(tx.Endorsements[nsIdx].EndorsementsWithIdentity, eid)
}
return tx, nil
}
func identity(signer msp.SigningIdentity) (*msppb.Identity, error) {
// signer identity with certificate attached
s, err := signer.Serialize()
if err != nil {
return nil, err
}
// signer identity with hash of certificate attached
// s, err := signer.SerializeWithIDOfCert()
// if err != nil {
// return nil, err
// }
var sid msppb.Identity
err = proto.Unmarshal(s, &sid)
if err != nil {
return nil, err
}
return &sid, nil
}
// GenerateTxID generates a unique transaction ID using SHA-256 hash of a random nonce.
func GenerateTxID() string {
nonce := readNonce(nil)
hasher := sha256.New()
hasher.Write(nonce)
return hex.EncodeToString(hasher.Sum(nil))
}
// readNonce reads a byte array of the given size from the source.
// It panics if the read fails, or cannot read the requested size.
// "crypto/rand" and "math/rand" never fail and always returns the correct length.
func readNonce(source io.Reader) []byte {
if source == nil {
source = rand.Reader
}
size := 24
value := make([]byte, size)
n, err := source.Read(value)
if err != nil {
panic(fmt.Errorf("error while creating nonce: %w", err))
}
if n != size {
panic(fmt.Errorf("cannot read enough bytes for nonce actual: %d wanted: %d", n, size))
}
return value
}