Skip to content

Commit

Permalink
C2C invocation for confidential contracts.
Browse files Browse the repository at this point in the history
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: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed Sep 16, 2016
1 parent 02a123c commit 5f9b3ea
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 18 deletions.
79 changes: 71 additions & 8 deletions core/chaincode/exectransaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -836,15 +848,14 @@ 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
if err != nil {
t.Fail()
t.Logf("Error initializing chaincode %s(%s)", chaincodeID1, err)
GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1})
closeListenerAndSleep(lis)
return
}

Expand All @@ -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
Expand All @@ -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
}

Expand All @@ -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)
Expand All @@ -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
}

Expand All @@ -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
Expand Down
52 changes: 42 additions & 10 deletions core/chaincode/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 != "" {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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{}
Expand All @@ -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())
Expand Down Expand Up @@ -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{}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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
}

Expand Down

0 comments on commit 5f9b3ea

Please sign in to comment.