Skip to content

Commit aeec387

Browse files
authored
Merge pull request #1544 from hyperledger/docs_v1.3.1
docs: ahead of v1.3.1
2 parents ee5f03e + c5718bb commit aeec387

File tree

8 files changed

+297
-39
lines changed

8 files changed

+297
-39
lines changed

doc-site/docs/reference/events.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ Once you have configured the blockchain event listener, every event detected
187187
from the blockchain will result in a FireFly event delivered to your application
188188
of type `blockchain_event_received`.
189189

190+
As of 1.3.1 a group of event filters can be established under a single topic when supported by the connector, which has benefits for ordering.
191+
See [Contract Listeners](../reference/types/contractlistener.md) for more detail
192+
190193
Check out the [Custom Contracts Tutorial](../tutorials/custom_contracts/index.md) for
191194
a walk-through of how to set up listeners for the events from your smart contracts.
192195

doc-site/docs/reference/types/_includes/contractlistener_description.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,144 @@ of the interface for that event.
44

55
Check out the [Custom Contracts Tutorial](../../tutorials/custom_contracts/index.md) for
66
a walk-through of how to set up listeners for the events from your smart contracts.
7+
8+
See below for a deep dive into the format of contract listeners and important concepts to understand when managing them.
9+
10+
### Event filters
11+
12+
#### Multiple filters
13+
14+
From v1.3.1 onwards, a contract listener can be created with multiple filters under a single topic, when supported by the connector. Each filter contains:
15+
16+
- a reference to a specific blockchain event to listen for
17+
- (optional) a specific location/address to listen from
18+
- a connector-specific signature (generated from the event and the location)
19+
20+
In addition to this list of multiple filters, the listener specifies a single `topic` to identify the stream of events.
21+
22+
Creating a single listener that listens for multiple events will allow for the easiest management of listeners, and for strong ordering of the events that they process.
23+
24+
#### Single filter
25+
26+
Before v1.3.1, each contract listener would only support listening to one specific event from a contract interface. Each listener would be comprised of:
27+
28+
- a reference to a specific blockchain event to listen for
29+
- (optional) a specific location/address to listen from
30+
- a connector-specific signature (generated from the event), which allows you to easily identify and search for the contact listener for an event
31+
- a `topic` which determines the ordered stream that these events are part of
32+
33+
For backwards compatibility, this format is still supported by the API.
34+
35+
### Signature strings
36+
37+
#### String format
38+
39+
Each filter is identified by a generated `signature` that matches a single event, and each contract listener is identified by a `signature` computed from its filters.
40+
41+
Ethereum provides a string standard for event signatures, of the form `EventName(uint256,bytes)`. Prior to v1.3.1, the signature of each Ethereum contract listener would exactly follow this Ethereum format.
42+
43+
As of v1.3.1, Ethereum signature strings have been changed, because this format does not fully describe the event - particularly because each top-level parameter can in the ABI definition be marked as `indexed`. For example, while the following two Solidity events have the same signature, they are serialized differently due to the different placement of `indexed` parameters, and thus a listener must define both individually to be able to process them:
44+
45+
- ERC-20 `Transfer`
46+
47+
```solidity
48+
event Transfer(address indexed _from, address indexed _to, uint256 _value)
49+
```
50+
51+
- ERC-721 `Transfer`
52+
53+
```solidity
54+
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
55+
```
56+
57+
The two above are now expressed in the following manner by the Ethereum blockchain connector:
58+
59+
```solidity
60+
Transfer(address,address,uint256) [i=0,1]
61+
Transfer(address,address,uint256) [i=0,1,2]
62+
```
63+
64+
The `[i=]` listing at the end of the signature indicates the position of all parameters that are marked as `indexed`.
65+
66+
Building on the blockchain-specific signature format for each event, FireFly will then compute the final signature for each filter and each contract listener as follows:
67+
68+
- Each filter signature is a combination of the location and the specific connector event signature, such as `0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1:Changed(address,uint256) [i=0]`
69+
- Each contract listener signature is a concatenation of all the filter signatures, separated by `;`
70+
71+
#### Duplicate filters
72+
73+
FireFly restricts the creation of a contract listener containing duplicate filters.
74+
75+
This includes the special case where one filter is a superset of another filter, due to a wildcard location.
76+
77+
For example, if two filters are listening to the same event, but one has specified a location and the other hasn't, then the latter will be a superset, and already be listening to all the events matching the first filter. Creation of duplicate or superset filters within a single listener will be blocked.
78+
79+
#### Duplicate listeners
80+
81+
As noted above, each listener has a generated signature. This signature - containing all the locations and event signatures combined with the listener topic - will guarantee uniqueness of the contract listener. If you tried to create the same listener again, you would receive HTTP 409. This combination can allow a developer to assert that their listener exists, without the risk of creating duplicates.
82+
83+
**Note:** Prior to v1.3.1, FireFly would detect duplicates simply by requiring a unique combination of signature + topic + location for each listener. The updated behavior for the listener signature is intended to preserve similar functionality, even when dealing with listeners that contain many event filters.
84+
85+
### Backwards compatibility
86+
87+
As noted throughout this document, the behavior of listeners is changed in v1.3.1. However, the following behaviors are retained for backwards-compatibility, to ensure that code written prior to v1.3.1 should continue to function.
88+
89+
- The response from all query APIs of `listeners` will continue to populate top-level `event` and `location` fields
90+
- The first entry from the `filters` array is duplicated to these fields
91+
- On input to create a new `listener`, the `event` and `location` fields are still supported
92+
- They function identically to supplying a `filters` array with a single entry
93+
- The `signature` field is preserved at the listener level
94+
- The format has been changed as described above
95+
96+
### Input formats
97+
98+
The two input formats supported when creating a contract listener are shown below.
99+
100+
**Muliple Filters**
101+
102+
```json
103+
{
104+
"filters": [
105+
{
106+
"interface": {
107+
"id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
108+
},
109+
"location": {
110+
"address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
111+
},
112+
"eventPath": "Changed"
113+
},
114+
{
115+
"interface": {
116+
"id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
117+
},
118+
"location": {
119+
"address": "0xa4ea5d0b6b2eaf194716f0cc73981939dca27da1"
120+
},
121+
"eventPath": "AnotherEvent"
122+
}
123+
],
124+
"options": {
125+
"firstEvent": "newest"
126+
},
127+
"topic": "simple-storage"
128+
}
129+
```
130+
131+
**One filter (old format)**
132+
133+
```json
134+
{
135+
"interface": {
136+
"id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
137+
},
138+
"location": {
139+
"address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
140+
},
141+
"eventPath": "Changed",
142+
"options": {
143+
"firstEvent": "newest"
144+
},
145+
"topic": "simple-storage"
146+
}
147+
```

doc-site/docs/reference/types/contractlistener.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,35 @@ title: ContractListener
3434
}
3535
]
3636
},
37-
"signature": "Changed(uint256)",
37+
"signature": "0x596003a91a97757ef1916c8d6c0d42592630d2cf:Changed(uint256)",
3838
"topic": "app1_topic",
3939
"options": {
4040
"firstEvent": "newest"
41-
}
41+
},
42+
"filters": [
43+
{
44+
"event": {
45+
"name": "Changed",
46+
"description": "",
47+
"params": [
48+
{
49+
"name": "x",
50+
"schema": {
51+
"type": "integer",
52+
"details": {
53+
"type": "uint256",
54+
"internalType": "uint256"
55+
}
56+
}
57+
}
58+
]
59+
},
60+
"location": {
61+
"address": "0x596003a91a97757ef1916c8d6c0d42592630d2cf"
62+
},
63+
"signature": "0x596003a91a97757ef1916c8d6c0d42592630d2cf:Changed(uint256)"
64+
}
65+
]
4266
}
4367
```
4468

doc-site/docs/releasenotes/index.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ title: Release Notes
44

55
[Full release notes](https://github.com/hyperledger/firefly/releases)
66

7+
## [v1.3.1 - Aug 5, 2024](https://github.com/hyperledger/firefly/releases/tag/v1.3.1)
8+
9+
What's New:
10+
11+
- Enable contract listeners with multiple filters
12+
See [Contract Listeners](../reference/types/contractlistener.md) for details
13+
- New multiparty status API at `/status/multiparty`
14+
715
## [v1.3.0 - April 25, 2024](https://github.com/hyperledger/firefly/releases/tag/v1.1.0)
816

917
[Migration guide](1.3_migration_guide.md)

doc-site/docs/tutorials/chains/index.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

doc-site/docs/tutorials/custom_contracts/ethereum.md

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -648,21 +648,25 @@ Here is an example of sending 100 wei with a transaction:
648648

649649
Now that we've seen how to submit transactions and preform read-only queries to the blockchain, let's look at how to receive blockchain events so we know when things are happening in realtime.
650650

651-
If you look at the source code for the smart contract we're working with above, you'll notice that it emits an event when the stored value of the integer is set. In order to receive these events, we first need to instruct FireFly to listen for this specific type of blockchain event. To do this, we create an **Event Listener**. The `/contracts/listeners` endpoint is RESTful so there are `POST`, `GET`, and `DELETE` methods available on it. To create a new listener, we will make a `POST` request. We are going to tell FireFly to listen to events with name `"Changed"` from the FireFly Interface we defined earlier, referenced by its ID. We will also tell FireFly which contract address we expect to emit these events, and the topic to assign these events to. Topics are a way for applications to subscribe to events they are interested in.
651+
If you look at the source code for the smart contract we're working with above, you'll notice that it emits an event when the stored value of the integer is set. In order to receive these events, we first need to instruct FireFly to listen for this specific type of blockchain event. To do this, we create an **Event Listener**. The `/contracts/listeners` endpoint is RESTful so there are `POST`, `GET`, and `DELETE` methods available on it. To create a new listener, we will make a `POST` request. We are going to tell FireFly to listen to events with name `"Changed"` from the FireFly Interface we defined earlier, referenced by its ID. We will also tell FireFly which contract address we expect to emit these events, and the topic to assign these events to. You can specify multiple filters for a listener, in this case we only specify one for our event. Topics are a way for applications to subscribe to events they are interested in.
652652

653653
### Request
654654

655655
`POST` `http://localhost:5000/api/v1/namespaces/default/contracts/listeners`
656656

657657
```json
658658
{
659-
"interface": {
660-
"id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
661-
},
662-
"location": {
663-
"address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
664-
},
665-
"eventPath": "Changed",
659+
"filters": [
660+
{
661+
"interface": {
662+
"id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
663+
},
664+
"location": {
665+
"address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
666+
},
667+
"eventPath": "Changed"
668+
}
669+
],
666670
"options": {
667671
"firstEvent": "newest"
668672
},
@@ -674,17 +678,17 @@ If you look at the source code for the smart contract we're working with above,
674678

675679
```json
676680
{
677-
"id": "1bfa3b0f-3d90-403e-94a4-af978d8c5b14",
681+
"id": "e7c8457f-4ffd-42eb-ac11-4ad8aed30de1",
678682
"interface": {
679-
"id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
683+
"id": "55fdb62a-fefc-4313-99e4-e3f95fcca5f0"
680684
},
681685
"namespace": "default",
682-
"name": "sb-66209ffc-d355-4ac0-7151-bc82490ca9df",
683-
"protocolId": "sb-66209ffc-d355-4ac0-7151-bc82490ca9df",
686+
"name": "019104d7-bb0a-c008-76a9-8cb923d91b37",
687+
"backendId": "019104d7-bb0a-c008-76a9-8cb923d91b37",
684688
"location": {
685689
"address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
686690
},
687-
"created": "2022-02-17T22:02:36.34549538Z",
691+
"created": "2024-07-30T18:12:12.704964Z",
688692
"event": {
689693
"name": "Changed",
690694
"description": "",
@@ -712,9 +716,49 @@ If you look at the source code for the smart contract we're working with above,
712716
}
713717
]
714718
},
719+
"signature": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1:Changed(address,uint256) [i=0]",
720+
"topic": "simple-storage",
715721
"options": {
716-
"firstEvent": "oldest"
717-
}
722+
"firstEvent": "newest"
723+
},
724+
"filters": [
725+
{
726+
"event": {
727+
"name": "Changed",
728+
"description": "",
729+
"params": [
730+
{
731+
"name": "from",
732+
"schema": {
733+
"type": "string",
734+
"details": {
735+
"type": "address",
736+
"internalType": "address",
737+
"indexed": true
738+
}
739+
}
740+
},
741+
{
742+
"name": "value",
743+
"schema": {
744+
"type": "integer",
745+
"details": {
746+
"type": "uint256",
747+
"internalType": "uint256"
748+
}
749+
}
750+
}
751+
]
752+
},
753+
"location": {
754+
"address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
755+
},
756+
"interface": {
757+
"id": "55fdb62a-fefc-4313-99e4-e3f95fcca5f0"
758+
},
759+
"signature": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1:Changed(address,uint256) [i=0]"
760+
}
761+
]
718762
}
719763
```
720764

doc-site/docs/tutorials/custom_contracts/fabric.md

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -576,16 +576,20 @@ The `/contracts/listeners` endpoint is RESTful so there are `POST`, `GET`, and `
576576

577577
```json
578578
{
579-
"interface": {
580-
"id": "f1e5522c-59a5-4787-bbfd-89975e5b0954"
581-
},
582-
"location": {
583-
"channel": "firefly",
584-
"chaincode": "asset_transfer"
585-
},
586-
"event": {
587-
"name": "AssetCreated"
588-
},
579+
"filters": [
580+
{
581+
"interface": {
582+
"id": "f1e5522c-59a5-4787-bbfd-89975e5b0954"
583+
},
584+
"location": {
585+
"channel": "firefly",
586+
"chaincode": "asset_transfer"
587+
},
588+
"event": {
589+
"name": "AssetCreated"
590+
}
591+
}
592+
],
589593
"options": {
590594
"firstEvent": "oldest"
591595
},
@@ -597,25 +601,39 @@ The `/contracts/listeners` endpoint is RESTful so there are `POST`, `GET`, and `
597601

598602
```json
599603
{
600-
"id": "6e7f5dd8-5a57-4163-a1d2-5654e784dc31",
604+
"id": "d6b5e774-c9e5-474c-9495-ec07fa47a907",
601605
"namespace": "default",
602-
"name": "sb-2cac2bfa-38af-4408-4ff3-973421410e5d",
603-
"backendId": "sb-2cac2bfa-38af-4408-4ff3-973421410e5d",
606+
"name": "sb-44aa348a-bafb-4243-594e-dcad689f1032",
607+
"backendId": "sb-44aa348a-bafb-4243-594e-dcad689f1032",
604608
"location": {
605609
"channel": "firefly",
606610
"chaincode": "asset_transfer"
607611
},
608-
"created": "2022-05-02T17:19:13.144561086Z",
612+
"created": "2024-07-22T15:36:58.514085959Z",
609613
"event": {
610614
"name": "AssetCreated",
611615
"description": "",
612616
"params": null
613617
},
614-
"signature": "AssetCreated",
618+
"signature": "firefly-asset_transfer:AssetCreated",
615619
"topic": "assets",
616620
"options": {
617621
"firstEvent": "oldest"
618-
}
622+
},
623+
"filters": [
624+
{
625+
"event": {
626+
"name": "AssetCreated",
627+
"description": "",
628+
"params": null
629+
},
630+
"location": {
631+
"channel": "firefly",
632+
"chaincode": "asset_transfer"
633+
},
634+
"signature": "firefly-asset_transfer:AssetCreated"
635+
}
636+
]
619637
}
620638
```
621639

0 commit comments

Comments
 (0)