@@ -17,6 +17,87 @@ import { PeerGroupAgent } from '../peer/PeerGroupAgent';
1717import { RNGImpl } from 'crypto/random' ;
1818import { MultiMap } from 'util/multimap' ;
1919
20+ /*
21+
22+ *Introduction*
23+
24+ The TerminalOpsSyncAgent's purpose is to synchronize the state of a MutableObject by keeping track
25+ of its "terminal ops". A MutableObject represents the initial state of the object, which is then
26+ updated by creating MutationOp instances that point to it through their "target" field.
27+
28+ The agent will be instantiated to sync a particular MutableObject present in the local store, and
29+ will perform state reconcilaition with any connected peers that either advertise new states
30+ through gossiping or request MutationOps in response to its own state advertisements.
31+
32+ The TerminalOpsSyncAgent only deals with actual state sync. The gossiping of new states is done
33+ by other agents (typically the StateGossipAgent, which batches together the states of all the
34+ objects that a peer wants to sync with a particular PeerGroup). The local TerminalOpsSyncAgent and
35+ StateGossipAgent communicate trough the local broadcasting mechanism of the AgentPod they share.
36+
37+
38+ *State by terminal ops*
39+
40+ The TerminalOpsSyncAgent uses the "prevOps" field in the MutationOp object to discard all the ops
41+ that have any following ops -that is another op that points to them through its "prevOps" field-
42+ and use the set of remaining "terminal ops" as a way to represent the state of the MutableObject
43+ being synchronized. This set of terminal ops can be obtained easily and quickly from the store
44+ itself.
45+
46+ *State broadcasting*
47+
48+ After discovering that the state of the object has changed, the agent will broadcast a message to
49+ the local agent pod informing of the update. Any gossiping agents active in the pod will pick up
50+ the update and inform any connected peers. Conversely, if any gossip agent picks up any state update
51+ from a connected peer, it will also broadcast a message on the local agent pod. The
52+ TermninalOpsSyncAgent will check the received state and start synchronizing with such a peer if
53+ necessary.
54+
55+ *State sync*
56+
57+ After determining (via gossip results broadcaste on the local pod by a gossiping agent) that it
58+ needs to perform state sync, the TerminalOpsSyncAgent exchanges a series of messages with the
59+ TerminalOpsSyncAgent on the peer that has advertised a new state. Since the state is just the
60+ set of "terminal ops" on the other end, the agent will just ask for any of this "terminal ops"
61+ that are missing on the local store. Since an op can only be persisted to the store once all
62+ its prevOps have been already persisted, the agent may need to make several calls until it can
63+ reconcile the local state with the remote one, following the trail of prevOps until all the
64+ dependencies of the terminalOps have been fetched. There are 4 types of messages used in this
65+ task:
66+
67+ The SendStateMessage is sent in reply to a RequestStateMessage, and it will send the set of
68+ terminalOps in full (gossiping usually would send just a hash of the terminalOp set).
69+
70+ The SendOpsMessage is sent in reply to the RequestObjsMessage, and will send the literalized
71+ version of the objects and their dependencies.
72+
73+ *Security measures, optimizations*
74+
75+ When sending state (in the form of literalized objects) the agent may omit some dependencies
76+ of the objects being sent, expecting the receiving peer to already have them in its store
77+ (e.g., the identities and public keys that are referenced again and again by the ops being
78+ applied to the target object). And of course, there may be more prevOps that the other end
79+ discovers as a result of the received objects, and that it also needs to request.
80+
81+ There are two security measures in place to prevent object exfiltration:
82+
83+ Rule 1. Every requested object needs to be referenced (probably indirectly) from an op that has the
84+ object being synchronized as its target.
85+ Rule 2. Every an object A that is sent, and has a reference B that is optimized away from the sent
86+ message, must provide also a proof that the sender has B locally in its store.
87+
88+ The purpose of Rule 1 is preventing an adversary from requesting arbitrary objects that have no
89+ relation to the object being synchronized.
90+
91+ The purpose of Rule 2 is a bit more subtle: an adversary may construct a legitimate MutationOp that
92+ he then applies to the object being syncronized in such a way that the op references some object
93+ that he wants to steal from another peer (perhaps an object
94+ unrelated to the one being synchronized). So even if the attacker knows the hash of the object that
95+ he wants to steal, and is able to construct a MutationOp that will be accepted by the type of the
96+ mutations that the mutable object accepts, he will not be able to provide the proof of ownership
97+ that is required for sending incomplete operations.
98+
99+
100+ */
20101
21102enum TerminalOpsSyncAgentMessageType {
22103 RequestState = 'request-state' ,
0 commit comments