2018-04-21 07:30:08 +00:00
|
|
|
from __future__ import absolute_import, print_function, unicode_literals
|
|
|
|
|
2017-07-04 17:50:21 +00:00
|
|
|
import re
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
from automat import MethodicalMachine
|
2018-04-21 07:30:08 +00:00
|
|
|
from zope.interface import implementer
|
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
from . import _interfaces
|
2017-03-17 23:50:37 +00:00
|
|
|
from ._wordlist import PGPWordList
|
2017-07-04 17:50:21 +00:00
|
|
|
from .errors import KeyFormatError
|
|
|
|
|
|
|
|
|
|
|
|
def validate_nameplate(nameplate):
|
|
|
|
if not re.search(r'^\d+$', nameplate):
|
2018-04-21 07:30:08 +00:00
|
|
|
raise KeyFormatError(
|
|
|
|
"Nameplate '%s' must be numeric, with no spaces." % nameplate)
|
|
|
|
|
2017-02-15 20:11:17 +00:00
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
@implementer(_interfaces.INameplate)
|
|
|
|
class Nameplate(object):
|
2017-02-15 20:11:17 +00:00
|
|
|
m = MethodicalMachine()
|
2018-04-21 07:30:08 +00:00
|
|
|
set_trace = getattr(m, "_setTrace",
|
|
|
|
lambda self, f: None) # pragma: no cover
|
2017-02-24 02:11:07 +00:00
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
def __init__(self):
|
|
|
|
self._nameplate = None
|
|
|
|
|
2017-03-17 23:50:37 +00:00
|
|
|
def wire(self, mailbox, input, rendezvous_connector, terminator):
|
2017-02-26 11:57:58 +00:00
|
|
|
self._M = _interfaces.IMailbox(mailbox)
|
2017-03-17 23:50:37 +00:00
|
|
|
self._I = _interfaces.IInput(input)
|
2017-02-22 19:26:11 +00:00
|
|
|
self._RC = _interfaces.IRendezvousConnector(rendezvous_connector)
|
2017-02-26 11:57:58 +00:00
|
|
|
self._T = _interfaces.ITerminator(terminator)
|
2017-02-15 20:11:17 +00:00
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
# all -A states: not connected
|
|
|
|
# all -B states: yes connected
|
|
|
|
# B states serialize as A, so they deserialize as unconnected
|
2017-02-15 20:11:17 +00:00
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
# S0: know nothing
|
2017-02-15 20:11:17 +00:00
|
|
|
@m.state(initial=True)
|
2018-04-21 07:30:08 +00:00
|
|
|
def S0A(self):
|
|
|
|
pass # pragma: no cover
|
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
@m.state()
|
2018-04-21 07:30:08 +00:00
|
|
|
def S0B(self):
|
|
|
|
pass # pragma: no cover
|
2017-02-26 11:57:58 +00:00
|
|
|
|
|
|
|
# S1: nameplate known, never claimed
|
|
|
|
@m.state()
|
2018-04-21 07:30:08 +00:00
|
|
|
def S1A(self):
|
|
|
|
pass # pragma: no cover
|
2017-02-26 11:57:58 +00:00
|
|
|
|
|
|
|
# S2: nameplate known, maybe claimed
|
|
|
|
@m.state()
|
2018-04-21 07:30:08 +00:00
|
|
|
def S2A(self):
|
|
|
|
pass # pragma: no cover
|
|
|
|
|
2017-02-15 20:11:17 +00:00
|
|
|
@m.state()
|
2018-04-21 07:30:08 +00:00
|
|
|
def S2B(self):
|
|
|
|
pass # pragma: no cover
|
2017-02-26 11:57:58 +00:00
|
|
|
|
|
|
|
# S3: nameplate claimed
|
2017-02-22 19:26:11 +00:00
|
|
|
@m.state()
|
2018-04-21 07:30:08 +00:00
|
|
|
def S3A(self):
|
|
|
|
pass # pragma: no cover
|
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
@m.state()
|
2018-04-21 07:30:08 +00:00
|
|
|
def S3B(self):
|
|
|
|
pass # pragma: no cover
|
2017-02-26 11:57:58 +00:00
|
|
|
|
|
|
|
# S4: maybe released
|
|
|
|
@m.state()
|
2018-04-21 07:30:08 +00:00
|
|
|
def S4A(self):
|
|
|
|
pass # pragma: no cover
|
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
@m.state()
|
2018-04-21 07:30:08 +00:00
|
|
|
def S4B(self):
|
|
|
|
pass # pragma: no cover
|
2017-02-26 11:57:58 +00:00
|
|
|
|
|
|
|
# S5: released
|
|
|
|
# we no longer care whether we're connected or not
|
2018-04-21 07:30:08 +00:00
|
|
|
# @m.state()
|
|
|
|
# def S5A(self): pass
|
|
|
|
# @m.state()
|
|
|
|
# def S5B(self): pass
|
2017-02-26 11:57:58 +00:00
|
|
|
@m.state()
|
2018-04-21 07:30:08 +00:00
|
|
|
def S5(self):
|
|
|
|
pass # pragma: no cover
|
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
S5A = S5
|
|
|
|
S5B = S5
|
|
|
|
|
|
|
|
# from Boss
|
2017-07-04 17:50:21 +00:00
|
|
|
def set_nameplate(self, nameplate):
|
2018-04-21 07:30:08 +00:00
|
|
|
validate_nameplate(nameplate) # can raise KeyFormatError
|
2017-07-04 17:50:21 +00:00
|
|
|
self._set_nameplate(nameplate)
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
@m.input()
|
2018-04-21 07:30:08 +00:00
|
|
|
def _set_nameplate(self, nameplate):
|
|
|
|
pass
|
2017-02-26 11:57:58 +00:00
|
|
|
|
|
|
|
# from Mailbox
|
|
|
|
@m.input()
|
2018-04-21 07:30:08 +00:00
|
|
|
def release(self):
|
|
|
|
pass
|
2017-02-26 11:57:58 +00:00
|
|
|
|
|
|
|
# from Terminator
|
|
|
|
@m.input()
|
2018-04-21 07:30:08 +00:00
|
|
|
def close(self):
|
|
|
|
pass
|
2017-02-15 20:11:17 +00:00
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
# from RendezvousConnector
|
2017-02-15 20:11:17 +00:00
|
|
|
@m.input()
|
2018-04-21 07:30:08 +00:00
|
|
|
def connected(self):
|
|
|
|
pass
|
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
@m.input()
|
2018-04-21 07:30:08 +00:00
|
|
|
def lost(self):
|
|
|
|
pass
|
2017-02-26 11:57:58 +00:00
|
|
|
|
2017-02-15 20:11:17 +00:00
|
|
|
@m.input()
|
2018-04-21 07:30:08 +00:00
|
|
|
def rx_claimed(self, mailbox):
|
|
|
|
pass
|
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
@m.input()
|
2018-04-21 07:30:08 +00:00
|
|
|
def rx_released(self):
|
|
|
|
pass
|
2017-02-15 20:11:17 +00:00
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
@m.output()
|
|
|
|
def record_nameplate(self, nameplate):
|
2017-07-04 17:50:21 +00:00
|
|
|
validate_nameplate(nameplate)
|
2017-02-26 11:57:58 +00:00
|
|
|
self._nameplate = nameplate
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
@m.output()
|
|
|
|
def record_nameplate_and_RC_tx_claim(self, nameplate):
|
2017-07-04 17:50:21 +00:00
|
|
|
validate_nameplate(nameplate)
|
2017-02-26 11:57:58 +00:00
|
|
|
self._nameplate = nameplate
|
|
|
|
self._RC.tx_claim(self._nameplate)
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
@m.output()
|
|
|
|
def RC_tx_claim(self):
|
|
|
|
# when invoked via M.connected(), we must use the stored nameplate
|
|
|
|
self._RC.tx_claim(self._nameplate)
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2017-02-15 20:11:17 +00:00
|
|
|
@m.output()
|
2017-03-17 23:50:37 +00:00
|
|
|
def I_got_wordlist(self, mailbox):
|
|
|
|
# TODO select wordlist based on nameplate properties, in rx_claimed
|
|
|
|
wordlist = PGPWordList()
|
|
|
|
self._I.got_wordlist(wordlist)
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2017-03-17 23:50:37 +00:00
|
|
|
@m.output()
|
2017-02-26 11:57:58 +00:00
|
|
|
def M_got_mailbox(self, mailbox):
|
|
|
|
self._M.got_mailbox(mailbox)
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2017-02-15 20:11:17 +00:00
|
|
|
@m.output()
|
2017-02-26 11:57:58 +00:00
|
|
|
def RC_tx_release(self):
|
|
|
|
assert self._nameplate
|
|
|
|
self._RC.tx_release(self._nameplate)
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2017-02-26 11:57:58 +00:00
|
|
|
@m.output()
|
|
|
|
def T_nameplate_done(self):
|
|
|
|
self._T.nameplate_done()
|
|
|
|
|
2017-07-04 17:50:21 +00:00
|
|
|
S0A.upon(_set_nameplate, enter=S1A, outputs=[record_nameplate])
|
2017-02-26 11:57:58 +00:00
|
|
|
S0A.upon(connected, enter=S0B, outputs=[])
|
|
|
|
S0A.upon(close, enter=S5A, outputs=[T_nameplate_done])
|
2018-04-21 07:30:08 +00:00
|
|
|
S0B.upon(
|
|
|
|
_set_nameplate, enter=S2B, outputs=[record_nameplate_and_RC_tx_claim])
|
2017-02-26 11:57:58 +00:00
|
|
|
S0B.upon(lost, enter=S0A, outputs=[])
|
|
|
|
S0B.upon(close, enter=S5A, outputs=[T_nameplate_done])
|
|
|
|
|
|
|
|
S1A.upon(connected, enter=S2B, outputs=[RC_tx_claim])
|
|
|
|
S1A.upon(close, enter=S5A, outputs=[T_nameplate_done])
|
|
|
|
|
|
|
|
S2A.upon(connected, enter=S2B, outputs=[RC_tx_claim])
|
|
|
|
S2A.upon(close, enter=S4A, outputs=[])
|
|
|
|
S2B.upon(lost, enter=S2A, outputs=[])
|
2017-03-17 23:50:37 +00:00
|
|
|
S2B.upon(rx_claimed, enter=S3B, outputs=[I_got_wordlist, M_got_mailbox])
|
2017-02-26 11:57:58 +00:00
|
|
|
S2B.upon(close, enter=S4B, outputs=[RC_tx_release])
|
|
|
|
|
|
|
|
S3A.upon(connected, enter=S3B, outputs=[])
|
|
|
|
S3A.upon(close, enter=S4A, outputs=[])
|
|
|
|
S3B.upon(lost, enter=S3A, outputs=[])
|
2018-04-21 07:30:08 +00:00
|
|
|
# S3B.upon(rx_claimed, enter=S3B, outputs=[]) # shouldn't happen
|
2017-02-26 11:57:58 +00:00
|
|
|
S3B.upon(release, enter=S4B, outputs=[RC_tx_release])
|
|
|
|
S3B.upon(close, enter=S4B, outputs=[RC_tx_release])
|
|
|
|
|
|
|
|
S4A.upon(connected, enter=S4B, outputs=[RC_tx_release])
|
|
|
|
S4A.upon(close, enter=S4A, outputs=[])
|
|
|
|
S4B.upon(lost, enter=S4A, outputs=[])
|
2017-03-02 03:55:23 +00:00
|
|
|
S4B.upon(rx_claimed, enter=S4B, outputs=[])
|
2017-02-26 11:57:58 +00:00
|
|
|
S4B.upon(rx_released, enter=S5B, outputs=[T_nameplate_done])
|
2018-04-21 07:30:08 +00:00
|
|
|
S4B.upon(release, enter=S4B, outputs=[]) # mailbox is lazy
|
2017-02-26 11:57:58 +00:00
|
|
|
# Mailbox doesn't remember how many times it's sent a release, and will
|
|
|
|
# re-send a new one for each peer message it receives. Ignoring it here
|
|
|
|
# is easier than adding a new pair of states to Mailbox.
|
|
|
|
S4B.upon(close, enter=S4B, outputs=[])
|
|
|
|
|
|
|
|
S5A.upon(connected, enter=S5B, outputs=[])
|
|
|
|
S5B.upon(lost, enter=S5A, outputs=[])
|
2018-04-21 07:30:08 +00:00
|
|
|
S5.upon(release, enter=S5, outputs=[]) # mailbox is lazy
|
2017-02-26 12:03:54 +00:00
|
|
|
S5.upon(close, enter=S5, outputs=[])
|