GithubHelp home page GithubHelp logo

fourthstate / plasma-mvp-sidechain Goto Github PK

View Code? Open in Web Editor NEW
112.0 12.0 34.0 13.69 MB

Cosmos SDK (Tendermint consensus) side-chain implementation for plasma

License: Apache License 2.0

Go 76.69% JavaScript 14.32% Solidity 7.89% Shell 0.48% Makefile 0.39% Dockerfile 0.22%

plasma-mvp-sidechain's Introduction

Plasma MVP Sidechain

Go Report Build Status codecov Discord license

Implementation of Minimum Viable Plasma compatible with our rootchain contract

Project Status

There is very little development occuring for this project. We will continue to maintain this repository by thoroughly reviewing any open source contributions. We will provide support and guidance for anyone looking to continue development.

What is Plasma?

Plasma has two major components: verification and computation. Verification is handled by the rootchain contract, which resolves any disputes and distributes funds accordingly. Computation is handled separately by a sidechain, which maintains its security through reporting proofs via merkle roots to the rootchain contract.

Plasma MVP utilizes a UTXO model, which allows for secure and compact proofs. Learn more about plasma on learnplasma.org!

We are using Tendermint for our consensus protocol. This sidechain currently supports a single validator, but will be updated in the future to support multiple validators.

Quick Start

Install using a script

This script can be used on a fresh server that has no dependencies installed.

curl https://raw.githubusercontent.com/FourthState/plasma-mvp-sidechain/develop/scripts/plasma_install.sh > install.sh
chmod +x install.sh
./install.sh

Manual Install

Requirements:

Pull the latest version of the develop branch.

make install

Plasma Node:

Run plasmad init to start an instance of a plasma node. Use the --home <dirpath> to specify a location where you want your plasma node to exist.

Navigate to <dirpath>/config/ (default is $HOME/.plasmad/config), set configuration parameters in config.toml and plasma.toml. Run plasmad start to begin running the plasma node.

Plasma Client:

Navigate to $HOME/.plasmacli, set ethereum client configuration parameters in plasma.toml. Use plasmacli to run any of the commands for this light client

Plasma Architecture

See our research repository for architectural explanations of our Plasma implementation.

Documentation

See our documentation

Contributing

See our contributing guidelines. Join our Discord Server.

plasma-mvp-sidechain's People

Contributors

adityasripal avatar colin-axner avatar dependabot[bot] avatar hamdiallam avatar legengliu avatar p0n1 avatar paulrberg avatar wesgraham avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

plasma-mvp-sidechain's Issues

Cosmos SDK Hash Function

Does anyone know what hash function is used in cosmos-sdk when forming blocks? We may have to adjust the root contract depending on how difficult it would be to change what is used to hash each transaction.

Documentation

Documentation for child chain and the client should be added to make it easier for new contributors to become familiar with repo.

Finish Implementing handleDepositMsg

handleDepositMsg in handler.go needs to be implemented. It should issue a new utxo to the corresponding address and update the utxo mapper accordingly.

Make Position struct or uint for spendMsg types

If we wanted to change the following:

  • blknum -> uint64
  • txindex -> uint16
  • oindex -> uint8

Then using a uint slice will no longer work for passing around the positions. We could leave all three field as uint's or we could create a Position struct which stores the three fields.

Here is an example:

type Position struct {
    blknum     uint64
    txindex    uint16
    oindex     uint8
}

We could store this struct in our BaseUTXO. For mapping from position to UTXO, I suggest we do the following: blknum * 1000000 + txindex * 10 + oindex. Since txindex * 10 < 1,000,000 and the total position % 2 will give the oindex. I am not sure what restrictions may exist on what type a key may be for the mapping.

Access to ConfirmSig Address/PubKey

In order for the ante handler to verify a confirm signature, it will need to have access to the relevant address/pubkey. Currently our utxo's store the address/pubkey belonging to the owner of those outputs. We could add 4 new methods to our utxo interface:

  • GetCSAddress
  • SetCSAddress
  • GetCSPubKey
  • SetCSPubKey

We could then set these attributes whenever a utxo is about to be spent. We could add extra fields into the SpendMsg, but I think adding extra fields/functions to our UTXO struct would be the more efficient solution.

Enable Light Clients

Create verifier so that people can run light clients that query full nodes with proof.

See code at client/context for our repo and cosmos-sdk's

Change denom field to sdk.Uint

Currently the denom (amount) of a UTXO is a uint64. When expressed as Wei, the maximum denom is less than 19 ether. Need to change to a big Int, can use sdk.UInt implementation.

Implementing Deposit Blocks

The child chain is responsible for taking note of deposit blocks on the rootchain. Here's one way I think we could do this.

Treat deposits like regular SpendMsg's as we do now. Let them go to the mempool, but in the mempool create a custom prioritization, giving messages formed as a deposit message top priority (might not be necessary if the core can quickly search the mempool). Tendermint core would then search for the correct deposit to propose as the next block (as ordered by the root chain).

This implementation would require adding a geth node to interact with tendermint core as well as adding in logic to handle when/how to form a deposit block. We would somehow need to add functionality for our ante handler to also check the root contract if it receives a deposit message.

Thoughts?

Finalized Exits

We will need a some mechanism for taking into account utxo's that have been exited via the rootchain. This can probably be done with a finalizedExits mapping similar to the rootchain. This should be implemented where ever the geth full node is, and in the ante handler to check that the position being referenced has not been exited.

Folder/Package Renaming

Currently the bulk of the application is under the package types. The name "types" does not accurately represent all the files it contains. I think we should split this package into 2. 1 folder/package containing files for validation and low level database changes (mapper) and the other folder/package for higher level application changes and structure definitions.

For example we could have the packages auth and db:
package auth contains:

  • mapper.go
  • ante.go
  • utxo.go

package db contains:

  • handler.go
  • tx.go
  • keeper.go
  • errors.go

To work with the current version of the cosmos sdk we should add a errors.go file to define the codespace our errors will function in as outlined here. Notice that none of the files in package auth need to return an sdk.Error.

Thoughts?

Implement txIndex

Since it is not safe to assume the persistent storage of txIndex in context, we will need to add txIndex into the app.go file. context.go and context_test.go can be removed.

Proposal for handling txIndex:

  • add a uint pointer to the child chain struct. Pass this into either ante.go or handler.go

Seems more appropriate to increment the txIndex at the end of the handler since this is near the end of DeliverTx running.

Other ideas?

Contribution Guidelines

Should be added before merge to master. Similar in style to the rootchain contribution guidelines

Update BaseTx struct/Update to sdk v0.18.0

BaseTx struct needs to be updated by removing StdSignature and replacing it with either:

type BaseSignature struct { crypto.PubKey crypto.Signature }

I also think we could just use crypto.Signature in replacement of StdSignature since, to the best of my knowledge, we never make use of the PubKey in the StdSignature struct.

We can make this adjustment since sdk.Tx interface now only requires the GetMsg() function. This will allow us to change the TxDecoder to support RLP decoding. RLP encoding will also be added to the client package, so all txBytes included in a tendermint block are RLP encoded.

If there are any other changes that need to be made with updating from 0.17.2 to 0.18.0, they should be noted on this issue.

Better error messages

Error messages should be more descriptive and also include the value that caused the error.

For example, if the UTXO does not exist for a transaction, error message should print out the position that tried to be spent.

plasmad start cause panic: unknown pubkey type: <nil> <nil>

I was able to follow the installation steps and use plasma init to generate the init files, but validator_pubkey is null for some reason

{
  "moniker": "",
  "chain_id": "test-chain-GMWOcq",
  "node_id": "b1652f568d1d102d892fcd7e6dc18b017fb83286",
  "app_message": {
    "genvalidator": {
      "validator_pubkey": null,
      "fee_address": ""
    },
    "UTXOs": null
  }
}

It successfully generates the files however then when I run plasmad start, it throws error panic: unknown pubkey type: <nil> <nil>

I[27116-11-27|22:37:22.989] Starting ABCI with Tendermint                module=main 
I[27116-11-27|22:37:22.995] Starting multiAppConn                        module=proxy impl=multiAppConn
I[27116-11-27|22:37:22.996] Starting localClient                         module=abci-client connection=query impl=localClient
I[27116-11-27|22:37:22.996] Starting localClient                         module=abci-client connection=mempool impl=localClient
I[27116-11-27|22:37:22.996] Starting localClient                         module=abci-client connection=consensus impl=localClient
I[27116-11-27|22:37:22.996] ABCI Handshake App Info                      module=consensus height=0 hash= software-version= protocol-version=0
I[27116-11-27|22:37:22.996] ABCI Replay Blocks                           module=consensus appHeight=0 storeHeight=0 stateHeight=0
panic: unknown pubkey type: <nil> <nil>

goroutine 1 [running]:
github.com/FourthState/plasma-mvp-sidechain/vendor/github.com/tendermint/tendermint/types.tm2pb.PubKey(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/Users/developer/go/src/github.com/FourthState/plasma-mvp-sidechain/vendor/github.com/tendermint/tendermint/types/protobuf.go:113 +0x472
github.com/FourthState/plasma-mvp-sidechain/app.(*ChildChain).initChainer(0xc0001139d0, 0x4d14f80, 0xc0009cca50, 0xc0004dd380, 0xb, 0x218ca368, 0xed38f236e, 0x0, 0xc000035a60, 0x11, ...)
	/Users/developer/go/src/github.com/FourthState/plasma-mvp-sidechain/app/app.go:120 +0x291
github.com/FourthState/plasma-mvp-sidechain/app.(*ChildChain).initChainer-fm(0x4d14f80, 0xc0009cca50, 0xc0004dd380, 0xb, 0x218ca368, 0xed38f236e, 0x0, 0xc000035a60, 0x11, 0xc0004dd280, ...)
	/Users/developer/go/src/github.com/FourthState/plasma-mvp-sidechain/app/app.go:85 +0xb6

Add initChainer method

We need to add an initChainer method to app.go. Should create an utxo's needed for the genesis state of the child chain.

Change Confirmation Hash to be compatible with rootchain

When deciding upon what the confirmation hash should consist of, we need to keep a couple a things in mind.

  • Confirmation Hash needs to be secure against a cross plasma chain attack outlined here
  • It needs to be unique so an attacker cannot reuse an old confirmation signature on a new tx.

Solution 1 (currently implemented on the rootchain): Sign over the Hash(Hash(txBytes) + block_hash). By taking the hash of txbytes, we have uniqueness since each set of txbytes contains at least one position and every position is unqiue. Signing over the block hash allows us to avoid a cross chain attack since the block hash is unique and can only be recreated if another chain has identical history up to that block. Deposits and fees will not have confirm signatures associated with them and therefore not cause any issues with this scheme.

Solution 2: Sign over the Hash(Hash(contract address) + Hash(position priority)) . The ethereum contract address allows us to avoid the cross plasma chain attack and the hash of the priority provides uniqueness. However, hashes of uints in golang can produce varying results to hashes on uints in solidity.

Solution 3: Sign over the Hash(Hash(contract address) + Hash(rlp encoded position)). This maintains the same properties as above except the hashes in golang and solidity will be consistent. Since solidity to the best of my knowledge does not have an onchain rlp encoder at the moment, we will pass in the position bytes into the start exit functions.

Solution 1 is our current decision on how to handle confirmation hashes.

Connecting sidechain via geth full node

In order to verify deposits on the rootchain, the sidechain will need the capability to ping the rootcontract for the latest deposits to have happened. This will need to be done via a full geth node.

I would suggest connecting the full geth node to either end blocker or begin blocker. Then any deposits that are verified to be on the root contract should be added into the utxo db with their position{0,0,0,depositNum}

AnteHandler

The ante handler is responsible for checking the following:

  • inputs = outputs + fee
  • Tx sig and confirm Sig are valid
  • The Utxo's trying to be spent exist and have not been spent

The last check will be dependent upon our implementation of finalizing utxo positions.

Deprecate decodePosition and nil utxo checks

The function decodePosition() can be deprecated since it should never be used. We only encode the position to use it as a key for the mapping from position to utxo.

Also, I think any check for utxo == nil that occurs after the ante handler has executed may also be deleted. One check is sufficient in the ante handler. The occurrence of this in keeper.go, SpendUTXO() may be deleted and in mapper.go in DeleteUTXO()

Deprecate SealedUTXOMapper

We can do away with our use of "sealing" in mapper.go. Instead we should register all interfaces and concrete types in the app.go function MakeCodec() and pass the codec in as a parameter when initializing the UTXOMapper

Switch UTXOMapper keys

Currently, the key is an encoded position.

We should switch this to:

<owner_address> + <encoded_position>

This will allow us to iterate through all UTXO's owned by an address using KVStorePrefixIterator.
Useful for client UX.

Confirm Signatures and Finalizing UTXO positions

In order to verify that a utxo being spent is valid we need check either:

  1. UTXO positions match up with the current state of our utxo database
  2. ConfirmSig is valid
    or we need to check:
  3. UTXO referenced existed in the block it is trying to be spent from (would need merkle proof)
  4. ConfirmSig is valid

In order to use the first method we would need to update our utxo database after a block is submitted. This is currently not possible using CheckTx and DeliverTx since we are missing the information for the transaction index. Currently we use a finalize message to be passed around to update the database. However, there are two issues with this:

  1. FinalizeMsg is included into blocks and would require a fee
  2. FinalizeMsg would probably need to supply a merkle proof to be verified

If we wanted to verify the existence of a UTXO using a merkle proof, we would need access to our cms (commitMultiStore) in base app. This is only accessible in baseapp. We would also need to encode merkle proofs into transactions (how large is our transaction size becoming?). Perhaps we could add a KVStore of merkle roots to our context which we could add when endBlock() is called? If this is possible then we could add a merkle proof to our spendMsg and the ante handler should have enough information to verify its existence. Perhaps we adjust our utxo database to hold all spentMsg's to protect against double spends?

In summary:

  • Add merkle proof to spendMsg
  • Add merkle proof mapper to ctx
  • Ante handler check existence of utxo inputs
  • Utxo db holds spent tx's to protect against double spend

Thoughts?

Mapping Position => UTXO

Originally we were mapping from Address => UTXOHolder.
Each UTXOHolder had a slice of UTXO's belonging to that address

Since we can now access the position of a utxo which is unique, we can map from Position => UTXO
Each position is go amino encoded into bytes and then used to map to encoded bytes of the UTXO

How to deal with finality on rootchain

The sidechain must make a judgement on when a transaction is finalized on the rootchain before it includes it into the sidechain state.

Examples:
For a sidechain to spend a deposit it must first determine whether that deposit has been finalized on the rootchain
Potential Solution: Use a finality gadget on the rootchain that sidechain must defer to for finality judgement. If validator includes spend deposit that has not been checkpointed by finality gadget, it is considered malicious.

For a sidechain to delete UTXO as explained in #24 , it must determine when the startExit transaction gets finalized since if the transaction gets reorged, user may try to spend on sidechain instead of resubmitting transaction.
Potential Solution: In this case, it may be better to ignore finality. If validator sees startExit simply delete the UTXO. If transaction gets reorged, user has no recourse but to resubmit transaction.

Makefile

Create makefile to easily build and install project.

README

README should cover necessary information for anyone looking to contribute to the sidechain or to use it

CLI tests

Need tests that actually construct commands and deliver to tm. Model after sdk/cmd/gaia/cli_test

Handling Fees on Sidechain

Continuation of discussion in #18

Ideally, we can implement the Fee as a transaction. This simplifies exiting fees as well as allows them to be easily spent on the sidechain.

The issue normally with creating fees as a transaction is that we don't want to include this tx in the mempool since it's position in the blockchain is nondeterministic:
tendermint/tendermint#1776

Trying to include the tx within the application naively will make it impossible to sign correctly.

Proposed solution:
Reserve the last tx index in a block n: (n, 2^16 - 1, 0, 0)
In EndBlocker, we should create a spend Msg with input position equal to last position in a block. Its GetSigners positions should be nil. This will require a change to SpendMsg interface.

Thus the tx can be constructed entirely in EndBlocker, we should then be able to Deliver(tx) and have it included in txHash. This will make the fee UTXO semantically identical to a regular UTXO, allowing the validator to exit, spend, and merge them as one normally would.

This construction is also safe with both Minimum and More Viable Plasma since its priority is always higher (lower is better) than every other tx in the block..
Thus, if the validator is malicious, every tx included in the submitted block (and in the current mempool with More Viable Plasma) can exit before the validator fee tx.

In Minimum Viable Plasma, validator has priority equal to: (n, t, 0, 0) where t is the last tx index in block n. May not be 2^16 if block is not full.

In More Viable Plasma, validator has priority equal to: (n, 2^16 - 1, 0, 0) even if the actual position is (n, t, 0, 0) where t is last tx index in block, t < 2^16.

Need some feedback on this construction before going ahead and implementing it.
@colin-axner

Adding Testing capabilities with Tendermint via Command Line

Before moving on and adding more complexity, we want to ensure that our current base layer of our blockchain works as expected.

At it's current state on master it should be capable of:

  • Sending transactions
  • Updating State
  • Validating transactions
  • Rejecting invalid transactions

To sync up with Tendermint via Command Line we will need:
Plasma Node
Plasma Light Client

The Plasma node is responsible for maintaining the application and processing received transactions. I have already done this in my branch colin/client. The node is simply just using the sdk's server client to start up a node with the plasma child chain. The node will be running a tendermint node also. To test out this functionality simply, pull my branch, do go build in plasmad/ directory, then do go install. Run plasmad init and then plasmad start. You should see empty blocks being committed by the tendermint node.

The Plasma Light Client is responsible for doing any users functions such as sending a transaction or querying their utxo's. The light client will need to be in communication with the node in order to do this.

I was planning on building the client directory as follows:

client/
    plasmad/
        main.go
    plasmacli/
        main.go
        cmd/
            sendtx.go
            version.go
            more.go
            commands.go
            here.go

Double Spend Bug

Validate Basic needs to check to ensure input1 != input2. It is currently possible to spend the same inputs twice in the same msg becuase they are being verified and updated in the same function calls.

Save confirm sigs in App State

Once confirm sigs have been validated, they should be stored in app state. Either in a seperate store or with unique prefix. This way, any client can easily retreive old confirm sigs

Make UTXO modular

Turn UTXO logic into its own module so that anyone building on SDK can use utxo model

Implement export.go

Currently one can manually import and export encrypted JSON key files.

  • Navigate to $HOME/.plasmacli/keys and copy/paste the keyfile you want to backup or export/import

Using the Import() and Export() function from the keystore we should support the capability for users to input the file location of the key they would like to import and the ability to save an export key file to a users desired location without having to navigate to /.plasmacli/keys

Testnet Checklist

Implementation:

  • eth/: Change HasTxBeenExited to be more robust like GetDeposit and return false if Exit gets successfully challenged.
  • auth/: Return error if the inputs to input (grandparents) are exitting/exitted. Allow tx to pass again if grandparent(s) have been successfully challenged.
  • auth/: Save validated confirm sigs in app state so they are retreivable by client. Ref: #101
  • auth/: Fail CheckTx if Fee is larger than first input Amount. Ref: FourthState/plasma-mvp-rootchain#84
  • client: Include Ethereum RPC url in client context so that client can check if UTXO has been deemed invalid because of ancestor's exit.
  • client: Include client commands that will generate all arguments necessary to challenge or start an exit. (Including providing confirm sigs for challenge if available)

Testing:

  • Deposit on rootchain, spend on sidechain - done manually on 975356c
  • Deposit on rootchain, exit, spend on sidechain fails - done manually on 975356c
  • Deposit on rootchain, spend on sidechain, exit deposit, challenge depositExit - hardcoded tests in rootchain repo
  • Exit regular UTXO on rootchain, spend on sidechain fails
  • Spend UTXO A, Exit UTXO A, Spend of A's child fails, challenge A, spend of A's child succeeds
  • Check no obvious synchrony issues with two nodes running two different geth nodes with same rootchain state. (perhaps one with delayed calls to rootchain). (Ex. On node 1, deposit at TM block 10. On node 2, deposit at TM block 50. Spend-deposit tx at TM block 100)

Logistics:

  • Merge implementation PR's to develop
  • Merge develop to master

Checking FeeUTXO Amount

The last tx in a block needs to be a fee utxo as per our FeeStructure

Currently this is being verified in ante.go by storing a temporary fee utxo that is updated with fee amounts until the txindex == feeTxIndex.

The creation and deletion of utxo's for checking the fee amount seems over complicated and unnecessary. Instead similar to txIndex we can have feeAmount stored in our application and passed into ante handler. This can be updated in the ante handler by adding msg.Fee at the end of the file.

Then if txIndex == feeTxIndex we just need to check that feeAmount == msg.Fee.

This check will only be done if it is a DeliverTx and not CheckTx

Creating a cluster locally to test

How can i create a cluster of sidechain nodes and test . Underneath i know its a cosmos app , is there a way to run a cluster of multiple cosmos apps locally ? This would indeed help with testing

Move Keeper from mapper.go to keeper.go

Our UTXOKeeper should not be in the same file as UTXOMapper. By putting the keeper into it's own file we can allow UTXOMapper to not be associated with an error codespace as it does not need to return an sdk.Error. This proposal is consistent with #16 as well as the conventions for codespacing outlined here

How to use plasmacli send to send tokens between two addresses?

I have successfully generated two addresses and added to genesis.json. They can be queried by plasmacli info and both return correct balance and info. Below is how they are configured in genesis

{
    ...
    "UTXOs":  [
          {
            "Address": "0x965dfa534B1bC4a88c60BA4287e4ceaF38eC7B50",
            "Denom": "1000",
            "Position": [
              "0",
              "0",
              "0",
              "1"
            ]
          },
          {
            "Address": "0xCBE12C2c094a773c076F1f2d189c06fAaCD34EDD",
            "Denom": "1000",
            "Position": [
              "0",
              "0",
              "0",
              "2" 
            ]
          }
    ]
  }
}

Then how can I send 1 token from address 0x965dfa534B1bC4a88c60BA4287e4ceaF38eC7B50 to address 0xCBE12C2c094a773c076F1f2d189c06fAaCD34EDD using plasmacli send?

if I run plasmacli send --help, it asks me to provide Input0ConfirmSigs and Input1ConfirmSigs, how do I produce these two using plasmacli sign giving my genesis.json config? and how to supply other parameters in a correct format?


Usage:
  plasmacli send [flags]

Flags:
      --Input0ConfirmSigs string   Input Confirmation Signatures for first input to be spent (separated by commas)
      --Input1ConfirmSigs string   Input Confirmation Signatures for second input to be spent (separated by commas)
      --address string             Address to sign with
      --amounts string             Amounts to be spent, format: amount1, amount2, fee
  -h, --help                       help for send
      --node string                <host>:<port> to tendermint rpc interface for this chain (default "tcp://localhost:26657")
      --position string            UTXO Positions to be spent, format: blknum0.txindex0.oindex0.depositnonce0::blknum1.txindex1.oindex1.depositnonce1
      --to string                  Addresses sending to (separated by commas)

Global Flags:
      --home string   directory for keystore (default "/Users/developer/.plasmacli/keys")

Improvements to CLI

Dependent on #47

Users should not be aware that plasma chain is UTXO-based. From client-side, it should look like an account system.

Change test file signatures and add tests

The app_test.go file needs to be changed to use the same signature as Ethereum. Tests also need to be added to ensure that the rlp encoding/decoding of transactions and the go amino encoding/decoding of utxoHolder's is correct.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.