Skip to content

Commit d75990e

Browse files
add sample application with fabric smart client and token sdk (#19)
#### Type of change - New feature #### Description This PR adds a sample application to the Fabric-X repo to make it easier for developers to get started. In this first iteration it implements a very basic fabric-smart-client plus fabric-token-sdk application. It relies on the ansible scripts to deploy a local Fabric-X network. The application can run in docker or natively, and is backwards compatible with Fabric v3. #### Related issues N/A --------- Signed-off-by: Arne Rutjes <arne123@gmail.com> Co-authored-by: Marcus Brandenburger <bur@zurich.ibm.com>
1 parent 3f1f396 commit d75990e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+9140
-1
lines changed

samples/tokens/.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
bin/
2+
keys/
3+
data/
4+
**/namespace/*.json
5+
fabric-samples/
6+
7+
out/
8+
9+
__debug_bin*

samples/tokens/Dockerfile

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright IBM Corp. All Rights Reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
6+
# build stage
7+
FROM golang:1.25.2 AS builder
8+
WORKDIR /go/src/app
9+
10+
ARG NODE_TYPE="owner"
11+
ARG PLATFORM="fabricx"
12+
13+
ENV GOCACHE=/go-cache \
14+
GOMODCACHE=/gomod-cache \
15+
CGO_ENABLED=0
16+
17+
COPY . .
18+
19+
RUN --mount=type=cache,target=/go-cache \
20+
--mount=type=cache,target=/gomod-cache \
21+
cd "${NODE_TYPE}" && go build -tags "${PLATFORM}" -o /app
22+
23+
FROM busybox
24+
25+
# API port. The FSC port must be exposed explicitly (configured in core.yaml).
26+
EXPOSE 9000
27+
28+
HEALTHCHECK --start-interval=2s --start-period=10s --interval=5s --timeout=1s --retries=5 CMD nc -z 127.0.0.1 9000
29+
30+
WORKDIR /conf
31+
COPY --from=builder /app /usr/bin/app
32+
33+
CMD ["app", "--conf", "/conf"]

samples/tokens/Makefile

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#
2+
# Copyright IBM Corp. All Rights Reserved.
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
PLATFORM ?= fabricx
7+
CONF_ROOT=conf
8+
9+
ifeq ($(PLATFORM),fabricx)
10+
include ./fabricx.mk
11+
else
12+
include ./fabric3.mk
13+
endif
14+
15+
include ./app.mk
16+
17+
# Install the utilities needed to run the components on the targeted remote hosts (e.g. make install-prerequisites).
18+
.PHONY: install-prerequisites
19+
install-prerequisites: install-prerequisites-fabric
20+
go mod tidy
21+
22+
# Build all the artifacts and binaries, and copy them to the application folders
23+
.PHONY: setup
24+
setup: clean setup-fabric setup-app
25+
26+
# Start a Fabric and token network.
27+
.PHONY: start
28+
start: start-fabric create-namespace start-app
29+
30+
# Teardown Fabric and the token network.
31+
.PHONY: teardown
32+
teardown: teardown-app teardown-fabric
33+
34+
# Stop Fabric and the token network.
35+
.PHONY: stop
36+
stop: stop-app stop-fabric
37+
38+
# Remove all generated crypto.
39+
.PHONY: clean
40+
clean: clean-app clean-fabric
41+
42+
# Print the list of supported commands.
43+
.PHONY: help
44+
help:
45+
@awk ' \
46+
/^#/ { \
47+
sub(/^#[ \t]*/, "", $$0); \
48+
help_msg = $$0; \
49+
} \
50+
/^[a-zA-Z0-9][^ :]*:/ { \
51+
if (help_msg) { \
52+
split($$1, target, ":"); \
53+
printf " %-40s %s\n", target[1], help_msg; \
54+
help_msg = ""; \
55+
} \
56+
} \
57+
' $(MAKEFILE_LIST)

samples/tokens/README.md

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
<!--
2+
SPDX-License-Identifier: Apache-2.0
3+
-->
4+
# Token SDK Sample
5+
6+
The **Token SDK Sample** demonstrates how to:
7+
8+
- Build a simple token-based application using the [Token SDK](https://github.com/hyperledger-labs/fabric-token-sdk).
9+
- Connect the application to both [Fabric-X](https://github.com/hyperledger/fabric-x) and classic [Fabric](https://github.com/hyperledger/fabric) networks.
10+
- Issue and transfer tokens via a REST API.
11+
12+
## About the Sample
13+
14+
This demo provides a set of services exposing REST APIs that integrate with the [Token SDK](https://github.com/hyperledger-labs/fabric-token-sdk)
15+
to issue, transfer, and redeem tokens backed by a **Hyperledger Fabric(x)** network for validation and settlement.
16+
17+
Together, these services form a *Layer 2 network* capable of transacting privately among participants.
18+
The ledger data does not reveal balances, transaction amounts, or participant identities.
19+
Tokens are represented as UTXOs owned by pseudonymous keys, with details hidden through **Zero-Knowledge Proofs (ZKPs)**.
20+
21+
The application follows the Fabric-X programming model, where business parties directly endorse transactions—rather than Fabric peers executing chaincode.
22+
Note that the [Token SDK](https://github.com/hyperledger-labs/fabric-token-sdk) builds on top of the [Fabric Smart Client](https://github.com/hyperledger-labs/fabric-smart-client) (FSC), a framework to build distributed applications for Fabric(x).
23+
24+
This sample helps you get familiar with Token SDK features and serves as a starting point for your own proof of concept.
25+
26+
**Components**
27+
28+
**Application services**
29+
- **Issuer service** - creates (issues) tokens.
30+
- **Owner services** - host user wallets.
31+
- **Endorser service** - validates and approves token transactions.
32+
33+
34+
**Fabric(x) Blockchain Network**
35+
- An offline Certificate Authority (CA).
36+
- Configuration for a **Fabric-X** test network.
37+
- Configuration for a **Fabric v3** test network.
38+
39+
## Architecture Overview
40+
41+
From now on, we’ll refer to the issuer, endorser, and owner services collectively as nodes (not to be confused with Fabric peer nodes).
42+
43+
Each node runs as a separate application with:
44+
45+
- A REST API
46+
- The FSC node runtime
47+
- The Token SDK
48+
49+
Nodes communicate via *websockets* to construct token transactions.
50+
Each node also acts as a Fabric user, submitting transactions to the settlement layer — any Fabric or Fabric-X network.
51+
52+
A namespace (`token_namespace`) is deployed, along with a committed transaction containing the identities of the issuer, endorsers, and CA, enabling transaction validation.
53+
54+
## Prerequisites
55+
56+
### Fabric-X Setup
57+
58+
We use the [Fabric-x Ansible Collection](https://github.com/LF-Decentralized-Trust-labs/fabric-x-ansible-collection?tab=readme-ov-file#option-2-install-from-source) to set up the Fabric-X test network.
59+
Please check the installation guidelines for more details.
60+
61+
#### Requirements
62+
63+
- `python`;
64+
- [`ansible`](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) >= **2.16**;
65+
- [`podman`](https://podman.io/docs/installation) or [`docker`](https://docs.docker.com/engine/install/);
66+
- [`go`](https://go.dev/doc/install).
67+
68+
#### Installation
69+
70+
Clone the repository (anywhere on your machine) and install the ansible collection.
71+
72+
```bash
73+
git clone https://github.com/LF-Decentralized-Trust-labs/fabric-x-ansible-collection.git
74+
cd fabric-x-ansible-collection
75+
make install
76+
```
77+
78+
Back in the Token SDK Sample directory:
79+
80+
```shell
81+
make install-prerequisites
82+
python3 -m pip install -r ansible/requirements.txt
83+
```
84+
85+
> [!NOTE]
86+
> Mac users:, Fabric-X components must communicate via host.docker.internal instead of localhost.
87+
> ```shell
88+
> export LOCAL_ANSIBLE_HOST="host.docker.internal"
89+
> ```
90+
91+
We use the Fabric CA for issuing idemix credentials for the accounts. Download it [here](https://github.com/hyperledger/fabric-ca/releases)
92+
or follow the v3 instructions below to install it (and make sure that it is in your `PATH`).
93+
94+
### Fabric v3 Setup
95+
96+
To run on Fabric v3, we use Fabric samples test network (`$(FABRIC_SAMPLES)/test-network/network.sh`).
97+
98+
Ensure Fabric binaries are in your `PATH` and Docker images are available. If not, install them as follows.
99+
100+
```shell
101+
curl -sSLO https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh && chmod +x install-fabric.sh
102+
./install-fabric.sh --fabric-version 3.1.1
103+
export PATH=$(pwd)/fabric-samples/bin:$PATH
104+
```
105+
106+
> [!TIP]
107+
> The scripts assume you have run the script from the directory of this README. If you have them somewhere else,
108+
> just make sure to `export FABRIC_SAMPLES=/path/to/fabric-samples"` so the scripts can start the test network.
109+
110+
## Getting Started
111+
112+
The sample uses Fabric-X as the default network.
113+
If you want to run the application on Fabric v3, just set the PLATFORM to fabric3.
114+
115+
```bash
116+
export PLATFORM=fabric3
117+
```
118+
119+
> [!NOTE]
120+
> When switching between platforms, always make sure to *first* (before changing the platform) clean up all artifacts:
121+
> ```shell
122+
> make teardown
123+
> make clean
124+
> ```
125+
126+
### Generate Crypto Material
127+
128+
Create the configurations and crypto material for the network:
129+
130+
```shell
131+
make setup
132+
```
133+
134+
### Start the Network and Application
135+
136+
Start the Fabric network, create the namespace, and start the application services.
137+
138+
```shell
139+
make start-fabric
140+
make create-namespace
141+
make start-app
142+
```
143+
144+
Or simply:
145+
146+
```shell
147+
make start
148+
```
149+
150+
### Interacting with the Application
151+
152+
All services run as Docker containers and expose REST APIs.
153+
They also communicate over P2P websockets as shown below:
154+
155+
| Rest API | P2P | Service |
156+
| -------- | ---- | --------------------------- |
157+
| 8080 | | API documentation (web) |
158+
| 9100 | 9101 | Issuer |
159+
| 9300 | 9301 | Endorer 1 |
160+
| 9400 | 9401 | Endorser 2 (Fabric v3 only) |
161+
| 9500 | 9501 | Owner 1 (alice and bob) |
162+
| 9600 | 9601 | Owner 2 (carlos and dan) |
163+
164+
We can use the Swagger API on [http://localhost:8080](http://localhost:8080) or call the API directly via `curl`.
165+
166+
Now let's issue and transfer some tokens!
167+
168+
#### Example: Issue tokens
169+
170+
We begin with initializing the token namespace (commit the parameters for the network) and issue `TOK` tokens to `alice`.
171+
172+
```bash
173+
curl -X POST http://localhost:9300/endorser/init # Fabric-X only
174+
175+
curl http://localhost:9100/issuer/issue --json '{
176+
"amount": {"code": "TOK","value": 1000},
177+
"counterparty": {"node": "owner1","account": "alice"},
178+
"message": "hello world!"
179+
}'
180+
181+
curl http://localhost:9500/owner/accounts/alice | jq
182+
curl http://localhost:9600/owner/accounts/dan | jq
183+
```
184+
185+
#### Example: Transfer tokens
186+
187+
Now `alice` transfers `100 TOK` to `dan`.
188+
189+
```bash
190+
curl http://localhost:9500/owner/accounts/alice/transfer --json '{
191+
"amount": {"code": "TOK","value": 100},
192+
"counterparty": {"node": "owner2","account": "dan"},
193+
"message": "hello dan!"
194+
}'
195+
196+
curl -X GET http://localhost:9600/owner/accounts/dan/transactions | jq
197+
curl -X GET http://localhost:9500/owner/accounts/alice/transactions | jq
198+
```
199+
200+
#### UTXO Model
201+
202+
Note that the application uses the UTXO model (like bitcoin).
203+
- The issuer creates a token of `1000 TOK` owned by `alice`.
204+
- When `alice` transfers `100 TOK` to `dan`, her `1000 TOK` token becomes the **input**.
205+
- Two **outputs** are created:
206+
1) `100 TOK` owned by `dan`
207+
2) `900 TOK` owned by `alice`
208+
209+
Every transfer consumes existing outputs and creates new ones, ensuring balance consistency.
210+
211+
### Deep Dive: What Happens During a Transfer?
212+
213+
Let’s examine how a private token transfer works between `alice` (Owner 1) and `dan` (Owner 2):
214+
215+
1. **Create Transaction:**
216+
217+
Alice requests an anonymous key from Dan, creates commitments that can be verified by anyone, but _only_ be opened (read) by Dan.
218+
The commitments contain the value, sender and recipient of each of the in- and output tokens.
219+
220+
2. **Get Endorsements:**
221+
222+
Alice submits the transaction to the endorser which validates the transaction using the token validation logic.
223+
In detail, it verifies that all the proofs are valid and all the necessary signatures are there.
224+
Note that the endorser cannot see the actual transfer details thanks to the zero knowledge proofs.
225+
226+
3. **Commit Transaction:**
227+
228+
Alice submits the endorsed fabric(x) transaction to the ordering service.
229+
Once committed, all involved nodes (Owner 1, Owner 2) receive events and update the transaction status to `Confirmed.`
230+
The transaction is now final; Dan now officially owns the `100 TOK`.
231+
232+
233+
![transfer](diagrams/transfer_transaction.png)
234+
235+
### Teardown and cleanup
236+
237+
Convenient Make targets are provided for shutting down, restarting, and cleaning the environment.
238+
239+
Run:
240+
241+
```bash
242+
make help
243+
```
244+
245+
for a list of available commands.
246+
247+
## Debug mode
248+
249+
For a faster development, you can run the services outside Docker.
250+
251+
First, add the following to `/etc/hosts`:
252+
253+
```
254+
127.0.0.1 peer0.org1.example.com
255+
127.0.0.1 peer0.org2.example.com
256+
127.0.0.1 orderer.example.com
257+
127.0.0.1 issuer.example.com
258+
127.0.0.1 endorser1.example.com
259+
127.0.0.1 endorser2.example.com
260+
127.0.0.1 owner1.example.com
261+
127.0.0.1 owner2.example.com
262+
127.0.0.1 committer-sidecar
263+
127.0.0.1 committer-queryservice
264+
127.0.0.1 host.docker.internal
265+
```
266+
267+
The application services discovers the peer addresses from the channel configuration after connecting to committer-queryservice (or a trusted peer in Fabric v3).
268+
269+
Next, start the network as before:
270+
271+
```bash
272+
make start-fabric
273+
make create-namespace
274+
# don't make start-app
275+
```
276+
277+
In separate terminals:
278+
279+
```bash
280+
cd conf/issuer && go run ../../issuer --port 9100
281+
cd conf/endorser1 && go run ../../endorser --port 9300
282+
cd conf/owner && go run ../../owner --port 9500
283+
```
284+
285+
### VSCode
286+
287+
If you use VSCode, copy:
288+
289+
```bash
290+
mkdir -p ../../.vscode
291+
cp launch.example.json ../../.vscode/launch.json
292+
```
293+
294+
Then run or debug the application services directly.

0 commit comments

Comments
 (0)