Giter Club home page Giter Club logo

libbitcoin-consensus's Introduction

Build Status

Coverage Status

Libbitcoin Consensus

Bitcoin consensus library

Installation

$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
$ sudo ldconfig

libbitcoin-consensus is now installed in /usr/local/.

Dependencies

This library requires libsecp256k1. The test cases have a boost dependency.

Configure Options

There is a dependency on boost test for make check builds (tests). The --without-tests option disables test builds and eliminates the boost check during configure.

Supported Platforms

Ubuntu (gcc and clang) and OSX (clang) are regularly tested via a travis build matrix. There are also Visual Studio 2017, 2015 and 2013 solutions for Windows builds, however the VS2013 build is not currently supported due to a compiler incompatibility introduced in recent versions.

About

This library includes the following 35 files considered to be bitcoin script consensus-critical (in bitcoin core). These files are identical to those used in version 0.19.0 of bitcoin core.

amount.h
attributes.h
hash.cpp
hash.h
prevector.h
pubkey.cpp
pubkey.h
serialize.h
span.h
tinyformat.h
uint256.cpp
uint256.h
version.h
compat/byteswap.h
compat/endian.h
crypto/common.h
crypto/hmac_sha512.cpp
crypto/hmac_sha512.h
crypto/ripemd160.cpp
crypto/ripemd160.h
crypto/sha1.cpp
crypto/sha1.h
crypto/sha256.cpp
crypto/sha256.h
crypto/sha512.cpp
crypto/sha512.h
primitives/transaction.cpp
primitives/transaction.h
script/interpreter.cpp
script/interpreter.h
script/script.cpp
script/script.h
script/script_error.h
util/strencodings.cpp
util/strencodings.h

Libbitcoin Integration

Libbitcoin natively implements consensus checks that are redundant with libbitcoin-consensus. Libbitcoin includes a full bitcoin client and server SDK. This includes the full node implementation libbitcoin-node, which builds on libbitcoin and libbitcoin-blockchain.

The libbitcoin-blockchain configuration provides the --with-consensus option. This allows the developer to select either libbitcoin native or libbitcoin-consensus checks. The option defaults to yes so that by default all libbitcoin-node and libbitcoin-server builds use the same script consensus checks as a bitcoin core node.

libbitcoin-consensus's People

Contributors

bardiharborow avatar benhc123 avatar bobalot avatar changlan avatar evoskuil avatar genjix avatar kdomanski avatar kevinejohn avatar lclc avatar ldz1 avatar pablocastellano avatar phantomcircuit avatar pmienk avatar rafalkrupinski avatar swansontec avatar thecodefactory avatar veox 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

Watchers

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

libbitcoin-consensus's Issues

Track satoshi client v0.12.0

This will eliminate the OpenSSL option, making secp256k1 non-optional.

This incorporates a flag for activating verification of BIP65's OP_CHECKLOCKTIMEVERIFY.

There are material changes to secp256k1, so this will create a dependency upgrade requirement for all libraries to a new secp256k1/version4 branch, and the need to update MSVC project/solution files for secp256k1.

Tracking: OpenSSL breaks consensus

See: libbitcoin/libbitcoin-server#61

15:09:10.960411 WARNING [validate]: Input script consensus validation failed
15:09:10.960601 WARNING [validate]: Validate input 46581355dad0b59ac492d9df490292d5311776aac0c5164c8e14fe32012ce43a:1 failed
15:09:10.960745 WARNING [poller]: Storing block 000000000000000009e531e76f8a1c35829b663eef46158ed3505c134a5d6319: Validation of inputs failed

There was an upgrade to OpenSSL that breaks consensus:

http://sourceforge.net/p/bitcoin/mailman/message/33221963

But as described in the about thread, a workaround for this issue was applied before the Satoshi Client 0.10.0 release, so this particular case should no longer be an issue. It is possible to receive invalid blocks, which would appear the same in the log.

Suppress build warnings (cloned code).

config.status: executing libtool commands
  CXX      src/clone/src_libbitcoin_consensus_la-hash.lo
  CXX      src/clone/src_libbitcoin_consensus_la-pubkey.lo
In file included from src/clone/hash.cpp:5:
In file included from ./src/clone/hash.h:11:
./src/clone/prevector.h:151:9: warning: anonymous structs are a GNU extension
      [-Wgnu-anonymous-struct]
In file included from         struct {
        ^
src/clone/pubkey.cpp:5:
In file included from ./src/clone/pubkey.h:9:
In file included from ./src/clone/hash.h:11:
./src/clone/prevector.h:151:9: warning: anonymous structs are a GNU extension
      [-Wgnu-anonymous-struct]
        struct {
        ^
In file included from In file included from src/clone/hash.cpp:5:
In file included from ./src/clone/hash.h:12:
./src/clone/serialize.h:829:71: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
                                                                      ^
src/clone/pubkey.cpp:5./src/clone/serialize.h:835:67: warning: unused parameter :
In file included from ./src/clone/pubkey.h:9:
In file included from ./src/clone/hash.h:12:
'ser_action'./src/clone/serialize.h:829:71: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
      [-Wunused-parameter]inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
                                                                      ^
inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
                                                                  ^./src/clone/serialize.h:835:67: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
                                                                  ^
./src/clone/serialize.h:869./src/clone/serialize.h:869:28: warning: unused parameter 'psz'
      [-Wunused-parameter]
    void write(const char *psz, size_t _nSize):
                           ^
28: warning./src/clone/serialize.h:896:28: : warning: unused parameter 's'
      [-Wunused-parameter]
void SerializeMany(Stream& s)
                           ^
unused parameter 'psz'
      [-Wunused-parameter]
    void write(const char *psz, size_t _nSize)
./src/clone/serialize.h:914:37: warning: unused parameter 's'
      [-Wunused-parameter]
inline void UnserializeMany(Stream& s)
                                    ^
                           ^
./src/clone/serialize.h:932:61: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, ...
                                                            ^
./src/clone/serialize.h:938:63: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
./src/clone/serialize.h:inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action...
                                                              ^
896:28: warning: unused parameter 's'
      [-Wunused-parameter]
void SerializeMany(Stream& s)
                           ^
./src/clone/serialize.h:914:37: warning: unused parameter 's'
      [-Wunused-parameter]
inline void UnserializeMany(Stream& s)
                                    ^
./src/clone/serialize.h:932:61: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, ...
                                                            ^
./src/clone/serialize.h:938:63: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action...
                                                              ^
8 warnings generated.
  CXX      src/clone/src_libbitcoin_consensus_la-uint256.lo
8 warnings generated.
  CXX      src/clone/src_libbitcoin_consensus_la-utilstrencodings.lo
  CXX      src/clone/crypto/src_libbitcoin_consensus_la-hmac_sha512.lo
  CXX      src/clone/crypto/src_libbitcoin_consensus_la-ripemd160.lo
src/clone/crypto/ripemd160.cpp:35:41: warning: unused parameter 'b'
      [-Wunused-parameter]
void inline Round(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint...
                                        ^
src/clone/crypto/ripemd160.cpp:35:66: warning: unused parameter 'd'
      [-Wunused-parameter]
void inline Round(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t ...
                                                                 ^
2 warnings generated.
  CXX      src/clone/crypto/src_libbitcoin_consensus_la-sha1.lo
src/clone/crypto/sha1.cpp:18:53: warning: unused parameter 'c'
      [-Wunused-parameter]
void inline Round(uint32_t a, uint32_t& b, uint32_t c, uint32_t d, uint3...
                                                    ^
src/clone/crypto/sha1.cpp:18:65: warning: unused parameter 'd'
      [-Wunused-parameter]
void inline Round(uint32_t a, uint32_t& b, uint32_t c, uint32_t d, uint32_t& ...
                                                                ^
  CXX      src/clone/crypto/src_libbitcoin_consensus_la-sha256.lo
2 warnings generated.
  CXX      src/clone/crypto/src_libbitcoin_consensus_la-sha512.lo
src/clone/crypto/sha256.cpp:148:6: warning: unused function 'SelfTest'
      [-Wunused-function]
bool SelfTest(TransformType tr) {
     ^
  CXX      src/clone/primitives/src_libbitcoin_consensus_la-transaction.lo
1 warning generated.
  CXX      src/clone/script/src_libbitcoin_consensus_la-interpreter.lo
In file included from src/clone/primitives/transaction.cpp:6:
In file included from ./src/clone/primitives/transaction.h:11:
In file included from ./src/clone/script/script.h:10:
./src/clone/prevector.h:151:9: warning: anonymous structs are a GNU extension
      [-Wgnu-anonymous-struct]
        struct {
        ^
In file included from src/clone/script/interpreter.cpp:6:
In file included from src/clone/script/interpreter.h:10:
In file included from ./src/clone/primitives/transaction.h:11:
In file included from ./src/clone/script/script.h:10:
./src/clone/prevector.h:151:9: warning: anonymous structs are a GNU extension
      [-Wgnu-anonymous-struct]
        struct {
        ^
In file included from src/clone/primitives/transaction.cpp:6:
In file included from ./src/clone/primitives/transaction.h:11:
In file included from ./src/clone/script/script.h:11:
./src/clone/serialize.h:829:71: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
                                                                      ^
./src/clone/serialize.h:835:67: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
                                                                  ^
./src/clone/serialize.h:869:28: warning: unused parameter 'psz'
      [-Wunused-parameter]
    void write(const char *psz, size_t _nSize)
                           ^
./src/clone/serialize.h:896:28: warning: unused parameter 's'
      [-Wunused-parameter]
void SerializeMany(Stream& s)
                           ^
./src/clone/serialize.h:914:37: warning: unused parameter 's'
      [-Wunused-parameter]
inline void UnserializeMany(Stream& s)
                                    ^
./src/clone/serialize.h:932:61: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, ...
                                                            ^
./src/clone/serialize.h:938:63: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action...
                                                              ^
In file included from src/clone/primitives/transaction.cpp:6:
In file included from ./src/clone/primitives/transaction.h:11:
./src/clone/script/script.h:480:40: warning: unused parameter 'b'
      [-Wunused-parameter]
    CScript& operator<<(const CScript& b)
                                       ^
In file included from src/clone/script/interpreter.cpp:6:
In file included from src/clone/script/interpreter.h:10:
In file included from ./src/clone/primitives/transaction.h:11:
In file included from ./src/clone/script/script.h:11:
./src/clone/serialize.h:829:71: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
                                                                      ^
./src/clone/serialize.h:835:67: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
                                                                  ^
./src/clone/serialize.h:869:28: warning: unused parameter 'psz'
      [-Wunused-parameter]
    void write(const char *psz, size_t _nSize)
                           ^
./src/clone/serialize.h:896:28: warning: unused parameter 's'
      [-Wunused-parameter]
void SerializeMany(Stream& s)
                           ^
./src/clone/serialize.h:914:37: warning: unused parameter 's'
      [-Wunused-parameter]
inline void UnserializeMany(Stream& s)
                                    ^
./src/clone/serialize.h:932:61: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, ...
                                                            ^
./src/clone/serialize.h:938:63: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action...
                                                              ^
In file included from src/clone/script/interpreter.cpp:6:
In file included from src/clone/script/interpreter.h:10:
In file included from ./src/clone/primitives/transaction.h:11:
./src/clone/script/script.h:480:40: warning: unused parameter 'b'
      [-Wunused-parameter]
    CScript& operator<<(const CScript& b)
                                       ^
In file included from src/clone/script/interpreter.cpp:6:
src/clone/script/interpreter.h:131:61: warning: unused parameter 'scriptSig'
      [-Wunused-parameter]
    virtual bool CheckSig(const std::vector<unsigned char>& scriptSig, c...
                                                            ^
src/clone/script/interpreter.h:131:106: warning: unused parameter 'vchPubKey'
      [-Wunused-parameter]
  ...char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CSc...
                                                         ^
src/clone/script/interpreter.h:131:132: warning: unused parameter 'scriptCode'
      [-Wunused-parameter]
  ...const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, ...
                                                                 ^
src/clone/script/interpreter.h:131:155: warning: unused parameter 'sigversion'
      [-Wunused-parameter]
  ...char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
                                                             ^
src/clone/script/interpreter.h:136:50: warning: unused parameter 'nLockTime'
      [-Wunused-parameter]
    virtual bool CheckLockTime(const CScriptNum& nLockTime) const
                                                 ^
src/clone/script/interpreter.h:141:50: warning: unused parameter 'nSequence'
      [-Wunused-parameter]
    virtual bool CheckSequence(const CScriptNum& nSequence) const
                                                 ^
src/clone/script/interpreter.cpp:1526:126: warning: unused parameter 'flags'
      [-Wunused-parameter]
  ...char>& witprogram, const CScriptWitness& witness, int flags)
                                                           ^
9 warnings generated.
  CXX      src/clone/script/src_libbitcoin_consensus_la-script.lo
In file included from src/clone/script/script.cpp:6:
In file included from src/clone/script/script.h:10:
./src/clone/prevector.h:151:9: warning: anonymous structs are a GNU extension
      [-Wgnu-anonymous-struct]
        struct {
        ^
In file included from src/clone/script/script.cpp:6:
In file included from src/clone/script/script.h:11:
./src/clone/serialize.h:829:71: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
                                                                      ^
./src/clone/serialize.h:835:67: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
                                                                  ^
./src/clone/serialize.h:869:28: warning: unused parameter 'psz'
      [-Wunused-parameter]
    void write(const char *psz, size_t _nSize)
                           ^
./src/clone/serialize.h:896:28: warning: unused parameter 's'
      [-Wunused-parameter]
void SerializeMany(Stream& s)
                           ^
./src/clone/serialize.h:914:37: warning: unused parameter 's'
      [-Wunused-parameter]
inline void UnserializeMany(Stream& s)
                                    ^
./src/clone/serialize.h:932:61: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, ...
                                                            ^
./src/clone/serialize.h:938:63: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action...
                                                              ^
In file included from src/clone/script/script.cpp:6:
src/clone/script/script.h:480:40: 16warning: unused parameter 'b'
      [-Wunused-parameter]
 warnings    CScript& operator<<(const CScript& b)
 generated.
                                       ^
  CXX      src/consensus/src_libbitcoin_consensus_la-consensus.lo
9 warnings generated.
In file included from src/consensus/consensus.cpp:30:
In file included from ./src/clone/primitives/transaction.h:11:
In file included from ./src/clone/script/script.h:10:
./src/clone/prevector.h:151:9: warning: anonymous structs are a GNU extension
      [-Wgnu-anonymous-struct]
        struct {
        ^
In file included from src/consensus/consensus.cpp:30:
In file included from ./src/clone/primitives/transaction.h:11:
In file included from ./src/clone/script/script.h:11:
./src/clone/serialize.h:829:71: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
                                                                      ^
./src/clone/serialize.h:835:67: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
                                                                  ^
./src/clone/serialize.h:869:28: warning: unused parameter 'psz'
      [-Wunused-parameter]
    void write(const char *psz, size_t _nSize)
                           ^
./src/clone/serialize.h:896:28: warning: unused parameter 's'
      [-Wunused-parameter]
void SerializeMany(Stream& s)
                           ^
./src/clone/serialize.h:914:37: warning: unused parameter 's'
      [-Wunused-parameter]
inline void UnserializeMany(Stream& s)
                                    ^
./src/clone/serialize.h:932:61: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, ...
                                                            ^
./src/clone/serialize.h:938:63: warning: unused parameter 'ser_action'
      [-Wunused-parameter]
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action...
                                                              ^
In file included from src/consensus/consensus.cpp:30:
In file included from ./src/clone/primitives/transaction.h:11:
./src/clone/script/script.h:480:40: warning: unused parameter 'b'
      [-Wunused-parameter]
    CScript& operator<<(const CScript& b)
                                       ^
In file included from src/consensus/consensus.cpp:32:
./src/clone/script/interpreter.h:131:61: warning: unused parameter 'scriptSig'
      [-Wunused-parameter]
    virtual bool CheckSig(const std::vector<unsigned char>& scriptSig, c...
                                                            ^
./src/clone/script/interpreter.h:131:106: warning: unused parameter 'vchPubKey'
      [-Wunused-parameter]
  ...char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CSc...
                                                         ^
./src/clone/script/interpreter.h:131:132: warning: unused parameter 'scriptCode'
      [-Wunused-parameter]
  ...const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, ...
                                                                 ^
./src/clone/script/interpreter.h:131:155: warning: unused parameter 'sigversion'
      [-Wunused-parameter]
  ...char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
                                                             ^
./src/clone/script/interpreter.h:136:50: warning: unused parameter 'nLockTime'
      [-Wunused-parameter]
    virtual bool CheckLockTime(const CScriptNum& nLockTime) const
                                                 ^
./src/clone/script/interpreter.h:141:50: warning: unused parameter 'nSequence'
      [-Wunused-parameter]
    virtual bool CheckSequence(const CScriptNum& nSequence) const
                                                 ^
15 warnings generated.
  CXXLD    src/libbitcoin-consensus.la

libsecp256k1/version4 dependency missing

  1. Where is version4 referenced by https://github.com/libbitcoin/libbitcoin-consensus/blob/master/install.sh#L527?
  2. The following fails for libbitcoin-server:
./install.sh --disable-shared --prefix=/Users/XXX/Projects/bitcoin-server/test --build-dir=/Users/XXX/Projects/bitcoin-server/build --build-boost
  ...
  echo timestamp > classnoinst.stamp
  CXX      src/clone/src_libbitcoin_consensus_la-hash.lo
  CXX      src/clone/src_libbitcoin_consensus_la-pubkey.lo
  CXX      src/clone/src_libbitcoin_consensus_la-uint256.lo
  CXX      src/clone/src_libbitcoin_consensus_la-utilstrencodings.lo
  CXX      src/clone/crypto/src_libbitcoin_consensus_la-hmac_sha512.lo
  CXX      src/clone/crypto/src_libbitcoin_consensus_la-ripemd160.lo
  CXX      src/clone/crypto/src_libbitcoin_consensus_la-sha1.lo
  CXX      src/clone/crypto/src_libbitcoin_consensus_la-sha256.lo
  CXX      src/clone/crypto/src_libbitcoin_consensus_la-sha512.lo
src/clone/pubkey.cpp:8:10: fatal error: 'secp256k1_recovery.h' file not found
#include <secp256k1_recovery.h>
         ^
1 error generated.
make: *** [src/clone/src_libbitcoin_consensus_la-pubkey.lo] Error 1
make: *** Waiting for unfinished jobs....

Optimize secp256k1 integration

The --with-secp256k1 option is not yet optimized for production use within libbitcoin-consensus. Because static state is reinitialized on each signature verification, performance lags --with-secp256k1 by about 10x. Libbitcoin's native consensus checks use libsecp256k1 (optimized) and achieve about 2x performance over libbitcoin-consensus --without-secp256k1 and about 20x over libbitcoin-consensus --with-secp256k1.

The static initialization can be performed in the wrapper files (outside of the consensus files).

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.