Skip to content

Commit d332d73

Browse files
committed
[FAB-3272] Only allow 1 action per tx
With this change set, the peer enforces that transactions contain only a single action. This is required because the ledger will silently discard any action after the first one without marking the transaction as invalid. This may lead to conflicting scenarios. Change-Id: I358cb58cffe67dd26bea5d830f062377314ca914 Signed-off-by: Alessandro Sorniotti <[email protected]>
1 parent c5c60c3 commit d332d73

File tree

2 files changed

+105
-2
lines changed

2 files changed

+105
-2
lines changed

core/common/validation/fullflow_test.go

+102
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,75 @@ func getProposal() (*peer.Proposal, error) {
4242
return proposal, err
4343
}
4444

45+
func createSignedTxTwoActions(proposal *peer.Proposal, signer msp.SigningIdentity, resps ...*peer.ProposalResponse) (*common.Envelope, error) {
46+
if len(resps) == 0 {
47+
return nil, fmt.Errorf("At least one proposal response is necessary")
48+
}
49+
50+
// the original header
51+
hdr, err := utils.GetHeader(proposal.Header)
52+
if err != nil {
53+
return nil, fmt.Errorf("Could not unmarshal the proposal header")
54+
}
55+
56+
// the original payload
57+
pPayl, err := utils.GetChaincodeProposalPayload(proposal.Payload)
58+
if err != nil {
59+
return nil, fmt.Errorf("Could not unmarshal the proposal payload")
60+
}
61+
62+
// fill endorsements
63+
endorsements := make([]*peer.Endorsement, len(resps))
64+
for n, r := range resps {
65+
endorsements[n] = r.Endorsement
66+
}
67+
68+
// create ChaincodeEndorsedAction
69+
cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements}
70+
71+
// obtain the bytes of the proposal payload that will go to the transaction
72+
propPayloadBytes, err := utils.GetBytesProposalPayloadForTx(pPayl, nil)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
// serialize the chaincode action payload
78+
cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea}
79+
capBytes, err := utils.GetBytesChaincodeActionPayload(cap)
80+
if err != nil {
81+
return nil, err
82+
}
83+
84+
// create a transaction
85+
taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes}
86+
taas := make([]*peer.TransactionAction, 2)
87+
taas[0] = taa
88+
taas[1] = taa
89+
tx := &peer.Transaction{Actions: taas}
90+
91+
// serialize the tx
92+
txBytes, err := utils.GetBytesTransaction(tx)
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
// create the payload
98+
payl := &common.Payload{Header: hdr, Data: txBytes}
99+
paylBytes, err := utils.GetBytesPayload(payl)
100+
if err != nil {
101+
return nil, err
102+
}
103+
104+
// sign the payload
105+
sig, err := signer.Sign(paylBytes)
106+
if err != nil {
107+
return nil, err
108+
}
109+
110+
// here's the envelope
111+
return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
112+
}
113+
45114
func TestGoodPath(t *testing.T) {
46115
// get a toy proposal
47116
prop, err := getProposal()
@@ -116,6 +185,39 @@ func TestGoodPath(t *testing.T) {
116185
}
117186
}
118187

188+
func TestTXWithTwoActionsRejected(t *testing.T) {
189+
// get a toy proposal
190+
prop, err := getProposal()
191+
if err != nil {
192+
t.Fatalf("getProposal failed, err %s", err)
193+
return
194+
}
195+
196+
response := &peer.Response{Status: 200}
197+
simRes := []byte("simulation_result")
198+
199+
// endorse it to get a proposal response
200+
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, simRes, nil, nil, signer)
201+
if err != nil {
202+
t.Fatalf("CreateProposalResponse failed, err %s", err)
203+
return
204+
}
205+
206+
// assemble a transaction from that proposal and endorsement
207+
tx, err := createSignedTxTwoActions(prop, signer, presp)
208+
if err != nil {
209+
t.Fatalf("CreateSignedTx failed, err %s", err)
210+
return
211+
}
212+
213+
// validate the transaction
214+
_, txResult := ValidateTransaction(tx)
215+
if txResult == peer.TxValidationCode_VALID {
216+
t.Fatalf("ValidateTransaction should have failed")
217+
return
218+
}
219+
}
220+
119221
var r *rand.Rand
120222

121223
func corrupt(bytes []byte) {

core/common/validation/msgvalidation.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,9 @@ func validateEndorserTransaction(data []byte, hdr *common.Header) error {
286286

287287
// TODO: validate ChaincodeHeaderExtension
288288

289-
if len(tx.Actions) == 0 {
290-
return errors.New("At least one TransactionAction is required")
289+
// hlf version 1 only supports a single action per transaction
290+
if len(tx.Actions) != 1 {
291+
return fmt.Errorf("Only one action per transaction is supported (tx contains %d)", len(tx.Actions))
291292
}
292293

293294
putilsLogger.Debugf("validateEndorserTransaction info: there are %d actions", len(tx.Actions))

0 commit comments

Comments
 (0)