func CreateCompositeKey(objectType string, attributes []string) (string, error) {
	if err := ValidateCompositeKeyAttribute(objectType); err != nil {
		return "", err
	}
	ck := CompositeKeyNamespace + objectType + string(rune(minUnicodeRuneValue))
	for _, att := range attributes {
		if err := ValidateCompositeKeyAttribute(att); err != nil {
			return "", err
		}
		ck += att + string(rune(minUnicodeRuneValue))
	}
	return ck, nil
}
CreateCompositeKey and its related functions and consts copied from core/chaincode/shim/chaincode.go
package keys
import (
	"fmt"
	"strconv"
	"unicode/utf8"
	token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token"
	"github.com/pkg/errors"
)
const (
	minUnicodeRuneValue                = 0             
	MaxUnicodeRuneValue                = utf8.MaxRune  
	CompositeKeyNamespace              = "\x00"
	TokenKeyPrefix                     = "ztoken"
	FabTokenKeyPrefix                  = "token"
	AuditTokenKeyPrefix                = "audittoken"
	TokenMineKeyPrefix                 = "mine"
	TokenSetupKeyPrefix                = "setup"
	IssuedHistoryTokenKeyPrefix        = "issued"
	TokenAuditorKeyPrefix              = "auditor"
	TokenNameSpace                     = "zkat"
	numComponentsInKey                 = 2  
	Action                             = "action"
	ActionIssue                        = "issue"
	ActionTransfer                     = "transfer"
	Precision                   uint64 = 64
	Info                               = "info"
	TokenRequestKeyPrefix              = "token_request"
	OwnerSeparator                     = "/"
	SerialNumber                       = "sn"
	IssueActionMetadata                = "iam"
)
func GetTokenIdFromKey(key string) (*token2.ID, error) {
	_, components, err := SplitCompositeKey(key)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("error splitting input composite key: '%s'", err))
	}
	if len(components) != numComponentsInKey {
		return nil, errors.New(fmt.Sprintf("not enough components in output ID composite key; expected 3, received '%s'", components))
	}
	txID := components[numComponentsInKey-2]
	index, err := strconv.ParseUint(components[numComponentsInKey-1], 10, 64)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("error parsing output index '%s': '%s'", components[numComponentsInKey-1], err))
	}
	return &token2.ID{TxId: txID, Index: index}, nil
}
func SplitCompositeKey(compositeKey string) (string, []string, error) {
	componentIndex := 1
	components := []string{}
	for i := 1; i < len(compositeKey); i++ {
		if compositeKey[i] == minUnicodeRuneValue {
			components = append(components, compositeKey[componentIndex:i])
			componentIndex = i + 1
		}
	}
	if len(components) < numComponentsInKey+1 {
		return "", nil, errors.Errorf("invalid composite key - not enough components found in key '%s', [%d][%v]", compositeKey, len(components), components)
	}
	return components[0], components[1:], nil
}
func CreateTokenKey(txID string, index uint64) (string, error) {
	return CreateCompositeKey(TokenKeyPrefix, []string{txID, strconv.FormatUint(index, 10)})
}
func CreateSNKey(sn string) (string, error) {
	return CreateCompositeKey(TokenKeyPrefix, []string{SerialNumber, sn})
}
func CreateFabtokenKey(txID string, index uint64) (string, error) {
	return CreateCompositeKey(FabTokenKeyPrefix, []string{txID, strconv.FormatUint(index, 10)})
}
func CreateAuditTokenKey(txID string, index uint64) (string, error) {
	return CreateCompositeKey(AuditTokenKeyPrefix, []string{txID, strconv.FormatUint(index, 10)})
}
func CreateTokenMineKey(txID string, index uint64) (string, error) {
	return CreateCompositeKey(TokenKeyPrefix, []string{TokenMineKeyPrefix, txID, strconv.FormatUint(index, 10)})
}
func CreateSetupKey() (string, error) {
	return CreateCompositeKey(TokenKeyPrefix, []string{TokenSetupKeyPrefix})
}
func CreateSetupBundleKey() (string, error) {
	return CreateCompositeKey(TokenKeyPrefix, []string{TokenSetupKeyPrefix, "bundle"})
}
func CreateTokenRequestKey(txID string) (string, error) {
	return CreateCompositeKey(TokenKeyPrefix, []string{TokenRequestKeyPrefix, txID})
}
func CreateIssueActionMetadataKey(hash string) (string, error) {
	return CreateCompositeKey(TokenKeyPrefix, []string{IssueActionMetadata, hash})
}