Closed guidiaz closed 2 years ago
Let's think of a permissionless WebNotary smart contract (powered by Witnet) in which anybody could openly:
(a) request a proof of current existence of any public file on the www; (b) subscribe knowledge of existence of any public file on the www, at a specific moment in time.
Off-chain apps relying on service (a):
On-chain dapps relying on services (a+b):
How the Javascript/Radon script could look alike:
import * as Witnet from "witnet-requests"
// This is the Witnet.Request object that needs to be exported
const request = new Witnet.Request()
.addSource(new Witnet.HttpGetBinarySource(
"https://witnet.io/witnet-whitepaper.pdf", {
"Content-Type": "application/octet-stream",
"Content-Disposition": "attachment",
}
)
.hash() // => SHA2-256
)
.setAggregator({ reducer: Witnet.Types.REDUCERS.mode })
.setTally({ filters: [[Witnet.Types.FILTERS.mode]], reducer: Witnet.Types.REDUCERS.mode })
.setQuorum(
16, // required number of witnesses
66, // two-third consensus required
)
.setFees(
10 ** 9, // 1 wit reward per valid witnessing
10 ** 9, // 1 wit reward per mined transaction
)
.setCollateral(
5 * 10 ** 9, // 5 wits collateral per witnessing node
)
// Do not forget to export the request object
export { request as default }
Although multiple hash functions are considered within the Witnet protocol, as for today only the SHA2-256 is implemented by the official witnet/witnet-rust node distribution. This hash function is currently refered as hash()
within the rad2sol
at the witnet/witnet-requests-js repo, and it can only be applied as a reducer to TYPES.BYTES
data types.
While it would be nice to have more hash functions implemented, the SHA2-256 seems just right for the purpose of this WebNotary idea. However, the rad2sol
current implementation lacks implementation of so-called "binary sources". As refered from the example above, rad2sol
should at least export implementation of Witnet.HttpGetBinarySource
, optionally accepting extra headers, or establishing by the default the ones included in the example. An additional nice-to-have feature would be to enable a way to specify the slice of bytes to download from the URL, although this would imply additional implementation on the witnet-rust side.
Provided that some formal validation on input URLs is performed on-chain, a fully trustless WebNotary seems quite feasible, requiring no governance of any kind. A first-temptative interface could well look like this:
interface IWebNotary {
event Stamping (bytes32 indexed urlHash, uint256 queryId);
event Subscribed(bytes32 indexed urlHash, address indexed subscriber, uint256 versionBlock);
function stampUrl(string calldata _scheme, string calldata _fqdn, string calldata _path) external payable;
function stampUrl(string calldata _url) external payable;
function subscribe(string calldata, bytes calldata) external payable;
function getFqdnPaths(string calldata) external view returns (uint256);
function getFqdnPath(string calldata, uint) external view returns (string memory);
function getUrlStrings(string calldata) external view returns (string memory, string memory, string memory);
function getUrlVersions(string calldata) external view returns (uint256);
function getUrlVersionBlock(string calldata, uint256) external view returns (uint256);
function getUrlSubscribers(string calldata) external view returns (uint256);
function getUrlSubscribersAfter(string calldata, uint) external view returns (uint256);
function getUrlSubscribersBefore(string calldata, uint) external view returns (uint256);
function getUrlSubscriber(string calldata, uint256) external view returns (address);
function getUrlSubscriberAfter(string calldata, uint, uint256) external view returns (address);
function getUrlSubsriberBefore(string calldata, uint, uint256) external view returns (address);
function getUrlSubscriptionBlock(string calldata, address) external view returns (uint256);
function getUrlSubscriptionBlockAfter(string calldata, uint, address) external view returns (uint256);
function getUrlSubscriptionBlockBefore(string calldata, uint, address) external view returns (uint256);
function getUrlSha2(string calldata) external view returns (bytes32);
function getUrlSha2After(string calldata, uint) external view returns (bytes32);
function getUrlSha2Before(string calldata, uint) external view returns (bytes32);
function isUrlSubscriber(string calldata, address) external view returns (bool);
function isUrlSubscriberAfter(string calldata, uint, address) external view returns (bool);
function isUrlSubscriberBefore(string calldata, uint, address) external view returns (bool);
function isValidUrl(string calldata) external pure returns (bool);
function lookupUrl(bytes32) external view returns (string memory);
function lookupUrlHash(string calldata) external pure returns (bytes32);
function parseUrl(string calldata) external pure returns (string memory, string memory, string memory);
function toBase58(bytes32) external pure returns (string memory);
}
While a Witnet-based implementation would then look like:
abstract contract WitnetWebNotary
is
UsingWitnet,
IWebNotary
{
bytes32 public constant WITNET_RADON_SCRIPT_HASH = 0xDeadBeefDeadBeefDeadBeefDeadBeefDeadBeefDeadBeefDeadBeefDeadBeef;
struct Url {
UrlStrings strings;
uint256[] versions;
}
struct UrlStrings {
string fqdn;
string path;
string scheme;
string url;
}
struct UrlStamp {
address[] subscribers;
mapping (address => uint256) subscriptions;
UrlWitnetStamp witnet;
}
struct UrlWitnetStamp {
uint256 queryId; // ==> queryTxHash
bytes32 tallyTxHash;
}
mapping (bytes32 => bytes32[]) internal __fqdnPaths;
mapping (bytes32 => UrlStrings) internal __urlStrings;
mapping (bytes32 => mapping (uint256 => UrlStamp)) internal __urlStamps;
constructor (WitnetRequestBoard _witnet)
UsingWitnet(_witnet)
{
// TODO
}
function estimateStampValue(uint256 _evmGasPrice, uint256 _witUnitaryReward) external view virtual returns (uint256);
function getUrlWitnetStamp(string calldata) external view virtual returns (UrlWitnetStamp memory);
function getUrlWitnetStampAfter(string calldata, uint) external view virtual returns (UrlWitnetStamp memory);
function getUrlWitnetStampBefore(string calldata, uint) external view virtual returns (UrlWitnetStamp memory);
function getUrlWitnetErrorCode(string calldata) external view virtual returns (Witnet.ErrorCodes);
function getUrlWitnetErrorCodeAfter(string calldata, uint) external view virtual returns (Witnet.ErrorCodes);
function getUrlWitnetErrorCodeBefore(string calldata, uint) external view virtual returns (Witnet.ErrorCodes);
function getUrlWitnetErrorMessage(string calldata) external view virtual returns (string memory);
function getUrlWitnetErrorMessageAfter(string calldata, uint) external view virtual returns (string memory);
function getUrlWitnetErrorMessageBefore(string calldata, uint) external view virtual returns (string memory);
}
A more advanced idea would be that of having a zero-knowledged web notary, or WebNotaryZK, where the hash of a public file is not saved on-chain, but some kind of cryptographic seed (would it be ever exist) that enables subscribers to actually prove knowledge of a file digest without actually revealing it on-chain. In other words, a smart contract in which anybody could openly:
(a) request a proof of current existence of any public file on the www; (b) subscribe a proof of knowledge of the actual contents of any public file on the www, at a specific moment in time.
Whether this is even feasible from a cryptographic point of view, and what primitives/reducers could be lacking within the Witnet protocol, should be matter of study in a separte issue, though.