diff --git a/core/chaincode/chaincode_support.go b/core/chaincode/chaincode_support.go
index 368a2c4278bdbbc378234afb1d325dc74e595775..30270202418615e530718862346b8156ebfc9f17 100644
--- a/core/chaincode/chaincode_support.go
+++ b/core/chaincode/chaincode_support.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/golang/protobuf/proto"
+	"github.com/hyperledger/fabric/common/util"
 	"github.com/hyperledger/fabric/core/common/ccprovider"
 	"github.com/hyperledger/fabric/core/common/sysccprovider"
 	"github.com/hyperledger/fabric/core/container/ccintf"
@@ -139,8 +140,9 @@ func (cs *ChaincodeSupport) HandleChaincodeStream(ctxt context.Context, stream c
 		ACLProvider:        cs.ACLProvider,
 		TXContexts:         NewTransactionContexts(),
 		ActiveTransactions: NewActiveTransactions(),
-
-		sccp: cs.sccp,
+		SystemCCProvider:   cs.sccp,
+		SystemCCVersion:    util.GetSysCCVersion(),
+		PolicyChecker:      CheckInstantiationPolicyFunc(ccprovider.CheckInstantiationPolicy),
 	}
 
 	return handler.ProcessStream(stream)
diff --git a/core/chaincode/chaincode_support_test.go b/core/chaincode/chaincode_support_test.go
index 097b3489c5b609f47bd83ef3e50827dd66070eae..2582f17ba9c7ce844ee7d93142e62c7c4146018c 100644
--- a/core/chaincode/chaincode_support_test.go
+++ b/core/chaincode/chaincode_support_test.go
@@ -1088,7 +1088,7 @@ func TestStartAndWaitLaunchError(t *testing.T) {
 }
 
 func TestGetTxContextFromHandler(t *testing.T) {
-	h := Handler{TXContexts: NewTransactionContexts(), sccp: &scc.Provider{Peer: peer.Default, PeerSupport: peer.DefaultSupport, Registrar: inproccontroller.NewRegistry()}}
+	h := Handler{TXContexts: NewTransactionContexts(), SystemCCProvider: &scc.Provider{Peer: peer.Default, PeerSupport: peer.DefaultSupport, Registrar: inproccontroller.NewRegistry()}}
 
 	chnl := "test"
 	txid := "1"
@@ -1300,7 +1300,7 @@ func TestCCFramework(t *testing.T) {
 	initializeCC(t, chainID, ccname, ccSide, chaincodeSupport)
 
 	//chaincode support should not allow dups
-	handler := &Handler{chaincodeID: &pb.ChaincodeID{Name: ccname + ":0"}, sccp: chaincodeSupport.sccp}
+	handler := &Handler{chaincodeID: &pb.ChaincodeID{Name: ccname + ":0"}, SystemCCProvider: chaincodeSupport.sccp}
 	if err := chaincodeSupport.HandlerRegistry.Register(handler); err == nil {
 		t.Fatalf("expected re-register to fail")
 	}
diff --git a/core/chaincode/handler.go b/core/chaincode/handler.go
index 3ac38d135f39c1bbc086db41209de8c6d536e954..7dbca793f460c80c3b7c837060b075c52fd8da5c 100644
--- a/core/chaincode/handler.go
+++ b/core/chaincode/handler.go
@@ -27,27 +27,6 @@ import (
 	"golang.org/x/net/context"
 )
 
-type state int
-
-const (
-	created state = iota
-	established
-	ready
-)
-
-func (s state) String() string {
-	switch s {
-	case created:
-		return "created"
-	case established:
-		return "established"
-	case ready:
-		return "ready"
-	default:
-		return "UNKNOWN"
-	}
-}
-
 var chaincodeLogger = flogging.MustGetLogger("chaincode")
 
 // ACLProvider is responsible for performing access control checks when invoking
@@ -56,20 +35,41 @@ type ACLProvider interface {
 	CheckACL(resName string, channelID string, idinfo interface{}) error
 }
 
+// Registry is responsible for tracking handlers.
 type Registry interface {
 	Register(*Handler) error
 	Ready(cname string)
 	Deregister(cname string) error
 }
 
+// SystemCCProvider provides system chaincode metadata.
+type SystemCCProvider interface {
+	IsSysCC(name string) bool
+	IsSysCCAndNotInvokableCC2CC(name string) bool
+}
+
+// PolicyChecker is used to evaluate instantiation policies.
+type PolicyChecker interface {
+	CheckInstantiationPolicy(name, version string, cd *ccprovider.ChaincodeData) error
+}
+
+// Adapter from function to PolicyChecker interface.
+type CheckInstantiationPolicyFunc func(name, version string, cd *ccprovider.ChaincodeData) error
+
+func (c CheckInstantiationPolicyFunc) CheckInstantiationPolicy(name, version string, cd *ccprovider.ChaincodeData) error {
+	return c(name, version, cd)
+}
+
 // Handler implements the peer side of the chaincode stream.
 type Handler struct {
+	// Keepalive specifies the interval at which keep-alive messages are sent.
+	Keepalive time.Duration
+	// SystemCCVersion specifies the current system chaincode version
+	SystemCCVersion string
 	// Lifecycle is used to access the Lifecycle System Chaincode.
 	Lifecycle *Lifecycle
 	// Executor is used to invoke chaincode.
 	Executor Executor
-	// Keepalive specifies the interval at which keep-alive messages are sent.
-	Keepalive time.Duration
 	// Registry is used to track active handlers.
 	Registry Registry
 	// ACLProvider is used to check if a chaincode invocation should be allowed.
@@ -79,15 +79,27 @@ type Handler struct {
 	TXContexts *TransactionContexts
 	// activeTransactions holds active transaction identifiers.
 	ActiveTransactions *ActiveTransactions
-
-	sccp sysccprovider.SystemChaincodeProvider
-
-	state       state
+	// SystemCCProvider provides access to system chaincode metadata
+	SystemCCProvider SystemCCProvider
+	// PolicyChecker is used to evaluate the chaincode instantiation policies.
+	PolicyChecker PolicyChecker
+
+	// state holds the current handler state. It will be created, established, or
+	// ready.
+	state state
+	// chaincodeID holds the ID of the chaincode that registered with the peer.
 	chaincodeID *pb.ChaincodeID
-	ccInstance  *sysccprovider.ChaincodeInstance
-	serialLock  sync.Mutex // serialLock is used to serialize sends across the grpc chat stream.
-	chatStream  ccintf.ChaincodeStream
-	errChan     chan error // chan to pass error in sync and nonsync mode
+	// ccInstances holds information about the chaincode instance associated with
+	// the peer.
+	ccInstance *sysccprovider.ChaincodeInstance
+
+	// serialLock is used to serialize sends across the grpc chat stream.
+	serialLock sync.Mutex
+	// chatStream is the bidirectional grpc stream used to communicate with the
+	// chaincode instance.
+	chatStream ccintf.ChaincodeStream
+	// errChan is used to communicate errors from the async send to the receive loop
+	errChan chan error
 }
 
 // HandleMessage is the entry point Chaincode messages.
@@ -239,7 +251,7 @@ func (h *Handler) serialSendAsync(msg *pb.ChaincodeMessage, sendErr bool) {
 func (h *Handler) checkACL(signedProp *pb.SignedProposal, proposal *pb.Proposal, ccIns *sysccprovider.ChaincodeInstance) error {
 	// ensure that we don't invoke a system chaincode
 	// that is not invokable through a cc2cc invocation
-	if h.sccp.IsSysCCAndNotInvokableCC2CC(ccIns.ChaincodeName) {
+	if h.SystemCCProvider.IsSysCCAndNotInvokableCC2CC(ccIns.ChaincodeName) {
 		return errors.Errorf("system chaincode %s cannot be invoked with a cc2cc invocation", ccIns.ChaincodeName)
 	}
 
@@ -250,7 +262,7 @@ func (h *Handler) checkACL(signedProp *pb.SignedProposal, proposal *pb.Proposal,
 	// - an application chaincode (and we still need to determine
 	//   whether the invoker can invoke it)
 
-	if h.sccp.IsSysCC(ccIns.ChaincodeName) {
+	if h.SystemCCProvider.IsSysCC(ccIns.ChaincodeName) {
 		// Allow this call
 		return nil
 	}
@@ -717,7 +729,7 @@ func (h *Handler) getTxContextForInvoke(channelID string, txid string, payload [
 	// If targetInstance is not an SCC, isValidTxSim should be called which will return an err.
 	// We do not want to propagate calls to user CCs when the original call was to a SCC
 	// without a channel context (ie, no ledger context).
-	if isscc := h.sccp.IsSysCC(targetInstance.ChaincodeName); !isscc {
+	if isscc := h.SystemCCProvider.IsSysCC(targetInstance.ChaincodeName); !isscc {
 		// normal path - UCC invocation with an empty ("") channel: isValidTxSim will return an error
 		return h.isValidTxSim("", txid, "could not get valid transaction")
 	}
@@ -831,11 +843,8 @@ func (h *Handler) handleInvokeChaincode(msg *pb.ChaincodeMessage, txContext *Tra
 
 	chaincodeLogger.Debugf("[%s] getting chaincode data for %s on channel %s", shorttxid(msg.Txid), targetInstance.ChaincodeName, targetInstance.ChainID)
 
-	// is the chaincode a system chaincode ?
-	isscc := h.sccp.IsSysCC(targetInstance.ChaincodeName)
-
-	var version string
-	if !isscc {
+	version := h.SystemCCVersion
+	if !h.SystemCCProvider.IsSysCC(targetInstance.ChaincodeName) {
 		// if its a user chaincode, get the details
 		cd, err := h.Lifecycle.GetChaincodeDefinition(ctxt, msg.Txid, txContext.signedProp, txContext.proposal, targetInstance.ChainID, targetInstance.ChaincodeName)
 		if err != nil {
@@ -844,13 +853,10 @@ func (h *Handler) handleInvokeChaincode(msg *pb.ChaincodeMessage, txContext *Tra
 
 		version = cd.CCVersion()
 
-		err = ccprovider.CheckInstantiationPolicy(targetInstance.ChaincodeName, version, cd.(*ccprovider.ChaincodeData))
+		err = h.PolicyChecker.CheckInstantiationPolicy(targetInstance.ChaincodeName, version, cd.(*ccprovider.ChaincodeData))
 		if err != nil {
 			return nil, errors.WithStack(err)
 		}
-	} else {
-		// this is a system cc, just call it directly
-		version = util.GetSysCCVersion()
 	}
 
 	// Launch the new chaincode if not already running
@@ -935,3 +941,24 @@ func (h *Handler) sendExecuteMessage(ctxt context.Context, chainID string, msg *
 func (h *Handler) Close() {
 	h.TXContexts.Close()
 }
+
+type state int
+
+const (
+	created state = iota
+	established
+	ready
+)
+
+func (s state) String() string {
+	switch s {
+	case created:
+		return "created"
+	case established:
+		return "established"
+	case ready:
+		return "ready"
+	default:
+		return "UNKNOWN"
+	}
+}