1 | """ |
---|
2 | Ported to Python 3. |
---|
3 | """ |
---|
4 | |
---|
5 | from zope.interface import Interface |
---|
6 | from foolscap.api import StringConstraint, SetOf, DictOf, Any, \ |
---|
7 | RemoteInterface, Referenceable |
---|
8 | FURL = StringConstraint(1000) |
---|
9 | |
---|
10 | # v2 protocol over foolscap: Announcements are 3-tuples of (msg, sig_vs, |
---|
11 | # claimed_key_vs): |
---|
12 | # * msg (bytes): UTF-8(json(ann_dict)) |
---|
13 | # * ann_dict has IntroducerClient-provided keys like "version", "nickname", |
---|
14 | # "app-versions", "my-version", "oldest-supported", and "service-name". |
---|
15 | # Plus service-specific keys like "anonymous-storage-FURL" and |
---|
16 | # "permutation-seed-base32" (both for service="storage"). |
---|
17 | # * sig_vs (bytes): "v0-"+base32(signature(msg)) |
---|
18 | # * claimed_key_vs (bytes): "v0-"+base32(pubkey) |
---|
19 | |
---|
20 | # (nickname, my_version, oldest_supported) refer to the client as a whole. |
---|
21 | # The my_version/oldest_supported strings can be parsed by an |
---|
22 | # allmydata.util.version.Version instance, and then compared. The first goal |
---|
23 | # is to make sure that nodes are not confused by speaking to an incompatible |
---|
24 | # peer. The second goal is to enable the development of |
---|
25 | # backwards-compatibility code. |
---|
26 | |
---|
27 | # Note that old v1 clients (which are gone now) did not sign messages, so v2 |
---|
28 | # servers would deliver v2-format messages with sig_vs=claimed_key_vs=None. |
---|
29 | # These days we should always get a signature and a pubkey. |
---|
30 | |
---|
31 | Announcement_v2 = Any() |
---|
32 | |
---|
33 | class RIIntroducerSubscriberClient_v2(RemoteInterface): |
---|
34 | __remote_name__ = "RIIntroducerSubscriberClient_v2.tahoe.allmydata.com" |
---|
35 | |
---|
36 | def announce_v2(announcements=SetOf(Announcement_v2)): |
---|
37 | """I accept announcements from the publisher.""" |
---|
38 | return None |
---|
39 | |
---|
40 | SubscriberInfo = DictOf(bytes, Any()) |
---|
41 | |
---|
42 | class RIIntroducerPublisherAndSubscriberService_v2(RemoteInterface): |
---|
43 | """To publish a service to the world, connect to me and give me your |
---|
44 | announcement message. I will deliver a copy to all connected subscribers. |
---|
45 | To hear about services, connect to me and subscribe to a specific |
---|
46 | service_name.""" |
---|
47 | __remote_name__ = "RIIntroducerPublisherAndSubscriberService_v2.tahoe.allmydata.com" |
---|
48 | |
---|
49 | def get_version(): |
---|
50 | return DictOf(bytes, Any()) |
---|
51 | |
---|
52 | def publish_v2(announcement=Announcement_v2, canary=Referenceable): |
---|
53 | return None |
---|
54 | |
---|
55 | def subscribe_v2(subscriber=RIIntroducerSubscriberClient_v2, |
---|
56 | service_name=bytes, subscriber_info=SubscriberInfo): |
---|
57 | """Give me a subscriber reference, and I will call its announce_v2() |
---|
58 | method with any announcements that match the desired service name. I |
---|
59 | will ignore duplicate subscriptions. The subscriber_info dictionary |
---|
60 | tells me about the subscriber, and is used for diagnostic/status |
---|
61 | displays.""" |
---|
62 | return None |
---|
63 | |
---|
64 | class IIntroducerClient(Interface): |
---|
65 | """I provide service introduction facilities for a node. I help nodes |
---|
66 | publish their services to the rest of the world, and I help them learn |
---|
67 | about services available on other nodes.""" |
---|
68 | |
---|
69 | def publish(service_name, ann, signing_key): |
---|
70 | """Publish the given announcement dictionary (which must be |
---|
71 | JSON-serializable), plus some additional keys, to the world. |
---|
72 | |
---|
73 | Each announcement is characterized by a (service_name, serverid) |
---|
74 | pair. When the server sees two announcements with the same pair, the |
---|
75 | later one will replace the earlier one. The serverid is derived from |
---|
76 | the signing_key, if present, otherwise it is derived from the |
---|
77 | 'anonymous-storage-FURL' key. |
---|
78 | |
---|
79 | signing_key (a SigningKey) will be used to sign the announcement.""" |
---|
80 | |
---|
81 | def subscribe_to(service_name, callback, *args, **kwargs): |
---|
82 | """Call this if you will eventually want to use services with the |
---|
83 | given SERVICE_NAME. This will prompt me to subscribe to announcements |
---|
84 | of those services. Your callback will be invoked with at least two |
---|
85 | arguments: a pubkey and an announcement dictionary, followed by any |
---|
86 | additional callback args/kwargs you gave me. The pubkey will be None |
---|
87 | unless the announcement was signed by the corresponding pubkey, in |
---|
88 | which case it will be a printable string like 'v0-base32..'. |
---|
89 | |
---|
90 | I will run your callback for both new announcements and for |
---|
91 | announcements that have changed, but you must be prepared to tolerate |
---|
92 | duplicates. |
---|
93 | |
---|
94 | The announcement that I give you comes from some other client. It |
---|
95 | will be a JSON-serializable dictionary which (by convention) is |
---|
96 | expected to have at least the following keys: |
---|
97 | |
---|
98 | version: 0 |
---|
99 | nickname: unicode |
---|
100 | app-versions: {} |
---|
101 | my-version: bytes |
---|
102 | oldest-supported: bytes |
---|
103 | |
---|
104 | service-name: bytes('storage') |
---|
105 | anonymous-storage-FURL: bytes(furl) |
---|
106 | |
---|
107 | In order to be JSON-serializable, all byte strings are assumed to be |
---|
108 | ASCII-encoded, and the receiver can therefore decode them into Unicode |
---|
109 | strings if they wish. Representation of these fields elsewhere in Tahoe |
---|
110 | may differ, e.g. by being unicode strings. |
---|
111 | |
---|
112 | Note that app-version will be an empty dictionary if either the |
---|
113 | publishing client or the Introducer are running older code. |
---|
114 | """ |
---|
115 | |
---|
116 | def connected_to_introducer(): |
---|
117 | """Returns a boolean, True if we are currently connected to the |
---|
118 | introducer, False if not.""" |
---|
119 | |
---|