Commit 5f9b3ea0 authored by Angelo De Caro's avatar Angelo De Caro
Browse files

C2C invocation for confidential contracts.

This PR addresses chaincode to chaincode invocation for confidential contracts
(https://jira.hyperledger.org/browse/FAB-67

). In order to achieve the goal
the chaincode handler has been modified to contruct proper ephemeral
transactions and security contexts.

Let us consider the following scenario to describe the modification
apported by this PR. Let us say that we have two chaincodes: A and B
where A invokes B at some point of its computation.
When a user invoke a chaincode A, using transaction tx,
the certificate that the user has put in tx is passed to B when A
invokes it. In this way, for example, chaincode B can perfom
attribute-based access control. In addition,
each chaincode can access it is own encrypted state and modify
it in a proper way without affecting other chaincodes' state.

This PR has been tested by adding a unit test in exectransaction_test.go.
The unit tests, verify that C2C invocation can be perfomed when
security is enabled.

What will come next:
1. Chaincode to chaincode query.
2. Additional fields transfered to the invoked chaincode to
support access control based on signature.

Change-Id: I649d1953ac76e8af32d917a089a454fc0bba9fc1
Signed-off-by: default avatarAngelo De Caro <adc@zurich.ibm.com>
parent 02a123ce
......@@ -827,6 +827,18 @@ func TestChaincodeInvokeChaincode(t *testing.T) {
go grpcServer.Serve(lis)
err = chaincodeInvokeChaincode(t, "")
if err != nil {
t.Fail()
t.Logf("Failed chaincode invoke chaincode : %s", err)
closeListenerAndSleep(lis)
return
}
closeListenerAndSleep(lis)
}
func chaincodeInvokeChaincode(t *testing.T, user string) (err error) {
var ctxt = context.Background()
// Deploy first chaincode
......@@ -836,7 +848,7 @@ func TestChaincodeInvokeChaincode(t *testing.T) {
f := "init"
args := util.ToChaincodeArgs(f, "a", "100", "b", "200")
spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Args: args}}
spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Args: args}, SecureContext: user}
_, err = deploy(ctxt, spec1)
chaincodeID1 := spec1.ChaincodeID.Name
......@@ -844,7 +856,6 @@ func TestChaincodeInvokeChaincode(t *testing.T) {
t.Fail()
t.Logf("Error initializing chaincode %s(%s)", chaincodeID1, err)
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1})
closeListenerAndSleep(lis)
return
}
......@@ -859,7 +870,7 @@ func TestChaincodeInvokeChaincode(t *testing.T) {
f = "init"
args = util.ToChaincodeArgs(f, "e", "0")
spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}}
spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}, SecureContext: user}
_, err = deploy(ctxt, spec2)
chaincodeID2 := spec2.ChaincodeID.Name
......@@ -868,7 +879,6 @@ func TestChaincodeInvokeChaincode(t *testing.T) {
t.Logf("Error initializing chaincode %s(%s)", chaincodeID2, err)
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1})
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2})
closeListenerAndSleep(lis)
return
}
......@@ -878,7 +888,7 @@ func TestChaincodeInvokeChaincode(t *testing.T) {
f = "invoke"
args = util.ToChaincodeArgs(f, "e", "1")
spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}}
spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}, SecureContext: user}
// Invoke chaincode
var uuid string
_, uuid, _, err = invoke(ctxt, spec2, pb.Transaction_CHAINCODE_INVOKE)
......@@ -888,7 +898,6 @@ func TestChaincodeInvokeChaincode(t *testing.T) {
t.Logf("Error invoking <%s>: %s", chaincodeID2, err)
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1})
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2})
closeListenerAndSleep(lis)
return
}
......@@ -899,13 +908,67 @@ func TestChaincodeInvokeChaincode(t *testing.T) {
t.Logf("Incorrect final state after transaction for <%s>: %s", chaincodeID1, err)
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1})
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2})
closeListenerAndSleep(lis)
return
}
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1})
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2})
closeListenerAndSleep(lis)
return
}
func TestChaincodeInvokeChaincodeWithSec(t *testing.T) {
testDBWrapper.CleanDB(t)
viper.Set("security.enabled", "true")
//Initialize crypto
if err := crypto.Init(); err != nil {
panic(fmt.Errorf("Failed initializing the crypto layer [%s]", err))
}
//set paths for memberservice to pick up
viper.Set("peer.fileSystemPath", filepath.Join(os.TempDir(), "hyperledger", "production"))
viper.Set("server.rootpath", filepath.Join(os.TempDir(), "ca"))
var err error
var memSrvcLis net.Listener
if memSrvcLis, err = initMemSrvc(); err != nil {
t.Fail()
t.Logf("Error registering user %s", err)
return
}
time.Sleep(2 * time.Second)
var peerLis net.Listener
if peerLis, err = initPeer(); err != nil {
finitMemSrvc(memSrvcLis)
t.Fail()
t.Logf("Error registering user %s", err)
return
}
if err = crypto.RegisterClient("jim", nil, "jim", "6avZQLwcUe9b"); err != nil {
finitMemSrvc(memSrvcLis)
finitPeer(peerLis)
t.Fail()
t.Logf("Error registering user %s", err)
return
}
//login as jim and test chaincode-chaincode interaction with security
if err = chaincodeInvokeChaincode(t, "jim"); err != nil {
finitMemSrvc(memSrvcLis)
finitPeer(peerLis)
t.Fail()
t.Logf("Error executing test %s", err)
return
}
//cleanup
finitMemSrvc(memSrvcLis)
finitPeer(peerLis)
}
// Test the execution of a chaincode that invokes another chaincode with wrong parameters. Should receive error from
......
......@@ -161,9 +161,9 @@ func (handler *Handler) deleteRangeQueryIterator(txContext *transactionContext,
delete(txContext.rangeQueryIteratorMap, txid)
}
//THIS CAN BE REMOVED ONCE WE SUPPORT CONFIDENTIALITY WITH CC-CALLING-CC
//we dissallow chaincode-chaincode interactions till confidentiality implications are understood
func (handler *Handler) canCallChaincode(txid string) *pb.ChaincodeMessage {
//THIS CAN BE REMOVED ONCE WE FULL SUPPORT (Invoke and Query) CONFIDENTIALITY WITH CC-CALLING-CC
//Only invocation are allowed, not queries
func (handler *Handler) canCallChaincode(txid string, isQuery bool) *pb.ChaincodeMessage {
secHelper := handler.chaincodeSupport.getSecHelper()
if secHelper == nil {
return nil
......@@ -176,7 +176,9 @@ func (handler *Handler) canCallChaincode(txid string) *pb.ChaincodeMessage {
} else if txctx.transactionSecContext == nil {
errMsg = fmt.Sprintf("[%s]Error transaction context is nil while checking for confidentiality. Sending %s", shorttxid(txid), pb.ChaincodeMessage_ERROR)
} else if txctx.transactionSecContext.ConfidentialityLevel != pb.ConfidentialityLevel_PUBLIC {
errMsg = fmt.Sprintf("[%s]Error chaincode-chaincode interactions not supported for with privacy enabled. Sending %s", shorttxid(txid), pb.ChaincodeMessage_ERROR)
if isQuery {
errMsg = fmt.Sprintf("[%s]Error chaincode-chaincode interactions not supported for with privacy enabled. Sending %s", shorttxid(txid), pb.ChaincodeMessage_ERROR)
}
}
if errMsg != "" {
......@@ -209,10 +211,12 @@ func (handler *Handler) encryptOrDecrypt(encrypt bool, txid string, payload []by
var err error
if txctx.transactionSecContext.Type == pb.Transaction_CHAINCODE_DEPLOY {
if enc, err = secHelper.GetStateEncryptor(handler.deployTXSecContext, handler.deployTXSecContext); err != nil {
chaincodeLogger.Errorf("error getting crypto encryptor for deploy tx :%s", err)
return nil, fmt.Errorf("error getting crypto encryptor for deploy tx :%s", err)
}
} else if txctx.transactionSecContext.Type == pb.Transaction_CHAINCODE_INVOKE || txctx.transactionSecContext.Type == pb.Transaction_CHAINCODE_QUERY {
if enc, err = secHelper.GetStateEncryptor(handler.deployTXSecContext, txctx.transactionSecContext); err != nil {
chaincodeLogger.Errorf("error getting crypto encryptor %s", err)
return nil, fmt.Errorf("error getting crypto encryptor %s", err)
}
} else {
......@@ -1046,7 +1050,9 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) {
err = ledgerObj.DeleteState(chaincodeID, key)
} else if msg.Type.String() == pb.ChaincodeMessage_INVOKE_CHAINCODE.String() {
//check and prohibit C-call-C for CONFIDENTIAL txs
if triggerNextStateMsg = handler.canCallChaincode(msg.Txid); triggerNextStateMsg != nil {
chaincodeLogger.Debugf("[%s] C-call-C", shorttxid(msg.Txid))
if triggerNextStateMsg = handler.canCallChaincode(msg.Txid, false); triggerNextStateMsg != nil {
return
}
chaincodeSpec := &pb.ChaincodeSpec{}
......@@ -1060,12 +1066,21 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) {
// Get the chaincodeID to invoke
newChaincodeID := chaincodeSpec.ChaincodeID.Name
chaincodeLogger.Debugf("[%s] C-call-C %s", shorttxid(msg.Txid), newChaincodeID)
// Create the transaction object
chaincodeInvocationSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: chaincodeSpec}
transaction, _ := pb.NewChaincodeExecute(chaincodeInvocationSpec, msg.Txid, pb.Transaction_CHAINCODE_INVOKE)
// Launch the new chaincode if not already running
tsc := handler.getTxContext(msg.Txid).transactionSecContext
transaction.Nonce = tsc.Nonce
transaction.ConfidentialityLevel = tsc.ConfidentialityLevel
transaction.ConfidentialityProtocolVersion = tsc.ConfidentialityProtocolVersion
transaction.Metadata = tsc.Metadata
transaction.Cert = tsc.Cert
// cd the new chaincode if not already running
_, chaincodeInput, launchErr := handler.chaincodeSupport.Launch(context.Background(), transaction)
if launchErr != nil {
payload := []byte(launchErr.Error())
......@@ -1217,7 +1232,7 @@ func (handler *Handler) initializeSecContext(tx, depTx *pb.Transaction) error {
return nil
}
func (handler *Handler) setChaincodeSecurityContext(tx *pb.Transaction, msg *pb.ChaincodeMessage) error {
func (handler *Handler) setChaincodeSecurityContext(tx, depTx *pb.Transaction, msg *pb.ChaincodeMessage) error {
chaincodeLogger.Debug("setting chaincode security context...")
if msg.SecurityContext == nil {
msg.SecurityContext = &pb.ChaincodeSecurityContext{}
......@@ -1248,6 +1263,13 @@ func (handler *Handler) setChaincodeSecurityContext(tx *pb.Transaction, msg *pb.
return err
}
msg.SecurityContext.Payload = ctorMsgRaw
// TODO: add deploy metadata
if depTx != nil {
msg.SecurityContext.ParentMetadata = depTx.Metadata
} else {
msg.SecurityContext.ParentMetadata = handler.deployTXSecContext.Metadata
}
msg.SecurityContext.Payload = ctorMsgRaw
msg.SecurityContext.TxTimestamp = tx.Timestamp
}
......@@ -1289,7 +1311,7 @@ func (handler *Handler) initOrReady(txid string, initArgs [][]byte, tx *pb.Trans
}
//if security is disabled the context elements will just be nil
if err := handler.setChaincodeSecurityContext(tx, ccMsg); err != nil {
if err := handler.setChaincodeSecurityContext(tx, depTx, ccMsg); err != nil {
return nil, err
}
......@@ -1317,7 +1339,7 @@ func (handler *Handler) handleQueryChaincode(msg *pb.ChaincodeMessage) {
}()
//check and prohibit C-call-C for CONFIDENTIAL txs
if serialSendMsg = handler.canCallChaincode(msg.Txid); serialSendMsg != nil {
if serialSendMsg = handler.canCallChaincode(msg.Txid, true); serialSendMsg != nil {
return
}
......@@ -1337,6 +1359,16 @@ func (handler *Handler) handleQueryChaincode(msg *pb.ChaincodeMessage) {
chaincodeInvocationSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: chaincodeSpec}
transaction, _ := pb.NewChaincodeExecute(chaincodeInvocationSpec, msg.Txid, pb.Transaction_CHAINCODE_QUERY)
tsc := handler.getTxContext(msg.Txid).transactionSecContext
transaction.Nonce = tsc.Nonce
transaction.ConfidentialityLevel = tsc.ConfidentialityLevel
transaction.ConfidentialityProtocolVersion = tsc.ConfidentialityProtocolVersion
transaction.Metadata = tsc.Metadata
transaction.Cert = tsc.Cert
chaincodeLogger.Debugf("[%s]Invoking another chaincode", shorttxid(msg.Txid))
// Launch the new chaincode if not already running
_, chaincodeInput, launchErr := handler.chaincodeSupport.Launch(context.Background(), transaction)
if launchErr != nil {
......@@ -1466,7 +1498,7 @@ func (handler *Handler) sendExecuteMessage(msg *pb.ChaincodeMessage, tx *pb.Tran
}
//if security is disabled the context elements will just be nil
if err := handler.setChaincodeSecurityContext(tx, msg); err != nil {
if err := handler.setChaincodeSecurityContext(tx, nil, msg); err != nil {
return nil, err
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment