|
| 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 | + |
| 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