{"id":13561982,"url":"https://github.com/karpathy/cryptos","last_synced_at":"2025-05-16T05:07:38.343Z","repository":{"id":38000715,"uuid":"361299026","full_name":"karpathy/cryptos","owner":"karpathy","description":"Pure Python from-scratch zero-dependency implementation of Bitcoin for educational purposes","archived":false,"fork":false,"pushed_at":"2021-06-21T04:53:12.000Z","size":212,"stargazers_count":1725,"open_issues_count":3,"forks_count":295,"subscribers_count":37,"default_branch":"main","last_synced_at":"2025-05-09T07:06:30.621Z","etag":null,"topics":["bitcoin","crypto","cryptocurrency","elliptic-curves"],"latest_commit_sha":null,"homepage":"","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karpathy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-04-25T00:56:19.000Z","updated_at":"2025-05-09T02:42:54.000Z","dependencies_parsed_at":"2022-07-12T15:20:35.220Z","dependency_job_id":null,"html_url":"https://github.com/karpathy/cryptos","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karpathy%2Fcryptos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karpathy%2Fcryptos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karpathy%2Fcryptos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karpathy%2Fcryptos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karpathy","download_url":"https://codeload.github.com/karpathy/cryptos/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254471060,"owners_count":22076585,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitcoin","crypto","cryptocurrency","elliptic-curves"],"created_at":"2024-08-01T13:01:03.291Z","updated_at":"2025-05-16T05:07:33.333Z","avatar_url":"https://github.com/karpathy.png","language":"Jupyter Notebook","readme":"\n# cryptos\n\nJust me developing a pure Python from-scratch zero-dependency implementation of Bitcoin for educational purposes, including all of the under the hood crypto primitives such as SHA-256 and elliptic curves over finite fields math.\n\n### SHA-256\n\nMy pure Python SHA-256 implementation closely following the [NIST FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf) spec, in `cryptos/sha256.py`. Since this is a from scratch pure Python implementation it is slow and obviously not to be used anywhere except for educational purposes. Example usage:\n\n```bash\n$ echo \"some test file lol\" \u003e testfile.txt\n$ shasum -a 256 testfile.txt\n4a79aed64097a0cd9e87f1e88e9ad771ddb5c5d762b3c3bbf02adf3112d5d375\n$ python -m cryptos.sha256 testfile.txt\n4a79aed64097a0cd9e87f1e88e9ad771ddb5c5d762b3c3bbf02adf3112d5d375\n```\n\n### Keys\n\n`getnewaddress.py` is a cli entryway to generate a new Bitcoin secret/public key pair and the corresponding (base58check compressed) address:\n\n```bash\n$ python getnewaddress.py\ngenerated secret key:\n0xc322622e6a0033bb93ff666753f77cc8b819d274d9edea007b7e4b2af4caf025\ncorresponding public key:\nx: 5B9D87FE091D52EA4CD49EA5CEFDD8C099DF7E6CCF510A9A94C763DE38C575D5\ny: 6049637B3683076C5568EC723CF7D38FD603B88447180829BBB508C554EEA413\ncompressed bitcoin address (b58check format):\n1DBGfUXnwTS2PRu8h3JefU9uYwYnyaTd2z\n```\n\n### Digital Signatures\n\nElliptic Curve Digital Signature Algorithm (ECDSA) implemented in `cryptos/ecdsa.py`, example usage:\n\n```python\n\u003e\u003e\u003e from cryptos.keys import gen_key_pair\n\u003e\u003e\u003e from cryptos.ecdsa import sign, verify\n\u003e\u003e\u003e sk1, pk1 = gen_key_pair()\n\u003e\u003e\u003e sk2, pk2 = gen_key_pair()\n\u003e\u003e\u003e message = ('pk1 wants to pay pk2 1 BTC').encode('ascii')\n\u003e\u003e\u003e sig = sign(sk1, message)\n\u003e\u003e\u003e verify(pk1, message, sig)\nTrue\n\u003e\u003e\u003e verify(pk2, message, sig)\nFalse\n```\n\n### Transactions\n\nBitcoin transaction objects (both legacy or segwit) can be instantiated and parsed from raw bytes. An example of parsing a legacy type transaction:\n\n```python\n\u003e\u003e\u003e from cryptos.transaction import Tx\n\u003e\u003e\u003e from io import BytesIO\n\n\u003e\u003e\u003e # example transaction in Programming Bitcoing Chapter 5\n\u003e\u003e\u003e raw = bytes.fromhex('0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000006b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278afeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600')\n\u003e\u003e\u003e tx = Tx.parse(BytesIO(raw))\n\u003e\u003e\u003e # we get back a Transaction object with parsed fields\n\u003e\u003e\u003e tx\n\nTx(version=1, tx_ins=[TxIn(prev_tx=b'\\xd1\\xc7\\x89\\xa9\\xc6\\x03\\x83\\xbfq_?j\\xd9\\xd1K\\x91\\xfeU\\xf3\\xde\\xb3i\\xfe]\\x92\\x80\\xcb\\x1a\\x01y?\\x81', prev_index=0, script_sig=3045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01 0349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a, sequence=4294967294, witness=None)], tx_outs=[TxOut(amount=32454049, script_pubkey=OP_DUP OP_HASH160 bc3b654dca7e56b04dca18f2566cdaf02e8d9ada OP_EQUALVERIFY OP_CHECKSIG), TxOut(amount=10011545, script_pubkey=OP_DUP OP_HASH160 1c4bc762dd5423e332166702cb75f40df79fea12 OP_EQUALVERIFY OP_CHECKSIG)], locktime=410393, segwit=False)\n```\n\nAnd we can verify that the transaction is Bitcoin law-abiding and cryptographically authentic:\n\n```python\n\u003e\u003e\u003e tx.validate()\nTrue\n```\n\nThis isn't exactly a complete verification as a Bitcoin full node would do and e.g. skips verification of double spends, script sizing limits, etc., and also it only supports the (simpler) p2pkh transactions. Notably, this does not include the \"modern\" segwit versions used predominantly in today's Bitcoin traffic since the soft fork of BIP141 around July 2017.\n\n### Blocks\n\nSee `cryptos/block.py` for Block class, functions and utilities.\n\n### Lightweight Node\n\nA lightweight Bitcoin Node that speaks a subset of the [Bitcoin protocol](https://en.bitcoin.it/wiki/Protocol_documentation) is in `cryptos/network.py`. This node connects to other nodes using Python's `socket`, performs version handshake and then can request block headers. E.g. we can walk the first 40,000 blocks (in batches of 2,000) and partially validate them. A Bitcoin full node would fetch the full block (not just headers) with all transactions and also validate those, etc. But a partial validation would look like:\n\n```python\n\nfrom io import BytesIO\nfrom cryptos.block import Block, GENESIS_BLOCK, calculate_new_bits\nfrom cryptos.network import SimpleNode\nfrom cryptos.network import (\n    GetHeadersMessage,\n    HeadersMessage,\n)\n\n# connect to a node and pretty please ask for\n# 20 block headers starting with the genesis block\n\n# Start with the genesis block\n# https://en.bitcoin.it/wiki/Genesis_block\n# class Block:\n#     version: int        # 4 bytes little endian\n#     prev_block: bytes   # 32 bytes, little endian\n#     merkle_root: bytes  # 32 bytes, little endian\n#     timestamp: int      # uint32, seconds since 1970-01-01T00:00 UTC\n#     bits: bytes         # 4 bytes, current target in compact format\n#     nonce: bytes        # 4 bytes, searched over in pow\nprevious = Block.decode(BytesIO(GENESIS_BLOCK['main']))\n\n# okay now let's crawl the blockchain block headers\nnode = SimpleNode(\n    host='mainnet.programmingbitcoin.com',\n    net='main',\n)\nnode.handshake()\n\nblocks = [previous]\nfor _ in range(20):\n\n    # request next batch of 2,000 headers\n    getheaders = GetHeadersMessage(start_block=bytes.fromhex(previous.id()))\n    node.send(getheaders)\n    headers = node.wait_for(HeadersMessage)\n\n    # extend our chain of block headers\n    blocks.extend(headers.blocks)\n\n    previous = headers.blocks[-1]\n    print(f\"received another batch of blocks, now have {len(blocks)}\")\n\nnode.close()\n# we now have 40,001 blocks total, 80 bytes each in raw, so total of ~3.2MB of data\n\n# now (partially) validate blockchain integrity\nfor i, block in enumerate(blocks):\n\n    # validate proof of work on this block\n    assert block.validate()\n\n    # validate pointer to the previous node matches\n    prev = blocks[i - 1]\n    expected_prev_block = b'\\x00'*32 if i == 0 else bytes.fromhex(prev.id())\n    assert block.prev_block == expected_prev_block\n\n    # validate the proof of work target calculation on the block was correct\n    if i % 2016 == 0:\n        if i == 0:\n            # genesis block had hardcoded value for bits\n            expected_bits = bytes.fromhex('ffff001d')\n        else:\n            # recalculate the target at every epoch (2016 blocks), approx 2 week period\n            # note that Satoshi had an off-by-one bug in this calculation because we are\n            # looking at timestamp difference between first and last block in an epoch,\n            # so these are only 2015 blocks apart instead of 2016 blocks apart ¯\\_(ツ)_/¯\n            prev_epoch = blocks[i - 2016]\n            time_diff = prev.timestamp - prev_epoch.timestamp\n            expected_bits = calculate_new_bits(prev.bits, time_diff)\n    assert block.bits == expected_bits\n\n    if i % 1000 == 0:\n        print(f\"on block {i+1}/{len(blocks)}\")\n```\n\nIt feels very nice to independently at least partially verify the integrity of the block chain :)\n\n### Unit tests\n\n```bash\n$ pytest\n```\n\n### License\nMIT\n","funding_links":[],"categories":["Jupyter Notebook"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarpathy%2Fcryptos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarpathy%2Fcryptos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarpathy%2Fcryptos/lists"}