git clone ...hyperledger/fabric-samples
git checkout v1.1.0
./scripts/bootstrap.sh
cd 到将要安装二进制文件的目录
curl -sSL https://goo.gl/6wtTN5 | bash -s 1.1.0
如有问题可将地址替换成https://github.com/hyperledger/fabric/blob/master/scripts/boot.sh
。
使用上面的命令可获得任何已发布版本的 HyperLedger Fabric,只需将 “1.1.0” 替换为想要安装的版本的标志符。
命令将下载六个特定于平台的二进制文件,分别是:cryptogen,configtxgen,configtxlator,peer,orderer,fabric-ca-client。
cd ..../fabric-samples/chaincode-docker-devmode
docker-compose -f docker-compose-simple.yaml up
上面命令使用单点式的 orderer 配置文件启动,在 “dev 模式” 中启动了 peer 节点。它还会启动两个额外的容器,一个用于智能合约环境和一个与智能合约交互的 cli。
创建和联接 channel 的命令嵌入到 cli 容器中,因此可以立即跳转到智能合约启动调用。
docker exec -it chaincode bash
cd sacc
go build
运行智能合约
CORE_PEER_ADDRESS=peer:7050 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc
安装实例化智能合约(后续版本的 dev 模式可能不再需要该步骤)。
peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0
peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C mycc
查询 a 账户资产
peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C mycc
智能合约调用者通过 “瞬态字段” 传递加密信息。
要测试 EncCC,需要首先生成一个 AES 256 位 key 使用 base64 编码的字符串,这样它可以作为 Json 传递给 Peer 节点智能合约调用的瞬态参数。
cd ..../enccc_example
govendor init
govendor add +external
生成加密和解密密钥
ENCKEY=`openssl rand 32 -base64` && DECKEY=$ENCKEY
进行加密键值对。前提是合约已经实例化,且 peer 节点已经加入了名为 “my-ch” 的 Channel。
peer chaincode invoke -n enccc -C my-ch -c '{"Args":["ENCRYPT","key1","value1"]}' -transient "{\"ENCKEY\":\"$ENCKEY\"}"
上面命令将使用随机的 IV(用来和密钥组合成密钥种子)进行加密。如果合约需要被多个 peer 节点支持,那么上面的操作是不可取的,它会导致各自背书中的读/写集相互冲突。可以指定 IV 的值进行确定性加密。
IV=`openssl rand 16 -base64`
peer chaincode invoke -n enccc -C my-ch -c '{"Args":["ENCRYPT","key2","value2"]}' --transient "{\"ENCKEY\":\"$ENCKEY\",\IV\":\"$IV\"}"
下面两个调用将产生相等的 KVS 写入结果,它可以被多个节点所支持。该值可以按如下方式检索,区别在于“key”。
peer chaincode query -n enccc -C my-ch -c '{"Args":["DECRYPT","key1"]}' --transient "{\"DECKEY\":\"$DECKEY\"}"
peer chaincode query -n enccc -C my-ch -c '{"Args":["DECRYPT","key2"]}' --transient "{\"DECKEY\":\"$DECKEY\"}"
注:上述使用链码查询操作,虽然瞬态字段的使用保证了内容不会被写入到账本中,但智能合约解密消息并将其放入请求响应中。调用会将结果保存在所有 Channel 读取器的账本中,而查询可以被丢弃,因此结果仍然是机密的。
测试签名并验证
用恰当曲线生成 ECDSA 密钥。
On Intel:
SIGKEY=`openssl ecparam -name prime256v1 -genkey | tail -n5 | base64 -w0` && VERKEY=$SIGKEY
On Mac:
SIGKEY=`openssl ecparam -name prime256v1 -genkey | tail -n5 | base64` && VERKEY=$SIGKEY
几种系统智能合约。
ESCC: 背书系统智能合约,用于支持建议;
QSCC: 查询系统智能合约,账本和其他与 Fabric 相关的查询;
VSCC: 验证系统智能合约,用于在提交时进行事务验证;
系统智能合约不需要安装和实例化,而是由 Peer 节点服务在启动时注册和部署的。
系统智能合约是一个用 Go 插件包编写的程序,并使用go build -buildmode=plugin
来构建。
现有的智能合约,如 QSCC,也可以作为某些特性的模板,例如访问控制,通常是通过系统智能合约实现的,现有的系统智能合约还可以作为日志和测试等方面的最佳实践的参考。
下面再参看一份比较简单的 go 语言智能合约
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
// SimpleAsset 实现一个简单的智能合约来管理资产。
type SimpleAsset struct {
}
// Init 在智能合约实例化过程中被调用来初始化任何数据。
// 注意,智能合约升级也调用这个方法来重置或迁移数据,
// 所以要小心避免在无意中破坏了账本数据的情况!
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
// Get the args from the transaction proposal
args := stub.GetStringArgs()
if len(args) != 2 {
return shim.Error("Incorrect arguments. Expecting a key and a value")
}
// 在这里通过调用 stub.PutState() 方法来存入资产信息
// 将资产的 value 和相关联 key 存入账本
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
}
return shim.Success(nil)
}
// 在智能合约上的每笔交易都会调用 Invoke。
// 每一个事务都是由 Init 方法创建的资产的 "get" 或 "set"。
// “set” 方法可以通过指定新的键-值对来创建新资产。
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// 从交易请求中获取方法名和请求参数数组。
fn, args := stub.GetFunctionAndParameters()
var result string
var err error
if fn == "set" {
result, err = set(stub, args)
} else { // assume 'get' even if fn is nil
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
}
// 返回成功结果
return shim.Success([]byte(result))
}
// 将资产(包括 key 和 value)存储在账本上。
// 如果 key 存在,它会用新 key 覆盖 value。
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != 2 {
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
}
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return "", fmt.Errorf("Failed to set asset: %s", args[0])
}
return args[1], nil
}
// 获取指定资产 key 的 value。
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != 1 {
return "", fmt.Errorf("Incorrect arguments. Expecting a key")
}
value, err := stub.GetState(args[0])
if err != nil {
return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
}
if value == nil {
return "", fmt.Errorf("Asset not found: %s", args[0])
}
return string(value), nil
}
// main 方法在实例化过程中启动容器中的智能合约。
func main() {
if err := shim.Start(new(SimpleAsset)); err != nil {
fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
}
}
除此之外,本章其余内容多是对peer chaincode
命令及参数的解释,其匀可以通过peer chaincode --help
查看到,另外本章还有一些较碎的知识点,如果对之前章节都认真实践了,也大体都能体会到,若仍想更详细的了解,还是前往作者原书。