bitboxswiss / bitbox02-firmware Goto Github PK
View Code? Open in Web Editor NEWFirmware code of the BitBox02 hardware wallet
Home Page: https://bitbox.swiss/bitbox02
License: Apache License 2.0
Firmware code of the BitBox02 hardware wallet
Home Page: https://bitbox.swiss/bitbox02
License: Apache License 2.0
I saw this PR: #472 and remembered that I've noticed that githubs appid changed. Not sure exactly when. IIRC it is just sha256('github.com')
now.
Connect BIP39 passphrases and wallet pins, let you define multiple complex BIP39 passphrases, each connected to an easy to remember pin.
There would be no hint if you are using a passphrase or not on the device, but depending on the entered pin you are using different wallets. This way you would ease the pain of entering complex passphrases every time you are using the wallet.
These make it harder to spot important messages:
src/ui/ugui/ugui.c: In function 'UG_MeasureStringCentered':
src/ui/ugui/ugui.c:622:45: warning: field precision specifier '.*' expects argument of type 'int', but argument 4 has
type 'long int' [-Wformat=]
snprintf(line, sizeof(line), "%.*s", c - start, start);
~~^~ ~~~~~~~~~
src/ui/ugui/ugui.c:630:37: warning: field precision specifier '.*' expects argument of type 'int', but argument 4 has
type 'long int' [-Wformat=]
snprintf(line, sizeof(line), "%.*s", c - start, start);
~~^~ ~~~~~~~~~
src/ui/ugui/ugui.c: In function 'UG_PutStringCentered':
src/ui/ugui/ugui.c:763:75: warning: field precision specifier '.*' expects argument of type 'int', but argument 4 has
type 'long int' [-Wformat=]
snprintf(lines[current_line], sizeof(lines[current_line]), "%.*s", c - start, start);
~~^~ ~~~~~~~~~
src/ui/ugui/ugui.c:768:67: warning: field precision specifier '.*' expects argument of type 'int', but argument 4 has
type 'long int' [-Wformat=]
snprintf(lines[current_line], sizeof(lines[current_line]), "%.*s", c - start, start);
~~^~ ~~~~~~~~~
src/ui/components/confirm.c: In function 'confirm_create':
src/ui/components/confirm.c:108:58: warning: format '%u' expects argument of type 'unsigned int', but argument 4 has
type 'size_t' {aka 'const long unsigned int'} [-Wformat=]
snprintf(size_label, sizeof(size_label), "Size: %uB", params->display_size);
~^ ~~~~~~~~~~~~~~~~~~~~
%lu
src/apps/eth/eth_sighash.c: In function 'app_eth_sighash':
src/apps/eth/eth_sighash.c:117:5: warning: nested extern declaration of 'rhash_keccak_final' [-Wnested-externs]
rhash_keccak_final(&ctx, sighash_out);
^~~~~~~~~~~~~~~~~~
test/unit-test/test_memory_functional.c: In function '_test_memory_multisig_full':
test/unit-test/test_memory_functional.c:113:53: warning: format '%ld' expects argument of type 'long int', but argume
nt 4 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
snprintf(names[i], sizeof(names[i]), "name%ld", i);
~~^ ~
%ld
so currently the building process is really not made for windows so maybe it should someday be made a bit better.
I basically just downloaded the bitbox02-firmware-master folder to my D drive and installed docker and ran the docker commands manually and hacked them to make them work in a way with my limited knowledge.
docker build --pull --force-rm --no-cache -t shiftcrypto/firmware_v2 .
docker run --detach --interactive --tty --name="bb02-dev" -v "$repo_path":"D:\bitbox02-firmware-master\" --cap-add SYS_PTRACE shiftcrypto/firmware_v2 bash
docker exec -it "bb02-dev" bash
once I am inside the docker, the mounting didnt work for whatever reason so I needed to git clone yet again but I got a build with the proper hash.
there are a few problems tho.
Hey,
I setup my new bitbox. After setting it up, I unplugged and replugged it.
I entered the password to unlock the device and got the following error:
Error: panicked at `keystore unlock failed`, src/bitbox02-rust/src/workflow/unlock.rs:87:14`
After I unplugged and tried again, it worked. Could not reproduce it a 2nd time.
Do I have to be worried? Any idea why this happened?
Today it's possible to use all features of BitBox even when the SD card is in the device.
The BitBox app does show a warning, but the device itself does not restrict its functionality in that case.
It would be better to block critical functionalities, like signing, if an SD card (with backup) is detected.
Especially beneficial for users who use alternative wallet software like Sparrow or Electrum.
https://github.com/digitalbitbox/bitbox02-firmware/releases lists binaries and hashes but I couldn't find a match.
So the device checks firmware hashes prior to installation? It should ask something like "Do you want to install the validly signed firmware update version X with hash Y?" so I can then check if that's really what I want, right? So regardless where the companion app pulled the update, if I pulled it from above link and verified both hash and reproducibility, I could approve the known hash but I only see a "random" hash. Where is the missing link?
The question is for my analysis of this wallet for walletscrutiny. My change currently would loo like this.
Hello, Electrum Wallet does not recognize BitBox02.
It is working fine with other hardware wallets.
Not sure what "udev rules" are needed for BB02.
https://electrum.readthedocs.io/en/latest/hardware-linux.html
Thanks.
Would be nice to see.
https://old.reddit.com/r/chia/comments/pfpjlx/hardware_wallet_support/
Currently not supported by any hardware wallet as it needs BLS support.
https://en.wikipedia.org/wiki/BLS_digital_signature
So might be hard to bring to BitBox02
Hi BitBox Team,
Are there plans to work with Blocknative in order for them to add support for the BitBox SDK in the Onboard.js library?
Once completed, the Gnosis team stated the BitBox will be a great option as a cosigner in a Gnosis Safe wallet because it will be able to verify the safeTxHash
along with the Trezor, Ledger, and GridPlus hardware wallets.
Much appreciated!
Trezor now ships with support for SLIP-0039 : Shamir's Secret-Sharing for Mnemonic Codes. It would be great if the BitBox02 would also add support for this emerging standard to prevent vendor lock-in and give users an easy way to restore their wallet on either device.
Hi,
Where can I please find the source design of BitBox 02?
Is it open source hardware licensed under Creative Common International 4.0?
i was just prompted to update the bitbbox02 firmware and did so. However, now MyEtherWallet cannot connect to the bitbox anymore saying that MyEtherWallet does not yet support this version of the firmware
.
This is fairly critical as MEW seems to be currently the only option to use the bitbox2 with Ethereum (other than sending simple ETH txs through the bitbox app which is not sufficient for most cases such as interfacing multisig wallets).
The error details from MEW below:
{
"exception": {
"values": [
{
"type": "Error",
"value": "Device or websocket not connected",
"stacktrace": {
"frames": [
{
"colno": 33691,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "WebSocket.s.socket.onmessage",
"in_app": true,
"lineno": 57
},
{
"colno": 81175,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "Object.e.$externalizeWrapper.e.$externalizeWrapper [as OnRead]",
"in_app": true,
"lineno": 161
},
{
"colno": 87974,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "?",
"in_app": true,
"lineno": 161
},
{
"colno": 81175,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "Object.e.$externalizeWrapper.e.$externalizeWrapper",
"in_app": true,
"lineno": 161
},
{
"colno": 3831439,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "Object.D.ptr.OnRead",
"in_app": true,
"lineno": 161
},
{
"colno": 77880,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "Lt",
"in_app": true,
"lineno": 161
},
{
"colno": 78153,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "?",
"in_app": true,
"lineno": 161
},
{
"colno": 77411,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "Rt",
"in_app": true,
"lineno": 161
},
{
"colno": 77303,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "Ot",
"in_app": true,
"lineno": 161
},
{
"colno": 76879,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "r",
"in_app": true,
"lineno": 161
},
{
"colno": 76946,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "e",
"in_app": true,
"lineno": 161
},
{
"colno": 3831927,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "Object.n [as $blk]",
"in_app": true,
"lineno": 161
},
{
"colno": 3768713,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "Object.U.ptr.Init [as $blk]",
"in_app": true,
"lineno": 161
},
{
"colno": 3770110,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "Object.U.ptr.changeStatus",
"in_app": true,
"lineno": 161
},
{
"colno": 3779858,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "Object.U.ptr.fireEvent",
"in_app": true,
"lineno": 161
},
{
"colno": 82500,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "?",
"in_app": true,
"lineno": 161
},
{
"colno": 33128,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "?",
"in_app": true,
"lineno": 57
},
{
"colno": 35527,
"filename": "https://www.myetherwallet.com/js/vendors.307df816.js",
"function": "c.firmware",
"in_app": true,
"lineno": 57
}
]
},
"mechanism": {
"handled": false,
"type": "onerror"
}
}
]
},
"platform": "javascript",
"sdk": {
"name": "sentry.javascript.browser",
"packages": [
{
"name": "npm:@sentry/browser",
"version": "5.15.5"
}
],
"version": "5.15.5",
"integrations": [
"InboundFilters",
"FunctionToString",
"TryCatch",
"Breadcrumbs",
"GlobalHandlers",
"LinkedErrors",
"UserAgent",
"Vue"
]
},
"environment": "web",
"release": "5.6.2",
"event_id": "e8304f1673534d9b84a9a38f20b6887c",
"request": {
"url": "https://www.myetherwallet.com/access-my-wallet",
"headers": {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"
}
},
"tags": {
"network": "",
"service": "",
"walletType": ""
}
}
It would be a useful feature to store PGP private keys securely on the device.
https://wiki.gnupg.org/SmartCard
It seems that electrum supports multiple bitboxes plugged in at the same time. Is this also possible through the python library?
Currently, this message suggests that it is not supported:
It would be great if the firmware would support BIP85.
BIP85 basically allows to derive other seeds from the master seed, which would allow to create multiple seeds for other wallets (e.g. hot wallets) and have them all backed up automatically with the seed of the BitBox.
Touching wildly around, especially top center and right together with bottom right
might break the UI and characters disappear.
might be related to #92 ?
To reproduce:
The error also occurs when going back to the "Manage devices" screen and changing the microSD there.
Workaround: Remove microSD & Restart of the Bitbox02 App. Restating with microSD inserted will continue to show the error.
As a user of multiple BitBox hardware wallets, I would like to see the name of the BitBox on the lock-screen so that I can easily identify the device before I enter the password.
Reason: every BitBox looks the same. Stickers are currently the only way to put a label that is visible before unlocking.
But stickers may peel off.
As a user of multiple Bitboxes, I run the risk of mixing up the devices and entering incorrect passwords.
An optional display of the device name in the lock-screen would solve the problem.
Hello, I wanted to use bitbox02 as a second device to use in case of emergency with the same BIP39 seeds of an other wallet, however since I have a 25th word containing ascii extended characters (such as !@#$%^&*(){}:;"'.,?/\ ..) I could not do that. Would be possible to add these characters to the firmware?
When restoring a wallet from mnemonic, I currently have only one attempt for each word. If I enter the wrong word, the entire process has to be aborted and repeated which is a painful process, especially for 24word mnemonics.
I suggest to allow users to review and edit previously entered words.
UX suggestion on Bitbox 2 screen:
Currently, after completing a word, there is a back
button in the top left corner of the screen:
<
Enter 6th word
abcd ijkl qrstu
efgh mnop vwxyz
That button takes us to the Do you really want to cancel
screen. I suggest to replace that screen and instead show:
replace word
on the bottom< >
5th word:
zoo
replace word
Then currently existing Do you really want to cancel
screen could be shown when tapping again on the <
button (top left) once the first word has been reached.
nuff said really. Ada is awesome.
Wrong repo
Upgraded to firmware v3.0.0 (beta device) and tried the new input method like in https://twitter.com/ShiftCryptoHQ/status/1162301973347147776. I can't always reproduce it reliably but here it is:
Note that my fingers aren't touching the device at all in the photo anymore.
Feels like a race condition somewhere in the UI code.
The wordlist is designed in a way that all words are unique after 4 letters (in fact several backup solutions including the Steelwallet only support entering 4 letters), so similar to on a keepkey you could just show the complete word instead for asking the extra letters.
while there are some words that are already unique on the third letter if that involves too much complexity one can ignore that.
deletion was brought up on telegram, one could for example check if the word currently has 4 or more letters to just cut to 3, and then proceed normally with deleting.
A simulator similar to the one added to the Bitbox 1 would be nice for people integrating Bitbox 2 into their own software in order to do CI.
when running make dockerdev
, which invokes scripts/dockerenv.sh
as root via sudo or otherwise (because docker needs root and using the docker group does not seem to be a recommended option), when not having a running container already (if stopped, it will just ax the current and then create a new one) the command will fail when creating the docker user and group for the reason that at that point both the uid and gid are 0 for root and the created docker already has a root user.
also if calling make dockerdev
after such a failure with the user/group creation it will fail again because it couldn't create the docker user it wants to jump into.
fixing this should not be TOO much of an issue:
when running make via sudo 2 extra variables will be created. $SUDO_UID
and $SUDO_GID
. if these are set one could just take these to replace the $(id -u)
and $(id -g)
, and if docker is run as a pure root, we either need a different solution or ask to at least use it via a normal user through sudo or whatever
This came up in #451:
// No other message than the attestation and unlock calls shall pass until the device is
// unlocked or ready to be initialized.
if bitbox02::memory::is_initialized() && bitbox02::keystore::is_locked() {
return Vec::new();
}
Instead of returning an empty slice, OP_STATUS_FAILURE
or something else could be returned to make it more consistent?
currently there are several confirm actions where some have the claw and some just have a tap on top-right, where some uniformity can help.
These are the actions I found so far (excluding actions that go to the next screen for more)
Tap | Claw |
---|---|
U2F Confirm | transaction confirm |
RNG Confirm | Password submit |
Recieve Address Confirm | Weak Password confirm |
Pairing Code | Enable/disable Passphrase |
Verifiy mSD Backup | Passphase submit |
Set Name | Reset |
according to the TG chat the claw should mainly be used on important actions that cannot be reversed like signing a transaction, which does make sense, but then I think at least Pairing Code and U2F should be the claw, as signatures like on U2F literally cause non-repudation (not being able to dispute it later) and the Pairing code is an important security ELement for the encrypted communication channel(s).
It would be great if the backup to the microSD card could be encrypted with a user defined password to prevent giving attackers full access to funds in case they get hold of the SD card (and no BIP39 passphrase is used).
It probably makes more sense to type the password on the PC doing the backup to allow the usage of a password manager to enforce a strong password instead of entering it on the BitBox02 itself. If we assume that the PC is compromised, the attacker could steal the password, but if they did not get a copy of the SD card before (which seems quite unlikely), they can not do anything with the stolen password, since the SD card is inside the BitBox and not on the pc itself, so I'd say the risks are low compared to the gained UX (entering a 20 char password on the BitBox is a bit tedious). However, if you decide otherwise, entering on the BitBox itself would also be ok I guess, but will most likely result in weaker passwords.
Proposed workflow:
When verifying or restoring a backup, the BitBox should detect that a password is required and prompt for it in the app / on the screen, then decrypt the backup and verify / restore as before.
With BB02, when adding a Multisig scheme on the device the firmware limits me to AZaz09 inputs (HWI wallets, like Sparrow). Could you pls expand that to allow special characters and the space ?
For example if I "display address" in Electrum which doesn't use HWI, Electrum triggers a dialog box on the host computer, where I can name the BB02 Multisig anything by typing in into the host computer keyboard, such as :
[A] 1 of 2 kraken (0)
(B) 3 of 5 Cold Wallet
Thanks
Hello,
Is there a way to store the seed generated with a passphrase without then having to insert the passphrase every time the device is turned on?
thank you
I noticed that there was an eth_sign
in the provided python library, but no btc_sign
. Could a btc_sign
function be provided?
If entering the password in a public setting, it is almost impossible not to leak it. The discreet left-middle-right clicks are very easily detected at a great distance.
Although it makes input even harder, the three groups should get shown in random order.
(Should my proposal #743 get implemented, then the alphabet could start looping at a random pace until click occurs. In this mode, the initial click may be very imprecise.)
The text input is understandably very cumbersome but I think a dasher style input could work very intuitively and less disorienting.
Currently, to type a "t" I have to type right-left-right but it's not initially clear that the second click goes left and the third right after having found "t" on the initial screen. The user can't follow those quick slides of the letters, at least not in the first transition from full ABC. With the proposed change, he would
The layout should get a slight change that the current input method already would benefit from:
Instead of
a b c d
e f g h
show
a c e g
b d f h
The current easing from the two-line layout to the "a b c d e f g h" comes with letters running other letters over wildly which makes it harder to follow for the eye. "e" is for example covering the "|" of the "d" for some frames.
The new easing could delay the first line sinking into the second line, avoiding all overlap. After all, the full alphabet almost fits in one line already.
As this would be a highly interactive input mode, it would not be for everybody, especially not all would agree on one optimal speed but if a short touch would by default do a certain zoom, users would get to the wanted letter with 5 taps almost the old way, faster and with more confidence than with the current 3 taps at discreet positions.
As a user of multi-sig setups with BitBox hardware wallets, I would like to back up the multi-sig configurations (name, xpubs, etc) together with the seed on SD card so that I can restore the full bitbox configuation easily.
The BitBox already provides good support for multi-sig setups, but sadly the sd card backup does not include the data for the multi-sig wallets.
Even though documentation and app state that the backup only contains the seed, some users may expect the backup to contain all data from the BitBox.
Acceptance criteria:
In contrast to competitors, the Bitbox2 needs actions on the app in order to restore a wallet. I do not feel good about having my laptop around while entering my mnemonic into the Bitbox2 - tell me that's ridiculous but that's how I feel :-/
Instead, I'd be happy if I could just connect my Bitbox2 to whatever USB-C power-supply (that I trust more than my laptop) and initiate the restore process just through the Bitbox interface without needing the app.
Suggested UI (on Bitbox2 screen) - if Bitbox2 is in factory reset mode:
1. create wallet
2. restore mnemonic
3. restore SD
(or see app on computer)
These 3 options could be selected by tapping on three corners of the screen or by sliding through a dropdown-like list. In each case, the same process that is currently being used when selecting the corresponding action on the app could be triggered here. Option 3 might be a bit more tricky as the different backups from the SD card need to be rendered - but I'm mostly interested in Option 2 anyway ;-)
Adds an informational meta-file about Bitbox02's features to its public GitHub repo for auto-import into WalletMatrix.
WalletMatrix is a Bitcoin wallet feature discovery tool for folks to find wallets by feature, language and platform alongside contextual help content, a developer API and heaps more. There are several similar services which rely on manual and centralised data curation, but since wallet projects iterate so quickly, WalletMatrix automates an otherwise unsustainable update process.
Merging this PR exposes Bitbox02's features via a standard matrix.json
file which allows WalletMatrix to automatically parse, consume and store Bitbox02's features, making them available to search almost immediately.
What do I need to do?
matrix.json
up-to-date :-)Resources
matrix.json
is currently based on our v3 JSON schemamatrix.json
file hereuuid
APIQuestions?
wrong repo
https://coldcardwallet.com/docs/passphrase#next-steps
this could be really helpful for people to make sure they havent screwed up their passphrase
Reading through the code and README here it doesn't look like the bitbox02 supports restoring from a seed other than through the SD card. Would be nice to be able to round-trip a physical (written, engraved, stamped, etc.) seed.
Bonus points for checking (probably with a warning, not failure) the BIP39 checksum.
I wanted to build the docker container to reproduce today's release build, but it failed at installing protoc-gen-doc:
Removing intermediate container 1212580db3af
The command '/bin/sh -c go get -v -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc' returned a non-zero code: 2
I investigated further and ran:
docker exec -ti bitbox02-firmware-dev go get -v -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc
Which in turn showed me the actual error:
opt/go/src/google.golang.org/protobuf/proto/proto_methods.go:18:23: cannot use m.ProtoMethods() (type *protoreflect.Message) as type *struct { pragma.NoUnkeyedLiterals; Flags uint64; Size func(struct { pragma.NoUnkeyedLiterals; Message protoreflect.Message; Flags uint8 }) struct { pragma.NoUnkeyedLiterals; Size int }; Marshal func(struct { pragma.NoUnkeyedLiterals; Message protoreflect.Message; Buf []byte; Flags uint8 }) (struct { pragma.NoUnkeyedLiterals; Buf []byte }, error); Unmarshal func(struct { pragma.NoUnkeyedLiterals; Message protoreflect.Message; Buf []byte; Flags uint8; Resolver interface { FindExtensionByName(protoreflect.FullName) (protoreflect.ExtensionType, error); FindExtensionByNumber(protoreflect.FullName, protowire.Number) (protoreflect.ExtensionType, error) } }) (struct { pragma.NoUnkeyedLiterals; Flags uint8 }, error); Merge func(struct { pragma.NoUnkeyedLiterals; Source protoreflect.Message; Destination protoreflect.Message }) struct { pragma.NoUnkeyedLiterals; Flags uint8 }; CheckInitialized func(struct { pragma.NoUnkeyedLiterals; Message protoreflect.Message }) (struct { pragma.NoUnkeyedLiterals }, error) } in return argument
After reading through this: golang/protobuf#1094 I am thinking that the problem is the go version. Should the Dockerfile be updated to 1.13?
Edit:
I tested building with a bumped go version and that indeed did the trick.
Would it be possible to get exact housing dimensions to design 3D-printable cases for the BitBox02?
following this: https://shiftcrypto.support/help/en-us/20-24-recovery-words/48-how-to-restore-an-externally-generated-wallet-on-a-bitbox02
soooo... what? I restart the whole dance again, only to be shown an incorrect date again? i mean.... what's the point? there is a point right?
BitBoxApp and Python libs enter utf8 strings. On the device, unicode chars are then not rendered properly.
The client libs should only accept printable ascii chars for names (and space?).
The firmware should also check that the name string is only printable ascii chars.
Hi,
Do you consider supporting Bitcoin Cash and other bitcoin forks?
Thanks a lot,
Steve
Is there any support for signing a message with a Bitcoin address? If not, what's the rationale?
the current time (converted to a UTC Unixtime) will be used to set the U2F counter in a way that even on a backup the counter wouldnt interfere (provided 2 devices arent used at the same time), however this could get weird when the displayed "local" date/time on the PC matches but the timezone doesnt and a U2F counter could be influenced solely by timezone of around 93,6k by the pure time difference of the timezones (not sure how this goes when you put the date line into the calculation)
if the relying party (website) uses the counter very strictly, this could lead to unintended or malicious lockouts, which would be very annoying.
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.