Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Documentation #41

Merged
merged 15 commits into from
Nov 5, 2021
Binary file added docs/contracts_sequence_diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
350 changes: 350 additions & 0 deletions docs/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,350 @@
# Sylo Network Phase 2 Smart Contracts

**Protocol and Economic Incentives For Decentralized Communications
Infrastructure**

Paul Freeman <paul@sylo.io> </br>
John Carlo San Pedro <john@sylo.io> </br>
Joshua Dawes <josh@sylo.io> </br>

## Table of Contents ###########################################

* [Introduction and Background](#introduction-and-background)
* [Probabilistic Micropayments](#probabilistic-micropayments)
* [Micropayment Tickets](#micropayment-tickets)
* [The Event Relay Protocol](#the-event-relay-protocol)
* [Nodes](#nodes)
* [Staking](#staking)
* [Delegated Staking](#delegated-staking)
* [Scanning for a node](#scanning-for-a-node)
* [Epochs](#epochs)
* [Rewards](#rewards)
* [Ticket decay with time](#ticket-decay-with-time)



## Introduction and Background ###########################################

Sylo Nodes are an application that anyone can run, to help provide network
services to Sylo users in a truly private, fully decentralised way.

Sylo Nodes provide Incentivised Event Relay to applications and users of the
Sylo Network.

Network traffic is allocated to Nodes based on the amount of Sylo Tokens staked.
This information is saved on-chain in a Stake Directory.

Sylo Tickets are the payment mechanism on the network; they are used to pay for
very small units of work, off-chain.

An Epoch is the main unit of time in the Sylo Network, measured as a number of
on-chain blocks.

The Sylo Network is currently in [phase two](spec.md#phase-two) of its
deployment.


## Probabilistic Micropayments ###########################################

Sylo Tickets are probabilistic micropayments used for rewarding Sylo Nodes for
their work. Because Sylo Nodes can be run by anybody, they need a financial
incentive to perform relay - the Sylo Network cannot rely on altruism and still
provide a reliable, high quality service.

A single relay is so inexpensive that the blockchain transaction costs
associated with paying per message would be unreasonably high. Because of this,
we need a way to exchange value for every relay performed, without relying on an
on-chain transaction each time.


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we please include a overview of messages? Some example of what is being passed between client and sender, and the limitations.

  • Message Length Limitation?
  • Do longer messages cost more?
  • Message Type limitation? Do we allow images/movies/audio? Do they cost more?
  • Do we break up messages?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Something like that could be included in the spec, or may be we need to write up a more technical spec for the Event Relay protocol itself (the spec included in this repo is more focused on the contracts).

@paul-freeman Do you have any metrics for the events?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The protocol currently has a message size limit of 2048 bytes (which I took from Firebase or APN, I think). This is for the opaque bytes in the actual event (not including the metadata). Having a limit is important since storage is a significant part of the service. But the actual value we settle on could be changed.

We are really assuming that messages will usually be small events or pointers to larger data on IPFS.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


### Micropayment Tickets

Instead of transferring currency every transaction, Sylo Tickets are a
probabilistic payment. A ticket is sent along with every transaction, but not
every ticket can be redeemed for currency.

Tickets only have a small percentage chance of winning, which means that most
Tickets are not winning. As a result, the vast majority of relay transactions
can occur without an on-chain transfer of funds. Neither the sender nor the
receiver of a ticket knows whether a ticket is a winning ticket until the relay
it is paying for is completed.

If the Ticket wins, it is worth it's full face value, and can be submitted
on-chain to claim that full payout for the sender's funds in escrow. These funds
are locked in escrow for a substantial period of time, to ensure that Tickets
redeemed in the future will still have funds available to redeem winnings from.

In addition, the owner of the escrow must post penalty escrow, which is burned
if the payment escrow is ever empty when a ticket is redeemed. This penalty
escrow prevents the owner of the escrow from emptying their own escrow balance
into another wallet, as a means of avoiding paying nodes for their work - an
attack known as front-running.

The value of a Ticket is it’s expected value: the Ticket's “face value” that is
paid out if it wins, multiplied by its probability of winning. By choosing
values of these two parameters, these probabilistic micropayment Tickets can be
used to pay for arbitrarily small units of work in a gas-efficient way.

## The Event Relay Protocol ###########################################


### Asynchronous Event Relay

Peer Alice wishes to send a packet to peer Bob via Bob’s node.

Both Alice and Bob use the Sylo network intermittently - in the worst case, they
may never be online at the same time as each other. This means that Alice is
unable to check in with Bob later, to ensure that her relays were delivered.
Alice needs a protocol that is "fire and forget" - once she has left a relay
request with Bob's node, Alice needs to be sure that the node will do it's best
to deliver her message to Bob, with no further input from her.

This means that Alice needs a trustless method of setting payment aside for
Bob's node, to be claimed once the relay is delivered. This is accomplished by
signing a ticket, which will pay out from money held in escrow in a smart
contract when the ticket is redeemed.

Alice also needs a way to release payment to Bob's node only once Bob has
received the relay packet. This is the difficult part, because Alice may never
have the opportunity to learn anything more about the outcome of her relay
request.


In reality, only two actors know when the relay packet has been delivered - Bob,
and Bob's node.

Bob's node cannot be trusted to be truthful about delivery, because it has a
financial incentive to lie, and claim payment for delivery without doing the
work. This is true in both the one-off case, and in the iterative game, where
the node’s optimal strategy is a mixed strategy that includes some cheating.

In general, Bob also cannot be trusted to unlock Alice’s payment for Bob's node.
Because Alice is sending to Bob, Bob and Alice may have some relationship with
one another, and so Bob is assumed to have some incentive to refuse to unlock

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible for a malicious third party to cut Bob's ability to confirm some messages have been received? I.e. Firewall allowing Bob to receive messages but not confirm them.

Possible mitigation: could the protocol hide information about sending party till delivery confirmation? I.e. Bob must confirm receipt of a message before being able to unlock information about the sender?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. In general, everything is encrypted (using Bob's public key). In the case of sender information, it would be encrypted by libp2p's p2p encryption mechanism (sort of like TLS). So the malicious third party would need access to the private key to do anything.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Libp2p streams are bidirectional, so you can't send Bob a message and then have him unable to respond. Connections are made by the receiver (like push notification services), so if Bob can't connect to his node, he needs to pick a different one (a feature that will be available when we have users on the network).

Alice's payment.

However, Bob only has one node - all of Bob’s relay traffic, from a variety of
peers, comes through that node. This gives Bob's node a mechanism to punish Bob
with, if Bob withholds payment - blacklisting.

By threatening to withhold Bob's future traffic as punishment for bad behavior,
Bob can be trusted to unlock Alice’s payment - the small incentive Bob has to
help any individual relay sender is outweighed by Bob's desire to remain in good
standing with his node.

This allows Bob to act as the service completion oracle. When Alice leaves a
relay request with Bob's node, it contains a secret from Alice that is encrypted
for Bob. Once Bob receives the relay, he decrypts the secret and passes it back
to the node, allowing the node to claim payment.


Blacklisting also prevents abuse in the case where Alice and Bob are the same
financial entity, with shared cost incentives. The goal in this case is to limit
the amount of free relay that Alice and Bob are able to extract from the
network. Bob's node is able to see which relays do not result in valid payment,
and adjust the reputation of both Alice and Bob accordingly. After a small
number of failures to deliver payment, Bob's node can blacklist Bob, and
decrease Alice’s reputation as a relay sender, eventually resulting in the node
ignoring relay requests from Alice altogether.

In the worst case attack, the attacker spins up many alternative receiving peers
(for free) that scan to a variety of different nodes, sending relay to each of
these peers in turn. This continues until Alice’s reputation has been “spent”
with all nodes on the network. With 100 nodes on the network, and 50 relays
worth of non-payment evidence required for the node to blacklist A (an allowance
for legitimate connection issues), this results in 5000 free relays before all
nodes will turn away Alice’s requests - about $0.00005 worth of relay, based on
monthly node operating costs and throughput.

After this, Alice’s escrow must be moved to another wallet to begin the process
again, which takes time for the escrow to unlock, and has an associated gas
cost. This gas cost puts an effective price on the “free” relay obtainable this
way, and makes the strategy of repeatedly spinning up new sending peers worse
than just paying for relay in the first place.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to do a premeditated multi wallet attack?

Escrow is sent to a reasonable number of wallets beforehand, say ~100-100000, 3 months before attack, at the best gas price possible. Thereby mitigating escrow unlock time (to an extent).

Ideally, would be better if senders whom do not have much recent-activity be trusted less? In addition to having escrow unlock times - but perhaps reducing the time it takes to unlock.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this. My intuition tells me that kind is still too expensive to be worth it.

@JoshD641 would have a better idea though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Escrow is sent to a reasonable number of wallets beforehand, say ~100-100000, 3 months before attack, at the best gas price possible. Thereby mitigating escrow unlock time (to an extent)."

You can start sending messages immediately once escrow is deposited - it's just that there's a delay between calling unlock() and the actual withdrawal of funds back to your wallet. The locked escrow has no Return On Investment, and can't be used for anything at all (even sending new messages) while it's unlocking, so there's a small opportunity cost associated with those funds being locked away for that time (compared to, say, putting that capital into a yield farm contract and earning 5% - those potential earnings are lost).

The more important cost is the gas associated with the token transfer from wallet to escrow, and then from escrow to wallet again to recover the funds. This gas cost scales linearly with the number of wallets (2 txns per wallet), which means that so long as the value of the relay "stolen" by one sender A is less than 2x the transaction gas cost, it doesn't make sense to do this attack. That limit is easy enough for a node to calculate, and the nodes can set their tolerances for non-delivery before they start blacklisting accordingly - though that isn't implemented yet on the node side.

In general you can't really measure the recent activity of a sender from a node in a reliable way. Node A doesn't know how many messages have been sent to the other nodes by this sender, and only a tiny fraction of messages result in on-chain evidence (a winning ticket), so you can't refer to that either.



### Nodes

Sylo Nodes are an application that anyone can run on their own server, to help
provide network services to Sylo users in a truly private, fully decentralised
way. Sylo Nodes currently provide incentivised event relay and will provide
additional services in future.

Sylo Nodes are financially incentivized to provide and maintain good service.
For good service they receive payouts via Sylo Tickets at a service price, which
is set each epoch.

Sylo Nodes require Stake before they are eligible to receive work to do.

## Staking ###########################################
In order to be allocated work on the Sylo Network, Sylo Nodes must have SYLO
Tokens staked against them on-chain. These tokens are still owned by the person
who staked them, but are locked in a smart contract for a period of time.

Sylo Node runners provide Node Staking. Sylo users can stake toward Nodes via
Delegated Staking.

Using the total amount staked to Nodes across the Network, the Stake Directory
is updated each epoch. The Stake Directory is used by the stake-weighted scan

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A major geographic network event could occur, disintegrating a high-stake-amount-node service for a period of time.

Would be nice to have quality of service metric in the future as well? This may be complex to implement - some sort of consensus algorithm that looks at number of successful receipts per node / weighted by sender reputation, (among other things to prevent cheating) for the last hour.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep you're right.

In the future, there will be an on-chain function that could be used to submit cryptographic evidence of work based on the amount of stake the Node has. That will then be used to determine if Nodes are performing as expected. It's not needed for phase 2 of the Sylo Network, so isn't implemented in the contracts or included in the spec, but there is a plan for it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function for users to know which Node is assigned to which user.

Staking is important because it ensures that the owners of nodes have a
financial stake in the overall network. Staking ensures that node owners’
incentives align with the network as a whole - taking actions that add value to
the network adds value to their stake, and taking actions that harm the network
reduces the value of their stake.

Stakers can initiate the withdrawal of their stake at any time, but the stake is
locked in for a period of time before it is withdrawn, to ensure that
speculators cannot take short-term control of the network’s services in order to
manipulate them for profit.

Staking against a node does two things:
- It increases the amount of traffic that the node receives from the network,
increasing that node’s income.
- It entitles the owner of the stake to a portion of the payout from each
winning ticket that the node redeems.


### Delegated staking

A node can be staked by the Node’s owner, or by other holders of the SYLO token
- the latter is known as delegated staking.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can a node owner prevent other holders from staking their node? Might be good to clarify this here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There will be some restrictions that can be set by the Node, for example, requiring delegated stakers to stake a minimum amount of SYLO. But currently not implemented.


- A percentage of every winning ticket is paid to the node owner first - the
remaining payment is then shared among all stakers, delegated or not, in
proportion to the amount they have staked.

### Scanning for a node

When a Sylo Node has SYLO Token staked against it, this is recorded in the stake
directory.

The stake directory is an on-chain data structure that holds information about
which Sylo Nodes are staked to provide services in the Sylo Network, and how
much SYLO Token is staked against each node.

Any peer with access to the blockchain can see the full list of staked Sylo
Nodes, and use the stake directory to determine which node they are assigned to
for the current epoch, by a process known as scanning.


When a peer wants to identify their Sylo Node, they query the blockchain using a
“scan” function. This function takes the peer ID as input, and pseudo-randomly
assigns them a Sylo Node.

#### Scanning Process

The scan function does this using the following steps:

- Compute the hash of the peer ID, concatenated with the current epoch number

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the peerID of the node or the peerID of the message recipient/sender?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PeerId of the recipient. I'll clarify it.

and the peer’s on-chain channel number. This hash can be mapped to a
pseudo-random number between 0 and 1. It is unique to each peer, and changes
each epoch.
- Multiply the total amount of stake in the stake tree by this random number, to
produce a number between 0 and total_staked.
- Binary search the stake directory to find which node is associated with the
number produced above, returning that node.


This scan function allows anyone who knows your peer ID to efficiently identify
your node for a given Epoch, and therefore identify which Sylo Node will accept
a relay message for you.

It also ensures that Sylo Nodes are able to tell when a relay message is
unlikely to be collected. If the recipient scans to a different Sylo Node, then
it is unlikely that the recipient will come to this Node to collect it. This
incentivizes nodes to only provide services to peers that “scan” to them, and
incentivizes peers to only use their assigned node.

SYLO holders can alter their staking arrangements at any time - however, to
avoid peer’s Sylo Nodes changing mid-epoch, these changes only take effect on
the stake directory when the epoch changes. This ensures that a peer’s Sylo Node
is consistent for the entire epoch.

Because the stake directory is updated each epoch, and because the scan function
takes the epoch number as input to it’s hash, each peer’s Sylo Node is
randomized each epoch. This provides additional security against traffic
analysis, and ensures that nodes receive work in proportion to their stake over
the long run, regardless of epoch-by-epoch traffic variation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that high stake nodes get too much traffic? (I think yes but small).

Is it possible for a node to report that it is too busy? If so, how does fallback / chain of backup nodes work?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that high stake nodes get too much traffic? (I think yes but small).

Yes it's possible. In phase 3, Nodes will be able to set a maximumDelegatedStake value that will restrict the amount of stake that will be used in the Stake directory. This would prevent the case of a Node suddenly receiving a lot of delegated stake, and then receiving too much traffic.

Is it possible for a node to report that it is too busy? If so, how does fallback / chain of backup nodes work?

The economic incentives should hopefully keep most nodes performing. There is also a section on Channels in the future works part, which briefly discusses how a user could switch nodes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since "percent stake" is relative, the work assigned to a node can change without any input from them (i.e. a bunch of other nodes unstake).

There is really no way to prevent such changes in the network, even with a maximum delegated stake value. Node operators will probably need to build in a safety margin to prevent being at risk of slashing. Ideally we would add features to the nodes to allow for horizontal scaling during times of heavy load, but such features will be down the road a bit.


## Epochs ###########################################

An epoch is the main unit of time in the Sylo Network, measured as a number of
on-chain blocks. At the start of each epoch, the stake directory is updated
based on all staking changes made during in the previous epoch, and each peer is
randomly reassigned to a Sylo Node.

Epochs have several benefits:

- It reduces the gas overhead of running the on-chain components of the network.
In particular it allows for an [optimization of the reward distribution
calculation](spec.md#reward-calculation-and-cumulative-reward-factor).
- It also provides a predictable time for changes to come into effect,
simplifying the process of peers monitoring the network for changes - e.g.
has my Node changed?


## Rewards ###########################################

Rewards gained from redeeming tickets are held in escrow, and will continue to
accumulate until either the Node or a delegated staker withdraws their rewards.
On redeeming a ticket, a portion of the reward is allocated to the Node itself
as direct payment for running the Event Relay service. The remaining portion is
then split amongst the Node's stakers on a pro-rata basis. Unclaimed staking
rewards are also automatically reconsidered as part of the Node's total stake,
bringing the Node additional network traffic until that stake is claimed.

Further detail of the staking rewards are calculated over multiple epochs can be
found in the [technical
specification](spec.md#reward-calculation-and-cumulative-reward-factor).

## Ticket decay with time ###########################################

To incentivize relays to be delivered as soon as possible, the mechanism that
pays out winning Sylo Tickets is modified, so that the [probability of a Ticket
winning decreases as the time since the relay request was made increases]
(spec.md#calculateWinningProbability).

The current blockchain block number is included in each Sylo Ticket when the
ticket is issued, and signed by the ticket's sender. This block number defines
the start of the ticket's valid duration, and is used to measure time. This
trustless measure of elapsed time is then used to modify the ticket's
probability of winning, decreasing the ticket's expected value as time goes on.

This creates a direct incentive for Sylo Nodes to perform relay as soon as
possible, to maximize their income from performing the service. It similarly
incentivizes them to invest in throughput and uptime improvements, so that they
can claim tickets as soon as possible, and earn more from the work that they do.

## Future Work

### Stake Redistribution

To further incentivize the performance of Nodes, a stake redistribution
mechanism will be introduced that punishes Nodes for failing to deliver
event relays.

Nodes that perform relay accumulate evidence of completing those relays, in the
form of cryptographically signed "receipts" from sending peers, which they keep
until the end of the epoch. These "receipts" can submitted to the blockchain
as proof of servicing relay requests. The number of receipts that must be
submitted will be proportional to the total stake against the Node, as more
stake indicates that the Node should be receiving more traffic. Failing to
submit sufficient evidence will result in the Node's stake being redistributed
to other high performing Nodes.

This mechanism is still a work in progress and will not be implemented for
phase two as the network traffic will not involve real users.

### Channels

Economic incentives should generally prevent any one Node from offering
a substandard relay service. However, in the case that a user wishes to
switch service providers, the user can set an on-chain integer value
known as a `channel`. Senders can retrieve the `channel` value for a user,
and include that value when computing the hash used in the
[scanning process](#scanning-process). The default channel value would be
0, but any non-zero value would result in a different Node being returned
from the scan output.
Loading