Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,13 @@ This directory contains the **Fabric Token SDK**, a project under Hyperledger La
* **Docker errors:** Ensure `make testing-docker-images` has been run.
* **Linting errors on commit:** Run `make lint-auto-fix`.
* **Test timeouts:** Integration tests can be slow. Ensure you have allocated enough resources to Docker.

## Workflow Rules

- Before implementing any task, create a `plan.md` file in the project root containing:
- A clear description of the goal
- A numbered list of implementation steps
- An "Implementation Progress" section with each step marked as `[ ] Pending`
- As you complete each step, update `plan.md` immediately, marking the step as `[x] Done` and adding a brief note about what was changed
- If you encounter a blocker or make a significant decision, log it under a `## Notes & Decisions` section in `plan.md`
- Mark the plan as `✅ COMPLETE` once all steps are done
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,8 @@ lint-auto-fix:
install-linter-tool:
@echo "Installing golangci Linter"
@curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(HOME)/go/bin v2.10.1

.PHONY: fmt
fmt: ## Run gofmt on the entire project
@echo "Running gofmt..."
@gofmt -l -s -w .
41 changes: 40 additions & 1 deletion docs/core-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,22 @@ token:
workers: 10
# queueSize is the size of the event buffer. Defaults to 1000.
queueSize: 1000

# fabricx configuration for FabricX-specific settings
fabricx:
# lookup configuration for the lookup service
lookup:
# permanent lookup configuration
permanent:
# interval is the polling interval for permanent lookups. Defaults to 1m.
interval: 1m
# one-time lookup configuration
once:
# deadline is the maximum time to wait for a one-time lookup. Defaults to 5m.
deadline: 5m
# interval is the polling interval for one-time lookups. Defaults to 2s.
interval: 2s

tms:
mytms: # unique name of this token management system
network: default # the name of the network this TMS refers to (Fabric, etc.)
Expand Down Expand Up @@ -239,4 +255,27 @@ Default values:
- delivery.blockProcessParallelism: 10
- delivery.lruSize: 30
- delivery.lruBuffer: 15
- delivery.listenerTimeout: 10s
- delivery.listenerTimeout: 10s

---

## Optional: token.fabricx.lookup

If not specified, the default configuration is:

```yaml
token:
fabricx:
lookup:
permanent:
interval: 1m
once:
deadline: 5m
interval: 2s
```

Default values:

- permanent.interval: 1m
- once.deadline: 5m
- once.interval: 2s
153 changes: 126 additions & 27 deletions docs/services/network.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,132 @@ The network service architecture is depicted below:

![network_service.png](../imgs/network_service.png)

## Overview

The Network Service is a critical component of the Fabric Token SDK that abstracts the complexities of the underlying Distributed Ledger Technology (DLT). It serves several key purposes:

- **Unified Interface**: Regardless of the backend (Fabric, Fabric-X, etc.), it provides a common set of APIs for transaction management and ledger interaction.
- **Transaction Lifecycle Management**: Handles the submission (broadcasting) of transactions and provides mechanisms to wait for their finality (commitment and validity).
- **Ledger Querying (QE)**: Allows services to retrieve the current state of tokens and other ledger entries through a specialized Query Engine.
- **Public Parameters (PP) Management**: Monitors the ledger for updates to the system's public parameters and ensures the SDK is always using the latest version to maintain cryptographic integrity.
- **Identity & Membership**: Interfaces with the local Membership Service Provider (MSP) to provide identities for signing and transaction creation.

The service uses a **Driver-based architecture**, allowing for different implementations to be plugged in based on the specific requirements and features of the target network.

## Fabric

The Fabric-based network implementation utilizes the Fabric Smart Client for configuration and operations, including chaincode queries and transaction broadcasting.
Comment thread
AkramBitar marked this conversation as resolved.

During bootstrap, the Token SDK processes the TMS defined in the configuration.
For each TMS, the network provider retrieves the network instance corresponding to the `network` and `channel` specified in the TMS ID.
Failure to retrieve the network instance results in a bootstrap failure.
Upon success, the `Connect` function on the `Network` instance is invoked with the target namespace.
This function establishes a connection to the backend, enabling the Token SDK to receive updates on public parameters and transaction finality.

When `Connect` is called, the Fabric network implementation establishes two `Fabric Delivery` streams to receive committed blocks:
- One stream is used to analyze transactions that update the public parameters.
More specifically, for each transaction in a block, the parser checks if the RW set contains a write whose key is the `setup key`.
The setup key is set as `\x00seU+0000`. If such a key is found in a valid transaction, then the listener added upon calling `Connect` does the following:
- It invokes the `Update` function of the TMS provider passing the TMS ID and the byte representation of the new public parameters.
The function works as follows: if a TMS instance with the passed ID does not exist, it creates one.
If a TMS with that ID already exists, then:
- A new instance of TMS is created with the new public parameters.
- If the previous step succeeds, then the `Done` function on the old TMS instance is invoked to release all allocated resources.
- If the above step succeeds, then the public parameters are appended to the `PublicParameters` table.
- The other stream is dedicated to transaction finality. Services can add listeners to the `Network` instance to listen for the finality of specific transactions.
The `ttx` service and the `audit` service add a listener when a transaction has reached the point of being ready to be submitted to the ordering service.
(For more information, look at the sections dedicated to these services). Both services use the same listener.
This listener performs the following actions upon notification of the finality of a transaction:
- If the transaction's status is valid, then the token request's hash contained in the transaction is matched against the hash of the token request stored in the database.
If they match, then the `Tokens` table is updated by inserting the new tokens and marking the spent tokens as deleted.
The corresponding token request in the `Requests` table is marked as `Valid` with a change of the status field.
- If the transaction's status is invalid, then the corresponding token request in the `Requests` table is marked as `Invalid` or `Deleted`.
In all other cases, an error is returned.
The Fabric-based network implementation utilizes the Fabric Smart Client (FSC) to interact with the underlying Hyperledger Fabric network. It leverages FSC's configuration, transaction management, and communication layers to provide a robust backend for the Token SDK.

### Lifecycle and Bootstrap
During the Token SDK bootstrap process, the system initializes a `Network` instance for each TMS (Token Management Service) defined in the configuration. The mapping is determined by the `network` and `channel` fields in the TMS identifier. If the specified network cannot be initialized (e.g., due to missing FSC configuration for that network), the bootstrap process will fail.

Upon successful initialization, the `Connect` function is invoked for the target namespace. This step is crucial as it:
- Registers listeners for **Public Parameters** updates.
- Initializes the **Endorsement Service** for the specific namespace.
- Sets up the **Finality** and **Lookup** managers.

### Public Parameters Monitoring
The Fabric driver monitors the ledger for updates to a specific "setup key" (usually `\x00seU+0000`). It uses a `PermanentLookupListener` that triggers whenever a valid transaction writes to this key. When an update is detected:
1. The **TMS Provider** is updated with the new parameters. If a TMS instance already exists, it is replaced by a new one initialized with the updated cryptographic material, and the old instance is gracefully decommissioned.
2. The new public parameters are persisted in the local **Tokens Database** to ensure consistency across restarts.

### Finality Management
The Fabric driver supports two primary modes for monitoring transaction finality, configurable via `token.finality.type`:

- **Delivery Mode (`delivery`)**: This is the default mode for Fabric. It establishes a dedicated block delivery stream from the peer. The driver parses incoming blocks, processes read-write sets, and notifies registered listeners when a specific transaction ID is committed and validated. It includes advanced features like:
- **Parallel Processing**: Blocks and transactions can be processed in parallel to improve throughput.
- **LRU Caching**: Uses a Least Recently Used cache to track recently processed blocks and prevent redundant work.
- **Notification Mode (`notification`)**: In this mode, the driver relies on event notifications from the underlying network service rather than pulling the entire block stream.

Regardless of the mode, the `ttx` and `audit` services utilize these listeners to update the local token vault and request status (e.g., marking a request as `Valid` or `Invalid`) once a transaction reaches finality on the ledger.

## FabricX

The `fabricx` driver is a specialized implementation designed for the Fabric-X network. It shares the same overall goals as the standard Fabric driver but introduces several implementation-specific optimizations and behaviors.

### Async Finality Processing
FabricX handles transaction finality notifications asynchronously using an internal `EventQueue`. This queue is serviced by a pool of workers (by default, 10 workers with a queue size of 1000). This decoupled architecture ensures that the main network event loop remains non-blocking even when processing a high volume of finality notifications or performing complex transaction checks.

### Robust Transaction Submission
The transaction submission process in FabricX involves a multi-step preparation phase:
1. **Transaction ID Calculation**: Computes a unique ID based on a nonce and the creator's identity.
2. **Namespace Marshaling**: Uses ASN1 marshaling for the target namespace (`TxNamespace`) before signing. This ensures the transaction structure meets the specific requirements of the Fabric-X MSP and ledger.
3. **Broadcasting & Confirmation**: Once signed, the transaction is broadcast to the network. The broadcaster includes retry logic specifically for `io.EOF` errors, which often occur during network startup or transient connectivity issues.

### Public Parameters Versioning
Unlike the standard Fabric driver, FabricX employs a `VersionKeeper` to manage the lifecycle of public parameters.
- **Initialization**: The first time public parameters are updated, the version is initialized (the counter does not increment).
- **Updates**: Subsequent updates to the public parameters increment an atomic version counter.
- **Setup**: The TMS deployment process writes both the raw public parameters and their SHA256 hash to the ledger using specific setup keys defined by the translator.

### Query Engine (QE) and Token Detection
The Query Engine in FabricX is responsible for retrieving the state of tokens from the ledger. For non-graph-hiding drivers, it determines if a token is spent by checking for the absence of its key in the ledger (a `nil` raw value). It supports batch retrieval of states to minimize network round-trips.

### Finality Retries
During the initial connection phase, FabricX implements a specific retry strategy for retrieving finality information. If block 0 is not yet committed (a common scenario during network cold-starts), the driver will retry the operation (up to 5 times with a 2-second delay) to ensure a stable connection is established.

## Configuration

The Network Service and its drivers can be fine-tuned through the application configuration. Below are the key configuration parameters and examples for both Fabric and FabricX.

### TMS Configuration
Each Token Management Service must be mapped to a network and channel.

```yaml
token:
enabled: true
tms:
my-tms-id:
network: fabric-network-name # Matches fsc.networks configuration
channel: my-channel
namespace: my-chaincode-id
```

### Fabric Finality Configuration
These settings control the behavior of the Fabric driver's finality manager.

```yaml
token:
finality:
# Mode: "delivery" (default) or "notification"
type: delivery
committer:
maxRetries: 3
retryWaitDuration: 5s
delivery:
# Number of parallel workers for mapping transactions
mapperParallelism: 10
# Number of parallel workers for processing blocks
blockProcessParallelism: 10
# Size of the LRU cache for block tracking
lruSize: 30
# Wait duration before timing out a delivery listener
listenerTimeout: 10s
```

### FabricX Specific Configuration
FabricX introduces additional settings for its asynchronous event queue and lookup service.

```yaml
token:
finality:
# FabricX defaults to "notification" mode
type: notification
notification:
# Number of worker goroutines for the async queue
workers: 10
# Size of the event buffer
queueSize: 1000

fabricx:
lookup:
permanent:
# Polling interval for permanent lookups (e.g., public params)
interval: 1m
once:
# Max time allowed for a one-time lookup
deadline: 5m
# Polling interval for one-time lookups
interval: 2s
```

5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ require (
github.com/IBM/mathlib v0.0.3-0.20260204160318-5a4d1a385dfa
github.com/dgraph-io/ristretto/v2 v2.4.0
github.com/gin-gonic/gin v1.11.0
github.com/go-co-op/gocron/v2 v2.19.1
github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef
github.com/hashicorp/go-uuid v1.0.3
github.com/hyperledger-labs/fabric-smart-client v0.8.3-0.20260219175226-8f512e10d1a0
github.com/hyperledger-labs/fabric-smart-client v0.8.3-0.20260302105910-adde99d4bf54
github.com/hyperledger/fabric-chaincode-go/v2 v2.3.0
github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7
Expand Down Expand Up @@ -145,6 +146,7 @@ require (
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jaegertracing/jaeger-idl v0.6.0 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kilic/bls12-381 v0.1.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
Expand Down Expand Up @@ -231,6 +233,7 @@ require (
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/quic-go/webtransport-go v0.10.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
Expand Down
10 changes: 8 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,8 @@ github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZ
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE=
github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
github.com/go-co-op/gocron/v2 v2.19.1 h1:B4iLeA0NB/2iO3EKQ7NfKn5KsQgZfjb2fkvoZJU3yBI=
github.com/go-co-op/gocron/v2 v2.19.1/go.mod h1:5lEiCKk1oVJV39Zg7/YG10OnaVrDAV5GGR6O0663k6U=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
Expand Down Expand Up @@ -1025,8 +1027,8 @@ github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/hyperledger-labs/SmartBFT v0.0.0-20250503203013-eb005eef8866 h1:Mu/6NJsfl9g3wM15Ue7hqPq4LtgYDoABh8MO4u8aW4g=
github.com/hyperledger-labs/SmartBFT v0.0.0-20250503203013-eb005eef8866/go.mod h1:9aNHNXsCVy/leGz2gpTC1eOL5QecxbSAGjqsLh4T1LM=
github.com/hyperledger-labs/fabric-smart-client v0.8.3-0.20260219175226-8f512e10d1a0 h1:iXfHzs9I08/ok4XuAbcxEcz5qa7nQF6+oImB5VOMo/0=
github.com/hyperledger-labs/fabric-smart-client v0.8.3-0.20260219175226-8f512e10d1a0/go.mod h1:DHm6Q7lmQC6TIEy++a/hmDNRi6q05bAfRt0v2wfzFGQ=
github.com/hyperledger-labs/fabric-smart-client v0.8.3-0.20260302105910-adde99d4bf54 h1:a/bkgpkgkKJvW57SDM8RuKEZrygqsguJztNw7pOo0rg=
github.com/hyperledger-labs/fabric-smart-client v0.8.3-0.20260302105910-adde99d4bf54/go.mod h1:e0xYtdh2E4DgaSlS0sQ7xWjIVD1NA2y+11x3llSLzGY=
github.com/hyperledger/aries-bbs-go v0.0.0-20240528084656-761671ea73bc h1:3Ykk6MtyfnlzMOQry9zkxsoLWpCWZwDPqehO/BJwArM=
github.com/hyperledger/aries-bbs-go v0.0.0-20240528084656-761671ea73bc/go.mod h1:Kofn6A6WWea1ZM8Rys5aBW9dszwJ7Ywa0kyyYL0TPYw=
github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 h1:B1Nt8hKb//KvgGRprk0h1t4lCnwhE9/ryb1WqfZbV+M=
Expand Down Expand Up @@ -1092,6 +1094,8 @@ github.com/jaegertracing/jaeger-idl v0.6.0 h1:LOVQfVby9ywdMPI9n3hMwKbyLVV3BL1XH2
github.com/jaegertracing/jaeger-idl v0.6.0/go.mod h1:mpW0lZfG907/+o5w5OlnNnig7nHJGT3SfKmRqC42HGQ=
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE=
github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
Expand Down Expand Up @@ -1386,6 +1390,8 @@ github.com/quic-go/webtransport-go v0.10.0/go.mod h1:LeGIXr5BQKE3UsynwVBeQrU1TPr
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
Expand Down
2 changes: 1 addition & 1 deletion integration/token/fungible/support.go
Original file line number Diff line number Diff line change
Expand Up @@ -1046,7 +1046,7 @@ func UpdatePublicParamsAndWait(network *integration.Infrastructure, publicParams
if node.Id() == "custodian" {
continue
}
gomega.Eventually(GetPublicParams).WithArguments(network, node).WithTimeout(30 * time.Second).WithPolling(15 * time.Second).Should(gomega.Equal(publicParams))
gomega.Eventually(GetPublicParams).WithArguments(network, node).WithTimeout(60 * time.Second).WithPolling(5 * time.Second).Should(gomega.Equal(publicParams))
Comment thread
AkramBitar marked this conversation as resolved.
}
}

Expand Down
Loading
Loading