| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A node in focus showed this node's `/peers` entry for it and the links it reports:
what this node knows of it, never what it says of itself, though the page subscribes to
it. Now a subscribed node in focus shows its own figures first (`Node:`), as this
node's are (its `/admin/stats`), then this node's entry for it (`as this node sees it`)
and its links, each a branch. A link in focus shows each end's paths to the other as
that end has them: this node's, and a subscribed node's from its `/peers`.
The page reads them from the node every 2 s while it is in focus (`readFigures`,
`figuresOf`), with its narrow token, which may look at a node as any user with a
capability may; pages of other origins may now call `/admin/stats` and `/peers`
(`cors::open`), as they do a node's links and messages. Where they can't be read, the
box says why.
|
| |
|
|
|
|
|
|
|
|
| |
A subscribed node's messages come without being written out (their stream leaves the
text out: up to 8 KiB each, 500 at a time), so the figures box said only when, both
ends, type and size of one. Now the node answers for one message by its number,
`GET /admin/dashboard/messages/own/{seq}` (`{"text": …}`, `404` once it isn't kept),
which pages of other origins may call as they do its streams (`cors::open`), and the
page reads it when the message is shown, with its narrow token, and keeps it on the
message. Where it can't be read, the box says why.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The console said with whom a message went, not how: the message log was noted above
the identity layer (`CountingTransport`), where a message is to or from a node, and the
address it went to (the paths layer picks one, `Paths::send_first`) or came from is
gone. Only the paths layer's own PINGs and PONGs, logged under their addresses, had one.
Now the identity layer notes the node's messages (`MessageLog::set_addressed`), with
the address each went to (`IdentityTransport::send_proven` returns it) or came from;
`CountingTransport` leaves them to it, and still notes them where there is no identity
layer (tests). A console line ends with the transport (the address's scheme, in its
colour on the map, the address in the tooltip), and the figures box shows the address.
A subscribed node's messages carry theirs too, and the map runs every message along
its transport's line, not only the PINGs and PONGs.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Setting a node's log filter took `write` on `_continuum`, all of the protocol data:
whoever could change one node's logging could change the swarm's members, remakes and
users too. Now what a node holds alone has paths of its own for capabilities, under
`_continuum/local/<node ID>/`: `log` (`/admin/log`), `config` (`/admin/config`) and
`debug` (`/debug`, always `write`), `read` to look and `write` to act
(`api::access::needed`, which takes the node's ID). So `_continuum/local/<node ID>` is
everything on one node, `_continuum/local/*/log` the log filter on every node, and
holders of `_continuum` keep it all.
Nothing is stored there: the store refuses writes under `_continuum/local`, and the
rules refuse a change touching it, as for epoch metadata. The quarantine stays with
`_continuum`: its entries are everyone's writes, which a node-local capability
mustn't show.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A capability's path covered by prefix only, so what is the same at every key of a map
(each node's log filter, say, at `_continuum/local/<id>/log` from the next patch on)
took a capability per key. Now a `*` segment stands for any one key, as it already does
in signed paths and sealed places (`envelope`, `sealed`, which now share one `agree`):
* covering (`Capability::covers`): the pattern no longer than the path, and agreeing
with it segment by segment;
* listings (`reaches`): pattern and path agreeing on the segments both have, so every
key on the way shows, nothing past where the pattern differs;
* narrowing (`Capability::holds`, for a token's limit narrowed again and a refresh's):
at or below, a `*` only under a `*`: a limit naming one key never widens to all.
A `*` first is outside protocol data, so it never covers `_continuum`, as `[]` doesn't.
A key that is `*` itself can't be named alone. Every node judges changes by
capabilities, so all must read them alike: they run the same version.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The node's keepalives take turns between the two sides (`PeerState::keepalive_due_at`),
but the paths layer pinged every path in use every `probe_every` (the keepalive interval,
30 s at least) whatever the peer did: a PING from the peer over the path didn't count.
So both sides pinged each path every interval, often within a second of each other
(seen on the dashboard: a PING answered, and our own PING right after it).
Now a PING from the peer over a path is noted (`PathState::peer_probe`), and a path is
pinged again (`rested`) an interval after the peer's PING, if that came last; after
ours, an interval and a half while the peer pings too (its PING within two intervals
before ours), so its turn comes first; else an interval. One exchange per interval
goes over a path, as the measurements' "two keepalives per neighbour per interval"
says, and each side measures every other interval; a shorter `--keepalive` measures
more often. A path whose way out broke while the peer's PINGs still come in is found by
our own PINGs half as often; a failed send or unanswered traffic (`suspect`) still
pings at once.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Requests weren't logged at all, short of what a handler logs itself. Now a middleware,
outermost on the API (and on a relay-only node's), logs a line per request as web
servers do: method and path (with the query), status, the milliseconds until the answer
began (a stream goes on after), where it came from (the address, or `unix`), and the
user (`-` where none was authenticated). The user comes back on the response:
`basic_auth` puts it there, for a token as for a password or a trusted proxy's header.
At debug, under the target `http` (`RUST_LOG=info,http=debug`, or `PUT /admin/log`):
an open dashboard alone would fill the log at info. With the target off, the request
passes untouched.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
A snapshot leaving out earlier epochs' metadata (`snapshot::left_out`) and a rejoining
node (`catchup::prepare`) read it from the document as it stands. It is never writable
through the store, and under a policy the rules take a change to it only from an
epoch's first; but with no policy, screening judges only an epoch's root, so a member's
own change could rewrite or remove an entry: a replay window narrowed or widened, a
translation or a clock changed, a strict remake's entry gone.
Now both read it at the epoch's snapshot change (`snapshot::as_built`), as
`verify_document` reads the epoch's own: a snapshot from its base's epoch's, a
rejoining node from the target's. Every earlier entry is a copy in that change, so what
was written there since doesn't count. The first epoch follows none and has none.
|
| |
|
|
|
|
|
|
|
| |
The remake dialog set the kind, its input, the settle time and the switch delay, but not
the replay window or the archive retention. Now each has two fields, epochs and an age,
their defaults as placeholders: both empty sends nothing (the node's defaults), one
given sends that limit alone, as the API reads it; in the body, or for an import in the
query (`replayWindow`, `archiveRetention`). Epochs that aren't a whole number are
refused in the page.
|
| |
|
|
|
|
|
| |
An import uploaded as `application/octet-stream` takes `rules`, `baseHeads`, `settle`
and `switchDelay` in the query, but not the retentions: for those the document had to
go base64-encoded in the JSON form. Now `replayWindow` and `archiveRetention` go in the
query too, as JSON like the rules (`{"epochs":1,"age":"7d"}`, as in the body).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Every snapshot carried the metadata of all earlier epochs (`_continuum/epochs`)
forward, so it grew by an entry, a clock per actor among it, with every remake. Only a
rejoining node reads an earlier entry: a node on epoch P reads P+1's (its clock, when
it was proposed, its replay window) and every one up to the swarm's epoch (kind,
translation, whether it was strict). It replays only while P+1's replay window covers
it; beyond, its writes are quarantined whatever the entries say.
So a snapshot now leaves out the generations older than the oldest whose replay window
still covers a node on the epoch before it (`snapshot::left_out`), judged by the
remake's `proposedAt`: the same on every node, and earlier than any rejoining node's
clock, so nothing a node can still replay through goes. Metadata that doesn't read
stays, with all after it.
A strict remake's metadata stays for good: by it a rejoining node, however far behind,
tells a strict remake of a swarm strict already (`catchup::prepare`, which reads the
generations it finds). There is at most one, short of such a remake. No new key and no
change to the rejoin: a node beyond the window that finds some entries and not others
quarantines with "cannot translate" rather than "outside the replay window".
|
| |
|
|
|
|
|
|
| |
A key's line was stored as the key alone (`ssh-ed25519 <base64>`), whatever comment it
came with, both from `PUT /auth/keys/<name>` and from `--bootstrap-keys`. Now its
comment is kept after it (`PublicKey::kept_line`), the spaces between its words made
one. The comment checks nothing: every use of a key parses the line, and the parse
reads the key alone.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
A page opened from another node's page takes that page's subscriptions
along; one of them can be this node, another there. The page then
followed this node as a subscribed one too, and showed its messages
twice, from its own log and from the subscription (`→ LLL PING` and
`LLL ← RRR PING`, the same message), on the map as in the console.
The page follows no subscription of its own node: not when it knows
which node it is, and one begun before that stops once it does; its
messages, if any came, are left out.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The credentials dialog listed a user's keys whole, to add one or
remove one but the last, and any failure replaced the dialog with the
error, the form lost.
* A key is shown short: its type, the first and last four characters
of what tells it apart (an Ed25519 key's base64 after the part all of
them share), its comment; the whole line in its tooltip.
* A key can be edited, its name and its line: saved under the name, a
new name written first and the old one removed.
* A user with a password on this node (`--htpasswd`) may remove its
last key: it logs in by the password. GET /auth/keys says so
(`password`), and DELETE /auth/keys/{id} takes it then; the rules no
longer keep the last key on every node (no other node can tell who
has a password where; under a strict policy the signed entry keeps
one still).
* What fails says why beside where it was asked (the add form, the
edited row, the removed one), which stays as it was, to be set right
and tried again.
|
| |
|
|
|
|
|
|
|
|
| |
A right press on a node started dragging it, as any press did: the map
took hold of the pointer, so the right-click went to the map and the
node's menu didn't open, and letting go put the node in focus. The
right button now opens the node's menu alone. Its entry for the node's
dashboard is `go to its dashboard`, greyed where the node publishes
none (and on this node's own), its tooltip saying where it goes or why
not.
|
| | |
|
| |
|
|
|
|
|
| |
Opened by a finger held on an entry, its menu had its top left corner
at the finger, under the hand holding it, a right hand mostly. By
touch it now has its top right corner there; opened by a pointer, its
top left, as before.
|
| |
|
|
|
|
|
|
| |
With the entries' menus (1238), editing had three ways in: the
double-click, the `+` on a map's or list's row, and the menu. The
double-click and the `+` go; the menu does all of it. The title,
which stands for the root (its row isn't shown), now takes the
right-click too: its menu adds a key at the top level.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
The document box edited an entry by a double-click and added to a map
or list by the `+` on its row; a key couldn't be renamed, and a list's
`+` always asked for an index.
A right-click on an entry, or a finger held on it half a second, opens
its menu: `Edit value` (as the double-click), `Rename key` for a map's
key, and on a map `Add a key`, on a list `Append an item` or `Insert
an item at…`. Renaming reads the value, writes it under the new key
and deletes the old one (two writes, the value as JSON; a key that
exists already isn't overwritten). Inside a field being edited, and
where an entry offers nothing, the browser's own menu shows.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
A stream's lag in the tooltip of `behind: …` was one figure, from when
a node logged a message to when the page had it: a second and a half
for a node a LAN away said nothing of where it went.
The node now stamps each `messages` event with when it sent it
(`{"sent": …, "messages": […]}`), and the tooltip splits each stream's
lag: how long its batches waited in the node, from logging the first
message to sending the batch (the node's own clock: exact), and how
long they took on the way, from sending to arriving. This node's own
stream is always listed, beside the three slowest.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The console appended messages as their streams brought them, a
subscribed node's later than this node's by its path, each line with
its node's own clock's time: lines of one moment came apart, and their
times read out of order.
Each message is now listed by when its node logged it, on this node's
clock, as the map runs them (`aheadOf`): one a slower stream brought
late goes in among those of its moment. The time shown is on this
node's clock; a subscribed node's own clock's time is in its tooltip.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Each node's message stream comes its own way to the page, a subscribed
node's over the network to it: messages logged 16 ms apart on three
nodes ran over a second apart, as their streams arrived.
Messages now run when they went, on this node's clock: each at the
time its node logged it, taken back by how far that node's clock is
ahead of this node's (the map's clock chain, `aheadOf`), a while
behind real time (`replayDelay`): the most any stream lagged in the
last 30 s and a tenth of a second more, shown after the sliders
(`behind: …`), each stream's lag behind the fastest in its tooltip.
The browser's clock is against all streams alike and drops out. A
message known only from its receiver (its sender not subscribed) is
placed at its arrival, starting a crossing before. Messages lagging
more than 10 s (a gap) don't run, nor hold the map behind.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The node sent its messages to the page every 2 s (`messages` events,
this node's and a subscribed node's), and this node's ran on the map
from its counts, which come with the state: the map showed a message 2
to 4 s after it went, the messages of each 2 s spread evenly over the
next.
The message log now tells those waiting of each message logged
(MessageLog::logged_after), and both streams send what was logged a
tenth of a second after the first of it (MESSAGES_GATHERED): a busy
node about ten events a second, a quiet one each message soon after it
went. The page runs messages as their streams bring them, this node's
from its own log too (with the addresses its PINGs went to), as far
apart as they were sent; more than six on a link and way at once are
shared out by kind over the moment, and after a gap only the last 2 s
run. The counts still give the rates and what was delivered.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A message between this node and a subscribed one ran from this node's
counts both ways: one the subscribed node sent ran once this node had
counted it received, as it arrived. Each message now runs from its
sender where the page reads the sender (this node's counts, a
subscribed node's messages), so it runs the way it went, also where it
never arrived; only one from a node the page doesn't read runs as it
arrived. The console lists them as before.
Where the page reads both ends of a link, what one sent the other
should count received: a message sent and not received within 5 s
makes the link breathe red once (`breatheLink`), its tooltip saying
how many. Kinds the paths and identity layers count under addresses
are left out, as this node's counts leave them out; a subscribed
node's tick that came short (the latest 500 of more) starts the count
afresh, so what the page didn't see isn't taken as lost.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Dots and flashes ran straight from sender to receiver, whatever line
the message took: a PING over another transport, drawn as a curved
line beside the one in use, crossed the map off its line.
Each message now runs along the line it went over, as drawn (`route`:
the link's quadratic curve, its control point where renderGraph put
it): a message naming the address it went to (the paths layer's PINGs
and PONGs) over that address's transport's line, any other over the
line in use; a flash's streak bends with it.
|
| |
|
|
|
|
|
|
| |
On a dynamic map, links pull toward their lengths against the nodes
pushing each other apart, at one strength. A `pull` slider before the
layout's toggle, shown while dynamic, sets it from a quarter to four
times as hard (2 to its value, once at first), kept in the browser;
the map moves to the new balance smoothly.
|
| |
|
|
|
|
|
|
|
|
| |
A link was dotted where an end reported no links. Members used to
report their links to every dashboard (LINKS); the page now reads a
member's links only while subscribed to it, so a dotted link said no
more than that its other end isn't subscribed, which the node shows
already (hollow), and most links were dotted. The style and its
legend entry go; the link's tooltip says when one end alone reports
it.
|
| |
|
|
|
|
|
|
| |
The map's dots and the console coloured six kinds of message; PROBE,
PRUNE/GRAFT, NEIGHBOUR and REFUSALS were drawn in the text colour and
missing from the legend. Each kind (`traffic`'s counters) now has a
colour, and the legend names them all, saying which messages a kind
stands for where its name doesn't.
|
| |
|
|
|
|
| |
Since load_delta is for tests (1190), nothing else in changes.rs uses
anyhow's Context, and builds without tests warned of an unused import.
load_delta imports it itself.
|
| |
|
|
|
|
|
| |
How many changes apply_bytes applied was read only by the handler of
SIGNATURES, which re-screened the changes waiting for them and
published those that went in. With SIGNATURES gone (1219), nothing
reads it: Outcome loses the field.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A sync message carried the signatures its peer wasn't sent yet, by a
cursor per peer. Changes sent again, as after a message taken as
delivered and lost on the way, went without them: the peer was past
them. Under a policy requiring signatures they waited until the
writer's next signed change, or the next session.
A message now carries the signatures of the changes it carries
(Signatures::for_changes), whatever the peer was sent before: changes
sent again arrive vouched for as the first time. Past 256 (a message
carrying the batches of many writers), each writer's latest instead,
as a delta carries. The cursor goes, and with it resetting it after a
failed send or a new session.
|
| |
|
|
|
|
|
|
|
| |
delivered_messages_are_answered_with_changes_we_lack failed now and
then: after the answer, the node still lacked the peer's change.
Automerge's sync tells what each side has by a Bloom filter, whose
false positives take more rounds. The test now exchanges up to four
rounds more, each asking for what is still missing, until both are
level.
|
| |
|
|
|
|
|
|
|
| |
api.md's Signed changes says what sent, all and none do, what each
costs, and which to choose: sent for nearly every swarm, all where each
change should carry its own proof, none to save signatures where only
protocol data and listed paths need them; and that signatures travel
with their changes, a message carrying none taking none. access.md no
longer says signatures come on their own within a second.
|
| |
|
|
|
|
|
|
|
|
| |
A sync message whose send fails gives back the changes it carried: the
next message takes them again. The signatures it carried weren't
given back: the peer's cursor stayed past them, so the changes went
again without them, and under a policy requiring signatures waited
for them until the next session; SIGNATURES no longer covers that.
A failed send now sends the peer all signatures again with the next
changes (each actor's latest, a few hundred at most).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Signatures sent on their own (SIGNATURES, every second, to each peer
not sent them yet) flooded every link: a signature made after its
change left, or one that came with changes, went to every neighbour,
whether or not it got the change along with its signature elsewhere.
With every change of a node signed as it leaves (1217) and signatures
going where their changes go (1218), none is left for them to carry:
the message, its task and its traffic counter go (wire version 46).
Signatures waiting for their changes are offered as before when sync
messages bring them, and the changes waiting for signatures judged
again with the next batch.
|
| |
|
|
|
|
|
|
|
|
|
| |
A sync message took the signatures its peer wasn't sent yet whenever it
had a body, an Automerge message saying only what we have included. A
signature belongs with the changes it vouches for: they go along the
trees, and a peer that got them elsewhere got the signatures with them.
Signatures now go in a message that carries changes (pushed, offered
as a delta, or in an Automerge message carrying some: SyncSession::
carrying), and in no other.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A node signed its changes no rule signs a while after them (--sign-every
1s): the change left at once, unsigned, and its signature followed on
its own a second later, to every neighbour, over every link. A few
writes made a cascade of SIGNATURES. Under a policy requiring
signatures it signed every change as it was made instead.
A node now signs its latest change, if no signature vouches for it, as
a sync message takes it (--sign-every sent, the default): the
signature leaves in the same message, and goes where the change goes.
One signature per message, however many changes it carries, each
vouching for those before it: a slow writer's every change is signed,
a fast writer's batches are, at no threshold to pick. A policy
requiring signatures signs what is sent too, even with `none`. `all`
still signs every change as it is made; changes to protocol data,
listed paths and ?signed=true writes are signed as they are made, as
before. The interval, and the task signing on it, go.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
A node commits a change, keeps it in its document file, and may stop
before any signature vouches for it. It starts again under a new
actor and only ever signed its current actor's latest change: the old
one stays unsigned for good. Peers pulling it then wait for a
signature under a policy requiring them, and so does everything the
node writes after, building on it. Signing every change at commit hid
this under such a policy; signing what is sent, next, won't.
At start, before anything leaves, a node signs each of its own
changes at the document's heads that no signature vouches for:
nothing it made since builds on them, so they are heads.
|
| |
|
|
|
|
| |
Review 3: since 1184 a proposal is written as the user who asks, so
under a policy the node doesn't know it answers 409 like every user
write. remakes.md says so.
|
| |
|
|
|
|
|
|
|
| |
Review 3, R5: a node given its root (--genesis-root) refused a peer
naming another only change by change while its document was empty,
each with an entry among the refused; the handshake went through, as
there was no root of its own yet to compare with. The root given is
now compared too: a peer naming another is refused at the handshake,
logged once, as one whose document starts from another root.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Review 3, R5: --refused-writes-per-minute bounds each user, but the
cost it guards, a document copy under the write lock, is the node's:
ten users, or ten accounts of one, are ten times the budget.
The node now takes six times a user's limit within a minute in all
(refused::NODE_FACTOR); past it, every user with a write refused
within the minute is answered 429 untried, until the oldest leave it.
A user none of whose writes was refused writes on, so honest writers
aren't held up by others' refusals.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Review 3, R4: whether a batch was judged followed the receiving
document's policy now (and whether the batch writes one). After the
policy was removed, or replaced by a version the node doesn't know,
changes written under it and received late went in unjudged, while
nodes that had them earlier judged them; REFUSALS gave up likewise.
Screening now remembers that the document held a policy, known or not
(Screening::policed), and judges what it receives while it does, each
change by the policy at its dependencies; REFUSALS judges then too
(not under a policy the node doesn't know, as before). Not across a
restart, which access.md says.
|
| |
|
|
|
|
|
|
| |
Review 3, R3, the replace side: a replace or import remake writes its
content as it is, at signed places too, so under a strict policy a
member majority can put plain values where envelopes were. access.md
says that remakes are outside the rule; keeping envelopes at signed
places in every kind is postponed to future-work.md.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Review 3, R3: 1186 kept honest nodes from proposing or voting for a
strict remake of a swarm strict already, but a majority of members,
each with keys of its own, could still commit one, and switch and
catchup followed it without asking what kind of remake made the
epoch: the registry, policy and bans of a strict swarm changing by a
member majority, the honest minority following.
A node now refuses to switch to such an epoch (its document's policy
strict, the remake's kind strict), and catchup refuses to rejoin one
whose chain of epochs holds a strict remake after the swarm was strict
(as the old document is, or once a remake since made it so). access.md
says so.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Review 3, R2: the root pin was set by the first HELLO_REPLY, and the
first sync, a round trip later, took that root; a peer naming another
afterwards was refused as another history. So the seed that answered
first decided, not "none while they disagree", and a malicious seed
makes itself fastest.
A node joining with an empty document of the first epoch now takes no
root (RootPin::Awaiting, roots left for later) until each of its seeds
named one, or ROOT_WAIT (10 s) passed since the first did; then the
one they name, none while they differ, as before. With --genesis-root
it takes that one alone, at once. access.md, the option's help and
example.toml say so; switching roots later, while the joiner holds
nothing of its own, is postponed.
|
| |
|
|
|
|
|
|
|
|
| |
With every message's signatures offered now (1205), Signatures::offer
runs for each, and it asked get_change_meta_by_hash whether the
document holds each signature's change, and each one waiting, up to
4096 of them: Automerge rebuilds the change's metadata from the
document's operations to answer. Ask changes::holds instead (1197),
there and in forks and kept_for; the metadata is still read where it
is used.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Review 3, R1: what a node received was vouched for only while its own
document's policy required signatures. A joiner's first sync, and
every node's message bringing `required`, went in by the rules alone:
a malicious first peer could plant changes under other members' actors
at their next seqs, their real ones then failing as duplicates for
good.
The signatures a message carries are always offered now, and the
screen always has them: a change waits for one where the policy at the
heads it is judged at requires it (rules::Reads::required, read once
per heads), whatever the receiving document holds. Changes written
under a policy not requiring signatures wait for none, as on every
other node; the check for forged changes runs always. Changes waiting
for a signature are judged again whenever signatures come.
The test of signatures required now takes the history into an empty
document; a new one takes changes written under a policy not requiring
them without any.
|
| |
|
|
|
|
|
|
| |
Each change taken but not yet applied got its own set of the heads it
is judged at, built in an ordered set and copied into a vector, and
kept so for those building on it. Most build on one change alone and
are judged at its heads: share them instead (reference counted), and
build a set only for a change with several dependencies.
|
| |
|
|
|
|
|
| |
Screening copied every change it took, and every one left waiting for
a signature, bytes and all, from the batch it had ordered and then
dropped. Move them out of the batch instead; only a refused change,
recorded with why, is still copied.
|
| |
|
|
|
|
|
| |
in_order is about a seventh of screening in the profile, most of it
hashing change hashes: each change went into and out of three maps,
and each dependency into two. Look each hash up once, in a map from
hash to place, and keep the rest by place in vectors.
|