Skip to content

Commit 0fe630a

Browse files
committed
feat(ttx): add WithApprovalMetadata option to forward metadata to approvers
Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
1 parent eec69e9 commit 0fe630a

19 files changed

Lines changed: 206 additions & 54 deletions

File tree

token/core/common/driver/mock/network_driver.go

Lines changed: 10 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

token/services/auditor/mock/network_driver.go

Lines changed: 10 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

token/services/network/driver/network.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ type Network interface {
6262
NewEnvelope() Envelope
6363

6464
// RequestApproval requests an endorsement for a token request from the network's approval service.
65-
RequestApproval(context view.Context, tms *token2.ManagementService, requestRaw []byte, signer view.Identity, txID TxID) (Envelope, error)
65+
// metadata carries optional application-level key-value pairs forwarded to the approver.
66+
RequestApproval(context view.Context, tms *token2.ManagementService, requestRaw []byte, signer view.Identity, txID TxID, metadata TransientMap) (Envelope, error)
6667

6768
// ComputeTxID calculates the ledger-specific transaction ID from an abstract TxID.
6869
ComputeTxID(id *TxID) string

token/services/network/fabric/endorsement/chaincode.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ func NewChaincodeEndorsementService(tmsID token2.TMSID) *ChaincodeEndorsementSer
2727
return &ChaincodeEndorsementService{TMSID: tmsID}
2828
}
2929

30-
func (e *ChaincodeEndorsementService) Endorse(context view.Context, requestRaw []byte, signer view.Identity, txID driver.TxID) (driver.Envelope, error) {
31-
env, err := chaincode.NewEndorseView(
30+
func (e *ChaincodeEndorsementService) Endorse(context view.Context, requestRaw []byte, signer view.Identity, txID driver.TxID, metadata driver.TransientMap) (driver.Envelope, error) {
31+
ev := chaincode.NewEndorseView(
3232
e.TMSID.Namespace,
3333
InvokeFunction,
3434
).WithNetwork(
@@ -44,7 +44,11 @@ func (e *ChaincodeEndorsementService) Endorse(context view.Context, requestRaw [
4444
Nonce: txID.Nonce,
4545
Creator: txID.Creator,
4646
},
47-
).Endorse(context)
47+
)
48+
for k, v := range metadata {
49+
ev = ev.WithTransientEntry(k, v)
50+
}
51+
env, err := ev.Endorse(context)
4852
if err != nil {
4953
return nil, err
5054
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0
77
package fsc
88

99
import (
10+
"encoding/json"
1011
"time"
1112

1213
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
@@ -16,6 +17,10 @@ import (
1617
"github.com/hyperledger-labs/fabric-token-sdk/token/services/network/driver"
1718
)
1819

20+
// TransientApprovalMetadataKey is the transient map key used to carry optional application-level
21+
// approval metadata from the initiator to the responder.
22+
const TransientApprovalMetadataKey = "approval_metadata"
23+
1924
// RequestApprovalView is the initiator of the request approval protocol
2025
type RequestApprovalView struct {
2126
TMSID token.TMSID
@@ -26,6 +31,8 @@ type RequestApprovalView struct {
2631
Nonce []byte
2732
// Endorsers are the identities of the FSC node that play the role of endorser
2833
Endorsers []view.Identity
34+
// Metadata carries optional application-level key-value pairs forwarded to the approver via transient data.
35+
Metadata driver.TransientMap
2936

3037
// EndorserService is the endorser service
3138
EndorserService EndorserService
@@ -41,6 +48,7 @@ func NewRequestApprovalView(
4148
nonce []byte,
4249
endorsers []view.Identity,
4350
endorserService EndorserService,
51+
metadata driver.TransientMap,
4452
) *RequestApprovalView {
4553
return &RequestApprovalView{
4654
TMSID: TMSID,
@@ -49,6 +57,7 @@ func NewRequestApprovalView(
4957
Nonce: nonce,
5058
Endorsers: endorsers,
5159
EndorserService: endorserService,
60+
Metadata: metadata,
5261
}
5362
}
5463

@@ -76,6 +85,15 @@ func (r *RequestApprovalView) Call(ctx view.Context) (any, error) {
7685
if err := tx.SetTransient(TransientTokenRequestKey, r.RequestRaw); err != nil {
7786
return nil, errors.WithMessagef(err, "failed to set token request transient")
7887
}
88+
if len(r.Metadata) > 0 {
89+
metadataRaw, err := json.Marshal(r.Metadata)
90+
if err != nil {
91+
return nil, errors.WithMessagef(err, "failed to marshal approval metadata")
92+
}
93+
if err := tx.SetTransient(TransientApprovalMetadataKey, metadataRaw); err != nil {
94+
return nil, errors.WithMessagef(err, "failed to set approval metadata transient")
95+
}
96+
}
7997

8098
logger.DebugfContext(ctx.Context(), "request endorsement on tx [%s] to [%v]...", tx.ID(), r.Endorsers)
8199
err = r.EndorserService.CollectEndorsements(ctx, tx, 2*time.Minute, r.Endorsers...)

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ func mockNewRequestApprovalView(t *testing.T, overrideTMSID *token.TMSID) *MockN
8080
nil,
8181
nil,
8282
es,
83+
nil,
8384
)
8485

8586
return &MockNewRequestApprovalView{
@@ -128,6 +129,22 @@ func TestRequestApprovalView(t *testing.T) {
128129
},
129130
expectError: false,
130131
},
132+
{
133+
name: "Success with approval metadata",
134+
setup: func() *MockNewRequestApprovalView {
135+
m := mockNewRequestApprovalView(t, nil)
136+
m.view.Metadata = driver.TransientMap{
137+
"info": []byte("extra"),
138+
}
139+
140+
return m
141+
},
142+
verify: func(m *MockNewRequestApprovalView, res any) {
143+
assert.Len(t, m.transientMap, 3)
144+
assert.Contains(t, m.transientMap, fsc.TransientApprovalMetadataKey)
145+
},
146+
expectError: false,
147+
},
131148
{
132149
name: "failed NewTransaction",
133150
setup: func() *MockNewRequestApprovalView {

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package fsc
88

99
import (
1010
"context"
11+
"encoding/json"
1112

1213
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
1314
"github.com/hyperledger-labs/fabric-smart-client/platform/fabric"
@@ -36,6 +37,7 @@ type Request struct {
3637
RequestRaw []byte
3738
Actions []any
3839
Meta map[string][]byte
40+
ApprovalMetadata map[string][]byte
3941
Tms *token2.ManagementService
4042
PublicParamsHash tdriver.PPHash
4143
}
@@ -114,10 +116,10 @@ func (r *RequestApprovalResponderView) receive(ctx view.Context) (*Request, erro
114116

115117
// validate transient
116118

117-
// check the number of transient keys
119+
// check the number of transient keys: 2 required (tmsID + token_request) plus 1 optional (approval_metadata)
118120
var tmsID token2.TMSID
119-
if len(tx.Transaction.Transient()) != 2 {
120-
return nil, errors.Wrapf(ErrInvalidTransient, "invalid number of transient field, expected 2, got %d", len(tx.Transaction.Transient()))
121+
if n := len(tx.Transaction.Transient()); n < 2 || n > 3 {
122+
return nil, errors.Wrapf(ErrInvalidTransient, "invalid number of transient fields, expected 2 or 3, got %d", n)
121123
}
122124

123125
// TMS ID
@@ -142,6 +144,14 @@ func (r *RequestApprovalResponderView) receive(ctx view.Context) (*Request, erro
142144
return nil, errors.Wrapf(ErrInvalidTransient, "empty token request")
143145
}
144146

147+
// approval metadata (optional)
148+
var approvalMetadata map[string][]byte
149+
if raw := tx.GetTransient(TransientApprovalMetadataKey); len(raw) > 0 {
150+
if err := json.Unmarshal(raw, &approvalMetadata); err != nil {
151+
return nil, errors.Wrapf(ErrInvalidTransient, "failed to unmarshal approval metadata")
152+
}
153+
}
154+
145155
// request anchor
146156
requestAnchor := tx.ID()
147157

@@ -183,6 +193,7 @@ func (r *RequestApprovalResponderView) receive(ctx view.Context) (*Request, erro
183193
TMSID: tmsID,
184194
RequestRaw: requestRaw,
185195
Anchor: requestAnchor,
196+
ApprovalMetadata: approvalMetadata,
186197
Tms: tms,
187198
PublicParamsHash: tms.PublicParametersManager().PublicParamsHash(),
188199
}, nil

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

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ func TestRequestApprovalResponderView(t *testing.T) {
191191
},
192192
},
193193
{
194-
name: "invalid number of transient field",
194+
name: "invalid number of transient fields",
195195
setup: func() *MockNewRequestApprovalResponderView {
196196
m := mockNewRequestApprovalResponderView(t, nil)
197197
m.fabricTx.TransientReturns(map[string][]byte{
@@ -202,7 +202,7 @@ func TestRequestApprovalResponderView(t *testing.T) {
202202
},
203203
expectError: true,
204204
expectErrorType: fsc.ErrReceivedProposal,
205-
expectErrContain: "invalid number of transient field, expected 2, got 1",
205+
expectErrContain: "invalid number of transient fields, expected 2 or 3, got 1",
206206
verify: func(m *MockNewRequestApprovalResponderView, res any) {
207207
assert.Equal(t, 0, m.rws.DoneCallCount())
208208
},
@@ -618,6 +618,44 @@ func TestRequestApprovalResponderView(t *testing.T) {
618618
assert.Equal(t, 1, m.rws.DoneCallCount())
619619
},
620620
},
621+
{
622+
name: "success with approval metadata",
623+
setup: func() *MockNewRequestApprovalResponderView {
624+
m := mockNewRequestApprovalResponderView(t, nil)
625+
metadataRaw, err := json.Marshal(map[string][]byte{"info": []byte("extra")})
626+
require.NoError(t, err)
627+
m.fabricTx.TransientReturns(map[string][]byte{
628+
fsc.TransientTMSIDKey: m.tmsIDRaw,
629+
fsc.TransientTokenRequestKey: []byte("a_token_request"),
630+
fsc.TransientApprovalMetadataKey: metadataRaw,
631+
})
632+
633+
return m
634+
},
635+
expectError: false,
636+
verify: func(m *MockNewRequestApprovalResponderView, res any) {
637+
assert.Equal(t, 1, m.rws.DoneCallCount())
638+
},
639+
},
640+
{
641+
name: "invalid approval metadata encoding",
642+
setup: func() *MockNewRequestApprovalResponderView {
643+
m := mockNewRequestApprovalResponderView(t, nil)
644+
m.fabricTx.TransientReturns(map[string][]byte{
645+
fsc.TransientTMSIDKey: m.tmsIDRaw,
646+
fsc.TransientTokenRequestKey: []byte("a_token_request"),
647+
fsc.TransientApprovalMetadataKey: []byte("not-valid-json"),
648+
})
649+
650+
return m
651+
},
652+
expectError: true,
653+
expectErrorType: fsc.ErrReceivedProposal,
654+
expectErrContain: "failed to unmarshal approval metadata",
655+
verify: func(m *MockNewRequestApprovalResponderView, res any) {
656+
assert.Equal(t, 0, m.rws.DoneCallCount())
657+
},
658+
},
621659
}
622660

623661
for _, tc := range testCases {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func NewEndorsementService(
106106
}, nil
107107
}
108108

109-
func (e *EndorsementService) Endorse(context view.Context, requestRaw []byte, signer view.Identity, txID driver.TxID) (driver.Envelope, error) {
109+
func (e *EndorsementService) Endorse(context view.Context, requestRaw []byte, signer view.Identity, txID driver.TxID, metadata driver.TransientMap) (driver.Envelope, error) {
110110
var endorsers []view.Identity
111111
switch e.PolicyType {
112112
case OneOutNPolicy:
@@ -125,6 +125,7 @@ func (e *EndorsementService) Endorse(context view.Context, requestRaw []byte, si
125125
nil,
126126
endorsers,
127127
e.EndorserService,
128+
metadata,
128129
))
129130
if err != nil {
130131
return nil, errors.WithMessagef(err, "failed to request approval")

0 commit comments

Comments
 (0)