witnet / witnet-solidity-bridge

Witnet Bridge for EVM-compatible blockchains
MIT License
60 stars 24 forks source link

Explore stampery-alike services as a Witnet use-case (take 1) #272

Closed guidiaz closed 2 years ago

guidiaz commented 2 years ago
guidiaz commented 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.

guidiaz commented 2 years ago

Off-chain apps relying on service (a):

guidiaz commented 2 years ago

On-chain dapps relying on services (a+b):

guidiaz commented 2 years ago

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 }
guidiaz commented 2 years ago

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.

guidiaz commented 2 years ago

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);
}
guidiaz commented 2 years ago

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);
}
guidiaz commented 2 years ago

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.