openst / facilitator Goto Github PK
View Code? Open in Web Editor NEWLicense: Apache License 2.0
License: Apache License 2.0
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
This function gets the hashLock
and unlockSecret
pair as the parameter, this call a function on DB interaction layer object to store this in the database.
Subscribe/Unsubscribe to all entities with one subscriber per graph-node instance (ie. per chain)
Worker facilitates the stake and mint process.
The proposed functions are provided for reference.
The implementer can think this in detail and can add/remove functions.
mosaic-config
, facilitator-config
as parameter. function to update DB with the latest state of facilitation. (Task execution table) (Update the ticket).
- REQUESTED
function to update DB with hashlock
and unlockSecret
.
- [ ] function to update DB with proof data.
function to create hashLock
and unlockSecret
.
function to approve bounty amount.
function to accept stake request.
function to generate account proof.
function to confirm stake intent.
function to progress stake.
function to progress mint.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
This function will generate random unlockSecret
and hashlock
pair.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #3 first for any updates in interface and params.
The facilitator will start the scanner process to listen to the StakeRequested
event.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #3 first for the interface.
Once StakeRequested
event is received by the scanner they pass the event data to the facilitator,
The facilitator needs to check if the gateway contract address from the event is supported by the facilitator.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #4 first for any updates in params.
This function will receive transaction receipts.
StakeRequested
eventThe proposed command is as given below, the implementer can improvise this.
./facilitator list
./facilitator list
command../facilitator --help
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
This function used contract interact to call accept stake request on OSTComposer contract.
ACCEPTED
if the transaction is successfulACCEPTED_FAILED
if the transaction is failed.n
block confirmations.The scanner listener reads each and every block from the chain and looks for StakeRequested
event for the given composer contract. Once the event is found it callbacks the facilitator with event data.
scanner listener keeps a record of the last block scanned so that it can resume the scanner listener from the same block height. For more details check the epic #1
The scanner listener can be for the origin and auxiliary chain.
The scanner listener should update the database tables.
The proposed functions are provided for reference.
The implementer can think this in detail and can add/remove functions
We can call this as EventListner.
constructor.
- Accepts mosaic-config
and facilitator-config
.
{contractAddresss: [Event topics]}
.function to scan the blocks in batches. (Start scan)
blockNumber
as a parameter.blockNumber
is an optional param, if this is provided then the - [ ] function to update the last scanned block number.
- [ ] function to get transactions for the given block.
- [ ] function to get events from the given transaction.
- [ ] function to emit an event when StakeRequested event is found.
- [ ] function to updates stake_request
table status. (create a new ticket for this.)
As a facilitator developer,
I want to ensure that it is possible to run only a single instance of each process like a facilitator, scanner per chain
So that I can avoid any possible race conditions. (Using same worker key)
Each process can make entry to Database when they start. Each process should be uniquely identified.
When there is no existing process running,
If I start a type of process like (Scanner or facilitator) for a chain
It should successfully run the process.
When there is an existing process running of a type,
If I start the process for a chain with the same worker keys,
It should break the process with an appropriate message.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
This function is to get the message status from the outbox of messageBox in origin chain (source).
This will be used by the worker to check the latest status of the facilitation from the chain before it performs the actual stake and mint facilitation.
These are the commands that can be used to run the facilitators.
The proposed command is as given below, the implementer can improve this.
./facilitator --mosaic-config <mosaic_config_file_path> --facilitator-config <facilitator_config_file_path> --chain-id <chain-id>
./facilitator
.mosaic_config_file_path
is the path for the mosaic.json
file.facilitator_config_file_path
is the path for the facilitator.json
file.chain-id
must be the value of the chainid to be started.$HOMEDIR/.mosaic/ethereum.json
$HOMEDIR/.mosaic/ropsten.json
$HOMEDIR/.mosaic/<chain-id>/facilitator.json
./facilitator --help
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #3 first for any updates in interface and params.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #2 first for any updates in params.
The facilitator-config
file provided must be verified before we start the facilitator.
Implement the function to validate the facilitator-config.
Config provides all the data that is needed to run the facilitator. It reads data from mosaic-config
and facilitator-config
JSON files and provides getter functions that can be used in other parts of the facilitator code base.
The proposed functions are provided for reference.
The implementer can think this in detail and can add/remove functions.
mosaic-config
and facilitator-config
as paramsconfig
(against schema for facilitator-config : mosaic-config
)encrypted_account
for a given address.Example of mosaic-config.json is given below.
{
"originChain": {
"chainId": 3,
"contractAddresses": { // Contracts deployed on origin chain, should be reused for all auxiliary chains.
"simpleTokenAddress": "",
"merklePatriciaLibAddress": "",
"GatewayLibAddress": "",
"messageBusAddress": ""
}
},
"auxiliaryChains": {
"1406": {
"chainId": 1406,
"bootNodes": [], //List of boot nodes
"genesis": {},
"contractAddresses": { // Contract addresses specific to single auxiliary chain
"origin": { // Contracts deployed on origin chain specific to single auxiliary chain
"anchorOrganizationAddress": "",
"anchorAddress": "",
"ostGatewayOrganizationAddress": "",
"ostEIP20GatewayAddress": "",
"ostComposerAddress": ""
},
"auxiliary": { // Contracts deployed on the auxiliary chain
"ostPrimeAddress": "",
"anchorOrganizationAddress": "",
"anchorAddress": "",
"merklePatriciaLibAddress": "",
"GatewayLibAddress": "",
"messageBusAddress": "",
"ostCoGatewayOrganizationAddress": "",
"ostEIP20CogatewayAddress": ""
}
}
},
"1407": {
"chainId": 1407,
"bootNodes": [], //List of boot nodes
"genesis": {},
"contractAddresses": { // Contract addresses specific to single auxiliary chain
"origin": { // Contracts deployed on origin chain specific to single auxiliary chain
"anchorOrganizationAddress": "",
"anchorAddress": "",
"ostGatewayOrganizationAddress": "",
"ostEIP20GatewayAddress": "",
"ostComposerAddress": ""
},
"auxiliary": { // Contracts deployed on the auxiliary chain
"ostPrimeAddress": "",
"anchorOrganizationAddress": "",
"anchorAddress": "",
"merklePatriciaLibAddress": "",
"GatewayLibAddress": "",
"messageBusAddress": "",
"ostCoGatewayOrganizationAddress": "",
"ostEIP20CogatewayAddress": ""
}
}
}
}
}
Example of facilitator-config.json is given below.
{
"db": {
"db_path": "<db_path>",
// other params
},
"chains": {
<chain_id_1>: {
"rpc": "http://localhost:3000",
"workers": [0xaddress1, 0xaddress2],
},
<chain_id_2>: {
"rpc": "http://localhost:4000",
"workers": [0xaddress3, 0xaddress4]
}
}
"encrypted_accounts": {
"0xaddress1": {
},
"0xaddress2": {
},
"0xaddress3": {
},
,
"0xaddress4": {
}
}
}
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #2 first for the interface.
Config provides an easy way to read the config from mosaic-config
and facilitation-config
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #4 first for any updates in params.
This function will get the block header.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
This function does the following.
progressStake
transaction.PROGRESS_STAKE
if the transaction is successfulPROGRESS_STAKE_FAILED
if the transaction is failed.This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #3 first for any updates in interface and params.
If the process encounters SIGTERM, then it should be intercepted.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #2 first for any updates in params.
Implement the constructor function for Config.
mosaic-config
and facilitator-config
as params.mosaic-config
.facilitator-config
.These are the commands that can be used to run the facilitators.
The proposed command is as given below, the implementer can improvise this.
./facilitator stop --chainid <chainId>
./facilitator stop
.chainid
is the chainid value to be stopped../facilitator --help
The ticket is about implementation of StakeRequest model.
Create folder models.
Create class StakeRequest inside models directory which will interact with stake_requests table.
All model classes which interacts with database tables should be put in models directory.
Create script to create stake_requests table. SequelJS has good support for migrations. https://sequelize.readthedocs.io/en/latest/docs/migrations/
StakeRequest model stores stake requests information and their processing status. Below is the proposed schema of stake_requests table.
Note: This will give a rough idea, the schema can be improved by the implementer.
name | type | references | description
-- | -- | -- | --
"id" | INTEGER | | Primary Key
"stake_request_hash" | VARCHAR | | Unique Key.
"message_hash" | VARCHAR | |
"origin_chain_id" | INTEGER | |
"aux_chain_id" | INTEGER | |
"gateway_address" | VARCHAR | |
"origin_worker_address" | VARCHAR | |
"auxiliary_worker_address" | VARCHAR | |
"hash_lock" | VARCHAR | |
"unlock_secret" | VARCHAR | |
"status" | INTEGER | |
"created_at" | DATETIME | | Time when the record was created
"updated_at" | DATETIME | | Time when worker completed it's process
Proposed STATUS Values (Add/Remove if needed)
REQUESTED
ACCEPTED
ACCEPTED_FAILED
ACCOUNT_PROVEN
ACCOUNT_PROVEN_FAILED
STAKE_INTENT_CONFIRMED
STAKE_INTENT_CONFIRMED_FAILED
PROGRESS_STAKE
PROGRESS_STAKE_FAILED
PROGRESS_MINT_COMPLETE
PROGRESS_MINT_FAILED
UNKNOWN
StakeRequest model should define methods for below use cases:
Define status enum values.
Method to fetch stake requests waiting for processing and mark them as in progress. After fetching the pending stake requests, update worker address(auxiliary or origin), updated_at in the table. We can process the requests in batches also.
Method to insert new entry for StakeRequested event in stake_requests table.
Method to update the status, updated_at in stake_requests table for a given stake_request_hash.
This command will create the facilitator config file.
The proposed command is
./facilitator init
--mosaic-config <mosaic_config_path>
--chain-id <aux_chainId>
--origin-password <origin_password>
--aux-password <aux_password>
--origin-rpc <RPC_endpoint>
--aux-rpc <RPC_endpoint>
--db-path <db_path>
--db-username <db_user_name>
--db-password <db_password>
--db-path
, --db-username
, --db-password
are optional, if it is passed then given values will be used, otherwise new db, username and password is created, and added to config.
chain-id
(auxiliary chain id), the origin chain id can be retrieved using mosaic-config
../mosaic/<chain>/facilitator-config.json
.{
"db": {
"db_path": "<db_path>",
// other params
},
"chains": {
<chain_id_1>: {
"rpc": "http://localhost:3000",
"workers": [0xaddress1, 0xaddress2],
},
<chain_id_2>: {
"rpc": "http://localhost:4000",
"workers": [0xaddress3, 0xaddress4]
}
}
"encrypted_accounts": {
"0xaddress1": {
},
"0xaddress2": {
},
"0xaddress3": {
},
,
"0xaddress4": {
}
}
}
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #3 first for any updates in params.
Implement the constructor function for Facilitator.
Chain[] param is optional if it is provided then the facilitation will be supported to these chains only.
By default, the facilitation is supported for all the chains specified in facilitator-config.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
This function does the following.
progressMint
transaction.PROGRESS_MINT
if the transaction is successfulPROGRESS_MINT_FAILED
if the transaction is failed.This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
If the process encounters SIGTERM
, then it should be intercepted.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #2 first for interface and any updates in params.
Worker address provided in the config must be validated so that they can be used for facilitation.
MOSAIC_ADDRESS_PASSW_<address>
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
Implement a function that checks the balance of worker address before performing the task.
The implementer can think of logic to optimize this, instead of querying the balance every time, the balance can be stored into a variable at the time of initialization. After each transactions, the balance can be updated.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
This function does the following.
confirmStakeIntent
transaction.STAKE_INTENT_CONFIRMED
if the transaction is successfulSTAKE_INTENT_CONFIRMED_FAILED
if the transaction is failed.The ticket is about setting up DB and defining npm to interact with the databases tables.
Setup docker+mysql sqlite. If we decide to chose any other database, update the docker accordingly.
Use a good javascript npm/library for interacting with database. Below npm can be used. It also supports migration.
http://docs.sequelizejs.com/
Acceptance criteria of the ticket is to run a sqlite mysql node and connect it with nodejs npm.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in params.
It should be able to perform the following:
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #4 first for any updates in params.
This function is to update the database with the latest block number that was scanned.
This will be useful when the scanner starts, it can resume the scanning from that block number.
The implementer can decide the table schema.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
This is a function that uses the contract interacts to approve the bounty amount.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #2 first for any updates in params.
The mosiac-config
file provided must be verified before we start the facilitator.
Implement the function to validate the mosaic-config.
mosaic-config
contains all the (mandatory and facilitator) params required for facilitation against mosaic schema, see schema mosaic-chains #44
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
This function is to get the message status from the inbox of messageBox in auxiliary chain (target).
This will be used by the worker to check the latest status of the facilitation from the chain before it performs the actual stake and mint facilitation.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
This function does the following.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #4 first for any updates in params.
Once the event is received.
Inform the parent process about the event data.
This function will accept the event data and send the message to the parent process id.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #3 first for any updates in interface and params.
gasLimit
is high enough to perform the stake and mint facilitation.gasPrice
provided is at par to perform stake and mint facilitation.For now, the par values will be in constants.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #4 first for any updates in interface and params.
If the process encounters SIGTERM, then it should be intercepted.
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #5 first for any updates in interface and params.
The worker addresses will perform the actual transaction, so they must be unlocked before.
This function will perform the following.
encrypted_account
from Config for the given address.The password should be available in the environment variable. The key will be in the format MOSAIC_ADDRESS_PASSW_<address>
. This is a proposal, the implementer can improvise.
Facilitator.ts is the main file that is responsible for the facilitation
The proposed functions are provided for reference.
The implementer can think this in detail and can add/remove functions.
constructor
mosaic-config
and facilitator-config
as params.function to validate gateways.
function to start the event scanner child-process.
function to resume facilitation.
function to start the worker child-process.
function to select worker address for chains.
function to determine if gasPrice / gasLimit is sufficient to perform facilitations.
function to handle SIGTERM
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #4 first for any updates in params.
Implement the constructor function for Scanner.
mosaic-config
and facilitator-config
.This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #3 first for any updates in interface and params.
Below is the proposed commander for starting listeners. Before this ticket, schema for process management should be finalized.
Create a migration script to create the finalized process management schema.
./listener start --mosaic-config <mosaic-config-path> --facilitator-config <facilitator-config-path> --chain-id <chain-id>
The executable must be able to support multiple chains simultaneously.
Support multiple workers (acceptStakeRequest & progressStake) simultaneously. (Q: private keys are held on node, or on client side? A: on the client side, local machine)
Subscribe / Scan StakeRequested
event.
Workers should avoid race on acceptStakeRequest; eg round-robin
Track the gas available for each worker, if the worker gas too low to perform any transaction then it must not do facilitation.
Calculate if the gasPrice
and gasLimit
should be acceptable to do facilitation. (What's the threshold value? for now, we can use a constant value)
It must act as a hunter for any failed facilitation. (Out of scope for now.)
Handle SIGTERM
gracefully.
Should be able to resume facilitation if it was not complete.
It should track the states of each step of facilitation in DB. Update the DB with the latest state e.g REQUESTED, ACCEPTED, PROGRESSED, etc.
Manage hash lock and unlock secret.
config.json :
{
"db_path": "<db_path>",
"chains": {
<chain_id_1>: {
"rpc": "http://localhost:3000",
"ws": "ws://localhost:3001",
"workers": [0xaddress1, 0xaddress2],
"composer_address": "0xcomposerContractAddress",
"supported_gateway_address": ["0xgatewayContractAddress_1", "0xgatewayContractAddress_2"]
},
<chain_id_2>: {
"rpc": "http://localhost:4000",
"ws": "ws://localhost:4001",
"workers": [0xaddress3, 0xaddress4]
}
}
"encrypted_accounts": {
"0xaddress1": {
},
"0xaddress2": {
},
"0xaddress3": {
},
,
"0xaddress4": {
}
}
}
db_path
: Database file pathchains
: Contains details of all the chains.chain_id
: Actual chain id number.rpc
: RPC end point.ws
: Websocket end point.workers
: Account address that can perform facilitation for the given chaincomposer_address
: Composer contract address for the given chainsupported_gateway_address
: Supported gateway contract address. If StakeRequested
event will have the gateway address other than specified here, that will be not taken for facilitation.encrypted_accounts
: This are the encrypted account that is generated by web3.eth.accounts.wallet.add(account);
Here is a reference https://web3js.readthedocs.io/en/1.0/web3-eth-accounts.html#wallet-addPlease note:
composer_address
and supported_gateway_address
both are provided for the origin chain only. This will be checked to start the event scanner.1. Start facilitator :
./facilitator start <config_file_path> --chains <specific chain ids>
config_file_path
: This is the path for the config file. If this is not provided then the facilitator will look at the default location.--chains
: This option is used to specify the chain ids that facilitator should facilitate. This will be origin chain ids2. help :
./facilitator --help
When facilitator starts.
./facilitator start
When facilitator gets the StakeRequested
events.
The facilitator will validate the Event data.
The facilitator will check if the gateway is supported.
The facilitator will check the addresses table to get the origin and auxiliary chain pair and addresses related to that.
If the address table does not have any data for the given chain id and gateway pair, then query the contract and get all the addresses and update the table.
Facilitator gets the worker addresses for the origin and auxiliary chain.
Facilitator forks a new worker child process and gets the PID.
Facilitator inserts a new entry in thestake_requests
table. This includes the stateRequestHash, chain id, gateway address, stake params, worker address of both chains, pid, state .. etc.
The worker creates the hash lock and unlock secret. Unlocks the accounts. Performs accept stake request. Updates the stake_requests
table with the latest state.
Worker checks if the latest state root is committed in the auxiliary chain. If it is committed then it proceeds with the facilitator. If it is not committed then it will give a call back to Facilitator and stop the process.
The facilitator can try to resume facilitation in time intervals, or there can be a new child process that can observe the state root commit event from the anchor, and give a call back to the facilitator. The facilitator can resume the facilitation.
On Handle SIGTERM:
Example
const { fork } = require('child_process');
const scanner = fork('scan_events.ts', args);
// TODO: Check how we can pass chain Id as and argument in the above statement.
// If we cannot pass the chain Id then we can use send command to
scanner.send({ chain_is: 3 });
// Listen call back.
scanner.on('message', (event) => {
console.log('Event received', event);
// Update DB
// Do things here
facilitate(event);
});
function facilitate(event) {
// Check if gateway address is known
// Check if gasPrice and gasLimit is sufficient to perform facilitation.
// Get worker address (round robin .. etc.)
// Validate worker address can perform transactions.
// Fork a new child process to do facilitation.
const facilitator = fork('facilitate.js');
facilitator.send(< Object with all params that are needed for facilitation>);
// Subscribe to all the events to handle the callbacks
}
Facilitator as a hunter:
There can be one more child process that can act as Hunter
which keeps looking for stake and mint events. Not in the scope for now.
DB proposal:
Please note: This will give a rough idea, the schema can be improved by the implementer.
This executable should resume any pending or failed task when started. To enable this the status of each StakeRequest
must be persisted. We can use a database to achieve this (e.g: sqlite3).
All the child process have the access to read, insert and update contents in the DB.
stake_requests
name | type | references | description |
---|---|---|---|
"stake_request_hash" | VARCHAR | The unique identifier for the table type. | |
"message_hash" | VARCHAR | ||
"origin_chain_id" | INTEGER | ||
"gateway_address" | VARCHAR | ||
"origin_account_address" | VARCHAR | ||
"auxiliary_account_address" | VARCHAR | ||
"hash_lock" | VARCHAR | ||
"unlock_secret" | VARCHAR | ||
"transactions" | VARCHAR | This will contain transaction hashed of the events(json string) | |
"pid" | INTEGER | This is process id, this is assigned when the facilitation is in progress by a worker process. | |
"state" | INTEGER | Enum : REQUESTED ACCEPTED ACCOUNT_PROVEN STAKE_INTENT_COMFIRMED PROGRESS_STAKE PROGRESS_MINT COMPLETE |
addresses
name | type | references | description |
---|---|---|---|
"orign_chain_id" | INTEGER | ||
"gateway_address" | VARCHAR | ||
"auxiliary_chain_id" | VARCHAR | ||
"composer_address" | VARCHAR | ||
"cogateway_address" | VARCHAR | ||
"anchor_address" | VARCHAR | ||
"co_anchor_address" | VARCHAR |
facilitator.ts
SIGTERM
.scanner.ts
StakeRequested
event is found.worker.ts
password_0xethereumAccountAddress
, that can be used for unlocking.hashLock
and unlockSecret
. Update DB with these values to keep track in case this process dies.Tickets:
Create a new repository. (Done)
Finalize config.json (Review and suggest)
Finalize DB schema. (Review and suggest)
Finalize the enum value for states column instake_request
table.
Implement DB interaction layer
StakeRequested
event in stake_requests
table.stake_requests
.stake_requests
.stake_requests
.stake_requests
.addresses
table.Implement block scanner.
StakeRequested
event is found.SIGTERM
to inform parent process.Add commander: ./facilitator start
and ./facilitator --help
following the proposed command
./facilitator start <config_file_path> --chains <specific_chain_ids>
./facilitator start
.config_file_path
is the path for the config file. If this is not provided then the default path is used.--chains <specific_chain_ids>
this options can be used to specify the chains. The config file may have many chains specified, if this option is provided then the facilitator will start the process for this specific chains only. By default, it will start the process for all origin chains.Implement worker. (can mosaic.js facilitator be used?)
SIGTERM
to inform parent process.Implement Facilitator
gasPrice
and gasLimit
is sufficient to perform facilitations.SIGTERM
to inform parent process.Update readme
This is an implementation ticket. The actual implementation may vary as we start working on it.
Please check #4 first for any updates in params.
blockNumber
as a parameter.blockNumber
is an optional param, if this is provided then the scanner will start scanning from that block, else it will start from the latest blockIn the constructor, we get the chain id. From this chain id we know if its origin chain or auxiliary chain.
We can get the contract address from the config that the listener is interested in listening to the events.
The event/topic names, model identifier is hardcoded for the contract type in the code.
ABI's are available in mosaic-contract
NPM package for decoding the events.
The implementer can decide how we update the database.
All the events related to OSTComposer, Anchor, Gateway, and CoGateway.
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.