Skip to content

Commit 778d198

Browse files
Hayim.Shaul@ibm.comadecaro
authored andcommitted
add proposal validation
Signed-off-by: Hayim.Shaul@ibm.com <hayimsha@fhe3.haifa.ibm.com>
1 parent 171adb0 commit 778d198

2 files changed

Lines changed: 83 additions & 1 deletion

File tree

token/services/network/fabric/endorsement/fsc/responder.go

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ func (r *RequestApprovalResponderView) Call(context view.Context) (any, error) {
7272
}
7373
defer request.Rws.Done()
7474

75+
// validate proposal
76+
err = r.validateProposal(context, request)
77+
if err != nil {
78+
return nil, errors.Join(ErrValidateProposal, err)
79+
}
80+
7581
// validate
7682
err = r.validate(context, request, func(id token.ID) ([]byte, error) {
7783
key, err := r.keyTranslator.CreateOutputKey(id.TxId, id.Index)
@@ -153,7 +159,7 @@ func (r *RequestApprovalResponderView) receive(ctx view.Context) (*Request, erro
153159
return nil, errors.Wrapf(ErrInvalidProposal, "non empty namespaces")
154160
}
155161

156-
// TODO: check that tx contains a valid endorser proposal
162+
// Validate chaincode name and version
157163
if name, version := tx.Chaincode(); name != tmsID.Namespace || version != ChaincodeVersion {
158164
return nil, errors.Wrapf(ErrInvalidProposal, "invalid chaincode")
159165
}
@@ -180,6 +186,80 @@ func (r *RequestApprovalResponderView) receive(ctx view.Context) (*Request, erro
180186
}, nil
181187
}
182188

189+
func (r *RequestApprovalResponderView) validateProposal(ctx view.Context, request *Request) error {
190+
logger.DebugfContext(ctx.Context(), "Validate proposal for TX [%s]", request.Anchor)
191+
192+
// Get the signed proposal from the underlying Fabric transaction
193+
signedProposal := request.Tx.Transaction.SignedProposal()
194+
if signedProposal == nil {
195+
return errors.Errorf("signed proposal is nil for tx [%s]", request.Anchor)
196+
}
197+
198+
// Get the proposal
199+
proposal := request.Tx.Transaction.Proposal()
200+
if proposal == nil {
201+
return errors.Errorf("proposal is nil for tx [%s]", request.Anchor)
202+
}
203+
204+
// Verify the proposal signature
205+
// The signature verification ensures that the proposal was signed by the creator
206+
creator := request.Tx.Transaction.Creator()
207+
if len(creator) == 0 {
208+
return errors.Errorf("creator is empty for tx [%s]", request.Anchor)
209+
}
210+
211+
// Get the proposal bytes for signature verification from the signed proposal
212+
proposalBytes := signedProposal.ProposalBytes()
213+
if len(proposalBytes) == 0 {
214+
return errors.Errorf("proposal bytes are empty for tx [%s]", request.Anchor)
215+
}
216+
217+
// Verify the signature on the proposal
218+
signature := signedProposal.Signature()
219+
if len(signature) == 0 {
220+
return errors.Errorf("proposal signature is empty for tx [%s]", request.Anchor)
221+
}
222+
223+
// Verify the signature over the proposal using the TMS signature service
224+
// Try to get a verifier for the creator identity - try owner, issuer, and auditor verifiers
225+
sigService := request.Tms.SigService()
226+
227+
// Try owner verifier first
228+
verifier, err := sigService.OwnerVerifier(ctx.Context(), creator)
229+
if err != nil {
230+
// Try issuer verifier
231+
verifier, err = sigService.IssuerVerifier(ctx.Context(), creator)
232+
if err != nil {
233+
// Try auditor verifier
234+
verifier, err = sigService.AuditorVerifier(ctx.Context(), creator)
235+
if err != nil {
236+
return errors.Wrapf(err, "failed to get verifier for creator for tx [%s]", request.Anchor)
237+
}
238+
}
239+
}
240+
241+
// Verify the signature
242+
err = verifier.Verify(proposalBytes, signature)
243+
if err != nil {
244+
return errors.Wrapf(err, "failed to verify proposal signature for tx [%s]", request.Anchor)
245+
}
246+
247+
// Validate that the token actions in the request are consistent with the proposal
248+
// The token request should match what's in the transient data
249+
// This ensures the relationship between the action, read-write set, and token actions
250+
if len(request.RequestRaw) == 0 {
251+
return errors.Errorf("token request is empty for tx [%s]", request.Anchor)
252+
}
253+
254+
// The actions will be validated in the validate() method which checks:
255+
// - Token actions are valid
256+
// - Read-write set is consistent with the actions
257+
// - Signatures on token actions are valid
258+
259+
logger.DebugfContext(ctx.Context(), "Proposal signature verified successfully for TX [%s]", request.Anchor)
260+
return nil
261+
}
262+
183263
func (r *RequestApprovalResponderView) translate(ctx context.Context, request *Request) error {
184264
// prepare the rws as usual
185265
txID := request.Anchor

token/services/network/fabric/endorsement/fsc/responder_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ func mockNewRequestApprovalResponderView(t *testing.T, overrideTMSID *token.TMSI
4141
es := &mock.EndorserService{}
4242
fabricTx := &mock.FabricTransaction{}
4343
fabricTx.IDReturns("a_tx_id")
44+
fabricTx.CreatorReturns([]byte("creator_identity"))
45+
4446
tmsID := token.TMSID{
4547
Network: "a_network",
4648
Channel: "a_channel",

0 commit comments

Comments
 (0)