shrub/pkg/arvo/sys/vane/ames.hoon

4012 lines
126 KiB
Plaintext
Raw Normal View History

2019-06-29 01:27:59 +03:00
:: Ames extends Arvo's %pass/%give move semantics across the network.
::
2019-12-21 09:56:51 +03:00
:: Ames receives packets as Arvo events and emits packets as Arvo
:: effects. The runtime is responsible for transferring the bytes in
:: an Ames packet across a physical network to another ship.
::
:: The runtime tells Ames which physical address a packet came from,
:: represented as an opaque atom. Ames can emit a packet effect to
:: one of those opaque atoms or to the Urbit address of a galaxy
:: (root node), which the runtime is responsible for translating to a
:: physical address. One runtime implementation sends UDP packets
:: using IPv4 addresses for ships and DNS lookups for galaxies, but
:: other implementations may overlay over other kinds of networks.
::
:: A local vane can pass Ames a %plea request message. Ames
:: transmits the message over the wire to the peer ship's Ames, which
:: passes the message to the destination vane.
::
:: Once the peer has processed the %plea message, it sends a
:: message-acknowledgment packet over the wire back to the local
:: Ames. This ack can either be positive to indicate the request was
:: processed, or negative to indicate the request failed, in which
:: case it's called a "nack". (Don't confuse Ames nacks with TCP
:: nacks, which are a different concept).
::
:: When the local Ames receives either a positive message-ack or a
:: combination of a nack and naxplanation (explained in more detail
2019-06-29 01:27:59 +03:00
:: below), it gives an %done move to the local vane that had
2019-12-21 09:56:51 +03:00
:: requested the original %plea message be sent.
::
:: A local vane can give Ames zero or more %boon response messages in
:: response to a %plea, on the same duct that Ames used to pass the
:: %plea to the vane. Ames transmits a %boon over the wire to the
:: peer's Ames, which gives it to the destination vane on the same
:: duct the vane had used to pass the original %plea to Ames.
::
:: %boon messages are acked automatically by the receiver Ames. They
:: cannot be nacked, and Ames only uses the ack internally, without
:: notifying the client vane that gave Ames the %boon.
::
:: If the Arvo event that completed receipt of a %boon message
:: crashes, Ames instead sends the client vane a %lost message
:: indicating the %boon was missed.
::
:: %plea messages can be nacked, in which case the peer will send
:: both a message-nack packet and a naxplanation message, which is
:: sent in a way that does not interfere with normal operation. The
:: naxplanation is sent as a full Ames message, instead of just a
:: packet, because the contained error information can be arbitrarily
:: large. A naxplanation can only give rise to a positive ack --
:: never ack an ack, and never nack a naxplanation.
::
:: Ames guarantees a total ordering of messages within a "flow",
:: identified in other vanes by a duct and over the wire by a "bone":
:: an opaque number. Each flow has a FIFO queue of %plea requests
:: from the requesting ship to the responding ship and a FIFO queue
:: of %boon's in the other direction.
::
:: Message order across flows is not specified and may vary based on
:: network conditions.
::
:: Ames guarantees that a message will only be delivered once to the
:: destination vane.
::
:: Ames encrypts every message using symmetric-key encryption by
:: performing an elliptic curve Diffie-Hellman using our private key
:: and the public key of the peer. For ships in the Jael PKI
:: (public-key infrastructure), Ames looks up the peer's public key
:: from Jael. Comets (128-bit ephemeral addresses) are not
:: cryptographic assets and must self-attest over Ames by sending a
:: single self-signed packet containing their public key.
::
:: When a peer suffers a continuity breach, Ames removes all
:: messaging state related to it. Ames does not guarantee that all
:: messages will be fully delivered to the now-stale peer. From
:: Ames's perspective, the newly restarted peer is a new ship.
:: Ames's guarantees are not maintained across a breach.
::
:: A vane can pass Ames a %heed $task to request Ames track a peer's
:: responsiveness. If our %boon's to it start backing up locally,
:: Ames will give a %clog back to the requesting vane containing the
:: unresponsive peer's urbit address. This interaction does not use
:: ducts as unique keys. Stop tracking a peer by sending Ames a
:: %jilt $task.
::
:: Debug output can be adjusted using %sift and %spew $task's.
2019-06-29 01:27:59 +03:00
::
::TODO fine
:: - receiving packets: +on-hear (1st) -> +on-hear-packet -> %fine
:: - sending packets: +on-plea -> +make-peer-core (make a function kind of like +on-memo) -> call +on-pump-send kind of like how +run-message-pump does
:: (assuming as event, scry just stateless)
::
2019-05-25 08:53:29 +03:00
:: protocol-version: current version of the ames wire protocol
::
2019-06-29 02:43:52 +03:00
!:
=/ protocol-version=?(%0 %1 %2 %3 %4 %5 %6 %7) %0
2019-06-29 02:43:52 +03:00
=, ames
2020-12-08 03:47:06 +03:00
=* point point:jael
=* public-keys-result public-keys-result:jael
2019-12-03 02:46:40 +03:00
:: veb: verbosity flags
::
=/ veb-all-off
:* snd=`?`%.n :: sending packets
rcv=`?`%.n :: receiving packets
odd=`?`%.n :: unusual events
2019-12-11 21:55:16 +03:00
msg=`?`%.n :: message-level events
2019-12-03 02:46:40 +03:00
ges=`?`%.n :: congestion control
2019-12-11 21:55:16 +03:00
for=`?`%.n :: packet forwarding
2019-12-03 02:46:40 +03:00
rot=`?`%.n :: routing attempts
2019-11-15 03:10:48 +03:00
==
2019-12-03 02:46:40 +03:00
=>
~% %ames ..part ~
2019-11-15 03:10:48 +03:00
|%
2020-12-01 17:51:14 +03:00
+| %helpers
2019-12-11 21:55:16 +03:00
:: +trace: print if .verb is set and we're tracking .ship
2019-12-03 02:46:40 +03:00
::
2019-11-15 03:10:48 +03:00
++ trace
2019-12-11 21:55:16 +03:00
|= [verb=? =ship ships=(set ship) print=(trap tape)]
^+ same
2019-11-15 03:10:48 +03:00
?. verb
same
2019-12-11 21:55:16 +03:00
?. => [ship=ship ships=ships in=in]
~+ |(=(~ ships) (~(has in ships) ship))
same
(slog leaf/"ames: {(scow %p ship)}: {(print)}" ~)
2020-12-01 17:51:14 +03:00
:: +qos-update-text: notice text for if connection state changes
::
++ qos-update-text
|= [=ship old=qos new=qos]
^- (unit tape)
::
?+ [-.old -.new] ~
[%unborn %live] `"; {(scow %p ship)} is your neighbor"
[%dead %live] `"; {(scow %p ship)} is ok"
[%live %dead] `"; {(scow %p ship)} not responding still trying"
[%unborn %dead] `"; {(scow %p ship)} not responding still trying"
[%live %unborn] `"; {(scow %p ship)} has sunk"
[%dead %unborn] `"; {(scow %p ship)} has sunk"
==
:: +lte-packets: yes if a is before b
::
++ lte-packets
|= [a=live-packet-key b=live-packet-key]
^- ?
::
?: (lth message-num.a message-num.b)
%.y
?: (gth message-num.a message-num.b)
%.n
(lte fragment-num.a fragment-num.b)
:: +split-message: split message into kilobyte-sized fragments
::
:: We don't literally split it here since that would allocate many
:: large atoms with no structural sharing. Instead, each
:: static-fragment has the entire message and a counter. In
:: +encrypt, we interpret this to get the actual fragment.
::
++ split-message
~/ %split-message
|= [=message-num =message-blob]
^- (list static-fragment)
::
=/ num-fragments=fragment-num (met 13 message-blob)
=| counter=@
::
|- ^- (list static-fragment)
?: (gte counter num-fragments)
~
::
:- [message-num num-fragments counter `@`message-blob]
$(counter +(counter))
:: +assemble-fragments: concatenate fragments into a $message
::
++ assemble-fragments
~/ %assemble-fragments
|= [num-fragments=fragment-num fragments=(map fragment-num fragment)]
^- *
::
=| sorted=(list fragment)
=. sorted
=/ index=fragment-num 0
|- ^+ sorted
?: =(index num-fragments)
sorted
$(index +(index), sorted [(~(got by fragments) index) sorted])
::
(cue (rep 13 (flop sorted)))
2021-01-20 23:29:45 +03:00
:: +jim: caching +jam
::
++ jim |=(n=* ~+((jam n)))
2020-12-01 17:51:14 +03:00
:: +bind-duct: find or make new $bone for .duct in .ossuary
::
++ bind-duct
|= [=ossuary =duct]
^+ [next-bone.ossuary ossuary]
::
?^ existing=(~(get by by-duct.ossuary) duct)
[u.existing ossuary]
::
:- next-bone.ossuary
:+ (add 4 next-bone.ossuary)
(~(put by by-duct.ossuary) duct next-bone.ossuary)
(~(put by by-bone.ossuary) next-bone.ossuary duct)
:: +make-bone-wire: encode ship and bone in wire for sending to vane
::
++ make-bone-wire
|= [her=ship =bone]
^- wire
::
/bone/(scot %p her)/(scot %ud bone)
:: +parse-bone-wire: decode ship and bone from wire from local vane
::
++ parse-bone-wire
|= =wire
^- [her=ship =bone]
::
~| %ames-wire-bone^wire
?> ?=([%bone @ @ ~] wire)
[`@p`(slav %p i.t.wire) `@ud`(slav %ud i.t.t.wire)]
:: +make-pump-timer-wire: construct wire for |packet-pump timer
::
++ make-pump-timer-wire
|= [her=ship =bone]
^- wire
/pump/(scot %p her)/(scot %ud bone)
:: +parse-pump-timer-wire: parse .her and .bone from |packet-pump wire
::
++ parse-pump-timer-wire
|= =wire
^- (unit [her=ship =bone])
::
~| %ames-wire-timer^wire
?. ?=([%pump @ @ ~] wire)
~
?~ ship=`(unit @p)`(slaw %p i.t.wire)
~
?~ bone=`(unit @ud)`(slaw %ud i.t.t.wire)
~
`[u.ship u.bone]
:: +derive-symmetric-key: $symmetric-key from $private-key and $public-key
::
:: Assumes keys have a tag on them like the result of the |ex:crub core.
::
++ derive-symmetric-key
~/ %derive-symmetric-key
|= [=public-key =private-key]
^- symmetric-key
::
?> =('b' (end 3 public-key))
=. public-key (rsh 8 (rsh 3 public-key))
2020-12-01 17:51:14 +03:00
::
?> =('B' (end 3 private-key))
=. private-key (rsh 8 (rsh 3 private-key))
2020-12-01 17:51:14 +03:00
::
`@`(shar:ed:crypto public-key private-key)
:: +encode-packet: serialize a packet into a bytestream
::
++ encode-packet
|= [ames=? packet]
^- blob
::
=/ sndr-meta (encode-ship-metadata sndr)
=/ rcvr-meta (encode-ship-metadata rcvr)
::
=/ body=@
;: mix
sndr-tick
(lsh 2 rcvr-tick)
(lsh 3 sndr)
(lsh [3 +(size.sndr-meta)] rcvr)
(lsh [3 +((add size.sndr-meta size.rcvr-meta))] content)
==
=/ checksum (end [0 20] (mug body))
=? body ?=(^ origin) (mix u.origin (lsh [3 6] body))
::
=/ header=@
%+ can 0
:~ [3 reserved=0]
[1 is-ames=ames]
[3 protocol-version]
[2 rank.sndr-meta]
[2 rank.rcvr-meta]
[20 checksum]
[1 relayed=.?(origin)]
==
(mix header (lsh 5 body))
2022-02-01 14:42:13 +03:00
::
++ decode-request
|= =hoot
^- twit
:- sig=(cut 3 [0 64] hoot)
-:(decode-request-info (rsh 3^64 hoot))
::
++ decode-request-info
|= =hoot
^- [=peep =purr]
=+ num=(cut 3 [0 4] hoot)
=+ len=(cut 3 [4 2] hoot)
=+ pat=(cut 3 [6 len] hoot)
:- [(stab pat) num]
:: if there is data remaining, it's the response
(rsh [3 (add 6 len)] hoot)
::
++ decode-response-packet
|= =purr
=; =rawr
~? !=(wid.rawr (met 3 dat.rawr)) [%fine %unexpected-dat-size]
rawr
:* sig=(cut 3 [0 64] purr)
siz=(cut 3 [64 4] purr)
wid=(cut 3 [68 2] purr)
dat=(rsh 3^70 purr)
==
++ response-size 13 :: 1kb
2022-02-01 14:42:13 +03:00
::
++ decode-response-msg
|= [total=@ud hav=(list have)]
2022-02-01 14:42:13 +03:00
^- roar
=/ mess=@
%+ rep response-size
%+ turn (flop hav)
|= =have
dat.have
:- sig=(cut 3 [0 64] mess)
=+ dat=(rsh 3^64 mess)
?~ dat ~
2022-02-01 14:42:13 +03:00
~| [%fine %response-not-cask]
;;((cask) (cue dat))
:: +decode-packet: deserialize packet from bytestream or crash
::
++ decode-packet
|= =blob
^- [ames=? =packet]
~| %decode-packet-fail
:: first 32 (2^5) bits are header; the rest is body
::
=/ header (end 5 blob)
=/ body (rsh 5 blob)
:: read header; first three bits are reserved
::
=/ is-ames (cut 0 [3 1] header)
:- =(& is-ames)
::
=/ version (cut 0 [4 3] header)
?. =(protocol-version version)
~| ames-protocol-version+version !!
::
=/ sndr-size (decode-ship-size (cut 0 [7 2] header))
=/ rcvr-size (decode-ship-size (cut 0 [9 2] header))
=/ checksum (cut 0 [11 20] header)
=/ relayed (cut 0 [31 1] header)
:: origin, if present, is 6 octets long, at the end of the body
::
=^ origin=(unit @) body
?: =(| relayed)
[~ body]
=/ len (sub (met 3 body) 6)
[`(end [3 6] body) (rsh [3 6] body)]
:: .checksum does not apply to the origin
::
?. =(checksum (end [0 20] (mug body)))
~| %ames-checksum !!
:: read fixed-length sndr and rcvr life data from body
::
:: These represent the last four bits of the sender and receiver
:: life fields, to be used for quick dropping of honest packets to
:: or from the wrong life.
::
=/ sndr-tick (cut 0 [0 4] body)
=/ rcvr-tick (cut 0 [4 4] body)
:: read variable-length .sndr and .rcvr addresses
::
=/ off 1
=^ sndr off [(cut 3 [off sndr-size] body) (add off sndr-size)]
?. (is-valid-rank sndr sndr-size)
~| ames-sender-impostor+[sndr sndr-size] !!
::
=^ rcvr off [(cut 3 [off rcvr-size] body) (add off rcvr-size)]
?. (is-valid-rank rcvr rcvr-size)
~| ames-receiver-impostor+[rcvr rcvr-size] !!
:: read variable-length .content from the rest of .body
::
=/ content (cut 3 [off (sub (met 3 body) off)] body)
[[sndr rcvr] sndr-tick rcvr-tick origin content]
:: +is-valid-rank: does .ship match its stated .size?
::
++ is-valid-rank
|= [=ship size=@ubC]
^- ?
.= size
=/ wid (met 3 ship)
?: (lte wid 1) 2
?: =(2 wid) 2
?: (lte wid 4) 4
?: (lte wid 8) 8
?> (lte wid 16) 16
:: +welt: like +weld but first argument is reversed
:: TODO: move to hoon.hoon
++ welt
~/ %welt
|* [a=(list) b=(list)]
=> .(a ^.(homo a), b ^.(homo b))
|- ^+ b
?~ a b
$(a t.a, b [i.a b])
2020-12-01 17:51:14 +03:00
:: +encode-open-packet: convert $open-packet attestation to $packet
::
++ encode-open-packet
~/ %encode-open-packet
|= [pac=open-packet =acru:ames]
^- packet
:* [sndr rcvr]:pac
(mod sndr-life.pac 16)
(mod rcvr-life.pac 16)
origin=~
content=`@`(sign:as:acru (jam pac))
==
:: +decode-open-packet: decode comet attestation into an $open-packet
::
++ decode-open-packet
~/ %decode-open-packet
|= [=packet our=ship our-life=@]
^- open-packet
:: deserialize and type-check packet contents
::
=+ ;; [signature=@ signed=@] (cue content.packet)
=+ ;; =open-packet (cue signed)
:: assert .our and .her and lives match
::
?> .= sndr.open-packet sndr.packet
?> .= rcvr.open-packet our
?> .= sndr-life.open-packet 1
?> .= rcvr-life.open-packet our-life
:: only a star can sponsor a comet
::
?> =(%king (clan:title (^sein:title sndr.packet)))
=/ crub (com:nu:crub:crypto public-key.open-packet)
2020-12-01 17:51:14 +03:00
:: comet public-key must hash to its @p address
::
?> =(sndr.packet fig:ex:crub)
2020-12-01 17:51:14 +03:00
:: verify signature
::
?> (safe:as:crub signature signed)
2020-12-01 17:51:14 +03:00
open-packet
:: +encode-shut-packet: encrypt and packetize a $shut-packet
::
++ encode-shut-packet
~/ %encode-shut-packet
|= $: =shut-packet
=symmetric-key
sndr=ship
rcvr=ship
sndr-life=@
rcvr-life=@
==
^- packet
::
=? meat.shut-packet
?& ?=(%& -.meat.shut-packet)
(gth (met 13 fragment.p.meat.shut-packet) 1)
==
%_ meat.shut-packet
fragment.p
(cut 13 [[fragment-num 1] fragment]:p.meat.shut-packet)
==
::
=/ vec ~[sndr rcvr sndr-life rcvr-life]
=/ [siv=@uxH len=@ cyf=@ux]
(~(en sivc:aes:crypto (shaz symmetric-key) vec) (jam shut-packet))
=/ content :(mix siv (lsh 7 len) (lsh [3 18] cyf))
2020-12-01 17:51:14 +03:00
[[sndr rcvr] (mod sndr-life 16) (mod rcvr-life 16) origin=~ content]
:: +decode-shut-packet: decrypt a $shut-packet from a $packet
::
++ decode-shut-packet
~/ %decode-shut-packet
|= [=packet =symmetric-key sndr-life=@ rcvr-life=@]
^- shut-packet
?. =(sndr-tick.packet (mod sndr-life 16))
~| ames-sndr-tick+sndr-tick.packet !!
?. =(rcvr-tick.packet (mod rcvr-life 16))
~| ames-rcvr-tick+rcvr-tick.packet !!
=/ siv (end 7 content.packet)
=/ len (end 4 (rsh 7 content.packet))
=/ cyf (rsh [3 18] content.packet)
2020-12-01 17:51:14 +03:00
~| ames-decrypt+[[sndr rcvr origin]:packet len siv]
=/ vec ~[sndr.packet rcvr.packet sndr-life rcvr-life]
;; shut-packet %- cue %- need
(~(de sivc:aes:crypto (shaz symmetric-key) vec) siv len cyf)
:: +decode-ship-size: decode a 2-bit ship type specifier into a byte width
::
:: Type 0: galaxy or star -- 2 bytes
:: Type 1: planet -- 4 bytes
:: Type 2: moon -- 8 bytes
:: Type 3: comet -- 16 bytes
::
++ decode-ship-size
|= rank=@ubC
^- @
::
?+ rank !!
%0b0 2
%0b1 4
%0b10 8
%0b11 16
==
:: +encode-ship-metadata: produce size (in bytes) and address rank for .ship
::
:: 0: galaxy or star
:: 1: planet
:: 2: moon
:: 3: comet
::
++ encode-ship-metadata
|= =ship
^- [size=@ =rank]
::
=/ size=@ (met 3 ship)
::
?: (lte size 2) [2 %0b0]
?: (lte size 4) [4 %0b1]
?: (lte size 8) [8 %0b10]
[16 %0b11]
2019-05-25 08:53:29 +03:00
+| %atomics
2019-05-25 05:03:33 +03:00
::
+$ private-key @uwprivatekey
2019-05-27 02:54:23 +03:00
+$ signature @uwsignature
:: $rank: which kind of ship address, by length
::
:: 0b0: galaxy or star -- 2 bytes
:: 0b1: planet -- 4 bytes
:: 0b10: moon -- 8 bytes
:: 0b11: comet -- 16 bytes
::
+$ rank ?(%0b0 %0b1 %0b10 %0b11)
2022-02-01 14:42:13 +03:00
+$ byuts [wid=@ud dat=@ux]
2019-05-25 08:53:29 +03:00
::
2019-05-27 06:22:38 +03:00
+| %kinetics
2019-05-27 04:48:41 +03:00
:: $channel: combined sender and receiver identifying data
2019-05-25 08:53:29 +03:00
::
2019-05-27 04:48:41 +03:00
+$ channel
2019-05-27 04:52:31 +03:00
$: [our=ship her=ship]
now=@da
2019-05-27 04:48:41 +03:00
:: our data, common to all dyads
::
$: =our=life
crypto-core=acru:ames
2019-12-11 21:55:16 +03:00
=bug
2019-05-27 04:48:41 +03:00
==
:: her data, specific to this dyad
::
$: =symmetric-key
=her=life
=her=public-key
her-sponsor=ship
2019-05-27 04:48:41 +03:00
== ==
:: $dyad: pair of sender and receiver ships
::
+$ dyad [sndr=ship rcvr=ship]
:: $packet: noun representation of an ames datagram packet
::
:: Roundtrips losslessly through atom encoding and decoding.
::
:: .origin is ~ unless the packet is being forwarded. If present,
:: it's an atom that encodes a route to another ship, such as an IPv4
:: address. Routes are opaque to Arvo and only have meaning in the
:: interpreter. This enforces that Ames is transport-agnostic.
::
+$ packet
$: dyad
sndr-tick=@ubC
rcvr-tick=@ubC
origin=(unit @uxaddress)
content=@uxcontent
==
2019-05-27 02:54:23 +03:00
:: $open-packet: unencrypted packet payload, for comet self-attestation
::
2019-11-27 09:15:05 +03:00
:: This data structure gets signed and jammed to form the .contents
:: field of a $packet.
2019-06-18 02:23:32 +03:00
::
2019-05-27 02:54:23 +03:00
+$ open-packet
2019-11-27 09:15:05 +03:00
$: =public-key
2019-06-18 02:23:32 +03:00
sndr=ship
2019-05-27 02:54:23 +03:00
=sndr=life
rcvr=ship
2019-06-18 02:23:32 +03:00
=rcvr=life
2019-05-27 02:54:23 +03:00
==
:: $shut-packet: encrypted packet payload
::
+$ shut-packet
2020-12-01 17:51:14 +03:00
$: =bone
2019-05-27 02:54:23 +03:00
=message-num
meat=(each fragment-meat ack-meat)
==
:: $fragment-meat: contents of a message-fragment packet
::
+$ fragment-meat
$: num-fragments=fragment-num
=fragment-num
=fragment
==
:: $ack-meat: contents of an acknowledgment packet; fragment or message
::
:: Fragment acks reference the $fragment-num of the target packet.
::
:: Message acks contain a success flag .ok, which is %.n in case of
:: negative acknowledgment (nack), along with .lag that describes the
:: time it took to process the message. .lag is zero if the message
:: was processed during a single Arvo event. At the moment, .lag is
:: always zero.
::
+$ ack-meat (each fragment-num [ok=? lag=@dr])
:: $naxplanation: nack trace; explains which message failed and why
::
+$ naxplanation [=message-num =error]
2019-05-25 08:53:29 +03:00
::
2019-05-27 04:52:31 +03:00
+| %statics
2019-05-25 08:53:29 +03:00
::
:: $ames-state: state for entire vane
::
2019-12-11 21:55:16 +03:00
:: peers: states of connections to other ships
:: unix-duct: handle to give moves to unix
:: life: our $life; how many times we've rekeyed
:: crypto-core: interface for encryption and signing
:: bug: debug printing configuration
::
2019-05-25 08:53:29 +03:00
+$ ames-state
$: peers=(map ship ship-state)
2019-06-08 13:13:38 +03:00
=unix=duct
2019-05-25 08:53:29 +03:00
=life
crypto-core=acru:ames
2019-12-11 21:55:16 +03:00
=bug
=fine-state
==
::
+$ ames-state-5
$: peers=(map ship ship-state-5)
=unix=duct
=life
crypto-core=acru-5
=bug
==
+$ ship-state-5
$% [%alien alien-agenda-5]
[%known peer-state]
==
+$ alien-agenda-5
$: messages=(list [=duct =plea])
packets=(set =blob)
heeds=(set duct)
==
++ acru-5 $_ ^?
|%
++ as ^?
|% ++ seal |~([a=pass b=@] *@)
++ sign |~(a=@ *@)
++ sure |~(a=@ *(unit @))
++ tear |~([a=pass b=@] *(unit @))
--
++ de |~([a=@ b=@] *(unit @))
++ dy |~([a=@ b=@] *@)
++ en |~([a=@ b=@] *@)
++ ex ^?
|% ++ fig *@uvH
++ pac *@uvG
++ pub *pass
++ sec *ring
--
++ nu ^?
|% ++ pit |~([a=@ b=@] ^?(..nu))
++ nol |~(a=ring ^?(..nu))
++ com |~(a=pass ^?(..nu))
--
--
:: $fine-state: remote scry subsystem state
::
:: hear: awaiting existence
:: want: awaiting response
:: part: partial responses
::
+$ fine-state
$: hear=(jug path duct)
want=(jug path duct)
part=(map path partial-fine)
::TODO re-send request timers?
==
2022-02-01 14:42:13 +03:00
+$ twit :: signed request
$: signature=@
peep
==
::
+$ peep :: request data
$: =path
num=@ud
==
::
+$ rawr :: response packet ::TODO meow
$: sig=@
siz=@ud
byts
==
::
+$ roar :: response message
$: sig=@
dat=$@(~ (cask))
2022-02-01 14:42:13 +03:00
==
:: $partial-fine: partial remote scry response
::
+$ partial-fine
$: num-fragments=@ud
num-received=@ud
fragments=(map @ud byts) ::TODO not byts, always 1024 bytes, just @
2019-12-11 21:55:16 +03:00
==
:: $bug: debug printing configuration
::
:: veb: verbosity toggles
:: ships: identity filter; if ~, print for all
::
+$ bug
$: veb=_veb-all-off
ships=(set ship)
2019-05-25 08:53:29 +03:00
==
2019-05-27 06:22:38 +03:00
::
+| %dialectics
::
:: $move: output effect; either request or response
::
2019-05-28 06:15:17 +03:00
+$ move [=duct card=(wind note gift)]
2019-06-29 00:26:40 +03:00
:: $queued-event: event to be handled after initial boot completes
::
2019-06-29 00:26:40 +03:00
+$ queued-event
2020-12-06 11:38:37 +03:00
$% [%call =duct wrapped-task=(hobo task)]
[%take =wire =duct =sign]
2019-05-28 04:43:10 +03:00
==
:: $note: request to other vane
::
2019-07-28 10:50:32 +03:00
:: Ames passes a %plea note to another vane when it receives a
:: message on a "forward flow" from a peer, originally passed from
:: one of the peer's vanes to the peer's Ames.
::
2019-07-28 10:50:32 +03:00
:: Ames passes a %plea to itself to trigger a heartbeat message to
2019-06-25 01:18:18 +03:00
:: our sponsor.
::
2019-06-25 02:52:22 +03:00
:: Ames passes a %private-keys to Jael to request our private keys.
:: Ames passes a %public-keys to Jael to request a peer's public
:: keys.
::
2019-05-28 04:43:10 +03:00
+$ note
2019-06-29 00:26:40 +03:00
$~ [%b %wait *@da]
$% $: %b
2019-05-28 04:43:10 +03:00
$% [%wait date=@da]
[%rest date=@da]
== ==
2022-02-02 05:30:11 +03:00
$: %c
$% $>(%warp task:clay)
== ==
$: %d
$% [%flog flog:dill]
2019-05-28 04:43:10 +03:00
== ==
$: %j
2019-06-29 00:26:40 +03:00
$% [%private-keys ~]
2019-06-29 01:27:59 +03:00
[%public-keys ships=(set ship)]
2019-05-28 04:43:10 +03:00
[%turf ~]
== ==
2019-06-29 00:26:40 +03:00
$: @tas
2019-07-28 10:50:32 +03:00
$% [%plea =ship =plea]
2019-05-28 04:43:10 +03:00
== == ==
:: $sign: response from other vane
::
+$ sign
2020-12-08 03:22:26 +03:00
$~ [%behn %wake ~]
$% $: %behn
$% $>(%wake gift:behn)
2019-05-28 04:43:10 +03:00
== ==
2022-02-02 05:30:11 +03:00
$: ?(%behn %clay)
$% [%writ p=riot:clay]
== ==
2020-12-08 03:22:26 +03:00
$: %jael
$% [%private-keys =life vein=(map life ring)]
[%public-keys =public-keys-result]
2019-06-22 01:17:09 +03:00
[%turf turfs=(list turf)]
2019-06-18 21:21:12 +03:00
== ==
2019-06-29 00:26:40 +03:00
$: @tas
2019-06-20 10:21:37 +03:00
$% [%done error=(unit error)]
2019-07-28 10:50:32 +03:00
[%boon payload=*]
2019-05-28 04:43:10 +03:00
== == ==
2019-05-27 06:22:38 +03:00
:: $message-pump-task: job for |message-pump
::
2019-06-25 19:00:03 +03:00
:: %memo: packetize and send application-level message
2019-06-20 10:21:37 +03:00
:: %hear: handle receipt of ack on fragment or message
2019-12-02 23:20:57 +03:00
:: %near: handle receipt of naxplanation
2019-05-27 06:22:38 +03:00
:: %wake: handle timer firing
::
+$ message-pump-task
2019-07-28 10:50:32 +03:00
$% [%memo =message-blob]
2019-06-20 10:21:37 +03:00
[%hear =message-num =ack-meat]
2019-12-02 23:20:57 +03:00
[%near =naxplanation]
2019-05-27 06:22:38 +03:00
[%wake ~]
==
:: $message-pump-gift: effect from |message-pump
::
2019-06-20 10:21:37 +03:00
:: %done: report message acknowledgment
2019-05-31 03:20:42 +03:00
:: %send: emit message fragment
:: %wait: set a new timer at .date
:: %rest: cancel timer at .date
2019-05-27 06:22:38 +03:00
::
+$ message-pump-gift
2019-12-02 23:20:57 +03:00
$% [%done =message-num error=(unit error)]
2019-05-31 03:20:42 +03:00
[%send =static-fragment]
[%wait date=@da]
[%rest date=@da]
2019-05-27 06:22:38 +03:00
==
:: $packet-pump-task: job for |packet-pump
::
2019-06-20 10:21:37 +03:00
:: %hear: deal with a packet acknowledgment
:: %done: deal with message acknowledgment
:: %halt: finish event, possibly updating timer
2019-05-27 06:22:38 +03:00
:: %wake: handle timer firing
::
+$ packet-pump-task
2019-06-20 10:21:37 +03:00
$% [%hear =message-num =fragment-num]
[%done =message-num lag=@dr]
[%halt ~]
[%wake current=message-num]
2019-05-27 06:22:38 +03:00
==
:: $packet-pump-gift: effect from |packet-pump
::
2019-05-31 03:20:42 +03:00
:: %send: emit message fragment
:: %wait: set a new timer at .date
:: %rest: cancel timer at .date
2019-05-27 06:22:38 +03:00
::
+$ packet-pump-gift
2019-05-31 03:20:42 +03:00
$% [%send =static-fragment]
[%wait date=@da]
[%rest date=@da]
2019-05-27 06:22:38 +03:00
==
:: $message-sink-task: job for |message-sink
::
2019-06-18 21:21:12 +03:00
:: %done: receive confirmation from vane of processing or failure
2019-06-20 10:21:37 +03:00
:: %drop: clear .message-num from .nax.state
2019-06-11 03:31:50 +03:00
:: %hear: handle receiving a message fragment packet
2019-08-06 02:05:40 +03:00
:: .ok: %.y unless previous failed attempt
::
+$ message-sink-task
2019-06-18 21:21:12 +03:00
$% [%done ok=?]
2019-06-20 10:21:37 +03:00
[%drop =message-num]
2019-08-06 02:05:40 +03:00
[%hear =lane =shut-packet ok=?]
==
:: $message-sink-gift: effect from |message-sink
::
2019-07-28 10:50:32 +03:00
:: %memo: assembled from received packets
2019-06-20 10:21:37 +03:00
:: %send: emit an ack packet
::
+$ message-sink-gift
2019-07-28 10:50:32 +03:00
$% [%memo =message-num message=*]
2019-06-20 10:21:37 +03:00
[%send =message-num =ack-meat]
==
2019-05-25 05:03:33 +03:00
--
2019-05-28 04:43:10 +03:00
:: external vane interface
::
2020-12-06 11:38:37 +03:00
|= our=ship
2019-06-29 00:26:40 +03:00
:: larval ames, before %born sets .unix-duct; wraps adult ames core
::
=< =* adult-gate .
=| queued-events=(qeu queued-event)
::
2020-12-06 11:38:37 +03:00
|= [now=@da eny=@ rof=roof]
2019-06-29 00:26:40 +03:00
=* larval-gate .
=* adult-core (adult-gate +<)
|%
:: +call: handle request $task
::
++ call
2020-12-06 11:38:37 +03:00
|= [=duct dud=(unit goof) wrapped-task=(hobo task)]
::
=/ =task ((harden task) wrapped-task)
::
2020-12-08 05:01:48 +03:00
:: reject larval error notifications
::
2020-12-08 05:01:48 +03:00
?^ dud
~|(%ames-larval-call-dud (mean tang.u.dud))
::
2019-06-29 00:26:40 +03:00
:: %born: set .unix-duct and start draining .queued-events
::
?: ?=(%born -.task)
2019-06-29 00:26:40 +03:00
:: process %born using wrapped adult ames
::
2020-12-06 11:38:37 +03:00
=^ moves adult-gate (call:adult-core duct dud task)
2019-06-29 00:26:40 +03:00
:: if no events were queued up, metamorphose
::
?~ queued-events
2019-08-10 21:48:40 +03:00
~> %slog.0^leaf/"ames: metamorphosis"
2019-06-29 00:26:40 +03:00
[moves adult-gate]
:: kick off a timer to process the first of .queued-events
::
=. moves :_(moves [duct %pass /larva %b %wait now])
[moves larval-gate]
:: any other event: enqueue it until we have a .unix-duct
::
2020-02-11 01:03:03 +03:00
:: XX what to do with errors?
::
2020-12-06 11:38:37 +03:00
=. queued-events (~(put to queued-events) %call duct task)
2019-06-29 00:26:40 +03:00
[~ larval-gate]
:: +take: handle response $sign
::
++ take
2020-12-06 11:38:37 +03:00
|= [=wire =duct dud=(unit goof) =sign]
?^ dud
~|(%ames-larval-take-dud (mean tang.u.dud))
2019-06-29 00:26:40 +03:00
:: enqueue event if not a larval drainage timer
::
2020-02-11 01:03:03 +03:00
:: XX what to do with errors?
::
2019-06-29 00:26:40 +03:00
?. =(/larva wire)
2020-12-06 11:38:37 +03:00
=. queued-events (~(put to queued-events) %take wire duct sign)
2019-06-29 00:26:40 +03:00
[~ larval-gate]
:: larval event drainage timer; pop and process a queued event
::
2020-12-08 03:22:26 +03:00
?. ?=([%behn %wake *] sign)
2019-08-10 21:48:40 +03:00
~> %slog.0^leaf/"ames: larva: strange sign"
[~ larval-gate]
2019-12-04 19:04:09 +03:00
:: if crashed, print, dequeue, and set next drainage timer
::
2019-12-04 17:45:52 +03:00
?^ error.sign
:: .queued-events should never be ~ here, but if it is, don't crash
::
?: =(~ queued-events)
=/ =tang [leaf/"ames: cursed metamorphosis" u.error.sign]
=/ moves [duct %pass /larva-crash %d %flog %crud %larva tang]~
[moves adult-gate]
:: dequeue and discard crashed event
::
2019-12-04 19:04:09 +03:00
=. queued-events +:~(get to queued-events)
:: .queued-events has been cleared; metamorphose
::
?~ queued-events
~> %slog.0^leaf/"ames: metamorphosis"
[~ adult-gate]
:: set timer to drain next event
::
2019-12-04 19:29:01 +03:00
=/ moves
=/ =tang [leaf/"ames: larva: drain crash" u.error.sign]
:~ [duct %pass /larva-crash %d %flog %crud %larva tang]
[duct %pass /larva %b %wait now]
==
2019-12-04 17:45:52 +03:00
[moves larval-gate]
:: normal drain timer; dequeue and run event
::
2019-06-29 00:26:40 +03:00
=^ first-event queued-events ~(get to queued-events)
=^ moves adult-gate
?- -.first-event
2020-12-06 11:38:37 +03:00
%call (call:adult-core [duct ~ wrapped-task]:+.first-event)
%take (take:adult-core [wire duct ~ sign]:+.first-event)
2019-06-29 00:26:40 +03:00
==
:: .queued-events has been cleared; metamorphose
::
?~ queued-events
2019-08-10 21:48:40 +03:00
~> %slog.0^leaf/"ames: metamorphosis"
2019-06-29 00:26:40 +03:00
[moves adult-gate]
:: set timer to drain next event
::
=. moves :_(moves [duct %pass /larva %b %wait now])
[moves larval-gate]
:: lifecycle arms; mostly pass-throughs to the contained adult ames
::
++ scry scry:adult-core
++ stay [%6 %larva queued-events ames-state.adult-gate]
2019-06-29 00:26:40 +03:00
++ load
2019-07-31 21:51:31 +03:00
|= $= old
2020-06-12 08:55:08 +03:00
$% $: %4
$% $: %larva
events=(qeu queued-event)
state=ames-state-5
2020-06-12 08:55:08 +03:00
==
[%adult state=ames-state-5]
2020-06-12 08:55:08 +03:00
== ==
$: %5
$% $: %larva
events=(qeu queued-event)
state=ames-state-5
==
[%adult state=ames-state-5]
== ==
$: %6
$% $: %larva
events=(qeu queued-event)
state=_ames-state.adult-gate
==
[%adult state=_ames-state.adult-gate]
== ==
==
2019-12-01 10:47:24 +03:00
?- old
2020-06-12 08:55:08 +03:00
[%4 %adult *] (load:adult-core %4 state.old)
2019-12-11 21:55:16 +03:00
::
2020-06-12 08:55:08 +03:00
[%4 %larva *]
2019-12-11 21:55:16 +03:00
~> %slog.1^leaf/"ames: larva: load"
=. queued-events events.old
2020-06-12 08:55:08 +03:00
=. adult-gate (load:adult-core %4 state.old)
larval-gate
::
[%5 %adult *] (load:adult-core %5 state.old)
::
[%5 %larva *]
~> %slog.1^leaf/"ames: larva: load"
=. queued-events events.old
=. adult-gate (load:adult-core %5 state.old)
larval-gate
::
[%6 %adult *] (load:adult-core %6 state.old)
::
[%6 %larva *]
~> %slog.1^leaf/"ames: larva: load"
=. queued-events events.old
=. adult-gate (load:adult-core %6 state.old)
larval-gate
2019-07-24 02:55:35 +03:00
==
2019-06-29 00:26:40 +03:00
--
:: adult ames, after metamorphosis from larva
::
=<
2019-05-28 04:43:10 +03:00
=| =ames-state
2020-12-06 11:38:37 +03:00
|= [now=@da eny=@ rof=roof]
2019-05-28 04:43:10 +03:00
=* ames-gate .
2019-12-11 21:55:16 +03:00
=* veb veb.bug.ames-state
2019-05-28 04:43:10 +03:00
|%
:: +call: handle request $task
::
++ call
2020-12-06 11:38:37 +03:00
|= [=duct dud=(unit goof) wrapped-task=(hobo task)]
2019-05-28 04:43:10 +03:00
^- [(list move) _ames-gate]
::
=/ =task ((harden task) wrapped-task)
2020-12-06 13:55:19 +03:00
=/ event-core (per-event [now eny rof] duct ames-state)
::
=^ moves ames-state
=< abet
2020-12-08 05:01:48 +03:00
:: handle error notifications
::
?^ dud
?+ -.task
(on-crud:event-core -.task tang.u.dud)
%hear (on-hear:event-core lane.task blob.task dud)
2020-12-08 05:01:48 +03:00
==
::
?- -.task
2019-06-22 01:26:26 +03:00
%born on-born:event-core
%hear (on-hear:event-core [lane blob ~]:task)
2019-11-04 04:35:45 +03:00
%heed (on-heed:event-core ship.task)
2020-12-06 11:38:37 +03:00
%init on-init:event-core
2019-11-04 04:35:45 +03:00
%jilt (on-jilt:event-core ship.task)
2019-12-11 21:55:16 +03:00
%sift (on-sift:event-core ships.task)
2019-12-03 02:46:40 +03:00
%spew (on-spew:event-core veb.task)
%stir (on-stir:event-core arg.task)
2020-05-20 10:29:54 +03:00
%trim on-trim:event-core
2019-06-22 01:19:24 +03:00
%vega on-vega:event-core
2019-07-28 10:50:32 +03:00
%plea (on-plea:event-core [ship plea]:task)
::
%keen (on-keen:fine:event-core +.task)
%yawn (on-yawn:fine:event-core +.task)
%bide (on-bide:fine:event-core +.task)
==
::
[moves ames-gate]
2019-05-28 04:43:10 +03:00
:: +take: handle response $sign
::
++ take
2020-12-06 11:38:37 +03:00
|= [=wire =duct dud=(unit goof) =sign]
2019-05-28 04:43:10 +03:00
^- [(list move) _ames-gate]
?^ dud
~|(%ames-take-dud (mean tang.u.dud))
::
2019-05-28 04:43:10 +03:00
::
2020-12-06 13:55:19 +03:00
=/ event-core (per-event [now eny rof] duct ames-state)
2019-06-18 21:21:12 +03:00
::
=^ moves ames-state
=< abet
?- sign
2019-06-29 00:26:40 +03:00
[@ %done *] (on-take-done:event-core wire error.sign)
2019-07-28 10:50:32 +03:00
[@ %boon *] (on-take-boon:event-core wire payload.sign)
2022-02-02 05:30:11 +03:00
::
[?(%clay %behn) %writ *] (on-take-clay-bide:fine:event-core wire p.sign)
::
2020-12-08 03:22:26 +03:00
[%behn %wake *] (on-take-wake:event-core wire error.sign)
::
2020-12-08 03:22:26 +03:00
[%jael %turf *] (on-take-turf:event-core turfs.sign)
[%jael %private-keys *] (on-priv:event-core [life vein]:sign)
[%jael %public-keys *] (on-publ:event-core wire public-keys-result.sign)
2019-06-18 21:21:12 +03:00
==
::
[moves ames-gate]
2019-05-28 04:43:10 +03:00
:: +stay: extract state before reload
::
++ stay [%6 %adult ames-state]
2019-05-28 04:43:10 +03:00
:: +load: load in old state after reload
::
++ load
|^
|= $= old-state
$% [%4 ames-state-5]
[%5 ames-state-5]
[%6 ^ames-state]
==
^+ ames-gate
=? old-state ?=(%4 -.old-state) %5^(state-4-to-5 +.old-state)
=? old-state ?=(%5 -.old-state) %6^(state-5-to-6 +.old-state)
::
?> ?=(%6 -.old-state)
ames-gate(ames-state +.old-state)
::
++ state-5-to-6
|= old=ames-state-5
^- ^ames-state
%= old
peers (~(run by peers.old) ship-state-5-to-6)
bug [bug.old *fine-state]
crypto-core (nol:nu:crub:crypto sec:ex:crypto-core.old)
==
::
++ ship-state-5-to-6
|= old=ship-state-5
^- ship-state
?. ?=(%alien -.old) old
old(heeds [heeds.old ~])
::
++ state-4-to-5
|= ames-state=ames-state-5
^- ames-state-5
=. peers.ames-state
%- ~(run by peers.ames-state)
|= ship-state=ship-state-5
?. ?=(%known -.ship-state)
ship-state
=. snd.ship-state
%- ~(run by snd.ship-state)
|= =message-pump-state
=. num-live.metrics.packet-pump-state.message-pump-state
~(wyt in live.packet-pump-state.message-pump-state)
message-pump-state
ship-state
ames-state
--
2019-05-28 04:43:10 +03:00
:: +scry: dereference namespace
::
++ scry
2020-12-08 00:52:12 +03:00
^- roon
|= [lyc=gang car=term bem=beam]
2019-05-28 04:43:10 +03:00
^- (unit (unit cage))
2020-12-08 00:52:12 +03:00
=* ren car
2020-11-24 00:06:50 +03:00
=* why=shop &/p.bem
=* syd q.bem
=* lot=coin $/r.bem
=* tyl s.bem
::
::TODO don't special-case whey scry
::
?: &(=(%$ ren) =(tyl /whey))
2020-05-07 11:51:08 +03:00
=/ maz=(list mass)
=+ [known alien]=(skid ~(val by peers.ames-state) |=(^ =(%known +<-)))
:~ peers-known+&+known
peers-alien+&+alien
==
``mass+!>(maz)
:: only respond for the local identity, %$ desk, current timestamp
::
?. ?& =(&+our why)
=([%$ %da now] lot)
=(%$ syd)
==
2020-09-18 23:06:13 +03:00
?. for.veb.bug.ames-state ~
~> %slog.0^leaf/"ames: scry-fail {<[why=why lot=lot now=now syd=syd]>}"
~
:: /ax/protocol/version @
:: /ax/peers (map ship ?(%alien %known))
:: /ax/peers/[ship] ship-state
:: /ax/peers/[ship]/forward-lane (list lane)
:: /ax/bones/[ship] [snd=(set bone) rcv=(set bone)]
:: /ax/snd-bones/[ship]/[bone] vase
:: /ax/fine/message/[path/...] song
::
?. ?=(%x ren) ~
?+ tyl ~
[%protocol %version ~]
``noun+!>(protocol-version)
::
[%peers ~]
:^ ~ ~ %noun
!> ^- (map ship ?(%alien %known))
(~(run by peers.ames-state) head)
::
[%peers @ *]
=/ who (slaw %p i.t.tyl)
?~ who [~ ~]
2020-12-06 07:02:31 +03:00
=/ peer (~(get by peers.ames-state) u.who)
?+ t.t.tyl [~ ~]
~
?~ peer
[~ ~]
``noun+!>(u.peer)
::
[%forward-lane ~]
::
:: this duplicates the routing hack from +send-blob:event-core
:: so long as neither the peer nor the peer's sponsoring galaxy is us:
::
:: - no route to the peer: send to the peer's sponsoring galaxy
:: - direct route to the peer: use that
:: - indirect route to the peer: send to both that route and the
:: the peer's sponsoring galaxy
::
:^ ~ ~ %noun
!> ^- (list lane)
?. ?& ?=([~ %known *] peer)
!=(our u.who)
==
~
=; zar=(trap (list lane))
?~ route.u.peer $:zar
=* rot u.route.u.peer
?:(direct.rot [lane.rot ~] [lane.rot $:zar])
::
|. ^- (list lane)
?: ?=(%czar (clan:title sponsor.u.peer))
?: =(our sponsor.u.peer)
~
[%& sponsor.u.peer]~
=/ next (~(get by peers.ames-state) sponsor.u.peer)
?. ?=([~ %known *] next)
~
$(peer next)
==
::
[%bones @ ~]
=/ who (slaw %p i.t.tyl)
?~ who [~ ~]
=/ per (~(get by peers.ames-state) u.who)
?. ?=([~ %known *] per) [~ ~]
=/ res
=, u.per
[snd=~(key by snd) rcv=~(key by rcv)]
``noun+!>(res)
::
[%snd-bones @ @ ~]
=/ who (slaw %p i.t.tyl)
?~ who [~ ~]
=/ ost (slaw %ud i.t.t.tyl)
?~ ost [~ ~]
=/ per (~(get by peers.ames-state) u.who)
?. ?=([~ %known *] per) [~ ~]
=/ mps (~(get by snd.u.per) u.ost)
?~ mps [~ ~]
=/ res
u.mps
``noun+!>(!>(res))
::
[%fine %message @ *]
::TODO separate endpoint for the full message (instead of packet list)
:: t.t.tyl is expected to be a scry path of the shape /vc/desk/rev/etc,
:: so we need to give it the right shape
::
2022-02-02 05:48:04 +03:00
=* path t.t.tyl
?~ nom=(de-omen path) ~
:: we only support scrying into clay,
:: and only if the data is fully public.
::
2022-02-01 14:42:13 +03:00
:: ?. =(%c (end 3 (snag 0 path))) ~
:: =/ perm-omen (need (de-omen %cp (slag 1 path)))
:: =+ pem=(rof lyc perm-omen)
:: ?> ?=(^ pem)
:: ?> ?=(^ u.pem)
:: =+ per=!<([r=dict:clay w=dict:clay] q.u.u.pem)
:: ?> =([%black ~ ~] rul.r.per)
=+ res=(rof lyc u.nom)
::TODO suggests we need to factor differently
=+ ven=(per-event [now 0v0 rof] *duct ames-state)
?- res
~ ~
[~ ~] ``noun+!>((encode-response:fine:ven path ~))
[~ ~ *] ``noun+!>((encode-response:fine:ven path [p q.q]:u.u.res))
==
==
2019-05-28 04:43:10 +03:00
--
2020-12-01 17:51:14 +03:00
:: |per-event: inner event-handling core
2019-05-28 04:43:10 +03:00
::
~% %per-event ..decode-packet ~
2019-05-25 05:03:33 +03:00
|%
++ per-event
=| moves=(list move)
2020-12-01 17:51:14 +03:00
~% %event-gate ..per-event ~
2020-12-06 13:55:19 +03:00
|= [[now=@da eny=@ rof=roof] =duct =ames-state]
2019-12-11 21:55:16 +03:00
=* veb veb.bug.ames-state
2020-12-01 17:51:14 +03:00
~% %event-core ..$ ~
|%
++ event-core .
++ abet [(flop moves) ames-state]
2019-05-28 06:15:17 +03:00
++ emit |=(=move event-core(moves [move moves]))
++ emil |=(mos=(list move) event-core(moves (weld (flop mos) moves)))
2019-12-11 21:55:16 +03:00
++ channel-state [life crypto-core bug]:ames-state
++ trace
|= [verb=? =ship print=(trap tape)]
^+ same
(^trace verb ship ships.bug.ames-state print)
2019-06-20 10:47:13 +03:00
:: +on-take-done: handle notice from vane that it processed a message
::
2019-06-20 10:47:13 +03:00
++ on-take-done
2019-06-19 02:59:25 +03:00
|= [=wire error=(unit error)]
2019-06-18 21:21:12 +03:00
^+ event-core
:: relay the vane ack to the foreign peer
2019-06-25 01:18:18 +03:00
::
2019-06-19 03:38:25 +03:00
=+ ^- [her=ship =bone] (parse-bone-wire wire)
::
2019-06-25 00:32:53 +03:00
=/ =peer-state (got-peer-state her)
2019-12-03 02:46:40 +03:00
=/ =channel [[our her] now channel-state -.peer-state]
2019-06-25 00:32:53 +03:00
=/ peer-core (make-peer-core peer-state channel)
2019-07-28 10:50:32 +03:00
:: if processing succeded, send positive ack packet and exit
2019-06-19 03:38:25 +03:00
::
2019-07-28 10:50:32 +03:00
?~ error
abet:(run-message-sink:peer-core bone %done ok=%.y)
2019-07-28 10:50:32 +03:00
:: failed; send message nack packet
2019-06-19 03:38:25 +03:00
::
=. event-core abet:(run-message-sink:peer-core bone %done ok=%.n)
=/ =^peer-state (got-peer-state her)
2019-12-03 02:46:40 +03:00
=/ =^channel [[our her] now channel-state -.peer-state]
2019-07-28 10:50:32 +03:00
:: construct nack-trace message, referencing .failed $message-num
::
=/ failed=message-num last-acked:(~(got by rcv.peer-state) bone)
=/ =naxplanation [failed u.error]
=/ =message-blob (jam naxplanation)
2019-07-28 10:50:32 +03:00
:: send nack-trace message on associated .nack-trace-bone
2019-06-19 03:38:25 +03:00
::
=. peer-core (make-peer-core peer-state channel)
=/ nack-trace-bone=^bone (mix 0b10 bone)
::
abet:(run-message-pump:peer-core nack-trace-bone %memo message-blob)
2019-12-11 21:55:16 +03:00
:: +on-sift: handle request to filter debug output by ship
::
++ on-sift
|= ships=(list ship)
^+ event-core
=. ships.bug.ames-state (sy ships)
event-core
:: +on-spew: handle request to set verbosity toggles on debug output
2019-12-03 02:46:40 +03:00
::
++ on-spew
|= verbs=(list verb)
^+ event-core
:: start from all %.n's, then flip requested toggles
::
2019-12-11 21:55:16 +03:00
=. veb.bug.ames-state
2019-12-03 02:46:40 +03:00
%+ roll verbs
|= [=verb acc=_veb-all-off]
2019-12-11 21:55:16 +03:00
^+ veb.bug.ames-state
2019-12-03 02:46:40 +03:00
?- verb
%snd acc(snd %.y)
%rcv acc(rcv %.y)
%odd acc(odd %.y)
%msg acc(msg %.y)
%ges acc(ges %.y)
%for acc(for %.y)
%rot acc(rot %.y)
==
event-core
:: +on-stir: start timers for any flow that lack them
::
:: .arg is unused, meant to ease future debug commands
::
++ on-stir
|= arg=@t
=/ states=(list [ship peer-state])
%+ murn ~(tap by peers.ames-state)
|= [=ship =ship-state]
^- (unit [^ship peer-state])
?. ?=(%known -.ship-state)
~
`[ship +.ship-state]
=/ snds=(list (list [ship bone message-pump-state]))
%+ turn states
|= [=ship peer-state]
%+ turn ~(tap by snd)
|= [=bone =message-pump-state]
[ship bone message-pump-state]
=/ next-wakes
%+ turn `(list [ship bone message-pump-state])`(zing snds)
|= [=ship =bone message-pump-state]
[ship bone next-wake.packet-pump-state]
=/ next-real-wakes=(list [=ship =bone =@da])
%+ murn next-wakes
|= [=ship =bone tym=(unit @da)]
^- (unit [^ship ^bone @da])
?~(tym ~ `[ship bone u.tym])
=/ timers
%- silt
;; (list [@da ^duct])
=< q.q %- need %- need
2020-12-06 13:55:19 +03:00
(rof ~ %b [[our %timers da+now] /])
=/ to-stir
%+ skip next-real-wakes
|= [=ship =bone =@da]
(~(has in timers) [da `^duct`~[a+(make-pump-timer-wire ship bone) /ames]])
~& [%stirring to-stir]
|- ^+ event-core
?~ to-stir
event-core
=/ =wire (make-pump-timer-wire [ship bone]:i.to-stir)
=. event-core (emit duct %pass wire %b %wait da.i.to-stir)
$(to-stir t.to-stir)
:: +on-crud: handle event failure; print to dill
::
++ on-crud
|= =error
^+ event-core
(emit duct %pass /crud %d %flog %crud error)
2019-11-04 04:35:45 +03:00
:: +on-heed: handle request to track .ship's responsiveness
::
++ on-heed
|= =ship
^+ event-core
=/ ship-state (~(get by peers.ames-state) ship)
?. ?=([~ %known *] ship-state)
%+ enqueue-alien-todo ship
|= todos=alien-agenda
todos(heeds (~(put in heeds.todos) duct))
::
=/ =peer-state +.u.ship-state
2019-12-03 02:46:40 +03:00
=/ =channel [[our ship] now channel-state -.peer-state]
2019-11-04 04:35:45 +03:00
abet:on-heed:(make-peer-core peer-state channel)
:: +on-jilt: handle request to stop tracking .ship's responsiveness
::
++ on-jilt
|= =ship
^+ event-core
=/ ship-state (~(get by peers.ames-state) ship)
?. ?=([~ %known *] ship-state)
%+ enqueue-alien-todo ship
|= todos=alien-agenda
todos(heeds (~(del in heeds.todos) duct))
::
=/ =peer-state +.u.ship-state
2019-12-03 02:46:40 +03:00
=/ =channel [[our ship] now channel-state -.peer-state]
2019-11-04 04:35:45 +03:00
abet:on-jilt:(make-peer-core peer-state channel)
2019-06-25 03:48:05 +03:00
:: +on-hear: handle raw packet receipt
2019-07-26 00:50:08 +03:00
::
++ on-hear
|= [l=lane b=blob d=(unit goof)]
^+ event-core
=/ [ames=? =packet]
(decode-packet b)
?: ames
(on-hear-packet l packet d)
?. response==(& (cut 0 [2 1] b))
~|([%fine %request-events-forbidden] !!)
(on-hear-response:fine l packet d)
2019-06-25 03:48:05 +03:00
:: +on-hear-packet: handle mildly processed packet receipt
::
++ on-hear-packet
2020-12-01 17:51:14 +03:00
~/ %on-hear-packet
|= [=lane =packet dud=(unit goof)]
2019-06-25 03:48:05 +03:00
^+ event-core
::
2019-07-24 03:31:51 +03:00
?: =(our sndr.packet)
event-core
::
2019-07-28 10:50:32 +03:00
%. +<
::
?. =(our rcvr.packet)
on-hear-forward
::
2020-12-01 17:51:14 +03:00
?: ?& ?=(%pawn (clan:title sndr.packet))
2021-02-26 03:07:00 +03:00
!?=([~ %known *] (~(get by peers.ames-state) sndr.packet))
2020-12-01 17:51:14 +03:00
==
on-hear-open
on-hear-shut
2019-06-22 00:21:58 +03:00
:: +on-hear-forward: maybe forward a packet to someone else
::
:: Note that this performs all forwarding requests without
:: filtering. Any protection against DDoS amplification will be
:: provided by Vere.
2019-06-22 00:35:07 +03:00
::
++ on-hear-forward
2020-12-01 17:51:14 +03:00
~/ %on-hear-forward
|= [=lane =packet dud=(unit goof)]
^+ event-core
2019-12-11 21:55:16 +03:00
%- %^ trace for.veb sndr.packet
|.("forward: {<sndr.packet>} -> {<rcvr.packet>}")
2019-08-21 00:44:44 +03:00
:: set .origin.packet if it doesn't already have one, re-encode, and send
::
2020-12-01 17:51:14 +03:00
=? origin.packet
&(?=(~ origin.packet) !=(%czar (clan:title sndr.packet)))
?: ?=(%& -.lane)
~
?. (lte (met 3 p.lane) 6)
~| ames-lane-size+p.lane !!
`p.lane
::
=/ =blob (encode-packet & packet)
(send-blob & rcvr.packet blob)
2019-06-18 02:23:32 +03:00
:: +on-hear-open: handle receipt of plaintext comet self-attestation
::
++ on-hear-open
2020-12-01 17:51:14 +03:00
~/ %on-hear-open
|= [=lane =packet dud=(unit goof)]
^+ event-core
:: assert the comet can't pretend to be a moon or other address
::
?> ?=(%pawn (clan:title sndr.packet))
2019-06-18 02:23:32 +03:00
:: if we already know .sndr, ignore duplicate attestation
::
2019-06-18 02:23:32 +03:00
=/ ship-state (~(get by peers.ames-state) sndr.packet)
?: ?=([~ %known *] ship-state)
event-core
::
2020-12-01 17:51:14 +03:00
=/ =open-packet (decode-open-packet packet our life.ames-state)
2019-06-18 02:23:32 +03:00
:: store comet as peer in our state
::
=. peers.ames-state
%+ ~(put by peers.ames-state) sndr.packet
^- ^ship-state
:- %known
=| =peer-state
2019-06-18 02:23:32 +03:00
=/ our-private-key sec:ex:crypto-core.ames-state
=/ =symmetric-key
(derive-symmetric-key public-key.open-packet our-private-key)
::
%_ peer-state
qos [%unborn now]
2019-06-18 02:23:32 +03:00
symmetric-key symmetric-key
life sndr-life.open-packet
public-key public-key.open-packet
sponsor (^sein:title sndr.packet)
2019-11-28 09:17:34 +03:00
route `[direct=%.n lane]
2019-06-18 02:23:32 +03:00
==
::
event-core
2019-06-22 00:35:07 +03:00
:: +on-hear-shut: handle receipt of encrypted packet
::
++ on-hear-shut
2020-12-01 17:51:14 +03:00
~/ %on-hear-shut
|= [=lane =packet dud=(unit goof)]
^+ event-core
=/ sndr-state (~(get by peers.ames-state) sndr.packet)
2021-02-26 03:29:29 +03:00
:: if we don't know them, ask jael for their keys and enqueue
::
?. ?=([~ %known *] sndr-state)
(enqueue-alien-todo sndr.packet |=(alien-agenda +<))
:: decrypt packet contents using symmetric-key.channel
2019-06-01 06:32:13 +03:00
::
:: If we know them, we have a $channel with them, which we've
:: populated with a .symmetric-key derived from our private key
:: and their public key using elliptic curve Diffie-Hellman.
::
2019-05-28 06:59:53 +03:00
=/ =peer-state +.u.sndr-state
2019-12-03 02:46:40 +03:00
=/ =channel [[our sndr.packet] now channel-state -.peer-state]
2019-11-22 16:42:19 +03:00
~| %ames-crash-on-packet-from^her.channel
2020-12-01 17:51:14 +03:00
=/ =shut-packet
(decode-shut-packet packet [symmetric-key her-life our-life]:channel)
:: non-galaxy: update route with heard lane or forwarded lane
::
2020-12-01 17:51:14 +03:00
=? route.peer-state !=(%czar (clan:title her.channel))
:: if new packet is direct, use that. otherwise, if the new new
:: and old lanes are indirect, use the new one. if the new lane
:: is indirect but the old lane is direct, then if the lanes are
:: identical, don't mark it indirect; if they're not identical,
:: use the new lane and mark it indirect.
::
:: if you mark lane as indirect because you got an indirect
:: packet even though you already had a direct identical lane,
:: then delayed forwarded packets will come later and reset to
:: indirect, so you're unlikely to get a stable direct route
:: (unless the forwarder goes offline for a while).
::
:: conversely, if you don't accept indirect routes with different
:: lanes, then if your lane is stale and they're trying to talk
:: to you, your acks will go to the stale lane, and you'll never
:: time it out unless you reach out to them. this manifests as
:: needing to |hi or dotpost to get a response when the other
:: ship has changed lanes.
::
?: ?=(~ origin.packet)
2019-08-16 12:26:03 +03:00
`[direct=%.y lane]
?: ?=([~ %& *] route.peer-state)
2020-12-01 17:51:14 +03:00
?: =(lane.u.route.peer-state |+u.origin.packet)
route.peer-state
2020-12-01 17:51:14 +03:00
`[direct=%.n |+u.origin.packet]
`[direct=%.n |+u.origin.packet]
:: perform peer-specific handling of packet
::
=/ peer-core (make-peer-core peer-state channel)
abet:(on-hear-shut-packet:peer-core lane shut-packet dud)
2019-07-28 10:50:32 +03:00
:: +on-take-boon: receive request to give message to peer
2019-06-19 02:51:06 +03:00
::
2019-07-28 10:50:32 +03:00
++ on-take-boon
|= [=wire payload=*]
2019-06-19 02:51:06 +03:00
^+ event-core
::
2019-06-21 00:46:31 +03:00
=+ ^- [her=ship =bone] (parse-bone-wire wire)
::
2019-06-25 00:32:53 +03:00
=/ =peer-state (got-peer-state her)
2019-12-03 02:46:40 +03:00
=/ =channel [[our her] now channel-state -.peer-state]
2019-06-19 02:51:06 +03:00
::
2019-11-27 07:52:43 +03:00
abet:(on-memo:(make-peer-core peer-state channel) bone payload %boon)
2019-07-28 10:50:32 +03:00
:: +on-plea: handle request to send message
2019-06-09 09:26:01 +03:00
::
2019-07-28 10:50:32 +03:00
++ on-plea
|= [=ship =plea]
2019-06-09 09:26:01 +03:00
^+ event-core
:: .plea is from local vane to foreign ship
2019-06-09 09:26:01 +03:00
::
2019-06-22 00:35:07 +03:00
=/ ship-state (~(get by peers.ames-state) ship)
2019-06-09 09:26:01 +03:00
::
2019-06-22 00:35:07 +03:00
?. ?=([~ %known *] ship-state)
2019-06-25 00:13:45 +03:00
%+ enqueue-alien-todo ship
|= todos=alien-agenda
todos(messages [[duct plea] messages.todos])
2019-06-09 09:26:01 +03:00
::
2019-06-22 00:35:07 +03:00
=/ =peer-state +.u.ship-state
2019-12-03 02:46:40 +03:00
=/ =channel [[our ship] now channel-state -.peer-state]
2019-06-09 09:26:01 +03:00
::
=^ =bone ossuary.peer-state (bind-duct ossuary.peer-state duct)
2019-12-11 21:55:16 +03:00
%- %^ trace msg.veb ship
2019-11-15 03:10:48 +03:00
|. ^- tape
=/ sndr [our our-life.channel]
=/ rcvr [ship her-life.channel]
2020-12-12 03:45:13 +03:00
"plea {<sndr^rcvr^bone=bone^vane.plea^path.plea>}"
2019-06-21 00:46:31 +03:00
::
2019-11-27 07:52:43 +03:00
abet:(on-memo:(make-peer-core peer-state channel) bone plea %plea)
2019-06-19 02:59:25 +03:00
:: +on-take-wake: receive wakeup or error notification from behn
::
++ on-take-wake
|= [=wire error=(unit tang)]
^+ event-core
?: ?=([%fine %behn %wake *] wire)
(on-take-wake:fine t.t.t.wire error)
2019-06-19 02:59:25 +03:00
::
2019-12-02 03:00:32 +03:00
=/ res=(unit [her=ship =bone]) (parse-pump-timer-wire wire)
?~ res
2019-12-03 10:13:48 +03:00
%- (slog leaf+"ames: got timer for strange wire: {<wire>}" ~)
2019-12-02 03:00:32 +03:00
event-core
2019-06-22 00:35:07 +03:00
::
2019-12-02 03:00:32 +03:00
=/ state=(unit peer-state) (get-peer-state her.u.res)
?~ state
2019-12-03 10:13:48 +03:00
%- (slog leaf+"ames: got timer for strange ship: {<her.u.res>}, ignoring" ~)
2019-12-02 03:00:32 +03:00
event-core
::
2019-12-03 02:46:40 +03:00
=/ =channel [[our her.u.res] now channel-state -.u.state]
2019-06-22 00:35:07 +03:00
::
2019-12-02 03:00:32 +03:00
abet:(on-wake:(make-peer-core u.state channel) bone.u.res error)
2019-06-22 01:25:18 +03:00
:: +on-init: first boot; subscribe to our info from jael
::
++ on-init
^+ event-core
::
=~ (emit duct %pass /turf %j %turf ~)
(emit duct %pass /private-keys %j %private-keys ~)
2019-06-25 00:48:53 +03:00
==
2019-06-25 02:52:22 +03:00
:: +on-priv: set our private key to jael's response
::
++ on-priv
|= [=life vein=(map life private-key)]
2019-06-25 02:52:22 +03:00
^+ event-core
::
=/ =private-key (~(got by vein) life)
2019-06-25 02:52:22 +03:00
=. life.ames-state life
=. crypto-core.ames-state (nol:nu:crub:crypto private-key)
:: recalculate each peer's symmetric key
::
=/ our-private-key sec:ex:crypto-core.ames-state
=. peers.ames-state
%- ~(run by peers.ames-state)
|= =ship-state
^+ ship-state
::
?. ?=(%known -.ship-state)
ship-state
::
=/ =peer-state +.ship-state
=. symmetric-key.peer-state
(derive-symmetric-key public-key.+.ship-state our-private-key)
::
[%known peer-state]
2019-06-25 02:52:22 +03:00
::
event-core
:: +on-publ: update pki data for peer or self
::
++ on-publ
|= [=wire =public-keys-result]
2019-06-25 02:52:22 +03:00
^+ event-core
::
|^ ^+ event-core
::
?- public-keys-result
[%diff @ %rift *]
2019-11-27 01:56:20 +03:00
event-core
2019-06-25 02:52:22 +03:00
::
[%diff @ %keys *]
(on-publ-rekey [who to.diff]:public-keys-result)
2019-06-25 02:52:22 +03:00
::
[%diff @ %spon *]
(on-publ-sponsor [who to.diff]:public-keys-result)
2019-06-25 02:52:22 +03:00
::
[%full *]
(on-publ-full points.public-keys-result)
2019-11-03 07:07:51 +03:00
::
[%breach *]
(on-publ-breach who.public-keys-result)
2019-06-25 02:52:22 +03:00
==
:: +on-publ-breach: handle continuity breach of .ship; wipe its state
::
2019-08-23 06:53:45 +03:00
:: Abandon all pretense of continuity and delete all messaging state
:: associated with .ship, including sent and unsent messages.
2019-12-21 09:56:51 +03:00
:: Also cancel all timers related to .ship.
::
2019-06-25 02:52:22 +03:00
++ on-publ-breach
2019-11-03 07:07:51 +03:00
|= =ship
2019-06-25 02:52:22 +03:00
^+ event-core
::
2019-08-23 05:51:51 +03:00
=/ ship-state (~(get by peers.ames-state) ship)
:: we shouldn't be hearing about ships we don't care about
::
?~ ship-state
2019-11-03 07:07:51 +03:00
~> %slog.0^leaf/"ames: breach unknown {<our^ship>}"
2019-08-23 05:51:51 +03:00
event-core
:: if an alien breached, this doesn't affect us
::
2019-08-23 06:53:45 +03:00
?: ?=([~ %alien *] ship-state)
2019-11-03 07:07:51 +03:00
~> %slog.0^leaf/"ames: breach alien {<our^ship>}"
2019-08-23 05:51:51 +03:00
event-core
2019-11-03 07:07:51 +03:00
~> %slog.0^leaf/"ames: breach peer {<our^ship>}"
2019-08-23 05:51:51 +03:00
:: a peer breached; drop messaging state
::
=/ =peer-state +.u.ship-state
2019-08-23 06:53:45 +03:00
=/ old-qos=qos qos.peer-state
2019-11-22 05:20:39 +03:00
:: cancel all timers related to .ship
::
2019-11-22 05:22:05 +03:00
=. event-core
2019-11-22 05:20:39 +03:00
%+ roll ~(tap by snd.peer-state)
2019-11-22 05:22:05 +03:00
|= [[=snd=bone =message-pump-state] core=_event-core]
2019-11-22 05:20:39 +03:00
^+ core
::
2019-11-22 05:23:34 +03:00
?~ next-wake=next-wake.packet-pump-state.message-pump-state
2019-11-22 05:20:39 +03:00
core
:: note: copies +on-pump-rest:message-pump
::
2019-11-22 05:24:34 +03:00
=/ wire (make-pump-timer-wire ship snd-bone)
=/ duct ~[/ames]
2019-11-22 05:20:39 +03:00
(emit:core duct %pass wire %b %rest u.next-wake)
2019-08-23 06:53:45 +03:00
:: reset all peer state other than pki data
::
=. +.peer-state +:*^peer-state
:: print change to quality of service, if any
::
=/ text=(unit tape) (qos-update-text ship old-qos qos.peer-state)
::
=? event-core ?=(^ text)
(emit duct %pass /qos %d %flog %text u.text)
:: reinitialize galaxy route if applicable
::
=? route.peer-state =(%czar (clan:title ship))
`[direct=%.y lane=[%& ship]]
::
2019-08-23 05:51:51 +03:00
=. peers.ames-state
2019-08-23 06:53:45 +03:00
(~(put by peers.ames-state) ship [%known peer-state])
2019-08-23 05:51:51 +03:00
::
event-core
2019-06-25 03:14:02 +03:00
:: +on-publ-rekey: handle new key for peer
::
:: TODO: assert .crypto-suite compatibility
2019-06-25 02:52:22 +03:00
::
++ on-publ-rekey
|= $: =ship
=life
crypto-suite=@ud
=public-key
2019-06-25 02:52:22 +03:00
==
^+ event-core
::
2019-08-14 03:56:56 +03:00
=/ ship-state (~(get by peers.ames-state) ship)
?. ?=([~ %known *] ship-state)
=| =point
=. life.point life
=. keys.point (my [life crypto-suite public-key]~)
2021-09-25 23:52:53 +03:00
=. sponsor.point `(^^sein:title rof our now ship)
2019-08-14 03:56:56 +03:00
::
(on-publ-full (my [ship point]~))
::
=/ =peer-state +.u.ship-state
::
=/ =private-key sec:ex:crypto-core.ames-state
=. symmetric-key.peer-state
(derive-symmetric-key public-key private-key)
::
=. life.peer-state life
=. public-key.peer-state public-key
::
=. peers.ames-state (~(put by peers.ames-state) ship %known peer-state)
event-core
:: +on-publ-sponsor: handle new or lost sponsor for peer
2019-06-25 03:30:43 +03:00
::
:: TODO: handle sponsor loss
2019-06-25 02:52:22 +03:00
::
++ on-publ-sponsor
|= [=ship sponsor=(unit ship)]
^+ event-core
::
2019-06-25 03:16:32 +03:00
?~ sponsor
2019-08-10 21:48:40 +03:00
~| %ames-lost-sponsor^our^ship !!
2019-06-25 03:16:32 +03:00
::
=/ =peer-state (got-peer-state ship)
2019-06-25 03:16:32 +03:00
=. sponsor.peer-state u.sponsor
::
=. peers.ames-state (~(put by peers.ames-state) ship %known peer-state)
event-core
2019-06-25 03:30:43 +03:00
:: +on-publ-full: handle new pki data for peer(s)
2019-06-25 02:52:22 +03:00
::
++ on-publ-full
|= points=(map ship point)
^+ event-core
::
2019-06-25 03:30:43 +03:00
=> .(points ~(tap by points))
|^ ^+ event-core
?~ points event-core
::
2019-08-06 03:00:04 +03:00
=+ ^- [=ship =point] i.points
2019-06-25 03:30:43 +03:00
::
?. (~(has by keys.point) life.point)
$(points t.points)
::
2019-08-14 03:56:56 +03:00
=/ old-ship-state (~(get by peers.ames-state) ship)
::
=. event-core (insert-peer-state ship point)
::
=? event-core ?=([~ %alien *] old-ship-state)
(meet-alien ship point +.u.old-ship-state)
2019-06-25 03:30:43 +03:00
::
$(points t.points)
::
++ meet-alien
|= [=ship =point todos=alien-agenda]
2019-06-25 03:30:43 +03:00
^+ event-core
:: if we're a comet, send self-attestation packet first
2019-06-26 21:45:07 +03:00
::
=? event-core =(%pawn (clan:title our))
(send-blob | ship (attestation-packet ship life.point))
2019-11-27 01:56:20 +03:00
:: save current duct
::
=/ original-duct duct
:: apply heeds
::
=. event-core
%+ roll ~(tap in heeds.todos)
|= [=^duct core=_event-core]
(on-heed:core(duct duct) ship)
2019-10-05 06:38:32 +03:00
:: apply outgoing messages, reversing for FIFO order
2019-06-25 03:48:05 +03:00
::
=. event-core
%+ reel messages.todos
2019-10-05 06:21:29 +03:00
|= [[=^duct =plea] core=_event-core]
(on-plea:core(duct duct) ship plea)
2019-06-26 21:45:07 +03:00
:: apply outgoing packet blobs
2019-06-25 03:48:05 +03:00
::
=. event-core
%+ roll ~(tap in packets.todos)
2019-10-05 06:21:29 +03:00
|= [=blob core=_event-core]
(send-blob:core | ship blob)
:: apply remote scry requests
::
=. event-core
%+ roll ~(tap in keens.todos)
|= [=path core=_event-core]
(send-request:fine:core ship path 1)
2019-06-25 03:48:05 +03:00
::
2019-11-27 01:56:20 +03:00
event-core(duct original-duct)
2019-06-25 03:30:43 +03:00
--
2019-06-25 03:48:05 +03:00
::
++ insert-peer-state
2019-08-14 03:56:56 +03:00
|= [=ship =point]
2019-06-25 03:48:05 +03:00
^+ event-core
::
2019-08-14 03:56:56 +03:00
=/ =peer-state (gut-peer-state ship)
=/ =public-key pass:(~(got by keys.point) life.point)
2019-06-25 03:48:05 +03:00
=/ =private-key sec:ex:crypto-core.ames-state
=/ =symmetric-key (derive-symmetric-key public-key private-key)
::
=. qos.peer-state [%unborn now]
2019-08-14 03:56:56 +03:00
=. life.peer-state life.point
2019-06-25 03:48:05 +03:00
=. public-key.peer-state public-key
=. symmetric-key.peer-state symmetric-key
=. sponsor.peer-state
?^ sponsor.point
u.sponsor.point
2021-09-25 23:52:53 +03:00
(^^sein:title rof our now ship)
2019-07-24 02:55:35 +03:00
:: automatically set galaxy route, since unix handles lookup
::
=? route.peer-state ?=(%czar (clan:title ship))
`[direct=%.y lane=[%& ship]]
2019-06-25 03:48:05 +03:00
::
=. peers.ames-state
(~(put by peers.ames-state) ship %known peer-state)
::
event-core
2019-06-25 02:52:22 +03:00
--
2019-06-22 01:17:09 +03:00
:: +on-take-turf: relay %turf move from jael to unix
::
++ on-take-turf
|= turfs=(list turf)
^+ event-core
::
(emit unix-duct.ames-state %give %turf turfs)
2019-06-22 01:26:26 +03:00
:: +on-born: handle unix process restart
2019-07-25 01:56:13 +03:00
::
2019-07-31 05:31:15 +03:00
++ on-born
^+ event-core
::
=. unix-duct.ames-state duct
::
=/ turfs
;; (list turf)
=< q.q %- need %- need
2020-12-06 13:55:19 +03:00
(rof ~ %j `beam`[[our %turf %da now] /])
2019-07-31 05:31:15 +03:00
::
(emit unix-duct.ames-state %give %turf turfs)
2020-05-21 10:45:08 +03:00
:: +on-trim: handle request to free memory
2019-06-22 01:19:24 +03:00
:: +on-vega: handle kernel reload
2020-05-20 10:29:54 +03:00
:: +on-trim: handle request to free memory
2019-06-22 01:19:24 +03:00
::
++ on-vega event-core
++ on-trim event-core ::TODO trim fine parts on high prio
2019-06-25 00:13:45 +03:00
:: +enqueue-alien-todo: helper to enqueue a pending request
::
2019-06-26 21:45:07 +03:00
:: Also requests key and life from Jael on first request.
:: On a comet, enqueues self-attestation packet on first request.
2019-06-25 00:13:45 +03:00
::
++ enqueue-alien-todo
|= [=ship mutate=$-(alien-agenda alien-agenda)]
2019-06-25 00:13:45 +03:00
^+ event-core
::
=/ ship-state (~(get by peers.ames-state) ship)
:: create a default $alien-agenda on first contact
2019-06-25 00:13:45 +03:00
::
=+ ^- [already-pending=? todos=alien-agenda]
2019-06-25 00:13:45 +03:00
?~ ship-state
[%.n *alien-agenda]
2019-06-25 00:13:45 +03:00
[%.y ?>(?=(%alien -.u.ship-state) +.u.ship-state)]
:: mutate .todos and apply to permanent state
::
=. todos (mutate todos)
=. peers.ames-state (~(put by peers.ames-state) ship %alien todos)
:: ask jael for .sndr life and keys on first contact
::
?: already-pending
event-core
:: NB: we specifically look for this wire in +public-keys-give in
:: Jael. if you change it here, you must change it there.
::
(emit duct %pass /public-keys %j %public-keys [n=ship ~ ~])
:: +send-blob: fire packet at .ship and maybe sponsors
::
2019-11-28 07:06:39 +03:00
:: Send to .ship and sponsors until we find a direct lane,
:: skipping .our in the sponsorship chain.
2019-08-16 12:26:03 +03:00
::
:: If we have no PKI data for a recipient, enqueue the packet and
:: request the information from Jael if we haven't already.
::
++ send-blob
2020-12-01 17:51:14 +03:00
~/ %send-blob
|= [for=? =ship =blob]
::
2019-12-12 04:59:21 +03:00
=/ final-ship ship
%- (trace rot.veb final-ship |.("send-blob: to {<ship>}"))
|-
2019-08-16 12:26:03 +03:00
|^ ^+ event-core
::
=/ ship-state (~(get by peers.ames-state) ship)
::
?. ?=([~ %known *] ship-state)
%+ enqueue-alien-todo ship
|= todos=alien-agenda
todos(packets (~(put in packets.todos) blob))
2019-08-16 12:26:03 +03:00
::
=/ =peer-state +.u.ship-state
::
2019-12-17 09:57:11 +03:00
:: XX routing hack to mimic old ames.
::
:: Before removing this, consider: moons when their planet is
:: behind a NAT; a planet receiving initial acknowledgment
:: from a star; a planet talking to another planet under
:: another galaxy.
::
?: ?| =(our ship)
?& !=(final-ship ship)
!=(%czar (clan:title ship))
==
==
(try-next-sponsor sponsor.peer-state)
::
2019-11-28 07:06:39 +03:00
?: =(our ship)
:: if forwarding, don't send to sponsor to avoid loops
::
?: for
event-core
2019-11-28 07:06:39 +03:00
(try-next-sponsor sponsor.peer-state)
::
2019-08-16 12:26:03 +03:00
?~ route=route.peer-state
2019-12-12 04:59:21 +03:00
%- (trace rot.veb final-ship |.("no route to: {<ship>}"))
2019-08-16 12:26:03 +03:00
(try-next-sponsor sponsor.peer-state)
::
2019-12-12 04:59:21 +03:00
%- (trace rot.veb final-ship |.("trying route: {<ship>}"))
2019-08-16 12:26:03 +03:00
=. event-core
(emit unix-duct.ames-state %give %send lane.u.route blob)
::
?: direct.u.route
event-core
(try-next-sponsor sponsor.peer-state)
::
2019-08-16 12:26:03 +03:00
++ try-next-sponsor
|= sponsor=^ship
^+ event-core
::
?: =(ship sponsor)
event-core
2019-08-16 12:26:03 +03:00
^$(ship sponsor)
--
:: +attestation-packet: generate signed self-attestation for .her
::
:: Sent by a comet on first contact with a peer. Not acked.
::
++ attestation-packet
|= [her=ship =her=life]
^- blob
%+ encode-packet &
2020-12-01 17:51:14 +03:00
%- encode-open-packet
:_ crypto-core.ames-state
:* ^= public-key pub:ex:crypto-core.ames-state
^= sndr our
^= sndr-life life.ames-state
^= rcvr her
^= rcvr-life her-life
==
2019-12-02 03:00:32 +03:00
:: +get-peer-state: lookup .her state or ~
::
++ get-peer-state
|= her=ship
^- (unit peer-state)
::
=- ?.(?=([~ %known *] -) ~ `+.u)
(~(get by peers.ames-state) her)
2019-06-25 00:32:53 +03:00
:: +got-peer-state: lookup .her state or crash
::
++ got-peer-state
|= her=ship
^- peer-state
::
~| %freaky-alien^her
=- ?>(?=(%known -<) ->)
(~(got by peers.ames-state) her)
2019-08-14 03:56:56 +03:00
:: +gut-peer-state: lookup .her state or default
::
++ gut-peer-state
|= her=ship
^- peer-state
=/ ship-state (~(get by peers.ames-state) her)
?. ?=([~ %known *] ship-state)
*peer-state
+.u.ship-state
2019-06-09 09:26:01 +03:00
:: +make-peer-core: create nested |peer-core for per-peer processing
::
++ make-peer-core
|= [=peer-state =channel]
2019-12-11 21:55:16 +03:00
=* veb veb.bug.channel
|%
++ peer-core .
++ emit |=(move peer-core(event-core (^emit +<)))
++ abet
^+ event-core
::
=. peers.ames-state
(~(put by peers.ames-state) her.channel %known peer-state)
::
event-core
2019-12-11 21:55:16 +03:00
++ trace
|= [verb=? print=(trap tape)]
^+ same
(^trace verb her.channel print)
2019-11-04 04:35:45 +03:00
++ on-heed peer-core(heeds.peer-state (~(put in heeds.peer-state) duct))
++ on-jilt peer-core(heeds.peer-state (~(del in heeds.peer-state) duct))
:: +update-qos: update and maybe print connection status
::
++ update-qos
|= =new=qos
^+ peer-core
::
=^ old-qos qos.peer-state [qos.peer-state new-qos]
2019-11-04 04:35:45 +03:00
:: if no update worth reporting, we're done
::
?~ text=(qos-update-text her.channel old-qos new-qos)
peer-core
2019-11-04 04:35:45 +03:00
:: print message
::
=. peer-core (emit duct %pass /qos %d %flog %text u.text)
2019-11-27 07:52:43 +03:00
:: if peer has stopped responding, check if %boon's are backing up
2019-11-04 04:35:45 +03:00
::
?. ?=(?(%dead %unborn) -.qos.peer-state)
peer-core
2019-11-27 07:52:43 +03:00
check-clog
:: +check-clog: notify clients if peer has stopped responding
::
++ check-clog
^+ peer-core
2019-11-04 04:35:45 +03:00
::
2019-11-22 16:43:08 +03:00
:: Only look at response bones. Request bones are unregulated,
:: since requests tend to be much smaller than responses.
::
=/ pumps=(list message-pump-state)
%+ murn ~(tap by snd.peer-state)
|= [=bone =message-pump-state]
?: =(0 (end 0 bone))
2019-11-22 16:43:08 +03:00
~
`u=message-pump-state
:: clogged: are five or more response messages unsent to this peer?
::
=/ clogged=?
=| acc=@ud
|- ^- ?
?~ pumps
%.n
=. acc
%+ add acc
%+ add
:: in-flight messages
::
(sub [next current]:i.pumps)
:: queued messages
::
~(wyt in unsent-messages.i.pumps)
::
2019-11-22 16:43:08 +03:00
?: (gte acc 5)
%.y
$(pumps t.pumps)
:: if clogged, notify client vanek
::
?. clogged
peer-core
2019-11-04 04:35:45 +03:00
%+ roll ~(tap in heeds.peer-state)
|=([d=^duct core=_peer-core] (emit:core d %give %clog her.channel))
:: +on-hear-shut-packet: handle receipt of ack or message fragment
::
++ on-hear-shut-packet
|= [=lane =shut-packet dud=(unit goof)]
^+ peer-core
:: update and print connection status
::
2019-08-08 08:18:25 +03:00
=. peer-core (update-qos %live last-contact=now)
::
=/ =bone bone.shut-packet
::
2019-06-10 19:13:11 +03:00
?: ?=(%& -.meat.shut-packet)
=+ ?. &(?=(^ dud) msg.veb) ~
2021-02-19 05:47:02 +03:00
%. ~
%- slog
:_ tang.u.dud
leaf+"ames: {<her.channel>} fragment crashed {<mote.u.dud>}"
(run-message-sink bone %hear lane shut-packet ?=(~ dud))
:: Just try again on error, printing trace
2019-08-06 02:05:40 +03:00
::
:: Note this implies that vanes should never crash on %done,
:: since we have no way to continue using the flow if they do.
::
=+ ?~ dud ~
%. ~
%+ slog leaf+"ames: {<her.channel>} ack crashed {<mote.u.dud>}"
?. msg.veb ~
:- >[bone=bone message-num=message-num meat=meat]:shut-packet<
tang.u.dud
2019-06-20 10:21:37 +03:00
(run-message-pump bone %hear [message-num +.meat]:shut-packet)
2019-06-20 11:18:07 +03:00
:: +on-memo: handle request to send message
::
++ on-memo
2019-11-27 07:52:43 +03:00
|= [=bone payload=* valence=?(%plea %boon)]
2019-06-20 11:18:07 +03:00
^+ peer-core
2021-01-20 23:29:45 +03:00
=/ =message-blob (dedup-message (jim payload))
2019-11-27 07:52:43 +03:00
=. peer-core (run-message-pump bone %memo message-blob)
::
?: ?& =(%boon valence)
(gte now (add ~s30 last-contact.qos.peer-state))
==
2019-11-27 07:52:43 +03:00
check-clog
peer-core
2020-05-02 08:55:14 +03:00
:: +dedup-message: replace with any existing copy of this message
::
++ dedup-message
|= =message-blob
^+ message-blob
2020-05-05 07:18:26 +03:00
?: (lte (met 13 message-blob) 1)
message-blob
2020-05-02 08:55:14 +03:00
=/ peers-l=(list [=ship =ship-state]) ~(tap by peers.ames-state)
|- ^+ message-blob
=* peer-loop $
?~ peers-l
message-blob
?. ?=(%known -.ship-state.i.peers-l)
peer-loop(peers-l t.peers-l)
=/ snd-l=(list [=bone =message-pump-state])
~(tap by snd.ship-state.i.peers-l)
|- ^+ message-blob
=* bone-loop $
?~ snd-l
peer-loop(peers-l t.peers-l)
=/ blob-l=(list ^message-blob)
~(tap to unsent-messages.message-pump-state.i.snd-l)
|- ^+ message-blob
=* blob-loop $
?^ blob-l
?: =(i.blob-l message-blob)
i.blob-l
blob-loop(blob-l t.blob-l)
?~ unsent-fragments.message-pump-state.i.snd-l
2020-05-02 08:55:14 +03:00
bone-loop(snd-l t.snd-l)
?: =(message-blob fragment.i.unsent-fragments.message-pump-state.i.snd-l)
`@`fragment.i.unsent-fragments.message-pump-state.i.snd-l
bone-loop(snd-l t.snd-l)
2019-06-22 00:35:07 +03:00
:: +on-wake: handle timer expiration
::
++ on-wake
|= [=bone error=(unit tang)]
^+ peer-core
2019-11-22 03:52:57 +03:00
:: if we previously errored out, print and reset timer for later
2019-06-22 00:35:07 +03:00
::
2019-11-22 03:52:57 +03:00
:: This really shouldn't happen, but if it does, make sure we
:: don't brick either this messaging flow or Behn.
::
?^ error
=. peer-core
(emit duct %pass /wake-fail %d %flog %crud %ames-wake u.error)
::
2019-11-22 05:13:09 +03:00
?~ message-pump-state=(~(get by snd.peer-state) bone)
peer-core
?~ next-wake.packet-pump-state.u.message-pump-state
peer-core
:: If we crashed because we woke up too early, assume another
:: timer is already set.
::
?: (lth now.channel u.next-wake.packet-pump-state.u.message-pump-state)
peer-core
2019-11-22 05:13:09 +03:00
::
2019-11-22 03:52:57 +03:00
=/ =wire (make-pump-timer-wire her.channel bone)
2019-11-22 04:41:17 +03:00
(emit duct %pass wire %b %wait (add now.channel ~s30))
:: update and print connection state
::
=. peer-core %- update-qos
=/ expiry=@da (add ~s30 last-contact.qos.peer-state)
=? -.qos.peer-state
(gte now.channel expiry)
%dead
qos.peer-state
2019-06-25 00:09:42 +03:00
:: expire direct route
::
:: If the peer is not responding, mark the .lane.route as
2019-06-25 00:09:42 +03:00
:: indirect. The next packets we emit will be sent to the
:: receiver's sponsorship chain in case the receiver's
:: transport address has changed and this lane is no longer
:: valid.
::
:: If .her is a galaxy, the lane will always remain direct.
::
=? route.peer-state
?& ?=(%dead -.qos.peer-state)
?=(^ route.peer-state)
2019-07-24 02:55:35 +03:00
direct.u.route.peer-state
!=(%czar (clan:title her.channel))
==
route.peer-state(direct.u %.n)
:: resend comet attestation packet if first message times out
::
:: The attestation packet doesn't get acked, so if we tried to
:: send a packet but it timed out, maybe they didn't get our
:: attestation.
::
:: Only resend on timeout of packets in the first message we
:: send them, since they should remember forever.
::
=? event-core
?& ?=(%pawn (clan:title our))
=(1 current:(~(got by snd.peer-state) bone))
==
(send-blob | her.channel (attestation-packet [her her-life]:channel))
:: maybe resend some timed out packets
2019-06-25 00:09:42 +03:00
::
2019-06-22 00:35:07 +03:00
(run-message-pump bone %wake ~)
2019-07-28 10:50:32 +03:00
:: +send-shut-packet: fire encrypted packet at rcvr and maybe sponsors
::
++ send-shut-packet
|= =shut-packet
^+ peer-core
2019-12-21 09:56:51 +03:00
:: swizzle last bone bit before sending
::
:: The peer has the opposite perspective from ours about what
:: kind of flow this is (forward/backward), so flip the bit
:: here.
2019-07-28 10:50:32 +03:00
::
2020-12-01 17:51:14 +03:00
=. event-core
%^ send-blob | her.channel
%+ encode-packet &
2020-12-01 17:51:14 +03:00
%: encode-shut-packet
shut-packet(bone (mix 1 bone.shut-packet))
symmetric-key.channel
our her.channel
our-life.channel her-life.channel
==
2019-07-28 10:50:32 +03:00
peer-core
:: +got-duct: look up $duct by .bone, asserting already bound
::
++ got-duct
|= =bone
^- ^duct
~| %dangling-bone^her.channel^bone
(~(got by by-bone.ossuary.peer-state) bone)
2019-06-10 19:13:11 +03:00
:: +run-message-pump: process $message-pump-task and its effects
2019-06-09 09:26:01 +03:00
::
++ run-message-pump
|= [=bone task=message-pump-task]
^+ peer-core
:: pass .task to the |message-pump and apply state mutations
::
=/ =message-pump-state
(~(gut by snd.peer-state) bone *message-pump-state)
::
2019-06-09 09:26:01 +03:00
=/ message-pump (make-message-pump message-pump-state channel)
=^ pump-gifts message-pump-state (work:message-pump task)
=. snd.peer-state (~(put by snd.peer-state) bone message-pump-state)
:: process effects from |message-pump
::
2019-06-09 09:26:01 +03:00
|^ ^+ peer-core
?~ pump-gifts peer-core
=* gift i.pump-gifts
=. peer-core
?- -.gift
%done (on-pump-done [message-num error]:gift)
2019-06-20 11:13:54 +03:00
%send (on-pump-send static-fragment.gift)
%wait (on-pump-wait date.gift)
%rest (on-pump-rest date.gift)
2019-06-09 09:26:01 +03:00
==
$(pump-gifts t.pump-gifts)
2019-06-20 11:13:54 +03:00
:: +on-pump-done: handle |message-pump's report of message (n)ack
::
2019-06-20 11:13:54 +03:00
++ on-pump-done
|= [=message-num error=(unit error)]
2019-06-09 09:26:01 +03:00
^+ peer-core
2019-06-20 03:48:29 +03:00
:: if odd bone, ack is on "subscription update" message; no-op
2019-06-19 03:52:10 +03:00
::
?: =(1 (end 0 bone))
2019-06-19 03:52:10 +03:00
peer-core
2019-06-20 03:48:29 +03:00
:: even bone; is this bone a nack-trace bone?
2019-06-19 03:38:25 +03:00
::
?: =(1 (end 0 (rsh 0 bone)))
:: nack-trace bone; assume .ok, clear nack from |message-sink
2019-06-19 03:52:10 +03:00
::
=/ target-bone=^bone (mix 0b10 bone)
::
(run-message-sink target-bone %drop message-num)
:: not a nack-trace bone; relay ack to client vane
::
(emit (got-duct bone) %give %done error)
:: +on-pump-send: emit message fragment requested by |message-pump
2019-06-09 09:26:01 +03:00
::
2019-06-20 11:13:54 +03:00
++ on-pump-send
2020-12-01 17:51:14 +03:00
|=(f=static-fragment (send-shut-packet bone [message-num %& +]:f))
2019-06-20 11:13:54 +03:00
:: +on-pump-wait: relay |message-pump's set-timer request
2019-06-09 09:26:01 +03:00
::
2019-06-20 11:13:54 +03:00
++ on-pump-wait
2019-06-09 09:26:01 +03:00
|= date=@da
^+ peer-core
::
2019-06-19 02:59:25 +03:00
=/ =wire (make-pump-timer-wire her.channel bone)
=/ duct ~[/ames]
(emit duct %pass wire %b %wait date)
2019-06-20 11:13:54 +03:00
:: +on-pump-rest: relay |message-pump's unset-timer request
2019-06-09 09:26:01 +03:00
::
2019-06-20 11:13:54 +03:00
++ on-pump-rest
2019-06-09 09:26:01 +03:00
|= date=@da
^+ peer-core
::
2019-06-19 02:59:25 +03:00
=/ =wire (make-pump-timer-wire her.channel bone)
=/ duct ~[/ames]
(emit duct %pass wire %b %rest date)
2019-06-09 09:26:01 +03:00
--
:: +run-message-sink: process $message-sink-task and its effects
::
++ run-message-sink
|= [=bone task=message-sink-task]
^+ peer-core
:: pass .task to the |message-sink and apply state mutations
2019-06-08 12:45:30 +03:00
::
=/ =message-sink-state
(~(gut by rcv.peer-state) bone *message-sink-state)
2019-06-08 12:45:30 +03:00
::
=/ message-sink (make-message-sink message-sink-state channel)
=^ sink-gifts message-sink-state (work:message-sink task)
=. rcv.peer-state (~(put by rcv.peer-state) bone message-sink-state)
:: process effects from |message-sink
2019-06-08 12:45:30 +03:00
::
2019-06-20 11:13:54 +03:00
|^ ^+ peer-core
2019-11-28 03:58:26 +03:00
?~ sink-gifts peer-core
=* gift i.sink-gifts
2019-06-20 11:13:54 +03:00
=. peer-core
?- -.gift
%memo (on-sink-memo [message-num message]:gift)
%send (on-sink-send [message-num ack-meat]:gift)
2019-06-20 11:13:54 +03:00
==
$(sink-gifts t.sink-gifts)
:: +on-sink-send: emit ack packet as requested by |message-sink
::
++ on-sink-send
2020-12-01 17:51:14 +03:00
|=([num=message-num ack=ack-meat] (send-shut-packet bone num %| ack))
:: +on-sink-memo: dispatch message received by |message-sink
2019-06-20 11:13:54 +03:00
::
2019-07-28 10:50:32 +03:00
:: odd bone: %plea request message
:: even bone, 0 second bit: %boon response message
:: even bone, 1 second bit: nack-trace %boon message
2019-06-20 11:13:54 +03:00
::
++ on-sink-memo
?: =(1 (end 0 bone))
on-sink-plea
?: =(0 (end 0 (rsh 0 bone)))
on-sink-boon
on-sink-nack-trace
:: +on-sink-boon: handle response message received by |message-sink
2019-07-28 10:50:32 +03:00
::
:: .bone must be mapped in .ossuary.peer-state, or we crash.
2019-12-21 09:56:51 +03:00
:: This means a malformed message will kill a flow. We
:: could change this to a no-op if we had some sort of security
:: reporting.
::
2019-12-03 01:49:10 +03:00
:: Note that if we had several consecutive packets in the queue
:: and crashed while processing any of them, the %hole card
:: will turn *all* of them into losts/nacks.
::
:: TODO: This handles a previous crash in the client vane, but not in
:: Ames itself.
::
++ on-sink-boon
2019-07-28 10:50:32 +03:00
|= [=message-num message=*]
2019-06-20 11:13:54 +03:00
^+ peer-core
:: send ack unconditionally
2019-06-20 11:13:54 +03:00
::
=. peer-core (emit (got-duct bone) %give %boon message)
=. peer-core (run-message-sink bone %done ok=%.y)
2019-06-20 11:13:54 +03:00
::
?. ?=([%hear * * ok=%.n] task)
:: fresh boon; give message to client vane
2019-06-25 01:18:18 +03:00
::
2021-02-19 05:47:02 +03:00
%- %+ trace msg.veb
=/ dat [her.channel bone=bone message-num=message-num -.task]
|.("sink boon {<dat>}")
peer-core
:: we previously crashed on this message; notify client vane
2019-06-20 11:13:54 +03:00
::
2020-12-12 03:45:13 +03:00
%- %+ trace msg.veb
2021-02-19 05:47:02 +03:00
=/ dat [her.channel bone=bone message-num=message-num -.task]
|.("crashed on sink boon {<dat>}")
boon-to-lost
:: +boon-to-lost: convert all boons to losts
::
++ boon-to-lost
^+ peer-core
=. moves
%+ turn moves
|= =move
?. ?=([* %give %boon *] move)
move
[duct.move %give %lost ~]
peer-core
:: +on-sink-nack-trace: handle nack-trace received by |message-sink
2019-07-28 10:50:32 +03:00
::
++ on-sink-nack-trace
2019-07-28 10:50:32 +03:00
|= [=message-num message=*]
^+ peer-core
2021-02-19 05:47:02 +03:00
%- %+ trace msg.veb
=/ dat [her.channel bone=bone message-num=message-num]
|.("sink naxplanation {<dat>}")
2019-06-20 11:13:54 +03:00
::
=+ ;; =naxplanation message
:: ack nack-trace message (only applied if we don't later crash)
2019-06-20 11:13:54 +03:00
::
=. peer-core (run-message-sink bone %done ok=%.y)
2019-06-20 11:13:54 +03:00
:: flip .bone's second bit to find referenced flow
::
=/ target-bone=^bone (mix 0b10 bone)
2019-12-02 23:20:57 +03:00
:: notify |message-pump that this message got naxplained
2019-06-20 11:13:54 +03:00
::
2019-12-02 23:20:57 +03:00
(run-message-pump target-bone %near naxplanation)
:: +on-sink-plea: handle request message received by |message-sink
2019-06-09 09:40:30 +03:00
::
++ on-sink-plea
2019-07-28 10:50:32 +03:00
|= [=message-num message=*]
^+ peer-core
2021-02-19 05:47:02 +03:00
%- %+ trace msg.veb
=/ dat [her.channel bone=bone message-num=message-num]
|.("sink plea {<dat>}")
2019-08-06 02:05:40 +03:00
:: is this the first time we're trying to process this message?
2019-07-28 10:50:32 +03:00
::
2019-08-06 02:05:40 +03:00
?. ?=([%hear * * ok=%.n] task)
:: fresh plea; pass to client vane
::
=+ ;; =plea message
::
=/ =wire (make-bone-wire her.channel bone)
::
?+ vane.plea ~| %ames-evil-vane^our^her.channel^vane.plea !!
%a (emit duct %pass wire %a %plea her.channel plea)
2019-08-06 02:05:40 +03:00
%c (emit duct %pass wire %c %plea her.channel plea)
%g (emit duct %pass wire %g %plea her.channel plea)
%j (emit duct %pass wire %j %plea her.channel plea)
2019-08-06 02:05:40 +03:00
==
:: we previously crashed on this message; send nack
::
=. peer-core (run-message-sink bone %done ok=%.n)
:: also send nack-trace with blank .error for security
2019-08-06 02:05:40 +03:00
::
=/ nack-trace-bone=^bone (mix 0b10 bone)
=/ =naxplanation [message-num *error]
=/ =message-blob (jam naxplanation)
2019-08-06 02:05:40 +03:00
::
(run-message-pump nack-trace-bone %memo message-blob)
2019-07-28 10:50:32 +03:00
--
--
::
++ fine
=* state fine-state.ames-state
=< |%
++ fine-peer
|_ $: =ship
peer=peer-state
==
+* scry scry.peer
++ pe-core .
++ pe-abed
|= s=^ship
^- (unit _pe-core)
?~ sta=(get-peer-state s)
~
`pe-core(ship s, peer u.sta)
::
++ pe-abet
^+ event-core
=. peers.ames-state
(~(put by peers.ames-state) ship known/peer)
event-core
++ pe-lane (get-lane ship)
++ pe-keen
|= =path
?: (~(has by order.scry) path)
pe-core
=/ keen-id=@ud seq.scry
=. seq.scry +(seq.scry)
=. order.scry
(~(put by order.scry) path keen-id)
=| =keen-state
=. keens.scry (put:orm keens.scry keen-id keen-state)
ke-abet:ke-start:(ke-abed:keen-core path)
::
2022-02-24 01:00:20 +03:00
:: TODO: fix, only cancel on when no more subscribers
++ pe-yawn
|= =path
=/ keen-id=@ud (~(got by order.scry) path)
=. order.scry (~(del by order.scry) path)
=. keens.scry +:(del:orm keens.scry keen-id)
~& yawn/path
pe-core
::
++ pe-hear
|= [=lane =packet]
?> =(sndr-tick.packet (mod life.peer 16))
::
=/ [=peep =purr] (decode-request-info `@ux`content.packet)
?. (~(has by order.scry) path.peep)
~&(dead-response/peep pe-core)
=< ke-abet
(ke-rcv:(ke-abed:keen-core path.peep) num.peep purr lane)
::
++ pe-update-qos
|= =new=qos
=^ old-qos qos.peer [qos.peer new-qos]
?~ text=(qos-update-text ship old-qos new-qos)
pe-core
:: print message
::
=. event-core (emit duct %pass /qos %d %flog %text u.text)
pe-core
::
++ pe-take-wake
|= =wire
^+ pe-core
ke-abet:ke-take-wake:(ke-abed:keen-core wire)
::
++ keen-core
|_ $: =path
keen-id=@ud
keen=keen-state
==
++ ke-core .
++ ke-abet
2022-02-15 17:47:38 +03:00
^+ pe-core
=/ gone=?
=, keen
:: num-fragments is 0 when unknown (i.e. no response
:: yet)
&(!=(0 num-fragments) =(num-fragments num-received))
2022-02-15 17:47:38 +03:00
?: gone
ke-abet-gone
=. ke-core ke-set-wake
=. keens.scry
(put:orm keens.scry keen-id keen)
2022-02-15 17:47:38 +03:00
pe-core
::
++ ke-show
=, keen
:* nex=(lent nex)
2022-02-15 17:47:38 +03:00
hav=(lent hav)
num-fragments=num-fragments
num-received=num-received
next-wake=next-wake
metrics=metrics
==
::
++ ke-abet-gone
=. ke-core ke-set-wake
=. keens.scry
+:(del:orm keens.scry keen-id)
=. order.scry
(~(del by order.scry) path)
pe-core
::
++ ke-abed
|= p=^path
~| no-keen-for-path/p
=. keen-id (~(got by order.scry) p)
ke-core(path p, keen (got:orm keens.scry keen-id))
::
++ ke-abed-id
|= id=@ud
%- ke-abed
~| no-path-for-id/id
%- need
^- (unit ^path)
%- ~(rep by order.scry)
|= [[p=^path i=@ud] out=(unit ^path)]
^- (unit ^path)
?^ out out
?:(=(id i) `p ~)
++ ke-deq
(deq want)
::
++ ke-encode-req
|= frag=@ud
(encode-request ship path frag)
::
++ ke-on-ack
=| marked=(list want)
|= fra=@ud
^- [? _ke-core]
=; [[found=? cor=_ke-core] wan=(pha want)]
?. found
[found ke-core]
[found cor(wan.keen wan)]
%^ (dip-left:ke-deq ,[found=? cor=_ke-core]) wan.keen
[| ke-core]
|= [[found=? cor=_ke-core] =want]
^- [(unit _want) stop=? [found=? cor=_ke-core]]
=. ke-core cor
?: =(fra fra.want)
=. metrics.keen
2022-02-15 17:47:38 +03:00
(on-ack:ke-gauge +>.want)
[~ %.y %.y ke-core]
=. skips.want +(skips.want)
2022-02-15 17:47:38 +03:00
=^ resend=? metrics.keen
(on-skipped-packet:ke-gauge +>.want)
?. resend
[`want %.n found ke-core]
=. tries.want +(tries.want)
=. last-sent.want now
=. ke-core
2022-02-15 17:47:38 +03:00
(ke-resend [fra hoot]:want)
[`want %.n found ke-core]
::
++ ke-start
~| tried-to-start-request-already-in-progress/path
~& start/now
?> =(num-fragments.keen 0)
=/ fra=@ 1
=/ req (ke-encode-req fra)
2022-02-15 17:47:38 +03:00
=/ =want [fra req now 1 0]
=. wan.keen (cons:ke-deq *(pha ^want) want)
2022-02-15 17:47:38 +03:00
=. metrics.keen (on-sent:ke-gauge 1)
=- ke-core(event-core -)
%- emit
[unix-duct.ames-state %give %send pe-lane `@ux`req]
::
++ ke-done
|= [sig=@ data=$@(~ (cask))]
?> (meri:keys ship life.peer path sig data)
~& got-response/path
2022-02-24 01:00:20 +03:00
=/ listeners ~(tap in listeners.keen)
=/ dat=(unit (cask))
?~(data ~ `data)
|- ^+ ke-core
?~ listeners
ke-core
=. event-core
(emit i.listeners %give %tune path dat)
$(listeners t.listeners)
::
++ ke-first-rcv
|= =rawr
^+ ke-core
=- ke-core(keen -)
::
2022-02-14 19:34:26 +03:00
=/ paz=(list want)
%+ turn (gulf 1 siz.rawr)
|= fra=@ud
^- want
2022-02-15 17:47:38 +03:00
[fra (ke-encode-req fra) now 0 0]
2022-02-14 19:34:26 +03:00
::
%_ keen
num-fragments siz.rawr
2022-02-15 17:47:38 +03:00
nex (tail paz)
==
:: +ke-continue: send packets according to normal congestion flow
2022-02-14 19:34:26 +03:00
::
++ ke-continue
2022-02-14 19:34:26 +03:00
=| inx=@ud
2022-02-15 17:47:38 +03:00
=| sent=(list @ud)
2022-02-14 19:34:26 +03:00
=/ max num-slots:ke-gauge
|- ^+ ke-core
2022-02-15 17:47:38 +03:00
?: |(=(~ nex.keen) =(inx max))
ke-core
=^ =want nex.keen nex.keen
2022-02-15 17:47:38 +03:00
=. last-sent.want now
=. tries.want +(tries.want)
=. wan.keen (snoc:ke-deq wan.keen want)
=. metrics.keen (on-sent:ke-gauge 1)
=. ke-core (ke-emit hoot.want)
$(inx +(inx))
2022-02-15 17:47:38 +03:00
::
++ ke-resend
2022-02-14 19:34:26 +03:00
|= [fra=@ud =hoot]
(ke-emit hoot)
::
++ ke-emit
|= =hoot
^+ ke-core
=- ke-core(event-core -)
%- emit
[unix-duct.ames-state %give %send pe-lane `@ux`hoot]
::
++ ke-decode-full
=, keen
~& num/num-received
~| %frag-mismatch
~| have/num-received
~| need/num-fragments
2022-02-24 01:00:20 +03:00
~| path/path
?> =(num-fragments num-received)
?> =((lent hav) num-received)
(decode-response-msg num-fragments hav)
::
::
++ ke-rcv
|= [fra=@ud =purr =lane:ames]
^+ ke-core
2022-02-15 17:47:38 +03:00
=/ =rawr (decode-response-packet purr)
=/ og ke-core
2022-02-15 17:47:38 +03:00
=. pe-core (pe-update-qos %live last-contact=now)
:: handle empty
?: =(0 siz.rawr)
?> =(~ dat.rawr)
(ke-done sig.rawr ~)
:: update congestion, or fill details
::
=? ke-core =(0 num-fragments.keen)
?> =(fra 1)
(ke-first-rcv rawr)
::
~| failed-signature/fra^`@ux`sig.rawr
~| life.peer
?> (veri-fra:keys ship life.peer path fra [dat sig]:rawr)
=^ found=? ke-core
(ke-on-ack fra)
::
?. found
:: discard changes
~& dupe-ack/fra
og
=/ =have [fra rawr]
=. hav.keen
`(list ^have)`[have hav.keen]
=. num-received.keen +(num-received.keen)
?: =(num-fragments num-received):keen
(ke-done [sig dat]:ke-decode-full)
ke-continue
::
++ ke-gauge
2022-02-15 17:47:38 +03:00
=* bug bug.ames-state
(make-pump-gauge now metrics.keen ship bug)
::
++ ke-timer-wire
`wire`(welp /fine/behn/wake/(scot %p ship) path)
::
++ ke-pass-timer
|= =note
2022-02-15 17:47:38 +03:00
ke-core(event-core (emit unix-duct.ames-state %pass ke-timer-wire note))
::
++ ke-wait |=(tim=@da (ke-pass-timer %b %wait tim))
++ ke-rest |=(tim=@da (ke-pass-timer %b %rest tim))
++ ke-set-wake
^+ ke-core
=/ next-wake=(unit @da)
=/ want=(unit want) (peek-left:ke-deq wan.keen)
?~ want ~
`(next-expiry:ke-gauge +>:u.want)
?: =(next-wake next-wake.keen)
ke-core
=? ke-core !=(~ next-wake.keen)
=/ old (need next-wake.keen)
=. next-wake.keen ~
(ke-rest old)
=? ke-core ?=(^ next-wake)
=. next-wake.keen next-wake
(ke-wait u.next-wake)
ke-core
2022-02-14 19:34:26 +03:00
:: +ke-take-wake: handle request packet timeout
::
++ ke-take-wake
^+ ke-core
2022-02-15 17:47:38 +03:00
=. next-wake.keen ~
=. pe-core %- pe-update-qos
=/ expiry=@da (add ~s30 last-contact.qos.peer)
=? -.qos.peer
(gte now expiry)
%dead
qos.peer
:: expire direct route
=? route.peer
?& ?=(%dead -.qos.peer)
?=(^ route.peer)
direct.u.route.peer
!=(%czar (clan:title ship))
==
route.peer(direct.u %.n)
2022-02-15 17:47:38 +03:00
=. metrics.keen on-timeout:ke-gauge
=^ want=(unit want) wan.keen
(pop-left:ke-deq wan.keen)
~| %took-wake-for-empty-want
?> ?=(^ want)
=: tries.u.want +(tries.u.want)
last-sent.u.want now
2022-02-14 19:34:26 +03:00
==
=. wan.keen (cons:ke-deq wan.keen u.want)
(ke-resend [fra hoot]:u.want)
--
--
++ on-keen
|= =path
^+ event-core
=/ omen
~| [%fine %invalid-namespace-path path]
(need (de-omen path))
=* ship p.bem.omen
=/ peer-core (pe-abed:fine-peer ship)
?^ peer-core pe-abet:(pe-keen:u.peer-core path)
%+ enqueue-alien-todo ship
|= todos=alien-agenda
todos(keens (~(put in keens.todos) path))
::
++ on-yawn
|= =path
^+ event-core
2022-02-24 01:00:20 +03:00
=/ omen
~| [%fine %invalid-namespace-path path]
(need (de-omen path))
=/ peer-core (pe-abed:fine-peer p.bem.omen)
?~ peer-core !!
pe-abet:(pe-yawn:u.peer-core path)
::
++ on-bide
|= =path
^+ event-core
=. hear.state (~(put ju hear.state) path duct)
2022-02-02 05:30:11 +03:00
:: TODO: other vanes?
(bide-clay path)
::
++ bide-clay
|= =path
^+ event-core
?> ?=([@ @ *] path)
=/ =wire
(welp /fine/bide path)
=/ =ship (slav %p i.t.path)
=/ [vis=view bem=beam]
(need (de-omen path))
=+ ;; =care:clay
?^ vis car.vis
(rsh 3^1 vis)
=/ =rave:clay
[%sing care r.bem s.bem]
(emit duct %pass wire %c %warp p.bem q.bem `rave)
::
++ on-take-clay-bide
|= [=wire =riot:clay]
?> ?=([%fine %bide @ *] wire)
=/ pax=path t.t.wire
=/ cas=(unit (cask))
?~ riot ~
`[p q.q]:r.u.riot
2022-02-02 05:30:11 +03:00
=/ wanted (~(get ju want.state) pax)
=. want.state (~(del by want.state) pax)
=/ =song
(encode-response pax (fall cas ~))
2022-02-02 05:30:11 +03:00
%- emil
%+ turn [unix-duct.ames-state ~(tap in wanted)]
|= d=^duct
[d %give %howl pax song]
::
++ on-take-wake
|= [=wire error=(unit tang)]
^+ event-core
~| fine-on-take-wake/wire
2022-02-15 17:47:38 +03:00
?^ error
%- (slog leaf/"bad wake" u.error)
event-core
:: TODO: handle error case
?> ?=([@ *] wire)
=/ =ship (slav %p i.wire)
~| %no-ship-for-wake
=/ peer-core (need (pe-abed:fine-peer ship))
pe-abet:(pe-take-wake:peer-core t.wire)
::
++ on-hear-response
|= [=lane =packet dud=(unit goof)]
^+ event-core
?^ dud
::TODO handle
2022-02-01 14:42:13 +03:00
~& [%fine %done-goofed mote.u.dud]
%- (slog tang.u.dud)
event-core
::NOTE we only send requests to ships we know,
:: so we should only get responses from ships we know.
:: below we assume sndr.packet is a known peer.
=* from sndr.packet
=/ peer-core (need (pe-abed:fine-peer from))
pe-abet:(pe-hear:peer-core lane packet)
--
|%
+$ twit :: signed request
$: signature=@
peep
==
::
+$ peep :: request data
$: =path
num=@ud
==
::
+$ rawr :: response packet ::TODO meow
$: sig=@
siz=@ud
byts
==
::
+$ roar :: response message
$: sig=@
dat=$@(~ (cask))
==
++ orm ((on @ud keen-state) lte)
::
++ spit
|= =path
^- [pat=@t wid=@ud]
=+ pat=(spat path)
=+ wid=(met 3 pat)
?> (lte wid 384) ::TODO check when we handle %keen, in addition to here
[pat wid]
::
++ request-body
|= [=path num=@ud]
::NOTE path is expected to be a namespace path without the ship
^- byts
?> (lth num (bex 32))
=+ (spit path)
2022-02-01 14:42:13 +03:00
:- :(add 4 2 wid)
%+ can 3
:~ 4^num :: fragment number
2^wid :: path size
wid^`@`pat :: namespace path
==
::
++ encode-request
|= [=ship =path num=@ud]
::NOTE path is expected to be a namespace path without the ship
^- hoot ^- @
=+ bod=(request-body path num)
=+ sig=64^(sign:keys dat.bod)
=+ syn=(can 3 sig bod ~)
%+ con 0b100 ::NOTE request bit
%^ encode-packet |
[our ship]
[(mod life.ames-state 16) (mod (lyfe:keys ship) 16) ~ syn]
::
++ encode-response ::TODO unit tests
|= [=path data=$@(~ (cask))]
^- song
:: prepend request descriptions to each response packet
::
=; pacs=(list @ux)
%- head
%^ spin pacs 1
|= [pac=@ux num=@ud]
^- [purr _num]
:_ +(num)
^- @ux
::NOTE we stub out the receiver & origin details,
:: runtime should replace them as appropriate.
(encode-packet | [our ~zod] (mod life.ames-state 16) 0b0 ~ pac)
:: prepend a signature and split the data into 1024-byte fragments
::
=/ frag=(list @)
=/ sig=@ (full:keys path data)
?~ data [sig]~
%+ rip response-size ::NOTE 1024 bytes
(cat 3 sig (jam data)) ::TODO should include life
:: sign & packetize the fragments
::
%- head
%^ spin frag 1
|= [dat=@ num=@ud]
:_ +(num)
^- @ux
=/ req=byts (request-body path num)
=/ bod=byts
=/ wid=@ud (met 3 dat)
:- :(add 4 2 wid)
%+ can 3
:~ 4^(lent frag) :: number of fragments
2^wid :: response data fragment size in bytes
wid^dat :: response data fragment
==
=/ sig=byts
64^(sign-fra:keys path num dat)
(can 3 req sig bod ~)
::
++ keys
|%
++ mess
|=([@p life path $@(~ (cask))] (jam +<))
::
++ full
|= [=path data=$@(~ (cask))]
(sign (mess our life.ames-state path data))
::
++ sign-fra
|= [=path fra=@ dat=@ux]
(sign (jam path fra dat))
::
++ veri-fra
|= [who=ship lyf=life =path fra=@ dat=@ux sig=@]
(veri who lyf sig (jam path fra dat))
::
++ sign
sigh:as:crypto-core.ames-state
::
++ lyfe
|= who=ship
^- life
~| [%fine %unknown-peer who]
=/ ship-state (~(got by peers.ames-state) who)
?> ?=([%known *] ship-state)
life.ship-state
::
++ pass
|= [who=ship lyf=life]
~| [%fine %unknown-peer who lyf]
=/ ship-state (~(got by peers.ames-state) who)
?> ?=([%known *] ship-state)
~| [%fine %life-mismatch who lyf]
?> =(lyf life.ship-state)
public-key.ship-state
::
++ veri
|= [who=ship lyf=life sig=@ dat=@]
^- ?
=/ =^pass (pass who lyf)
(safe:as:(com:nu:crub:crypto pass) sig dat)
::
++ meri
|= [who=ship lyf=life pax=path sig=@ dat=$@(~ (cask))]
(veri who lyf sig (mess who lyf pax dat))
--
2022-02-01 14:42:13 +03:00
:: TODO: should not crash,
:: improve routing?
++ get-lane
|= =ship
^- lane:ames
2022-02-01 14:42:13 +03:00
=/ =peer-state
(got-peer-state ship)
lane:(need route.peer-state)
::
++ send-request
|= [=ship =path num=@ud]
^+ event-core
=/ =lane:ames (get-lane ship)
=/ =hoot (encode-request ship path 1)
%- emit
[unix-duct.ames-state %give %send lane `@ux`hoot]
::
++ process-response
|= [[from=ship =life] =path sig=@ data=$@(~ (cask))]
^+ event-core
?> (meri:keys from life path sig data)
~& got-response/path
=. event-core
%- emil
%+ turn ~(tap in (~(get ju want.state) path))
(late [%give %tune path ?@(data data `data)])
=. want.state (~(del by want.state) path)
=. part.state (~(del by part.state) path)
event-core
::
++ handle-response
|= [[from=ship =life =lane:ames] =peep =rawr]
^+ event-core
2022-02-01 14:42:13 +03:00
?: =(0 siz.rawr)
?> =(~ dat.rawr)
2022-02-02 05:48:04 +03:00
(process-response [from life] path.peep sig.rawr ~)
?. (~(has by part.state) path.peep)
:: we did not initiate this request, or it's been cancelled
::
!!
=/ partial=partial-fine
(~(got by part.state) path.peep)
=. partial
?: (~(has by fragments.partial) num.peep)
partial
=, partial
:+ ~| [%fine %response-size-changed have=num-fragments new=siz.rawr]
?> |(=(0 num-fragments) =(num-fragments siz.rawr))
2022-02-01 14:42:13 +03:00
siz.rawr
+(num-received)
:: ?> (veri:keys from life path.peep num.peep dat.rawr)
(~(put by fragments) num.peep [wid dat]:rawr)
::
?: =(num-fragments num-received):partial
:: we have all the parts now, construct the full response
::
=| =roar
(process-response [from life] path.peep [sig dat]:roar)
:: otherwise, store the part, and send out the next request
::
=. part.state (~(put by part.state) path.peep partial)
=/ next-num=@ud
=/ next=@ud +(num.peep)
:: we should receive responses in order, but in case we don't...
::
|-
?. (~(has by fragments.partial) next) next
$(next +((mod next num-fragments.partial)))
::
=/ =hoot (encode-request from path.peep next-num)
::TODO ask amsden, should we shotgun? we can tweak this
:: for now (mvp) though, stay 1-to-1
::TODO update lane in ames state
::TODO is reusing the lane fine?
%- emit
[unix-duct.ames-state %give %send lane `@ux`hoot]
--
--
2019-06-01 06:32:13 +03:00
:: +make-message-pump: constructor for |message-pump
::
++ make-message-pump
|= [state=message-pump-state =channel]
2019-12-11 21:55:16 +03:00
=* veb veb.bug.channel
=| gifts=(list message-pump-gift)
2019-05-29 05:56:05 +03:00
::
|%
2019-05-29 05:56:05 +03:00
++ message-pump .
++ give |=(gift=message-pump-gift message-pump(gifts [gift gifts]))
2019-06-19 02:33:53 +03:00
++ packet-pump (make-packet-pump packet-pump-state.state channel)
2019-12-11 21:55:16 +03:00
++ trace
|= [verb=? print=(trap tape)]
^+ same
(^trace verb her.channel ships.bug.channel print)
2019-05-29 05:56:05 +03:00
:: +work: handle a $message-pump-task
::
++ work
|= task=message-pump-task
^+ [gifts state]
::
2019-06-19 02:33:53 +03:00
=~ (dispatch-task task)
2019-05-29 05:56:05 +03:00
feed-packets
2019-06-20 10:21:37 +03:00
(run-packet-pump %halt ~)
assert
[(flop gifts) state]
2019-05-29 05:56:05 +03:00
==
2019-06-19 02:33:53 +03:00
:: +dispatch-task: perform task-specific processing
::
++ dispatch-task
|= task=message-pump-task
^+ message-pump
::
?- -.task
2019-07-28 10:50:32 +03:00
%memo (on-memo message-blob.task)
%wake (run-packet-pump %wake current.state)
2019-06-20 10:21:37 +03:00
%hear
2019-06-19 02:33:53 +03:00
?- -.ack-meat.task
2019-06-20 10:21:37 +03:00
%& (on-hear [message-num fragment-num=p.ack-meat]:task)
2019-12-02 23:20:57 +03:00
%| (on-done [message-num ?:(ok.p.ack-meat [%ok ~] [%nack ~])]:task)
==
%near (on-done [message-num %naxplanation error]:naxplanation.task)
==
2019-06-25 19:01:05 +03:00
:: +on-memo: handle request to send a message
2019-05-29 05:56:05 +03:00
::
2019-06-25 19:01:05 +03:00
++ on-memo
2019-07-28 10:50:32 +03:00
|= =message-blob
2019-05-29 05:56:05 +03:00
^+ message-pump
::
2019-07-28 10:50:32 +03:00
=. unsent-messages.state (~(put to unsent-messages.state) message-blob)
2019-05-29 05:56:05 +03:00
message-pump
2019-06-20 10:21:37 +03:00
:: +on-hear: handle packet acknowledgment
::
2019-06-20 10:21:37 +03:00
++ on-hear
|= [=message-num =fragment-num]
^+ message-pump
:: pass to |packet-pump unless duplicate or future ack
::
?. (is-message-num-in-range message-num)
2019-11-15 03:10:48 +03:00
%- (trace snd.veb |.("hear pump out of range"))
message-pump
2019-06-20 10:21:37 +03:00
(run-packet-pump %hear message-num fragment-num)
:: +on-done: handle message acknowledgment
2019-05-29 05:56:05 +03:00
::
2019-12-02 23:20:57 +03:00
:: A nack-trace message counts as a valid message nack on the
:: original failed message.
::
:: This prevents us from having to wait for a message nack packet,
:: which would mean we couldn't immediately ack the nack-trace
:: message, which would in turn violate the semantics of backward
:: flows.
::
2019-06-20 10:21:37 +03:00
++ on-done
2019-12-02 23:20:57 +03:00
|= [=message-num =ack]
2019-05-29 05:56:05 +03:00
^+ message-pump
2019-06-21 04:36:16 +03:00
:: unsent messages from the future should never get acked
::
?> (lth message-num next.state)
:: ignore duplicate message acks
::
?: (lth message-num current.state)
2020-12-12 03:45:13 +03:00
%- %+ trace snd.veb
|.("duplicate done {<current=current.state message-num=message-num>}")
2019-06-21 04:36:16 +03:00
message-pump
:: ignore duplicate and future acks
2019-05-29 05:56:05 +03:00
::
?. (is-message-num-in-range message-num)
2019-05-29 05:56:05 +03:00
message-pump
:: clear and print .unsent-fragments if nonempty
::
=? unsent-fragments.state
2019-06-09 09:31:42 +03:00
&(=(current next) ?=(^ unsent-fragments)):state
::
~> %slog.0^leaf/"ames: early message ack {<her.channel>}"
2019-05-29 05:56:05 +03:00
~
:: clear all packets from this message from the packet pump
::
2019-12-02 23:20:57 +03:00
=. message-pump (run-packet-pump %done message-num lag=*@dr)
:: enqueue this ack to be sent back to local client vane
2019-05-29 05:56:05 +03:00
::
2019-12-02 23:20:57 +03:00
:: Don't clobber a naxplanation with just a nack packet.
::
=? queued-message-acks.state
=/ old (~(get by queued-message-acks.state) message-num)
!?=([~ %naxplanation *] old)
(~(put by queued-message-acks.state) message-num ack)
:: emit local acks from .queued-message-acks until incomplete
2019-05-29 05:56:05 +03:00
::
|- ^+ message-pump
:: if .current hasn't been fully acked, we're done
2019-05-29 05:56:05 +03:00
::
2019-12-02 23:20:57 +03:00
?~ cur=(~(get by queued-message-acks.state) current.state)
2019-05-29 05:56:05 +03:00
message-pump
:: .current is complete; pop, emit local ack, and try next message
2019-05-29 05:56:05 +03:00
::
=. queued-message-acks.state
(~(del by queued-message-acks.state) current.state)
2020-12-12 03:43:13 +03:00
:: clear all packets from this message from the packet pump
::
:: Note we did this when the original packet came in, a few lines
:: above. It's not clear why, but it doesn't always clear the
:: packets when it's not the current message. As a workaround,
:: we clear the packets again when we catch up to this packet.
::
:: This is slightly inefficient because we run this twice for
:: each packet and it may emit a few unnecessary packets, but
:: but it's not incorrect. pump-metrics are updated only once,
:: at the time when we actually delete the packet.
::
=. message-pump (run-packet-pump %done current.state lag=*@dr)
2019-12-02 23:20:57 +03:00
:: give %done to vane if we're ready
::
?- -.u.cur
%ok
=. message-pump (give %done current.state ~)
$(current.state +(current.state))
2019-05-29 05:56:05 +03:00
::
2019-12-02 23:20:57 +03:00
%nack
message-pump
2019-05-29 05:56:05 +03:00
::
2019-12-02 23:20:57 +03:00
%naxplanation
=. message-pump (give %done current.state `error.u.cur)
$(current.state +(current.state))
==
:: +is-message-num-in-range: %.y unless duplicate or future ack
::
++ is-message-num-in-range
|= =message-num
^- ?
::
?: (gte message-num next.state)
%.n
?: (lth message-num current.state)
%.n
!(~(has by queued-message-acks.state) message-num)
2019-06-01 06:32:13 +03:00
:: +feed-packets: give packets to |packet-pump until full
2019-05-29 05:56:05 +03:00
::
++ feed-packets
:: if nothing to send, no-op
::
?: &(=(~ unsent-messages) =(~ unsent-fragments)):state
2019-05-29 05:56:05 +03:00
message-pump
:: we have unsent fragments of the current message; feed them
::
?. =(~ unsent-fragments.state)
=/ res (feed:packet-pump unsent-fragments.state)
=+ [unsent packet-pump-gifts packet-pump-state]=res
2019-05-29 05:56:05 +03:00
::
=. unsent-fragments.state unsent
=. packet-pump-state.state packet-pump-state
2019-05-29 05:56:05 +03:00
::
=. message-pump (process-packet-pump-gifts packet-pump-gifts)
:: if it sent all of them, feed it more; otherwise, we're done
::
?~ unsent
feed-packets
message-pump
:: .unsent-messages is nonempty; pop a message off and feed it
::
2019-07-28 10:50:32 +03:00
=^ =message-blob unsent-messages.state ~(get to unsent-messages.state)
2019-06-01 06:32:13 +03:00
:: break .message into .chunks and set as .unsent-fragments
2019-05-29 05:56:05 +03:00
::
2019-07-28 10:50:32 +03:00
=. unsent-fragments.state (split-message next.state message-blob)
2019-06-01 06:32:13 +03:00
:: try to feed packets from the next message
2019-05-29 05:56:05 +03:00
::
=. next.state +(next.state)
2019-05-29 05:56:05 +03:00
feed-packets
2019-06-01 06:32:13 +03:00
:: +run-packet-pump: call +work:packet-pump and process results
2019-05-29 05:56:05 +03:00
::
++ run-packet-pump
|= =packet-pump-task
^+ message-pump
::
=^ packet-pump-gifts packet-pump-state.state
2019-05-29 05:56:05 +03:00
(work:packet-pump packet-pump-task)
::
(process-packet-pump-gifts packet-pump-gifts)
2019-06-01 06:32:13 +03:00
:: +process-packet-pump-gifts: pass |packet-pump effects up the chain
2019-05-29 05:56:05 +03:00
::
++ process-packet-pump-gifts
|= packet-pump-gifts=(list packet-pump-gift)
^+ message-pump
::
?~ packet-pump-gifts
message-pump
=. message-pump (give i.packet-pump-gifts)
::
$(packet-pump-gifts t.packet-pump-gifts)
:: +assert: sanity checks to isolate error cases
::
++ assert
^+ message-pump
=/ top-live
(pry:packet-queue:*make-packet-pump live.packet-pump-state.state)
?. |(?=(~ top-live) (lte current.state message-num.key.u.top-live))
~| [%strange-current current=current.state key.u.top-live]
!!
message-pump
2019-05-29 05:56:05 +03:00
--
2019-06-01 06:32:13 +03:00
:: +make-packet-pump: construct |packet-pump core
2019-05-29 05:56:05 +03:00
::
++ make-packet-pump
2019-06-01 05:03:09 +03:00
|= [state=packet-pump-state =channel]
2019-12-11 21:55:16 +03:00
=* veb veb.bug.channel
2019-05-29 05:56:05 +03:00
=| gifts=(list packet-pump-gift)
|%
++ packet-pump .
2019-06-01 03:50:22 +03:00
++ give |=(packet-pump-gift packet-pump(gifts [+< gifts]))
2019-12-11 21:55:16 +03:00
++ trace
|= [verb=? print=(trap tape)]
^+ same
(^trace verb her.channel ships.bug.channel print)
2019-06-01 06:32:13 +03:00
:: +packet-queue: type for all sent fragments, ordered by sequence number
::
++ packet-queue
%- (ordered-map live-packet-key live-packet-val)
lte-packets
:: +gauge: inflate a |pump-gauge to track congestion control
::
2019-12-11 21:55:16 +03:00
++ gauge (make-pump-gauge now.channel metrics.state [her bug]:channel)
:: +work: handle $packet-pump-task request
::
2019-05-29 05:56:05 +03:00
++ work
|= task=packet-pump-task
2019-06-01 05:03:09 +03:00
^+ [gifts state]
2019-05-29 05:56:05 +03:00
::
2019-06-01 05:03:09 +03:00
=- [(flop gifts) state]
::
?- -.task
2019-06-20 10:21:37 +03:00
%hear (on-hear [message-num fragment-num]:task)
%done (on-done message-num.task)
%wake (on-wake current.task)
2019-06-20 10:21:37 +03:00
%halt set-wake
==
:: +on-wake: handle packet timeout
::
++ on-wake
|= current=message-num
^+ packet-pump
:: assert temporal coherence
2019-06-01 03:50:22 +03:00
::
?< =(~ next-wake.state)
?> (gte now.channel (need next-wake.state))
=. next-wake.state ~
:: tell congestion control a packet timed out
::
2019-10-02 10:46:34 +03:00
=. metrics.state on-timeout:gauge
:: re-send first packet and update its state in-place
::
2019-11-15 03:10:48 +03:00
=- =* res -
=. live.state live.res
=? packet-pump ?=(^ static-fragment)
%- %+ trace snd.veb
=/ nums [message-num fragment-num]:u.static-fragment.res
|.("dead {<nums^show:gauge>}")
(give %send u.static-fragment.res)
packet-pump
2019-06-01 03:50:22 +03:00
::
=| acc=(unit static-fragment)
^+ [static-fragment=acc live=live.state]
2019-06-01 03:50:22 +03:00
::
%^ (dip:packet-queue _acc) live.state acc
|= $: acc=_acc
2019-06-01 03:50:22 +03:00
key=live-packet-key
val=live-packet-val
==
^- [new-val=(unit live-packet-val) stop=? _acc]
:: if already acked later message, don't resend
::
?: (lth message-num.key current)
%- %- slog :_ ~
leaf+"ames: strange wake queue, expected {<current>}, got {<key>}"
[~ stop=%.n ~]
:: packet has expired; update it in-place, stop, and produce it
2019-06-01 03:50:22 +03:00
::
=. last-sent.val now.channel
=. tries.val +(tries.val)
2019-06-01 03:50:22 +03:00
::
[`val stop=%.y `(to-static-fragment key val)]
:: +feed: try to send a list of packets, returning unsent and effects
::
++ feed
2019-05-29 05:56:05 +03:00
|= fragments=(list static-fragment)
2019-06-01 05:03:09 +03:00
^+ [fragments gifts state]
:: return unsent back to caller and reverse effects to finalize
::
=- [unsent (flop gifts) state]
::
^+ [unsent=fragments packet-pump]
2019-06-01 06:05:21 +03:00
:: bite off as many fragments as we can send
::
=/ num-slots num-slots:gauge
=/ sent (scag num-slots fragments)
=/ unsent (slag num-slots fragments)
::
:- unsent
^+ packet-pump
:: if nothing to send, we're done
::
?~ sent packet-pump
:: convert $static-fragment's into +ordered-set [key val] pairs
::
=/ send-list
2019-06-01 06:05:21 +03:00
%+ turn sent
|= static-fragment
^- [key=live-packet-key val=live-packet-val]
2019-06-01 06:05:21 +03:00
::
:- [message-num fragment-num]
:- [sent-date=now.channel tries=1 skips=0]
2019-06-01 06:05:21 +03:00
[num-fragments fragment]
:: update .live and .metrics
::
=. live.state (gas:packet-queue live.state send-list)
=. metrics.state (on-sent:gauge (lent send-list))
:: TMI
2019-06-01 06:05:21 +03:00
::
=> .(sent `(list static-fragment)`sent)
:: emit a $packet-pump-gift for each packet to send
::
2019-10-05 06:21:29 +03:00
%+ roll sent
|= [packet=static-fragment core=_packet-pump]
(give:core %send packet)
:: +fast-resend-after-ack: resend timed out packets
::
:: After we finally receive an ack, we want to resend all the live
:: packets that have been building up.
::
++ fast-resend-after-ack
|= [=message-num =fragment-num]
^+ packet-pump
=; res=[resends=(list static-fragment) live=_live.state]
=. live.state live.res
%+ reel resends.res
|= [packet=static-fragment core=_packet-pump]
(give:core %send packet)
::
=/ acc
resends=*(list static-fragment)
::
%^ (dip:packet-queue _acc) live.state acc
|= $: acc=_acc
key=live-packet-key
val=live-packet-val
==
^- [new-val=(unit live-packet-val) stop=? _acc]
?: (lte-packets key [message-num fragment-num])
[new-val=`val stop=%.n acc]
::
?: (gth (next-expiry:gauge -.val) now.channel)
[new-val=`val stop=%.y acc]
::
=. last-sent.val now.channel
=. resends.acc [(to-static-fragment key val) resends.acc]
[new-val=`val stop=%.n acc]
2019-06-20 10:21:37 +03:00
:: +on-hear: handle ack on a live packet
::
:: If the packet was in our queue, delete it and update our
2019-12-21 09:56:51 +03:00
:: metrics, possibly re-sending skipped packets. Otherwise, no-op.
::
2019-06-20 10:21:37 +03:00
++ on-hear
|= [=message-num =fragment-num]
^+ packet-pump
::
=- :: if no sent packet matches the ack, don't apply mutations or effects
::
?. found.-
2019-11-15 03:10:48 +03:00
%- (trace snd.veb |.("miss {<show:gauge>}"))
packet-pump
::
=. metrics.state metrics.-
2019-06-01 05:03:09 +03:00
=. live.state live.-
2019-12-03 02:46:40 +03:00
%- ?. ?| =(0 fragment-num)
=(0 (mod counter.metrics.state 20))
==
same
2020-12-12 03:45:13 +03:00
(trace snd.veb |.("send: {<[fragment=fragment-num show:gauge]>}"))
2019-10-05 06:21:29 +03:00
:: .resends is backward, so fold backward and emit
::
=. packet-pump
%+ reel resends.-
|= [packet=static-fragment core=_packet-pump]
(give:core %send packet)
(fast-resend-after-ack message-num fragment-num)
2019-10-05 06:21:29 +03:00
::
=/ acc
:* found=`?`%.n
resends=*(list static-fragment)
metrics=metrics.state
==
::
2019-10-05 06:21:29 +03:00
^+ [acc live=live.state]
::
%^ (dip:packet-queue _acc) live.state acc
|= $: acc=_acc
key=live-packet-key
val=live-packet-val
==
^- [new-val=(unit live-packet-val) stop=? _acc]
::
2019-12-11 21:55:16 +03:00
=/ gauge (make-pump-gauge now.channel metrics.acc [her bug]:channel)
:: is this the acked packet?
::
?: =(key [message-num fragment-num])
:: delete acked packet, update metrics, and stop traversal
::
2019-10-05 06:21:29 +03:00
=. found.acc %.y
=. metrics.acc (on-ack:gauge -.val)
[new-val=~ stop=%.y acc]
:: is this a duplicate ack?
::
?. (lte-packets key [message-num fragment-num])
:: stop, nothing more to do
::
[new-val=`val stop=%.y acc]
2019-10-05 06:21:29 +03:00
:: ack was on later packet; mark skipped, tell gauge, and continue
::
=. skips.val +(skips.val)
=^ resend metrics.acc (on-skipped-packet:gauge -.val)
?. resend
[new-val=`val stop=%.n acc]
::
2019-10-05 06:21:29 +03:00
=. last-sent.val now.channel
=. tries.val +(tries.val)
2019-10-05 06:21:29 +03:00
=. resends.acc [(to-static-fragment key val) resends.acc]
[new-val=`val stop=%.n acc]
2019-06-20 10:21:37 +03:00
:: +on-done: apply ack to all packets from .message-num
::
2019-06-20 10:21:37 +03:00
++ on-done
2019-05-29 05:56:05 +03:00
|= =message-num
^+ packet-pump
::
=- =. metrics.state metrics.-
=. live.state live.-
::
2020-12-12 03:45:13 +03:00
%- (trace snd.veb |.("done {<message-num=message-num^show:gauge>}"))
(fast-resend-after-ack message-num `fragment-num`0)
::
2019-10-05 06:21:29 +03:00
^+ [metrics=metrics.state live=live.state]
::
%^ (dip:packet-queue pump-metrics) live.state acc=metrics.state
|= $: metrics=pump-metrics
key=live-packet-key
val=live-packet-val
==
^- [new-val=(unit live-packet-val) stop=? pump-metrics]
::
2019-12-11 21:55:16 +03:00
=/ gauge (make-pump-gauge now.channel metrics [her bug]:channel)
:: if we get an out-of-order ack for a message, skip until it
::
?: (lth message-num.key message-num)
[new-val=`val stop=%.n metrics]
:: if packet was from acked message, delete it and continue
::
?: =(message-num.key message-num)
[new-val=~ stop=%.n metrics=(on-ack:gauge -.val)]
:: we've gone past the acked message; we're done
::
[new-val=`val stop=%.y metrics]
:: +set-wake: set, unset, or reset timer, emitting moves
::
++ set-wake
2019-05-29 05:56:05 +03:00
^+ packet-pump
:: if nonempty .live, pry at head to get next wake time
::
=/ new-wake=(unit @da)
?~ head=(pry:packet-queue live.state)
~
`(next-expiry:gauge -.val.u.head)
:: no-op if no change
::
?: =(new-wake next-wake.state) packet-pump
:: unset old timer if non-null
::
=? packet-pump !=(~ next-wake.state)
=/ old (need next-wake.state)
=. next-wake.state ~
(give %rest old)
:: set new timer if non-null
::
=? packet-pump ?=(^ new-wake)
=. next-wake.state new-wake
(give %wait u.new-wake)
::
packet-pump
--
2019-10-05 06:21:29 +03:00
:: +to-static-fragment: convenience function for |packet-pump
::
2019-10-05 06:21:29 +03:00
++ to-static-fragment
|= [live-packet-key live-packet-val]
^- static-fragment
[message-num num-fragments fragment-num fragment]
2019-06-01 03:50:22 +03:00
:: +make-pump-gauge: construct |pump-gauge congestion control core
2019-06-01 06:05:21 +03:00
::
++ make-pump-gauge
2019-12-11 21:55:16 +03:00
|= [now=@da pump-metrics =ship =bug]
=* veb veb.bug
2019-12-03 02:46:40 +03:00
=* metrics +<+<
|%
2019-12-11 21:55:16 +03:00
++ trace
|= [verb=? print=(trap tape)]
^+ same
(^trace verb ship ships.bug print)
2019-06-01 06:05:21 +03:00
:: +next-expiry: when should a newly sent fresh packet time out?
2019-06-01 03:50:22 +03:00
::
:: Use rtt + 4*sigma, where sigma is the mean deviation of rtt.
:: This should make it unlikely that a packet would time out from a
:: delay, as opposed to an actual packet loss.
2019-06-01 06:05:21 +03:00
::
++ next-expiry
|= packet-state
2019-06-01 06:05:21 +03:00
^- @da
(add last-sent rto)
2019-06-01 06:05:21 +03:00
:: +num-slots: how many packets can we send right now?
::
++ num-slots
^- @ud
(sub-safe cwnd num-live)
:: +on-sent: adjust metrics based on sending .num-sent fresh packets
2019-06-01 03:50:22 +03:00
::
++ on-sent
|= num-sent=@ud
^- pump-metrics
::
=. num-live (add num-live num-sent)
2019-06-01 05:03:09 +03:00
metrics
2019-06-01 03:50:22 +03:00
:: +on-ack: adjust metrics based on a packet getting acknowledged
::
++ on-ack
|= =packet-state
2019-06-01 05:03:09 +03:00
^- pump-metrics
::
=. counter +(counter)
=. num-live (dec num-live)
:: if below congestion threshold, add 1; else, add avg. 1 / cwnd
2019-08-29 03:28:31 +03:00
::
=. cwnd
?: in-slow-start
+(cwnd)
(add cwnd !=(0 (mod (mug now) cwnd)))
:: if this was a re-send, don't adjust rtt or downstream state
::
?: (gth tries.packet-state 1)
metrics
:: rtt-datum: new rtt measurement based on this packet roundtrip
::
=/ rtt-datum=@dr (sub-safe now last-sent.packet-state)
:: rtt-error: difference between this rtt measurement and expected
::
=/ rtt-error=@dr
?: (gte rtt-datum rtt)
(sub rtt-datum rtt)
(sub rtt rtt-datum)
:: exponential weighting ratio for .rtt and .rttvar
::
2019-11-28 03:43:09 +03:00
%- %+ trace ges.veb
|.("ack update {<show rtt-datum=rtt-datum rtt-error=rtt-error>}")
=. rtt (div (add rtt-datum (mul rtt 7)) 8)
=. rttvar (div (add rtt-error (mul rttvar 7)) 8)
=. rto (clamp-rto (add rtt (mul 4 rttvar)))
::
metrics
2019-10-05 06:21:29 +03:00
:: +on-skipped-packet: handle misordered ack
2019-06-01 03:50:22 +03:00
::
++ on-skipped-packet
|= packet-state
2019-10-05 06:21:29 +03:00
^- [resend=? pump-metrics]
2019-06-01 06:05:21 +03:00
::
=/ resend=? &((lte tries 1) |(in-recovery (gte skips 3)))
2019-10-05 06:21:29 +03:00
:- resend
::
=? cwnd !in-recovery (max 2 (div cwnd 2))
2019-11-15 03:10:48 +03:00
%- %+ trace snd.veb
|.("skip {<[resend=resend in-recovery=in-recovery show]>}")
metrics
:: +on-timeout: (re)enter slow-start mode on packet loss
2019-06-01 06:05:21 +03:00
::
++ on-timeout
2019-06-01 05:03:09 +03:00
^- pump-metrics
::
2019-12-03 02:46:40 +03:00
%- (trace ges.veb |.("timeout update {<show>}"))
=: ssthresh (max 1 (div cwnd 2))
cwnd 1
rto (clamp-rto (mul rto 2))
==
2019-06-04 15:38:08 +03:00
metrics
:: +clamp-rto: apply min and max to an .rto value
::
++ clamp-rto
|= rto=@dr
^+ rto
(min ~m2 (max ^~((div ~s1 5)) rto))
2019-10-05 06:21:29 +03:00
:: +in-slow-start: %.y iff we're in "slow-start" mode
::
++ in-slow-start
^- ?
(lth cwnd ssthresh)
2019-10-05 06:21:29 +03:00
:: +in-recovery: %.y iff we're recovering from a skipped packet
::
:: We finish recovering when .num-live finally dips back down to
:: .cwnd.
::
++ in-recovery
^- ?
(gth num-live cwnd)
:: +sub-safe: subtract with underflow protection
::
++ sub-safe
|= [a=@ b=@]
^- @
?:((lte a b) 0 (sub a b))
2019-10-05 06:21:29 +03:00
:: +show: produce a printable version of .metrics
::
++ show
=/ ms (div ~s1 1.000)
::
:* rto=(div rto ms)
rtt=(div rtt ms)
rttvar=(div rttvar ms)
ssthresh=ssthresh
cwnd=cwnd
num-live=num-live
counter=counter
==
--
:: +make-message-sink: construct |message-sink message receiver core
::
++ make-message-sink
|= [state=message-sink-state =channel]
2019-12-11 21:55:16 +03:00
=* veb veb.bug.channel
=| gifts=(list message-sink-gift)
|%
++ message-sink .
++ give |=(message-sink-gift message-sink(gifts [+< gifts]))
2019-12-11 21:55:16 +03:00
++ trace
|= [verb=? print=(trap tape)]
^+ same
(^trace verb her.channel ships.bug.channel print)
:: +work: handle a $message-sink-task
::
++ work
|= task=message-sink-task
2019-06-09 20:32:15 +03:00
^+ [gifts state]
::
2019-06-09 20:32:15 +03:00
=- [(flop gifts) state]
::
?- -.task
2019-06-20 10:21:37 +03:00
%done (on-done ok.task)
%drop (on-drop message-num.task)
2019-08-06 02:05:40 +03:00
%hear (on-hear [lane shut-packet ok]:task)
==
:: +on-hear: receive message fragment, possibly completing message
::
++ on-hear
2019-08-06 02:05:40 +03:00
|= [=lane =shut-packet ok=?]
^+ message-sink
2019-06-09 20:32:15 +03:00
:: we know this is a fragment, not an ack; expose into namespace
::
2019-06-09 20:32:15 +03:00
?> ?=(%& -.meat.shut-packet)
=+ [num-fragments fragment-num fragment]=+.meat.shut-packet
:: seq: message sequence number, for convenience
::
=/ seq message-num.shut-packet
:: ignore messages from far future; limit to 10 in progress
::
?: (gte seq (add 10 last-acked.state))
2020-12-12 03:45:13 +03:00
%- %+ trace odd.veb
|.("future %hear {<seq=seq^last-acked=last-acked.state>}")
message-sink
2019-06-09 20:32:15 +03:00
::
=/ is-last-fragment=? =(+(fragment-num) num-fragments)
:: always ack a dupe!
::
?: (lte seq last-acked.state)
?. is-last-fragment
:: single packet ack
::
2020-12-12 03:45:13 +03:00
%- %+ trace rcv.veb
|.("send dupe ack {<seq=seq^fragment-num=fragment-num>}")
2019-06-20 10:21:37 +03:00
(give %send seq %& fragment-num)
2019-06-19 03:38:25 +03:00
:: whole message (n)ack
2019-06-09 20:32:15 +03:00
::
2019-08-21 00:44:44 +03:00
=/ ok=? !(~(has in nax.state) seq)
2020-12-12 03:45:13 +03:00
%- (trace rcv.veb |.("send dupe message ack {<seq=seq>} ok={<ok>}"))
2019-06-20 10:21:37 +03:00
(give %send seq %| ok lag=`@dr`0)
2019-06-09 20:32:15 +03:00
:: last-acked<seq<=last-heard; heard message, unprocessed
::
:: Only true if we've heard some packets we haven't acked, which
:: doesn't happen for boons.
::
2019-06-09 20:32:15 +03:00
?: (lte seq last-heard.state)
?: is-last-fragment
:: drop last packet since we don't know whether to ack or nack
::
2019-11-28 03:58:26 +03:00
%- %+ trace rcv.veb
|. ^- tape
=/ data
2020-12-12 03:45:13 +03:00
:* her.channel seq=seq
fragment-num=fragment-num num-fragments=num-fragments
2019-11-28 03:58:26 +03:00
la=last-acked.state lh=last-heard.state
==
"hear last in-progress {<data>}"
message-sink
2019-06-09 20:32:15 +03:00
:: ack all other packets
::
2020-12-12 03:45:13 +03:00
%- %+ trace rcv.veb |.
=/ data
[seq=seq fragment-num=fragment-num num-fragments=num-fragments]
"send ack-1 {<data>}"
2019-06-20 10:21:37 +03:00
(give %send seq %& fragment-num)
2019-06-09 20:32:15 +03:00
:: last-heard<seq<10+last-heard; this is a packet in a live message
::
=/ =partial-rcv-message
:: create default if first fragment
::
?~ existing=(~(get by live-messages.state) seq)
[num-fragments num-received=0 fragments=~]
:: we have an existing partial message; check parameters match
::
?> (gth num-fragments.u.existing fragment-num)
?> =(num-fragments.u.existing num-fragments)
::
u.existing
::
2019-08-01 06:14:28 +03:00
=/ already-heard-fragment=?
(~(has by fragments.partial-rcv-message) fragment-num)
2019-06-09 20:32:15 +03:00
:: ack dupes except for the last fragment, in which case drop
::
2019-08-01 06:14:28 +03:00
?: already-heard-fragment
2019-06-09 20:32:15 +03:00
?: is-last-fragment
2019-12-18 02:40:51 +03:00
%- %+ trace rcv.veb |.
2020-12-12 03:45:13 +03:00
=/ data
[her.channel seq=seq lh=last-heard.state la=last-acked.state]
2019-12-18 02:40:51 +03:00
"hear last dupe {<data>}"
message-sink
2020-12-12 03:45:13 +03:00
%- %+ trace rcv.veb
|.("send dupe ack {<her.channel^seq=seq^fragment-num=fragment-num>}")
2019-06-20 10:21:37 +03:00
(give %send seq %& fragment-num)
2019-06-09 20:32:15 +03:00
:: new fragment; store in state and check if message is done
::
=. num-received.partial-rcv-message
+(num-received.partial-rcv-message)
::
=. fragments.partial-rcv-message
(~(put by fragments.partial-rcv-message) fragment-num fragment)
::
=. live-messages.state
(~(put by live-messages.state) seq partial-rcv-message)
:: ack any packet other than the last one, and continue either way
::
=? message-sink !is-last-fragment
2020-12-12 03:45:13 +03:00
%- %+ trace rcv.veb |.
=/ data
[seq=seq fragment-num=fragment-num num-fragments=num-fragments]
"send ack-2 {<data>}"
2019-06-20 10:21:37 +03:00
(give %send seq %& fragment-num)
2019-06-09 20:32:15 +03:00
:: enqueue all completed messages starting at +(last-heard.state)
::
|- ^+ message-sink
2019-06-09 20:32:15 +03:00
:: if this is not the next message to ack, we're done
::
?. =(seq +(last-heard.state))
message-sink
2019-06-09 20:32:15 +03:00
:: if we haven't heard anything from this message, we're done
::
?~ live=(~(get by live-messages.state) seq)
message-sink
2019-06-09 20:32:15 +03:00
:: if the message isn't done yet, we're done
::
?. =(num-received num-fragments):u.live
message-sink
2019-06-09 20:32:15 +03:00
:: we have whole message; update state, assemble, and send to vane
::
=. last-heard.state +(last-heard.state)
=. live-messages.state (~(del by live-messages.state) seq)
::
2019-12-03 02:46:40 +03:00
%- %+ trace msg.veb
2020-12-12 03:45:13 +03:00
|.("hear {<her.channel>} {<seq=seq>} {<num-fragments.u.live>}kb")
2019-07-28 10:50:32 +03:00
=/ message=* (assemble-fragments [num-fragments fragments]:u.live)
=. message-sink (enqueue-to-vane seq message)
2019-06-09 20:32:15 +03:00
::
$(seq +(seq))
2019-06-19 03:38:25 +03:00
:: +enqueue-to-vane: enqueue message to be sent to local vane
2019-06-09 20:32:15 +03:00
::
++ enqueue-to-vane
2019-07-28 10:50:32 +03:00
|= [seq=message-num message=*]
^+ message-sink
2019-06-09 20:32:15 +03:00
::
=/ empty=? =(~ pending-vane-ack.state)
=. pending-vane-ack.state (~(put to pending-vane-ack.state) seq message)
?. empty
message-sink
2019-06-20 10:21:37 +03:00
(give %memo seq message)
2019-06-11 03:31:50 +03:00
:: +on-done: handle confirmation of message processing from vane
::
++ on-done
2019-06-18 21:21:12 +03:00
|= ok=?
^+ message-sink
::
2019-06-11 03:31:50 +03:00
=^ pending pending-vane-ack.state ~(get to pending-vane-ack.state)
2019-06-19 03:38:25 +03:00
=/ =message-num message-num.p.pending
::
=. last-acked.state +(last-acked.state)
=? nax.state !ok (~(put in nax.state) message-num)
2019-06-11 03:31:50 +03:00
::
=. message-sink (give %send message-num %| ok lag=`@dr`0)
=/ next ~(top to pending-vane-ack.state)
?~ next
message-sink
(give %memo u.next)
2019-06-20 10:21:37 +03:00
:: +on-drop: drop .message-num from our .nax state
2019-06-19 03:38:25 +03:00
::
2019-06-20 10:21:37 +03:00
++ on-drop
2019-06-19 03:38:25 +03:00
|= =message-num
^+ message-sink
2019-06-19 03:38:25 +03:00
::
=. nax.state (~(del in nax.state) message-num)
::
message-sink
--
2019-05-25 05:03:33 +03:00
--