github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/examples/chaincode/go/asset_management_interactive/asset_management.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"encoding/base64"
    21  	"errors"
    22  	"fmt"
    23  
    24  	"github.com/hyperledger/fabric/accesscontrol/impl"
    25  	"github.com/hyperledger/fabric/core/chaincode/shim"
    26  	"github.com/hyperledger/fabric/core/crypto/primitives"
    27  	pb "github.com/hyperledger/fabric/protos/peer"
    28  	"github.com/op/go-logging"
    29  )
    30  
    31  var myLogger = logging.MustGetLogger("asset_mgm")
    32  
    33  // AssetManagementChaincode is simple chaincode implementing a basic Asset Management system
    34  // with access control enforcement at chaincode level.
    35  // Look here for more information on how to implement access control at chaincode level:
    36  // https://github.com/hyperledger/fabric/blob/master/docs/tech/application-ACL.md
    37  // An asset is simply represented by a string.
    38  type AssetManagementChaincode struct {
    39  }
    40  
    41  // Init method will be called during deployment.
    42  // The deploy transaction metadata is supposed to contain the administrator cert
    43  func (t *AssetManagementChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
    44  	_, args := stub.GetFunctionAndParameters()
    45  	myLogger.Debug("Init Chaincode...")
    46  	if len(args) != 0 {
    47  		return shim.Error("Incorrect number of arguments. Expecting 0")
    48  	}
    49  
    50  	// Create ownership table
    51  	err := stub.CreateTable("AssetsOwnership", []*shim.ColumnDefinition{
    52  		&shim.ColumnDefinition{Name: "Asset", Type: shim.ColumnDefinition_STRING, Key: true},
    53  		&shim.ColumnDefinition{Name: "Owner", Type: shim.ColumnDefinition_BYTES, Key: false},
    54  	})
    55  	if err != nil {
    56  		return shim.Error("Failed creating AssetsOnwership table.")
    57  	}
    58  
    59  	// Set the admin
    60  	// The metadata will contain the certificate of the administrator
    61  	adminCert, err := stub.GetCallerMetadata()
    62  	if err != nil {
    63  		myLogger.Debug("Failed getting metadata")
    64  		return shim.Error("Failed getting metadata.")
    65  	}
    66  	if len(adminCert) == 0 {
    67  		myLogger.Debug("Invalid admin certificate. Empty.")
    68  		return shim.Error("Invalid admin certificate. Empty.")
    69  	}
    70  
    71  	myLogger.Debug("The administrator is [%x]", adminCert)
    72  
    73  	stub.PutState("admin", adminCert)
    74  
    75  	myLogger.Debug("Init Chaincode...done")
    76  
    77  	return shim.Success(nil)
    78  }
    79  
    80  func (t *AssetManagementChaincode) assign(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    81  	myLogger.Debug("Assign...")
    82  
    83  	if len(args) != 2 {
    84  		return shim.Error("Incorrect number of arguments. Expecting 2")
    85  	}
    86  
    87  	asset := args[0]
    88  	owner, err := base64.StdEncoding.DecodeString(args[1])
    89  	if err != nil {
    90  		return shim.Error("Failed decodinf owner")
    91  	}
    92  
    93  	// Verify the identity of the caller
    94  	// Only an administrator can invoker assign
    95  	adminCertificate, err := stub.GetState("admin")
    96  	if err != nil {
    97  		return shim.Error("Failed fetching admin identity")
    98  	}
    99  
   100  	ok, err := t.isCaller(stub, adminCertificate)
   101  	if err != nil {
   102  		return shim.Error("Failed checking admin identity")
   103  	}
   104  	if !ok {
   105  		return shim.Error("The caller is not an administrator")
   106  	}
   107  
   108  	// Register assignment
   109  	myLogger.Debugf("New owner of [%s] is [% x]", asset, owner)
   110  
   111  	ok, err = stub.InsertRow("AssetsOwnership", shim.Row{
   112  		Columns: []*shim.Column{
   113  			&shim.Column{Value: &shim.Column_String_{String_: asset}},
   114  			&shim.Column{Value: &shim.Column_Bytes{Bytes: owner}}},
   115  	})
   116  
   117  	if !ok && err == nil {
   118  		return shim.Error("Asset was already assigned.")
   119  	}
   120  
   121  	if err != nil {
   122  		return shim.Error(err.Error())
   123  	}
   124  
   125  	myLogger.Debug("Assign...done!")
   126  
   127  	return shim.Success(nil)
   128  }
   129  
   130  func (t *AssetManagementChaincode) transfer(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   131  	myLogger.Debug("Transfer...")
   132  
   133  	if len(args) != 2 {
   134  		return shim.Error("Incorrect number of arguments. Expecting 2")
   135  	}
   136  
   137  	asset := args[0]
   138  	newOwner, err := base64.StdEncoding.DecodeString(args[1])
   139  	if err != nil {
   140  		return shim.Error("Failed decoding owner")
   141  	}
   142  
   143  	// Verify the identity of the caller
   144  	// Only the owner can transfer one of his assets
   145  	var columns []shim.Column
   146  	col1 := shim.Column{Value: &shim.Column_String_{String_: asset}}
   147  	columns = append(columns, col1)
   148  
   149  	row, err := stub.GetRow("AssetsOwnership", columns)
   150  	if err != nil {
   151  		return shim.Error(fmt.Sprintf("Failed retrieving asset [%s]: [%s]", asset, err))
   152  	}
   153  
   154  	prvOwner := row.Columns[1].GetBytes()
   155  	myLogger.Debugf("Previous owener of [%s] is [% x]", asset, prvOwner)
   156  	if len(prvOwner) == 0 {
   157  		return shim.Error("Invalid previous owner. Nil")
   158  	}
   159  
   160  	// Verify ownership
   161  	ok, err := t.isCaller(stub, prvOwner)
   162  	if err != nil {
   163  		return shim.Error("Failed checking asset owner identity")
   164  	}
   165  	if !ok {
   166  		return shim.Error("The caller is not the owner of the asset")
   167  	}
   168  
   169  	// At this point, the proof of ownership is valid, then register transfer
   170  	err = stub.DeleteRow(
   171  		"AssetsOwnership",
   172  		[]shim.Column{shim.Column{Value: &shim.Column_String_{String_: asset}}},
   173  	)
   174  	if err != nil {
   175  		return shim.Error("Failed deliting row.")
   176  	}
   177  
   178  	_, err = stub.InsertRow(
   179  		"AssetsOwnership",
   180  		shim.Row{
   181  			Columns: []*shim.Column{
   182  				&shim.Column{Value: &shim.Column_String_{String_: asset}},
   183  				&shim.Column{Value: &shim.Column_Bytes{Bytes: newOwner}},
   184  			},
   185  		})
   186  	if err != nil {
   187  		return shim.Error("Failed inserting row.")
   188  	}
   189  
   190  	myLogger.Debug("New owner of [%s] is [% x]", asset, newOwner)
   191  
   192  	myLogger.Debug("Transfer...done")
   193  
   194  	return shim.Success(nil)
   195  }
   196  
   197  func (t *AssetManagementChaincode) isCaller(stub shim.ChaincodeStubInterface, certificate []byte) (bool, error) {
   198  	myLogger.Debug("Check caller...")
   199  
   200  	// In order to enforce access control, we require that the
   201  	// metadata contains the signature under the signing key corresponding
   202  	// to the verification key inside certificate of
   203  	// the payload of the transaction (namely, function name and args) and
   204  	// the transaction binding (to avoid copying attacks)
   205  
   206  	// Verify \sigma=Sign(certificate.sk, tx.Payload||tx.Binding) against certificate.vk
   207  	// \sigma is in the metadata
   208  
   209  	sigma, err := stub.GetCallerMetadata()
   210  	if err != nil {
   211  		return false, errors.New("Failed getting metadata")
   212  	}
   213  	payload, err := stub.GetArgsSlice()
   214  	if err != nil {
   215  		return false, errors.New("Failed getting payload")
   216  	}
   217  	binding, err := stub.GetBinding()
   218  	if err != nil {
   219  		return false, errors.New("Failed getting binding")
   220  	}
   221  
   222  	myLogger.Debugf("passed certificate [% x]", certificate)
   223  	myLogger.Debugf("passed sigma [% x]", sigma)
   224  	myLogger.Debugf("passed payload [% x]", payload)
   225  	myLogger.Debugf("passed binding [% x]", binding)
   226  
   227  	ok, err := impl.NewAccessControlShim(stub).VerifySignature(
   228  		certificate,
   229  		sigma,
   230  		append(payload, binding...),
   231  	)
   232  	if err != nil {
   233  		myLogger.Errorf("Failed checking signature [%s]", err)
   234  		return ok, err
   235  	}
   236  	if !ok {
   237  		myLogger.Error("Invalid signature")
   238  	}
   239  
   240  	myLogger.Debug("Check caller...Verified!")
   241  
   242  	return ok, err
   243  }
   244  
   245  // Invoke will be called for every transaction.
   246  // Supported functions are the following:
   247  // "assign(asset, owner)": to assign ownership of assets. An asset can be owned by a single entity.
   248  // Only an administrator can call this function.
   249  // "transfer(asset, newOwner)": to transfer the ownership of an asset. Only the owner of the specific
   250  // asset can call this function.
   251  // An asset is any string to identify it. An owner is representated by one of his ECert/TCert.
   252  func (t *AssetManagementChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
   253  	function, args := stub.GetFunctionAndParameters()
   254  	// Handle different functions
   255  	if function == "assign" {
   256  		// Assign ownership
   257  		return t.assign(stub, args)
   258  	} else if function == "transfer" {
   259  		// Transfer ownership
   260  		return t.transfer(stub, args)
   261  	}
   262  
   263  	return shim.Error("Received unknown function invocation")
   264  }
   265  
   266  // Query callback representing the query of a chaincode
   267  // Supported functions are the following:
   268  // "query(asset)": returns the owner of the asset.
   269  // Anyone can invoke this function.
   270  func (t *AssetManagementChaincode) Query(stub shim.ChaincodeStubInterface) pb.Response {
   271  	function, args := stub.GetFunctionAndParameters()
   272  	myLogger.Debugf("Query [%s]", function)
   273  
   274  	if function != "query" {
   275  		return shim.Error("Invalid query function name. Expecting 'query' but found '" + function + "'")
   276  	}
   277  
   278  	var err error
   279  
   280  	if len(args) != 1 {
   281  		myLogger.Debug("Incorrect number of arguments. Expecting name of an asset to query")
   282  		return shim.Error("Incorrect number of arguments. Expecting name of an asset to query")
   283  	}
   284  
   285  	// Who is the owner of the asset?
   286  	asset := args[0]
   287  
   288  	myLogger.Debugf("Arg [%s]", string(asset))
   289  
   290  	var columns []shim.Column
   291  	col1 := shim.Column{Value: &shim.Column_String_{String_: asset}}
   292  	columns = append(columns, col1)
   293  
   294  	row, err := stub.GetRow("AssetsOwnership", columns)
   295  	if err != nil {
   296  		myLogger.Debugf("Failed retriving asset [%s]: [%s]", string(asset), err)
   297  		return shim.Error(fmt.Sprintf("Failed retriving asset [%s]: [%s]", string(asset), err))
   298  	}
   299  
   300  	myLogger.Debugf("Query done [% x]", row.Columns[1].GetBytes())
   301  
   302  	return shim.Success(row.Columns[1].GetBytes())
   303  }
   304  
   305  func main() {
   306  	primitives.SetSecurityLevel("SHA3", 256)
   307  	err := shim.Start(new(AssetManagementChaincode))
   308  	if err != nil {
   309  		fmt.Printf("Error starting AssetManagementChaincode: %s", err)
   310  	}
   311  }