编写您的第一个Chaincode
Asset Transfer Chaincode(资产交易智能合约)
Our application is a basic sample chaincode to initialize a ledger with assets, create, read, update, and delete assets, check to see if an asset exists, and transfer assets from one owner to another.
我们的程序是一个基本简单的链码,利用资产初始化账本,创建、读取,更新以及更新资产,检查一个资产是否存在并且把资产从一个所有者交易给另一个所有者。
Choosing a Location for the Code(给代码选一个位置)
If you haven’t been doing programming in Go, you may want to make sure that you have Go installed and your system properly configured. We assume you are using a version that supports modules.
如果你还没有使用过Go,你需要确认你的环境已经安装了 Go并配置好了环境变量。我们假设你使用的是支持模块的版本。
Now, you will want to create a directory for your chaincode application.
To keep things simple, let’s use the following command:
现在你想要为你的链码程序创建一个目录,简单起见,我们使用下面的命令:
1 | // atcc is shorthand for asset transfer chaincode |
Now, let’s create the module and the source file that we’ll fill in with code:
现在让我们创建模块和源文件,并用代码填充它们:
1 | go mod init atcc |
Housekeeping(整理工作)
First, let’s start with some housekeeping. As with every chaincode, it implements the fabric-contract-api interface, so let’s add the Go import statements for the necessary dependencies for our chaincode. We’ll import the fabric contract api package and define our SmartContract.
首先让我们做一些整理工作。所有的链码都实现了fabric-contract-api interface,所以让我们为Go程序添加必要的依赖。我们会导入fabric合同API包来定义我们的智能合约。
1 | package main |
Next, let’s add a struct Asset
to represent simple assets on the ledger. Note the JSON annotations, which will be used to marshal the asset to JSON which is stored on the ledger.
下一步让我们增加一个Asset
类来表示账本上的简单的资产。请注意JSON注释,该注释将用于将资产编组为存储在分类帐中的JSON。
1 | // Asset describes basic details of what makes up a simple asset |
Initializing the Chaincode(初始化链码)
Next, we’ll implement the InitLedger
function to populate the ledger with some initial data.
接下来我们实现InitLedger
方法,这个方法使用一些初始化的数据来填充账本。
1 | // InitLedger adds a base set of assets to the ledger |
Next, we write a function to create an asset on the ledger that does not yet exist. When writing chaincode, it is a good idea to check for the existence of something on the ledger prior to taking an action on it, as is demonstrated in the CreateAsset
function below.
接下来,我们写一个创建一个账本上不存在的资产的方法。当编写链码时,最好先对分类帐进行检查,然后再对其进行操作,如下面的CreateAsset函数所示。
1 | // CreateAsset issues a new asset to the world state with given details. |
Now that we have populated the ledger with some initial assets and created an asset, let’s write a function ReadAsset
that allows us to read an asset from the ledger.
现在我们已经使用初始化资产和创建一个资产来填充了账本,让我们写一个 ReadAsset
方法来允许我们读取账本上的资产。
1 | // ReadAsset returns the asset stored in the world state with given id. |
Now that we have assets on our ledger we can interact with, let’s write a chaincode function UpdateAsset
that allows us to update attributes of the asset that we are allowed to change.
现在我们已经在我们的账本上有资产并能够与他们交互了,让我们写一个链码方法 UpdateAsset
来使我们能够更新允许更改的资产的属性。
1 | // UpdateAsset updates an existing asset in the world state with provided parameters. |
There may be cases where we need the ability to delete an asset from the ledger, so let’s write a DeleteAsset
function to handle that requirement.
可能我本需要从账本上删除一个资产,所以让我们写一个 DeleteAsset
方法来处理这个需求。
1 | // DeleteAsset deletes an given asset from the world state. |
We said earlier that is was a good idea to check to see if an asset exists before taking an action on it, so let’s write a function called AssetExists
to implement that requirement.
我们之前说在对一个资产进行操作之前,最好先检查这个资产是否存在,所以让我们写一个名为 AssetExists
的方法来实现这个需求。
1 | // AssetExists returns true when asset with given ID exists in world state |
Next, we’ll write a function we’ll call TransferAsset
that enables the transfer of an asset from one owner to another.
接着,我们写一个我们称作TransferAsset
的方法,该函数可将资产从一个所有者转移到另一个所有者。
1 | // TransferAsset updates the owner field of asset with given id in world state. |
Let’s write a function we’ll call GetAllAssets
that enables the querying of the ledger to return all of the assets on the ledger.
让我们写一个 GetAllAssets
方法来查询账本上的资产,这个方法返回账本上的所有资产。
1 | // GetAllAssets returns all assets found in world state |
Pulling it All Together(把这些代码放到一起)
Finally, we need to add the main
function, which will call the ContractChaincode.Start function. Here’s the whole chaincode program source.
最终我们需要添加一个调用 ContractChaincode.Start 方法的 main
方法。下面就是全部的链码程序源码。
1 | package main |
Chaincode access control
Chaincode can utilize the client (submitter) certificate for access control decisions with ctx.GetStub().GetCreator()
. Additionally the Fabric Contract API provides extension APIs that extract client identity from the submitter’s certificate that can be used for access control decisions, whether that is based on client identity itself, or the org identity, or on a client identity attribute.
链码可以通过 ctx.GetStub().GetCreator()
利用客户端(提交者)证书进行访问控制决策。此外,Fabric Contract API还提供了扩展API,这些API从提交者的证书中提取客户端身份,可用于访问控制决策,不论是基于客户端身份本身,组织身份或客户端身份属性。
For example an asset that is represented as a key/value may include the client’s identity as part of the value (for example as a JSON attribute indicating that asset owner), and only this client may be authorized to make updates to the key/value in the future. The client identity library extension APIs can be used within chaincode to retrieve this submitter information to make such access control decisions.
例如,表示为键/值的资产可能包含客户的身份作为值的一部分(如作为表示该资产所有者的JSON属性),并且只有这个客户端可能有权限来在将来更新这些 key/value。客户端身份库扩展API可以在链码中使用,以检索此提交者信息以做出此类访问控制决策。