A simple real estate negotiation blockchain platform which allows parties to make offers directly to each other without a centralized authority. Thus, buyers and sellers can close deals only using digital signatures and digital wallets.
The project also aims to be a sample for building and running an entire Hyperledger Fabric solution, from its infrastructure to the application code, and deploying this entire stack on a Kubernetes cluster.
The repository contains
api
folder, which holds an REST API to interact with the fabric network and call smart contract transactionschaincode
folder, which holds the smart contract source codekubernetes
folder, which holds the kubernetes configuration for each fabric component and the APInetwork
folder, which holds some configuration files for the fabric infrastructure
- Hyperledger Fabric v1.4.6
- Kubernetes
- Docker and Docker Compose
- Typescript
This section aims to demonstrate how to deploy the entire project stack on a kubernetes cluster. Also, for developing purposes, there are also the steps to run everything locally with docker compose.
Deploying everything into kubernetes is going to require a few steps
- Create a shared volume to hold common configuration files for Hyperledger Fabric
- Copy those configutation files into the volume
- Generate some basic blockchain artefacts, such as the genesis block
- Start up the necessary infrastructure containers (Certificate authority, Peer, Orderer and more)
- Install and instatiate the chaincode (aka smart contract) into the peers
- Deploy the REST API to interact with the network
So let's go!
Assuming that we're already connected to a kubernetes cluster, check if all nodes are up and running
kubectl get nodes
The first thing to do is to create a persistent shared volume between all pods to hold the fabric configuration files
kubectl apply -f ./kubernetes/fabric-volume.yml
Secondly, start up the fabric tools pod, which is a helper pod used to interact with the hyperledger network
kubectl apply -f ./kubernetes/fabric-tools.yml
Now, copy all configuration files to the shared volume
kubectl cp ./network/config/ fabric-tools:/fabric/
kubectl cp ./network/crypto-config/ fabric-tools:/fabric/
kubectl cp ./network/peer/configtx.yaml fabric-tools:/fabric/configtx.yaml
Also, copy the chaincode source files to the volume
kubectl cp ./chaincode/ fabric-tools:/fabric/
Since the basic files are already inside the volume, access the fabric helper pod to generate some Hyperledger artefacts. The first one is the genesis block, which will contains our network topology information with all nodes addresses. Then we're going to create a channel transaction and also a transaction to update the anchor peers.
kubectl exec -it fabric-tools -- /bin/bash
cd /fabric
configtxgen -profile OneOrgOrdererGenesis -outputBlock /fabric/config/genesis.block -channelID test -configPath .
configtxgen inspect /fabric/config/genesis.block
configtxgen -profile OneOrgChannel -outputCreateChannelTx ./config/channel.tx -channelID mychannel -configPath .
configtxgen -profile OneOrgChannel -outputAnchorPeersUpdate ./config/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP -configPath .
exit
After generating the artefacts, start up the infrastructure containers.
Create a fabric certificate authority
kubectl apply -f ./kubernetes/fabric-ca.yml
Create a fabric orderer
kubectl apply -f ./kubernetes/fabric-orderer.yml
Create a fabric peer and its couchdb
kubectl apply -f ./kubernetes/fabric-peer.yml
Check if all pods are running
kubectl get pods
Go back into our helper pod to join our peer in the channel and install the smart contract code in it
kubectl exec -it fabric-tools -- /bin/bash
Create a channel
peer channel create -o ${ORDERER_URL} -c ${CHANNEL_NAME} -f /fabric/config/channel.tx
Join the fabric peer into the channel
peer channel join -b mychannel.block
Package the chaincode source directory
peer chaincode package mycc.tar.gz --path /fabric/chaincode --lang node -n mycc -v 1.0
Install chaincode on peer
peer chaincode install mycc.tar.gz
We can check if the installation went smoothly with
peer chaincode list --installed
Instantiate chaincode
peer chaincode instantiate -o ${ORDERER_URL} -C mychannel -n mycc -l node -v 1.0 -c '{"Args":[]}' -P 'OR ("Org1MSP.member")'
To check if it worked
peer chaincode list --instantiated -C mychannel
Update the anchor peer to reflect on the discovery services
peer channel update -o ${ORDERER_URL} -c mychannel -f /fabric/config/Org1MSPanchors.tx
Now that the entire Hyperledger Fabric blockchain network is up and running inside the kubernetes cluster, start up the API service
kubectl apply -f ./kubernetes/api.yml
You can now test the API endpoints and see everything working.
To run the project locally, it is possible to start everything up using docker compose.
First, go to the network directory
cd network
Start up the local hyperledger fabric infrastructure
docker-compose -f docker-compose.yml up -d ca.example.com orderer.example.com peer0.org1.example.com couchdb
Create a channel
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel create -o orderer.example.com:7050 -c mychannel -f /etc/hyperledger/configtx/channel.tx
Join peer to channel
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel join -b mychannel.block
Initialize the fabric tools container, which is a helper cli that allows us to interact with the network
docker-compose run cli
Package the chaincode source directory
peer chaincode package mycc.tar.gz --path /opt/gopath/src/github.com/hyperledger/chaincode --lang node -n mycc -v 1.0
Install chaincode on peer
peer chaincode install mycc.tar.gz
We can check if the installation went smoothly with
peer chaincode list --installed
Instantiate chaincode
peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n mycc -l node -v 1.0 -c '{"Args":[]}' -P 'OR ("Org1MSP.member")'
To check if it worked
peer chaincode list --instantiated -C mychannel
Update the anchor peer to reflect on the discovery services
peer channel update -o orderer.example.com:7050 -c mychannel -f /opt/gopath/src/github.com/hyperledger/fabric/config/Org1MSPanchors.tx
curl -XPOST "http://localhost:3000/accounts" --header "Content-Type: application/json" --data '{"ownerName": "Bob" }'
{
"ownerName": "Bob",
"id": "acc_b958c34c-c156-4098-a947-faea5b44b0d3",
"balance": 0,
"createdAt": "2021-01-03T03:19:50.304Z",
"updatedAt": "2021-01-03T03:19:50.304Z"
}
curl --request POST \
--url http://localhost:3000/real_estate \
--header 'content-type: application/json' \
--data '{
"description": "Cozy apartment in NYC",
"price": 1500000,
"ownerAccountId": "acc_350a8ee7-1398-41a7-b3c3-b51434d54eb9",
"totalArea": "100m2",
"address": "Fifty avenue"
}'
{
"description": "Cozy apartment in NYC",
"price": 1500000,
"ownerAccountId": "acc_350a8ee7-1398-41a7-b3c3-b51434d54eb9",
"totalArea": "100m2",
"address": "Fifty avenue",
"id": "re_7254466e-8283-437e-bd20-48f0e449f394",
"offers": [],
"createdAt": "2020-12-17T01:44:47.264Z",
"updatedAt": "2020-12-17T01:44:47.264Z"
}
curl --request POST \
--url http://localhost:3000/real_estate/re_7254466e-8283-437e-bd20-48f0e449f394/transfers \
--header 'content-type: application/json' \
--data '{
"offerId": "of_ed81df4a-d570-492d-90f2-62da7e3eff86"
}'
{
"address": "Fifty avenue",
"createdAt": "2020-12-17T01:44:47.264Z",
"description": "Cozy apartment in NYC",
"id": "re_7254466e-8283-437e-bd20-48f0e449f394",
"offers": [],
"ownerAccountId": "acc_233837ec-1045-42b7-85e2-357712cf85cb",
"price": 1500000,
"totalArea": "100m2",
"updatedAt": "2020-12-17T01:44:47.264Z"
}
curl --request POST \
--url http://localhost:3000/offers \
--header 'content-type: application/json' \
--data '{
"realEstateId": "re_7254466e-8283-437e-bd20-48f0e449f394",
"buyerAccountId": "acc_233837ec-1045-42b7-85e2-357712cf85cb",
"amount": 1200000
}'
{
"realEstateId": "re_7254466e-8283-437e-bd20-48f0e449f394",
"buyerAccountId": "acc_233837ec-1045-42b7-85e2-357712cf85cb",
"amount": 1200000,
"id": "of_ed81df4a-d570-492d-90f2-62da7e3eff86",
"status": "PENDING_SIGNATURES",
"createdAt": "2020-12-17T01:47:04.008Z",
"updatedAt": "2020-12-17T01:47:04.008Z",
"sellerAccountId": "acc_350a8ee7-1398-41a7-b3c3-b51434d54eb9"
}
curl --request POST \
--url http://localhost:3000/offers/of_ed81df4a-d570-492d-90f2-62da7e3eff86/signatures \
--header 'content-type: application/json' \
--data '{
"signee": "buyer"
}'
{
"amount": 1200000,
"buyerAccountId": "acc_233837ec-1045-42b7-85e2-357712cf85cb",
"createdAt": "2020-12-17T01:47:04.008Z",
"id": "of_ed81df4a-d570-492d-90f2-62da7e3eff86",
"realEstateId": "re_7254466e-8283-437e-bd20-48f0e449f394",
"sellerAccountId": "acc_350a8ee7-1398-41a7-b3c3-b51434d54eb9",
"sellerSignature": "32c01a2002e1d98dd61acacf6b3f4f174ed95180ea6dbec830219170b9a6d0cb",
"status": "PENDING_SIGNATURES",
"updatedAt": "2020-12-17T01:47:31.047Z",
"buyerSignature": "92658cf4dde39084051672000069df5b71c8fa696baec45d91e9cccb32680117"
}