Vital to the correct operation of flud is the presence of a mechanism which prevents nodes from overconsuming resources. In flud, a node which uses more of other nodes' disk, bandwidth, or processing time than it provides to the flud network itself is considered to be overconsuming, or 'leeching,' resources.
Overconsumption is prevented in flud through two mechanisms: symmetric storage relationships and trust.
Symmetric Storage Relationships
Due to the decoupling of file data and metadata, nodes are allowed some choice in where they store data. They may also be selective about granting storage to other nodes. Since both a storee and a storer have free agency with regard to whether a storage operation will be performed, the transaction must be beneficial to both parties, and both parties must act fairly if they hope to continue the storing relationship. There must be symmetry among the parties.
Symmetry is provided by nodes trading equivalent storage resources. If node A wants to store 5MB of data on node B, it must also provide 5MB of storage for node B to store. Node B may not have 5MB of data to store at the time of the operation, but may wish to preserve some space for future storage operations, and so will agree to the trade. Of course, node B may have sufficient future storage already, and reject the offer for a trade, forcing node A to look for another trading partner. All this is handled automatically by flud through protocols designed to find optimal trading partners and through unforgeable future storage claims. Claims in flud follow the Samsara model: if a node wishes to reserve future storage, it generates an unforgeable claim as ci = sha256(Kr,i), where i is the sequence of the claim. Multiple claims can be concatenated to any size, and remote storage of these claims is handled by the storing node as any other data would be, with the exception that the owner of the claim may replace it at a future date with non-claim (file) data.
Retribution for dishonoring trading relationships is also an important piece of the protocol; agents must be held accountable for the improper use of their agency. If a node is not able to correctly answer verify operations on data it has previously agreed to store, the originating node is allowed to discard data that the cheating node has stored on it. Nodes are thus incentivized to honor their storage relationships. Note also that this punishes unreliable nodes; if a node cannot respond to verify requests because of network downtime or other reliability issues, other nodes will prefer not to enter into nor keep trading relationships with it (but also note that the protocols do not unfaily punish transient or temporary failures; data is discarded on a graduated, probablistic schedule, i.e., nodes which fail have a 'grace period' during which they may become reliable again before any of their data is destroyed).
In flud, all storage operations are incentivized. Nodes wishing to store data have an incentive to choose to store that data at other nodes which are reliable and trustworthy. Nodes which have the opportunity to store data are incentivized to be generous in exchange for immediate or future storage resources. All nodes are incentivized to act reliably and fairly, as not doing so has a direct impact on their ability to use external resources in the network.
Freedom of choice in storage relationships also allows nodes to limit the number of other nodes they deal with, and to keep track of these nodes' reliability and honesty. A history of previous actions can be expressed in a trust metric, which nodes use to rate their peers for both potential symmetrical storage operations, and for potential termination of storage relationships. More about the flud's trust system can be found here.
These trust metrics are also used to punish nodes which behave maliciously in terms of CPU or bandwidth usage.
The following are technical details on how to implement the negotiation protocol for storage. This should be moved into the STORE primitive description at some point.
The STORE primitive is used to negotiate storage trades. When a node wishes to enter into a trade with another node, it sends a STORE request (initial STORE requests don't contain data, as they must be resent with AUTH when the transaction is authorized). If the receiver of the STORE request does not wish to enter into the trade, it responds with a rejection. If it does wish to enter into the trade, it responds with the authentication challenge. The authentication request that comes back from the requester then contains a field showing that space is granted to the granter in exchange for storage. The sender of the request then must keep some state for when the granter of the request reciprocates with its storage request. This should be hard state, to be honored in case of a restart. One approach is to create an empty file for the granter in the storage area, named after the granter's nodeID and containing the amount of storage granted, as well as a timestamp. If subsequent grants are given before the grant is traded in for actual storage, the grant is simply expanded by the amount of space in the new grant.
If the file sent exceeds the amount of space previously negotiated, the receiving node drops the connection and deletes the portion of the file received.
When receiving STORE requests, these 'grants' are checked first. If one is present and it contains enough space for the request, it is automatically granted (after authentication), and the requestor is told in the response that this is in fulfillment of a previous grant (so that they don't grant yet more space locally for a new trade offer). The grant is decremented by the amount of the store, or removed if this fulfills all the grant space.