hyperledger-fabric - 如何通过链码以动态方式授权访问 Hyperledger Fabric 上的私有集合?
问题描述
基于此示例https://github.com/hyperledger/fabric-samples/blob/master/asset-transfer-secured-agreement/chaincode-go/asset_transfer.go我想创建一个安全的应用程序以共享私有信息。更详细地说,我用公共和私人数据创建了一个智能合约,其中所有者能够授权另一个组织访问公共集合。但是,应用程序在背书政策方面失败了。我不明白为什么会这样。有 2 个组织,每个组织有 1 个同伴。智能合约部署在测试网络(Fabric v2)上。以下是频道的操作:
./network.sh up createChannel -ca
./network.sh deployCC -ccn secured -ccl go -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
这是我的链码:
type SmartContract struct {
contractapi.Contract
}
type Asset struct {
ObjectType string `json:"objectType"` // ObjectType is used to distinguish different object types in the same chaincode namespace
ID string `json:"assetID"`
Filename string `json:"filename"`
OwnerOrg string `json:"ownerOrg"`
AuthOrg string `json:"AuthOrg"`
PublicDescription string `json:"publicDescription"`
type PrivateAsset struct {
ID string `json:"assetID"`
PrivateData string `json:"privatedata"`
}
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, assetID, filename, publicDescription, authOrg string) error {
clientOrgID, err := getClientOrgID(ctx, true)
if err != nil {
return fmt.Errorf("failed to get verified OrgID: %v", err)
}
asset := Asset{
ObjectType: "asset",
ID: assetID,
Filename: filename,
OwnerOrg: clientOrgID,
AuthOrg: authOrg,
PublicDescription: publicDescription,
}
assetBytes, err := json.Marshal(asset)
if err != nil {
return fmt.Errorf("failed to create asset JSON: %v", err)
}
err = ctx.GetStub().PutState(asset.ID, assetBytes)
if err != nil {
return fmt.Errorf("failed to put asset in public data: %v", err)
}
return nil
}
func (s *SmartContract) CreatePrivateAsset(ctx contractapi.TransactionContextInterface, assetID, data string) error {
transientMap, err := ctx.GetStub().GetTransient()
if err != nil {
return fmt.Errorf("error getting transient data: %v", err)
}
privatePropertiesJSON, ok := transientMap["asset_private"]
if !ok {
return fmt.Errorf("asset_properties key not found in the transient map")
}
clientOrgID, err := getClientOrgID(ctx, true)
if err != nil {
return fmt.Errorf("failed to get verified OrgID: %v", err)
}
asset, err := s.ReadAsset(ctx, assetID)
if err != nil {
return fmt.Errorf("failed to get asset: %v", err)
}
if clientOrgID != asset.OwnerOrg {
return fmt.Errorf("a client from %s cannot sell an asset owned by %s", clientOrgID, asset.OwnerOrg)
}
if assetID != asset.ID {
return fmt.Errorf("%s does not exists", assetID)
}
privatedata := PrivateAsset{
ID: assetID,
Data: data,
}
assetBytes, err := json.Marshal(privatedata)
if err != nil {
return fmt.Errorf("failed to create asset JSON: %v", err)
}
err = setAssetStateBasedEndorsement(ctx, asset.ID, clientOrgID)
if err != nil {
return fmt.Errorf("failed setting state based endorsement for owner: %v", err)
}
collection := buildCollectionName(clientOrgID)
err = ctx.GetStub().PutPrivateData(collection, asset.ID, privatePropertiesJSON)
if err != nil {
return fmt.Errorf("failed to put Asset private details: %v", err)
}
err = ctx.GetStub().PutPrivateData(collection, asset.ID, assetBytes)
if err != nil {
return fmt.Errorf("failed to put Asset private details: %v", err)
}
return nil
}
func (s *SmartContract) UpdateOrg(ctx contractapi.TransactionContextInterface, assetID string, authOrgID string) error {
transientMap, err := ctx.GetStub().GetTransient()
if err != nil {
return fmt.Errorf("error getting transient: %v", err)
}
privatePropertiesJSON, ok := transientMap["asset_private"]
if !ok {
return fmt.Errorf("asset_properties key not found in the transient map")
}
clientOrgID, err := getClientOrgID(ctx, false)
if err != nil {
return fmt.Errorf("failed to get verified OrgID: %v", err)
}
asset, err := s.ReadAsset(ctx, assetID)
if err != nil {
return fmt.Errorf("failed to get asset: %v", err)
}
if clientOrgID != asset.OwnerOrg {
return fmt.Errorf("a client from %s cannot update the description of a asset owned by %s", clientOrgID, asset.OwnerOrg)
}
asset.AuthOrg = authOrgID
updatedAssetJSON, err := json.Marshal(asset)
if err != nil {
return fmt.Errorf("failed to marshal asset: %v", err)
}
err = ctx.GetStub().PutState(asset.ID, updatedAssetJSON)
if err != nil {
return fmt.Errorf("failed to put asset in public data: %v", err)
}
var file File
err = json.Unmarshal(privatePropertiesJSON, &file)
if err != nil {
return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
}
collection := buildCollectionName(clientOrgID)
immutableProperties, err := ctx.GetStub().GetPrivateData(collection, assetID)
if err != nil {
return fmt.Errorf("failed to read asset private properties from client org's collection: %v", err)
}
if immutableProperties == nil {
return fmt.Errorf("asset private details does not exist in client org's collection: %s", assetID)
}
err = setAssetStateBasedEndorsement(ctx, asset.ID, authOrgID)
if err != nil {
return fmt.Errorf("failed setting state based endorsement for owner: %v", err)
}
collectionNew := buildCollectionName(authOrgID)
err = ctx.GetStub().PutPrivateData(collectionNew, asset.ID, immutableProperties)
if err != nil {
return fmt.Errorf("failed to put Asset private details: %v", err)
}
return nil
}
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, assetID string) (*Asset, error) {
// Since only public data is accessed in this function, no access control is required
assetJSON, err := ctx.GetStub().GetState(assetID)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if assetJSON == nil {
return nil, fmt.Errorf("%s does not exist", assetID)
}
var asset *Asset
err = json.Unmarshal(assetJSON, &asset)
if err != nil {
return nil, err
}
return asset, nil
}
func getClientOrgID(ctx contractapi.TransactionContextInterface, verifyOrg bool) (string, error) {
clientOrgID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return "", fmt.Errorf("failed getting client's orgID: %v", err)
}
if verifyOrg {
err = verifyClientOrgMatchesPeerOrg(clientOrgID)
if err != nil {
return "", err
}
}
return clientOrgID, nil
}
func buildCollectionName(clientOrgID string) string {
return fmt.Sprintf("_implicit_org_%s", clientOrgID)
}
func setAssetStateBasedEndorsement(ctx contractapi.TransactionContextInterface, assetID string, orgToEndorse string) error {
endorsementPolicy, err := statebased.NewStateEP(nil)
if err != nil {
return err
}
err = endorsementPolicy.AddOrgs(statebased.RoleTypePeer, orgToEndorse)
if err != nil {
return fmt.Errorf("failed to add org to endorsement policy: %v", err)
}
policy, err := endorsementPolicy.Policy()
if err != nil {
return fmt.Errorf("failed to create endorsement policy bytes from org: %v", err)
}
err = ctx.GetStub().SetStateValidationParameter(assetID, policy)
if err != nil {
return fmt.Errorf("failed to set validation parameter on asset: %v", err)
}
return nil
}
最后,这是关于转移的脚本的一部分:
const asset_properties_string = JSON.stringify(asset_private);
//console.log(`${asset_properties_string}`);
transaction = contractOrg1.createTransaction('UpdateOrg');
//console.log(`Setting Transient map`);
transaction.setEndorsingOrganizations(org1,org2);
transaction.setTransient({
asset_private: Buffer.from(asset_properties_string)
});
console.log(`Wait to submit transaction`);
await transaction.submit(asset_private.id, asset_private.authOrg);
但是,应用程序失败并显示以下输出:
失败:UpdateOrg - TransactionError:提交事务 d8f06177d7eb683590809ea943d65ad4f685126d8bda9719c552b37bd4889439 在对等 peer0.org1.example.com:7051 上失败,状态为 ENDORSEMENT_POLICY_FAILURE
PS对不起,很长的帖子,但我想详细一点