A primitive Yelp clone using Linnia Smart Contract. There are 3 types o participants on Zelp:
- The Zelp "owner"
- The reviewer
- A Zelp user/visitor
More specifically:
- The Zelp "owner"
- Owns the server that hosts the web app (could be hosted in IPFS)
- Owns the smart contract
- Owns the code that will run on Linnia Mesh or AWS etc.
- The reviewer
- Is paid for high quality reviews
- Reviews the code the owner provides.
- Runs the command to deploy it to it's Linnia Mesh/AWS serverless
- Pays fees for encoding/decoding in Linnia Mesh/AWS serverless
- Pays fees to acknowledge "consumption" on the smart contract
- A Zelp user/visitor
- Pays micro-payments for content/reviews
- Upvotes good content (there's no incentive for this)
- Might be an Editor etc. in which case the upvote counts more (might be automatically-paid role)
Zelp consists of 3 main components, highlighted with yellow in the diagram above.
- The first one is the zelp single-page app. This requires MetaMask to run the web3 business logic in the browser. Just used jQuery for simpliciy. It's a few hundred lines.
- The second part is the
Zelp.sol
smart contract that runs in Ethereum. It has all the business logic that allows addition, purchase and rating of content. - The final part, is an AWS lambda serverless application. This can be replaced with Linnia Computation Mesh at some point. This application is run by the content providers and if the signed URLs requested are valid, it decrypts data stored in (fake-implementation for now) IPFS and it returns content to the app to present. Those users can review the code provided by the zelp framework and upload it in AWS by using 'serverless'.
serverless install -u https://github.com/lookfwd/zelp
The only "vector" for abuse I can see is reviewers running modified versions of the code on the AWS, thus getting fees, while delivering spam content. A reporting system could attack this. A more technical solution would be to use something along the lines of SGX Secure Enclaves, to ensure that the results you get come from the expected, signed , source code. This is more complex and likely would only run in Azure right now.
A nice benefit is that this model works for complex apps e.g. medical imaging. A company creates the Linnia Mesh/AWS serverless app that does some kind of feature extraction from the X-Ray. Data (which for high-res X-Ray might be 100's of MBs) doesn't leave the server - just features do. The patient reviews the code (or trust someone else that has reviewed) and deploys it. Then it goes to a medical data database and researchers can search the db based on the metadata. If they want to use an X-Ray image, they pay the fee, which goes to the patient, and then they run their algorithm on patient's Linnia Mesh/AWS serverless infrastructure and get the results.
Watch the demo video here.
To run the demo you will need three consoles. I will also assume a clean new instance of an EC2 server:
Ubuntu Server 16.04 LTS (HVM), SSD Volume Type
Type vCPUs Memory (GiB)
t2.medium 2 4
Highly likely your dev environment will be more powerful than that. You have to note down the IP address of your EC2 server. Below we will assume it's 34.238.28.80
. For your local dev machine, localhost
will work.
You will have to open a few ports on EC2 firewall for incoming connections. Those are: 8080
, 8545
and 3000
.
We should then, be ready to start.
First let's install some essential software:
git clone https://github.com/lookfwd/zelp.git
cd zelp
sudo apt-get update
sudo apt-get install npm
sudo npm cache clean -f
sudo npm install -g n
sudo n stable
sudo npm install -g truffle ganache-cli serverless
Then we install the requirements for the serverless component of the application:
cd serverless
npm install serverless-offline --save-dev
npm install ethereumjs-util url-expand web3
Finally, we go to bit.ly and create a short url for http://<MY IP>:3000/load/
e.g. http://34.238.28.80:3000/load/
. The reason we do this, is that the URL of your real AWS Lambda function is likely going to be quite long. It's way easier and cheaper to store in Ethereum, strings up to 32 characters. That's why we use shortened URLs.
We will replace all the instances of the short URL in default_records.json
e.g.
$ perl -pi -e 's/https:\/\/goo.gl\/L9oDYV/https:\/\/bit.ly\/2HiPHVE/' default_records.json
Note that default_records.json
is used by both the serverless part and the contract migration code (in Linnia-Smart-Contracts at the moment. This URL part is stored in the blockchain.
We can do a quick diff with git diff .
to ensure that the URL changes are the only changes we've done.
We can now start the serverless component offline:
serverless offline start --host 0.0.0.0
In the second console we deploy the smart contract. We do:
cd Linnia-Smart-Contracts
ganache-cli --rpcaddr 0.0.0.0 &
Now we should be able to connect with MetaMask to http://34.238.28.80:8545/
. We can log-out (if we were logged in) and use the passphrase from console to restore the main account (should have about Ξ 100).
Now we can install some dependencies:
npm install babel-polyfill zeppelin-solidity
And finally compile and migrate the contract:
truffle compile && truffle migrate
truffle migrate
runs the migration script and this in-turn creates an abi.json
file in each of the web
and serverless
directories. This file is used for development and allows those two components to avoid having hardcoded contract addresses and ABI definitions. This also means that those two components won't work unless you do truffle migrate
. Obviously in production, one can hardcode the ABI and contract's address.
We are now ready to run the web application.
cd web/
The URL expander also runs on the serverless component (for now) so we have to change the (hardcoded for now) URL in index.html. This should have the form http://<MY IP>:3000/expand/
e.g. http://34.238.28.80:3000/expand/
. We can do the replace:
perl -pi -e 's/http:\/\/localhost:3000\/expand/http:\/\/34.238.28.80:3000\/expand/' index.html
We can do a quick diff with git diff .
to ensure that the URL changes are the only changes we've done.
Now we can start a python web server to deliver the html pages:
python -m SimpleHTTPServer 8080
Now we can browse to the main url, http://<MY IP>:8080
e.g. http://34.238.28.80:8080
and Zelp's main page should load short after.
- The extended use of
msg.sender
in Linnia, to identify who makes the change, doesn't scale well, when a single caller contract does the changes (in my caseMapDomain
). There could be a better way to register signatures for people and the ability for a singleProvider
to sign multiple times (ideally it would allow me to implement the state machine, I useused
for). Also self-registered people (Patients
) should have someprovenance
(see next). - I would like to map the price to
irisScore
, but there's not fine-grained control ofprovenance
yet. - Accessing
FileRecord
values throughrecords
, is incomplete because of the mapping to tuple, the default getter does. Now I have to use(patient, sigCount, irisScore, recordType, ipfsHash, timestamp) = hub.recordsContract().records(resourceId)
but this means that a) I losesignatures
, which might be ok, but also b) if the order changes there might be a problem. - I would like to map
endpoint
to someuserdata
inFileRecord
. It could also be stored inmetadata
somehow. - I now have my own
metadata
structure. Metadata (both Contextual and Quality) as described in Data and Metadata sound like exactly what I try to do with that data structure. If we were generalising to create a News Portal (see below) such metadata would be the taxonomy of the article as well as the teaser (thumbnail and first few lines of the text).
Note that the theme here is, in general, generic web content "gating" by using single-use URLs that consume credits as you browse. It's easy to adapt the code of Zelp to create e.g. news portals or forums, where the visitor adds Ether and then browses the metadata (teaser) of the content and has to make micro-payments to access gated content. Further roles and access control (by mapping and extending the Patient/Provider roels) can enable/disable and rate content as e.g. Editors, moderators etc.
- I think there's something wrong with
MeataMask
'sweb3.personal.sign()
feature. When I put as much info as I want andsha3
it as a first argument, the warning message shows garbage. When I give a nice message, I can put very little info in it and I have to reconstruct it in Solidity (expensive) to doecrecover
. There mus be a better solution. - In order to enable this use case, we need much faster (sub-second) contract processing times and 1000x times smaller fees. Right now fees for contract execution are many times larger than the payments. The system now would be suitable for expensive content e.g. scientific papers.
This is intented to be a demo-only application for now. It requires work on IPFS and more. Nevertherless, here are some notes on deployment. A user has to populate evn.yml
in the serverless directory with the private key for IPFS, the Ethereum address of the zelp contract and the url of a web3 provider:
ZELP_KEY: 'the-key'
ZELP_ADDRESS: '0x31...'
ZELP_WEB3_PROVIDER: 'ws://localhost:8545'