Last active
June 22, 2018 08:03
-
-
Save nnkken/6a08bab720d1ac8a6046ac4749b3ef54 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module.exports = { | |
| storageProviders: { | |
| puttyimages: '0x0102030405060708091011121314151617181920', | |
| }, | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const express = require('express'); | |
| const bodyParser = require('body-parser'); | |
| const ipfsAPI = require('ipfs-api'); | |
| const level = require('level'); | |
| const sigUtil = require('eth-sig-util'); | |
| const jsonStringify = require('json-stable-stringify'); | |
| const { promisify } = require('util'); | |
| const ipfs = ipfsAPI({ host: 'localhost', port: '5001', protocol: 'http' }); | |
| const ipfsDagPut = promisify(ipfs.dag.put); | |
| const db = level('./likechain'); | |
| const dbGet = promisify((...args) => db.get(...args)); | |
| const dbPut = promisify((...args) => db.put(...args)); | |
| const app = express(); | |
| const { storageProviders } = require('./config.js'); | |
| function checkBase58(s) { | |
| return /^[a-km-zA-HJ-NP-Z1-9+/]+$/.test(s); | |
| } | |
| function checkHex(s) { | |
| return /^[a-fA-F0-9]+$/.test(s); | |
| } | |
| function verifyMetadata(metadata) { | |
| if (!Array.isArray(metadata.creator)) return false; | |
| if (metadata.creator.length < 1) return false; | |
| if (metadata.dateCreated === undefined) return false; | |
| if (metadata.description === undefined) return false; | |
| if (metadata.license === undefined) return false; | |
| if (!checkHex(metadata.likeFingerprint)) return false; | |
| if (!Array.isArray(metadata.likeFootprint)) return false; | |
| if (metadata.likePreviousVersion === undefined) return false; | |
| if (metadata.type === undefined) return false; | |
| if (metadata.uploadDate === undefined) return false; | |
| return true; | |
| } | |
| app.post('/upload', bodyParser.json(), async (req, res) => { | |
| try { | |
| const { | |
| likeIpfs, | |
| metadata, | |
| ownerSig, | |
| storageProvider, | |
| storageProviderSig, | |
| } = req.body; | |
| if ( | |
| !likeIpfs || !metadata || !ownerSig || !storageProvider || !storageProviderSig | |
| ) { | |
| throw new Error('Missing field(s)'); | |
| } | |
| const { likeFingerprint, creator } = metadata; | |
| if (!checkHex(likeFingerprint)) { | |
| throw new Error('Invalid fingerprint'); | |
| } | |
| if (!checkBase58(likeIpfs)) { | |
| throw new Error('Invalid IPFS address'); | |
| } | |
| const normalizedFingerprint = likeFingerprint.toLowerCase(); | |
| try { | |
| await dbGet(normalizedFingerprint); | |
| throw new Error('Content hash already occupied'); | |
| } catch (err) { | |
| if (err.type !== 'NotFoundError') { | |
| throw err; | |
| } | |
| // Not found, so OK to add | |
| } | |
| if (!verifyMetadata(metadata)) { | |
| throw new Error('Invalid metadata'); | |
| } | |
| const ownerSigString = jsonStringify(metadata); | |
| console.log(ownerSigString); | |
| const recoveredOwner = sigUtil.recoverPersonalSignature({ | |
| data: Buffer.from(ownerSigString).toString('hex'), | |
| sig: ownerSig, | |
| }); | |
| // TODO: query address of metadata.creator[0] (from like.co temporary) | |
| console.log(creator[0]); | |
| console.log(recoveredOwner); | |
| const spAddr = storageProviders[storageProvider]; | |
| if (!spAddr) { | |
| throw new Error('Invalid storage provider'); | |
| } | |
| const spSigString = jsonStringify({ likeIpfs, metadata }); | |
| console.log(spSigString); | |
| const recoveredSp = sigUtil.recoverPersonalSignature({ | |
| data: Buffer.from(spSigString).toString(), | |
| sig: storageProviderSig, | |
| }); | |
| if (spAddr.toLowerCase() !== recoveredSp.toLowerCase()) { | |
| throw new Error('Invalid storage provider signature'); | |
| } | |
| const cid = await ipfsDagPut(metadata, { format: 'dag-cbor', hashAlg: 'sha2-256' }); | |
| const metadataIpldAddr = cid.toBaseEncodedString(); | |
| await ipfs.pin.add(metadataIpldAddr); | |
| await dbPut(normalizedFingerprint, metadataIpldAddr); | |
| res.send({ status: 'success', metadataIpldAddr }); | |
| res.end(); | |
| } catch (err) { | |
| console.error(err); | |
| res.status(400); | |
| res.send(err.message || err); | |
| } | |
| }); | |
| app.get('/content/:fingerprint', async (req, res) => { | |
| try { | |
| const { fingerprint } = req.params; | |
| if (!checkHex(fingerprint)) { | |
| throw new Error('Invalid fingerprint'); | |
| } | |
| const normalizedFingerprint = fingerprint.toLowerCase(); | |
| const metadataIpldAddr = await dbGet(normalizedFingerprint); | |
| res.send({ status: 'success', metadataIpldAddr }); | |
| } catch (err) { | |
| console.error(err); | |
| res.status(400); | |
| res.send(err.message || err); | |
| } | |
| }); | |
| app.listen(8000); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "name": "fake-likechain", | |
| "version": "1.0.0", | |
| "description": "", | |
| "main": "index.js", | |
| "scripts": { | |
| "start": "node index.js" | |
| }, | |
| "keywords": [], | |
| "author": "", | |
| "license": "ISC", | |
| "dependencies": { | |
| "body-parser": "^1.18.3", | |
| "eth-sig-util": "^1.4.2", | |
| "express": "^4.16.3", | |
| "ipfs-api": "^22.0.1", | |
| "json-stable-stringify": "^1.0.1", | |
| "level": "^4.0.0", | |
| "web3-utils": "^1.0.0-beta.34" | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment