在Fabric中交易受保护的资产
在Fabric中交易受保护的资产
官方文档:Secured asset transfer in Fabric
This tutorial will demonstrate how an asset can be represented and traded between organizations in a Hyperledger Fabric blockchain channel, while keeping details of the asset and transaction private using private data. Each on-chain asset is a non-fungible token (NFT) that represents a specific asset having certain immutable metadata properties (such as size and color) with a unique owner. When the owner wants to sell the asset, both parties need to agree to the same price before the asset is transferred. The private asset transfer smart contract enforces that only the owner of the asset can transfer the asset. In the course of this tutorial, you will learn how Fabric features such as state based endorsement, private data, and access control come together to provide secured transactions that are both private and verifiable.
本文档会演示如何在Hyperledger Fabric区块链的通道中的组织之间表示和交易资产,同时保持资产和交易的私有性。链上的每个资产都是一个NFT(non-fungible token,不可替代令牌),这个NFT代表具有唯一拥有者、具有某些不变的元数据属性(例如大小和颜色)的特定资产。当所有者想要出售资产时,双方需要在转让资产之前达成一致的价格。私有资产转让智能合约强制规定只有资产所有者才能转让资产。在本教程的过程中,您将学习如何把Fabric的特性(如基于状态的背书、私有数据和访问控制)组合在一起,以提供私有且可验证的安全交易。
This tutorial will deploy the secured asset transfer sample to demonstrate how to transfer a private asset between two organizations without publicly sharing data. You should have completed the task Install Samples, Binaries, and Docker Images.
本教程会部署 secured asset transfer sample 来演示如何在两个组织之间转移私有资产而不公开共享数据。
Scenario requirements(方案要求)
The private asset transfer scenario is bound by the following requirements:
- An asset may be issued by the first owner’s organization (in the real world issuance may be restricted to some authority that certifies an asset’s properties).
- Ownership is managed at the organization level (the Fabric permissioning scheme would equally support ownership at an individual identity level within an organization).
- The asset identifier and owner is stored as public channel data for all channel members to see.
- The asset metadata properties however are private information known only to the asset owner (and prior owners).
- An interested buyer will want to verify an asset’s private properties.
- An interested buyer will want to verify an asset’s provenance, specifically the asset’s origin and chain of custody. They will also want to verify that the asset has not changed since issuance, and that all prior transfers have been legitimate.
- To transfer an asset, a buyer and seller must first agree on the sales price.
- Only the current owner may transfer their asset to another organization.
- The actual private asset transfer must verify that the legitimate asset is being transferred, and verify that the price has been agreed to. Both buyer and seller must endorse the transfer.
私有资产交易方案受以下要求约束:
- 一个资产可能由第一个拥有者的组织发行(在现实世界中,可能只能由某个权威的可以证明资产属性的组织来发行)。
- 所有权是在组织级别进行管理的(Fabric许可方案将同样支持组织内个人身份级别的所有权)。
- 资产的ID和所有人作为可以被channel上的所有成员可见的公共数据被存储。
- 但是资产的元数据作为私有信息,只能被资产的所有者看到(也包括前所有者)。
- 有兴趣的买家将希望验证资产的私有属性。
- 有兴趣的买家将希望验证资产的出处,特别是资产的来源和产销监管链。他们还希望验证资产自从发行之后没有被篡改过,并且这个资产的前面的所有交易都是合法的。
- 在资产交易之前,买卖双方必须对资产的价格达成一致。
- 只有当前拥有者可以把他们的资产转让给另一个组织。
- 实际的私有资产交易必须确认合法资产正在转让,并确认双方已经对价格达成一致。买卖双方都必须对交易进行背书。
How privacy is maintained(如何保持隐私性)
The smart contract uses the following techniques to ensure that the asset properties remain private:
- The asset metadata properties are stored in the current owning organization’s implicit private data collection on the organization’s peers only. Each organization on a Fabric channel has a private data collection that their own organization can use. This collection is implicit because it does not need to be explicitly defined in the chaincode.
- Although a hash of the private properties is automatically stored on-chain for all channel members to see, a random salt is included in the private properties so that other channel members cannot guess the private data pre-image through a dictionary attack.
- Smart contract requests utilize the transient field for private data so that private data does not get included in the final on-chain transaction.
- Private data queries must originate from a client whose org id matches the peer’s org id, which must be the same as the asset owner’s org id.
智能合约使用以下技术来确保资产属性保持隐私:
- 资产元数据属性仅存储在当前拥有组织的节点的隐式私有数据集合中。每个组织在Fabric通道上都有一个只能由组织自己可以使用的私有数据集合。该集合是隐式的,因为不需要在链码中显式定义它。
- 虽然私有属性的hash值被自动保存在链上然后通道上所有成员都可以看到,但是私有属性中包含随机盐,因此其他的通道成员无法通过字典攻击猜测私有数据原像。
- 智能合约请求将瞬态字段用于私有数据,因此私有数据不会包含在最终的链上交易中。
- 对私有数据的查询必须来自组织ID与peer的组织ID一致的客户端,并且该客户端的ID必须与资产所有者的组织ID相同。
How the transfer is implemented(交易是如何实施的)
Before we start using the private asset transfer smart contract we will provide an overview of the transaction flow and how Fabric features are used to protect the asset created on the blockchain:
在我们开始使用私有资产交易的智能合约之前,我们将概述交易流程以及如何使用Fabric功能来保护在区块链上创建的资产:
Creating the asset(创建资产)
The private asset transfer smart contract is deployed with an endorsement policy that requires an endorsement from any channel member. This allows any organization to create an asset that they own without requiring an endorsement from other channel members. The creation of the asset is the only transaction that uses the chaincode level endorsement policy. Transactions that update or transfer existing assets will be governed by state based endorsement policies or the endorsement policies of private data collections. Note that in other scenarios, you may want an issuing authority to also endorse create transactions.
私有资产交易智能合约在部署时有一个背书策略,这个背书策略是:不需要任何渠道成员的认可。这样任何组织都可以创建属于他们自己的资产,而不需要其他的通道成员批准。资产的创建是唯一使用链码级背书策略的交易。更新或交易现有资产的交易将受基于状态认可政策或私有数据集合认可政策的约束。注意在其他情况下,你可能想要一个发行机构也认可创建交易。
The smart contract uses the following Fabric features to ensure that the asset can only be updated or transferred by the organization that owns the asset:
- When the asset is created, the smart contract gets the MSP ID of the organization that submitted the request, and stores the MSP ID as the owner in the asset key/value in the public chaincode world state. Subsequent smart contract requests to update or transfer the asset will use access control logic to verify that the requesting client is from the same organization. Note that in other scenarios, the ownership could be based on a specific client identity within an organization, rather than an organization itself.
- Also when the asset is created, the smart contract sets a state based endorsement policy for the asset key. The state based policy specifies that a peer from the organization that owns the asset must endorse a subsequent request to update or transfer the asset. This prevents any other organization from updating or transferring the asset using a smart contract that has been maliciously altered on their own peers.
智能合约使用以下的Fabric功能来确保资产只能由拥有它的组织进行更新和交易:
- 在资产创建后,智能合约会获取提交请求的组织的MSP ID ,并且把这个MSP ID作为资产的拥有者存储在公共链码世界状态的key/value中。后续的更新或交易资产的智能合约请求将会使用访问控制逻辑来验证发出请求的客户端来自同一个组织。请注意在其他情况下,所有权可以基于组织内的特定客户端身份,而不是组织本身。
- 同样,在创建资产时,智能合约会为资产密钥设置基于状态的背书策略。基于状态的策略指定拥有资产的组织中的peer节点必须认可后续的更新或转让资产的请求。这样可以防止任何其他组织利用恶意更改的智能合约来更改和交易资产。
Agreeing to the transfer(同意交易)
After a asset is created, channel members can use the smart contract to agree to transfer the asset:
- The owner of the asset can change the description in the public ownership record, for example to advertise that the asset is for sale. Smart contract access control enforces that this change needs to be submitted from a member of the asset owner organization. The state based endorsement policy enforces that this description change must be endorsed by a peer from the owner’s organization.
在资产创建之后,通道上的成员可以使用智能合约来同意交易资产:
- 资产的拥有者可以更改公共记录上的描述,例如广告说这个资产要卖出。智能合约访问控制强制要求此更改需要从拥有此资产的组织的成员提交。基于状态的背书策略规定,此描述更改必须得到拥有资产的组织的成员确认。
The asset owner and the asset buyer agree to transfer the asset for a certain price:
- The price agreed to by the buyer and the seller is stored in each organization’s implicit private data collection. The private data collection keeps the agreed price secret from other members of the channel. The endorsement policy of the private data collection ensures that the respective organization’s peer endorsed the price agreement, and the smart contract access control logic ensures that the price agreement was submitted by a client of the associated organization.
- A hash of each price agreement is stored on the ledger. The two hashes will match only if the two organizations have agreed to the same price. This allows the organizations to verify that they have come to agreement on the transfer details before the transfer takes place. A random trade id is added to the price agreement, which serves as a salt to ensure that other channel members can not use the hash on the ledger to guess the price.
资产所有者和资产购买者同意以一定价格交易资产:
- 买卖双方都同意的价格被存储在他们各自的隐式私有数据集合中。私有数据集合保证成交价格对通道上的其他成员来说是私密的。私有数据集合背书策略确保各自组织的peer节点对价格达成了一致,智能合约访问控制逻辑确保价格协议是由与组织有关联的客户端提交的。
- 交易双方生成的价格协议的hash值被存储在了账本上。只有在两个组织同意同一个价格时,这两个hash值才会匹配。这使组织能够在转移发生之前验证他们已就转移细节达成协议。价格协议中添加了随机交易ID,这是确保通道其他成员不能使用账本上的哈希值来猜测价格的一种盐。
Transferring the asset(交易资产)
After the two organizations have agreed to the same price, the asset owner can use the transfer function to transfer the asset to the buyer:
- Smart contract access control ensures that the transfer must be initiated by a member of the organization that owns the asset.
- The transfer function verifies that the asset’s private immutable properties passed to the transfer function matches the on chain hash of the asset data in private collection, to ensure that the asset owner is selling the same asset that they own.
- The transfer function uses the hash of the price agreement on the ledger to ensure that both organizations have agreed to the same price.
- If the transfer conditions are met, the transfer function adds the asset to the implicit private data collection of the buyer, and deletes the asset from the collection of the seller. The transfer also updates the owner in the public ownership record.
- Because of the endorsement policies of the seller and buyer implicit data collections, and the state based endorsement policy of the public record (requiring the seller to endorse), the transfer needs to be endorsed by peers from both buyer and seller.
- The state based endorsement policy of the public asset record is updated so that only a peer of the new owner of the asset can update or sell their new asset.
- The price agreements are also deleted from both the seller and buyer implicit private data collection, and a sales receipt is created in each private data collection.
在两个组织已经对相同的价格达成一致之后,资产的拥有者可以使用转让方法来把资产转让给买方:
- 智能合约访问控制确保转账必须由拥有资产的组织的成员来发起
- 转账方法验证传递给它的资产的私有不可变属性是否与私有集合中资产数据的链上哈希匹配,确保资产所有者在出售他们拥有的相同的资产。
- 转账方法使用账本上的价格协议的hash值来确保两个组织已经同意了相同的价格。
- 如果满足转让条件,则转账函数会将资产添加到买方的隐式私有数据集合中,并且从卖方的集合中删除这个资产。转让还会更新公共所有权记录中的资产的所有者。
- 因为买卖双方隐式数据集合的背书策略和公共数据的基于状态的背书策略(要求卖方确认),所以交易需要得到买卖双方节点的认可。
- 公共资产记录的基于状态的背书策略需要更新,这样只有新的所有者的peer节点才能够更新和出售他们的新资产。
- 价格协议也从买卖双方隐式私人数据集合中删除,并且在每个私人数据集合中创建销售收据。
Running the private asset transfer smart contract(运行私有资产转账智能合约)
You can use the Fabric test network to run the private asset transfer smart contract. The test network contains two peer organizations, Org1 and Org1, that operate one peer each. In this tutorial, we will deploy the smart contract to a channel of the test network joined by both organizations. We will first create an asset that is owned by Org1. After the two organizations agree on the price, we will transfer the asset from Org1 to Org2.
我们可以使用Fabric的test网络来运行私有资产转账智能合约。(省略。。。)我们先通过Org1组织创建一个资产,在两个组织对价格达成一致之后,我们把资产由Org1组织交易到Org2组织。
Deploy the test network(部署测试网络)
请参考:Fabric测试网络使用
Deploy the smart contract(部署智能合约)
You can use the test network script to deploy the secured asset transfer smart contract to the channel. Run the following command to deploy the smart contract to mychannel
:
你可以使用测试网络脚本来部署安全资产交易智能合约到通道上。运行下面的命令来部署智能合约到mychannel
:
1 | ./network.sh deployCC -ccn secured -ccp ../asset-transfer-secured-agreement/chaincode-go/ -ccl go -ccep "OR('Org1MSP.peer','Org2MSP.peer')" |
Note that we are using the -ccep
flag to deploy the smart contract with an endorsement policy of "OR('Org1MSP.peer','Org2MSP.peer')"
. This allows either organization to create an asset without receiving an endorsement from the other organization.
注意我们使用-ccep
标志来部署智能合约,这个智能合约有一个背书策略"OR('Org1MSP.peer','Org2MSP.peer')"
。者允许任意一个组织在创建一个新的资产时不需要其他组织的确认。
Set the environment variables to operate as Org2(设置环境变量以Org2的管理员身份操作)
略,可以使用两个终端分别以Org1和Org2组织管理员的身份操作peer。环境变量设置参考:[Fabric测试网络使用](https://guozhe001.github.io/2024/11/22/blockchain/fabric/how_to/Fabric测试网络使用/
[)
Create an asset(创建一个资产)
Any channel member can use the smart contract to create an asset that is owned by their organization. The details of the asset will be stored in a private data collection, and can only accessed by the organization that owns the asset. A public record of the asset, its owner, and a public description is stored on the channel ledger. Any channel member can access the public ownership record to see who owns the asset, and can read the description to see if the asset is for sale.
任何一个通道成员都可以使用这个智能合约来创建一个属于组织自己的资产。资产的详情会存储在私有数据集合中,并且只能由拥有资产的组织访问。资产,其所有者和公共描述的公共记录存储在通道账本中。任何通道成员都可以访问公共记录来查看谁拥有这个资产,并且可以查看描述来判断资产是否在出售。
Operate from the Org1 terminal(通过Org1的终端操作)
Before we create the asset, we need to specify the details of what our asset will be. Issue the following command to create a JSON that will describe the asset. The "salt"
parameter is a random string that would prevent another member of the channel from guessing the asset using the hash on the ledger. If there was no salt, a user could theoretically guess asset parameters until the hash of the of the guess and the hash on the ledger matched (this is known as a dictionary attack). This string is encoded in Base64 format so that it can be passed to the creation transaction as transient data.
在创建资产之前,我们需要指定资产的详细信息。通过下面的命令来创建一个JSON来描述资产。"salt"
参数是一个随机字符串来防止通道上的另一个成员通过账本上的hash值来猜测这个资产。如果没有盐,理论上用户可以猜测资产参数,直到猜测的哈希值和账本的哈希值匹配(这称为字典攻击)为止。该字符串以Base64格式编码,因此可以作为临时数据传递给创建交易。
1 | export ASSET_PROPERTIES=$(echo -n "{\"object_type\":\"asset_properties\",\"asset_id\":\"asset1\",\"color\":\"blue\",\"size\":35,\"salt\":\"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\"}" | base64 | tr -d \\n) |
We can now use the following command to create a asset that belongs to Org1:
我们现在可以使用下面的命令来创建一个属于Org1组织的资产:
1 | peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"CreateAsset","Args":["asset1", "A new asset for Org1MSP"]}' --transient "{\"asset_properties\":\"$ASSET_PROPERTIES\"}" |
We can can query the Org1 implicit data collection to see the asset that was created:
我们可以查询Org1组织的隐私数据集合来查看创建的资产:
1 | peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"GetAssetPrivateProperties","Args":["asset1"]}' |
When successful, the command will return the following result:
如果成功,命令会返回下面的结果:
1 | { |
We can also query the ledger to see the public ownership record:
我们还可以查询账本来查看公共记录:
1 | peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"ReadAsset","Args":["asset1"]}' |
The command will return the record that the asset1 is owned by Org1:
这个命令会返回数据说明资产asset1属于Org1:
1 | { |
Because the market for assets is hot, Org1 wants to flip this asset and put it up for sale. As the asset owner, Org1 can update the public description to advertise that the asset is for sale. Run the following command to change the asset description:
因为资产市场很热,所以Org1希望翻转该资产并将其出售。作为资产的拥有者,Org1组织可以更新公共描述来广告说资产在出售。运行下面的命令来更新资产描述:
1 | peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"ChangePublicDescription","Args":["asset1","This asset is for sale"]}' |
Query the ledger again to see the updated description:
重新查询账本来查看更新后的描述:
1 | peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"ReadAsset","Args":["asset1"]}' |
We can now see that the asset is for sale:
我们现在可以看到这个资产在出售:
1 | { |
Figure 1: When Org1 creates an asset that they own, the asset details are stored in the Org1 implicit data collection on the Org1 peer. The public ownership record is stored in the channel world state, and is stored on both the Org1 and Org2 peers. A hash of the asset key and a hash the asset details are also visible in the channel world state and are stored on the peers of both organizations.
图片1:当组织Org1创建了一个属于他们的资产时,资产的细节保存在属于组织Org1的peer的隐式数据集合中。公共的记录被保存在通道的世界状态,并且保存在组织Org1和Org2的peer节点。资产的key的hash和资产详情的hash在通道的世界状态是可访问的,并且存储在所有组织的peer节点。
Operate from the Org2 terminal(通过组织Org2的终端操作)
If we operate from the Org2 terminal, we can use the smart contract query the public asset data:
如果我们通过组织Org2的终端操作,我们可以使用智能合约来查询公共的资产数据:
1 | peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"ReadAsset","Args":["asset1"]}' |
From this query, Org2 learns that asset1 is for sale(通过这个查询,组织Org2知道了资产asset1正在出售):
1 | { |
In a real chaincode you may want to query for all assets for sale, by using a JSON query, or by creating a different sale key and using a key range query to find the assets currently for sale. Any changes to the public description of the asset owned by Org1 needs to be endorsed by Org1. The endorsement policy is reinforced by an access control policy within the chaincode that any update needs to be submitted by the organization that owns the asset. Lets see what happens if Org2 tried to change the public description as a prank:
*在真实的链码中,您可能希望通过使用JSON查询或通过创建其他销售密钥并使用密钥的范围来查找当前待售资产,以查询所有待售资产。*所有属于组织Org1的资产的公共描述在进行任何更改时都必须得到组织Org1的认可。链码中的访问控制策略加强了背书策略,任何更新都必须由来自于资产拥有者的组织来提交。让我们看看如果Org2试图以恶作剧方式更改公共描述会发生什么:
1 | peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"ChangePublicDescription","Args":["asset1","the worst asset"]}' |
The smart contract does not allow Org2 to access the public description of the asset.
智能合约不允许组织Org2访问这个资产的公共描述。
1 | Error: endorsement failure during invoke. response: status:500 message:"a client from Org2MSP cannot update the description of a asset owned by Org1MSP" |
Agree to sell the asset(允许出售资产)
To sell an asset, both the buyer and the seller must agree on an asset price. Each party stores the price that they agree to in their own private data collection. The private asset transfer smart contract enforces that both parties need to agree to the same price before the asset can be transferred.
为了出售资产。买卖双方都必须同意相同的资产价格。每一方都需要在他们的私有数据集合中保存他们同意的价格。私有资产交易智能合约强制双方必须同意相同的价格才能转让资产。
Agree to sell as Org1(以Org1的身份同意出售)
Operate from the Org1 terminal. Org1 will agree to set the asset price as 110 dollars. The trade_id
is used as salt to prevent a channel member that is not a buyer or a seller from guessing the price. This value needs to be passed out of band, through email or other communication, between the buyer and the seller. The buyer and the seller can also add salt to the asset key to prevent other members of the channel from guessing which asset is for sale.
通过组织Org1的终端进行操作。Org1同意将资产价格设置为110美元。trade_id用作盐以防止不是买家或卖家的通道成员猜测价格。这个价格需要通过买卖双方之间以电子邮件或其他通信方式在账本外传递。买卖双方还可以对资产的key进行“加盐”,以防止渠道的其他成员猜测要出售的资产。
1 | export ASSET_PRICE=$(echo -n "{\"asset_id\":\"asset1\",\"trade_id\":\"109f4b3c50d7b0df729d299bc6f8e9ef9066971f\",\"price\":110}" | base64) |
We can query the Org1 private data collection to read the agreed to selling price:
我们可以查询组织Org1的私有数据集合来读取同意的销售价格:
1 | peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"GetAssetSalesPrice","Args":["asset1"]}' |
Agree to buy as Org2(以Org2的身份同意购买)
Operate from the Org2 terminal. Run the following command to verify the asset properties before agreeing to buy. The asset properties and salt would be passed out of band, through email or other communication, between the buyer and seller.
通过Org2的终端操作。运行下面的命令在同意购买之前验证资产的属性。资产的属性和盐会在区块链账本外在买卖双方之间通过邮件或者其他方式进行传递。
1 | export ASSET_PROPERTIES=$(echo -n "{\"object_type\":\"asset_properties\",\"asset_id\":\"asset1\",\"color\":\"blue\",\"size\":35,\"salt\":\"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\"}" | base64) |
Run the following command to agree to buy asset1 for 100 dollars. As of now, Org2 will agree to a different price than Org2. Don’t worry, the two organizations will agree to the same price in a future step. However, we we can use this temporary disagreement as a test of what happens if the buyer and the seller agree to a different price. Org2 needs to use the same trade_id
as Org1.
使用下面的命令来同意以100刀的价格购买asset1。现在Org2同意了一个与Org1不同的价格。不要担心,这两个组织会在接下来的步骤同意相同的价格。但是我们通过临时的分歧来测试如果买方和卖方同意了不同的价格将会发生什么。Org2需要使用与Org1相同的 trade_id
。
1 | export ASSET_PRICE=$(echo -n "{\"asset_id\":\"asset1\",\"trade_id\":\"109f4b3c50d7b0df729d299bc6f8e9ef9066971f\",\"price\":100}" | base64) |
You can read the agreed purchase price from the Org2 implicit data collection:
您可以从Org2隐式数据集合中读取约定的购买价格:
1 | peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"GetAssetBidPrice","Args":["asset1"]}' |
Figure 2: After Org1 and Org2 agree to transfer the asset, the price agreed to by each organization is stored in their private data collections. A composite key for the seller and the buyer is used to prevent a collision with the asset details and asset ownership record. The price that is agreed to is only stored on the peers of each organization. However, the hash of both agreements is stored in the channel world state on every peer joined to the channel.
图2:在Org1和Org2同意交易这个资产,每个组织的同意的价格被存储在他们的私有数据集合中。买卖双方的组合键用于防止与资产详细信息和资产所有权记录发生冲突。已经同意的价格只是存储在双方组织的peer节点。但是两个协议的hash值被存储在加入通道的所有peer节点的通道的世界状态中。
Transfer the asset from Org1 to Org2(把资产从Org1交易到Org2)
After both organizations have agreed to their price, Org1 can attempt to transfer the asset to Org2. The private asset transfer function in the smart contract uses the hash on the ledger to check that both organizations have agreed to the same price. The function will also use the hash of the private asset details to check that the asset that is transferred is the same asset that Org1 owns.
在双方组织都同意了他们的价格之后,Org1可以尝试将资产交易给Org2。在智能合约中的私有资产交易方法使用账本上的hash来检查两个组织是否同意了相同的价格。这个方法也会使用私有资产详情的hash值来检查被交易的资产与属于Org1的资产是同一笔资产。
Transfer the asset as Org1(以Org1的身份交易资产)
Operate from the Org1 terminal. The owner of the asset needs to initiate the transfer. Note that the command below uses the --peerAddresses
flag to target the peers of both Org1 and Org2. Both organizations need to endorse the transfer. Also note that the asset properties and price are passed in the transfer request as transient properties. These are passed so that the current owner can be sure that the correct asset is transferred for the correct price. These properties will be checked against the on-chain hashes by both endorsers.
通过Org1的终端操作。资产的所有者需要发起这个交易。注意下面的命令使用--peerAddresses
标志来指定Org1和Org2的peer节点。两个组织都需要确认这个交易。另外请注意,资产属性和价格在交易请求中作为临时属性传递。传递这些是为了当前的拥有者可以确保以当前的价格来转让当前的资产。两个背书者将对照链上哈希检查这些属性。
1 | peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"TransferAsset","Args":["asset1","Org2MSP"]}' --transient "{\"asset_properties\":\"$ASSET_PROPERTIES\",\"asset_price\":\"$ASSET_PRICE\"}" --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt |
Because the two organizations have not agreed to the same price, the transfer cannot be completed:
因为两个组织同意的价格不一致,这个交易不能完成:
1 | Error: endorsement failure during invoke. response: status:500 message:"failed transfer verification: hash 0fc413250501855af7c9896af00993b973510995fb10d56cddbb85ca47bd5dba for passed price JSON {\"asset_id\":\"asset1\",\"trade_id\":\"109f4b3c50d7b0df729d299bc6f8e9ef9066971f\",\"price\":110} does not match on-chain hash 84b0d57eaa5c77076483ae8f482c96a64912c47df5541451e94fb7698bf37ee9, buyer hasn't agreed to the passed trade id and price" |
As a result, Org1 and Org2 come to a new agreement on the price at which the asset will be purchased. Org1 drops the price of the asset to 100:
结果,Org1和Org2就购买资产的价格达成了新协议。 Org1将资产价格降至100:
1 | export ASSET_PRICE=$(echo -n "{\"asset_id\":\"asset1\",\"trade_id\":\"109f4b3c50d7b0df729d299bc6f8e9ef9066971f\",\"price\":100}" | base64) |
Now that the buyer and seller have agreed to the same price, Org1 can transfer the asset to Org2.
现在买卖双方已经同意了相同的价格,Org1可以将资产交易给Org2。
1 | peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"TransferAsset","Args":["asset1","Org2MSP"]}' --transient "{\"asset_properties\":\"$ASSET_PROPERTIES\",\"asset_price\":\"$ASSET_PRICE\"}" --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt |
You can query the asset ownership record to verify that the transfer was successful.
你可以查询资产的归属信息来验证交易已经成功.
1 | peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"ReadAsset","Args":["asset1"]}' |
The record now lists Org2 as the asset owner:
1 | { |
Figure 3: After the asset is transferred, the asset details are placed in the Org2 implicit data collection and deleted from the Org1 implicit data collection. As a result, the asset details are now only stored on the Org2 peer. The asset ownership record on the ledger is updated to reflect that the asset is owned by Org1.
图3:在资产交易之后,资产细节存在于Org2的隐式数据集合并且从Org1的隐式数据集合中被删除。结果,资产的私有信息值保存在Org2的peer节点。在账本上的资产的归属信息也被更新,以反映资产归Org2所有。
Update the asset description as Org2(以Org2的身份更新资产描述)
Operate from the Org2 terminal. Now that Org2 owns the asset, we can read the asset details from the Org2 implicit data collection:
通过Org2的终端操作,现在Org2拥有这个资产,我们可以通过Org2的隐式数据集合读取资产的详情:
1 | peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"GetAssetPrivateProperties","Args":["asset1"]}' |
Org2 can now update the asset public description(Org2现在可以更新资产的公共描述):
1 | peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"ChangePublicDescription","Args":["asset1","This asset is not for sale"]}' |
Query the ledger to verify that the asset is no longer for sale:
1 | peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n secured -c '{"function":"ReadAsset","Args":["asset1"]}' |
Clean up
When you are finished transferring assets, you can bring down the test network. The command will remove all the nodes of the test network, and delete any ledger data that you created:
1 | ./network.sh down |
流程图
自己的理解画的流程图如下: