Skip to content

Commit db1e874

Browse files
authored
fabricx: notification service integration (#1313)
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
1 parent d264d0d commit db1e874

50 files changed

Lines changed: 4443 additions & 234 deletions

Some content is hidden

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

docs/core-token.md

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,11 @@ token:
4242
# b) The transaction will reach finality shortly, so we append a listener and wait for a timeout. If the listener reaches timeout, we proceed to step c.
4343
# c) The transaction reached finality long ago, so we query the whole ledger for this specific transaction. If the query returns no result, we proceed to step d.
4444
# d) The transaction will reach finality at some point beyond the timeout or never, so we return Unknown. Then it is up to the client to either append another listener or accept that the transaction will never reach finality.
45-
# committer: The manager subscribes to the commit pipeline and receives all finalized transactions.
46-
# If we subscribe for a transaction that hasn't been finalized yet, we will get notified once it reaches the commit pipeline.
47-
# For listeners that haven't been invoked yet, either the transaction hasn't been finalized or it was finalized before we subscribed.
48-
# For this reason, there is a periodic polling period (1s) that queries all txIDs for which there is a pending listener and invokes the listeners for the ones found.
49-
# Once we get notified about finality, we try (repeatedly) to fetch the additional tx information needed from the vault (e.g., transactionRequest).
50-
# If we work without replicas, there is no need to try more than once to fetch the additional tx information, because the finality notification and vault update happen in sync.
51-
# It is only needed in case we have replicas, where another replica may have updated the vault shortly before.
45+
# notification: The manager is notified about finality events via a notification service (e.g. for FabricX).
46+
# When a new notification arrives, an event is added to a queue for asynchronous processing.
47+
# When a client subscribes to the manager for a specific transaction, we perform an immediate query to check its status.
48+
# The field can also be left empty. In that case, the default option will be used depending on the network type each TMS refers to.
5249
type: delivery
53-
# Only applicable when type = 'committer'
54-
committer:
55-
# maxRetries is the number of times we try to fetch the additional tx information from the vault.
56-
maxRetries: 3
57-
# retryWaitDuration is the duration to wait before retrying.
58-
retryWaitDuration: 5s
5950
# Only applicable when type = 'delivery'
6051
delivery:
6152
# mapperParallelism is the number of goroutines that process incoming transactions in parallel. Defaults to 1.
@@ -76,6 +67,12 @@ token:
7667
# If the timeout is not set, then the listener will never be evicted and we will never proceed to step c.
7768
# We will wait forever for the transaction to return (as is done for the 'committer' type).
7869
listenerTimeout: 10s
70+
# Only applicable when type = 'notification'
71+
notification:
72+
# workers is the number of goroutines that process events in parallel. Defaults to 10.
73+
workers: 10
74+
# queueSize is the size of the event buffer. Defaults to 1000.
75+
queueSize: 1000
7976
tms:
8077
mytms: # unique name of this token management system
8178
network: default # the name of the network this TMS refers to (Fabric, etc.)

docs/services/network.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## The Network Service
1+
# The Network Service
22

33
The `network` service, located under `token/services/network`, provides other services with a consistent and predictable interface to the backend (e.g., Fabric).
44
Internally, the network service mirrors the structure of the Token API, consisting of a `Provider` of network instances and the `Network` instances themselves.
@@ -7,6 +7,8 @@ The network service architecture is depicted below:
77

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

10+
## Fabric
11+
1012
The Fabric-based network implementation utilizes the Fabric Smart Client for configuration and operations, including chaincode queries and transaction broadcasting.
1113

1214
During bootstrap, the Token SDK processes the TMS defined in the configuration.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ require (
1313
github.com/gin-gonic/gin v1.11.0
1414
github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef
1515
github.com/hashicorp/go-uuid v1.0.3
16-
github.com/hyperledger-labs/fabric-smart-client v0.8.1
16+
github.com/hyperledger-labs/fabric-smart-client v0.8.2-0.20260213141142-cafdc428edab
1717
github.com/hyperledger/fabric-chaincode-go/v2 v2.3.0
1818
github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5
1919
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,8 +1023,8 @@ github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
10231023
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
10241024
github.com/hyperledger-labs/SmartBFT v0.0.0-20250503203013-eb005eef8866 h1:Mu/6NJsfl9g3wM15Ue7hqPq4LtgYDoABh8MO4u8aW4g=
10251025
github.com/hyperledger-labs/SmartBFT v0.0.0-20250503203013-eb005eef8866/go.mod h1:9aNHNXsCVy/leGz2gpTC1eOL5QecxbSAGjqsLh4T1LM=
1026-
github.com/hyperledger-labs/fabric-smart-client v0.8.1 h1:w5wE564sydDWoz4725r0GjVtb3fb8vMVHre7cEcyYQg=
1027-
github.com/hyperledger-labs/fabric-smart-client v0.8.1/go.mod h1:bRA/GQB0XOhjbnckKjaJ0Yhj5Gf1rzwUN1VfcHyrLIk=
1026+
github.com/hyperledger-labs/fabric-smart-client v0.8.2-0.20260213141142-cafdc428edab h1:9K0uUke+X779dsbbAbQy8ZxpVH9uYWKv/TZx/2DEqDE=
1027+
github.com/hyperledger-labs/fabric-smart-client v0.8.2-0.20260213141142-cafdc428edab/go.mod h1:yKdQlHiq7Q5B8ngBCjwrwCOez7nLMh8R3zSdpSCtmeM=
10281028
github.com/hyperledger/aries-bbs-go v0.0.0-20240528084656-761671ea73bc h1:3Ykk6MtyfnlzMOQry9zkxsoLWpCWZwDPqehO/BJwArM=
10291029
github.com/hyperledger/aries-bbs-go v0.0.0-20240528084656-761671ea73bc/go.mod h1:Kofn6A6WWea1ZM8Rys5aBW9dszwJ7Ywa0kyyYL0TPYw=
10301030
github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 h1:B1Nt8hKb//KvgGRprk0h1t4lCnwhE9/ryb1WqfZbV+M=

integration/nwo/token/template.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ token:
1313
selector:
1414
driver: {{ TokenSelector }}
1515
finality:
16-
type: {{ FinalityType }}
16+
# we leave type empty so that the default is peaked per type of network
17+
type:
1718
delivery:
1819
mapperParallelism: 10
1920
lruSize: 100

integration/token/fungible/views/transfer.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,10 +477,12 @@ func (t *BroadcastPreparedTransferView) Call(context view.Context) (interface{},
477477
assert.NoError(err, "failed unmarshalling transaction")
478478

479479
// broadcast the transaction to the ordering service
480+
logger.Debugf("broadcasting transaction [%s]", tx.ID())
480481
_, err = context.RunView(ttx.NewOrderingView(tx))
481482
assert.NoError(err, "failed asking ordering")
482483

483484
if t.Finality {
485+
logger.Debugf("transaction [%s], wait for finality", tx.ID())
484486
// wait for finality
485487
_, err = context.RunView(ttx.NewFinalityView(tx))
486488
assert.NoError(err, "failed asking ordering")

token/services/auditor/auditor.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ func (a *Service) Append(ctx context.Context, tx Transaction) error {
120120
logger.DebugfContext(ctx, "register tx status listener for tx [%s] at network [%s]", tx.ID(), tx.Network())
121121
var r driver.FinalityListener = finality.NewListener(
122122
logger,
123+
net,
124+
tx.Namespace(),
123125
a.tmsProvider,
124126
a.tmsID,
125127
a.auditDB,

token/services/auditor/manager.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,20 @@ func (cm *ServiceManager) RestoreTMS(tmsID token.TMSID) error {
122122
return iterators.ForEach(it, func(record *storage.TokenRequestRecord) error {
123123
logger.Debugf("restore transaction [%s] with status [%s]", record.TxID, TxStatusMessage[record.Status])
124124

125-
return net.AddFinalityListener(tmsID.Namespace, record.TxID, finality.NewListener(logger, cm.tmsProvider, tmsID, auditor.auditDB, tokenDB, auditor.finalityTracer))
125+
return net.AddFinalityListener(
126+
tmsID.Namespace,
127+
record.TxID,
128+
finality.NewListener(
129+
logger,
130+
net,
131+
tmsID.Namespace,
132+
cm.tmsProvider,
133+
tmsID,
134+
auditor.auditDB,
135+
tokenDB,
136+
auditor.finalityTracer,
137+
),
138+
)
126139
})
127140
}
128141

token/services/network/driver/network.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,6 @@ type Network interface {
7878
// In this case, the listener is not removed
7979
AddFinalityListener(namespace string, txID string, listener FinalityListener) error
8080

81-
// RemoveFinalityListener unregisters the passed listener.
82-
RemoveFinalityListener(id string, listener FinalityListener) error
83-
8481
// LookupTransferMetadataKey searches for a transfer metadata key containing the passed sub-key starting from the passed transaction id in the given namespace.
8582
// The operation gets canceled if the passed timeout elapses or, if stopOnLastTx is true, when the last transaction in the vault is reached.
8683
LookupTransferMetadataKey(namespace string, key string, timeout time.Duration) ([]byte, error)
@@ -96,7 +93,4 @@ type FinalityListenerManager interface {
9693
// If the transaction id is empty, the listener will be called on status changes of any transaction.
9794
// In this case, the listener is not removed
9895
AddFinalityListener(namespace string, txID string, listener FinalityListener) error
99-
100-
// RemoveFinalityListener unregisters the passed listener.
101-
RemoveFinalityListener(id string, listener FinalityListener) error
10296
}

token/services/network/fabric/config/config.go

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,28 @@ type ListenerManagerConfig interface {
2424
DeliveryLRUBuffer() int
2525
}
2626

27+
const (
28+
Type = "token.finality.type"
29+
CommitterMaxRetries = "token.finality.committer.maxRetries"
30+
CommitterRetryWaitDuration = "token.finality.committer.retryWaitDuration"
31+
DeliveryMapperParallelism = "token.finality.delivery.mapperParallelism"
32+
DeliveryBlockProcessParallelism = "token.finality.delivery.blockProcessParallelism"
33+
DeliveryLRUSize = "token.finality.delivery.lruSize"
34+
DeliveryLRUBuffer = "token.finality.delivery.lruBuffer"
35+
DeliveryListenerTimeout = "token.finality.delivery.listenerTimeout"
36+
DefaultCommitterMaxRetries = 3
37+
DefaultCommitterRetryWaitDuration = 5 * time.Second
38+
DefaultDeliveryMapperParallelism = 10
39+
DefaultDeliveryLRUSize = 30
40+
DefaultDeliveryLRUBuffer = 15
41+
DefaultDeliveryListenerTimeout = 10 * time.Second
42+
)
43+
2744
type ManagerType string
2845

2946
const (
30-
Delivery ManagerType = "delivery"
47+
Delivery ManagerType = "delivery"
48+
Notification ManagerType = "notification"
3149
)
3250

3351
func NewListenerManagerConfig(configService driver.ConfigService) *serviceListenerManagerConfig {
@@ -39,63 +57,63 @@ type serviceListenerManagerConfig struct {
3957
}
4058

4159
func (c *serviceListenerManagerConfig) Type() ManagerType {
42-
if v := ManagerType(c.c.GetString("token.finality.type")); len(v) > 0 {
60+
if v := ManagerType(c.c.GetString(Type)); len(v) > 0 {
4361
return v
4462
}
4563

4664
return Delivery
4765
}
4866

4967
func (c *serviceListenerManagerConfig) CommitterMaxRetries() int {
50-
if v := c.c.GetInt("token.finality.committer.maxRetries"); v >= 0 {
68+
if v := c.c.GetInt(CommitterMaxRetries); v >= 0 {
5169
return v
5270
}
5371

54-
return 3
72+
return DefaultCommitterMaxRetries
5573
}
5674

5775
func (c *serviceListenerManagerConfig) CommitterRetryWaitDuration() time.Duration {
58-
if v := c.c.GetDuration("token.finality.committer.retryWaitDuration"); v >= 0 {
76+
if v := c.c.GetDuration(CommitterRetryWaitDuration); v >= 0 {
5977
return v
6078
}
6179

62-
return 5 * time.Second
80+
return DefaultCommitterRetryWaitDuration
6381
}
6482

6583
func (c *serviceListenerManagerConfig) DeliveryMapperParallelism() int {
66-
if v := c.c.GetInt("token.finality.delivery.mapperParallelism"); v > 0 {
84+
if v := c.c.GetInt(DeliveryMapperParallelism); v > 0 {
6785
return v
6886
}
6987

70-
return 10
88+
return DefaultDeliveryMapperParallelism
7189
}
7290

7391
func (c *serviceListenerManagerConfig) DeliveryBlockProcessParallelism() int {
74-
return c.c.GetInt("token.finality.delivery.blockProcessParallelism")
92+
return c.c.GetInt(DeliveryBlockProcessParallelism)
7593
}
7694

7795
func (c *serviceListenerManagerConfig) DeliveryLRUSize() int {
78-
if v := c.c.GetInt("token.finality.delivery.lruSize"); v >= 0 {
96+
if v := c.c.GetInt(DeliveryLRUSize); v >= 0 {
7997
return v
8098
}
8199

82-
return 30
100+
return DefaultDeliveryLRUSize
83101
}
84102

85103
func (c *serviceListenerManagerConfig) DeliveryLRUBuffer() int {
86-
if v := c.c.GetInt("token.finality.delivery.lruBuffer"); v >= 0 {
104+
if v := c.c.GetInt(DeliveryLRUBuffer); v >= 0 {
87105
return v
88106
}
89107

90-
return 15
108+
return DefaultDeliveryLRUBuffer
91109
}
92110

93111
func (c *serviceListenerManagerConfig) DeliveryListenerTimeout() time.Duration {
94-
if v := c.c.GetDuration("token.finality.delivery.listenerTimeout"); v >= 0 {
112+
if v := c.c.GetDuration(DeliveryListenerTimeout); v >= 0 {
95113
return v
96114
}
97115

98-
return 10 * time.Second
116+
return DefaultDeliveryListenerTimeout
99117
}
100118

101119
func (c *serviceListenerManagerConfig) String() string {

0 commit comments

Comments
 (0)