Skip to content

Commit

Permalink
add token sdk sample application
Browse files Browse the repository at this point in the history
Signed-off-by: Arne Rutjes <arne123@gmail.com>
  • Loading branch information
arner committed Oct 6, 2023
1 parent 62f304a commit 66b4935
Show file tree
Hide file tree
Showing 57 changed files with 13,974 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Additional samples demonstrate various Fabric use cases and application patterns
| **Sample** | **Description** | **Documentation** |
| -------------|------------------------------|------------------|
| [Off chain data](off_chain_data) | Learn how to use block events to build an off-chain database for reporting and analytics. | [Peer channel-based event services](https://hyperledger-fabric.readthedocs.io/en/latest/peer_event_services.html) |
| [Token SDK](token-sdk) | Sample REST API around the Hyperledger Labs [Token SDK](https://github.com/hyperledger-labs/fabric-token-sdk) for privacy friendly (zero knowledge proof) UTXO transactions. | [README](token-sdk/README.md) |
| [Token ERC-20](token-erc-20) | Smart contract demonstrating how to create and transfer fungible tokens using an account-based model. | [README](token-erc-20/README.md) |
| [Token UTXO](token-utxo) | Smart contract demonstrating how to create and transfer fungible tokens using a UTXO (unspent transaction output) model. | [README](token-utxo/README.md) |
| [Token ERC-1155](token-erc-1155) | Smart contract demonstrating how to create and transfer multiple tokens (both fungible and non-fungible) using an account based model. | [README](token-erc-1155/README.md) |
Expand Down
1 change: 1 addition & 0 deletions token-sdk/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
oapi-server.yaml
7 changes: 7 additions & 0 deletions token-sdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
keys/
data/
tokenchaincode/zkatdlog_pp.json

auditor/auditor
issuer/issuer
owner/owner
19 changes: 19 additions & 0 deletions token-sdk/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#build stage
FROM golang:1.20.7-bookworm AS builder
WORKDIR /go/src/app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN go build -o /go/bin/app

#final stage
FROM golang:1.20.7-bookworm
COPY --from=builder /go/bin/app /app
ENTRYPOINT /app
LABEL Name=tokens Version=0.1.0

ENV PORT=9000
ENV CONF_DIR=/conf
EXPOSE 9000
EXPOSE 9001
339 changes: 339 additions & 0 deletions token-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,339 @@
# Token SDK Sample API

This is a service with a REST API that wraps the [Token SDK](https://github.com/hyperledger-labs/fabric-token-sdk) to issue, transfer and redeem tokens backed by a Hyperledger Fabric network for validation and settlement.

Several instances of this service form a Layer 2 network that can transact amongst each other, with an (optional but currently configured to be required) auditor role who has to approve every transaction. The ledger data does not reveal balances, transaction amounts and identities of transaction parties. UTXO Tokens are owned by pseudonymous keys and other details are obscured with Zero Knowledge Proofs.

This sample is intended to get familiar with the features of the Token SDK and as a starting point for a proof of concept. The repo contains a basic development setup with:

- An issuer node
- An auditor node
- Two owner nodes, with wallets for Alice, Bob, Carlos and Dan
- A Certificate Authority
- Configuration to use a Fabric test network.

![components](./components.png)

[plantuml source](https://www.plantuml.com/plantuml/uml/ZPB1IiD048RlUOgXteHM4nIXbD0O4RnO3mKllKmtssR9PYRiRYWYlhlPNPMsIkrjcFs_d-LZvjQXSNsh4ziewj1W2wsYKgErhwfoDQGtrqdIeMXmAs6qv4OIF4ktOzEC02quRk0z0H3STaoI78nMT123iWX9WJ2RmGRNHecnm6awkPtSGPuVmqLVASScCDXN7ffSOLmESRWekauhWKun7RDFrlOoeihQY2g_-vTSx6W8fIkwX6B8I3_SypfKSHgRU4Vd5cMUBz5ejdvwG8fDsQccZptJZy4JBALr1xvhlVdj-qNwluVtRXXJs3Fj5rEDpXVb-PzazaDcPuCBKqdpfPhZlCV6pGayNaXPeoB1bOod94Iqu_oZ-7uBcfPIrCIQjs_UaZ-wyJZtCfAvfAflzIS0)

The source code contains three separate applications: issuer, auditor and owner. Each of the applications runs as a separate service ('node'). The nodes talk to each other (via a protocol called libp2p) to create token transactions, and each of them also has a Hyperledger Fabric user to be able to submit the transaction to the settlement layer. The settlement layer is just any Fabric network that runs the Token Chaincode (which is configured with the identities of the issuer, auditor and CA).

## Features

Main flows:

- [X] issue token
- [X] transfer
- [X] redeem / burn
- [X] owner get balances
- [X] owner transaction history
- [ ] auditor get balances
- [X] auditor transaction history
- [ ] issuer transaction history
- [ ] swap

Additional features:

- [X] Documented REST API
- [X] Basic end to end tests
- [X] Support for multiple token types
- [X] Multiple accounts per node
- [X] Use Idemix (privacy preserving) accounts created by a Fabric CA
- [X] Pre-configured and easy to start for development

### Out of scope for now

- HTLC locks (hashed timelock contracts)
- Register/enroll new token accounts on a running network
- Business flows for redemption or issuance
- Advanced transaction history (queries, rolling balance, pagination, etc)
- Denylist / revocation and other business logic for auditor
- Idemix users to submit the transactions to Fabric anonymously
- Production configuration (e.g. deployment, networking, security, resilience, key management)

## Getting started

Prerequisites:

- bash
- golang 1.20+
- git
- docker
- docker-compose

### Install dependencies

Download the Fabric docker images and binaries. The code only works with Fabric CA 1.5.7+, so even if you cloned the fabric-samples repo before, you may have to re-run it to get the latest versions.

```bash
curl -sSLO https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh && chmod +x install-fabric.sh
./install-fabric.sh docker binary
```

Make sure that the new binaries are in your path. Change the following line (replace `<your/path/to/> with the actual path`) and add them to your `~/.bashrc` or `~/.zshrc` file (and restart your terminal):

```bash
export PATH=</your/path/to/>fabric-samples/bin:$PATH
```

> Note: if you are *not* running this code from the fabric-samples/token-sdk folder, set the following environment variable:
> ```bash
> export TEST_NETWORK_HOME=</your/path/to>/fabric-samples/test-network
> ```
Validate that the CA is at 1.5.7 by executing `fabric-ca-client version`.
Install tokengen.
```bash
go install github.com/hyperledger-labs/fabric-token-sdk/cmd/tokengen@v0.3.0
```
Now go to the root folder of your token-sdk sample, for instance in the fabric-samples dir:

```bash
cd fabric-samples/token-sdk
```

> You can have the token-sdk folder anywhere. The commands keep working, as long as you have the TEST_NETWORK_HOME variable set.
> See the bottom of this readme for instructions to use another Fabric network.
### Quick start

The quickest way to get going is to run:

```bash
./scripts/up.sh
```

This generates the crypto material, starts Fabric, deploys the chaincode, and starts the token nodes.

When you're done and want to delete everything:

```bash
./scripts/down.sh
```

#### Using the application

The services are running on the following ports:

- 8080 API documentation
- 9000 auditor
- 9100 issuer
- 9200 owner (alice and bob)
- 9300 owner 2 (carlos and dan)

Now let's issue and transfer some tokens! View the API documentation and try some actions at [http://localhost:8080](http://localhost:8080). Or, directly from the commandline:

```bash
curl -X POST http://localhost:9100/api/v1/issuer/issue -H 'Content-Type: application/json' -d '{
"amount": {"code": "EURX","value": 1000},
"counterparty": {"node": "owner1","account": "alice"},
"message": "hello world!"
}'

curl -X GET http://localhost:9200/api/v1/owner/accounts

curl -X POST http://localhost:9200/api/v1/owner/accounts/alice/transfer -H 'Content-Type: application/json' -d '{
"amount": {"code": "EURX","value": 100},
"counterparty": {"node": "owner1","account": "bob"},
"message": "hello bob!"
}'

curl -X GET http://localhost:9200/api/v1/owner/accounts/bob/transactions
```

### Alternative: manual start

#### Generate crypto material

In this step, we create all the identities which are used by the Token network. We use a normal Fabric CA for this. Technically, only the Owner identities (the wallets that will hold the tokens) need some form of hierarchy; they use Idemix credentials which must be issued by a single, known issuer (see [Fabric documentation](https://hyperledger-fabric.readthedocs.io/en/latest/idemix.html) for more info about idemix). To keep things simple, we use the same CA for the other identities too. The Token SDK expects the folders for the identities to be in Fabric's 'msp' structure.

The following crypto will be generated:

- Fabric Smart Client node identities, used by the nodes to authenticate each other
- Token Issuer identity (x509 certificate and private key)
- Token Auditor identity (x509 certificate and private key)
- Owner identities (idemix credentials)

```bash
docker-compose -f compose-ca.yaml up -d
./scripts/enroll-users.sh
```

> If you want, you can stop the CA now. You don't need it unless you want to register more users.
>
> ```bash
> docker-compose -f compose-ca.yaml down
> ```
The Issuer and Auditor identities are used by the Token Chaincode to validate token transactions. It also needs the identity of the CA that issues the Idemix credentials to the Owner wallets. The tokengen command generates the configuration that contains these identities and the cryptographic parameters for the proofs. We store it in the `tokenchaincode` folder, so that it will be baked into the chaincode docker image later.
```bash
tokengen gen dlog --base 300 --exponent 5 --issuers keys/issuer/iss/msp --idemix keys/owner1/wallet/alice --auditors keys/auditor/aud/msp --output tokenchaincode
```
> You only have to do this once. But if for any reason you want to re-generate the material: `rm -rf keys; rm tokenchaincode/zkatdlog_pp.json` and execute the steps above again.
#### Start Fabric and install the chaincode
For simplicity, in this sample all nodes use the credentials of User1 from Org1MSP and have Peer1 as a trusted peer. In a more serious setup, each instance would have its own (idemix) Fabric user and _may_ have it's own MSP and peers, depending on the network topology and trust relationships.
Start a Fabric sample network and deploy the Token Chaincode as a service:
```bash
bash "$TEST_NETWORK_HOME/network.sh" up createChannel
INIT_REQUIRED="--init-required" "$TEST_NETWORK_HOME/network.sh deployCCAAS" -ccn tokenchaincode -ccp $(pwd)/tokenchaincode -cci "init" -verbose -ccs 1
mkdir -p keys/fabric
cp -r "$TEST_NETWORK_HOME/organizations" keys/fabric/
```
> To fully remove the whole network:
> ```bash
> docker stop peer0org1_tokenchaincode_ccaas peer0org2_tokenchaincode_ccaas
> bash "$TEST_NETWORK_HOME/network.sh" down
> rm -rf keys/fabric
> ```
#### Start the Token network
```bash
docker-compose up -d
```
Visit [http://localhost:8080](http://localhost:8080) to view the API documentation and execute some transactions.
## Development
### End to end tests
See the `e2e` folder for some tests that exercise the APIs. The end to end tests require the services to be running. They create new transactions, so don't run them on a deployment you want to keep clean.
```bash
go test ./e2e -count=1 -v
```
### Code structure
This repo contains 3 different, isolated golang applications, one for each of the roles: *issuer*, *auditor*, and *owner*. They are maintained separately and each have their own dependencies. In a production scenario these would have their own lifecycle, and most likely be maintained and deployed by different organizations.
The code structure of each of the roles is the same. There is overlap between the roles; each has the boilerplate code to start the Fabric Smart Client and Token SDK. The main.go is almost identical; the only difference is which 'responders' the application registers. Also the contents of the routes and the services will depend on the features that a role needs:
- Issuers can issue funds
- Auditors see and sign every transaction
- Owners can transfer funds.
Here's an example of the code structure for the auditor:
```
auditor
├── main.go
├── oapi-server.yaml
├── conf
│ └── core.yaml
├── routes
│ ├── operations.go
│ ├── routes.gen.go
│ ├── routes.go
│ └── server.go
└── service
├── audit.go
├── balance.go
└── history.go
```
As you can see, the business logic is all in the 'service' directory. The 'routes' are purely the code needed for the REST API. We chose to use *openapi-codegen* to generate the code for the routes, and *echo* as the server. The 'routes' package is just the presentation layer; you could easily replace it and call the code from the 'service' package from somewhere else. For instance if you wanted to create a CLI application for the issuer!
![dependencies](./dependencies.png)
[plantuml](http://www.plantuml.com/plantuml/uml/RP71QlCm3CVlVWhHxnpw1X_jeR32e6FhRVIWEafgubX6ThgMqNTVQZjqAOD0PFd7pt_Pgn1Huj1RrGZs18lTboE19Mn365An7ceJMHRmhG36ht1R5qaSMl2eEsmfB00369ymWCyUZJlSM_S2_gszjqPZDEpoll0GAIGYbtymWUHiD2KedFKpSLEGxLLT_Ry3itMsAfZq1NbCy7Br99RgbWHUyHZcav2FqoWD7iNeAlGeiTBMa8ifKXF6I7lI9yUMs-iCZjoHgqBT9NByFv7pxEIZWZHYMJnIxkA91EYIRxj4uocQxzebYR24GvPcINQo-kNPN9xU2neMUDzyx67zjYrUdBoCtbIQQsh97NAhCwvYJsxSAPrn7fwEBPTSJaPrKojozT3R7m00)
For more information about how we interact with the Token SDK, check out an example on the [Token SDK GitHub](https://github.com/hyperledger-labs/fabric-token-sdk/blob/main/samples/fungible/README.md).
### Add or change a REST API endpoint
We generate the API based on `swagger.yaml`. To keep things a bit simple, we have only one definition which includes all of the roles (even though they are separate applications, running on different ports!) Any changes should be made in this file first. Then generate the code with:
```bash
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest
oapi-codegen -config auditor/oapi-server.yaml swagger.yaml
oapi-codegen -config issuer/oapi-server.yaml swagger.yaml
oapi-codegen -config owner/oapi-server.yaml swagger.yaml
oapi-codegen -config e2e/oapi-client.yaml swagger.yaml
```
### Upgrade the Token SDK and Fabric Smart Client versions
Token SDK and Fabric Smart Client are under active development. To upgrade to the latest versions:
- change the commit hash of fabric-smart-client and fabric-token-sdk in {auditor,issuer,owner}/go.mod (and do `go mod tidy`)
- change the commit hash in tokenchaincode/Dockerfile
- install tokengen with the new commit hash
- update the readme
### Use another Fabric network
Of course you're not tied to the Fabric samples testnetwork. If you want to anchor your Token services to another blockchain, you have to:
1. Deploy the token chaincode with the generated parameters to the Fabric network
2. Configure the token services with the correct channel, peer and orderer addresses and certs, MSP configuration, and a user (see the `core.yaml` files in the `conf` dir).
### Add a user / account
To add another user, simply register and enroll it at the Token CA (see `scripts/enroll-users.sh`), and configure it at one of the owner nodes (see `conf` dir).
### Run the service directly (instead of with docker-compose)
For a faster development cycle, you may choose to run the services outside of docker. It requires some adjustments to your environment to make the paths and routes work.
Add the following to your `/etc/hosts`:
```
127.0.0.1 peer0.org1.example.com
127.0.0.1 peer0.org2.example.com
127.0.0.1 orderer.example.com
127.0.0.1 owner1.example.com
127.0.0.1 owner2.example.com
127.0.0.1 auditor.example.com
127.0.0.1 issuer.example.com
```
> The Token SDK discovers the peer addresses from the channel config (after connecting to a configured trusted peer).
For the paths you have two options:
1. Find/replace all instances of /var/fsc in the conf directory with the path to this repo. **Or**
2. Create a symlink to this folder to make the configuration files work (they don't play nice with relative paths):
```bash
sudo ln -s "${PWD}" /var/fsc
```
The advantage of this approach is that the configuration is portable across developer laptops and works with docker-compose as well as without.
Start the blockchain and deploy the chaincode (see above).
Instead of doing docker-compose up, start the token services with (each in their own terminal):
```bash
mkdir bin
go build -o bin/auditor ./auditor
go build -o bin/issuer ./issuer
go build -o bin/owner ./owner
PORT=9000 CONF_DIR=./auditor/conf ./bin/auditor
PORT=9100 CONF_DIR=./issuer/conf ./bin/issuer
PORT=9200 CONF_DIR=./owner/conf/owner1 ./bin/owner
PORT=9300 CONF_DIR=./owner/conf/owner2 ./bin/owner
```
Now you can use the REST APIs to control the services (see the swagger definition).
When you made changes in the code, stop a service with CTRL+C, `go build -o bin/owner ./owner` and start it again.
If you want to reset the transaction history:
```bash
rm -rf data/auditor data/issuer data/owner1 data/owner2 && mkdir data/auditor data/issuer data/owner1 data/owner2
```
Loading

0 comments on commit 66b4935

Please sign in to comment.