Skip to content

Commit 16fa08e

Browse files
committed
TX proposal/endorsement/validation flow (+MSP)
This change set contains a set of functions to generate a transaction (from proposal, endorsements and a signing identity) and validate it (given a set of root CAs). The validation code will be used by the committer. The tx assembling code should be helpful for the SDK team to understand how transactions should be assembled. Additionally, it has changed the type of messages exchanged everywhere to be of the proper type and with signatures (obtained from a fixed identity for now). Finally, it contains an initial implementation of VSCC with unit tests (which is however not yet called by the committer). Change-Id: I375ecc7e61516f3c4ab8fd874aa564e99cc720fb Signed-off-by: Alessandro Sorniotti <[email protected]>
1 parent 21a4c6a commit 16fa08e

23 files changed

+1662
-438
lines changed

core/chaincode/exectransaction_test.go

+68-9
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ import (
3535
"github.com/hyperledger/fabric/core/ledger/kvledger"
3636
"github.com/hyperledger/fabric/core/peer"
3737
"github.com/hyperledger/fabric/core/util"
38-
"github.com/hyperledger/fabric/protos/common"
3938
pb "github.com/hyperledger/fabric/protos/peer"
4039
putils "github.com/hyperledger/fabric/protos/utils"
4140

4241
"github.com/golang/protobuf/proto"
42+
"github.com/hyperledger/fabric/core/crypto/primitives"
43+
"github.com/hyperledger/fabric/msp"
4344
"github.com/spf13/viper"
4445
"golang.org/x/net/context"
4546
"google.golang.org/grpc"
@@ -127,7 +128,37 @@ func startTxSimulation(ctxt context.Context) (context.Context, ledger.TxSimulato
127128
return ctxt, txsim, nil
128129
}
129130

130-
func endTxSimulation(txsim ledger.TxSimulator, payload []byte, commit bool) error {
131+
func endTxSimulationCDS(txsim ledger.TxSimulator, payload []byte, commit bool, cds *pb.ChaincodeDeploymentSpec) error {
132+
// get serialized version of the signer
133+
ss, err := signer.Serialize()
134+
if err != nil {
135+
return err
136+
}
137+
// get a proposal - we need it to get a transaction
138+
prop, err := putils.CreateProposalFromCDS(cds, ss)
139+
if err != nil {
140+
return err
141+
}
142+
143+
return endTxSimulation(txsim, payload, commit, prop)
144+
}
145+
146+
func endTxSimulationCIS(txsim ledger.TxSimulator, payload []byte, commit bool, cis *pb.ChaincodeInvocationSpec) error {
147+
// get serialized version of the signer
148+
ss, err := signer.Serialize()
149+
if err != nil {
150+
return err
151+
}
152+
// get a proposal - we need it to get a transaction
153+
prop, err := putils.CreateProposalFromCIS(cis, ss)
154+
if err != nil {
155+
return err
156+
}
157+
158+
return endTxSimulation(txsim, payload, commit, prop)
159+
}
160+
161+
func endTxSimulation(txsim ledger.TxSimulator, payload []byte, commit bool, prop *pb.Proposal) error {
131162
txsim.Done()
132163
ledgername := string(DefaultChain)
133164
if lgr := kvledger.GetLedger(ledgername); lgr != nil {
@@ -139,16 +170,26 @@ func endTxSimulation(txsim ledger.TxSimulator, payload []byte, commit bool) erro
139170
if txSimulationResults, err = txsim.GetTxSimulationResults(); err != nil {
140171
return err
141172
}
142-
tx, err := putils.CreateTx(common.HeaderType_ENDORSER_TRANSACTION, util.ComputeCryptoHash([]byte("dummyProposal")), []byte("dummyCCEvents"), txSimulationResults, []*pb.Endorsement{&pb.Endorsement{}})
173+
174+
// assemble a (signed) proposal response message
175+
resp, err := putils.CreateProposalResponse(prop.Header, prop.Payload, txSimulationResults, nil, nil, signer)
143176
if err != nil {
144177
return err
145178
}
146-
txBytes, err := proto.Marshal(tx)
179+
180+
// get the envelope
181+
env, err := putils.CreateSignedTx(prop, signer, resp)
147182
if err != nil {
148183
return err
149184
}
185+
186+
envBytes, err := putils.GetBytesEnvelope(env)
187+
if err != nil {
188+
return err
189+
}
190+
150191
//create the block with 1 transaction
151-
block := &pb.Block2{Transactions: [][]byte{txBytes}}
192+
block := &pb.Block2{Transactions: [][]byte{envBytes}}
152193
if _, _, err = lgr.RemoveInvalidTransactionsAndPrepare(block); err != nil {
153194
return err
154195
}
@@ -252,10 +293,10 @@ func deploy2(ctx context.Context, chaincodeDeploymentSpec *pb.ChaincodeDeploymen
252293
//no error, lets try commit
253294
if err == nil {
254295
//capture returned error from commit
255-
err = endTxSimulation(txsim, []byte("deployed"), true)
296+
err = endTxSimulationCDS(txsim, []byte("deployed"), true, chaincodeDeploymentSpec)
256297
} else {
257298
//there was an error, just close simulation and return that
258-
endTxSimulation(txsim, []byte("deployed"), false)
299+
endTxSimulationCDS(txsim, []byte("deployed"), false, chaincodeDeploymentSpec)
259300
}
260301
}()
261302

@@ -299,10 +340,10 @@ func invoke(ctx context.Context, spec *pb.ChaincodeSpec) (ccevt *pb.ChaincodeEve
299340
//no error, lets try commit
300341
if err == nil {
301342
//capture returned error from commit
302-
err = endTxSimulation(txsim, []byte("invoke"), true)
343+
err = endTxSimulationCIS(txsim, []byte("invoke"), true, chaincodeInvocationSpec)
303344
} else {
304345
//there was an error, just close simulation and return that
305-
endTxSimulation(txsim, []byte("invoke"), false)
346+
endTxSimulationCIS(txsim, []byte("invoke"), false, chaincodeInvocationSpec)
306347
}
307348
}()
308349

@@ -1112,7 +1153,25 @@ func TestGetEvent(t *testing.T) {
11121153
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
11131154
}
11141155

1156+
var signer msp.SigningIdentity
1157+
11151158
func TestMain(m *testing.M) {
1159+
var err error
1160+
primitives.SetSecurityLevel("SHA2", 256)
1161+
1162+
// setup the MSP manager so that we can sign/verify
1163+
mspMgrConfigFile := "../../msp/peer-config.json"
1164+
msp.GetManager().Setup(mspMgrConfigFile)
1165+
mspId := "DEFAULT"
1166+
id := "PEER"
1167+
signingIdentity := &msp.IdentityIdentifier{Mspid: msp.ProviderIdentifier{Value: mspId}, Value: id}
1168+
signer, err = msp.GetManager().GetSigningIdentity(signingIdentity)
1169+
if err != nil {
1170+
os.Exit(-1)
1171+
fmt.Printf("Could not initialize msp/signer")
1172+
return
1173+
}
1174+
11161175
SetupTestConfig()
11171176
os.Exit(m.Run())
11181177
}

core/committer/committer_test.go

+20-3
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,29 @@ import (
2424
"github.com/hyperledger/fabric/core/ledger/testutil"
2525
"github.com/stretchr/testify/assert"
2626

27+
"fmt"
28+
29+
"github.com/hyperledger/fabric/core/crypto/primitives"
30+
"github.com/hyperledger/fabric/msp"
2731
pb "github.com/hyperledger/fabric/protos/peer"
2832
)
2933

3034
func TestKVLedgerBlockStorage(t *testing.T) {
35+
primitives.SetSecurityLevel("SHA2", 256)
36+
37+
// setup the MSP manager so that we can sign/verify
38+
mspMgrConfigFile := "../../msp/peer-config.json"
39+
msp.GetManager().Setup(mspMgrConfigFile)
40+
mspId := "DEFAULT"
41+
id := "PEER"
42+
signingIdentity := &msp.IdentityIdentifier{Mspid: msp.ProviderIdentifier{Value: mspId}, Value: id}
43+
signer, err := msp.GetManager().GetSigningIdentity(signingIdentity)
44+
if err != nil {
45+
os.Exit(-1)
46+
fmt.Printf("Could not initialize msp/signer")
47+
return
48+
}
49+
3150
conf := kvledger.NewConf("/tmp/tests/ledger/", 0)
3251
defer os.RemoveAll("/tmp/tests/ledger/")
3352

@@ -36,8 +55,6 @@ func TestKVLedgerBlockStorage(t *testing.T) {
3655

3756
committer := NewLedgerCommitter(ledger)
3857

39-
var err error
40-
4158
height, err := committer.LedgerHeight()
4259
assert.Equal(t, uint64(0), height)
4360
assert.NoError(t, err)
@@ -53,7 +70,7 @@ func TestKVLedgerBlockStorage(t *testing.T) {
5370
simulator.Done()
5471

5572
simRes, _ := simulator.GetTxSimulationResults()
56-
block1 := testutil.ConstructBlockForSimulationResults(t, [][]byte{simRes})
73+
block1 := testutil.ConstructBlockForSimulationResults(t, [][]byte{simRes}, signer)
5774

5875
err = committer.CommitBlock(block1)
5976
assert.NoError(t, err)

core/committer/noopssinglechain/client.go

+18-5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"golang.org/x/net/context"
3333
"google.golang.org/grpc"
3434

35+
"github.com/hyperledger/fabric/core/peer"
3536
pb "github.com/hyperledger/fabric/protos/peer"
3637
)
3738

@@ -126,13 +127,25 @@ func (d *DeliverService) readUntilClose() {
126127
block := &pb.Block2{}
127128
for _, d := range t.Block.Data.Data {
128129
if d != nil {
129-
if tx, err := putils.GetEndorserTxFromBlock(d); err != nil {
130+
if env, err := putils.GetEnvelopeFromBlock(d); err != nil {
130131
fmt.Printf("Error getting tx from block(%s)\n", err)
131-
} else if tx != nil {
132-
if t, err := proto.Marshal(tx); err == nil {
133-
block.Transactions = append(block.Transactions, t)
132+
} else if env != nil {
133+
// validate the transaction: here we check that the transaction
134+
// is properly formed, properly signed and that the security
135+
// chain binding proposal to endorsements to tx holds. We do
136+
// NOT check the validity of endorsements, though. That's a
137+
// job for VSCC below
138+
_, err := peer.ValidateTransaction(env)
139+
if err != nil {
140+
// TODO: this code needs to receive a bit more attention and discussion: it's not clear what it means if a transaction which causes a failure in validation is just dropped on the floor
141+
logger.Errorf("Invalid transaction, error %s", err)
134142
} else {
135-
fmt.Printf("Cannot marshal transactoins %s\n", err)
143+
// TODO: call VSCC now
144+
if t, err := proto.Marshal(env); err == nil {
145+
block.Transactions = append(block.Transactions, t)
146+
} else {
147+
fmt.Printf("Cannot marshal transactoins %s\n", err)
148+
}
136149
}
137150
} else {
138151
fmt.Printf("Nil tx from block\n")

core/endorser/endorser.go

+4-110
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ import (
2626
"github.com/hyperledger/fabric/core/chaincode"
2727
"github.com/hyperledger/fabric/core/ledger"
2828
"github.com/hyperledger/fabric/core/ledger/kvledger"
29+
"github.com/hyperledger/fabric/core/peer"
2930
"github.com/hyperledger/fabric/msp"
30-
"github.com/hyperledger/fabric/protos/common"
3131
pb "github.com/hyperledger/fabric/protos/peer"
3232
putils "github.com/hyperledger/fabric/protos/utils"
3333
)
@@ -234,116 +234,10 @@ func (e *Endorser) endorseProposal(ctx context.Context, proposal *pb.Proposal, s
234234
return prBytes, nil
235235
}
236236

237-
// FIXME: this method might be of general interest, should we package it somewhere else?
238-
// validateChaincodeProposalMessage checks the validity of a CHAINCODE Proposal message
239-
func (e *Endorser) validateChaincodeProposalMessage(prop *pb.Proposal, hdr *common.Header) (*pb.ChaincodeHeaderExtension, error) {
240-
endorserLogger.Infof("validateChaincodeProposalMessage starts for proposal %p, header %p", prop, hdr)
241-
242-
// 4) based on the header type (assuming it's CHAINCODE), look at the extensions
243-
chaincodeHdrExt, err := putils.GetChaincodeHeaderExtension(hdr)
244-
if err != nil {
245-
return nil, fmt.Errorf("Invalid header extension for type CHAINCODE")
246-
}
247-
248-
endorserLogger.Infof("validateChaincodeProposalMessage info: header extension references chaincode %s", chaincodeHdrExt.ChaincodeID)
249-
250-
// - ensure that the chaincodeID is correct (?)
251-
// TODO: should we even do this? If so, using which interface?
252-
253-
// - ensure that the visibility field has some value we understand
254-
// TODO: we need to define visibility fields first
255-
256-
// TODO: should we check the payload as well?
257-
258-
return chaincodeHdrExt, nil
259-
}
260-
261-
// FIXME: this method might be of general interest, should we package it somewhere else?
262-
// validateProposalMessage checks the validity of a generic Proposal message
263-
// this function returns Header and ChaincodeHeaderExtension messages since they
264-
// have been unmarshalled and validated
265-
func (e *Endorser) validateProposalMessage(signedProp *pb.SignedProposal) (*pb.Proposal, *common.Header, *pb.ChaincodeHeaderExtension, error) {
266-
endorserLogger.Infof("validateProposalMessage starts for signed proposal %p", signedProp)
267-
268-
// extract the Proposal message from signedProp
269-
prop, err := putils.GetProposal(signedProp.ProposalBytes)
270-
if err != nil {
271-
return nil, nil, nil, err
272-
}
273-
274-
// 1) look at the ProposalHeader
275-
hdr, err := putils.GetHeader(prop)
276-
if err != nil {
277-
return nil, nil, nil, err
278-
}
279-
280-
// TODO: validate the type
281-
282-
endorserLogger.Infof("validateProposalMessage info: proposal type %d", hdr.ChainHeader.Type)
283-
284-
// - ensure that the version is what we expect
285-
// TODO: Which is the right version?
286-
287-
// - ensure that the chainID is valid
288-
// TODO: which set of APIs is supposed to give us this info?
289-
290-
// ensure that there is a nonce and a creator
291-
if hdr.SignatureHeader.Nonce == nil || len(hdr.SignatureHeader.Nonce) == 0 {
292-
return nil, nil, nil, fmt.Errorf("Invalid nonce specified in the header")
293-
}
294-
if hdr.SignatureHeader.Creator == nil || len(hdr.SignatureHeader.Creator) == 0 {
295-
return nil, nil, nil, fmt.Errorf("Invalid creator specified in the header")
296-
}
297-
298-
// get the identity of the creator
299-
creator, err := msp.GetManager().DeserializeIdentity(hdr.SignatureHeader.Creator)
300-
if err != nil {
301-
return nil, nil, nil, fmt.Errorf("Failed to deserialize creator identity, err %s", err)
302-
}
303-
304-
// ensure that creator is a valid certificate
305-
valid, err := creator.Validate()
306-
if err != nil {
307-
return nil, nil, nil, fmt.Errorf("Could not determine whether the identity is valid, err %s", err)
308-
} else if !valid {
309-
return nil, nil, nil, fmt.Errorf("The creator certificate is not valid, aborting")
310-
}
311-
312-
// get the identifier and log info on the creator
313-
identifier := creator.Identifier()
314-
endorserLogger.Infof("validateProposalMessage info: creator identity is %s", identifier)
315-
316-
// - ensure that creator is trusted (signed by a trusted CA)
317-
// TODO: We need MSP APIs for this
318-
319-
// - ensure that creator can transact with us (some ACLs?)
320-
// TODO: which set of APIs is supposed to give us this info?
321-
322-
// 2) validate the signature of creator on header and payload
323-
verified, err := creator.Verify(signedProp.ProposalBytes, signedProp.Signature)
324-
if err != nil {
325-
return nil, nil, nil, fmt.Errorf("Could not determine whether the signature is valid, err %s", err)
326-
} else if !verified {
327-
return nil, nil, nil, fmt.Errorf("The creator's signature over the proposal is not valid, aborting")
328-
}
329-
330-
// 3) perform a check against replay attacks
331-
// TODO
332-
333-
// validation of the proposal message knowing it's of type CHAINCODE
334-
chaincodeHdrExt, err := e.validateChaincodeProposalMessage(prop, hdr)
335-
if err != nil {
336-
return nil, nil, nil, err
337-
}
338-
339-
return prop, hdr, chaincodeHdrExt, err
340-
}
341-
342237
// ProcessProposal process the Proposal
343238
func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
344239
// at first, we check whether the message is valid
345-
// TODO: Do the checks performed by this function belong here or in the ESCC? From a security standpoint they should be performed as early as possible so here seems to be a good place
346-
prop, _, hdrExt, err := e.validateProposalMessage(signedProp)
240+
prop, _, hdrExt, err := peer.ValidateProposalMessage(signedProp)
347241
if err != nil {
348242
return &pb.ProposalResponse{Response: &pb.Response2{Status: 500, Message: err.Error()}}, err
349243
}
@@ -394,8 +288,8 @@ func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedPro
394288
// Only exposed for testing purposes - commit the tx simulation so that
395289
// a deploy transaction is persisted and that chaincode can be invoked.
396290
// This makes the endorser test self-sufficient
397-
func (e *Endorser) commitTxSimulation(pResp *pb.ProposalResponse) error {
398-
tx, err := putils.CreateTxFromProposalResponse(pResp)
291+
func (e *Endorser) commitTxSimulation(proposal *pb.Proposal, signer msp.SigningIdentity, pResp *pb.ProposalResponse) error {
292+
tx, err := putils.CreateSignedTx(proposal, signer, pResp)
399293
if err != nil {
400294
return err
401295
}

0 commit comments

Comments
 (0)