您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“如何利用Hyperledger Fabric的SDK來開發REST API服務器”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“如何利用Hyperledger Fabric的SDK來開發REST API服務器”這篇文章吧。
整個系統包含兩個物理節點:
Fabric節點:運行Fabric示例中的First Network,并且實例化了Fabcar鏈碼
API服務器節點:運行REST API Server代碼供外部訪問
下面是部署在AWS上的兩個節點實例的情況:
首先參考官方文檔安裝hyperledger fabric。
然后運行腳本fabcar/startFabric.sh
:
cd fabric-samples/fabcar ./startFabric.sh
上述腳本運行之后,我們就得到一個正常運轉的Hyperledger Fabric網絡(著名的演示網絡First Network),包含2個機構/4個對等節點,通道為mychannel,鏈碼Fabcar安裝在全部4個對等節點上并且在mychannel上激活。賬本中有10條車輛記錄,這是調用合約的initLedger
方法的結果。
現在我們為REST API Server準備身份標識數據。使用fabcar/javascript創建一個用戶標識user1,我們將在REST API Server中使用這個身份標識:
cd javascript npm install node enrollAdmin.js node registerUser.js ls wallet/user1
運行結果如下:
現在Rest API Server需要的東西都備齊了:
org1的連接配置文件:first-network/connection-org1.json
Node.js包文件:fabcar/package.json
User1身份錢包:fabcar/javascript/wallet/user1/
后面我們會把這些數據文件拷貝到Rest API Server。
我們使用ExressJS來開發API服務,利用query.js和invoke.js中的代碼實現與fabric交互的邏輯。API設計如下:
GET /api/queryallcars:返回全部車輛記錄
GET /api/query/CarID:返回指定ID的車輛記錄
POST /api/addcar/:添加一條新的車輛記錄
PUT /api/changeowner/CarID:修改指定ID的車輛記錄
apiserver.js代碼如下:
var bodyParser = require('body-parser'); var app = express(); app.use(bodyParser.json()); // Setting for Hyperledger Fabric const { FileSystemWallet, Gateway } = require('fabric-network'); const path = require('path'); const ccpPath = path.resolve(__dirname, '.', 'connection-org1.json'); app.get('/api/queryallcars', async function (req, res) { try { // Create a new file system based wallet for managing identities. const walletPath = path.join(process.cwd(), 'wallet'); const wallet = new FileSystemWallet(walletPath); console.log(`Wallet path: ${walletPath}`); // Check to see if we've already enrolled the user. const userExists = await wallet.exists('user1'); if (!userExists) { console.log('An identity for the user "user1" does not exist in the wallet'); console.log('Run the registerUser.js application before retrying'); return; } // Create a new gateway for connecting to our peer node. const gateway = new Gateway(); await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: false } }); // Get the network (channel) our contract is deployed to. const network = await gateway.getNetwork('mychannel'); // Get the contract from the network. const contract = network.getContract('fabcar'); // Evaluate the specified transaction. // queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4') // queryAllCars transaction - requires no arguments, ex: ('queryAllCars') const result = await contract.evaluateTransaction('queryAllCars'); console.log(`Transaction has been evaluated, result is: ${result.toString()}`); res.status(200).json({response: result.toString()}); } catch (error) { console.error(`Failed to evaluate transaction: ${error}`); res.status(500).json({error: error}); process.exit(1); } }); app.get('/api/query/:car_index', async function (req, res) { try { // Create a new file system based wallet for managing identities. const walletPath = path.join(process.cwd(), 'wallet'); const wallet = new FileSystemWallet(walletPath); console.log(`Wallet path: ${walletPath}`); // Check to see if we've already enrolled the user. const userExists = await wallet.exists('user1'); if (!userExists) { console.log('An identity for the user "user1" does not exist in the wallet'); console.log('Run the registerUser.js application before retrying'); return; } // Create a new gateway for connecting to our peer node. const gateway = new Gateway(); await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: false } }); // Get the network (channel) our contract is deployed to. const network = await gateway.getNetwork('mychannel'); // Get the contract from the network. const contract = network.getContract('fabcar'); // Evaluate the specified transaction. // queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4') // queryAllCars transaction - requires no arguments, ex: ('queryAllCars') const result = await contract.evaluateTransaction('queryCar', req.params.car_index); console.log(`Transaction has been evaluated, result is: ${result.toString()}`); res.status(200).json({response: result.toString()}); } catch (error) { console.error(`Failed to evaluate transaction: ${error}`); res.status(500).json({error: error}); process.exit(1); } }); app.post('/api/addcar/', async function (req, res) { try { // Create a new file system based wallet for managing identities. const walletPath = path.join(process.cwd(), 'wallet'); const wallet = new FileSystemWallet(walletPath); console.log(`Wallet path: ${walletPath}`); // Check to see if we've already enrolled the user. const userExists = await wallet.exists('user1'); if (!userExists) { console.log('An identity for the user "user1" does not exist in the wallet'); console.log('Run the registerUser.js application before retrying'); return; } // Create a new gateway for connecting to our peer node. const gateway = new Gateway(); await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: false } }); // Get the network (channel) our contract is deployed to. const network = await gateway.getNetwork('mychannel'); // Get the contract from the network. const contract = network.getContract('fabcar'); // Submit the specified transaction. // createCar transaction - requires 5 argument, ex: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom') // changeCarOwner transaction - requires 2 args , ex: ('changeCarOwner', 'CAR10', 'Dave') await contract.submitTransaction('createCar', req.body.carid, req.body.make, req.body.model, req.body.colour, req.body.owner); console.log('Transaction has been submitted'); res.send('Transaction has been submitted'); // Disconnect from the gateway. await gateway.disconnect(); } catch (error) { console.error(`Failed to submit transaction: ${error}`); process.exit(1); } }) app.put('/api/changeowner/:car_index', async function (req, res) { try { // Create a new file system based wallet for managing identities. const walletPath = path.join(process.cwd(), 'wallet'); const wallet = new FileSystemWallet(walletPath); console.log(`Wallet path: ${walletPath}`); // Check to see if we've already enrolled the user. const userExists = await wallet.exists('user1'); if (!userExists) { console.log('An identity for the user "user1" does not exist in the wallet'); console.log('Run the registerUser.js application before retrying'); return; } // Create a new gateway for connecting to our peer node. const gateway = new Gateway(); await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: false } }); // Get the network (channel) our contract is deployed to. const network = await gateway.getNetwork('mychannel'); // Get the contract from the network. const contract = network.getContract('fabcar'); // Submit the specified transaction. // createCar transaction - requires 5 argument, ex: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom') // changeCarOwner transaction - requires 2 args , ex: ('changeCarOwner', 'CAR10', 'Dave') await contract.submitTransaction('changeCarOwner', req.params.car_index, req.body.owner); console.log('Transaction has been submitted'); res.send('Transaction has been submitted'); // Disconnect from the gateway. await gateway.disconnect(); } catch (error) { console.error(`Failed to submit transaction: ${error}`); process.exit(1); } }) app.listen(8080);
代碼中對原來fabcar的query.js和invoke.js修改如下:
ccpPath修改為當前目錄,因為我們要使用同一路徑下的連接配置文件connection-org1.json
在gateway.connect調用中,修改選項discovery.asLocalhost為false
API服務依賴于連接配置文件來正確連接fabric網絡。文件 connection-org1.json 可以直接從 fabric網絡中獲取:
{ "name": "first-network-org1", "version": "1.0.0", "client": { "organization": "Org1", "connection": { "timeout": { "peer": { "endorser": "300" } } } }, "organizations": { "Org1": { "mspid": "Org1MSP", "peers": [ "peer0.org1.example.com", "peer1.org1.example.com" ], "certificateAuthorities": [ "ca.org1.example.com" ] } }, "peers": { "peer0.org1.example.com": { "url": "grpcs://localhost:7051", "tlsCACerts": { "pem": "-----BEGIN CERTIFICATE-----\nMIICVjCCAf2gAwIBAgIQEB1sDT11gzTv0/N4cIGoEjAKBggqhkjOPQQDA jB2MQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGF tcGxlLmNvbTEfMB0GA1UEAxMWdGxz\nY2Eub3JnMS5leGFtcGxlLmNvbTAeFw0xOTA5MDQwMjQzMDBaFw0yOTA5MDEwMjQz\nMDBaMHYxCzAJB gNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\nEw1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcxLmV4YW1wbGUuY29tM R8wHQYD\nVQQDExZ0bHNjYS5vcmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D\nAQcDQgAEoN0qd5hM2SDfvGzNjTCXuQqyk+X K4VISa16/y9iXBPpa0onyAXJuv7T0\noPf+mh4T7/g8uYtV2bwTpT2XFO3Q6KNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1Ud\nJQQWMBQGCCsGA QUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1Ud\nDgQiBCCalpyChmrLtpgOll6TVmlMOO/2iiyI2PadNPsIYx51mTAKBggqh kjOPQQD\nAgNHADBEAiBLNoAYWe9LvoxxBxl3sUM64kl7rx6dI3JU+dJG6FRxWgIgCu1ONEyp\nfux9lZWr6gcrIdsn/8fQuWiOIbAgq0HSr60 =\n-----END CERTIFICATE-----\n" }, "grpcOptions": { "ssl-target-name-override": "peer0.org1.example.com", "hostnameOverride": "peer0.org1.example.com" } }, "peer1.org1.example.com": { "url": "grpcs://localhost:8051", "tlsCACerts": { "pem": "-----BEGIN CERTIFICATE-----\nMIICVjCCAf2gAwIBAgIQEB1sDT11gzTv0/N4cIGoEjAKBggqhkjOPQQDA jB2MQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGF tcGxlLmNvbTEfMB0GA1UEAxMWdGxz\nY2Eub3JnMS5leGFtcGxlLmNvbTAeFw0xOTA5MDQwMjQzMDBaFw0yOTA5MDEwMjQz\nMDBaMHYxCzAJB gNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\nEw1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcxLmV4YW1wbGUuY29tM R8wHQYD\nVQQDExZ0bHNjYS5vcmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D\nAQcDQgAEoN0qd5hM2SDfvGzNjTCXuQqyk+X K4VISa16/y9iXBPpa0onyAXJuv7T0\noPf+mh4T7/g8uYtV2bwTpT2XFO3Q6KNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1Ud\nJQQWMBQGCCsGA QUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1Ud\nDgQiBCCalpyChmrLtpgOll6TVmlMOO/2iiyI2PadNPsIYx51mTAKBggqh kjOPQQD\nAgNHADBEAiBLNoAYWe9LvoxxBxl3sUM64kl7rx6dI3JU+dJG6FRxWgIgCu1ONEyp\nfux9lZWr6gcrIdsn/8fQuWiOIbAgq0HSr60 =\n-----END CERTIFICATE-----\n" }, "grpcOptions": { "ssl-target-name-override": "peer1.org1.example.com", "hostnameOverride": "peer1.org1.example.com" } } }, "certificateAuthorities": { "ca.org1.example.com": { "url": "https://localhost:7054", "caName": "ca-org1", "tlsCACerts": { "pem": "-----BEGIN CERTIFICATE-----\nMIICUTCCAfegAwIBAgIQSiMHm4n9QvhD6wltAHkZPTAKBggqhkjOPQQDA jBzMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGF tcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\nb3JnMS5leGFtcGxlLmNvbTAeFw0xOTA5MDQwMjQzMDBaFw0yOTA5MDEwMjQzMDBa\nMHMxCzAJBgNVB AYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcxLmV4YW1wbGUuY29tMRwwG gYDVQQD\nExNjYS5vcmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\nz93lOhLJG93uJQgnh93QcPPal5NQXQnAutF KYkun/eMHMe23wNPd0aJhnXdCjWF8\nMRHVAjtPn4NVCJYiTzSAnaNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1UdJQQWMBQG\nCCsGAQUFBwMCB ggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdDgQiBCDK\naDhLwl3RBO6eKgHh5lHJovIyDJO3jTNb1ix1W86bFjAKBggqhkjOPQQDA gNIADBF\nAiEA8KTKkjQwb1TduTWWkmsLmKdxrlE6/H7CfsdeGE+onewCIHJ1S0nLhbWYv+G9\nTbAFlNCpqr0AQefaRT3ghdURrlbo\n----- END CERTIFICATE-----\n" }, "httpOptions": { "verify": false } } } }
當fabcar/startFabric.sh執行時,我們可以交叉檢查證書的傳播是否正確。 peer0.org1和 peer1.org1 的證書是org1的 TLS root CA 證書簽名的。
注意所有的節點都以localhost引用,我們稍后會將其修改為Fabric Node的公開IP地址。
我們已經在Fabric節點上生成了一個用戶標識user1并保存在wallet目錄中,我們可以看到有三個對應的文件:私鑰、公鑰和證書對象:
稍后我們會把這些文件拷貝到Rest API Server上。
1、首先在Rest API Server節點上安裝npm、node:
sudo apt-get update sudo apt install curl curl -sL https://deb.nodesource.com/setup_8.x | sudo bash - sudo apt install -y nodejs sudo apt-get install build-essentialnode -v npm -v
驗證結果如下:
2、然后在Rest API Server上創建一個目錄:
mkdir apiserver cd apiserver
3、接下來將下面的文件從Fabric節點拷貝到Rest API Server節點。我們 利用loccalhost在兩個EC2實例間拷貝:
# localhost (update your own IP of the two servers) # temp is an empty directory cd temp scp -i ~/Downloads/aws.pem ubuntu@[Fabric-Node-IP]:/home/ubuntu/fabric-samples/first-network/connection-org1.json . scp -i ~/Downloads/aws.pem ubuntu@[Fabric-Node-IP]:/home/ubuntu/fabric-samples/fabcar/javascript/package.json . scp -r -i ~/Downloads/aws.pem ubuntu@[Fabric-Node-IP]:/home/ubuntu/fabric-samples/fabcar/javascript/wallet/user1/ . scp -r -i ~/Downloads/aws.pem * ubuntu@[API-Server-Node-IP]:/home/ubuntu/apiserver/
運行結果如下:
4、可以看到現在所有的文件都拷貝到Rest API Server了,為了保持一致,我們將user1/改名為wallet/user1/:
cd apiserver mkdir wallet mv user1 wallet/user1
運行結果如下:
5、現在在Rest API Server上創建上面的apiserver.js文件。
6、修改連接配置文件connection-org1.json 中的fabric節點的ip地址:
sed -i 's/localhost/[Fabric-Node-IP]/g' connection-org1.json
運行結果如下:
7、在/etc/hosts中增加條目以便可以正確解析fabric節點的IP:
127.0.0.1 localhost [Fabric-Node-IP] orderer.example.com [Fabric-Node-IP] peer0.org1.example.com [Fabric-Node-IP] peer1.org1.example.com [Fabric-Node-IP] peer0.org2.example.com [Fabric-Node-IP] peer1.org2.example.com
運行結果如下:
8、安裝必要的依賴包:
npm install npm install express body-parser --save
9、萬事俱備,啟動Rest API Server:
node apiserver.js
我們的API服務在8080端口監聽,在下面的示例中,我們使用curl來 演示如何訪問。
1、查詢所有車輛記錄
curl http://[API-Server-Node-IP]:8080/api/queryallcars
運行結果如下:
2、添加新的車輛記錄并查詢
curl -d '{"carid":"CAR12","make":"Honda","model":"Accord","colour":"black","owner":"Tom"}' -H "Content-Type: application/json" -X POST http://[API-Server-Node-IP]:8080/api/addcar curl http://[API-Server-Node-IP]:8080/api/query/CAR12
運行結果如下:
3、修改車輛所有者并再次查詢
curl http://[API-Server-Node-IP]:8080/api/query/CAR4 curl -d '{"owner":"KC"}' -H "Content-Type: application/json" -X PUT http://[API-Server-Node-IP]:8080/api/changeowner/CAR4 curl http://[API-Server-Node-IP]:8080/api/query/CAR4
運行結果如下:
我們也可以用postman得到同樣的結果:
以上是“如何利用Hyperledger Fabric的SDK來開發REST API服務器”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。