Hyperledger Fabric 整理体系:
- Fabric 整体架构和交易流程
- Fabric Samples 在Mac部署和运行
- Fabric Samples 运行日志记录
- Fabric Samples BYFN 命令行详解
- Fabric Samples 运行时架构和Java客户端具体实现
各项解释如下:
- APP:代表一个客户端或者SDK,作用是创建交易并获取到足够的背书之后向Orderer排序服务节点提交交易请求。(peer和orderer提供了gRPC远程访问接口,供客户端调用)
- CA:负责对网络中所有证书进行管理,提供标准的KPI服务
- MSP:为客户端和peer提供证书的系统抽象组件
- Channel:通道提供了一种通讯机制,将peers和orderer连接在一起,形成一个具有保密性的通讯链路;将一个大网络分割成不同私有子网,进行数据隔离
- Orderer:对客户端提交的交易请求进行排序,之后生成区块广播给通道内所有peer节点
- Org1:代表联盟中的某-组织
- Peer:表示组织中的节点;peer节点以区块的形式从orderer排序服务节点接收有序状态更新,维护状态和账本。fabric中Peer节点可划分如下:
- Endorsing Peer:根据指定的策略调用智能合约,对结果进行背书,返回提案相应到客户端
- Committing Peer:验证数据并保存到账本中
- Anchor Peer:跨组织通讯
- Leading Peer:做为组织内所有节点的代表连接到Orderer排序服务节点,将从排序服务节点接收到的批量区块广播给组织内的其他节点
- Chaincode:链码,部署在fabric的网络节点中,独立运行在具有安全特性的受保护容器中,以gRPC协议与相应Peer节点进行通讯,提供相应的API与账本数据进行交互
- Ledger:有排序服务构建的一个全部有序的交易哈希链块,保存在所有的Peer节点中。
Java SDK提供了一种执行用户链码,查询块数据,在通道进行交易及模拟事件的方式。
此模式下,区块链网络由两个组织,每个组织包含两个结点,和一个order服务;将展示怎样创建和初始化通道,安装和部署链码,和执行调用和查询操作。
基本原理
在fabric区块链中,应用通过结点RPC协议接口访问链码。
类似于Shim API对链码通讯协议的封装,JavaSDK提供了对结点RPC协议接口封装,其入口类为HFClient;对链码的交易和查询操作则封装在channel类中。
简单实例
-
实现User接口
HFClient访问fabric网络身份使用User接口实现。
一个用户的身份由它的证书来标识的,同时交易还需要证书对应的私钥,因此LocalUser的核心逻辑就是利用指定的证书和私钥PEM文件满足User接口的要求。
访问链码
创建HFClient实例,然后获取通道对象,就可以查询链码或者提交链码交易。
命令行操作: hyperledger fabric byfn up详解
代码具体实现
- 运行fabric-samples中fabcar,创建区块链网络
- 新建Java工程,引入fabric-gateway-java,实现具体实现
pom.xml
oss-sonatype
OSS Sonatype
https://oss.sonatype.org/content/repositories/snapshots
org.hyperledger.fabric
fabric-gateway-java
1.4.1-SNAPSHOT
加入Admins角色:
public class EnrollAdmin {
static {
System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
}
public static void main(String[] args) throws Exception {
// Create a CA client for interacting with the CA.
Properties props = new Properties();
props.put("pemFile",
"/Users/macserver/Documents/BlockChain/src/github.com/hyperledger/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem");
props.put("allowAllHostNames", "true");
HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
caClient.setCryptoSuite(cryptoSuite);
// Create a wallet for managing identities
Wallet wallet = Wallet.createFileSystemWallet(Paths.get("wallet"));
// Check to see if we've already enrolled the admin user.
boolean adminExists = wallet.exists("admin");
if (adminExists) {
System.out.println("An identity for the admin user \"admin\" already exists in the wallet");
return;
}
// Enroll the admin user, and import the new identity into the wallet.
final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest();
enrollmentRequestTLS.addHost("localhost");
enrollmentRequestTLS.setProfile("tls");
Enrollment enrollment = caClient.enroll("admin", "adminpw", enrollmentRequestTLS);
Identity user = Identity.createIdentity("Org1MSP", enrollment.getCert(), enrollment.getKey());
wallet.put("admin", user);
System.out.println("Successfully enrolled user \"admin\" and imported it into the wallet");
}
}
加入user1角色:
public class RegisterUser {
static {
System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
}
public static void main(String[] args) throws Exception {
// Create a CA client for interacting with the CA.
Properties props = new Properties();
props.put("pemFile",
"/Users/macserver/Documents/BlockChain/src/github.com/hyperledger/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem");
props.put("allowAllHostNames", "true");
HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
caClient.setCryptoSuite(cryptoSuite);
// Create a wallet for managing identities
Wallet wallet = Wallet.createFileSystemWallet(Paths.get("wallet"));
// Check to see if we've already enrolled the user.
boolean userExists = wallet.exists("user1");
if (userExists) {
System.out.println("An identity for the user \"user1\" already exists in the wallet");
return;
}
userExists = wallet.exists("admin");
if (!userExists) {
System.out.println("\"admin\" needs to be enrolled and added to the wallet first");
return;
}
Identity adminIdentity = wallet.get("admin");
User admin = new User() {
@Override
public String getName() {
return "admin";
}
@Override
public Set getRoles() {
return null;
}
@Override
public String getAccount() {
return null;
}
@Override
public String getAffiliation() {
return "org1.department1";
}
@Override
public Enrollment getEnrollment() {
return new Enrollment() {
@Override
public PrivateKey getKey() {
return adminIdentity.getPrivateKey();
}
@Override
public String getCert() {
return adminIdentity.getCertificate();
}
};
}
@Override
public String getMspId() {
return "Org1MSP";
}
};
// Register the user, enroll the user, and import the new identity into the wallet.
RegistrationRequest registrationRequest = new RegistrationRequest("user1");
registrationRequest.setAffiliation("org1.department1");
registrationRequest.setEnrollmentID("user1");
String enrollmentSecret = caClient.register(registrationRequest, admin);
Enrollment enrollment = caClient.enroll("user1", enrollmentSecret);
Identity user = Identity.createIdentity("Org1MSP", enrollment.getCert(), enrollment.getKey());
wallet.put("user1", user);
System.out.println("Successfully enrolled user \"user1\" and imported it into the wallet");
}
}
客户端:查询及添加新数据
public class ClientApp {
static {
System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
}
public static void main(String[] args) throws Exception {
// Load a file system based wallet for managing identities.
Path walletPath = Paths.get("wallet");
Wallet wallet = Wallet.createFileSystemWallet(walletPath);
// load a CCP
Path networkConfigPath = Paths.get("/Users/macserver/Documents/BlockChain/src/github.com/hyperledger/fabric-samples/fabcar", "..", "first-network", "connection-org1.yaml");
Gateway.Builder builder = Gateway.createBuilder();
builder.identity(wallet, "user1").networkConfig(networkConfigPath).discovery(true);
// create a gateway connection
try (Gateway gateway = builder.connect()) {
// get the network and contract
Network network = gateway.getNetwork("mychannel");
Contract contract = network.getContract("fabcar");
byte[] result;
result = contract.evaluateTransaction("queryAllCars");
System.out.println(new String(result));
contract.submitTransaction("createCar", "CAR10", "VW", "Polo", "Grey", "Mary");
result = contract.evaluateTransaction("queryCar", "CAR10");
System.out.println(new String(result));
contract.submitTransaction("changeCarOwner", "CAR10", "Archie");
result = contract.evaluateTransaction("queryCar", "CAR10");
System.out.println(new String(result));
}
}
}
实现方式 二
如果跳过gateway,直接使用fabric-sdk-java,数据查询-实现代码如下:
private static void queryAllCars() throws Exception {
HFClient hfClient = HFClient.createNewInstance();
CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
hfClient.setCryptoSuite(cryptoSuite);
Enrollment enrollment = new X509Enrollment(getPrivateKey(Paths.get("wallet/user1/user1-priv")), getCertificate(certificate_user1));
User user = new User() {
@Override
public String getName() {
return "gateway";
}
@Override
public Set getRoles() {
return Collections.emptySet();
}
@Override
public String getAccount() {
return "";
}
@Override
public String getAffiliation() {
return "";
}
@Override
public Enrollment getEnrollment() {
return enrollment;
}
@Override
public String getMspId() {
return "Org1MSP";
}
};
hfClient.setUserContext(user);
NetworkConfig networkConfig = NetworkConfig.fromYamlFile(new File(path + "first-network/connection-org1.yaml"));
Channel channel = hfClient.newChannel("mychannel");
List peerNames = networkConfig.getClientOrganization().getPeerNames();
for (String name : peerNames) {
String url = networkConfig.getPeerUrl(name);
Properties props = networkConfig.getPeerProperties(name);
System.err.println("xxxxxxxxx:" + name + "..." + url);
Peer peer = hfClient.newPeer(name, url, props);
Channel.PeerOptions peerOptions = Channel.PeerOptions.createPeerOptions().setPeerRoles(EnumSet.allOf(Peer.PeerRole.class));
channel.addPeer(peer, peerOptions);
}
channel.initialize();
QueryByChaincodeRequest request = QueryByChaincodeRequest.newInstance(user);
request.setChaincodeID(getChaincodeId("fabcar"));
request.setFcn("queryAllCars");
request.setArgs("");
Collection responses = channel.queryByChaincode(request, Collections.singletonList(channel.getPeers().iterator().next()));
ProposalResponse response = responses.iterator().next();
byte[] bytes = response.getChaincodeActionResponsePayload();
System.err.println("xxxxxxx:" + new String(bytes));
}
private static String getCertificate(String certificate) {
BufferedReader certReader = new BufferedReader(new StringReader(certificate));
return certReader.lines().collect(Collectors.joining("\n", "", "\n"));
}
private static PrivateKey getPrivateKey(Path pemFile) {
try {
PEMParser parser = new PEMParser(Files.newBufferedReader(pemFile));
Object key = parser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
return converter.getPrivateKey((PrivateKeyInfo) key);
} catch (IOException e) {
System.err.println("private key 获取失败");
return null;
}
}
private static ChaincodeID getChaincodeId(String chaincodeId) {
return ChaincodeID.newBuilder()
.setName(chaincodeId)
.build();
}