Skip to content

Commit

Permalink
[FAB-3272] Only allow 1 action per tx
Browse files Browse the repository at this point in the history
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 <ale.linux@sopit.net>
  • Loading branch information
ale-linux committed Apr 26, 2017
1 parent c5c60c3 commit d332d73
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 2 deletions.
102 changes: 102 additions & 0 deletions core/common/validation/fullflow_test.go
Expand Up @@ -42,6 +42,75 @@ func getProposal() (*peer.Proposal, error) {
return proposal, err
}

func createSignedTxTwoActions(proposal *peer.Proposal, signer msp.SigningIdentity, resps ...*peer.ProposalResponse) (*common.Envelope, error) {
if len(resps) == 0 {
return nil, fmt.Errorf("At least one proposal response is necessary")
}

// the original header
hdr, err := utils.GetHeader(proposal.Header)
if err != nil {
return nil, fmt.Errorf("Could not unmarshal the proposal header")
}

// the original payload
pPayl, err := utils.GetChaincodeProposalPayload(proposal.Payload)
if err != nil {
return nil, fmt.Errorf("Could not unmarshal the proposal payload")
}

// fill endorsements
endorsements := make([]*peer.Endorsement, len(resps))
for n, r := range resps {
endorsements[n] = r.Endorsement
}

// create ChaincodeEndorsedAction
cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements}

// obtain the bytes of the proposal payload that will go to the transaction
propPayloadBytes, err := utils.GetBytesProposalPayloadForTx(pPayl, nil)
if err != nil {
return nil, err
}

// serialize the chaincode action payload
cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea}
capBytes, err := utils.GetBytesChaincodeActionPayload(cap)
if err != nil {
return nil, err
}

// create a transaction
taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes}
taas := make([]*peer.TransactionAction, 2)
taas[0] = taa
taas[1] = taa
tx := &peer.Transaction{Actions: taas}

// serialize the tx
txBytes, err := utils.GetBytesTransaction(tx)
if err != nil {
return nil, err
}

// create the payload
payl := &common.Payload{Header: hdr, Data: txBytes}
paylBytes, err := utils.GetBytesPayload(payl)
if err != nil {
return nil, err
}

// sign the payload
sig, err := signer.Sign(paylBytes)
if err != nil {
return nil, err
}

// here's the envelope
return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
}

func TestGoodPath(t *testing.T) {
// get a toy proposal
prop, err := getProposal()
Expand Down Expand Up @@ -116,6 +185,39 @@ func TestGoodPath(t *testing.T) {
}
}

func TestTXWithTwoActionsRejected(t *testing.T) {
// get a toy proposal
prop, err := getProposal()
if err != nil {
t.Fatalf("getProposal failed, err %s", err)
return
}

response := &peer.Response{Status: 200}
simRes := []byte("simulation_result")

// endorse it to get a proposal response
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, simRes, nil, nil, signer)
if err != nil {
t.Fatalf("CreateProposalResponse failed, err %s", err)
return
}

// assemble a transaction from that proposal and endorsement
tx, err := createSignedTxTwoActions(prop, signer, presp)
if err != nil {
t.Fatalf("CreateSignedTx failed, err %s", err)
return
}

// validate the transaction
_, txResult := ValidateTransaction(tx)
if txResult == peer.TxValidationCode_VALID {
t.Fatalf("ValidateTransaction should have failed")
return
}
}

var r *rand.Rand

func corrupt(bytes []byte) {
Expand Down
5 changes: 3 additions & 2 deletions core/common/validation/msgvalidation.go
Expand Up @@ -286,8 +286,9 @@ func validateEndorserTransaction(data []byte, hdr *common.Header) error {

// TODO: validate ChaincodeHeaderExtension

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

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

0 comments on commit d332d73

Please sign in to comment.