Skip to content

krutt/thnbr

Repository files navigation

thnbr

Bitcoin-only License: MIT Top Languages Size Last commit

Thonburi banner

Stack

Project structure
thnbr/
├── LICENSE                              # MIT License permission outline
├── README.md                            # Project setup and test setup walkthrough
├── index.html                           # Hypertext template where application is embedded
├── package.json                         # Project definitions and dependencies 
├── src/
│   ├── App.vue                          # Vue single-page application base view
│   ├── assets/                          # Collection of stylesheets and vector assets
│   │   └── ...                          # TODO: expand descriptions
│   │
│   ├── components/                      # Collection of Vue frontend components
│   │   └── ...                          # TODO: expand descriptions
│   │
│   ├── main.ts                          # Vue single-page application entrypoint
│   ├── stores/                          # Collection of Vue composable stores
│   │   └── ...                          # TODO: expand descriptions
│   │
│   ├── style.css                        # Vue single-page application cascade styling sheet
│   ├── views/                           # Collection of Vue frontend pages
│   │   └── ...                          # TODO: expand descriptions
│   │
│   └── workers/                         # Collection of web workers
│       └── interpreter.ts               # Pyodide interpreter with required shims
│
├── static/                              # Static assets
│   ├── coi-serviceworker.min.js         # Cross-origin isolation through a service worker
│   ├── chrome.svg                       # Vector asset of Chrome web browser logo
│   ├── favicon.ico                      # Small graphic image for the browser's address bar
│   ├── firefox.svg                      # Vector asset of Firefox logo used by README.md
│   ├── pymatt-0.0.1-py3-none-any.whl    # Wheel file for `pymatt` python package
│   ├── pyodide.asm.js                   # The JS half of the main "binary"
│   ├── pyodide.asm.wasm                 # The WebAssembly half of the main "binary"
│   ├── pyodide.js                       # Legacy support for browsers unable to use es6 module
│   ├── pyodide.mjs                      # Small JS loader shim which exports `loadPyodide`
│   ├── pyodide-lock.json                # Lockfile for Python packages
│   ├── python_stdlib.zip                # Python standard libraries for Pyodide
│   ├── thnbr.svg                        # Project logo
│   ├── thnbr-banner.svg                 # Project banner
│   └── thnbr-social.svg                 # Project social link preview
│
├── tailwind.config.js                   # Styling plugins and rules for TailwindCSS
└── vite.config.ts                       # Bundler configuration and static asset bypass

Notable exemptions: dotfiles, lockfile and component library generated files

Contributions

Prerequisites

  • node - Run JavaScript Everywhere
  • pnpm - Fast, disk space efficient package manager
  • Web browser of choice (Chrome or Firefox)

Getting started

Install the project dependencies with the following command:

pnpm install
Sample output of successful dependency installation
$ pnpm install
> Lockfile is up to date, resolution step is skipped
> Packages: +263
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> Downloading @biomejs/cli-darwin-x64@2.2.2: 14.67 MB/14.67 MB, done
> Progress: resolved 263, reused 260, downloaded 3, added 263, done
>
> dependencies:
> + @codemirror/commands 6.8.1
> + @codemirror/lang-python 6.2.1
> + @codemirror/language 6.11.1
> + @codemirror/state 6.5.2
> + @codemirror/view 6.37.2
> + @lezer/common 1.2.3
> + @lezer/highlight 1.2.1
> + @noble/hashes 1.8.0
> + @tanstack/vue-table 8.21.3
> + class-variance-authority 0.7.1
> + clsx 2.1.1
> + jdenticon 3.3.0
> + lucide-vue-next 0.541.0
> + pinia 2.3.1
> + pyodide 0.27.7
> + reka-ui 2.4.1
> + tailwind-merge 3.3.1
> + tailwindcss-animate 1.0.7
> + vue 3.5.17
> + vue-router 4.5.1
> + vue-sonner 2.0.0
>
> devDependencies:
> + @biomejs/biome 2.2.2
> + @tailwindcss/typography 0.5.16
> + @vitejs/plugin-vue 4.6.2
> + @vueuse/core 11.3.0
> + autoprefixer 10.4.21
> + tailwindcss 3.4.17
> + typescript 5.8.3
> + unplugin-auto-import 0.18.6
> + vite 5.4.19
> + vite-plugin-top-level-await 1.5.0
> + vite-svg-loader 5.1.0
> + vue-tsc 2.2.10
>
> Done in 11s

Before the current repository can be built with pnpm run build command, you must first download the required shadcn/vue components first. We currently use two of the many components listed under shadcn/vue components which are:

Using the following commands:

pnpm dlx shadcn-vue@latest add button

and

pnpm dlx shadcn-vue@latest add dropdown-menu
Sample outputs of running the component download commands successfully
$ pnpm dlx shadcn-vue@latest add button
> ✔ Checking registry.
> ✔ Installing dependencies.
> ✔ Created 2 files:
>   - src/components/ui/button/Button.vue                                                                                                 6:43:58 PM
>   - src/components/ui/button/index.ts
$ pnpm dlx shadcn-vue@latest add dropdown-menu
> ✔ Checking registry.
> ✔ Installing dependencies.
> ✔ Created 15 files:
>   - src/components/ui/dropdown-menu/DropdownMenu.vue                                                                                    6:45:07 PM
>   - src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue                                                                        6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuContent.vue                                                                             6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuGroup.vue                                                                               6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuItem.vue                                                                                6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuLabel.vue                                                                               6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue                                                                          6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue                                                                           6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuSeparator.vue                                                                           6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuShortcut.vue                                                                            6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuSub.vue                                                                                 6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuSubContent.vue                                                                          6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue                                                                          6:45:08 PM
>   - src/components/ui/dropdown-menu/DropdownMenuTrigger.vue                                                                             6:45:08 PM
>   - src/components/ui/dropdown-menu/index.ts

Patches

The online interpreter has been patched with few moderations in order to ease development.

Override hashlib.pbkdf2_hmac

This normally is an operation where hashlib binds to OpenSSL in user's environment and not supported in pyodide environment, but interpreter overrides this with a pure python implementation at runtime to make possible some of buidl method calls.

# NOTE: shim pbkdf2_hmac without openssl
#       https://pyodide.org/en/stable/usage/wasm-constraints.html#modules-with-limited-functionality
import hashlib
import hmac
def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None):
  hash_func = getattr(hashlib, hash_name)
  hlen = hash_func().digest_size
  if dklen is None:
    dklen = hlen
  blocks = -(-dklen // hlen)  # ceil division
  def F(block_index):
    U = hmac.new(password, salt + block_index.to_bytes(4, "big"), hash_func).digest()
    result = bytearray(U)
    for _ in range(iterations - 1):
      U = hmac.new(password, U, hash_func).digest()
      result = bytearray(x ^ y for x, y in zip(result, U))
    return result
  dk = b''.join(F(i + 1) for i in range(blocks))
  return dk[:dklen]
hashlib.pbkdf2_hmac = pbkdf2_hmac
Shim OP_CODES into global built-ins

This gives access to developer to immediately use OP_CODES such as OP_0, OP_ADD, OP_EQUAL, and etc. without imports

# NOTE: shim opcodes from buidl.op.OP_CODE_NAMES
from buidl.op import OP_CODE_NAMES
OP_CODES: dict[str, int] = {value: key for key, value in OP_CODE_NAMES.items()}
globals().update(OP_CODES)

Samples

Locking and Spending a Taproot Script

from binascii import unhexlify
from buidl.ecc import S256Point
from buidl.taproot import TapLeaf, TapBranch, TapScript


# buidl doesn't have constants/variables defined for op codes hence why
# we use use their integer values
script_a = TapScript([OP_1, OP_ADD, OP_2, OP_EQUAL])
script_b = TapScript([OP_2, OP_ADD, OP_4, OP_EQUAL])
script_c = TapScript([OP_1, OP_ADD, OP_5, OP_EQUAL])
leaf_a = TapLeaf(script_a)
leaf_b = TapLeaf(script_b)
leaf_c = TapLeaf(script_c)

# we need to construct a tree of branches ourselves
branch_bc = TapBranch(leaf_b, leaf_c)
branch_abc = TapBranch(leaf_a, branch_bc)

internal_pubkey = S256Point.parse_xonly(unhexlify("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"))
addr = internal_pubkey.p2tr_address(branch_abc.hash(), network="regtest")
print("Send 0.01 BTC to the following address: {}".format(addr))
# prints: Send 0.01 BTC to the following address: bcrt1py7e8e0q7pdkxwyp2tjn65k4kd5kgch0aqe03jzjczn96xat6nwjswsu892

Hash Timelock Contract using RIPEMD-160 (Lightning)

from binascii import unhexlify
from buidl.ecc import PrivateKey, S256Point
from buidl.script import Script
from buidl.helper import decode_base58, sha256, hash160

# secret and preimage
preimage_secret = b"super secret code"
preimage = sha256(preimage_secret)

# create private keys from WIF
sender_wif = "cUZLbjpNRAAuu5sV8e8ocwMqAYDtdxY2EXCPyjvyeGATzCnCCaMK"
sender_decoded = decode_base58(sender_wif)
seckey_sender = PrivateKey(int.from_bytes(sender_decoded[1:], "big"))
pubkey_sender = seckey_sender.point.sec()
recipient_wif = "cVhjB76GwuZiva15i88Hwbgc1ZZB6KFMUAjAgiauD1mpugQDVGTc"
recipient_decoded = decode_base58(recipient_wif)
seckey_recipient = PrivateKey(int.from_bytes(recipient_decoded[1:], "big"))
pubkey_recipient = seckey_recipient.point.sec()

# construct redeem script
redeem_script = Script([
  OP_IF,
    OP_SHA256,
    preimage,
    OP_EQUALVERIFY,
    OP_DUP,
    OP_HASH160,
    hash160(pubkey_recipient),
  OP_ELSE,
    10,
    OP_CHECKSEQUENCEVERIFY,
    OP_DROP,
    OP_DUP,
    OP_HASH160,
    hash160(pubkey_sender),
  OP_ENDIF,
  OP_EQUALVERIFY,
  OP_CHECKSIG,
])

# redeem script hash for P2WSH
redeem_script_hash = sha256(redeem_script.serialize())

# P2WSH scriptPubKey: OP_0 <redeem_script_hash>
script_pubkey = Script([OP_0, redeem_script_hash])
internal_pubkey = S256Point.parse_xonly(unhexlify("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"))
address = internal_pubkey.p2tr_address(script_pubkey.serialize(), network="regtest")

Acknowledgements

  1. วง - Wong typeface by uvSOV - Worawut Thanawatanawanich
  2. VitePress Python Editor by @zqianem
  3. YT: Build in-browser 3D experiences with WebGL and PyScript by Łukasz Langa at Pycon US 2024

License

This project is licensed under the terms of the MIT license.

About

Online Integrated Development Environment for Bitscript via Python powered by WASM

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published