Hyperledger Fabric application practice (9)– smart contract writing

1. Fabric chaincode version difference

Fabric chain code is divided into two major versions, 1.x and 2.x versions. The main differences between the two are:

1. Different import packages

The package imported by 1.x is:

"[github.com/hyperledger/fabric/core/chaincode/shim](http://github.com/hyperledger/fabric/core/chaincode/shim)"

  pb "[github.com/hyperledger/fabric/protos/peer](http://github.com/hyperledger/fabric/protos/peer)"

The package imported by 2.0 is:

"[github.com/hyperledger/fabric-contract-api-go/contractapi](http://github.com/hyperledger/fabric-contract-api-go/contractapi)"

2. The method structure is different

Fabric 2.0 chaincode does not require the Invoke and Init methods.

3. The call formal parameter type and return value in the method are different

The 1.x method is:

createCar1(stub shim.ChaincodeStubInterface, args []string) pb.Response { }

The 2.0 method is:

Create(ctx contractapi.TransactionContextInterface, key string, value string) error { }

The freerent chain code has not yet been written. This article mainly sorts out the main ideas and some APIs of chain code writing.

2. Simple analysis of contractapi package

From the various simple Go chaincodes provided by the official fabirc-samples, we can see that generally our chaincode method is a custom SmartContract struct, which contains the Contract in the contractapi package structure. The Contract struct implements the ContractInterface interface, and the custom structure can quickly help me implement the ContractInterface through the horizontal combination of Contract interface.

type SmartContract struct {
  contractapi.Contract
}

type Contract struct {
  name string
  Info metadata. InfoMetadata
  UnknownTransaction interface{}
  BeforeTransaction interface{}
  AfterTransaction interface{}
  TransactionContextHandler SettableTransactionContextInterface
}

ContractInterface defines the functionality that a valid contract should have. Contracts used in chaincode must implement this interface. Therefore, the application chaincode will directly nest contractapi.Contract in the chaincode structure to implement the ContractInterface interface.

type ContractInterface interface {

  // Get the metadata currently describing the smart contract
  GetInfo() metadata.InfoMetadata

  //When the contract is converted to the chain code, get the unknown transaction set in the contract
  GetUnknownTransaction() interface{}
  //Get the before transaction collection
  GetBeforeTransaction() interface{}
   //Get after transaction collection
  GetAfterTransaction() interface{}

  GetName() string

//GetTransactionContextHandler returns the SettableTransactionContextInterface used by the agreed function.
// When the contract is converted to chaincode, this function will be called and the returned transaction context will be stored.
// When calling chaincode via Init/Invoke, if the function requires context in its parameter list,
// will create a transaction context of storage type and send it as a parameter to the named contract function (and before/after and unknown functions).
// If a function that takes a transaction context takes an interface as a context, the transaction context returned by this function must satisfy the interface
  GetTransactionContextHandler() SettableTransactionContextInterface
}

In addition to the normal chain code, the contractapi package also provides many extended functions of the normal chain code:

  • IgnoreContractInterface extends ContractInterface and provides additional functions that can be used to mark which functions should not be accessed by calling/querying chaincode;
  • EvaluationContractInterface extends ContractInterface, indicating that these functions should be query (query) instead of invoke (call)

From the figure above, we can see that the application structure we defined can be converted into ContractChaincode by entering the NewChaincode(contracts ...ContractInterface) (*ContractChaincode, error) function. Structure, the basic logic of NewChaincode is that this function parses each passed function and stores the composition details to be used by the chaincode. The public functions of the contract are stored in the chaincode and can be called. At the same time, the system contract is added to the chain code, which provides the function of obtaining the metadata of the chain code. The generated metadata is a JSON-formatted MetadataContractChaincode containing the name of each contract and details of the public functions and types they accept/return. It also outlines contract and chaincode version details.

At the same time, ContractChaincode implements the Chaincode interface, which is an interface that every chaincode in the fabric must implement. It provides Invoke and Init methods, which is the fabric1.X chain Code interaction interface, fabric2.X chain code implementation is to encapsulate fabric1.X, which is convenient for developers to write chain code.

2. Simple analysis of shim package

2.1 shim package structure content

It is found in the contractapi package that the contractapi package is only to encapsulate the shim for the convenience of interaction, and the calling interface still needs to obtain the ChaincodeStub in the shim package

//ctx contractapi.TransactionContextInterface
ctx.GetStub().PutState(id, assetJSON)

type ChaincodeStub struct {
  TxID string
  ChannelID string
  chaincodeEvent *pb.ChaincodeEvent
  args[][]byte
  handler *Handler
  signedProposal *pb.SignedProposal
  proposal *pb. Proposal
  validationParameter Metakey string

  // Additional fields extracted from the signedProposal
  creator []byte
  transient map[string][]byte
  binding []byte

  decorations map[string][]byte
}

The handler.go in the shim is the functional code for the communication between the chaincode service and the Peer service. We mainly focus on which API interfaces are provided to interact with the ledger, so as to facilitate the writing of chaincode. It can be seen from interfaces.go that the chaincode’s API for interacting with ledgers is mainly provided by ChaincodeStubInterface, and the remaining three Iterator are iterator interfaces for rich ledger queries.

ChaincodeStubInterface is implemented by ChaincodeStub, understand the method of ChaincodeStub structure, we have basically mastered the method of fabric chain code writing, in fact, directly You can also learn from the official API documentation.

2.2 API function classification

The most important function of the chain code is to operate the ledger, plus some additional functions of msp identity authentication, the purpose is also to operate the ledger more securely.

  • Auxiliary functions: such as parameter acquisition, acquisition of transactions, network information, etc.
    • Parameter acquisition: This kind of method is no longer needed in chaincode 2.X. The NewChaincode function in the contractapi package has helped us fill in the parameters, so that we can write chaincode methods like other methods.
    • Functions for obtaining information types: GetTxID(), GetChannelID(), GetCreator(), GetSignedProposal(), GetTxTimestamp()
  • State operation: operate on the k-v of the ledger,
    • Read and write: PutState, DelState, GetStateByRange, GetStateByRangeWithPagination, GetHistoryForKey
    • Composite keys: SplitCompositeKey, CreateCompositeKey, GetStateByPartialCompositeKey, GetStateByPartialCompositeKeyWithPagination
    • The endorsement policy settings for this Key: SetStateValidationParameter, GetStateValidationParameter
  • Private database operations:
    • Read and write: GetPrivateData, GetPrivateDataHash (to facilitate non-private members to verify the transaction, only the Hash of the data can be read), PutPrivateData, DelPrivateData, PurgePrivateData, GetPrivateDataByRange
    • Key-level endorsement policy settings: SetPrivateDataValidationParameter, GetPrivateDataValidationParameter
    • Composite key: GetPrivateDataByPartialCompositeKey
  • Rich query: GetQueryResult, GetPrivateDataQueryResult
  • Transient data: GetTransient (transient data is mainly used to protect data, it is literally understood that the data passed in through transient data will not be stored permanently, there will be special data for temporary storage, currently only use to pass structure data.)
  • Event setting: SetEvent (mainly bound to trigger the set event** after the special function is executed, at most one Event** can be set in a method, otherwise the first one will be used by the second Two are covered.)
  • GetBinding (never used): Returns a transactional binding, which is used to enforce a link between application data (like the data stored in the transient fields above) and the proposal itself. This helps avoid possible replay attacks.
  • GetDecorations (never used): Returns additional data about proposals originating from peers (if applicable). This data is set by the peer’s decorators that append or mutate the chaincode inputs passed to the chaincode.