-
Notifications
You must be signed in to change notification settings - Fork 1
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
Changes from 12 commits
f511693
293947d
cf67e52
74669d4
1ec9245
6968b74
dc16c36
8b45ab4
6c53afc
f679956
048e69c
9231bb8
e93d43a
5196b57
6b0c4ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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. | ||
|
||
|
||
|
||
### 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you'd like to look into about our ideas for how that might work, you can have a read here: https://app.gitbook.com/o/-MdydbpBeh4lPp-v7Xwz/s/-Mdyc49lpdv1XGu_u5XS/c/-MeNz7KBWPJ0K5DJS5hi/how/incentivised-event-relay/incentivizing-performance |
||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes it's possible. In phase 3, Nodes will be able to set a
The economic incentives should hopefully keep most nodes performing. There is also a section on There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 (thespec
included in this repo is more focused on the contracts).@paul-freeman Do you have any metrics for the events?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/dn3010/go-sylo-node/blob/5bc1504d7dcfc39a0adec9a84100ea711d24be7b/events/events.go#L32