mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-26 19:45:10 +00:00
198 lines
6.6 KiB
Go
198 lines
6.6 KiB
Go
/*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
|
)
|
|
|
|
// SmartContract provides functions for managing an Asset
|
|
type SmartContract struct {
|
|
contractapi.Contract
|
|
}
|
|
|
|
// CreateAsset issues a new asset to the world state with given details.
|
|
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, value string, owner string) error {
|
|
exists, err := s.AssetExists(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if exists {
|
|
return fmt.Errorf("the asset %s already exists", id)
|
|
}
|
|
ownerOrg, err := s.getClientOrgId(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
asset := Asset{
|
|
ID: id,
|
|
Value: value,
|
|
Owner: owner,
|
|
OwnerOrg: ownerOrg,
|
|
}
|
|
assetJSON, err := json.Marshal(asset)
|
|
if err != nil {
|
|
return err
|
|
} // Set the endorsement policy of the assetId Key, such that current owner Org is required to endorse future updates
|
|
setStateBasedEndorsement(ctx, id, ownerOrg)
|
|
|
|
// Optionally, set the endorsement policy of the assetId Key, such that any 1 Org (N) out of the specified Orgs can endorse future updates
|
|
// setStateBasedEndorsementNOutOf(ctx, assetId, 1, new String[]{"Org1MSP", "Org2MSP"});
|
|
|
|
return ctx.GetStub().PutState(id, assetJSON)
|
|
}
|
|
|
|
// ReadAsset returns the asset stored in the world state with given id.
|
|
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
|
|
assetJSON, err := ctx.GetStub().GetState(id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read from world state: %v", err)
|
|
}
|
|
if assetJSON == nil {
|
|
return nil, fmt.Errorf("the asset %s does not exist", id)
|
|
}
|
|
|
|
var asset Asset
|
|
err = json.Unmarshal(assetJSON, &asset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &asset, nil
|
|
}
|
|
|
|
// UpdateAsset updates an existing asset in the world state with provided parameters.
|
|
// Needs an endorsement of current owner Org Peer.
|
|
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, value string, owner string) error {
|
|
exists, err := s.AssetExists(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
return fmt.Errorf("the asset %s does not exist", id)
|
|
}
|
|
ownerOrg, err := s.getClientOrgId(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// overwriting original asset with new asset
|
|
asset := Asset{
|
|
ID: id,
|
|
Value: value,
|
|
Owner: owner,
|
|
OwnerOrg: ownerOrg,
|
|
}
|
|
assetJSON, err := json.Marshal(asset)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return ctx.GetStub().PutState(id, assetJSON)
|
|
}
|
|
|
|
// DeleteAsset deletes an given asset from the world state.
|
|
//Needs an endorsement of current owner Org Peer.
|
|
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
|
|
exists, err := s.AssetExists(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
return fmt.Errorf("the asset %s does not exist", id)
|
|
}
|
|
|
|
return ctx.GetStub().DelState(id)
|
|
}
|
|
|
|
// AssetExists returns true when asset with given ID exists in world state
|
|
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
|
|
assetJSON, err := ctx.GetStub().GetState(id)
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to read from world state: %v", err)
|
|
}
|
|
|
|
return assetJSON != nil, nil
|
|
}
|
|
|
|
// TransferAsset updates the owner field of asset with given id in world state.
|
|
// Needs an endorsement of current owner Org Peer.
|
|
// Re-sets the endorsement policy of the assetId Key, such that new owner Org Peer is required to endorse future updates.
|
|
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string, newOwnerOrg string) error {
|
|
asset, err := s.ReadAsset(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
asset.Owner = newOwner
|
|
asset.OwnerOrg = newOwnerOrg
|
|
assetJSON, err := json.Marshal(asset)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Re-Set the endorsement policy of the assetId Key, such that a new owner Org Peer is required to endorse future updates
|
|
setStateBasedEndorsement(ctx, id, newOwnerOrg)
|
|
|
|
// Optionally, set the endorsement policy of the assetId Key, such that any 1 Org (N) out of the specified Orgs can endorse future updates
|
|
// setStateBasedEndorsementNOutOf(ctx, assetId, 1, new String[]{"Org1MSP", "Org2MSP"});
|
|
|
|
return ctx.GetStub().PutState(id, assetJSON)
|
|
}
|
|
|
|
// Retrieves the client's OrgId (MSPID)
|
|
func (s *SmartContract) getClientOrgId(ctx contractapi.TransactionContextInterface) (string, error) {
|
|
return ctx.GetClientIdentity().GetMSPID()
|
|
}
|
|
|
|
// Sets an endorsement policy to the assetId Key.
|
|
// Enforces that the owner Org must endorse future update transactions for the specified assetId Key.
|
|
func setStateBasedEndorsement(ctx contractapi.TransactionContextInterface, assetId string, ownerOrgs string) {
|
|
var ep KeyEndorsementPolicy
|
|
ep.AddOrgs("MEMBER", ownerOrgs)
|
|
ctx.GetStub().SetStateValidationParameter(assetId, ep.getPolicy())
|
|
}
|
|
|
|
// Sets an endorsement policy to the assetId Key.
|
|
// Enforces that a given number of Orgs (N) out of the specified Orgs must endorse future update transactions for the specified assetId Key.
|
|
func setStateBasedEndorsementNOutOf(ctx contractapi.TransactionContextInterface, assetId string, nOrgs int, ownerOrgs []string) {
|
|
ctx.GetStub().SetStateValidationParameter(assetId, policy(nOrgs, ownerOrgs))
|
|
}
|
|
|
|
// Create a policy that requires a given number (N) of Org principals signatures out of the provided list of Orgs
|
|
func policy(nOrgs int, mspids []string) []byte {
|
|
sort.Strings(mspids)
|
|
var principals []string
|
|
var signPolicy []string
|
|
for i := 0; i < len(mspids); i++ {
|
|
mspid := mspids[i]
|
|
var mspRole MSPRole
|
|
mspRole := MSPRole{
|
|
mspId,
|
|
MSPRole.MSPRoleType.MEMBER}
|
|
var MSPPrincipal principals
|
|
principal := MSPPrincipal{
|
|
fabprotos.common.MSPPrincipal.Classification.ROLE,
|
|
fabprotos.common.MSPRole.encode(mspRole).finish()}
|
|
principals = append(principals, MspPrincipal.MSPPrincipal.newBuilder().setPrincipalClassification(MspPrincipal.MSPPrincipal.Classification.ROLE).setPrincipal(MspPrincipal.MSPRole.newBuilder().setMspIdentifier(mspid).setRole(MspPrincipal.MSPRole.MSPRoleType.MEMBER).build().toByteString()).build())
|
|
var signedBy SignaturePolicy_SignedBy
|
|
signedBy := SignaturePolicy_SignedBy{i}
|
|
signPolicy = append(signPolicy, signedBy)
|
|
}
|
|
// create the policy such that it requires any N signature's from all of the principals provided
|
|
var allOf SignaturePolicy_NOutOf
|
|
allOf := SignaturePolicy_NOutOf{nOrgs, signPolicies}
|
|
var nOutof SignaturePolicy_NOutOf_
|
|
noutof := SignaturePolicy_NOutOf_{allOf}
|
|
var spe SignaturePolicyEnvelope
|
|
spe := SignaturePolicyEnvelope{
|
|
version: 0,
|
|
rule: noutof,
|
|
identities: principals}
|
|
return
|
|
}
|