├── README.md ├── about.html ├── b.html ├── boards.html ├── coming.html ├── contracts └── permawall.sol ├── forcedata.json ├── images ├── Cat-In-Space-Wallpaper-1032x774.jpg ├── ETHEREUM_NAV-BAR-LOGO.png ├── ipfs-logo-text-256-ice.png └── space-wallpapers-6.jpg ├── index.html ├── javascripts ├── bundle.js ├── ipfsapi.js └── wallApp.js ├── login.html ├── perma.json ├── register.html ├── stylesheets └── style.css └── tech.html /README.md: -------------------------------------------------------------------------------- 1 | #swarmchan 2 |

React Rebuild

3 | Switching to IPFS now that v0.4.0-dev has kind of been released to handle data aggregation from multiple sources. This will hopefully work and soon you won't have to pay ether to post. Ether will remain as a tipping system. Each user will automagically have their ether address stamped on their posts so others can send ether for liked content. Also migrating the app framework over to react, angular is good but I have found react to work well for static single page applications that can be hosted on IPFS. Big thanks to the javascript guys at IPFS and the core devs for working hard on 0.4.0 release as well as to fazo for ipfs-boards. This redesign owes a lot to them. 4 | 5 |

About

6 |

This is a chan style message board. Unlike other sites, this application does not have a mod, an owner, or a server. This is an experiment in IPFS - the InterPlanetary File System and the Ethereum blockchain and a WIP. Adding posts will distribute the data p2p in an attempt to make something as simple as text (for now, images coming soon) persist. Currently what you post here has no way of being censored or removed. As long as a computer has the app files and ethereum can resolve the current data state and assuming someone has it... and well I can't promise this will work and you will need a local ipfs and ethereum go client node running to use this. Be warned that just viewing the posts in this application will download them to your hard drive. I would suggest running ipfs repo gc after viewing this site.Try it here!

7 | 8 |

How This Works

9 |

This site is built in Javascript with Angular MVC. All of the code here is either client side JS or browserified nodejs. This application is distributed by the IPFS protocol and connects to the Ethereum api to provide consensus on the current state of the posts on any given wall. The contents of this application are hashed and distributed with a DHT routing protocol. When a post is made you append your post data to a json object and add the new object to ipfs making it ready to be requested via the DHT by other users. A transaction to a smart contract is then bundled to set the hashs of the new data objects in the blockchain. Sending this transaction shouldn't cost too much (~0.002 Ether currently) but the contract will reject your post if you don't provide enough ether to pay the gas to set your data (currently hard coded to 70,000 gas per TX). All of the nodes running this application will then be able to resolve the same data from the last change to the wall from the blockchain and then download the data through the ipfs dht.

10 | 11 |

Instuctions On Using

12 |

Run Locally 13 |
14 |
15 | Boot up and ipfs client... Instructions on installing ipfs 16 | 17 | Boot up an ethereum geth client with a small amount of ether on it and you're set. This currently defaults to account[0] or your primary account, I should probably build a drop down menu to select different accounts. Just remember to add origins to the CORS accept in both ipfs and ethereum if you get resource sharing problems. Also you need to unlock your geth account to post. Starting geth with the line below will set everything up to post. 18 |
19 |
20 | In ipfs: 21 | export API_ORIGIN="http://localhost:8080" 22 |
23 |
24 | In geth: 25 | geth --rpc --rpccorsdomain "*" --unlock address 26 |
27 |
28 | Run on a hosted node 29 |
30 |
31 | Coming soon! <---- link goes here, something like client.voxelot.us... but not that... or anything .us probably :) 32 |

33 | 34 |

Anon

35 |

IPFS does not guarantee any anonymity. IPFS and Ethereum are inherently public when it comes to sharing your connection info. In the future IPFS could have ways of dialing into the TOR network and Ethereum could develop some mixing service similar to Darkcoin.

36 | 37 |

In progress

38 |

In the future we may have an IPFS/IPNS implementation that can connect browsers p2p so that gateways or IPFS installations would not be needed. Also IPNS should be able to use shared keys soon to allow others to publish to an IPNS ID which could elminate the need for ethereum. I will still keep ether built into the site as a tipping system for those who choose to run a geth node. Very interested in making this as easy as possible to use. I have experimented with creating a server to host the nodes for people who do not know how to run one of their own. I need to fix some bugs. Would be cool if there was some ipfs connect info by the ether balance like number of peers etc. Getting a user system set up would be interesting, but probably not applicable for this site. Spread the data out to multiple objects and perhaps just allow old walls to fall off into obscurity while still maintaining the option to index any old data that has ever been posted to this application. Setting the scope variable by watching new blocks coming in and resolving the new data on the fly would be nice as well. I would also like to add a way for people to earn ether to fund their posts, perhaps a tipping system. Still need to build the ability to create sub threads like a normal chan site. Add video support.

39 | 40 |

Caveats

41 |

The data object will just continue to grow with every post. This implies that every user has to download a copy of the database to use the application and DBs can get large. Also every time someone posts the new data hash to the blockchain they will be the only ones with that data on their ipfs node limiting the ability for others to connect to the current state of the data if they can't resolve that node in the routing. I am working on service discovery atm as suggested in this issue. For now if your board looks blank but has a current data hash, try resolving the hash on an IPFS gateway. IPNS now serves back muliple records with a sequence number to choose the most recent revision which will help keep users from appending new posts to an old data object and publishing data that deletes some recent posts as in the ethereum build. Finally there is currently the possibility for any person to read the contract code from the source code of this app and send in empty objects to the contract, effectively deleting all of the visible data. Or they could just send in any data they want displayed or even non-json formatted data and potentially break the application for everyone. I haven't yet built in any checks that would stop someone from doing this.

42 | -------------------------------------------------------------------------------- /about.html: -------------------------------------------------------------------------------- 1 |

About

2 |

This is a chan style message board. Unlike other sites, this application does not have a mod, an owner, or a server. This is an experiment in IPFS - the InterPlanetary File System and the Ethereum blockchain and a WIP. Adding posts will distribute the data p2p in an attempt to make something as simple as text (for now, images coming soon) persist. Currently what you post here has no way of being censored or removed. As long as a computer has the app files and ethereum can resolve the current data state and assuming someone has it... and well I can't promise this will work and you will need a local ipfs and ethereum go client node running to use this. Be warned that just viewing the posts in this application will download them to your hard drive. I would suggest running ipfs repo gc after viewing this site.Try it here!

3 | 4 |

How This Works

5 |

This site is built in Javascript with Angular MVC. All of the code here is either client side JS or browserified nodejs. This application is distributed by the IPFS protocol and connects to the Ethereum api to provide consensus on the current state of the posts on any given wall. The contents of this application are hashed and distributed with a DHT routing protocol. When a post is made you append your post data to a json object and add the new object to ipfs making it ready to be requested via the DHT by other users. A transaction to a smart contract is then bundled to set the hashs of the new data objects in the blockchain. Sending this transaction shouldn't cost too much (~0.002 Ether currently) but the contract will reject your post if you don't provide enough ether to pay the gas to set your data (currently hard coded to 70,000 gas per TX). All of the nodes running this application will then be able to resolve the same data from the last change to the wall from the blockchain and then download the data through the ipfs dht.

6 | 7 |

Instuctions On Using

8 |

Run Locally 9 |
10 |
11 | Good News! Currently, if you are reading this it means that you are running ipfs!...(unless you are viewing this through a gateway) so just boot up an ethereum geth client with a small amount of ether on it and you're set. This currently defaults to account[0] or your primary account, I should probably build a drop down menu to select different accounts. Just remember to add origins to the CORS accept in both ipfs and ethereum if you get resource sharing problems. Also you need to unlock your geth account to post. Starting geth with the line below will set everything up to post. 12 |
13 |
14 | In ipfs: 15 | export API_ORIGIN="http://localhost:8080" 16 |
17 |
18 | In geth: 19 | geth --rpc --rpccorsdomain "*" --unlock address 20 |
21 |
22 | Run on a hosted node 23 |
24 |
25 | Coming soon! <---- link goes here, something like client.voxelot.us... but not that... or anything .us probably :) 26 |

27 | 28 |

Anon

29 |

IPFS does not guarantee any anonymity. IPFS and Ethereum are inherently public when it comes to sharing your connection info. In the future IPFS could have ways of dialing into the TOR network and Ethereum could develop some mixing service similar to Darkcoin.

30 | 31 |

In progress

32 |

In the future we may have an IPFS/IPNS implementation that can connect browsers p2p so that gateways or IPFS installations would not be needed. Also IPNS should be able to use shared keys soon to allow others to publish to an IPNS ID which could elminate the need for ethereum. I will still keep ether built into the site as a tipping system for those who choose to run a geth node. Very interested in making this as easy as possible to use. I have experimented with creating a server to host the nodes for people who do not know how to run one of their own. I need to fix some bugs. Would be cool if there was some ipfs connect info by the ether balance like number of peers etc. Getting a user system set up would be interesting, but probably not applicable for this site. Spread the data out to multiple objects and perhaps just allow old walls to fall off into obscurity while still maintaining the option to index any old data that has ever been posted to this application. Setting the scope variable by watching new blocks coming in and resolving the new data on the fly would be nice as well. I would also like to add a way for people to earn ether to fund their posts, perhaps a tipping system. Still need to build the ability to create sub threads like a normal chan site. Add video support.

33 | 34 |

Caveats

35 |

The data object will just continue to grow with every post. This implies that every user has to download a copy of the database to use the application and DBs can get large. Also every time someone posts the new data hash to the blockchain they will be the only ones with that data on their ipfs node limiting the ability for others to connect to the current state of the data if they can't resolve that node in the routing. I am working on service discovery atm as suggested in this issue. For now if your board looks blank but has a current data hash, try resolving the hash on an IPFS gateway. IPNS now serves back muliple records with a sequence number to choose the most recent revision which will help keep users from appending new posts to an old data object and publishing data that deletes some recent posts as in the ethereum build. Finally there is currently the possibility for any person to read the contract code from the source code of this app and send in empty objects to the contract, effectively deleting all of the visible data. Or they could just send in any data they want displayed or even non-json formatted data and potentially break the application for everyone. I haven't yet built in any checks that would stop someone from doing this.

-------------------------------------------------------------------------------- /b.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

warning! this will cost ~ 0.002 Ether

4 | 5 | 6 | Drag and drop images or video here! 7 |
8 | 9 |
10 | 11 |
12 | 13 |
14 |
15 |

Current data hash: {{hash}}

16 |
17 | 25 |
26 |
27 |
28 |

{{post.text}}reply

29 | 30 |

31 |

32 | 33 | 39 | 40 | 41 |

42 | 43 | 44 | ID: {{post.id}} Posted by @{{post.created_by}} 45 | {{post.created_at | date:"h:mma 'on' MMM d, y"}} 46 |
47 |
48 | 51 | {{currentPage+1}}/{{numberOfPages()}} 52 | 55 |
56 |
57 | -------------------------------------------------------------------------------- /boards.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 4chan - Verification Required 12 | 13 | 14 | 15 | 16 |
17 |
18 | 21 |
22 | 23 | 24 |

 Boards

25 | 26 |
27 | 28 | 29 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 |
Japanese Culture
Interests
Creative
Other
Anime & Manga
Video Games
PhotographyTravel
TransportationRobotics
Food & CookingParanormal
CultureComics & Cartoons
Artwork/CritiqueAdvice
HistoryTechnologyMusicLiterature
Martial Arts
TV & Film
Graphic DesignRandom
147 |
148 |
149 |
150 |
151 |
152 |
153 | 154 | 155 | -------------------------------------------------------------------------------- /coming.html: -------------------------------------------------------------------------------- 1 | coming soon! -------------------------------------------------------------------------------- /contracts/permawall.sol: -------------------------------------------------------------------------------- 1 | contract permawall{ 2 | 3 | string dataHash1; 4 | string dataHash2; 5 | string imgHash1; 6 | string imgHash2; 7 | //string prevHash = get previous hash from blockchain; 8 | 9 | function setHash(string firstPart, string secondPart, string check) { 10 | if(check == prevHash) { 11 | dataHash1 = firstPart; 12 | dataHash2 = secondPart; 13 | prevHash = firstPart; 14 | } 15 | } 16 | 17 | function setImgHash(string firstPart, string secondPart, string check) { 18 | if(check == prevHash) { 19 | dataHash1 = firstPart; 20 | dataHash2 = secondPart; 21 | prevHash = firstPart; 22 | } 23 | } 24 | 25 | function getHash1() constant returns (string part1 ) { 26 | return dataHash1; 27 | } 28 | 29 | function getHash2() constant returns (string part2 ) { 30 | return dataHash2; 31 | } 32 | 33 | function getImgHash1() constant returns (string imgPart1 ) { 34 | return imgHash1; 35 | } 36 | 37 | function getImgHash2() constant returns (string imgPart2 ) { 38 | return imgHash2; 39 | } 40 | } -------------------------------------------------------------------------------- /forcedata.json: -------------------------------------------------------------------------------- 1 | [{"created_by":"voxelot","text":"Somewhere, something incredible is waiting to be known. -Carl Sagan","created_at":"2442430727681","$$hashKey":"046"},{"created_by":"vox","text":"Will this work?","created_at":"1444257893665","$$hashKey":"045"},{"created_by":"Work!","text":"This will work!","created_at":"1444258357040","$$hashKey":"00X"},{"created_by":"Nathan","text":"Perma Posts! Images next? fml","created_at":"1444259360185","$$hashKey":"00W"},{"created_by":"newton","text":"Truth is ever to be found in simplicity, and not in the multiplicity and confusion of things.","created_at":"1444261907176","$$hashKey":"00V"},{"created_by":"vox","text":"First test from a different computer.","created_at":"1444322209998","$$hashKey":"00U"},{"created_by":"voxe","text":"Just talkin to myself... whistles","created_at":"1444361518481","$$hashKey":"00T"},{"created_by":"Anonymous","text":"It's over 9000!!!","created_at":"1444441898156","$$hashKey":"03Z"},{"created_by":"t1to2o","text":"Normality is a paved road: it's comfortable to walk, but no flowers grow on it. (Van Gogh)","created_at":"1444443631801","$$hashKey":"007"},{"created_by":"voxelot","text":"First image post!","created_at":"1445035140765","pic":"QmaF4N9HqevuRnZVu8VChvWHc6VZfDd5nEubwtSYpECnRe","resize":"196","$$hashKey":"069"},{"created_by":"Anonymous","text":"gifs... webm next?","created_at":"1445211368951","pic":"QmQ8wzHub4aPwTZcUMAxkfQaeZLMZcj5i2rjnTm3kGmZzi","resize":"203","id":"300359c4","$$hashKey":"eda"},{"created_by":"Anonymous","text":"can haz videos","created_at":"1445392391451","pic":"","video":"http://localhost:8080/ipfs/QmQGnhThpPEXJ53LnsxTDEfCXoUYkD59KJ5YBv7z33TZMa","resize":"0","id":"d4d05faa","$$hashKey":"034"},{"created_by":"Anonymous","text":"can haz p2p chan?","created_at":"1445392573736","pic":"QmYTtVVhK5iG4MDh8JmCYQqyag5C6Yr9qzLjg75sEqrxcc","video":"","resize":"158","id":"a43c790e","$$hashKey":"071"},{"created_by":"Anonymous","text":"wat","created_at":"1445392748536","pic":"QmVXqkVJKQGvoCZD6y5zerpCk7e9rqRbEQtk2xu3AgZF7F","video":"","resize":"300","id":"04811f5e","$$hashKey":"0f1"},{"created_by":"Anonymous","text":"Post from SF meetup!","created_at":"1445400717633","pic":"","video":"","resize":"0","id":"98891532","$$hashKey":"00c"},{"created_by":"Anonymous","text":"BUNNIES","created_at":"1445401034251","pic":"","video":"http://localhost:8080/ipfs/QmQGnhThpPEXJ53LnsxTDEfCXoUYkD59KJ5YBv7z33TZMa","resize":"0","id":"c65811fb","$$hashKey":"003"},{"created_by":"Anonymous","text":"test","created_at":"1445483049042","pic":"","video":"http://localhost:8080/ipfs/QmQGnhThpPEXJ53LnsxTDEfCXoUYkD59KJ5YBv7z33TZMa","resize":"0","id":"bb1db179","$$hashKey":"04f"},{"created_by":"Anonymous","text":"testing larger files... need loading status","created_at":"1445490323019","pic":"","video":"http://localhost:8080/ipfs/Qmaf2XkCq2em5GqjQJRvXNvFDtmsa5XuxDd91eyGfQo9L1", "resize":"0", "id":"04cb1e02", "$$hashKey":"071420"}] 2 | 3 | QmQ1U85RGCai7KDnoudActb2 4 | WpvXjNvNNiVae2bjNY4jm9 -------------------------------------------------------------------------------- /images/Cat-In-Space-Wallpaper-1032x774.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginnever/swarmchan/7db96948ad21b4cbfdf5917faef2009a10d9b40c/images/Cat-In-Space-Wallpaper-1032x774.jpg -------------------------------------------------------------------------------- /images/ETHEREUM_NAV-BAR-LOGO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginnever/swarmchan/7db96948ad21b4cbfdf5917faef2009a10d9b40c/images/ETHEREUM_NAV-BAR-LOGO.png -------------------------------------------------------------------------------- /images/ipfs-logo-text-256-ice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginnever/swarmchan/7db96948ad21b4cbfdf5917faef2009a10d9b40c/images/ipfs-logo-text-256-ice.png -------------------------------------------------------------------------------- /images/space-wallpapers-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginnever/swarmchan/7db96948ad21b4cbfdf5917faef2009a10d9b40c/images/space-wallpapers-6.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | swarmchan 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 44 | 53 |
54 |
55 | 56 |
57 |
58 |
59 | 60 | 67 | -------------------------------------------------------------------------------- /javascripts/wallApp.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('wallApp', ['ngRoute']); 2 | var ipfs_api = require('ipfs-api'); 3 | var ipfs = ipfs_api('localhost', '5001'); 4 | var http = require("http"); 5 | var web3 = require('web3'); 6 | var dragDrop = require('drag-drop'); 7 | 8 | var permaObj = []; 9 | var hash = ''; 10 | 11 | 12 | app.config(function($routeProvider){ 13 | $routeProvider 14 | //the timeline display 15 | .when('/wall', { 16 | templateUrl: 'main.html', 17 | controller: 'mainController' 18 | }) 19 | //the about display 20 | .when('/', { 21 | templateUrl: 'about.html', 22 | controller: 'dataController' 23 | }) 24 | //the image boards display 25 | .when('/boards', { 26 | templateUrl: 'boards.html', 27 | controller: 'dataController' 28 | }) 29 | 30 | //controllers for each board 31 | .when('/b', { 32 | templateUrl: 'b.html', 33 | controller: 'bController' 34 | }) 35 | 36 | .when('/coming', { 37 | templateUrl: 'coming.html' //lulz 38 | }) 39 | 40 | // //the signup display *** we won't be doing any of this ;) 41 | // .when('/register', { 42 | // templateUrl: 'register.html', 43 | // controller: 'authController' 44 | // }); 45 | 46 | }); 47 | 48 | //ethereum connections 49 | window.getAddress = function(){ 50 | web3.setProvider(new web3.providers.HttpProvider()); 51 | 52 | var coinbase = web3.eth.accounts[1]; 53 | var balance = web3.fromWei(web3.eth.getBalance(coinbase), "ether"); 54 | return coinbase; 55 | } 56 | 57 | window.getBalance = function(){ 58 | web3.setProvider(new web3.providers.HttpProvider()); 59 | 60 | var coinbase = web3.eth.accounts[1]; 61 | var balance = web3.fromWei(web3.eth.getBalance(coinbase), "ether"); 62 | return balance; 63 | } 64 | 65 | //use this function for autoupdating the wall 66 | // window.watchBlocks = function(){ 67 | // var filter = web3.eth.filter('pending'); 68 | // filter.watch(function(err, log){ 69 | // web3.eth.getBlock('latest', function(error, result){ 70 | // if(!error){ 71 | // console.log(result) 72 | // } 73 | // else 74 | // console.error(error); 75 | // }); 76 | 77 | // if(!err){ 78 | // //console.log(log); 79 | // } 80 | // }); 81 | // } 82 | 83 | 84 | app.controller('bController', function($scope){ 85 | //do some smart contract call here to get data hash for b board 86 | }); 87 | 88 | app.controller('dataController', function($scope){ 89 | //var id = 'QmNjRVohhWBX31EoaAXkrj5mPF9vQNcTVvQgWHNwdxweCN'; 90 | //var id = 'Qmd4kFg6HMp7c1Uqnm2UZqx7iGoECKA8r3sXHN2UcTY2pB'; 91 | //var id = 'QmdprAq8ZvnfpRfFsDUjLbNoZXEzrE8rc4quSaje3m5dgN'; 92 | 93 | console.log(web3.currentProvider) 94 | if(!web3.currentProvider){ 95 | console.log("no provider... creating") 96 | web3.setProvider(new web3.providers.HttpProvider()); 97 | } 98 | 99 | 100 | //console.log(web3.eth.getCode('0x73a389029e7720e9203b636666b28c51b77a71cc')); 101 | 102 | //permachanInstance.setHash(firstHalf2, secondHalf2 {from: web3.eth.accounts[1], gas: 70000}); 103 | 104 | function resolveID(callback){ 105 | //pulling the latest data hash from ethereum 106 | var contractCode = '60606040526104b9806100126000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806311c171ba1461004f57806333c0f099146100ca578063e15fe023146101455761004d565b005b61005c6004805050610341565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156100bc5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100d760048050506103fd565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156101375780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101e06004808035906020019082018035906020019191908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050909091908035906020019082018035906020019191908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509090919050506101e2565b005b8160006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023157805160ff1916838001178555610262565b82800160010185558215610262579182015b82811115610261578251826000505591602001919060010190610243565b5b50905061028d919061026f565b80821115610289576000818150600090555060010161026f565b5090565b50508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102de57805160ff191683800117855561030f565b8280016001018555821561030f579182015b8281111561030e5782518260005055916020019190600101906102f0565b5b50905061033a919061031c565b80821115610336576000818150600090555060010161031c565b5090565b50505b5050565b602060405190810160405280600081526020015060006000508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103ee5780601f106103c3576101008083540402835291602001916103ee565b820191906000526020600020905b8154815290600101906020018083116103d157829003601f168201915b505050505090506103fa565b90565b602060405190810160405280600081526020015060016000508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104aa5780601f1061047f576101008083540402835291602001916104aa565b820191906000526020600020905b81548152906001019060200180831161048d57829003601f168201915b505050505090506104b6565b9056'; 107 | var permachanContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"getHash1","outputs":[{"name":"part1","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"getHash2","outputs":[{"name":"part2","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"firstPart","type":"string"},{"name":"secondPart","type":"string"}],"name":"setHash","outputs":[],"type":"function"}]); 108 | var permachanInstance = permachanContract.at('0x73a389029e7720e9203b636666b28c51b77a71cc'); 109 | hash = permachanInstance.getHash1()+permachanInstance.getHash2(); 110 | console.log('from ethereum contract: '+permachanInstance.getHash1()+permachanInstance.getHash2()); 111 | callback(); 112 | } 113 | 114 | //ipns resolving - replaced with ethereum 115 | // function resolveID(callback){ 116 | // ipfs.name.resolve(id, function(err,res){ 117 | // if(err|| !res){ 118 | // return console.error(err) 119 | // } 120 | // var jsonString = String(res.Path); 121 | // jsonString = jsonString.replace('/ipfs/',''); 122 | // hash = jsonString; 123 | // console.log(jsonString); 124 | // callback(); 125 | // }); 126 | // }; 127 | 128 | resolveID(function(){ 129 | ipfs.cat(hash, function (err, res) { 130 | if (err || !res) { 131 | return console.error(err) 132 | } 133 | 134 | if (res.readable) { 135 | // Returned as a stream 136 | //comment out as to not use response object on the console 137 | //res.pipe(process.stdout); 138 | var string = ''; 139 | 140 | //turn the buffer response from ipfs into string then json 141 | res.setEncoding('utf8'); 142 | res.on('readable', function () { 143 | var part = res.read().toString(); 144 | string += part; 145 | var obj = JSON.parse(string); 146 | permaObj = obj; 147 | }) 148 | } else { 149 | var obj = res; 150 | permaObj = obj; 151 | console.log(obj); 152 | } 153 | }); 154 | }); 155 | }); 156 | 157 | 158 | //* 159 | app.controller('pageCtrl', function($scope){ 160 | $scope.currentPage = 0; 161 | $scope.limit = 10; 162 | }) 163 | 164 | app.filter('startFrom', function() { 165 | return function(input, start) { 166 | start = +start; //parse to int 167 | return input.slice(start); 168 | } 169 | }); 170 | 171 | app.directive('fdInput', [function () { 172 | return { 173 | link: function (scope, element, attrs) { 174 | element.on('change', function (evt) { 175 | var files = evt.target.files; 176 | console.log(files[0].name); 177 | console.log(files[0].size); 178 | }); 179 | } 180 | } 181 | }]); 182 | 183 | app.controller('mainController', function($scope){ 184 | var imgHash = ''; 185 | var resize = 0; 186 | var resizePost = 0; 187 | //drag-drop 188 | dragDrop('#dropTarget', function (files) { 189 | console.log('Here are the dropped files', files) 190 | 191 | // `files` is an Array! 192 | files.forEach(function (file) { 193 | console.log(file.name) 194 | console.log(file.size) 195 | console.log(file.type) 196 | console.log(file.lastModifiedData) 197 | console.log(file.fullPath) 198 | 199 | // convert the file to a Buffer that we can use! 200 | //use this reader to read preview of image 201 | var imageType = /image.*/; 202 | //var resize = 0; 203 | 204 | if (file.type.match(imageType)) { 205 | //console.log('this is an image') 206 | var previewReader = new FileReader() 207 | previewReader.addEventListener('load', function (e) { 208 | var image = new Image(); 209 | image.src = e.target.result; 210 | image.onload = function() { 211 | var x = this.width; 212 | var y = this.height; 213 | resize = parseInt((150/x)*y); 214 | resizePost = parseInt((300/x)*y); 215 | // console.log(this.width); 216 | // console.log(this.height); 217 | // console.log("resize height to " +resize); 218 | } 219 | }) 220 | previewReader.readAsDataURL(file) 221 | var reader = new FileReader() 222 | reader.addEventListener('load', function (e) { 223 | // e.target.result is an ArrayBuffer 224 | var arr = new Uint8Array(e.target.result) 225 | var buffer = new Buffer(arr) 226 | 227 | // do something with the buffer! 228 | ipfs.add(new Buffer(buffer), function (err, res) { 229 | if (err || !res) return console.error(err) 230 | imgHash = res.Hash; 231 | dropTarget.innerHTML = ""; 232 | //imgPath = ""; 233 | //preview = "" 234 | //todo: add smart contract support for images hashs 235 | console.log(imgHash); 236 | }); 237 | console.log(buffer) 238 | }) 239 | reader.addEventListener('error', function (err) { 240 | console.error('FileReader error' + err) 241 | }) 242 | console.log('test') 243 | reader.readAsArrayBuffer(file) 244 | //for the image to preview reader.readAsDataURL(file); 245 | }else{ 246 | alert('Please upload an image') 247 | } 248 | }) 249 | }) 250 | 251 | 252 | 253 | var returnHash = ''; 254 | $scope.posts2 = permaObj; 255 | $scope.hash = hash; 256 | $scope.newPost = {created_by: '', text: '', created_at: '', pic:'', resize:'', id:''}; 257 | 258 | //pagenation stuff 259 | $scope.currentPage = 0; 260 | $scope.pageSize = 5; 261 | $scope.mySort = $scope.newestFirst = function(post) { 262 | return -$scope.posts2.indexOf(post); 263 | } 264 | $scope.numberOfPages=function(){ 265 | return Math.ceil(Object.keys(permaObj).length/$scope.pageSize); 266 | } 267 | 268 | 269 | //post handler 270 | $scope.post = function(){ 271 | dropTarget.innerHTML = ''; 272 | //$scope.imagePath = imgPath; 273 | //$scope.imagePreview = preview; 274 | console.log(imgHash); 275 | var newObjStr = JSON.stringify(permaObj); 276 | if($scope.newPost.created_by == ""){ 277 | $scope.newPost.created_by = "Anonymous" 278 | } 279 | 280 | newObjStr = newObjStr.replace(']',''); 281 | newObjStr += ',{\"created_by\":\"'+$scope.newPost.created_by+'\",\"text\":\"'+$scope.newPost.text+'\",\"created_at\":\"'+Date.now()+'\",\"pic\":\"'+imgHash+'\", \"resize\":\"'+resizePost+'\"}]'; 282 | console.log(newObjStr) 283 | var newObj = JSON.parse(newObjStr); 284 | 285 | 286 | 287 | // function wait(callback){ 288 | // console.log("adding this object " + newObjStr); 289 | // ipfs.add(new Buffer(newObjStr), function (err, res) { 290 | // if (err || !res) return console.error(err) 291 | // returnHash = res.Hash; 292 | // console.log(returnHash); 293 | // callback(); 294 | // }); 295 | // } 296 | 297 | // wait(function(){ 298 | // //set the variables in contract 299 | // var splitHash = returnHash; 300 | // var firstHalf = splitHash.substr(0, 24); 301 | // var secondHalf = splitHash.substr(24); 302 | // console.log(firstHalf); 303 | // console.log(secondHalf); 304 | 305 | // var contractCode = '60606040526104b9806100126000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806311c171ba1461004f57806333c0f099146100ca578063e15fe023146101455761004d565b005b61005c6004805050610341565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156100bc5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100d760048050506103fd565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156101375780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101e06004808035906020019082018035906020019191908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050909091908035906020019082018035906020019191908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509090919050506101e2565b005b8160006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023157805160ff1916838001178555610262565b82800160010185558215610262579182015b82811115610261578251826000505591602001919060010190610243565b5b50905061028d919061026f565b80821115610289576000818150600090555060010161026f565b5090565b50508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102de57805160ff191683800117855561030f565b8280016001018555821561030f579182015b8281111561030e5782518260005055916020019190600101906102f0565b5b50905061033a919061031c565b80821115610336576000818150600090555060010161031c565b5090565b50505b5050565b602060405190810160405280600081526020015060006000508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103ee5780601f106103c3576101008083540402835291602001916103ee565b820191906000526020600020905b8154815290600101906020018083116103d157829003601f168201915b505050505090506103fa565b90565b602060405190810160405280600081526020015060016000508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104aa5780601f1061047f576101008083540402835291602001916104aa565b820191906000526020600020905b81548152906001019060200180831161048d57829003601f168201915b505050505090506104b6565b9056'; 306 | // var permachanContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"getHash1","outputs":[{"name":"part1","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"getHash2","outputs":[{"name":"part2","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"firstPart","type":"string"},{"name":"secondPart","type":"string"}],"name":"setHash","outputs":[],"type":"function"}]); 307 | // var permachanInstance = permachanContract.at('0x73a389029e7720e9203b636666b28c51b77a71cc'); 308 | 309 | // permachanInstance.setHash(firstHalf, secondHalf, {from: web3.eth.accounts[1], gas: 70000}); 310 | 311 | 312 | // // ipfsPublish(returnHash, function(o){ 313 | // // console.log("Hash: "+o.Value + " Published to ID: "+o.Name); 314 | // // }) 315 | // // function ipfsPublish(returnHash, callback){ 316 | // // var options = { 317 | // // host: 'localhost', 318 | // // port: 5001, 319 | // // path: '/api/v0/name/publish?arg='+returnHash+'&stream-channels=true', 320 | // // method: 'POST' 321 | // // }; 322 | 323 | // // var req = http.request(options, function(res) { 324 | // // res.setEncoding('utf8'); 325 | // // res.on('data', function (chunk) { 326 | // // json = JSON.parse(chunk); 327 | // // callback(json); 328 | // // }); 329 | // // }); 330 | // // req.on('error', function(e) { 331 | // // console.log('problem with request: ' + e.message); 332 | // // }); 333 | 334 | // // req.end(); 335 | // // } 336 | // }); 337 | 338 | $scope.newPost.created_at = Date.now(); 339 | $scope.newPost.pic = imgHash; 340 | $scope.newPost.resize = resizePost; 341 | //objStr = JSON.stringify(permaObj); 342 | $scope.posts2.push($scope.newPost); 343 | $scope.newPost = {created_by: '', text: '', created_at: '', pic:'', resize:''}; 344 | imgHash = ''; 345 | }; 346 | }); 347 | 348 | app.controller('authController', function($scope){ 349 | $scope.user = {username: '', password: ''}; 350 | $scope.error_message = ''; 351 | 352 | $scope.login = function(){ 353 | $scope.error_message = 'login request for ' + $scope.user.username; 354 | }; 355 | 356 | $scope.register = function(){ 357 | $scope.error_message = 'registeration request for ' + $scope.user.username; 358 | }; 359 | }); -------------------------------------------------------------------------------- /login.html: -------------------------------------------------------------------------------- 1 |
2 |

Log In

3 |

{{error_message}}

4 |
5 |
6 | 7 |
8 |

Coming soon!

9 | -------------------------------------------------------------------------------- /perma.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "created_by": "voxelot", 4 | "text": "Somewhere, something incredible is waiting to be known. -Carl Sagan", 5 | "created_at": "2442430727681" 6 | } 7 | ] 8 | 9 | -------------------------------------------------------------------------------- /register.html: -------------------------------------------------------------------------------- 1 |
2 |

Register

3 |

{{error_message}}

4 |
5 |
6 | 7 |
8 |

Coming soon!

-------------------------------------------------------------------------------- /stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top:70px; 3 | width: 100%; 4 | height: 100%; 5 | width: 100%; 6 | height: 100%; 7 | font-family: "Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif; 8 | } 9 | 10 | a { 11 | color: #00B7FF; 12 | } 13 | 14 | ul { 15 | list-style-type: none; 16 | } 17 | 18 | @media all and (max-width: 1000px) { 19 | #sidebar-wrapper { display: none; } 20 | } 21 | 22 | @media all and (max-width: 1000px) { 23 | #hide { display: none; } 24 | } 25 | 26 | #dropTarget { 27 | width: 150px; 28 | height: 100px; 29 | border-radius: 5px; 30 | border: 2px dashed #CCC; 31 | margin-top: 10px; 32 | } 33 | 34 | .dthover:hover { 35 | outline: 0px none; 36 | box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.075) inset, 0px 0px 8px rgba(102, 175, 233, 0.6); 37 | border-color: #66AFE9; 38 | } 39 | 40 | #sidebar-wrapper { 41 | overflow: hidden; 42 | margin-left: -250px; 43 | left: 250px; 44 | width: 300px; 45 | background: #ffffff; 46 | position: fixed; 47 | height: 100%; 48 | overflow-y: auto; 49 | z-index: 1000; 50 | transition: all 0.4s ease 0s; 51 | } 52 | 53 | 54 | .submit-btn { 55 | background-color: #b1dbff; 56 | margin: 10px 0 10px 0; 57 | } 58 | 59 | .post { 60 | padding: 10px; 61 | margin-bottom: 5px; 62 | border-radius: 5px; 63 | } 64 | 65 | .odd { 66 | background-color: #f2f9ff; 67 | 68 | } 69 | .even { 70 | background-color: #eceff3; 71 | } 72 | 73 | .form-auth { 74 | max-width: 330px; 75 | padding: 5px; 76 | margin: 0 auto; 77 | } -------------------------------------------------------------------------------- /tech.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

warning! this will cost ~ 0.002 Ether

4 | 5 | 6 | Drag and drop images or video here! 7 |
8 | 9 |
10 | 11 |
12 |
13 |
14 |

Current data hash: {{hash}}

15 |
16 | 24 |
25 |
26 |
27 |

{{post.text}}reply

28 | 29 |

30 |

31 | 32 | 38 | 39 | 40 |

41 | 42 | 43 | ID: {{post.id}} Posted by @{{post.created_by}} 44 | {{post.created_at | date:"h:mma 'on' MMM d, y"}} 45 |
46 |
47 | 50 | {{currentPage+1}}/{{numberOfPages()}} 51 | 54 |
55 |
56 |
57 | 58 |
59 |
60 |
61 | 62 |
63 |
64 |
65 | --------------------------------------------------------------------------------