aragon / aragon-court Goto Github PK
View Code? Open in Web Editor NEW🧿 A subjective oracle secured by cryptoeconomics
Home Page: https://aragon.org/court
License: GNU General Public License v3.0
🧿 A subjective oracle secured by cryptoeconomics
Home Page: https://aragon.org/court
License: GNU General Public License v3.0
Add proper documentation for all the contracts of the court:
Contracts that MUST be documented
Currently juror tokens need to be activated in every period if they want to be selected to be a juror
Enforce that config changes need to be scheduled at least a certain number of terms in advance. This limit (MIN_SCHEDULE_CONFIG_CHANGE_TERMS
) should be a constant in the Court and needs to be higher or equal to the number of terms that a dispute (or appeal round) can be scheduled in advance.
At the moment when changing the config, if there was already a scheduled config change, it is overridden by the new incoming config. This can be problematic if the term in which the config was scheduled to change is close enough to the current term that there can be disputes or appeal rounds initiated with the fee schedule of a future config which can be cancelled by scheduling a new config change. Config changes shouldn't be possible if there is a pending config change scheduled to happen in less than MIN_SCHEDULE_CONFIG_CHANGE_TERMS
A couple of interesting mechanisms (like having a participation rate target) to incentivize juror participation would require having a way to reward all the jurors that were active at a given term. Some options:
Checkpointing the sum tree, to allow historical queries for its state at any term. This would also remove the need of rescheduling disputes when their draft transaction doesn't occur during the term. Checkpointing would make tree transitions even more expensive to maintain gas-wise.
Using EVM Storage Proofs: would require performing an account proof of the Court at least every term (at the same block number that we request the randomness for). It would break when multiple term transitions occur in the same block (as the storage proofs can only look at the storage of a contract after all transactions in a block have been processed).
An immediate first step would be to save the total number of tokens that are active for a term in the term state, and then figure out a way for distributing rewards proportionally among jurors.
We use solium in other repos, see aragonOS' config for reference: https://github.com/aragon/aragonOS/blob/dev/.soliumrc.json
Currently running test/hex-tree.js
takes a lot of time given that we are adding a lot of entries to the tree to test the gas usage.
We should be doing this in a separate script and not in the unit tests.
It may be interesting to turn the CRVoting into an Aragon app itself. We will be able to leverage some things like upgrades, permissions, etc
For instance:
https://github.com/aragonone/aragon-court/blob/778ab8b28f93911940152f4925acd57f3ad2f4a1/contracts/Court.sol#L969
https://github.com/aragonone/aragon-court/blob/778ab8b28f93911940152f4925acd57f3ad2f4a1/contracts/Court.sol#L1039
https://github.com/aragonone/aragon-court/blob/778ab8b28f93911940152f4925acd57f3ad2f4a1/contracts/Court.sol#L1045
https://github.com/aragonone/aragon-court/blob/778ab8b28f93911940152f4925acd57f3ad2f4a1/contracts/Court.sol#L1051
Or if we are completely sure that those can not over/underflow due to the inner logic, add a comment explaining it.
For simplicity, the Court implementation has been done in just one contract as the state of the contract is quite interdependent between most parts of the mechanism. An idea for separating concerns would be to split the general court state from the dispute management and adjudication.
We are still well below the block gas limit (at the moment ~5.5m gas), but as we keep implementing new functionality, we will start approaching the limit.
Some maintenance function calls like heartbeat
, draftAdjudicationRound
or settleRoundSlashing
can reward the sender with a fee for executing them to ensure that the Court state is kept up to date.
At the moment, like all other fees, the governor
of the contract gets a share of these fees when they are paid. Removing the governance cut from these will allow us to make these cheaper and remove some extra storage costs when awarding the fee.
See: #45 (comment)
It will improve code readability a lot and will probably help solve #43
Please follow the convention used for the JurorsRegistry
tests, some of them are:
test-helpers
module provided by @aragon
let
scoped variables instead of properties to define test variablesAdd logic to the court to slash jurors in case their vote is leaked
We need to add tests for draft
and slash
functions of the JurorsRegistry
At the moment, and unless something like #11 is implemented, an adjudication round's jurors can only be drafted during a specific term that was known in advance.
Even though there is an economic incentive for executing the draft, it could fail to be drafted on time. In this case the round should be rescheduled to be drafted at a later term (could be as soon as term + 1
when the reschedule function is called)
We could consider requiring the caller to pay another heartbeat fee for the term it is rescheduled to, this fee could also be extracted from the draft fee.
For an appeal to occur, both parties of the appeal must offer collateral. If the current ruling for a dispute was X
and one of the parties adds collateral in favor of another ruling Y
but the other party does not back X
with collateral, the final ruling will be Y
. If both parties back their appeal with collateral, a new dispute round gets created.
Not an essential functionality, this way we can make contract leaner.
If any of the queues for a term is too large, this could result in the Court bricking due to heartbeat()
always going OOG as the gas it needs to process its queues is greater than the block gas limit.
See #73 for reference:
"We may consider not grouping at all on CourtStaking.draft
function, avoiding the use of weights there. Depending on the nature of data it may be better: if jurors are often repeated, size of arrays passed through contracts would be smaller, but if they are not, we would save weights
array and simplify logic on one side (as right now we are checking duplicates twice, in different ways)"
Please follow the convention used for the JurorsRegistry
tests, some of them are:
test-helpers
module provided by @aragon
let
scoped variables instead of properties to define test variablesWe should have scripts to run the test suite against a geth
node as well.
Explore if we should do this only for certain scenarios in the CI to avoid waiting a lot to merge PRs.
Some context:
#6 (comment)
#6 (comment)
Right now, if the first draft of a term happens more than 256 blocks after the term starts, the blockhash that will be returned for the randomnessBN
of the term will be 0.
Options to work around this problem:
heartbeat()
function (which can be called one block after the actual heartbeat
) that stores the randomness
for the term. The caller could get the current dependingDrafts * heartbeatFee
that is being paid to the heartbeat()
caller and pay heartbeat fees just from activation, updates and deactivation fees (#7)Please follow the convention used for the JurorsRegistry
tests, some of them are:
test-helpers
module provided by @aragon
let
scoped variables instead of properties to define test variablesAfter the maximum number of appeals has been reached, if a new appeal is created, the final ruling by the Court should be decided with a stake-weighted vote of all active jurors.
In most functions we are doing:
Dispute storage dispute = disputes[_disputeId];
Since this is a mapping, should we check that the dispute actually exists? (Downside being adding more code, of course)
The same for:
AdjudicationRound storage round = dispute.rounds[_roundId];
In a lot of them we are then doing too:
AdjudicationRound storage round = dispute.rounds[dispute.rounds.length - 1];
Which would fail because rounds length is zero, but it would be nice to have a proper error message.
Look here:
https://github.com/aragon/aragon-court/blob/split_staking_3/contracts/Court.sol#L646
and here:
https://github.com/aragon/aragon-court/blob/split_staking_3/contracts/Court.sol#L656
Let's say that jurorMinStake = 10
, that FINAL_ROUND_WEIGHT_PRECISION = 1000
, that juror stake was 9 and penalytPct
is 10%.
Then first line will give: weight = 1000*9/10 = 900
.
Weight will be >0, so it will get into the 2nd one, which will give:
weightedPenalty = 10*10/100 * 900 / 1000 = 1 * 900 / 1000 = 0
(due to Solidity rounding).
So it will be able to vote but it won't be slashed at all.
Should the new ruling for leaked votes be RefusedRuling
or Missing
?
See discusssion: #6 (comment)
To avoid Out Of gas errors.
Right now a call with 3 jurors costs ~160k
See this comment for more context.
As explained in 3rd open issue here:
At the moment, all jurors selected for adjudicating a dispute can have the same maximum penalty in case that they vote for a losing ruling. This results in the fact that the security of disputes grows linearly with the number of jurors that can be drafted in the multiple adjudication rounds. And in the current implementation there is a limit (due to gas) to how many jurors can be drafted for to rule in a round, which is in the 100-150 juror range.
Even though the potential final appeal in which all active jurors are required to vote increases the security (at the expense of inefficiency as potentially thousands of people will need to vote), during the regular appeals rounds the maximum amount of tokens at stake will be just two orders of magnitude more than the minimum token amount for juror activation (assuming a quite unlikely 100% penalization ratio), which will likely not be much.
It’d be positive to increase the juror stakes as the appeal rounds advance. A couple of options for doing this:
Please explore which patch version should we use having in mind its adoption and stability
Currently it is just an internal function used to set the initial configuration, but the governor
should be able to change the config.
There should be a threshold of terms during which no config changes are allowed, if there is an incoming configuration change happening within the threshold, a configuration change overriding it should not be allowed either.
The logic for changing the config after setting it for the first time should also be reviewed, as I believe that both
aragon-court/contracts/Court.sol
Line 1094 in 24cb091
aragon-court/contracts/Court.sol
Line 1119 in 24cb091
_fromTermId
instead of configChangeTermId
Currently, all the initialization logic is triggered by the Court
constructor.
Explore if we can make things clearer having all these logic in a separate contract (or more than one)
We may want to use solcover
like we are doing in many other repos
Agreement
should implement the generic management of stake locks:
commit(...)
function that ensures the sender has a valid lock and exposes the sender to potential challenges during a period of time.challenge(...)
function that will ensure the sender has a valid lock and creates a dispute in the Court.rule(...)
: if the Court issues a valid ruling (not Missing
or RefuseRuling
), it should perform a transfer from the lock of the losing party to the lock of the winning party, and mark both locks as unlocked (so Agreement
will return true to canUnlock
). We should consider allowing for contracts inheriting from Agreement
to specify a different distribution for locks depending on the ruling, for example in case of a proposal agreement, if the court rules in favor of the proposer, a part of the challenger's deposit could be sent to the DAO (as everyone in the DAO suffered from deferring the vote).Currently, the Court
settings are updated simply for a given term id. This allows some weird scenarios such as:
Make sure (add tests) that the accountancy of all funds flowing in and out matches porperly, including possible errors due to rounding.
Added to scripts:
"scripts": {
"test": "TRUFFLE_TEST=true npm run ganache-cli:test",
"analysis:tree": "TRUFFLE_TEST=true SUMTREE_GAS_ANALYSIS=true npm run ganache-cli:test",
"ganache-cli:test": "./node_modules/@aragon/test-helpers/ganache-cli.sh",
"version": "./node_modules/ganache-cli/cli.js --version"
},
npm run version
> @aragon/[email protected] version /Users/ms/Code/aragon-court
> ./node_modules/ganache-cli/cli.js --version
Ganache CLI v6.4.3 (ganache-core: 2.5.5)
Error: Exceeds block gas limit
at Object.InvalidResponse (/Users/ms/Code/aragon-court/node_modules/truffle/build/webpack:/~/web3/lib/web3/errors.js:38:1)
at /Users/ms/Code/aragon-court/node_modules/truffle/build/webpack:/~/web3/lib/web3/requestmanager.js:86:1
at /Users/ms/Code/aragon-court/node_modules/truffle/build/webpack:/packages/truffle-migrate/index.js:225:1
at /Users/ms/Code/aragon-court/node_modules/truffle/build/webpack:/packages/truffle-provider/wrapper.js:134:1
at XMLHttpRequest.request.onreadystatechange (/Users/ms/Code/aragon-court/node_modules/truffle/build/webpack:/~/web3/lib/web3/httpprovider.js:128:1)
at XMLHttpRequestEventTarget.dispatchEvent (/Users/ms/Code/aragon-court/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:64:1)
at XMLHttpRequest._setReadyState (/Users/ms/Code/aragon-court/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:354:1)
at XMLHttpRequest._onHttpResponseEnd (/Users/ms/Code/aragon-court/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:509:1)
at IncomingMessage.<anonymous> (/Users/ms/Code/aragon-court/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:469:1)
at IncomingMessage.emit (events.js:194:15)
at endReadableNT (_stream_readable.js:1125:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
Right now, when jurors activate and deactivate, all their stake goes in to and out of the tree.
The idea is that if jurors earn tokens from their work, they may want to withdraw some not to be drafted so often (as work time is limited) or just to cash out.
Currently they can do that by deactivating, partially withdrawing and then activating again, but it's definitely not user friendly and they could miss the chance of being drafted for some terms during this process.
Right now most of the Court's state can be reconstructed checking the logs of the contract, but we should provide some getters (external view
s) that allow for quickly fetching the state of a dispute, adjudication round and juror accounts.
Currently the HexSumTree is a singleton-type object which we only modify in the heartbeat()
function so the state of the tree stays constant during the term allowing deterministic juror drafts throughout the term. This comes with the following drawbacks:
Because all modifications to the tree are batched for the heartbeat()
of a future term, we need to limit how many mutations can be performed in a given term to avoid heartbeat()
to require more gas than the block gas limit and bricking the court.
Disputes need to draft their jurors strictly during the correct term because the juror sum tree can mutate in the transition to the next term. If the jurors of a dispute aren't drafted during the term, the dispute needs to be rescheduled for a future term.
By checkpointing the state of the tree every term we would be able to:
Immediately perform tree modifications for the next term, no longer scheduling modifications to a future term's heartbeat()
.
Draft jurors for a dispute at any term in the future, as the drafting function would be able to perform sortition on a past state of the tree.
Being able to prove to a contract how many tokens a juror had active and for how long, which would be really useful for distributing passive rewards (as discussed in #9)
Downsides:
Gas cost:
I am particularly worried about the added SLOAD
cost while traversing the tree performing a sortition, as for every node in the tree we'd need to get its checkpointed value (even though it is a binary search, it would increase the number SLOAD
s by at least a factor of 2 and grows logarithmically with the number of balance updates of a juror has had).
The SSTORE
cost for modifying tree entries wouldn't be that different for trees of a reasonable size specially taking into account that the Court would no longer have to manage queues in storage. The SSTORE
overhead would be an extra ~20k * log16(N)
(N
being the number of jurors) in the worst case (the first update for the next term), which would add up to an extra ~100k gas for a tree of ~1.04M (16 ^ 5
) jurors. A positive side-effect is that this gas would be paid by the juror directly in almost all instances.
Removing the requirement to draft an adjudication round during an specific term, presents some challenges of its own with the current design:
Deferred juror unstaking: because at the time drafting can only happen during one specific term and adjudication rounds that fail to draft on time need to be rescheduled, the moment a juror is deactivated we know exactly how many tokens they have at stake (and we can allow them to withdraw all their tokens that aren't at risk of being slashed). If an adjudication round no longer needs to be drafted during an specific term, we'd need to prevent the juror from unstaking for at least the duration of a full adjudication round (commit + reveal + appeal) which is a reasonable time span for cancelling/rescheduling the round if the draft didn't happen.
Higher risk of missing block hashes: the block hash used as a randomness seed for a term needs to be queried and saved within 257 blocks after the term is transitioned. If a round no longer needs to be drafted within the term, we may need to add an extra incentive to perform a secondary hearbeat()
to store the randomness seed for all terms with adjudication rounds.
Scheduling: at the moment, jurors can schedule their activation and deactivation to a custom term. This is a nice side-effect of not being able to perform activations immediately, as we don't care whether a modification happens in the next term because it needs to be scheduled anyway. If we were to add checkpointing, we'd likely remove the ability to schedule activations and deactivations.
I would like to npm install
and run the thing.
Recently installed Python 3
because I had to run some other stuff.
Worried it is breaking now.
Before I start messing with it again (undoing) can you verify if this is Python issue or something else?
Maybe you have experience how to troubleshoot?
usr/local/opt/python/libexec/bin/python
Python 3.7.3 (default, Mar 27 2019, 09:23:15)
$ npm install
> [email protected] preinstall /Users/ms/Code/aragon-court/node_modules/scrypt
> node node-scrypt-preinstall.js
> [email protected] install /Users/ms/Code/aragon-court/node_modules/scrypt
> node-gyp rebuild
gyp ERR! configure error
gyp ERR! stack Error: Command failed: /usr/local/opt/python/libexec/bin/python -c import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack File "<string>", line 1
gyp ERR! stack import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack ^
gyp ERR! stack SyntaxError: invalid syntax
gyp ERR! stack
gyp ERR! stack at ChildProcess.exithandler (child_process.js:294:12)
gyp ERR! stack at ChildProcess.emit (events.js:189:13)
gyp ERR! stack at maybeClose (internal/child_process.js:970:16)
gyp ERR! stack at Socket.stream.socket.on (internal/child_process.js:389:11)
gyp ERR! stack at Socket.emit (events.js:189:13)
gyp ERR! stack at Pipe._handle.close (net.js:597:12)
gyp ERR! System Darwin 18.5.0
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/ms/Code/aragon-court/node_modules/scrypt
gyp ERR! node -v v10.15.3
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
npm WARN @aragon/[email protected] requires a peer of solidity-coverage@^0.5.11 but none is installed. You must install peer dependencies yourself.
npm WARN @aragon/[email protected] No repository field.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] (node_modules/scrypt):
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] install: `node-gyp rebuild`
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: Exit status 1
audited 8757 packages in 8.037s
found 0 vulnerabilities
$ git clone https://github.com/aragon/aragon-court.git
Cloning into 'aragon-court'...
remote: Enumerating objects: 54, done.
remote: Counting objects: 100% (54/54), done.
remote: Compressing objects: 100% (28/28), done.
remote: Total 456 (delta 30), reused 40 (delta 26), pack-reused 402
Receiving objects: 100% (456/456), 138.22 KiB | 68.00 KiB/s, done.
Resolving deltas: 100% (275/275), done.
$ cd aragon-court/
$ npm install
> [email protected] preinstall /Users/ms/Code/aragon-court/node_modules/scrypt
> node node-scrypt-preinstall.js
> [email protected] install /Users/ms/Code/aragon-court/node_modules/keccak
> npm run rebuild || echo "Keccak bindings compilation fail. Pure JS implementation will be used."
> [email protected] rebuild /Users/ms/Code/aragon-court/node_modules/keccak
> node-gyp rebuild
gyp ERR! configure error
gyp ERR! stack Error: Command failed: /usr/local/opt/python/libexec/bin/python -c import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack File "<string>", line 1
gyp ERR! stack import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack ^
gyp ERR! stack SyntaxError: invalid syntax
gyp ERR! stack
gyp ERR! stack at ChildProcess.exithandler (child_process.js:294:12)
gyp ERR! stack at ChildProcess.emit (events.js:189:13)
gyp ERR! stack at maybeClose (internal/child_process.js:970:16)
gyp ERR! stack at Socket.stream.socket.on (internal/child_process.js:389:11)
gyp ERR! stack at Socket.emit (events.js:189:13)
gyp ERR! stack at Pipe._handle.close (net.js:597:12)
gyp ERR! System Darwin 18.5.0
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/ms/Code/aragon-court/node_modules/keccak
gyp ERR! node -v v10.15.3
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] rebuild: `node-gyp rebuild`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] rebuild script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/ms/.npm/_logs/2019-05-06T12_06_23_585Z-debug.log
Keccak bindings compilation fail. Pure JS implementation will be used.
> [email protected] install /Users/ms/Code/aragon-court/node_modules/scrypt
> node-gyp rebuild
gyp ERR! configure error
gyp ERR! stack Error: Command failed: /usr/local/opt/python/libexec/bin/python -c import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack File "<string>", line 1
gyp ERR! stack import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack ^
gyp ERR! stack SyntaxError: invalid syntax
gyp ERR! stack
gyp ERR! stack at ChildProcess.exithandler (child_process.js:294:12)
gyp ERR! stack at ChildProcess.emit (events.js:189:13)
gyp ERR! stack at maybeClose (internal/child_process.js:970:16)
gyp ERR! stack at Socket.stream.socket.on (internal/child_process.js:389:11)
gyp ERR! stack at Socket.emit (events.js:189:13)
gyp ERR! stack at Pipe._handle.close (net.js:597:12)
gyp ERR! System Darwin 18.5.0
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/ms/Code/aragon-court/node_modules/scrypt
gyp ERR! node -v v10.15.3
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
> [email protected] install /Users/ms/Code/aragon-court/node_modules/secp256k1
> npm run rebuild || echo "Secp256k1 bindings compilation fail. Pure JS implementation will be used."
> [email protected] rebuild /Users/ms/Code/aragon-court/node_modules/secp256k1
> node-gyp rebuild
gyp ERR! configure error
gyp ERR! stack Error: Command failed: /usr/local/opt/python/libexec/bin/python -c import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack File "<string>", line 1
gyp ERR! stack import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack ^
gyp ERR! stack SyntaxError: invalid syntax
gyp ERR! stack
gyp ERR! stack at ChildProcess.exithandler (child_process.js:294:12)
gyp ERR! stack at ChildProcess.emit (events.js:189:13)
gyp ERR! stack at maybeClose (internal/child_process.js:970:16)
gyp ERR! stack at Socket.stream.socket.on (internal/child_process.js:389:11)
gyp ERR! stack at Socket.emit (events.js:189:13)
gyp ERR! stack at Pipe._handle.close (net.js:597:12)
gyp ERR! System Darwin 18.5.0
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/ms/Code/aragon-court/node_modules/secp256k1
gyp ERR! node -v v10.15.3
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] rebuild: `node-gyp rebuild`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] rebuild script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/ms/.npm/_logs/2019-05-06T12_06_24_443Z-debug.log
Secp256k1 bindings compilation fail. Pure JS implementation will be used.
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN @aragon/[email protected] requires a peer of solidity-coverage@^0.5.11 but none is installed. You must install peer dependencies yourself.
npm WARN @aragon/[email protected] No repository field.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] (node_modules/scrypt):
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] install: `node-gyp rebuild`
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: Exit status 1
added 487 packages from 600 contributors and audited 8757 packages in 119.19s
found 0 vulnerabilities
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.