Added discussion for implementing peer to peer. It is harder than it

seems, because you typically want to communicate with multiple peers at
the same time.

Minor updates, and moved files to more meaningful locations,
which required updating links.
This commit is contained in:
reaction.la 2023-12-20 04:08:52 +00:00
parent 8e7bdfa35a
commit 6dfee3e91f
No known key found for this signature in database
GPG Key ID: 99914792148C8388
10 changed files with 294 additions and 45 deletions

View File

@ -8,8 +8,10 @@ name system, SSL, and email. This is covered at greater length in
# Implementation issues # Implementation issues
There is a great [pile of RFCs](TCP.html) on issues that arise with using udp and icmp There is a great [pile of RFCs on issues that arise with using udp and icmp
to communicate. to communicate.
[Peer-to-Peer Communication Across Network Address Translators]
(https://bford.info/pub/net/p2pnat/){target="_blank"}
## timeout ## timeout
@ -30,7 +32,7 @@ needed. They never bothered with keep alive. They also found that a lot of
the time, both parties were behind the same NAT, sometimes because of the time, both parties were behind the same NAT, sometimes because of
NATs on top of NATs NATs on top of NATs
[hole punching]:http://www.mindcontrol.org/~hplus/nat-punch.html [hole punching]:https://tailscale.com/blog/how-nat-traversal-works
"How to communicate peer-to-peer through NAT firewalls" "How to communicate peer-to-peer through NAT firewalls"
{target="_blank"} {target="_blank"}

View File

@ -1,7 +1,7 @@
<div class="button-bar"> <div class="button-bar">
<a href="vision.html">vision</a> <a href="../manifesto/vision.html">vision</a>
<a href="scalability.html">scalability</a> <a href="../manifesto/scalability.html">scalability</a>
<a href="social_networking.html">social networking</a> <a href="../manifesto/social_networking.html">social networking</a>
<a href="Revelation.html">revelation</a> <a href="../manifesto/Revelation.html">revelation</a>
</div> </div>

View File

@ -8,23 +8,253 @@ notmine: false
::: myabstract ::: myabstract
[abstract:]{.bigbold} [abstract:]{.bigbold}
Most things follow the client server model, so it makes sense to have a distinction between server Most things follow the client server model,
sockets and client sockets. But ultimately what we are doing is passing messages between entities so it makes sense to have a distinction between server sockets
and the revolutionary and subversive technologies, bittorrent, bitcoin, and bitmessage are peer to and client sockets. But ultimately what we are doing is
peer, so it makes sense that all sockets, however created wind up with same properties. passing messages between entities and the revolutionary
and subversive technologies, bittorrent, bitcoin, and
bitmessage are peer to peer, so it makes sense that all sockets,
however created wind up with same properties.
::: :::
# factoring # factoring
In order to pass messages, the socket has to know a whole lot of state. And in order handle messages, In order to pass messages, the socket has to know a whole lot of state. And
the entity handling the messages has to know a whole lot of state. So a socket api is an answer in order handle messages, the entity handling the messages has to know a
to the question how we factor this big pile of state into two smaller piles of state. whole lot of state. So a socket api is an answer to the question how we
factor this big pile of state into two smaller piles of state.
Each big bundle of state represents a concurrent communicating process. Some of the state of this Each big bundle of state represents a concurrent communicating process.
concurrent communicating process is on one side of our socket division, and is transparent to Some of the state of this concurrent communicating process is on one side
one side of our division. The application knows the internals of the some of the state, but the of our socket division, and is transparent to one side of our division. The
internals of socket state are opaque, while the socket knows the internals of the socket state, but application knows the internals of the some of the state, but the internals
the internals of the application state are opaque to it. For each of them, it is a pointer. For the of socket state are opaque, while the socket knows the internals of the
socket code, a pointer to void, for the application code, a pointer to the opaque class peer::socket socket state, but the internals of the application state are opaque to it.
The socket state machines think that they are passing messages of one class
or a very small number of classes, to one big state machine, which messages
contain an opaque block of bytes that application class serializes and
deserializes.
$$\lfloor{(h-1000)/4096}\rfloor*4096$$ ## layer responsibilities
The sockets layer just sends and receives arbitrary size blocks
of opaque bytes over the wire between two machines.
They can be sent with or without flow control
and with or without reliability,
but if the block is too big to fit in this connection's maximum
packet size, the without flow control and without
reliability option is ignored. Flow control and reliability is
always applied to messages too big to fit in a packet.
The despatch layer parses out the in-reply-to and the
in-regards-to values from the opaque block of bytes and despatches them
to the appropriate application layer state machine, which parses out
the message type field, deserializes the message,
and despatches it to the appropriate fully typed event handler
of that state machine.
# Representing concurrent communicating processes
A message may contain a reply-to field and or an in-regards-to field.
The recipient must have associated a handler, consisting of a
call back and an opaque pointer to the state of the concurrent process
on the recipient with the messages referenced by at least one of
these fields. In the event of conflicting values, the reply-to takes
precedence, but the callback of the reply-to has access to both its
data structure, and the in-regards-to dat structure, a pointer to which
is normally in its state. The in-regards-to being the state machine,
and the in-reply-to the event that modifies the
state of the state machine.
When we initialize a connection, we establish a state machine
at both ends, both the application factor of the state machine,
and the socket factor of the state machine.
But a single state machine at the application level could be
handling several connections, and a single connection could have
several state machines running independently, and the
socket code should not need to care.
Further, these concurrent communicating processes are going to
be sending messages to each other on the same machine.
We need to model Go's goroutines.
A goroutine is a function, and functions always terminate --
and in Go are unceremoniously and abruptly ended when their parent
function ends, because they are variables inside its dataspace,
as are their channels.
And, in Go, a channel is typically passed by the parent to its children,
though they can also be passed in a channel.
Obviously this structure is impossible and inapplicable
when processes may live, and usually do live,
in different machines.
The equivalent of Go channel is not a connection. Rather,
one sends a message to the other to request it create a state machine,
which will be the in-regards-to message, and the equivalent of a
Go channel is a message type, the in-regards-to message id,
and the connection id. Which we pack into a single class so that we
can use it the way Go uses channels.
The sockets layer (or another state machine on the application layer)
calls the callback routine with the message and the state.
The sockets layer treats the application layer as one big state
machine, and the information it sends up to the application
enables the application layer to despatch the event to the
correct factor of that state machine, which we have factored into
as many very small, and preferably stateless, state machines as possible.
We factor the potentially ginormous state machine into
many small state machines, in the same style as Go factors a potentially
ginormous Goroutine into many small goroutines.
The socket code being a state machine composed of many
small state machines, which communicates with the application layer
over a very small number of channels,
these channels containing blocks of bytes that are
opaque to the socket code,
but are serialized and deserialized by the application layer code.
From the point of view of the application layer code,
it is many state machines,
and the socket layer is one big state machine.
From the point of view of the socket code, it is many state machines,
and the application layer is one big state machine.
The application code, parsing the the in-reply-to message id,
and the in-regard-to message id, figures out where to send
the opaque block of bytes, and the recipient deserializes,
and sends it to a routine that acts on an object of that
deserialized class.
Since the sockets layer does not know the internals of the message struct, the message has
to be serialized and deserialized into the corresponding class by the application layer.
Or perhaps the callback routine deserializes the object into a particular class, and then calls
a routine for that class, but another state machine on the application layer would call the
class specific routine directly. The equivalent of Go channel between one state machine on the
application layer and another in the same application layer is directly calling what
the class specific routine that the callback routine would call.
The state machine terminates when its job is done,
freeing up any allocated memory,
but the connection endures for the life of the program,
and most of the data about a connection endures in
an sql database between reboots.
Because we can have many state machines on a connection,
most of our state machines can have very little state,
typically an infinite receive loop, an infinite send receive loop,
or an infinite receive send loop, which have no state at all,
are stateless. We factorize the state machine into many state machines
to keep each one manageable.
Go code tends to consist of many concurrent processes
continually being spawned by a master concurrent process,
and themselves spawning more concurrent processes.
For most state machines, we do not need recursion,
so it is reasonable for their state to be a fixed allocation
inside the state of their master concurrent process.
In the unlikely event we do need recursion
we usually only have one instance running at one time,
so we can allocate an `std::stack` in the master concurrent process.
And what if we do want to spawn many in parallel?
Well, they will usually be stateless.
What if they are not not stateless?
Well that would require an `std::vector` of states.
And if we need many running in parallel with recursion,
an `std::vector` with each element containing an `std::stack`.
And to avoid costly reallocations, we create the `std::vector`
and the `std::vector`s underlying the `std::stack`s with
realistic initial allocations that are only infrequently exceeded.
# flow control and reliability
If we want to transmit a big pile of data, a big message, well,
this is the hard problem, for the sender has to throttle according
to the recipient's readiness to handle it and the physical connections capability to transmit it.
Quic is a UDP protocol that provides flow control, and the obvious thing
to handle bulk data transfer is to fork it to use Zooko based keys.
[Tailscale]:https://tailscale.com/blog/how-nat-traversal-works
"How to communicate peer-to-peer through NAT firewalls"{target="_blank"}
[Tailscale] has solved a problem very similar to the one I am trying to solve, albeit their solutions rely on a central human authority, and they recommend:
> If youre reaching for TCP because you want a
> streamoriented connection when the NAT traversal is done,
> consider using QUIC instead. It builds on top of UDP,
> so we can focus on UDP for NAT traversal and still have a
> nice stream protocol at the end.
But to interface QUIC to a system capable of handling a massive
number of state machines, going to need something like Tokio,
because we want the thread to service other state machines while
QUIC is stalling the output or waiting for input. Indeed, no
matter what, if we stall in the socket layer rather than the
application layer, which makes life a whole lot easier for the
application programmer, going to need something like Tokio.
On the application side, we have to lock each state machine
when it is active. It can only handle one message at at time.
So the despatch layer has to queue up messages and stash them somewhere,
and if it has too many messages stashed,
it wants to push back on the state machine at the application layer
at the other end of the wire. So the despatch layer at the receiving end
has to from time to time tell the despatch layer at the sending end
"I have `n` bytes in regard to message 'Y', and can receive `m` more.
And when the despatch layer at the other end, which unlike the socket
layer knows which state machine is communicating with which,
has more than that amount of data to send, it then blocks
and locks the state machine at its end in its send operation.
The socket layer does not know about that and does not worry about that.
What it worries about packets getting lost on the wire, and caches
piling up in the middle of the wire.
It adds to each message a send time and a receive time
and if the despatch layer wants to send data faster
than it thinks is suitable, it has to push back on the despatch layer.
Which it does in the same style.
It tells it the connection can handle up to `m` further bytes.
Or we might have two despatch layers, one for sending and one for
receiving, with the send state machine sending events to the receive state
machine, but not vice versa, in which case the socket layer
*can* block the send layer.
# Tokio
Most of this machinery seems like a re-implementation of Tokio-rust,
which is a huge project. I don't wanna learn Tokio-rust, but equally
I don't want to re-invent the wheel.
# Minimal system
Prototype. Limit global bandwidth at the application
state machine level -- they adjust their policy according to how much
data is moving, and they spread the non response outgoing
messages out to a constant rate (constant per counterparty,
and uniformly interleaved.)
Single threaded, hence no state machine locking.
Tweet style limit on the size of messages, hence no fragmentation
and re-assembly issue. Our socket layer becomes trivial - it just
send blobs like a zeromq socket.
If you are trying to download a sackload of data, you request a counterparty to send a certain amount to you at a given rate, he immediately responds (without regard to global bandwidth limits) with the first instalment, and a promise of further instalments at a certain time)
Each instalment records how much has been sent, and when, when the next instalment is coming, and the schedule for further instalments.
If you miss an instalment, you nack it after a delay. If he receives
a nack, he replaces the promised instalments with the missing ones.
The first thing we implement is everyone sharing a list of who they have successfully connected to, in recency order, and everyone keeps everyone else's list, which catastrophically fails to scale, and also how up to date their counter parties are with their own list, so that they do not have
endlessly resend data (unless the counterparty has a catastrophic loss of data, and requests everything from the beginning.)
We assume everyone has an open port, which is sucks intolerably, but once that is working we can handle ports behind firewalls, because we are doing UDP. Knowing who the other guy is connected to, and you are not, you can ask him to initiate a peer connection for the two of you, until you have
enough connections that the keep alive works.
And once everyone can connect to everyone else by their public username, then we can implement bitmessage.

View File

@ -1,12 +1,13 @@
--- ---
title: title:
proof of share proof of share
sidebar: true
notmine: false
... ...
::: {style="background-color : #ffdddd; font-size:120%"}
![run!](tealdeer.gif)[TL;DR Map a blockdag algorithm equivalent to the
Generalized MultiPaxos Byzantine
protocol to the corporate form:]{style="font-size:150%"}
::: myabstract
[abstract:]{.bigbold}
Map a blockdag algorithm to the corporate form.
The proof of share crypto currency will work like The proof of share crypto currency will work like
shares. Crypto wallets, or the humans controlling the wallets, shares. Crypto wallets, or the humans controlling the wallets,
correspond to shareholders. correspond to shareholders.
@ -49,8 +50,9 @@ that in substantial part, it made such behavior compulsory.  Which is
why Gab is now doing an Initial Coin Offering (ICO) instead of an why Gab is now doing an Initial Coin Offering (ICO) instead of an
Initial Public Offering (IPO). Initial Public Offering (IPO).
[Sarbanes-Oxley]:sox_accounting.html [Sarbanes-Oxley]:../manifesto/sox_accounting.html
"Sarbanes-Oxley accounting" "Sarbanes-Oxley accounting"
{target="_blank"}
Because current blockchains are proof of work, rather than proof of Because current blockchains are proof of work, rather than proof of
stake, they give coin holders no power. Thus an initial coin offering stake, they give coin holders no power. Thus an initial coin offering
@ -125,24 +127,22 @@ currency, to which existing crypto currencies are not well adapted.
# How proof of share works # How proof of share works
One way out of this is proof of share, plus an incomplete, One way out of this is proof of share, plus evidence of good
imperfect, and far from bulletproof proof of spacetime connectivity, bandwidth, and disk speed. You have a crypto currency
(disk access and cpu bandwidth) and an even more grossly imperfect
and gameable proof of connectivity. You have a crypto currency
that works like shares in a startup. Peers have a weight in that works like shares in a startup. Peers have a weight in
the consensus, a likelihood of their view of the past becoming the the consensus, a likelihood of their view of the past becoming the
consensus view, that is proportional to the amount of consensus view, that is proportional to the amount of
crypto currency their client wallets possessed at a certain block height, crypto currency their client wallets possessed at a certain block height,
$\lfloor(h1000)/4096\rfloor4096$, where h is the current block height, $\lfloor(h1000)/4096\rfloor4096$, where $h$ is the current block height,
provided they maintain adequate data, disk access, provided they maintain adequate data, disk access,
and connectivity. The trouble with this is that it reveals and connectivity. The trouble with this is that it reveals
what machines know where the whales are, and those machines what machines know where the whales are, and those machines
could be raided, and then the whales raided, so we have to have could be raided, and then the whales raided, so we have to have
a mechanism that can hide the ips of whales delegating weight a mechanism that can hide the ips of whales delegating weight
in the consensus to peers from the peers exercising that weight in the consensus to peers from the peers exercising that weight
in the consensus. And in fact I intend to do that mechanism in the consensus. And [in fact I intend to do that mechanism
before any crypto currency, because bitmessage is abandonware before any crypto currency, because bitmessage is abandonware
and needs to be replaced. and needs to be replaced](file:///C:/Users/john/src/reactionId/wallet/docs/manifesto/social_networking.html#monetization){target="_blank"}.
Plus the peers consense over time on a signature that Plus the peers consense over time on a signature that
represents human board, which nominates another signature that represents represents human board, which nominates another signature that represents
@ -153,30 +153,40 @@ the capability to do known and legitimate things. Dictating
the consensus and thus rewriting the past not being one of those the consensus and thus rewriting the past not being one of those
legitimate things. legitimate things.
## algorithm details ## consensus algorithm details
Each peer gets a weight at each block height that is a Each peer gets a weight at each block height that is a
deterministically random function of the block height, deterministically random function of the block height,
its public the hash of the previous block that it is building its block its public key, the hash of the previous block that it is building its block
on top of, and the amount of crypto currency (shares) on top of, and the amount of crypto currency (shares)
that it represents, with the likelihood of it getting a high weight that it represents, with the likelihood of it getting a high weight
proportional to the amount of crypto currency it represents. proportional to the amount of crypto currency it represents, such
that the likelihood of a peer having a decisive vote is proportional
to the amount of share it represents.
Each peer sees the weight of a proposed block as Each peer sees the weight of a proposed block as
the median weight of the three highest weighted peers the median weight of the three highest weighted peers
that it knows know or knew of the block and its contents according to that it knows know or knew of the block and its contents according to
their current weight at this block height, plus the weight of their current weight at this block height and perceived it has highest
weighted at the time they synchronized on it, plus the weight of
the median weighted peer among up to three peers the median weighted peer among up to three peers
that were recorded by the proposer that were recorded by the proposer
as knowing to the previous block that the proposed block as knowing to the previous block that the proposed block
is being built on at the previous block height, plus the is being built on at the previous block height, plus the
weight of the chain of preceding blocks similarly. weight of the chain of preceding blocks similarly.
When it synchronizes with another peer on a block, When it synchronizes with another peer on a block, and the block is
at that time the highest weighted block proposed block known to both
of them,
both record the other's signature as knowing that block both record the other's signature as knowing that block
as the highest weighted known at that time. If one of them
knows of a higher weighted proposed block, then they
synchronize on whichever block will be the highest weighted block.
when both have synchronized on it.
if it has a record of less than three knowing that block, if it has a record of less than three knowing that block,
or if the other has a higher weight than one of the three, or if the other has a higher weight than one of the three,
and they also synchronize their knowledge of the highest weighted three. then they also synchronize their knowledge of the highest weighted three.
This algorithm favors peers that represent a lot of shares, and also This algorithm favors peers that represent a lot of shares, and also
favors peers with good bandwidth and data access, and peers that favors peers with good bandwidth and data access, and peers that
@ -279,7 +289,7 @@ intent was for buying drugs, buying guns, violating copyright, money
laundering, and capital flight. laundering, and capital flight.
These are all important and we need to support them all, especially These are all important and we need to support them all, especially
violating copyright, capital flight and buying guns under repressive violating copyright, capital flight, and buying guns under repressive
regimes.  But we now see big demand for crypto currencies to support a regimes.  But we now see big demand for crypto currencies to support a
replacement for Charles the Seconds corporate form, which is being replacement for Charles the Seconds corporate form, which is being
destroyed by HR, and to restore double entry accounting, which is being destroyed by HR, and to restore double entry accounting, which is being

View File

@ -145,4 +145,4 @@ worth, probably several screens.
- [How to do VPNs right](how_to_do_VPNs.html) - [How to do VPNs right](how_to_do_VPNs.html)
- [How to prevent malware](safe_operating_system.html) - [How to prevent malware](safe_operating_system.html)
- [The cypherpunk program](cypherpunk_program.html) - [The cypherpunk program](cypherpunk_program.html)
- [Replacing TCP and UDP](names/TCP.html) - [Replacing TCP and UDP](design/TCP.html)

View File

@ -1167,7 +1167,7 @@ which could receive a packet at any time. I need to look at the
GameNetworkingSockets code and see how it listens on lots and lots of GameNetworkingSockets code and see how it listens on lots and lots of
sockets. If it uses [overlapped IO], then it is golden. Get it up first, and it put inside a service later. sockets. If it uses [overlapped IO], then it is golden. Get it up first, and it put inside a service later.
[Overlapped IO]:server.html#the-select-problem [Overlapped IO]:design/server.html#the-select-problem
{target="_blank"} {target="_blank"}
The nearest equivalent Rust application gave up on congestion control, having programmed themselves into a blind alley. The nearest equivalent Rust application gave up on congestion control, having programmed themselves into a blind alley.

View File

@ -1,7 +1,8 @@
<div class="button-bar"> <div class="button-bar">
<a href="./manifesto/vision.html">vision</a> <a href="../manifesto/vision.html">vision</a>
<a href="./manifesto/scalability.html">scalability</a> <a href="../manifesto/scalability.html">scalability</a>
<a href="./manifesto/social_networking.html">social networking</a> <a href="../manifesto/social_networking.html">social networking</a>
<a href="./manifesto/Revelation.html">revelation</a> <a href="../manifesto/Revelation.html">revelation</a>
</div> </div>

View File

@ -3032,6 +3032,12 @@ ssh and gpg key under profile and settings / ssh gpg keys, and to
prevent the use of https/certificate authority as a backdoor, require prevent the use of https/certificate authority as a backdoor, require
commits to be gpg signed by people listed as collaborators. commits to be gpg signed by people listed as collaborators.
Git now supports signing commits with ssh keys, so probably
we should go on the ssh model, rather than the gpg model,
but I have not yet deleted the great pile of stuff concerning gpg
because I have not yet moved to ssh signing, and do not yet know
what awaits if we base Gitea identity on ssh keys.
It can be set to require everything to be ssh signed, thus moving our It can be set to require everything to be ssh signed, thus moving our
identity model from username/password to ssh key. Zooko minus names instead of minus keys identity model from username/password to ssh key. Zooko minus names instead of minus keys