Skip to content

Commit d534a52

Browse files
committed
feat: Add TokenX integration and fix FabricX transaction mismatch
Signed-off-by: gutama <ginanjar.utama@gmail.com>
1 parent a042a1d commit d534a52

File tree

26 files changed

+4791
-16
lines changed

26 files changed

+4791
-16
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ install-tools: ## Install all tools
4040
.PHONY: install-linter
4141
install-linter-tool: ## Install linter in $(go env GOPATH)/bin
4242
@echo "Installing golangci Linter"
43-
@curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.4.0
43+
@curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v2.4.0
4444

4545
.PHONY: install-fxconfig
4646
install-fxconfig: ## Install fxconfig in $(FAB_BINS)
@@ -151,6 +151,7 @@ INTEGRATION_TARGETS += fabric-twonets
151151
## fabricx section
152152
INTEGRATION_TARGETS += fabricx-iou
153153
INTEGRATION_TARGETS += fabricx-simple
154+
INTEGRATION_TARGETS += fabricx-tokenx
154155

155156
.PHONE: list-integration-tests
156157
list-integration-tests: ## List all integration tests
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.PHONY: test test-verbose clean help
2+
3+
# Default target
4+
help:
5+
@echo "TokenX - FabricX Token Management"
6+
@echo ""
7+
@echo "Available targets:"
8+
@echo " test - Run integration tests"
9+
@echo " test-verbose - Run tests with verbose output"
10+
@echo " clean - Clean generated artifacts"
11+
@echo " help - Show this help message"
12+
13+
# Run integration tests
14+
test:
15+
go test ./...
16+
17+
# Run tests with verbose output
18+
test-verbose:
19+
go test -v ./...
20+
21+
# Run with Ginkgo
22+
test-ginkgo:
23+
ginkgo -v
24+
25+
# Clean generated artifacts
26+
clean:
27+
rm -rf ./out
28+
rm -rf ./testdata
29+
30+
# Generate OpenAPI documentation (requires swagger-codegen)
31+
docs:
32+
@echo "OpenAPI spec available at: api/openapi.yaml"
33+
@echo "View with: npx @redocly/cli preview-docs api/openapi.yaml"
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
# TokenX - FabricX Token Management Application
2+
3+
A comprehensive token management system built on FabricX with privacy-preserving identities, multiple token types, and atomic swaps.
4+
5+
## Features
6+
7+
- **Three Roles**: Issuer, Auditor, Owner
8+
- **UTXO Token Model**: Tokens as discrete states with splitting support
9+
- **Idemix Privacy**: Anonymous identities for all token owners
10+
- **Multiple Token Types**: USD, EUR, GOLD, or any custom type
11+
- **Transfer Limits**: Configurable per-transaction limits
12+
- **Atomic Swaps**: Exchange tokens of different types atomically
13+
- **REST API**: Documented OpenAPI 3.0 specification
14+
- **Decimal Support**: 8 decimal places precision
15+
16+
## Quick Start
17+
18+
### Prerequisites
19+
20+
- Go 1.19+
21+
- Docker (for Fabric network)
22+
- Make
23+
24+
### Run Tests
25+
26+
```bash
27+
cd /path/to/fabric-smart-client/integration/fabricx/tokenx
28+
29+
# Run all tests
30+
go test -v ./...
31+
32+
# Or with Ginkgo
33+
ginkgo -v
34+
```
35+
36+
## Architecture
37+
38+
```
39+
┌─────────────────────────────────────────────────────────────────┐
40+
│ TokenX Network │
41+
├─────────────────────────────────────────────────────────────────┤
42+
│ Fabric Topology │
43+
│ - 3 Organizations: Org1 (Issuer), Org2 (Auditor), Org3 (Owners)│
44+
│ - Idemix enabled for anonymous identities │
45+
│ - Namespace: tokenx with Org1 endorsement │
46+
├─────────────────────────────────────────────────────────────────┤
47+
│ FSC Nodes │
48+
│ ┌──────────┐ ┌──────────┐ ┌────────────────────────┐ │
49+
│ │ Issuer │ │ Auditor │ │ Owners (Idemix) │ │
50+
│ │ (Org1) │ │ (Org2) │ │ alice, bob, charlie │ │
51+
│ │ - issue │ │ -balances│ │ - transfer │ │
52+
│ │ - approve│ │ -history │ │ - redeem │ │
53+
│ └──────────┘ └──────────┘ │ - swap │ │
54+
│ └────────────────────────┘ │
55+
└─────────────────────────────────────────────────────────────────┘
56+
```
57+
58+
## Token Operations
59+
60+
### Issue Tokens
61+
62+
The issuer creates new tokens and assigns them to an owner:
63+
64+
```go
65+
// Issue 1000 USD tokens to Alice
66+
result, _ := client.CallView("issue", &views.Issue{
67+
TokenType: "USD",
68+
Amount: states.TokenFromFloat(1000), // 100000000000
69+
Recipient: aliceIdentity,
70+
})
71+
```
72+
73+
### Transfer Tokens
74+
75+
Owners can transfer tokens to other owners:
76+
77+
```go
78+
// Alice transfers 300 USD to Bob
79+
result, _ := client.CallView("transfer", &views.Transfer{
80+
TokenLinearID: "TKN:abc123",
81+
Amount: states.TokenFromFloat(300),
82+
Recipient: bobIdentity,
83+
Approver: issuerIdentity,
84+
})
85+
```
86+
87+
**Partial transfers** are supported - if you transfer less than the token amount, a "change" token is created for the sender.
88+
89+
### Redeem Tokens
90+
91+
Owners can burn tokens (with issuer approval):
92+
93+
```go
94+
result, _ := client.CallView("redeem", &views.Redeem{
95+
TokenLinearID: "TKN:abc123",
96+
Amount: states.TokenFromFloat(100),
97+
Approver: issuerIdentity,
98+
})
99+
```
100+
101+
### Atomic Swap
102+
103+
Exchange tokens of different types atomically:
104+
105+
```go
106+
// Alice proposes: give 100 USD, want 80 EUR
107+
proposalID, _ := aliceClient.CallView("swap_propose", &views.SwapPropose{
108+
OfferedTokenID: "TKN:usd123",
109+
RequestedType: "EUR",
110+
RequestedAmount: states.TokenFromFloat(80),
111+
ExpiryMinutes: 60,
112+
})
113+
114+
// Bob accepts with his EUR token
115+
txID, _ := bobClient.CallView("swap_accept", &views.SwapAccept{
116+
ProposalID: proposalID,
117+
OfferedTokenID: "TKN:eur456",
118+
Approver: issuerIdentity,
119+
})
120+
```
121+
122+
## Token Amounts
123+
124+
All amounts use **8 decimal places** precision (similar to Bitcoin satoshis):
125+
126+
| Display Amount | Internal Value |
127+
|----------------|----------------|
128+
| 1.00000000 | 100000000 |
129+
| 0.50000000 | 50000000 |
130+
| 0.00000001 | 1 |
131+
132+
Use the helper functions:
133+
```go
134+
amount := states.TokenFromFloat(100.5) // 10050000000
135+
display := token.AmountFloat() // 100.5
136+
```
137+
138+
## Transfer Limits
139+
140+
Default transfer limits are configured in `states/states.go`:
141+
142+
| Limit | Default Value |
143+
|-------|---------------|
144+
| Max per transaction | 1,000,000 tokens |
145+
| Min amount | 0.00000001 tokens |
146+
| Daily limit | Unlimited |
147+
148+
## REST API
149+
150+
The API is documented in OpenAPI 3.0 format: `api/openapi.yaml`
151+
152+
### Endpoints
153+
154+
| Method | Endpoint | Description |
155+
|--------|----------|-------------|
156+
| POST | `/v1/tokens/issue` | Issue new tokens |
157+
| POST | `/v1/tokens/transfer` | Transfer tokens |
158+
| POST | `/v1/tokens/redeem` | Redeem/burn tokens |
159+
| GET | `/v1/tokens/balance` | Get token balance |
160+
| GET | `/v1/tokens/history` | Get transaction history |
161+
| POST | `/v1/tokens/swap/propose` | Propose atomic swap |
162+
| POST | `/v1/tokens/swap/accept` | Accept atomic swap |
163+
| GET | `/v1/audit/balances` | Auditor: all balances |
164+
| GET | `/v1/audit/history` | Auditor: all transactions |
165+
166+
## Project Structure
167+
168+
```
169+
tokenx/
170+
├── api/
171+
│ ├── handlers.go # REST API handlers
172+
│ └── openapi.yaml # API documentation
173+
├── states/
174+
│ └── states.go # Token, TransactionRecord, SwapProposal
175+
├── views/
176+
│ ├── issue.go # Issue tokens
177+
│ ├── transfer.go # Transfer tokens
178+
│ ├── redeem.go # Burn tokens
179+
│ ├── balance.go # Query balances
180+
│ ├── auditor.go # Auditor views
181+
│ ├── swap.go # Atomic swaps
182+
│ ├── approver.go # Validation logic
183+
│ └── utils.go # Helpers
184+
├── sdk.go # SDK registration
185+
├── topology.go # Network topology
186+
├── tokenx_test.go # Integration tests
187+
├── tokenx_suite_test.go # Test suite
188+
└── README.md # This file
189+
```
190+
191+
## Privacy with Idemix
192+
193+
All owner nodes use Idemix anonymous identities:
194+
195+
- **Unlinkability**: Transactions from the same owner cannot be linked
196+
- **Privacy**: Owner identities are not revealed on-chain
197+
- **Multiple Accounts**: Each owner can have multiple Idemix credentials
198+
199+
Enabled in topology:
200+
```go
201+
fscTopology.AddNodeByName("alice").
202+
AddOptions(fabric.WithAnonymousIdentity()) // Idemix
203+
```
204+
205+
## Auditor Restrictions
206+
207+
Auditors can view:
208+
- ✅ Token types and amounts
209+
- ✅ Transaction history
210+
- ✅ Aggregate supply
211+
212+
Auditors **cannot** view:
213+
- ❌ Token metadata
214+
- ❌ Private properties
215+
- ❌ Detailed owner information (due to Idemix)
216+
217+
## Development
218+
219+
### Adding a New Token Type
220+
221+
Simply issue tokens with a new type name:
222+
```go
223+
IssueTokens(ii, "MY_NEW_TOKEN", amount, "alice")
224+
```
225+
226+
### Extending Swap Functionality
227+
228+
The swap implementation is designed for extension. Key areas:
229+
- `SwapProposal` struct in `states/states.go` - add new fields
230+
- `validateSwap` in `views/approver.go` - add new validations
231+
- Add new swap-related views as needed
232+
233+
## Development Notes
234+
235+
### Running Integration Tests
236+
237+
**Important:** Always clean Docker before running tests:
238+
239+
```bash
240+
# Clean up Docker environment
241+
docker stop $(docker ps -q) 2>/dev/null
242+
docker rm $(docker ps -aq) 2>/dev/null
243+
docker network prune -f
244+
245+
# Verify port 7050 is free
246+
sudo lsof -i :7050 || echo "Port 7050 is free"
247+
248+
# Run the test
249+
cd /path/to/fabric-smart-client
250+
make integration-tests-fabricx-tokenx
251+
```
252+
253+
### Known Issues & Fixes
254+
255+
#### RWSet Endorsement Mismatch (FIXED)
256+
257+
**Issue:** Endorsement collection failed with "received different results" error.
258+
259+
**Root Cause:** FabricX's RWSet serialization includes namespace versions read from the local vault. When the approver re-serialized the RWSet, it used its own versions which differed from the issuer's.
260+
261+
**Fix Applied:** Modified `platform/fabricx/core/transaction/transaction.go` to use received RWSet bytes directly instead of re-serializing. See [TASK.md](TASK.md) for details.
262+
263+
#### Sidecar Port Mismatch (FIXED)
264+
265+
**Issue:** Test hanged at "Post execution for FSC nodes...".
266+
267+
**Root Cause:** The Sidecar container used a dynamic port (e.g., 5420), but the client configuration was hardcoded to `5411`.
268+
269+
**Fix Applied:** Updated `integration/nwo/fabricx/extensions/scv2` to dynamically propagate the correct sidecar port to the client configuration.
270+
271+
#### Docker Port Conflicts
272+
273+
**Issue:** Test fails with "port 7050 already allocated"
274+
275+
**Solution:** Clean Docker containers before running tests (see above).
276+
277+
### Comparing with Simple Example
278+
279+
The `integration/fabricx/simple/` project is a minimal working example of the same pattern. When debugging tokenx, compare with simple:
280+
281+
| TokenX | Simple |
282+
|--------|--------|
283+
| `views/issue.go` | `views/create.go` |
284+
| `views/approver.go` | `views/approve.go` |
285+
| `states/states.go` | `views/state.go` |
286+
| `topology.go` | `topo.go` |
287+
288+
### Debug Logging
289+
290+
The codebase has extensive debug logging. Enable by checking `fsc.SetLogging()` in topology.go:
291+
292+
```go
293+
fscTopology.SetLogging("grpc=error:fabricx=debug:info", "")
294+
```
295+
296+
### Documentation
297+
298+
- [TASK.md](TASK.md) - Current development status and remaining work
299+
- [WALKTHROUGH.md](WALKTHROUGH.md) - Detailed code walkthrough
300+
- [SPECIFICATION.md](SPECIFICATION.md) - Full system specification
301+
302+
## License
303+
304+
Apache-2.0

0 commit comments

Comments
 (0)