An Efficient Customized Blockchain System for Inter-Organizational Processes
We construct a blockchain system by embedding the customized process engine into the blockchain nodes based on a permissioned blockchain platform Hyperledger Fabric and a process engine Activiti. The customized process engine executes the inter-organizational business processes via a blockchain-style procedure, i.e., checking the validity of transactions, adding the valid transactions into the blockchain through the consensus mechanism, and then updating the process states according to the committed transactions.
Puwei Wang, Zhouxing Sun, Rui Li, Jinchuan Chen, Ping Gong, Xiaoyong Du, An Efficient Customized Blockchain System for Inter-Organizational Processes, IEEE International Conference on Web Services (ICWS), page 615-625, 2023
* Docker - v 20.10.7 or higher
* Docker Compose - v1.27.4 or higher
* Node.js v8.17.0
* Java 1.8
* [Download Docker images]
hyperledger/fabric-orderer:1.4
hyperledger/fabric-zookeeper:x86_64-0.4.6
hyperledger/fabric-kafka:x86_64-0.4.6
cd workflow-engine
docker load -i workflow-peer-v2
docker load -i workflow-ca
note:
cd workflow-engine
docker-compose -f workflow.yaml up
* 3 CAs
IMAGE NAMES
workflow-ca:1.0 ca.org3.workflow.com
workflow-ca:1.0 ca.org2.workflow.com
workflow-ca:1.0 ca.org1.workflow.com
* An orderer with a kafka node and a zookeeper node
IMAGE NAMES
hyperledger/fabric-orderer:1.4.0 orderer.workflow.com
hyperledger/fabric-kafka:x86_64-0.4.6 kafka.workflow.com
hyperledger/fabric-zookeeper:x86_64-0.4.6 zookeeper.workflow.com
* 3 peers (1 peers per Org)
IMAGE NAMES
workflow-peer:latest peer0.org3.workflow.com
workflow-peer:latest peer0.org2.workflow.com
workflow-peer:latest peer0.org1.workflow.com
cd workflow-engine/sdk-application
./runApp.sh
output
[2023-11-02 15:17:33.147] [INFO] SampleWebApp - ****************** SERVER STARTED ************************
[2023-11-02 15:17:33.151] [INFO] SampleWebApp - *************** http://localhost:4000 ******************
jq: instructions https://stedolan.github.io/jq/cd workflow-engine/sdk-application
./createChannelAndJoin.sh
output
{"channels":[{"channel_id":"workflowchannel"}]}
note
docker exec peer0.org1.workflow.com sh /usr/local/scripts/wfServiceScripts/wfServiceStart.sh &
It will return some warnings, but it does not affect the startup of the workflow engine service
Similarly, initiate the workflow engine services embedded within peer0.org2.workflow.com and peer0.org3.workflow.com
docker exec peer0.org2.workflow.com sh /usr/local/scripts/wfServiceScripts/wfServiceStart.sh &
docker exec peer0.org3.workflow.com sh /usr/local/scripts/wfServiceScripts/wfServiceStart.sh &
docker exec peer0.org1.workflow.com sh /usr/local/scripts/nacosScripts/nacosStart.sh &
output
…
2023-11-02 04:22:45,333 INFO Nacos Log files: /nacos/logs/
2023-11-02 04:22:45,334 INFO Nacos Conf files: /nacos/conf/
2023-11-02 04:22:45,336 INFO Nacos Data files: /nacos/data/
2023-11-02 04:22:45,337 INFO Nacos started successfully in cluster mode.
If it displays 'Nacos started successfully in cluster mode', it means that Nacos has started successfully. You can view it through the web page http://{localhost IP}:7848/nacos
Similarly, initiate the Nacos services embedded within peer0.org2.workflow.com and peer0.org3.workflow.com
docker exec peer0.org2.workflow.com sh /usr/local/scripts/nacosScripts/nacosStart.sh &
docker exec peer0.org3.workflow.com sh /usr/local/scripts/nacosScripts/nacosStart.sh &
cd workflow-engine/testService
mvn clean package
cd target
java -jar testService.jar --server.port=${service port}
#example
java -jar testService.jar --server.port=6666
curl --request POST \
--url http://{service IP}:{service port}/test \
--header 'content-type: application/json' \
--data '{
"aaa":"bbb"
}'
#example
curl --request POST \
--url http://10.47.33.219:6666/test \
--header 'content-type: application/json' \
--data '{
"aaa":"bbb"
}'
curl -X POST 'http://{localhost IP}:{port of nacos}/nacos/v1/ns/instance?port={service port}&healthy=true&ip={service IP}&weight=1.0&serviceName={serviceGroup}@@{serviceName}&encoding=GBK'
#example
curl -X POST 'http://127.0.0.1:7848/nacos/v1/ns/instance?port=6666&healthy=true&ip=10.47.33.219&weight=1.0&serviceName=WORKFLOW@@testService&encoding=GBK'
note
ServiceGroup can be left blank and used the default value, directly as serviceName={serviceName};
The service IP cannot be 127.0.0.1 or localhost; it needs to be a specific IP address.
cd workflow-engine/bpmn-vue
npm install
npm run dev
bpmn editor http://{localhost IP}:8085/
example
http://127.0.0.1:8085/
General-Details-*Implementation*: Java Class
General-Details-*Java Class*: com.wq.wfEngine.taskService.web
curl --request POST \
--url http://{localhost IP}:{port of workflow engine service}/grafana/wfRequest/deploy \
--header 'content-type: multipart/form-data' \
--form file=@{path of bpmn file}\
--form deploymentName={workflow name}
#example
curl --request POST \
--url http://127.0.0.1:8999/grafana/wfRequest/deploy \
--header 'content-type: multipart/form-data' \
--form file=@/users/dreamingworld/Fabric/workflow-engine/simpleSample.bpmn \
--form deploymentName=simpleSample.bpmn
output
{
"code": 200,
"Oid": "simpleSample.bpmn",
"body": "等待上链,更改状态",
"模拟执行结果": true
}
#The response will indicate successful execution and await writing into the blockchain. It will include an attribute called Oid, which can be used to check whether the write operation was successful or not
curl --request POST \
--url http://{localhost IP}:{port of workflow engine service}/grafana/queryDeployments
#example
curl --request POST \
--url http://127.0.0.1:8999/grafana/queryDeployments
output
Oids of deployed bpmn diagrams, ["simpleSample.bpmn"]
curl --request POST \
--url http://{localhost IP}:{port of workflow engine service}/grafana/wfRequest/instance \
--header 'content-type: application/json' \
--data '{
"deploymentName":"{bpmn oid}",
"businessData":"{}",
"processData":"{}",
"staticAllocationTable":"{ the task allocation table used to assign executors for user tasks }"
}'
#example
curl --request POST \
--url http://127.0.0.1:8999/grafana/wfRequest/instance \
--header 'content-type: application/json' \
--data '{
"deploymentName":"simpleSample.bpmn",
"businessData":"{}",
"processData":"{}",
"staticAllocationTable":"{\"submit\":\"sun\"}"
}'
curl --request POST \
--url http://{localhost IP}:{port of workflow engine service}/grafana/queryStatusByDeploymentName/{deploymentName}
#example
curl --request POST \
--url http://127.0.0.1:8999/grafana/queryStatusByDeploymentName/simpleSample.bpmn
output
[
{
"oid": "simpleSample.bpmn@0f513fc9-3a9e-40cf-8ffb-3d5bbf88c62f",
"tasks": ["submit"]
}
]
Dynamic binding allows the registered service to be dynamically bound to the service task in the workflow instance, or for the executor to be dynamically assigned to the user task.
curl --request POST \
--url http://{localhost IP}:{port of workflow engine service}/grafana/userDynamicBind \
--header 'content-type: application/x-www-form-urlencoded' \
--data oid={oid} \
--data taskName={taskName} \
--data user={userName}
#example
curl --request POST \
--url http://127.0.0.1:8999/grafana/userDynamicBind \
--header 'content-type: application/x-www-form-urlencoded' \
--data oid=simpleSample.bpmn@0f513fc9-3a9e-40cf-8ffb-3d5bbf88c62f \
--data taskName=submit \
--data user=testone
curl --request POST \
--url http://{localhost IP}:{port of workflow engine service}/grafana/serviceDynamicBind \
--header 'content-type: application/x-www-form-urlencoded' \
--data oid={oid of bmpn instance} \
--data taskName={task} \
--data serviceName={serviceName} \
--data httpMethod={httpMethod} \
--data route={route} \
--data '{input}' \
--data serviceGroup={serviceGroup}
# example
curl --request POST \
--url http://127.0.0.1:8999/grafana/serviceDynamicBind \
--header 'content-type: application/x-www-form-urlencoded' \
--data oid=simpleSample.bpmn@0f513fc9-3a9e-40cf-8ffb-3d5bbf88c62f \
--data taskName=task3 \
--data serviceName=testService \
--data httpMethod=POST \
--data route=/test \
--data 'input={"ID":"init.ID","Address":"sun.Address"}' \
--data serviceGroup=WORKFLOW
Property description of serviceTask
serviceName: the name of the web service registration which the serviceTask will invoke
httpMethod: the method that the serviceTask will invoke , GET, POST, DELETE, etc.
route: URL
serviceGroup: the group of the service registration which the serviceTask will invoke
input: used to indicate input for the serviceTasks, default is the previous serviceTask response.
example
{"input field name":"<one of the pre serviceTasks name>.<the field of this previous. serviceTask response>"}
input={"ID":"init.ID","Address":"sun.Address"}
curl --request POST \
--url http://{localhost IP}:{port of workflow engine service}/grafana/wfRequest/complete \
--header 'content-type: application/json' \
--data '{
"Oid":"{oid}",
"taskName":"{taskName}",
"businessData":"{businessData in JSON format}",
"processData":"{processData in JSON format}",
"user":"{the executor who is assigned to execute the user task}"
}'
#example
curl --request POST \
--url http://127.0.0.1:8999/grafana/wfRequest/complete \
--header 'content-type: application/json' \
--data '{
"Oid":"simpleSample.bpmn@0f513fc9-3a9e-40cf-8ffb-3d5bbf88c62f",
"taskName":"submit",
"businessData":"{\"ID\":\"001\", \"Name\":\"Wang\", \"Address\":\"Beijing\"}",
"processData":"{}",
"user":"testone"
}'
output
{
"code":200,
"Oid":"simpleSample.bpmn@0f513fc9-3a9e-40cf-8ffb-3d5bbf88c62f",
"body":"等待上链,更改状态",
"模拟执行结果":true
}
curl --request GET \
--url http://{localhost IP}:{port of workflow engine service}/grafana/getResponseByOid/{Oid}
#example
curl --request GET \
--url http://127.0.0.1:8999/grafana/getResponseByOid/simpleSample.bpmn@0f513fc9-3a9e-40cf-8ffb-3d5bbf88c62f
output
{
"startPutToBlockChain":1698925187420,
"toTaskName":"[]",
"isDeploy":false,
"flushStartTime":1698925187836,
"businessData":"{\"Address\":\"Beijing\",\"ID\":\"001\"}",
"fromTaskName":"[submit]",
"deploymentName":"simpleSample.bpmn",
"startTime":1698925185877,
"flushEndTime":1698925188053,
"isEnd":true,
"simulationEndTime":1698925187187
}
Property description of the response
| property's name | description |
|---|---|
| startPutToBlockChain | The time at which data writing to the blockchain begins |
| isDeploy | Indicates whether the deployment process has taken place or not |
| flushStartTime | The time when the data has been written to the blockchain and the state database starts to change |
| isEnd | Indicates whether the workflow instance has ended or not |
| toTaskName | The name of the next user task |
| businessData | The response containing business-related information |
| fromTaskName | The name of the completed user task |
| deploymentName | The deployment name of the workflow instance |
| startTime | The time at which the request handling begins |
| flushEndTime | The time when the state database has completed its changes |
| simulationEndTime | The time at which the simulation has been completed. |