SIP: 3
Title: Remote Notifications and Triggers
Author: Lily Anne Hall
Status: Active
Type: Standard
Created: 2016-08-10
Protocol extension that gives nodes the capability to opt-in to passive generic notifications from other nodes and services as well as authorize certain types of notifications and senders to trigger behavior within client applications implementing this SIP.
Applications implementing the Storj protocol are designed for a variety of use-cases and the resulting relationships between applications on the network can become far more nuanced than a simple 1-1 transaction. To illustrate this we cite the introduction of Storj Bridge as a broker between a “thin client” renter and a farmer.
The introduction of a broker brings with it the need for arbitration measures in the event that one of the parties acts outside of the terms of the storage contract. Given that it is difficult to predict how future systems will implement these measures, we propose a generic notification format to allow nodes to invent meta-protocols that may trigger application specific behaviors.
Organizations (like Storj Labs) may be responsible for collecting information from farmers due to regulatory compliance and tax purposes when issuing payouts above a certain amount. To notify a farmer when that information needs to be provided, the Storj Share client application could opt-in to receive triggers from Storj Labs to show a prompt when the owed amount exceeds a threshold.
Contract brokers, like the operator of a Storj Bridge, need to know when a data transfer between two parties succeeds or fails so that it can appropriately track the performance and reliability of the farmers it selects in the future. The method for implementing these proofs and verifications is outside the scope of this SIP, however it is likely that there will be a number of future strategies for carrying out this work. To avoid the need for a new strategy to become accepted into the core protocol, nodes that support the given strategy can use these custom triggers to request and deliver the information needed to carry them out.
This SIP proposes the addition of a new protocol extension to add a TRIGGER
RPC message for the purpose of sending a custom payload to a consenting node on
the network. The intent is to allow nodes to establish a relationship with each
other that grants one or both parties license to “trigger” some behavior in the
software running on the other party’s host.
This specification does not rigidly define any application behaviors - it’s only purpose is to define the message structure and rules surrounding how an application-specific trigger should be issued and handled. Like the rest of the Storj Protocol, this message is formatted according to the JSON-RPC 2.0 definition and includes all of the standard required Storj protocol parameters.
In addition to the required parameters, the TRIGGER
method requires:
Below is a possible example of a trigger that might be sent from a farmer to a third party bridge node to prove that she successfully received all of the data from a renter whose storage contract was brokered by the bridge:
{
"method": "TRIGGER",
"params": {
"behavior": "ProveUploadReceipt",
"contents": {
"rmd160sha265": "cebaa98c19807134434d107b0d3e5692a516ea66",
"sha1whirlpool": "43144cac0a406eb72f4d3be7292438ac15725e1d"
},
"contact": {
"address": "10.0.0.2",
"port": 1337,
"nodeID": "89cc3ddb4209c6e7e301c10c0257adf4fd85f253",
"protocol": "0.7.2"
},
"nonce": 1455216323786,
"signature": "3045022100de2e162d017a1e9d0ebfe2a94df3fc847b68281a9882..."
},
"id": "7b6a2ab35da6826995abf3310a4875097df88cdb"
}
In the example above the renter has provided the bridge with two different hashes of the data it wishes to upload. The bridge publishes a contract to the network that is keyed on the RMD-160(SHA256(data)), so that hash is known. Once the bridge negotiates a contract with a farmer and brokers the data channel, the farmer must prove to the bridge that she received all of the data from the renter by calculating the second secret hash from the transferred data.
Once the secret hash is calculated, the farmer sends a TRIGGER
to the bridge
(who has authorized the trigger previously and is expecting it) that includes
both hashes. This would trigger a behavior in the bridge to update it’s record
that the farmer did receive the data.
Note that the
ProveUploadReceipt
behavior is not part of the Storj protocol, but is an application-specific trigger that must be implemented by the both the client software and the target node.
Likewise, the response to a trigger message follows the Storj protocol and can include any arbitrary response values:
{
"result": {
"message": "Upload proof successfully recorded",
"contact": {
"address": "api.storj.io",
"port": 4001,
"nodeID": "98dc026fa01ae26822bfb23f98e725444d6775b0",
"protocol": "0.7.2"
},
"nonce": 1455216323786,
"signature": "904502207e8a439f2cb33055e0b2e2d90e775f29d90b3ad85aec0c..."
},
"id": "7b6a2ab35da6826995abf3310a4875097df88cdb"
}
To further clarify the point, the TRIGGER
message format and the advisements
for handling should become an extension to the Storj protocol, but the types
of behaviors and their associated content may remain application-specific,
or alternatively, be proposed as their own SIP if desired. The trigger RPC acts
as a sort of envelope for any number of sub-protocols that tenants of the
Storj network may implement and adopt without the need to reach community
consensus as the handling of triggers is up to each individual application.
Clients choosing to implement support for triggers should take care to provide a safe interface for enabling triggers. To ensure that application behavior can only be triggered by parties that have been explicitly authorized to do so, the minimum best practices to follow are:
nodeID
and behavior
Assuming the use of Storj Core, authorizing a trigger might resemble the following:
var storj = require('storj');
var network = new storj.RenterInterface(options);
// When a contract is negotiated and data channel is authorized...
network.triggers.add(farmerNodeId, {
ProveUploadReceipt: function(msgParams, replyToSender, destroyTrigger) {
var secretHashExpected = somehowGetSecretHash();
var secretHashReceived = msgParams.contents.sha1whirlpool;
// If the provided hash doesn't match, then respond with error and destroy
// this trigger handler (to be added again when needed)
if (secretHashReceived !== secretHashExpected) {
replyToSender(new Error('Invalid secret hash provided'));
return destroyTrigger();
}
// Update our record of the farmer's receipt of the data and respond with
// an acknowledgement
somehowUpdateThisFarmersRecord();
replyToSender(null, { message: 'Upload proof successfully recorded' });
destroyTrigger();
}
});
This example demonstrates a safe interface for accepting triggers for a couple of reasons: