Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit 72066f7

Browse files
committed
[FABG-735] Allow client to provide nonce/creator
Options were added allow clients to provide the nonce and/or creator in the transaction header. Change-Id: I0773fe740ced408e18962a0b88ae5d846078fc25 Signed-off-by: Bob Stasyszyn <Bob.Stasyszyn@securekey.com>
1 parent 8d8c7b8 commit 72066f7

File tree

9 files changed

+126
-20
lines changed

9 files changed

+126
-20
lines changed

pkg/client/channel/invoke/txnhandler.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@ import (
2121
pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer"
2222
)
2323

24+
// TxnHeaderOptsProvider provides transaction header options which allow
25+
// the provider to specify a custom creator and/or nonce.
26+
type TxnHeaderOptsProvider func() []fab.TxnHeaderOpt
27+
2428
//EndorsementHandler for handling endorse transactions
2529
type EndorsementHandler struct {
26-
next Handler
30+
next Handler
31+
headerOptsProvider TxnHeaderOptsProvider
2732
}
2833

2934
//Handle for endorsing transactions
@@ -35,7 +40,17 @@ func (e *EndorsementHandler) Handle(requestContext *RequestContext, clientContex
3540
}
3641

3742
// Endorse Tx
38-
transactionProposalResponses, proposal, err := createAndSendTransactionProposal(clientContext.Transactor, &requestContext.Request, peer.PeersToTxnProcessors(requestContext.Opts.Targets))
43+
var TxnHeaderOpts []fab.TxnHeaderOpt
44+
if e.headerOptsProvider != nil {
45+
TxnHeaderOpts = e.headerOptsProvider()
46+
}
47+
48+
transactionProposalResponses, proposal, err := createAndSendTransactionProposal(
49+
clientContext.Transactor,
50+
&requestContext.Request,
51+
peer.PeersToTxnProcessors(requestContext.Opts.Targets),
52+
TxnHeaderOpts...,
53+
)
3954

4055
requestContext.Response.Proposal = proposal
4156
requestContext.Response.TransactionID = proposal.TxnID // TODO: still needed?
@@ -214,6 +229,11 @@ func NewEndorsementHandler(next ...Handler) *EndorsementHandler {
214229
return &EndorsementHandler{next: getNext(next)}
215230
}
216231

232+
//NewEndorsementHandlerWithOpts returns a handler that endorses a transaction proposal
233+
func NewEndorsementHandlerWithOpts(next Handler, provider TxnHeaderOptsProvider) *EndorsementHandler {
234+
return &EndorsementHandler{next: next, headerOptsProvider: provider}
235+
}
236+
217237
//NewEndorsementValidationHandler returns a handler that validates an endorsement
218238
func NewEndorsementValidationHandler(next ...Handler) *EndorsementValidationHandler {
219239
return &EndorsementValidationHandler{next: getNext(next)}
@@ -252,15 +272,15 @@ func createAndSendTransaction(sender fab.Sender, proposal *fab.TransactionPropos
252272
return transactionResponse, nil
253273
}
254274

255-
func createAndSendTransactionProposal(transactor fab.ProposalSender, chrequest *Request, targets []fab.ProposalProcessor) ([]*fab.TransactionProposalResponse, *fab.TransactionProposal, error) {
275+
func createAndSendTransactionProposal(transactor fab.ProposalSender, chrequest *Request, targets []fab.ProposalProcessor, opts ...fab.TxnHeaderOpt) ([]*fab.TransactionProposalResponse, *fab.TransactionProposal, error) {
256276
request := fab.ChaincodeInvokeRequest{
257277
ChaincodeID: chrequest.ChaincodeID,
258278
Fcn: chrequest.Fcn,
259279
Args: chrequest.Args,
260280
TransientMap: chrequest.TransientMap,
261281
}
262282

263-
txh, err := transactor.CreateTransactionHeader()
283+
txh, err := transactor.CreateTransactionHeader(opts...)
264284
if err != nil {
265285
return nil, nil, errors.WithMessage(err, "creating transaction header failed")
266286
}

pkg/client/channel/invoke/txnhandler_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,19 @@ func TestEndorsementHandler(t *testing.T) {
174174
handler.Handle(requestContext, clientContext)
175175
assert.Nil(t, requestContext.Error)
176176

177+
optsProviderCalled := false
178+
optsProvider := func() []fab.TxnHeaderOpt {
179+
optsProviderCalled = true
180+
var opts []fab.TxnHeaderOpt
181+
opts = append(opts, fab.WithCreator([]byte("somecreator")))
182+
opts = append(opts, fab.WithNonce([]byte("somenonce")))
183+
return opts
184+
}
185+
186+
handler = NewEndorsementHandlerWithOpts(nil, optsProvider)
187+
handler.Handle(requestContext, clientContext)
188+
assert.Nil(t, requestContext.Error)
189+
assert.Truef(t, optsProviderCalled, "expecting opts provider to be called")
177190
}
178191

179192
// Target filter

pkg/client/common/mocks/mocktransactor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type MockTransactor struct {
2424
}
2525

2626
// CreateTransactionHeader creates a Transaction Header based on the current context.
27-
func (t *MockTransactor) CreateTransactionHeader() (fab.TransactionHeader, error) {
27+
func (t *MockTransactor) CreateTransactionHeader(opts ...fab.TxnHeaderOpt) (fab.TransactionHeader, error) {
2828
txh, err := txn.NewHeader(t.Ctx, t.ChannelID)
2929
if err != nil {
3030
return nil, errors.WithMessage(err, "new transaction ID failed")

pkg/common/providers/fab/proposer.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,32 @@ type ProposalProcessor interface {
1717
ProcessTransactionProposal(reqContext.Context, ProcessProposalRequest) (*TransactionProposalResponse, error)
1818
}
1919

20+
// TxnHeaderOptions contains options for creating a Transaction Header
21+
type TxnHeaderOptions struct {
22+
Nonce []byte
23+
Creator []byte
24+
}
25+
26+
// TxnHeaderOpt is a Transaction Header option
27+
type TxnHeaderOpt func(*TxnHeaderOptions)
28+
29+
// WithNonce specifies the nonce to use when creating the Transaction Header
30+
func WithNonce(nonce []byte) TxnHeaderOpt {
31+
return func(options *TxnHeaderOptions) {
32+
options.Nonce = nonce
33+
}
34+
}
35+
36+
// WithCreator specifies the creator to use when creating the Transaction Header
37+
func WithCreator(creator []byte) TxnHeaderOpt {
38+
return func(options *TxnHeaderOptions) {
39+
options.Creator = creator
40+
}
41+
}
42+
2043
// ProposalSender provides the ability for a transaction proposal to be created and sent.
2144
type ProposalSender interface {
22-
CreateTransactionHeader() (TransactionHeader, error)
45+
CreateTransactionHeader(opts ...TxnHeaderOpt) (TransactionHeader, error)
2346
SendTransactionProposal(*TransactionProposal, []ProposalProcessor) ([]*TransactionProposalResponse, error)
2447
}
2548

pkg/fab/channel/transactor.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,14 @@ func orderersByTarget(ctx context.Client) (map[string]fab.OrdererConfig, error)
147147
}
148148

149149
// CreateTransactionHeader creates a Transaction Header based on the current context.
150-
func (t *Transactor) CreateTransactionHeader() (fab.TransactionHeader, error) {
150+
func (t *Transactor) CreateTransactionHeader(opts ...fab.TxnHeaderOpt) (fab.TransactionHeader, error) {
151151

152152
ctx, ok := contextImpl.RequestClientContext(t.reqCtx)
153153
if !ok {
154154
return nil, errors.New("failed get client context from reqContext for txn Header")
155155
}
156156

157-
txh, err := txn.NewHeader(ctx, t.ChannelID)
157+
txh, err := txn.NewHeader(ctx, t.ChannelID, opts...)
158158
if err != nil {
159159
return nil, errors.WithMessage(err, "new transaction ID failed")
160160
}

pkg/fab/channel/transactor_test.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,19 @@ import (
2626

2727
func TestCreateTxnID(t *testing.T) {
2828
transactor := createTransactor(t)
29-
createTxnID(t, transactor)
29+
30+
txh := createTxnID(t, transactor)
31+
assert.NotEmpty(t, txh.Nonce())
32+
assert.NotEmpty(t, txh.Creator())
33+
assert.NotEmpty(t, txh.TransactionID())
34+
35+
creator := []byte("creator")
36+
nonce := []byte("12345")
37+
38+
txh = createTxnID(t, transactor, fab.WithCreator(creator), fab.WithNonce(nonce))
39+
assert.Equal(t, nonce, txh.Nonce())
40+
assert.Equal(t, creator, txh.Creator())
41+
assert.NotEmpty(t, txh.TransactionID())
3042
}
3143

3244
func TestTransactionProposal(t *testing.T) {
@@ -78,8 +90,8 @@ func createTransactor(t *testing.T) *Transactor {
7890
return transactor
7991
}
8092

81-
func createTxnID(t *testing.T, transactor *Transactor) fab.TransactionHeader {
82-
txh, err := transactor.CreateTransactionHeader()
93+
func createTxnID(t *testing.T, transactor *Transactor, opts ...fab.TxnHeaderOpt) fab.TransactionHeader {
94+
txh, err := transactor.CreateTransactionHeader(opts...)
8395
assert.Nil(t, err, "creation of transaction ID failed")
8496

8597
return txh

pkg/fab/mocks/mocktransactor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type MockTransactor struct {
2222
}
2323

2424
// CreateTransactionHeader creates a Transaction Header based on the current context.
25-
func (t *MockTransactor) CreateTransactionHeader() (fab.TransactionHeader, error) {
25+
func (t *MockTransactor) CreateTransactionHeader(opts ...fab.TxnHeaderOpt) (fab.TransactionHeader, error) {
2626
return &MockTransactionHeader{}, nil
2727
}
2828

pkg/fab/txn/env.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,29 @@ func (th *TransactionHeader) ChannelID() string {
5353

5454
// NewHeader computes a TransactionID from the current user context and holds
5555
// metadata to create transaction proposals.
56-
func NewHeader(ctx contextApi.Client, channelID string) (*TransactionHeader, error) {
57-
// generate a random nonce
58-
nonce, err := crypto.GetRandomNonce()
59-
if err != nil {
60-
return nil, errors.WithMessage(err, "nonce creation failed")
56+
func NewHeader(ctx contextApi.Client, channelID string, opts ...fab.TxnHeaderOpt) (*TransactionHeader, error) {
57+
var options fab.TxnHeaderOptions
58+
for _, opt := range opts {
59+
opt(&options)
6160
}
6261

63-
creator, err := ctx.Serialize()
64-
if err != nil {
65-
return nil, errors.WithMessage(err, "identity from context failed")
62+
nonce := options.Nonce
63+
if nonce == nil {
64+
// generate a random nonce
65+
var err error
66+
nonce, err = crypto.GetRandomNonce()
67+
if err != nil {
68+
return nil, errors.WithMessage(err, "nonce creation failed")
69+
}
70+
}
71+
72+
creator := options.Creator
73+
if creator == nil {
74+
var err error
75+
creator, err = ctx.Serialize()
76+
if err != nil {
77+
return nil, errors.WithMessage(err, "identity from context failed")
78+
}
6679
}
6780

6881
ho := cryptosuite.GetSHA256Opts() // TODO: make configurable

pkg/fab/txn/proposal_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"strings"
1212
"testing"
1313

14+
"github.com/stretchr/testify/require"
15+
1416
"github.com/golang/mock/gomock"
1517
"github.com/golang/protobuf/proto"
1618
"github.com/stretchr/testify/assert"
@@ -30,6 +32,29 @@ const (
3032
testChannel = "testchannel"
3133
)
3234

35+
func TestNewHeader(t *testing.T) {
36+
user := mspmocks.NewMockSigningIdentity("test", "1234")
37+
ctx := mocks.NewMockContext(user)
38+
39+
creator, err := ctx.Serialize()
40+
require.NoError(t, err)
41+
42+
txh, err := NewHeader(ctx, testChannel)
43+
require.NoError(t, err)
44+
require.NotEmptyf(t, txh.nonce, "Expecting nonce")
45+
require.Equal(t, creator, txh.creator)
46+
require.NotEmpty(t, txh.id)
47+
48+
creator = []byte("someothercreator")
49+
nonce := []byte("123456")
50+
51+
txh, err = NewHeader(ctx, testChannel, fab.WithCreator(creator), fab.WithNonce(nonce))
52+
require.NoError(t, err)
53+
require.Equal(t, nonce, txh.nonce)
54+
require.Equal(t, creator, txh.creator)
55+
require.NotEmpty(t, txh.id)
56+
}
57+
3358
func TestNewTransactionProposal(t *testing.T) {
3459
user := mspmocks.NewMockSigningIdentity("test", "1234")
3560
ctx := mocks.NewMockContext(user)

0 commit comments

Comments
 (0)