Skip to content

Commit b6cd5c4

Browse files
committed
import the SimpleChat app, a tool for testing at app-level
1 parent e7fe3f5 commit b6cd5c4

16 files changed

+2987
-0
lines changed

py/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Python programs for tinySSB
22

3+
- [simplechat](simplechat) - a TUI client for the tinySSB non-private chat application, based on simplepub
34
- [simplepub](simplepub) - a server for feed replicas with two interfaces: websocket and BLE
45

56
---

py/simplechat/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# emacs backup files
2+
*!
3+
4+
__pycache__

py/simplechat/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Computer Networks Group, University of Basel
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

py/simplechat/README.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# tinySSB Simple Chat
2+
3+
SimpleChat is a text-user-interface app for reading and writing public
4+
chat messages in tinySSB. It serves mostly as a testing tool.
5+
6+
The main program is ```simplechat.py```. It is built on top of
7+
SimplePub, hence shares several configuration options with
8+
it. Internal logging messages can be seen by toggeling with ESC the
9+
screen between the chat and log view.
10+
11+
In comparison to SimplePub, SimpleChat has a notion of authorship. If
12+
not existing, an ED25519 private key is generated and the corresponding
13+
append-only log is added to the repository of the unterlying
14+
SimplePub. Authorship permits to create and sign new log entries,
15+
which SimplChat requires in order to post messages.
16+
17+
## Description
18+
19+
"tinySSB simple chat" displays all public 'Text-And-Voice' messages found in
20+
a repo's content; new messages can be added. The replication logic running underneath SimpleChat:
21+
- is accessible via web sockets, and
22+
- is accessible via BLE (central-only)
23+
- runs the tinySSB synchronization protocol
24+
- datagram-based, packets have 120 Bytes or less
25+
- growOnlySet protocol for compressing feed IDs
26+
- vectors with WANT and CHNK information
27+
- has adaptive timers, made for reliable connections
28+
- is crash resistant: the ```frontier.bin``` file for a log is updated on startup, should the log have been extended but the frontier failed to be updated
29+
30+
See the README.md of SimplePub for the limitation.
31+
32+
A log entry for the ```TAV``` public chat of tinySSB has the following
33+
format:
34+
35+
```
36+
[ "TAV", # identifies the app
37+
"a text string", # the posted text
38+
#c0dec2010..#, # codec2-encoded voice, or null
39+
1691098012 ] # timestamp
40+
```
41+
42+
This list of four values is BIPF-encoded and stored in the tinySSB
43+
append-only log as a "chain20" log entry type.
44+
45+
46+
## ```simplechat.py``` - a p2p tinySSB chat client
47+
48+
```
49+
usage: simplechat.py [-h] [-data DATAPATH] [-id IDPATH] [-role {in,inout,out}] [-v] [uri_or_port]
50+
51+
positional arguments:
52+
uri_or_port TCP port if responder, URI if intiator (default is ws://127.0.0.1:8080)
53+
54+
options:
55+
-h, --help show this help message and exit
56+
-ble enable Bluetooth Low Energ (default: off)
57+
-data DATAPATH path to persistency directory (default: ./data)
58+
-id IDPATH path to tinySSB private directory (default: ~/.tinySSB)
59+
-role {in,inout,out} direction of data flow (default: inout)
60+
-v print i/o timestamps
61+
```
62+
63+
Example how Alice and Bob start their chat clients (on the same machine):
64+
```
65+
% ./simplechat.py -d ./alice -i ./alice -v 8080 # Alice' clients will respond on websocket port 8080
66+
67+
% ./simplechat.py -d ./bob -i ./bob -v ws://127.0.0.1:8080 # Bob is initiator
68+
```
69+
70+
## Screenshots
71+
72+
The screen of Bob, before sending the reply:
73+
```
74+
-────────── tinyS─────-- <w> connection up─┐
75+
│ │
76+
│ #19 [TVUK3-ZVR4B] │
77+
│ Heyo local butts, what's going on! │
78+
│ _ ⏲ 2023-06-24 19:48:45 │
79+
│ │
80+
│ #20 [6F24K-PBSQ6] │
81+
│ <voice msg> │
82+
│ _ ⏲ 2023-06-26 01:10:18 │
83+
│ │
84+
│ #21 [73ZOO-QOWFW] │
85+
│ Message │
86+
│ _ ⏲ 2023-07-04 07:50:33 │
87+
│ │
88+
│ #22 [QDS2Z-Z3U6W] │
89+
│ Message │
90+
│ _ ⏲ 2023-07-04 08:02:44 │
91+
│ │
92+
│ #23 [7NQQX-YZUDL] │
93+
│ Hi Bob, how are you? Cheers, Alice │
94+
│ _ ⏲ 2023-08-03 23:22:45 │
95+
│ │
96+
┌────────────────────────────────────./bob─┐
97+
│> Hi Alice, all is fine. Best, Bob │
98+
└─────────────────── ESC=log/chat ^C=exit ─┘
99+
```
100+
101+
The screen of Alice, after having received the reply:
102+
```
103+
/────────── tinyS─────────────sending 120B─┐
104+
│ │
105+
│ #20 [6F24K-PBSQ6] │
106+
│ <voice msg> │
107+
│ _ ⏲ 2023-06-26 01:10:18 │
108+
│ │
109+
│ #21 [73ZOO-QOWFW] │
110+
│ Message │
111+
│ _ ⏲ 2023-07-04 07:50:33 │
112+
│ │
113+
│ #22 [QDS2Z-Z3U6W] │
114+
│ Message │
115+
│ _ ⏲ 2023-07-04 08:02:44 │
116+
│ │
117+
│ #23 [7NQQX-YZUDL] │
118+
│ Hi Bob, how are you? Cheers, Alice │
119+
│ _ ⏲ 2023-08-03 23:22:45 │
120+
│ │
121+
│ #24 [LJUGR-FULBS] │
122+
│ Hi Alice, all is fine. Best, Bob │
123+
│ _ ⏲ 2023-08-03 23:26:52 │
124+
│ │
125+
┌──────────────────────────────────./alice─┐
126+
│> │
127+
└─────────────────── ESC=log/chat ^C=exit ─┘
128+
```
129+
130+
131+
Showing the log view (switching is done with ESC)
132+
```
133+
-────^[──── tinyS─────-- <w> connection up─┐
134+
│ │
135+
│ gset dmx 613dfa70c47aba │
136+
│ want dmx 343fc019f3c9ad │
137+
│ chnk dmx 2af30fda04c000 │
138+
│ Starting websocket responder on port 808 │
139+
│ w> 0.000 o=1 61B 0x343fc019f3c9ada40│
140+
│ <w 0.001 i=2 22B 0x2af30fda04c000743│
141+
│ <w 0.001 i=3 105B 0x613dfa70c47aba631│
142+
│ w> 4.002 o=4 61B 0x343fc019f3c9ada40│
143+
│ <w 4.003 i=5 22B 0x2af30fda04c000743│
144+
│ new c=[ 1.6.32 1.7.0 ] │
145+
│ w> 8.004 o=6 61B 0x343fc019f3c9ada40│
146+
│ <w 8.007 i=7 22B 0x2af30fda04c000743│
147+
│ new c=[ 1.6.32 1.7.0 ] │
148+
│ w> 12.006 o=8 61B 0x343fc019f3c9ada4│
149+
│ <w 12.007 i=9 22B 0x2af30fda04c00074│
150+
│ <w 15.002 i=10 105B 0x613dfa70c47aba63│
151+
│ w> 16.008 o=11 61B 0x343fc019f3c9ada4│
152+
│ <w 16.010 i=12 22B 0x2af30fda04c00074│
153+
│ new c=[ 1.6.32 1.7.0 ] │
154+
│ =C [ 1.6.32 1.7.0 ] [] │
155+
┌──────────────────────────────────./alice─┐
156+
│> │
157+
└─────────────────── ESC=log/chat ^C=exit ─┘
158+
```
159+
160+
---

py/simplechat/frontier.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env python3
2+
3+
# frontier.py
4+
# display local frontier state
5+
6+
# Jul 2023 <christian.tschudin@unibas.ch>
7+
8+
# ---------------------------------------------------------------------------
9+
10+
def bytes2hex(d):
11+
if type(d) == list:
12+
return [bytes2hex(x) for x in d]
13+
if type(d) == dict:
14+
return {bytes2hex(k):bytes2hex(v) for k,v in d.items()}
15+
if type(d) == bytes:
16+
return f'#{d.hex()}#'
17+
return d
18+
19+
def bytes2content(buf):
20+
if buf == None:
21+
return '||'
22+
try:
23+
buf = bipf.loads(buf)
24+
return f"bipf({bytes2hex(buf)})"
25+
except:
26+
return buf.decode('utf-8', 'replace')
27+
28+
if __name__ == '__main__':
29+
30+
import argparse
31+
import os
32+
33+
from simplepub import bipf, replica
34+
35+
ap = argparse.ArgumentParser()
36+
ap.add_argument('-d', type=str, default='./data', metavar='DATAPATH',
37+
help='path to persistency directory')
38+
ap.add_argument('-raw', action='store_true', default=False,
39+
help='dump raw log entries and side chain packets')
40+
ap.add_argument('-stat', action="store_true", default=False,
41+
help='only show stats (no content), default: False')
42+
args = ap.parse_args()
43+
44+
keys = [ bytes.fromhex(fn) for fn in os.listdir(args.d)
45+
if len(fn) == 64 and os.path.isdir(args.d + '/' + fn)]
46+
keys.sort()
47+
48+
cnt_entries = 0
49+
cnt_chunks = 0
50+
cnt_missing = 0
51+
missing = []
52+
53+
for i in range(len(keys)):
54+
fid = keys[i];
55+
r = replica.Replica(args.d,fid,None)
56+
if not args.stat:
57+
print(f"* key {i} {fid.hex()}")
58+
ms = r.state['max_seq']
59+
cnt_entries += ms
60+
if not args.stat:
61+
print(f" max_seq = {ms}, prev = {r.state['prev'].hex()}")
62+
psc = r.state['pend_sc']
63+
for k in range(ms):
64+
e = r.get_entry_pkt(k+1)
65+
if e[7] == 1 and not k+1 in psc: # chain20 with full sidechain
66+
clen, sz = bipf.varint_decode(e[8:])
67+
clen -= 48 - 20 - sz
68+
if clen > 0:
69+
cnt_chunks += (clen + 99) // 100
70+
if len(psc) != 0:
71+
for s,v in psc.items():
72+
missing.append(f"{i}.{s}.{v[0]}ff")
73+
cnt_chunks += v[0]
74+
cnt_missing += v[1]
75+
psc = {s:f"{v[0]}/{v[0]+v[1]}" for s,v in psc.items()}
76+
if not args.stat:
77+
print(f" pend_sc = {psc}")
78+
if ms > 0 and not args.stat:
79+
for k in range(ms):
80+
a,l = r.get_content_len(k+1)
81+
print(f" #{k+1} "[:10] + f" {a}/{l} "[-13:], end='')
82+
if args.raw:
83+
print(r.read(k+1).hex())
84+
else:
85+
print(bytes2content(r.read(k+1)))
86+
87+
if not args.stat:
88+
print()
89+
print("Stats:")
90+
print(f"- {len(keys)} feeds")
91+
print(f"- {cnt_entries} available entries")
92+
print(f"- {cnt_chunks} available chunks")
93+
print(f"- {cnt_missing} missing chunks: {', '.join(missing)}")
94+
# eof

py/simplechat/pure25519/LICENSE

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2022-03-21 <christian.tschudin@unibas.ch> moving/merging code among files
2+
3+
"python-pure25519" Copyright (c) 2015 Brian Warner and other contributors
4+
https://github.com/warner/python-pure25519
5+
6+
Permission is hereby granted, free of charge, to any person
7+
obtaining a copy of this software and associated documentation
8+
files (the "Software"), to deal in the Software without
9+
restriction, including without limitation the rights to use,
10+
copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the
12+
Software is furnished to do so, subject to the following
13+
conditions:
14+
15+
The above copyright notice and this permission notice shall be
16+
included in all copies or substantial portions of the Software.
17+
18+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25+
OTHER DEALINGS IN THE SOFTWARE.
26+

0 commit comments

Comments
 (0)