Skip to content

Getting started

Angelo Laub edited this page Jun 15, 2019 · 10 revisions

Installing the privEOS client

If you're adding privEOS to an existing npm project:

npm install priveos --save

or

yarn add priveos

If you want to use it in the browser, you can include from jsdelivr CDN like this:

<script src="https://cdn.jsdelivr.net/npm/priveos@latest/dist/browser/priveos.js" crossorigin="anonymous"></script>

Using privEOS

const Priveos = require('priveos')

const config = {
  dappContract: 'mydappcontract',
  priveosContract: 'priveosrules',
  httpEndpoint: "http://127.0.0.1:8888",
  chainId: 'cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f',
  brokerUrl: 'http://127.0.0.1:4401',
  
  // for development, you can hard code your private key
  privateKey: '5JkTGn3D9GRQnsviY9n5pz9Bvx6KqCQq5XDUSEHR3tM6ETWaqY7', // only for development
  publicKey: 'EOS64uPJNTB92xtKvfEaSEXZnvW45U71vGaRvXzMNxN4bzsuLf3Cm', // only for development
  
  // for production use, generate a one-time ephemeral key and use that one together with scatter
  ephemeralKeyPrivate: "5JwFYEMuig875HKHTtQaUjDvto5eN6T8YS6NvFaYHmfyDpVbcqL",
  ephemeralKeyPublic: "EOS59hAE31XCLoBscnWa8QdDWp6dZBA343tiV9AjsMydWscv9gaE9",
  eos: eos_client_instance, // for production use
}
const priveos = new Priveos(config)

The parameter dappContract should be the EOS account where your application smart contract is deployed. priveosContract should always be "priveosrules" if you're using a public network like Jungle or the EOS mainnet. httpEndpoint is the EOSIO API endpoint that you want to use. If you're using a local testnet for development, it's normally "http://127.0.0.1:8888". If you're using the EOS mainnet, you can use any public API endpoint that you would like. Under chainId, please specify the chain ID of the chain you're connecting to. If using a local testnet, you can get it by cleos get info. If you're using the EOS mainnet, it's always "aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906". The brokerURL is the public privEOS endpoint that you're going to use to connect to the privEOS network. You can check the privEOS Monitor to see a list of available endpoints. The eos_client_instance should be the EOS client instance returned by scatter.eos(network, Eos). Please refer to the scatter documentation on how to integrate Scatter into your app.

Submitting a file to privEOS

Our instance of privEOS should now be configured and we are ready to use it now. You can generate an encryption key and encrypt a message like this:

const { TextEncoder, TextDecoder } = require('util') // Node.js only
const { TextEncoder, TextDecoder } = require('text-encoding') // React Native or older Browsers only

const message = "Secret message"
const keyUint8Array = Priveos.encryption.generateKey()
const messageUint8Array = (new TextEncoder()).encode(message)
const encryptedUint8Array = Priveos.encryption.encrypt(messageUint8Array, keyUint8Array)

Priveos.encryption.encrypt generates a unique nonce for every message and encrypts and authenticates the message using tweetnacl-js. This is just a suggestion to make it easy for developers of course. You are welcome to use your favourite encryption library and algorithm.

The variable encryptedUint8Array now contains the encrypted bytes of your message. You can now save the encrypted message wherever you want (for example on IPFS or AWS). If you want to store it on the blockchain, a good way to encode the data to a string would be base64 or base58. Once we have safely tucked away the encrypted data, we can submit the key to privEOS like this:

const user = "eos_account_name_of_user_who_owns_the_data"
const identifier = "file_identifier_unique_to_your_app"
priveos.store(user, identifier, key).then((tx) => {
  console.log("Success!")
})

Now, depending on the use-case and the design of your application, you can now either delete the key from the device of the user, or keep it. If the user should be able to access the file later on, you can retain the key on the user's device. Alternatively, the user can, if your smart contract allows it, request access to the file at a later time, and retrieve the key again from privEOS.

Retrieving a file from privEOS

priveos.accessgrant(user, identifier).then((txid) => {
  console.log("We have sucessfully been granted access")
  priveos.read(user, identifier, txid).then((keyUint8Array) => {
    console.log("Awesome, we have received the decryption key")
    // retrieve the encrypted file and decrypt it
    const encryptedMessageUint8Array = get_from_ipfs(identifier)
    const decryptedMessageUint8Array = Priveos.encryption.decrypt(encryptedMessageUint8Array, keyUint8Array)
    const messageText = (new TextDecoder()).decode(decryptedMessageUint8Array)
    console.log(`The recovered secret message is ${messageText}`)
  })
})

Adopting the privEOS Smart Contract Protocol

We have now learned how to use submit files and request access to files from the client. Since we have not yet implemented a smart contract, anyone can will get access to all files if they request it. Let's change that and implement some access rules.

In order to implement the privEOS Smart Contract Protocol, the only thing you need to do is to add and implement the accessgrant notification action to your contract. It will be called every time a user calls priveos.accessgrant. The following example is taken from our data marketplace demo application:

ACTION example::accessgrant(const name user, const name contract, const std::string uuid, const public_key public_key, const symbol token, const bool contractpays) {
  require_auth(user);
  
  const auto& file = get_file_byuuid(uuid);
  if(user != file.owner) {
    perms_table perms(_self, user.value);
    perms.get(file.id, "Access denied");    
  }
}

Inside the accessgrant action, you can implement any logic and checks you want to determine if a user should get access to a file. It's freely programmable. If you want to deny access, just raise an assertion using eosio::check.

In order to receive this notification, you need to implement a custom dispatch action. The following example was taken from our decentralised private twitter example app:

extern "C" {
  [[noreturn]] void apply(uint64_t receiver, uint64_t code, uint64_t action) {
    if (action == "accessgrant"_n.value && code == "priveosrules"_n.value) {
      execute_action(eosio::name(receiver), eosio::name(code), &squeakr::accessgrant);
    }

    if (code == receiver) {
      switch (action) { 
        EOSIO_DISPATCH_HELPER(squeakr, 
          (followreq)
          (post) 
          (accept)
          (admclear)
        ) 
      }    
    }

    eosio_exit(0);
  }
}

For more information on writing custom dispatchers, please refer to the eosio documentation.