52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WikiGit
2 |
3 | World's first ecosystem for decentralized innovation & cooperation, powered by Ethereum.
4 |
5 | **TL;DR:** WikiGit empowers projects with governing, crowdsourcing, and crowdfunding mechanisms, which form reinforcement loops that provide projects with rapid and sustainable growth from their moments of creation.
6 |
7 | Current version: 0.2.0 (Grasshopper)
8 |
9 | ## Introduction
10 |
11 | ### The Vision
12 |
13 | WikiGit implements an ecosystem for creating and interacting with what we call **Decentralized Autonomous Self-sustaining Projects (DASPs)**. In the simplest terms, a DASP is a reputation-based decentralized organization that comes with its own Git repository, governing mechanism, crowdfunding mechanism, and crowdsourcing mechanism. By offering any project with governance infrastructure as well as funding and recruiting resources which are comparable to those available to giant corporations, WikiGit will allow ingenious ideas to swiftly incarnate into full-fledged projects that are self-sustaining in both finances and talent, with minimal cost and hindrances, similar to how Wikipedia allowed an entire encyclopedia to spontaneously come into being. In fact, in the near future the WikiGit project itself will be a DASP powered by WikiGit.
14 |
15 | In its complete form, WikiGit will realize the following scenario: One or more person has a great idea, creates a DASP with a cost of ~US$1, writes a white paper or something similar that explains the idea, finds some likeminded people online to form an initial team, pitch in and/or crowdfund some Ether and use them to post tasks onto the crowdsourcing platform, personally contribute to the project and/or recruit some interested freelancers as part-time or full-time contributors; and voila, a self-sustaining project is born, with its own funding, government, contributors, freelancers, and shareholders, and the initial idea would blossom into something wonderful through the power of the crowd.
16 |
17 | ### The Project
18 |
19 |
20 |
21 | The WikiGit project consists of three parts: the ***back end***, which is a set of Ethereum smart contracts that govern all of the core logics in a DASP; the ***front end***, which is a client-side web app that acts as a UI for interacting with the back end as well as a relay for connecting a DASP with its Git repository hosted by IPFS; and the ***developer tools***, which is an application for deploying a DASP onto the Ethereum blockchain. A particular DASP is defined by its back end, and different DASPs usually differ in their back ends; the front end and the developer tools are shared by all DASPs, although different implementations can be made as long as they are compatible with the back end's API.
22 |
23 | ### Project State
24 |
25 | Currently, WikiGit is in its early stages and is being worked by a handful of people. We have finished a preliminary implementation of the back end, a partial implementation of the front end (specifically the UI and back end for interacting with the IPFS Git repo and the member manifest) and the back end of the developer tools. We will do my best to find some more contributors, and anyone is welcome to join! (\***cough**\*you\***cough**\*)
26 |
27 | ## Usage
28 |
29 | ### Front End UI
30 |
31 | #### Prerequisites
32 |
33 | * Node.js ^6.11.0
34 | * Meteor ^1.6
35 | * Coffeescript ^2.0.1
36 | * Browser with web3.js support
37 |
38 | #### Running
39 |
40 | Under /client_ui/, enter the following in command line:
41 |
42 | > npm install
43 | >
44 | > coffee -c .
45 | >
46 | > meteor run
47 |
48 | The UI would be running at http://localhost:3000
49 |
50 | Note: you need the back end smart contracts deployed on the current network first in order to use the UI.
51 |
52 | ### Developer Tools
53 |
54 | #### Prerequisites
55 |
56 | * Node.js ^6.11.0
57 | * Truffle ^4.0.1
58 | * Coffeescript ^2.0.1
59 | * TestRPC ^4.1.3 (or Geth)
60 |
61 | #### Running
62 |
63 | First, edit /dev/migrate/config.json to include desired migration parameters. Then, under /dev/, enter the following in command line:
64 |
65 | > testrpc -l 5500000
66 |
67 | Or, if you want to use Geth,
68 |
69 | > geth -rpc -unlock 0
70 |
71 | In a new command line instance, enter
72 |
73 | > npm install
74 | >
75 | > truffle migrate
76 |
77 | The contracts would be deployed onto the specified network.
78 |
79 | ## Technical Details
80 |
81 | Details of the project is in [Technical Details.md](https://github.com/Project-WikiGit/WikiGit/blob/master/Technical%20Details.md)
82 |
83 | ---
84 |
85 | ## The WikiGit Ecosystem, And Why It's Awesome
86 |
87 | The WikiGit ecosystem contains three major components: ***Self-governing Projects, a Crowdsourcing Platform, and a Crowdfunding Platform***. Each of the components have been implemented individually before, but the existing products all fail to cooperate and form a complete ecosystem:
88 |
89 | * **GitHub** has been the go-to place for hosting open source projects, with support for pull requests that utilize naïve crowdsourcing to help develop projects, as well as other project management tools (issues, projects, wiki, etc.) However, GitHub does not account for many other crucial parts of creating and maintaining a project, such as
90 |
91 | * obtaining funding;
92 | * making collective decisions that shape the future of a project;
93 | * providing financial incentives for contribution;
94 | * systematically recognizing someone's contribution to the project.
95 |
96 | * **Topcoder** is currently the prominent crowdsourcing platform for software development and UX design, where developers and designers compete to submit the best solution and receive the bounty. It has helped companies large and small to efficiently solve problems that they aren't familiar with/specialized in without the need to seek out full-time talents, as well as offers companies a new way of allocating project resources. However, Topcoder is less of a ***crowdsourcing*** platform and more of an ***outsourcing*** platform, because the companies and talents only have a tangential relationship, in that
97 |
98 | * talents can only work on relatively insignificant tasks nonessential to the companies' products, cannot continue working on projects they found interesting, and cannot join companies they found interesting;
99 | * companies do not have a formal channel for recruiting talent as full-time or part-time employees, and thus cannot rely on crowdsourcing as the main method for product development.
100 |
101 | * **Kickstarter** is one of the most successful crowdfunding platforms, where creators with brilliant ideas can fund their projects through the financial support of people who love their ideas, and ordinary users can easily fund projects they find interesting and receive numerous benefits depending on the amount of their donation. However,
102 |
103 | * Kickstarter patrons who have the necessary skills cannot easily contribute to the project through means other than financial support, which is bad for both the patrons and the creators;
104 | * project teams are not necessarily transparent in sharing the state of their projects;
105 | * there is no formal way for patrons to suggest how the projects should be developed and influence the outcome of the projects.
106 |
107 | * **Initial Coin Offering (ICO)** is a crowdfunding method not dissimilar to IPOs that recently emerged on the Ethereum blockchain, where project teams offer custom ERC20 tokens in exchange for Ether, a type of cryptocurrency. The tokens are usually claimed to have utility on the applications that the teams are building. Funders would judge a project's feasibility based on its whitepaper, the current state of the project, the roadmap, etc., and send Ether to the project's team during the ICO if they feel optimistic about the project's future. ICOs have seen amazing success, with small teams raising millions of US$ of funding, but it suffers many severe problems that most people in the space have observed:
108 |
109 | * any small team with a whitepaper and a good-looking website seem to have been able to raise millions, which many observers have claimed as "scammy", and in fact many of the projects *are* scams. This problem stems from the lack of transparency in the projects.
110 | * Most of the time, the applications that the project teams are building don't actually require a custom token to function, and Ether, the native currency of Ethereum, would easily suffice. This would lead to a fragmented blockchain, where a user must hold a type of token for each application one is using.
111 | * IPO usually occurs before a team has any presentable product, and the uncertainty would a) scare many investors away and b) make it difficult for investors to determine the feasibility of a project.
112 |
113 |
114 | WikiGit aims to solve all of the abovementioned problems through organically combining self-governing projects, crowdsourcing, and crowdfunding to form a vibrant ecosystem for innovation & cooperation, where
115 |
116 | * project teams can easily obtain funding, recruit talented people, and use bounties to crowdsource tasks;
117 | * talents can do freelancing, work on projects they're excited about without any barriers, and join any project team with minimal red-tape;
118 | * contributors and patrons can vote on proposals to make decisions for the project, their votes are weighted by the amount of their contribution and investment, and neither party would be able to dominate the other;
119 | * investors can invest in projects with minimal risk and uncertainty, as everything in a project is transparent and instead of a single million-dollar ICO, a project will have many small-scale coin offerings spread out across different phases of the project.
120 |
121 | The details of the WikiGit ecosystem will be explained below.
122 |
123 | ### Contribution-based Fair Governance
124 |
125 | In a DASP, members use voting sessions to make collective decisions about the project. Each member's vote is weighted not only by how much shares (tokens issued during coin offerings) they hold, but also the member's ***reputation score***: a metric for measuring how involved a member is with the project, determined mostly by **the number of tasks** a member has completed.
126 |
127 | Each member has two types of reputation: *good reputation* and *bad reputation*. A member can earn good reputation through completing tasks, and the amount rewarded depends on the difficulty of the task. A member would receive bad reputation when the member submits a task solution that's deemed malicious. A member's reputation score is specific to each DASP, but there will be a platform for viewing a user's reputation score in all of the DASPs they're in.
128 |
129 | The reputation score can be used by project teams to determine, say, whether or not to add a user as a full/part time member. The hope is that the reputation system can
130 |
131 | * incentivize users to contribute to projects they're passionate about, since a better reputation means more say in project decisions;
132 | * incentivize users to behave honestly, since even a small amount of bad reputation may discourage project teams from recruiting a user, thus tainting all the good reputation a user have earned;
133 | * **incentivize users to complete all the low-hanging tasks across DASPs to get a better overall reputation, so that**
134 | * **users are encouraged to discover different projects, and**
135 | * **even infant projects without existing funds can get tasks done by offering reputation rewards.**
136 |
137 | Of course, since reputation systems are complex and need thorough designing, the system described above is only an early version and may be subject to change.
138 |
139 | A voting-based governing system where both financial support and actual contribution to the project are taken into account can, perhaps, be truly "fair", in that both the investors' and the contributors' interests are represented, instead of only the investors' interests---what we have in today's corporate world.
140 |
141 | ### Zero-barrier Crowdsourcing & Talent Recruitment
142 |
143 | In WikiGit, a user can choose to complete any task on the crowdsourcing platform as a freelancer and get paid, without asking for any authorization etc.. This is the existing norm on crowdsourcing platforms, but what differentiates WikiGit is that WikiGit is project-oriented rather than task-oriented, meaning that the user can continue working on a particular project if they found it interesting, and even attempt to join the project team backed by all the reputation they earned through completing tasks.
144 |
145 | **Coupled with the fact that users are incentivized to discover projects and freelance for them (see *Contribution-based Fair Governance*, 3rd paragraph), the WikiGit ecosystem automatically pairs users with projects they would be interested in, which in turn solves the problem of finding talents for DASPs.**
146 |
147 | ### Transparent & Effective Coin Offerings
148 |
149 | WikiGit DASPs use coin offerings as the main method for funding the projects. Compared to the traditional projects that use ICOs to fund themselves after they have their white papers and minimalistic websites, DASPs have the following advantages when it comes to funding:
150 |
151 | * When evaluating the state of a project, investors can base their decisions on maximal information gathered from looking at the project's Git repo, checking out current task listings, and viewing the team members' reputation and contribution history. This ensures that investors can make far more educated and low-risk decisions, and that only honest projects with active development can stand out to investors.
152 | * In contrast, a traditional ICO only provides investors with the team's grand vision and some rough roadmap, and at most some generic descriptions of the work currently being done. Yikes.
153 | * DASPs can use multiple small-scale coin offerings spread throughout different phases of the project as their source of funding, rather than single million-dollar ICOs held before the project team had even made any tangible progress. It cannot be more obvious that investing in DASPs' coin offerings has far lower uncertainty and risk for investors than investing in ICOs.
154 | * The tokens offered by DASPs have intrinsic value, in that holders can use them as votes during DASP voting sessions and influence the future of the projects.
155 | * In contrast, the value of most ICO tokens come from vague promises of future utility in the dApps the teams may or may not be building, and most project teams can't even justify why their dApps can't just use Ether instead of their custom tokens. This trend can lead to the fragmentation of the Ethereum blockchain, which will force future users to hold a different token for each dApp they use.
156 |
157 | In conclusion, DASP coin offerings are low-risk and low-uncertainty for investors, and the transparency of DASPs ensures that only honest projects can get funding from investors.
158 |
159 | ### Customization & Expandability
160 |
161 | Virtually all parameters of a DASP---length of a voting session, rights of different member groups, etc.--- are designed to be customizable, in that all one needs to do to change a certain parameter is to start a voting session. Also, since DASPs are essentially smart contracts, WikiGit allows project teams to customize literally every last detail of their DASPs via altering the contract code, as long as the contracts remain compatible with the rest of the ecosystem.
162 |
163 | WikiGit also allows DASPs to plug in third party modules to expand their functionalities, so the possibilities are endless. Here are a few examples:
164 |
165 | * A DASP can connect its task listings with a third party crowdsourcing platform, so that its talent pool is larger.
166 | * A DASP can be connected to a prediction market that predicts which of the task solution submissions are likely to be accepted, so that the validators can have a heuristic for deciding which solutions are the best.
167 | * A DASP can connect its reputation system with its discussion forum so that users who contribute valuable content are rewarded with good reputation, and users who violate the forum policies receive bad reputation.
168 |
169 | We expect to see a new industry of DASPs that are dedicated to making custom modules, and it's possible that WikiGit will have a module manager similar to npm for Node.js and pip for Python.
170 |
171 | ## Contact
172 |
173 | Feel free to contact us at info@wikigit.org !
174 |
--------------------------------------------------------------------------------
/Technical Details.md:
--------------------------------------------------------------------------------
1 | ## Technical Details
2 |
3 | This document describes the details of the three components of WikiGit: the back end, the front end, and the developer tools. The back end is under [/dev/contracts/](/dev/contracts/), the front end is under [/client_ui/](/client_ui/), and the developer tools is under [/dev/](/dev/).
4 |
5 | This document is for WikiGit version **0.2.0 (Grasshopper)**.
6 |
7 | ### The Back End
8 |
9 | The back end includes a ***DAO*** with a fully fledged voting mechanism; a ***MemberHandler*** that acts as a member manifest, with different rights associated with each member group; a ***Vault*** that allows for the safe withdrawl of funds, as well as holding coin offerings; a ***TasksHandler*** for publishing tasks and accepting solutions on the crowdsourcing platform; and a ***RepoHandler*** for democratically determining the IPFS hash of the DASP's git repository.
10 |
11 | #### Modular Design
12 |
13 | All of the core smart contracts mentioned above were designed as individual modules. This design allows for both updating the code down the road via contract swapping, and introducing new functionalities via adding lightweight modules in order to prevent frequent updates of the heavyweight core contracts. Even though the frequent intercontract communication of a modularized design entails costing more gas during normal usage than a single-contract infrastructure, the benefits of this design means that it's a lot more easier to maintain, update, and debug such systems, which makes large-scale, complex DASPs possible.
14 |
15 | Also, the modularized design means that it is possible to swap out a core module for something that's compatible with any third-party app of your choosing. For instance, you can replace the TaskHandler module for a contract that publishes task listings onto a third-party platform.
16 |
17 | As of Grasshopper, upgrading a module without writing a specific migration script would result in the loss of all data stored in the module. We intend to address this issue later on.
18 |
19 | #### The Main Contract
20 |
21 | The main contract serves as an index for all of a DASP's modules. Modules can be added/removed through DAO votings. It is the only contract in the back end that cannot be upgraded, but it's simple enough that you won't need to.
22 |
23 | #### DAO
24 |
25 | The DAO module handles the voting mechanics of the DASP. Any member that has the right to do so can start voting sessions, vote on proposals, conclude voting sessions after the alotted time has passed, execute the proposal, and so on.
26 |
27 | The votes that each member have are weighted sums of multiple values: the member's reputation (determined by how many tasks they've completed) and the amount of DASP specific token they own.
28 |
29 | The attributes of a voting session are determined at DAO's creation, where one can set properties such as the quorum, minimum proportion of support votes required, the length of the voting session, and the weights involved in calculating each member's vote.
30 |
31 | The execution of a voting works as follows: when creating a voting, you can specify a transaction bytecode (via passing in its hash) and an address that will receive the transaction. After the voting has been passed, anyone can call the executeVoting() function to execute the transaction bytecode, provided that they have the bytecode that correspond to the stored hash. It's similar to the democracy contract at https://www.ethereum.org/dao .
32 |
33 | #### MemberHandler
34 |
35 | The MemberHandler module stores the information about members of the DASP, and about member groups with different rights in the DASP. It also implements basic member manipulation features, like adding, removing, and banning members.
36 |
37 | There are three groups of members: Team members, contributors, and pure shareholders.
38 |
39 | * Team members are the core contributors to the project, with the right to vote, start voting sessions, post tasks with bounties, submit task solutions, vote on task solutions, and provide IPFS hashes to the DASP's repo.
40 |
41 | * Contributors are people who contribute to the DASP part-time, with the right to vote, start voting sessions, post tasks without bounties, and submit task solutions.
42 |
43 | * Pure shareholders are people who own DASP specific tokens issued during coin offerings, and do not contribute to the DASP, with the right to vote and start voting sessions.
44 |
45 | In Grasshopper, the member groups and the associated rights can be modified, but this feature will not be included in the UI.
46 |
47 | #### Vault
48 |
49 | The Vault module implements the safe withdrawl of funds through freezing pending withdrawals for a certain period of time. During this time, the DAO can use a voting session to veto a malicious withdrawl. The freeze period can be different for withdrawals initiated by the DAO and withdrawls initiated by task posters who want to reward the submitter of solutions they accepted, so that you can shorten the freeze period for DAO withdrawals if you choose to trust DAO votings (you should).
50 |
51 | The Vault also implements ***coin offerings***, which means that during set periods of time, the Vault can tell a token contract to mint a certain amount of tokens proportional to the amount of Ether someone donates to the Vault contract, and send them to that person's address.
52 |
53 | #### TasksHandler
54 |
55 | TasksHandler handles functions such as posting tasks, submitting task solutions, and accepting task solutions.
56 |
57 | The life of a task is as follows: a member posts a task => interested members clone the repository => members complete the task => members upload the Git patch to IPFS => members submit the IPFS hash to the DASP => team members vote on solutions => A solution upvoted by more than 2/3 team members is accepted, while solutions downvoted by more than 2/3 team members are penalized => reward can be withdrawn by the winner after the freeze period => team members download patch, apply to repo, and upload to IPFS => team members update their version of the most up-to-date repo in RepoHandler, in the form of IPFS hashes => the IPFS hash with majority support is displayed as the repo's hash in the UI
58 |
59 | Completing tasks can not only provide financial rewards like Ether and tokens, but also provide good reputation. The amount of reputational reward you receive for completing a task is specified by the task's poster. On the flip side, all members also have a bad reputation attached to them. You receive bad reputation when you're penalized by the DASP for submitting malicious task solutions. Reputations are local to the DASP, but it's possible to build a platform for viewing an account's reputation among all DASPs. If a member behaves too maliciously in a DASP, the DAO can ban the member through a voting session.
60 |
61 | #### RepoHandler
62 |
63 | The purpose of RepoHandler has been explained in the TasksHandler section.
64 |
65 |
66 |
67 | For more information about the back end, just check out the code! All of the contracts were written with readability in mind and have been well commented, so it'd be a treat for those who prefer code to English.
68 |
69 | ### The Front End
70 |
71 | The front end is a client-side web app that will be hosted on the IPFS network. It will include a registry where users can discover projects, as well as the UI needed to interact with DASPs.
72 |
73 | The front end is based on Meteor and Node.js. It uses web3.js to communicate with a DASP's back end, and gift (a wrapper for the Git CLI) & js-ipfs-api to communicate with the DASP's Git repository on IPFS.
74 |
75 | ### Developer Tools
76 |
77 | The developer tools is used for deploying DASPs onto the Ethereum blockchain. It is based on Truffle and node.js. Currently, it can only be used via command line, but an UI will be built in the near future.
--------------------------------------------------------------------------------
/assets/project_structure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Project-WikiGit/WikiGit/8ef5be8d3015d26685b34bbf414c65bd8d721eb2/assets/project_structure.png
--------------------------------------------------------------------------------
/client_ui/.meteor/.finished-upgraders:
--------------------------------------------------------------------------------
1 | # This file contains information which helps Meteor properly upgrade your
2 | # app when you run 'meteor update'. You should check it into version control
3 | # with your project.
4 |
5 | notices-for-0.9.0
6 | notices-for-0.9.1
7 | 0.9.4-platform-file
8 | notices-for-facebook-graph-api-2
9 | 1.2.0-standard-minifiers-package
10 | 1.2.0-meteor-platform-split
11 | 1.2.0-cordova-changes
12 | 1.2.0-breaking-changes
13 | 1.3.0-split-minifiers-package
14 | 1.4.0-remove-old-dev-bundle-link
15 | 1.4.1-add-shell-server-package
16 | 1.4.3-split-account-service-packages
17 | 1.5-add-dynamic-import-package
18 |
--------------------------------------------------------------------------------
/client_ui/.meteor/.gitignore:
--------------------------------------------------------------------------------
1 | local
2 |
--------------------------------------------------------------------------------
/client_ui/.meteor/.id:
--------------------------------------------------------------------------------
1 | # This file contains a token that is unique to your project.
2 | # Check it into your repository along with the rest of this directory.
3 | # It can be used for purposes such as:
4 | # - ensuring you don't accidentally deploy one app on top of another
5 | # - providing package authors with aggregated statistics
6 |
7 | 4jp2jziomsb0x59v7n
8 |
--------------------------------------------------------------------------------
/client_ui/.meteor/packages:
--------------------------------------------------------------------------------
1 | # Meteor packages used by this project, one per line.
2 | # Check this file (and the other files in this directory) into your repository.
3 | #
4 | # 'meteor add' and 'meteor remove' will edit this file for you,
5 | # but you can also edit it by hand.
6 |
7 | meteor-base@1.2.0 # Packages every Meteor app needs to have
8 | mobile-experience@1.0.5 # Packages for a great mobile UX
9 | mongo@1.3.0 # The database Meteor supports right now
10 | blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
11 | reactive-var@1.0.11 # Reactive variable for tracker
12 | tracker@1.1.3 # Meteor's client-side reactive programming library
13 |
14 | standard-minifier-css@1.3.5 # CSS minifier run for production mode
15 | standard-minifier-js@2.2.0 # JS minifier run for production mode
16 | es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers
17 | ecmascript@0.9.0 # Enable ECMAScript2015+ syntax in app code
18 | shell-server@0.3.0 # Server-side component of the `meteor shell` command
19 |
20 | autopublish@1.0.7 # Publish all data to the clients (for prototyping)
21 | insecure@1.0.7 # Allow all DB writes from clients (for prototyping)
22 |
--------------------------------------------------------------------------------
/client_ui/.meteor/platforms:
--------------------------------------------------------------------------------
1 | server
2 | browser
3 |
--------------------------------------------------------------------------------
/client_ui/.meteor/release:
--------------------------------------------------------------------------------
1 | METEOR@1.6
2 |
--------------------------------------------------------------------------------
/client_ui/.meteor/versions:
--------------------------------------------------------------------------------
1 | allow-deny@1.1.0
2 | autopublish@1.0.7
3 | autoupdate@1.3.12
4 | babel-compiler@6.24.7
5 | babel-runtime@1.1.1
6 | base64@1.0.10
7 | binary-heap@1.0.10
8 | blaze@2.3.2
9 | blaze-html-templates@1.1.2
10 | blaze-tools@1.0.10
11 | boilerplate-generator@1.3.1
12 | caching-compiler@1.1.9
13 | caching-html-compiler@1.1.2
14 | callback-hook@1.0.10
15 | check@1.2.5
16 | ddp@1.4.0
17 | ddp-client@2.2.0
18 | ddp-common@1.3.0
19 | ddp-server@2.1.1
20 | deps@1.0.12
21 | diff-sequence@1.0.7
22 | dynamic-import@0.2.1
23 | ecmascript@0.9.0
24 | ecmascript-runtime@0.5.0
25 | ecmascript-runtime-client@0.5.0
26 | ecmascript-runtime-server@0.5.0
27 | ejson@1.1.0
28 | es5-shim@4.6.15
29 | geojson-utils@1.0.10
30 | hot-code-push@1.0.4
31 | html-tools@1.0.11
32 | htmljs@1.0.11
33 | http@1.3.0
34 | id-map@1.0.9
35 | insecure@1.0.7
36 | jquery@1.11.10
37 | launch-screen@1.1.1
38 | livedata@1.0.18
39 | logging@1.1.19
40 | meteor@1.8.2
41 | meteor-base@1.2.0
42 | minifier-css@1.2.16
43 | minifier-js@2.2.2
44 | minimongo@1.4.2
45 | mobile-experience@1.0.5
46 | mobile-status-bar@1.0.14
47 | modules@0.11.0
48 | modules-runtime@0.9.1
49 | mongo@1.3.0
50 | mongo-dev-server@1.1.0
51 | mongo-id@1.0.6
52 | npm-mongo@2.2.33
53 | observe-sequence@1.0.16
54 | ordered-dict@1.0.9
55 | promise@0.10.0
56 | random@1.0.10
57 | reactive-var@1.0.11
58 | reload@1.1.11
59 | retry@1.0.9
60 | routepolicy@1.0.12
61 | shell-server@0.3.0
62 | spacebars@1.0.15
63 | spacebars-compiler@1.1.3
64 | standard-minifier-css@1.3.5
65 | standard-minifier-js@2.2.2
66 | templating@1.3.2
67 | templating-compiler@1.3.3
68 | templating-runtime@1.3.2
69 | templating-tools@1.1.2
70 | tracker@1.1.3
71 | ui@1.0.13
72 | underscore@1.0.10
73 | url@1.1.0
74 | webapp@1.4.0
75 | webapp-hashing@1.0.9
76 |
--------------------------------------------------------------------------------
/client_ui/client/main.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Project-WikiGit/WikiGit/8ef5be8d3015d26685b34bbf414c65bd8d721eb2/client_ui/client/main.css
--------------------------------------------------------------------------------
/client_ui/client/main.html:
--------------------------------------------------------------------------------
1 |
2 | WikiGit
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/client_ui/client/main.js:
--------------------------------------------------------------------------------
1 | import '../imports/ui/dasp_dashboard.js';
--------------------------------------------------------------------------------
/client_ui/imports/abi/mainABI.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "constant": true,
4 | "inputs": [
5 | {
6 | "name": "",
7 | "type": "bytes32"
8 | }
9 | ],
10 | "name": "moduleAddresses",
11 | "outputs": [
12 | {
13 | "name": "",
14 | "type": "address"
15 | }
16 | ],
17 | "payable": false,
18 | "type": "function"
19 | },
20 | {
21 | "constant": true,
22 | "inputs": [],
23 | "name": "hasInitedABIs",
24 | "outputs": [
25 | {
26 | "name": "",
27 | "type": "bool"
28 | }
29 | ],
30 | "payable": false,
31 | "type": "function"
32 | },
33 | {
34 | "constant": true,
35 | "inputs": [
36 | {
37 | "name": "",
38 | "type": "bytes32"
39 | }
40 | ],
41 | "name": "abiHashId",
42 | "outputs": [
43 | {
44 | "name": "",
45 | "type": "uint256"
46 | }
47 | ],
48 | "payable": false,
49 | "type": "function"
50 | },
51 | {
52 | "constant": true,
53 | "inputs": [],
54 | "name": "metadata",
55 | "outputs": [
56 | {
57 | "name": "",
58 | "type": "string"
59 | }
60 | ],
61 | "payable": false,
62 | "type": "function"
63 | },
64 | {
65 | "constant": true,
66 | "inputs": [
67 | {
68 | "name": "",
69 | "type": "uint256"
70 | }
71 | ],
72 | "name": "moduleNames",
73 | "outputs": [
74 | {
75 | "name": "",
76 | "type": "string"
77 | }
78 | ],
79 | "payable": false,
80 | "type": "function"
81 | },
82 | {
83 | "constant": true,
84 | "inputs": [
85 | {
86 | "name": "",
87 | "type": "uint256"
88 | }
89 | ],
90 | "name": "abiIPFSHashes",
91 | "outputs": [
92 | {
93 | "name": "",
94 | "type": "bytes"
95 | }
96 | ],
97 | "payable": false,
98 | "type": "function"
99 | },
100 | {
101 | "constant": false,
102 | "inputs": [
103 | {
104 | "name": "index",
105 | "type": "uint256"
106 | }
107 | ],
108 | "name": "removeModuleAtIndex",
109 | "outputs": [],
110 | "payable": false,
111 | "type": "function"
112 | },
113 | {
114 | "constant": false,
115 | "inputs": [
116 | {
117 | "name": "modId",
118 | "type": "uint256"
119 | },
120 | {
121 | "name": "abiHash",
122 | "type": "bytes"
123 | }
124 | ],
125 | "name": "initializeABIHashForMod",
126 | "outputs": [],
127 | "payable": false,
128 | "type": "function"
129 | },
130 | {
131 | "constant": false,
132 | "inputs": [
133 | {
134 | "name": "modName",
135 | "type": "string"
136 | },
137 | {
138 | "name": "addr",
139 | "type": "address"
140 | },
141 | {
142 | "name": "isNew",
143 | "type": "bool"
144 | }
145 | ],
146 | "name": "setModuleAddress",
147 | "outputs": [],
148 | "payable": false,
149 | "type": "function"
150 | },
151 | {
152 | "constant": false,
153 | "inputs": [
154 | {
155 | "name": "meta",
156 | "type": "string"
157 | }
158 | ],
159 | "name": "setMetadata",
160 | "outputs": [],
161 | "payable": false,
162 | "type": "function"
163 | },
164 | {
165 | "constant": false,
166 | "inputs": [
167 | {
168 | "name": "addrs",
169 | "type": "address[]"
170 | }
171 | ],
172 | "name": "initializeModuleAddresses",
173 | "outputs": [],
174 | "payable": false,
175 | "type": "function"
176 | },
177 | {
178 | "constant": true,
179 | "inputs": [
180 | {
181 | "name": "modHash",
182 | "type": "bytes32"
183 | }
184 | ],
185 | "name": "getABIHashForMod",
186 | "outputs": [
187 | {
188 | "name": "abiHash",
189 | "type": "bytes"
190 | }
191 | ],
192 | "payable": false,
193 | "type": "function"
194 | },
195 | {
196 | "constant": true,
197 | "inputs": [],
198 | "name": "hasInitedAddrs",
199 | "outputs": [
200 | {
201 | "name": "",
202 | "type": "bool"
203 | }
204 | ],
205 | "payable": false,
206 | "type": "function"
207 | },
208 | {
209 | "constant": false,
210 | "inputs": [
211 | {
212 | "name": "modHash",
213 | "type": "bytes32"
214 | },
215 | {
216 | "name": "abiHash",
217 | "type": "bytes"
218 | }
219 | ],
220 | "name": "setABIHashForMod",
221 | "outputs": [],
222 | "payable": false,
223 | "type": "function"
224 | },
225 | {
226 | "inputs": [
227 | {
228 | "name": "_metadata",
229 | "type": "string"
230 | },
231 | {
232 | "name": "_abiIPFSHash",
233 | "type": "bytes"
234 | }
235 | ],
236 | "payable": false,
237 | "type": "constructor"
238 | },
239 | {
240 | "payable": false,
241 | "type": "fallback"
242 | }
243 | ]
--------------------------------------------------------------------------------
/client_ui/imports/daemon/git-ipfs.coffee:
--------------------------------------------------------------------------------
1 | ###
2 | git-ipfs.coffee
3 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project.
4 |
5 | This file implements a daemon that listens for the TaskSolutionAccepted() event
6 | from the GitHandler module. Upon such an event, the daemon would clone the
7 | DASP's Git repo, pull from the updated repo where the task has been completed
8 | to merge the solution into the DASP's repo, publish the resulting repo onto IPFS,
9 | and send its IPFS multihash back to GitHandler as the current location of the DASP's repo.
10 | ###
11 |
12 | import {dasp} from '../ui/dasp_dashboard.js'
13 |
14 | #Import web3
15 | Web3 = require 'web3'
16 | web3 = window.web3
17 | if typeof web3 != undefined
18 | web3 = new Web3(web3.currentProvider)
19 | else
20 | web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"))
21 |
22 | web3.eth.getAccounts().then(
23 | (accounts) ->
24 | web3.eth.defaultAccount = accounts[0]
25 | )
26 |
27 | #Import node modules
28 | ipfsAPI = require 'ipfs-api'
29 | ipfs = ipfsAPI('ipfs.infura.io', '5001', {protocol: 'https'})
30 | git = require 'gift'
31 | fs = require 'fs'
32 | keccak256 = require('js-sha3').keccak256
33 |
34 | #Helper functions
35 | hexToStr = (hex) ->
36 | hex = hex.substr(2)
37 | str = ''
38 | for i in [0..hex.length - 1] by 2
39 | str += String.fromCharCode(parseInt(hex.substr(i, 2), 16))
40 | return str
41 |
42 | export StartDaemon = () ->
43 | #Fetch contract abstractions
44 | tasksHandlerContract = dasp.contracts.tasks
45 | gitHandlerContract = dasp.contracts.git
46 |
47 | solutionAcceptedEvent = tasksHandlerContract.events.TaskSolutionAccepted()
48 | solutionAcceptedEvent.on('data', (event) ->
49 | patchIPFSHash = hexToStr event.returnValues.patchIPFSHash
50 | gitHandlerContract.methods.getCurrentIPFSHash().call().then(
51 | (result) ->
52 | masterIPFSHash = hexToStr result
53 | masterPath = "./tmp/#{masterIPFSHash}/"
54 |
55 | #Create repo directory if it doesn't exist
56 | if !fs.existsSync(masterPath)
57 | if !fs.existsSync('./tmp')
58 | fs.mkdirSync('./tmp')
59 | fs.mkdirSync(masterPath)
60 |
61 | #Clone the master
62 | git.clone("git@gateway.ipfs.io/ipfs/" + masterIPFSHash.toString(), masterPath, Number.POSITIVE_INFINITY, "master", (error, _repo) ->
63 | if error != null
64 | throw error
65 | repo = _repo
66 | #Add patched repo as remote
67 | repo.remote_add("solution", "gateway.ipfs.io/ipfs/#{patchIPFSHash}", (error) ->
68 | if error != null
69 | throw error
70 | #Pull the patched repo and merge with the master
71 | repo.pull("solution", "master", (error) ->
72 | if error != null
73 | throw error
74 | #Add new repo to the IPFS network
75 | ipfs.util.addFromFs(masterPath, {recursive: true}, (error, result) ->
76 | if error != null
77 | throw error
78 | for entry in result
79 | if entry.path is masterIPFSHash
80 | gitHandlerContract.methods.commitTaskSolutionToRepo(
81 | event.returnValues.taskId,
82 | event.returnValues.solId,
83 | entry.hash
84 | ).send()
85 | break
86 | )
87 | )
88 | )
89 | )
90 | )
91 | )
92 | return
93 |
--------------------------------------------------------------------------------
/client_ui/imports/daemon/git-ipfs.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 2.0.1
2 | /*
3 | git-ipfs.coffee
4 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project.
5 |
6 | This file implements a daemon that listens for the TaskSolutionAccepted() event
7 | from the GitHandler module. Upon such an event, the daemon would clone the
8 | DASP's Git repo, pull from the updated repo where the task has been completed
9 | to merge the solution into the DASP's repo, publish the resulting repo onto IPFS,
10 | and send its IPFS multihash back to GitHandler as the current location of the DASP's repo.
11 | */
12 | var Web3, fs, git, hexToStr, ipfs, ipfsAPI, keccak256, web3;
13 |
14 | import {
15 | dasp
16 | } from '../ui/dasp_dashboard.js';
17 |
18 | //Import web3
19 | Web3 = require('web3');
20 |
21 | web3 = window.web3;
22 |
23 | if (typeof web3 !== void 0) {
24 | web3 = new Web3(web3.currentProvider);
25 | } else {
26 | web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
27 | }
28 |
29 | web3.eth.getAccounts().then(function(accounts) {
30 | return web3.eth.defaultAccount = accounts[0];
31 | });
32 |
33 | //Import node modules
34 | ipfsAPI = require('ipfs-api');
35 |
36 | ipfs = ipfsAPI('ipfs.infura.io', '5001', {
37 | protocol: 'https'
38 | });
39 |
40 | git = require('gift');
41 |
42 | fs = require('fs');
43 |
44 | keccak256 = require('js-sha3').keccak256;
45 |
46 | //Helper functions
47 | hexToStr = function(hex) {
48 | var i, j, ref, str;
49 | hex = hex.substr(2);
50 | str = '';
51 | for (i = j = 0, ref = hex.length - 1; j <= ref; i = j += 2) {
52 | str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
53 | }
54 | return str;
55 | };
56 |
57 | export var StartDaemon = function() {
58 | var gitHandlerContract, solutionAcceptedEvent, tasksHandlerContract;
59 | //Fetch contract abstractions
60 | tasksHandlerContract = dasp.contracts.tasks;
61 | gitHandlerContract = dasp.contracts.git;
62 | solutionAcceptedEvent = tasksHandlerContract.events.TaskSolutionAccepted();
63 | solutionAcceptedEvent.on('data', function(event) {
64 | var patchIPFSHash;
65 | patchIPFSHash = hexToStr(event.returnValues.patchIPFSHash);
66 | return gitHandlerContract.methods.getCurrentIPFSHash().call().then(function(result) {
67 | var masterIPFSHash, masterPath;
68 | masterIPFSHash = hexToStr(result);
69 | masterPath = `./tmp/${masterIPFSHash}/`;
70 | if (!fs.existsSync(masterPath)) {
71 | if (!fs.existsSync('./tmp')) {
72 | fs.mkdirSync('./tmp');
73 | }
74 | fs.mkdirSync(masterPath);
75 | }
76 | //Clone the master
77 | return git.clone("git@gateway.ipfs.io/ipfs/" + masterIPFSHash.toString(), masterPath, Number.POSITIVE_INFINITY, "master", function(error, _repo) {
78 | var repo;
79 | if (error !== null) {
80 | throw error;
81 | }
82 | repo = _repo;
83 | //Add patched repo as remote
84 | return repo.remote_add("solution", `gateway.ipfs.io/ipfs/${patchIPFSHash}`, function(error) {
85 | if (error !== null) {
86 | throw error;
87 | }
88 | //Pull the patched repo and merge with the master
89 | return repo.pull("solution", "master", function(error) {
90 | if (error !== null) {
91 | throw error;
92 | }
93 | //Add new repo to the IPFS network
94 | return ipfs.util.addFromFs(masterPath, {
95 | recursive: true
96 | }, function(error, result) {
97 | var entry, j, len, results;
98 | if (error !== null) {
99 | throw error;
100 | }
101 | results = [];
102 | for (j = 0, len = result.length; j < len; j++) {
103 | entry = result[j];
104 | if (entry.path === masterIPFSHash) {
105 | gitHandlerContract.methods.commitTaskSolutionToRepo(event.returnValues.taskId, event.returnValues.solId, entry.hash).send();
106 | break;
107 | } else {
108 | results.push(void 0);
109 | }
110 | }
111 | return results;
112 | });
113 | });
114 | });
115 | });
116 | });
117 | });
118 | };
119 |
120 | //# sourceMappingURL=git-ipfs.js.map
121 |
--------------------------------------------------------------------------------
/client_ui/imports/daemon/git-ipfs.js.map:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "file": "git-ipfs.js",
4 | "sourceRoot": "",
5 | "sources": [
6 | "git-ipfs.coffee"
7 | ],
8 | "names": [],
9 | "mappings": ";AAAA;;;;;;;;;;AAAA,IAAA;;AAWA,OAAA;EAAQ,IAAR;CAAA,MAAA,0BAXA;;;AAcA,IAAA,GAAO,OAAA,CAAQ,MAAR;;AACP,IAAA,GAAO,MAAM,CAAC;;AACd,IAAG,OAAO,IAAP,KAAe,MAAlB;EACE,IAAA,GAAO,IAAI,IAAJ,CAAS,IAAI,CAAC,eAAd,EADT;CAAA,MAAA;EAGE,IAAA,GAAO,IAAI,IAAJ,CAAS,IAAI,IAAI,CAAC,SAAS,CAAC,YAAnB,CAAgC,uBAAhC,CAAT,EAHT;;;AAKA,IAAI,CAAC,GAAG,CAAC,WAAT,CAAA,CAAsB,CAAC,IAAvB,CACE,QAAA,CAAC,QAAD,CAAA;SACE,IAAI,CAAC,GAAG,CAAC,cAAT,GAA0B,QAAS,CAAA,CAAA;AADrC,CADF,EArBA;;;AA2BA,OAAA,GAAU,OAAA,CAAQ,UAAR;;AACV,IAAA,GAAO,OAAA,CAAQ,gBAAR,EAA0B,MAA1B,EAAkC;EAAC,QAAA,EAAU;AAAX,CAAlC;;AACP,GAAA,GAAM,OAAA,CAAQ,MAAR;;AACN,EAAA,GAAK,OAAA,CAAQ,IAAR;;AACL,SAAA,GAAY,OAAA,CAAQ,SAAR,CAAkB,CAAC,UA/B/B;;;AAkCA,QAAA,GAAW,QAAA,CAAC,GAAD,CAAA;AACT,MAAA;EAAA,GAAA,GAAM,GAAG,CAAC,MAAJ,CAAW,CAAX;EACN,GAAA,GAAM;EACN,KAAS,qDAAT;IACE,GAAA,IAAO,MAAM,CAAC,YAAP,CAAoB,QAAA,CAAS,GAAG,CAAC,MAAJ,CAAW,CAAX,EAAc,CAAd,CAAT,EAA2B,EAA3B,CAApB;EADT;AAEA,SAAO;AALE;;AAOX,OAAA,IAAO,WAAP,GAAqB,QAAA,CAAA,CAAA;AAEnB,MAAA,+DAAA;;EAAA,oBAAA,GAAuB,IAAI,CAAC,SAAS,CAAC;EACtC,kBAAA,GAAqB,IAAI,CAAC,SAAS,CAAC;EAEpC,qBAAA,GAAwB,oBAAoB,CAAC,MAAM,CAAC,oBAA5B,CAAA;EACxB,qBAAqB,CAAC,EAAtB,CAAyB,MAAzB,EAAiC,QAAA,CAAC,KAAD,CAAA;AAC/B,QAAA;IAAA,aAAA,GAAgB,QAAA,CAAS,KAAK,CAAC,YAAY,CAAC,aAA5B;WAChB,kBAAkB,CAAC,OAAO,CAAC,kBAA3B,CAAA,CAA+C,CAAC,IAAhD,CAAA,CAAsD,CAAC,IAAvD,CACE,QAAA,CAAC,MAAD,CAAA;AACE,UAAA;MAAA,cAAA,GAAiB,QAAA,CAAS,MAAT;MACjB,UAAA,GAAa,CAAA,MAAA,CAAA,CAAS,cAAT,CAAwB,CAAxB;MAGb,IAAG,CAAC,EAAE,CAAC,UAAH,CAAc,UAAd,CAAJ;QACE,IAAG,CAAC,EAAE,CAAC,UAAH,CAAc,OAAd,CAAJ;UACE,EAAE,CAAC,SAAH,CAAa,OAAb,EADF;;QAEA,EAAE,CAAC,SAAH,CAAa,UAAb,EAHF;OAJA;;aAUA,GAAG,CAAC,KAAJ,CAAU,2BAAA,GAA8B,cAAc,CAAC,QAAf,CAAA,CAAxC,EAAmE,UAAnE,EAA+E,MAAM,CAAC,iBAAtF,EAAyG,QAAzG,EAAmH,QAAA,CAAC,KAAD,EAAQ,KAAR,CAAA;AACjH,YAAA;QAAA,IAAG,KAAA,KAAS,IAAZ;UACE,MAAM,MADR;;QAEA,IAAA,GAAO,MAFP;;eAIA,IAAI,CAAC,UAAL,CAAgB,UAAhB,EAA4B,CAAA,qBAAA,CAAA,CAAwB,aAAxB,CAAA,CAA5B,EAAqE,QAAA,CAAC,KAAD,CAAA;UACnE,IAAG,KAAA,KAAS,IAAZ;YACE,MAAM,MADR;WAAA;;iBAGA,IAAI,CAAC,IAAL,CAAU,UAAV,EAAsB,QAAtB,EAAgC,QAAA,CAAC,KAAD,CAAA;YAC9B,IAAG,KAAA,KAAS,IAAZ;cACE,MAAM,MADR;aAAA;;mBAGA,IAAI,CAAC,IAAI,CAAC,SAAV,CAAoB,UAApB,EAAgC;cAAC,SAAA,EAAW;YAAZ,CAAhC,EAAmD,QAAA,CAAC,KAAD,EAAQ,MAAR,CAAA;AACjD,kBAAA;cAAA,IAAG,KAAA,KAAS,IAAZ;gBACE,MAAM,MADR;;AAEA;cAAA,KAAA,wCAAA;;gBACE,IAAG,KAAK,CAAC,IAAN,KAAc,cAAjB;kBACE,kBAAkB,CAAC,OAAO,CAAC,wBAA3B,CACE,KAAK,CAAC,YAAY,CAAC,MADrB,EAEE,KAAK,CAAC,YAAY,CAAC,KAFrB,EAGE,KAAK,CAAC,IAHR,CAIC,CAAC,IAJF,CAAA;AAKA,wBANF;iBAAA,MAAA;uCAAA;;cADF,CAAA;;YAHiD,CAAnD;UAJ8B,CAAhC;QAJmE,CAArE;MALiH,CAAnH;IAXF,CADF;EAF+B,CAAjC;AANmB",
10 | "sourcesContent": [
11 | "###\r\n git-ipfs.coffee\r\n Created by Zefram Lou (Zebang Liu) as part of the WikiGit project.\r\n\r\n This file implements a daemon that listens for the TaskSolutionAccepted() event\r\n from the GitHandler module. Upon such an event, the daemon would clone the\r\n DASP's Git repo, pull from the updated repo where the task has been completed\r\n to merge the solution into the DASP's repo, publish the resulting repo onto IPFS,\r\n and send its IPFS multihash back to GitHandler as the current location of the DASP's repo.\r\n###\r\n\r\nimport {dasp} from '../ui/dasp_dashboard.js'\r\n\r\n#Import web3\r\nWeb3 = require 'web3'\r\nweb3 = window.web3\r\nif typeof web3 != undefined\r\n web3 = new Web3(web3.currentProvider)\r\nelse\r\n web3 = new Web3(new Web3.providers.HttpProvider(\"http://localhost:8545\"))\r\n\r\nweb3.eth.getAccounts().then(\r\n (accounts) ->\r\n web3.eth.defaultAccount = accounts[0]\r\n)\r\n\r\n#Import node modules\r\nipfsAPI = require 'ipfs-api'\r\nipfs = ipfsAPI('ipfs.infura.io', '5001', {protocol: 'https'})\r\ngit = require 'gift'\r\nfs = require 'fs'\r\nkeccak256 = require('js-sha3').keccak256\r\n\r\n#Helper functions\r\nhexToStr = (hex) ->\r\n hex = hex.substr(2)\r\n str = ''\r\n for i in [0..hex.length - 1] by 2\r\n str += String.fromCharCode(parseInt(hex.substr(i, 2), 16))\r\n return str\r\n\r\nexport StartDaemon = () ->\r\n #Fetch contract abstractions\r\n tasksHandlerContract = dasp.contracts.tasks\r\n gitHandlerContract = dasp.contracts.git\r\n\r\n solutionAcceptedEvent = tasksHandlerContract.events.TaskSolutionAccepted()\r\n solutionAcceptedEvent.on('data', (event) ->\r\n patchIPFSHash = hexToStr event.returnValues.patchIPFSHash\r\n gitHandlerContract.methods.getCurrentIPFSHash().call().then(\r\n (result) ->\r\n masterIPFSHash = hexToStr result\r\n masterPath = \"./tmp/#{masterIPFSHash}/\"\r\n\r\n #Create repo directory if it doesn't exist\r\n if !fs.existsSync(masterPath)\r\n if !fs.existsSync('./tmp')\r\n fs.mkdirSync('./tmp')\r\n fs.mkdirSync(masterPath)\r\n\r\n #Clone the master\r\n git.clone(\"git@gateway.ipfs.io/ipfs/\" + masterIPFSHash.toString(), masterPath, Number.POSITIVE_INFINITY, \"master\", (error, _repo) ->\r\n if error != null\r\n throw error\r\n repo = _repo\r\n #Add patched repo as remote\r\n repo.remote_add(\"solution\", \"gateway.ipfs.io/ipfs/#{patchIPFSHash}\", (error) ->\r\n if error != null\r\n throw error\r\n #Pull the patched repo and merge with the master\r\n repo.pull(\"solution\", \"master\", (error) ->\r\n if error != null\r\n throw error\r\n #Add new repo to the IPFS network\r\n ipfs.util.addFromFs(masterPath, {recursive: true}, (error, result) ->\r\n if error != null\r\n throw error\r\n for entry in result\r\n if entry.path is masterIPFSHash\r\n gitHandlerContract.methods.commitTaskSolutionToRepo(\r\n event.returnValues.taskId,\r\n event.returnValues.solId,\r\n entry.hash\r\n ).send()\r\n break\r\n )\r\n )\r\n )\r\n )\r\n )\r\n )\r\n return\r\n"
12 | ]
13 | }
--------------------------------------------------------------------------------
/client_ui/imports/objects/dasp.coffee:
--------------------------------------------------------------------------------
1 | #Import web3
2 | Web3 = require 'web3'
3 | web3 = window.web3
4 | if typeof web3 != undefined
5 | web3 = new Web3(web3.currentProvider)
6 | else
7 | web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"))
8 |
9 | web3.eth.getAccounts().then(
10 | (accounts) ->
11 | web3.eth.defaultAccount = accounts[0]
12 | )
13 |
14 | #Import node modules
15 | ipfsAPI = require 'ipfs-api'
16 | ipfs = ipfsAPI('ipfs.infura.io', '5001', {protocol: 'https'})
17 | git = require 'gift'
18 | fs = require 'fs'
19 | bl = require 'bl'
20 | keccak256 = require('js-sha3').keccak256
21 |
22 | #Helper functions
23 | hexToStr = (hex) ->
24 | hex = hex.substr(2)
25 | str = ''
26 | for i in [0..hex.length - 1] by 2
27 | str += String.fromCharCode(parseInt(hex.substr(i, 2), 16))
28 | return str
29 |
30 | export DASP = () ->
31 | self = this
32 | self.metadata = null
33 |
34 | self.addrs =
35 | main: null
36 | dao: null
37 | member: null
38 | vault: null
39 | tasks: null
40 | git: null
41 |
42 | self.contracts =
43 | main: null
44 | dao: null
45 | member: null
46 | vault: null
47 | tasks: null
48 | git: null
49 |
50 | self.repoIPFSHash = null
51 | self.memberList = [];
52 | self.payBehaviorList = [];
53 | self.pendingWithdrawlList = [];
54 | self.pendingTokenWithdrawlList = [];
55 |
56 | self.initWithAddr = (addr, options, callback) ->
57 | self.addrs.main = addr
58 | mainAbi = (options && options.mainAbi) || require "../abi/mainABI.json"
59 | self.contracts.main = new web3.eth.Contract(mainAbi, self.addrs.main)
60 | moduleNames = (options && options.moduleNames) || ['DAO', 'MEMBER', 'VAULT', 'TASKS', 'GIT']
61 | #Todo: read module names from main contract
62 | initMod = (mod) ->
63 | #Get module address
64 | return self.contracts.main.methods.moduleAddresses('0x' + keccak256(mod)).call().then(
65 | (result) ->
66 | lowerMod = mod.toLowerCase()
67 | self.addrs[lowerMod] = result
68 | #Get module ABI's IPFS hash
69 | return self.contracts.main.methods.getABIHashForMod('0x' + keccak256(mod)).call().then(
70 | (abiHash) ->
71 | return new Promise((fullfill, reject) ->
72 | #Get module ABI
73 | ipfs.files.cat(hexToStr(abiHash),
74 | (error, stream) ->
75 | if error != null
76 | if callback != null
77 | callback(error)
78 | reject(error)
79 | throw error
80 | stream.pipe(bl((error, data) ->
81 | if error != null
82 | if callback != null
83 | callback(error)
84 | reject(error)
85 | throw error
86 | abi = JSON.parse(data.toString()).abi
87 | #Initialize module contract
88 | self.contracts[lowerMod] = new web3.eth.Contract(abi, self.addrs[lowerMod])
89 |
90 | fullfill()
91 | return
92 | ))
93 | return
94 | )
95 | )
96 | )
97 | )
98 | initAllMods = (initMod(mod) for mod in moduleNames)
99 | Promise.all(initAllMods).then(
100 | ()->
101 | self.contracts.git.methods.getCurrentIPFSHash().call(
102 | (error, result) ->
103 | if error != null
104 | if callback != null
105 | callback(error)
106 | else
107 | self.repoIPFSHash = hexToStr result
108 | if callback != null
109 | callback(null)
110 |
111 | return
112 | )
113 | )
114 | return
115 |
116 | self.getRepoFile = (path, callback) ->
117 | fullPath = "#{self.repoIPFSHash}#{path}"
118 | ipfs.files.cat(fullPath, (err, stream) ->
119 | if err != null
120 | ipfs.ls(fullPath, (error, result) ->
121 | if error != null
122 | if callback != null
123 | callback(error, 'dir', null)
124 | else
125 | if callback != null
126 | callback(null, 'dir', result.Objects[0].Links)
127 | )
128 | else
129 | stream.pipe(bl((error, data) ->
130 | if callback != null
131 | callback(null, 'file', data)
132 | ))
133 | )
134 |
135 | return
136 |
137 | self.getMemberList = (callback) ->
138 | self.memberList = []
139 | return self.contracts.member.methods.getMemberListCount().call().then(
140 | (memberCount) ->
141 | memberCount = +memberCount
142 | if memberCount == 0
143 | return
144 | getMember = (id) ->
145 | return self.contracts.member.methods.memberList(id).call().then(
146 | (member) ->
147 | return new Promise((fullfill, reject) ->
148 | self.memberList.push(member)
149 | if member.userAddress != '0x'
150 | fullfill()
151 | else
152 | if callback != null
153 | callback(new Error('Load Member Data Error'), null)
154 | reject()
155 | return
156 | )
157 | )
158 | getAllMembers = (getMember(id) for id in [1..memberCount-1])
159 | return Promise.all(getAllMembers).then(
160 | () ->
161 | if callback != null
162 | callback(null, self.memberList)
163 | )
164 | )
165 |
166 | self.signUp = (type, userName, callback) ->
167 | ethFunction = null
168 | if type == 'freelancer'
169 | ethFunction = self.contracts.member.methods.setSelfAsFreelancer(userName)
170 | if type == 'shareholder'
171 | ethFunction = self.contracts.member.methods.setSelfAsPureShareholder(userName)
172 |
173 | if ethFunction == null
174 | callback(new Error('Invalid type'))
175 | return
176 | return web3.eth.getAccounts().then(
177 | (accounts) ->
178 | web3.eth.defaultAccount = accounts[0]
179 | return ethFunction.send({from: web3.eth.defaultAccount, gas: 1000000}).on(
180 | 'receipt',
181 | (receipt) ->
182 | if callback != null
183 | callback(null)
184 | ).on(
185 | 'error',
186 | (error) ->
187 | if callback != null
188 | callback(error)
189 | )
190 | )
191 |
192 | self.getVaultBalance = () ->
193 | return web3.eth.getBalance(self.addrs.vault)
194 |
195 | self.getAccounts = () ->
196 | return web3.eth.getAccounts().then(
197 | (accounts) ->
198 | web3.eth.defaultAccount = accounts[0]
199 | )
200 |
201 | self.fundDasp = (amountInEther) ->
202 | return web3.eth.sendTransaction({from: web3.eth.defaultAccount, to: self.addrs.vault, value: amountInEther * Math.pow(10, 18)})
203 |
204 | self.getPayBehaviorList = (callback) ->
205 | self.payBehaviorList = []
206 | return self.contracts.vault.methods.getPayBehaviorListCount().call().then(
207 | (payBehaviorCount) ->
208 | payBehaviorCount = +payBehaviorCount
209 | if payBehaviorCount == 0
210 | return
211 | getPayBehavior = (id) ->
212 | return self.contracts.vault.methods.payBehaviorList(id).call().then(
213 | (payBehavior) ->
214 | return new Promise((fullfill, reject) ->
215 | self.payBehaviorList.push(payBehavior)
216 | if payBehavior.tokenAddress != '0x'
217 | fullfill()
218 | else
219 | if callback != null
220 | callback(new Error('Load Pay Behaviors Error'))
221 | reject()
222 | return
223 | )
224 | )
225 | getAllPayBehaviors = (getPayBehavior(id) for id in [1..payBehaviorCount-1])
226 | return Promise.all(getAllPayBehaviors).then(
227 | () ->
228 | if callback != null
229 | callback(null, self.payBehaviorList)
230 | )
231 | )
232 |
233 | self.getPendingWithdrawlList = (callback) ->
234 | self.pendingWithdrawlList = []
235 | return self.contracts.vault.methods.getPendingWithdrawlListCount().call().then(
236 | (pendingWithdrawlCount) ->
237 | pendingWithdrawlCount = +pendingWithdrawlCount
238 | if pendingWithdrawlCount == 0
239 | return
240 | getPendingWithdrawl = (id) ->
241 | return self.contracts.vault.methods.pendingWithdrawlList(id).call().then(
242 | (pendingWithdrawl) ->
243 | return new Promise((fullfill, reject) ->
244 | self.payBehaviorList.push(pendingWithdrawl)
245 | if pendingWithdrawl != null
246 | fullfill()
247 | else
248 | if callback != null
249 | callback(new Error('Load Pending Withdrawls Error'), null)
250 | reject()
251 | return
252 | )
253 | )
254 | getAllPendingWithdrawls = (getPendingWithdrawl(id) for id in [1..pendingWithdrawlCount-1])
255 | return Promise.all(getAllPendingWithdrawls).then(
256 | () ->
257 | if callback != null
258 | callback(null, self.pendingWithdrawlList)
259 | )
260 | )
261 |
262 | self.getPendingTokenWithdrawlList = (callback) ->
263 | self.pendingTokenWithdrawlList = []
264 | return self.contracts.vault.methods.getPendingTokenWithdrawlListCount().call().then(
265 | (pendingWithdrawlCount) ->
266 | pendingWithdrawlCount = +pendingWithdrawlCount
267 | if pendingWithdrawlCount == 0
268 | return
269 | getPendingWithdrawl = (id) ->
270 | return self.contracts.vault.methods.pendingTokenWithdrawlList(id).call().then(
271 | (pendingWithdrawl) ->
272 | return new Promise((fullfill, reject) ->
273 | self.payBehaviorList.push(pendingWithdrawl)
274 | if pendingWithdrawl != null
275 | fullfill()
276 | else
277 | if callback != null
278 | callback(new Error('Load Pending Token Withdrawls Error'), null)
279 | reject()
280 | return
281 | )
282 | )
283 | getAllPendingWithdrawls = (getPendingWithdrawl(id) for id in [1..pendingWithdrawlCount-1])
284 | return Promise.all(getAllPendingWithdrawls).then(
285 | () ->
286 | if callback != null
287 | callback(null, self.pendingTokenWithdrawlList)
288 | )
289 | )
290 |
291 | return
292 |
--------------------------------------------------------------------------------
/client_ui/imports/ui/dasp_dashboard.coffee:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating'
2 |
3 | import {ReactiveVar} from 'meteor/reactive-var'
4 |
5 | import {DASP} from '../objects/dasp.js'
6 |
7 | import './dasp_dashboard.html'
8 |
9 | export DASP_Address = new ReactiveVar("")
10 |
11 | export dasp = new DASP()
12 |
13 | import {StartDaemon} from '../daemon/git-ipfs.js'
14 |
15 | showToastMsg = (msg) ->
16 | snackbarContainer = document.querySelector('#status_toast')
17 | snackbarContainer.MaterialSnackbar.showSnackbar({message: msg})
18 | return
19 |
20 | copyTextToClipboard = (text) ->
21 | textArea = document.createElement("textarea")
22 | # *** This styling is an extra step which is likely not required. ***
23 | # Why is it here? To ensure:
24 | # 1. the element is able to have focus and selection.
25 | # 2. if element was to flash render it has minimal visual impact.
26 | # 3. less flakyness with selection and copying which **might** occur if
27 | # the textarea element is not visible.
28 | # The likelihood is the element won't even render, not even a flash,
29 | # so some of these are just precautions. However in IE the element
30 | # is visible whilst the popup box asking the user for permission for
31 | # the web page to copy to the clipboard.
32 |
33 | # Place in top-left corner of screen regardless of scroll position.
34 | textArea.style.position = 'fixed'
35 | textArea.style.top = 0
36 | textArea.style.left = 0
37 |
38 | # Ensure it has a small width and height. Setting to 1px / 1em
39 | # doesn't work as this gives a negative w/h on some browsers.
40 | textArea.style.width = '2em'
41 | textArea.style.height = '2em'
42 |
43 | # We don't need padding, reducing the size if it does flash render.
44 | textArea.style.padding = 0
45 |
46 | # Clean up any borders.
47 | textArea.style.border = 'none'
48 | textArea.style.outline = 'none'
49 | textArea.style.boxShadow = 'none'
50 |
51 | # Avoid flash of white box if rendered for any reason.
52 | textArea.style.background = 'transparent'
53 |
54 | textArea.value = text
55 |
56 | document.body.appendChild(textArea)
57 |
58 | textArea.select()
59 |
60 | try
61 | successful = document.execCommand('copy')
62 | msg = if successful then 'Copied Clone Address to Clipboard' else 'Oops, unable to copy'
63 | showToastMsg(msg)
64 | catch err
65 | showToastMsg('Oops, unable to copy')
66 |
67 | document.body.removeChild(textArea)
68 | return
69 |
70 | refreshDasp = () ->
71 | dasp.initWithAddr(DASP_Address.get(), null, (error) ->
72 | if error != null
73 | showToastMsg('Ethereum Connection Error')
74 | throw error
75 | #Start daemon
76 | StartDaemon()
77 |
78 | #Init repo tab
79 | dasp.getRepoFile('', (error, type, result) ->
80 | if error != null
81 | showToastMsg('List Repo Error')
82 | throw error
83 | currentFileList.set(result)
84 | )
85 |
86 | #Init finances tab
87 | dasp.getVaultBalance().then(
88 | (result) ->
89 | vaultBalance.set(result / Math.pow(10, 18))
90 | )
91 | dasp.getPayBehaviorList((error, result) ->
92 | if error != null
93 | showToastMsg('Get Coin Offerings Error')
94 | throw error
95 | #Descending by startBlockNumber
96 | result.sort((a, b) ->
97 | return parseInt(b.startBlockNumber) - parseInt(a.startBlockNumber)
98 | )
99 | payBehaviorList.set(result)
100 | )
101 | dasp.getPendingWithdrawlList((error, result) ->
102 | if error != null
103 | showToastMsg('Get Pending Withdrawls Error')
104 | throw error
105 | dasp.getPendingTokenWithdrawlList((e, r) ->
106 | if error != null
107 | showToastMsg('Get Pending Token Withdrawls Error')
108 | throw error
109 | result.concat(r)
110 | )
111 | #Descending by frozenUntilBlock
112 | result.sort((a, b) ->
113 | return parseInt(b.frozenUntilBlock) - parseInt(a.frozenUntilBlock)
114 | )
115 | pendingWithdrawlList.set(result)
116 | )
117 |
118 |
119 | #Init member tab
120 | dasp.getMemberList((error, result) ->
121 | if error != null
122 | showToastMsg('Load Member Data Error')
123 | throw error
124 | memberList.set(result)
125 | )
126 | )
127 |
128 | #Repo tab variables
129 | currentFileList = new ReactiveVar([])
130 | currentRepoPath = new ReactiveVar("")
131 | displayFileList = new ReactiveVar(true)
132 | fileData = new ReactiveVar("")
133 | fileName = new ReactiveVar("")
134 |
135 | #Member tab variables
136 | memberList = new ReactiveVar([])
137 | isSigningUp = new ReactiveVar(false)
138 | signUpType = new String()
139 | SIGNUP_STATUS_SHOWTIME = 3000
140 |
141 | #Finances tab variables
142 | vaultBalance = new ReactiveVar(0)
143 | isEnteringFundAmount = new ReactiveVar(false)
144 | payBehaviorList = new ReactiveVar([])
145 | pendingWithdrawlList = new ReactiveVar([])
146 |
147 | #Helpers
148 |
149 | Template.body.helpers(
150 | initialized:
151 | () ->
152 | return DASP_Address.get().length != 0
153 | )
154 |
155 | Template.repo_tab.helpers(
156 | ls_file:
157 | () ->
158 | return currentFileList.get()
159 |
160 | display_file_list:
161 | () ->
162 | return displayFileList.get()
163 |
164 | file_data:
165 | () ->
166 | return fileData.get()
167 |
168 | file_name:
169 | () ->
170 | return fileName.get()
171 |
172 | file_download_url:
173 | () ->
174 | return "https://gateway.ipfs.io/ipfs/#{dasp.repoIPFSHash}#{currentRepoPath.get()}/#{fileName.get()}"
175 |
176 | current_path:
177 | () ->
178 | if currentRepoPath.get().length == 0
179 | return '/'
180 | return currentRepoPath.get()
181 | )
182 |
183 | Template.tasks_tab.helpers(
184 | task_list:
185 | () ->
186 |
187 | )
188 |
189 | Template.finances_tab.helpers(
190 | vault_balance:
191 | () ->
192 | return vaultBalance.get()
193 |
194 | not_entering_fund_amount:
195 | () ->
196 | return !isEnteringFundAmount.get()
197 |
198 | pay_behavior_list:
199 | () ->
200 | return payBehaviorList.get()
201 |
202 | pay_behavior_list_empty:
203 | () ->
204 | return payBehaviorList.get().length == 0
205 |
206 | pending_withdrawl_list:
207 | () ->
208 | return pendingWithdrawlList.get()
209 |
210 | pending_withdrawl_list_empty:
211 | () ->
212 | return pendingWithdrawlList.get().length == 0
213 |
214 | wei_to_ether:
215 | (wei) ->
216 | return +wei / Math.pow(10, 18)
217 |
218 | currency_symbol:
219 | (symbol) ->
220 | if symbol == null
221 | return 'ETH'
222 | return symbol
223 | )
224 |
225 | Template.members_tab.helpers(
226 | member_list:
227 | () ->
228 | return memberList.get()
229 |
230 | not_signing_up:
231 | () ->
232 | return !isSigningUp.get()
233 | )
234 |
235 | #Events
236 |
237 | Template.body.events(
238 | 'submit .dasp_addr_input':
239 | (event) ->
240 | # Prevent default browser form submit
241 | event.preventDefault()
242 | # Get value from form element
243 | target = event.target
244 | text = target.dasp_addr.value
245 |
246 | DASP_Address.set(text)
247 |
248 | refreshDasp()
249 |
250 | 'click .refresh_dasp':
251 | (event) ->
252 | refreshDasp()
253 | )
254 |
255 | Template.repo_tab.events(
256 | 'dblclick .file_item':
257 | (event) ->
258 | item = this
259 | if item.Name == '..'
260 | currentRepoPath.set(currentRepoPath.get().slice(0, currentRepoPath.get().lastIndexOf('/')))
261 | dasp.getRepoFile(currentRepoPath.get(), (error, type, result) ->
262 | if error != null
263 | showToastMsg('List Repo Error')
264 | throw error
265 | if currentRepoPath.get().length != 0
266 | upperDirItem =
267 | Name: '..'
268 | result.splice(0, 0, upperDirItem)
269 | currentFileList.set(result)
270 | return
271 | )
272 | else
273 | dasp.getRepoFile("#{currentRepoPath.get()}/#{item.Name}", (error, type, result) ->
274 | if error != null
275 | showToastMsg('List Repo Error')
276 | throw error
277 | if type == 'dir'
278 | currentRepoPath.set(currentRepoPath.get() + "/" + item.Name)
279 | upperDirItem =
280 | Name: '..'
281 | result.splice(0, 0, upperDirItem)
282 | currentFileList.set(result)
283 | if type == 'file'
284 | displayFileList.set(false)
285 | fileName.set(item.Name)
286 | fileData.set(result)
287 | )
288 |
289 | 'click .back_to_dir':
290 | (event) ->
291 | displayFileList.set(true)
292 | fileData.set("")
293 |
294 | 'click .upper_dir_btn':
295 | (event) ->
296 | currentRepoPath.set(currentRepoPath.get().slice(0, currentRepoPath.get().lastIndexOf('/')))
297 | dasp.getRepoFile(currentRepoPath.get(), (error, type, result) ->
298 | if error != null
299 | showToastMsg('List Repo Error')
300 | throw error
301 | currentFileList.set(result)
302 | )
303 |
304 | 'click .clone_repo_btn':
305 | (event) ->
306 | copyTextToClipboard("https://gateway.ipfs.io/ipfs/#{dasp.repoIPFSHash}/repo.git")
307 | )
308 |
309 | Template.tasks_tab.events(
310 | 'click .post_task_btn':
311 | (event) ->
312 |
313 | )
314 |
315 | Template.finances_tab.events(
316 | 'click .fund_dasp_btn':
317 | (event) ->
318 | isEnteringFundAmount.set(true)
319 | return
320 |
321 | 'click .cancel_funding':
322 | (event) ->
323 | isEnteringFundAmount.set(false)
324 | return
325 |
326 | 'submit .fund_amount_entry':
327 | (event) ->
328 | event.preventDefault()
329 |
330 | target = event.target
331 | fundAmount = +target.fund_amount.value
332 |
333 | isEnteringFundAmount.set(false)
334 | target.fund_amount.value = ''
335 |
336 | dasp.getAccounts().then(
337 | () ->
338 | dasp.fundDasp(fundAmount).on('receipt', (receipt) ->
339 | showToastMsg('Fund Success')
340 | refreshDasp()
341 | ).on('error', (error) ->
342 | showToastMsg('Fund Failed')
343 | throw error
344 | )
345 | )
346 |
347 | return
348 | )
349 |
350 | Template.members_tab.events(
351 | 'click .signup_freelancer':
352 | (event) ->
353 | isSigningUp.set(true)
354 | signUpType = 'freelancer'
355 |
356 | 'click .signup_shareholder':
357 | (event) ->
358 | isSigningUp.set(true)
359 | signUpType = 'shareholder'
360 |
361 | 'click .cancel_signup':
362 | (event) ->
363 | isSigningUp.set(false)
364 |
365 | 'submit .signup_username':
366 | (event) ->
367 | event.preventDefault()
368 |
369 | target = event.target
370 | userName = target.username.value
371 |
372 | dasp.signUp(signUpType, userName, (error) ->
373 | if error != null
374 | showToastMsg('Sign Up Error')
375 | throw error
376 | else
377 | dasp.getMemberList((error, result) ->
378 | if error != null
379 | showToastMsg('Sign Up Error')
380 | throw error
381 | else
382 | memberList.set(result)
383 | showToastMsg('Sign Up Success')
384 | throw error
385 | )
386 | )
387 | isSigningUp.set(false)
388 | target.username.value = ''
389 | )
--------------------------------------------------------------------------------
/client_ui/imports/ui/dasp_dashboard.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Project-WikiGit/WikiGit/8ef5be8d3015d26685b34bbf414c65bd8d721eb2/client_ui/imports/ui/dasp_dashboard.css
--------------------------------------------------------------------------------
/client_ui/imports/ui/dasp_dashboard.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |