Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Tests

on:
push:
branches: [master, develop]
pull_request:
branches: [master, develop]

jobs:
test-python:
name: Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install package
run: pip install -e .

- name: Run tests
run: |
cd tests
python run_tests.py

test-micropython:
name: MicroPython (Unix port)
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install MicroPython Unix port
run: |
sudo apt-get update
sudo apt-get install -y build-essential libffi-dev pkg-config
git clone --depth 1 https://github.com/micropython/micropython.git /tmp/micropython
cd /tmp/micropython/ports/unix
make submodules
make

- name: Run tests on MicroPython
run: |
cd tests
/tmp/micropython/ports/unix/build-standard/micropython run_tests.py
continue-on-error: true # MicroPython may lack some deps
35 changes: 35 additions & 0 deletions src/embit/networks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
from .misc import const


def get_network(name):
"""
Get network by name with testnet4 fallback.
Bitcoin Core 28.0+ uses 'testnet4' as the network name,
which has the same address parameters as testnet3.
"""
if name in NETWORKS:
return NETWORKS[name]
# testnet4 uses same parameters as testnet3
if name == "testnet4":
return NETWORKS["test"]
return None


NETWORKS = {
"main": {
"name": "Mainnet",
Expand Down Expand Up @@ -73,4 +88,24 @@
"Zpub": b"\x02\x57\x54\x83",
"bip32": const(1),
},
# testnet4: Bitcoin Core 28.0+ replacement for testnet3
# Uses identical address parameters (bech32 "tb", same xpub/xprv versions)
"testnet4": {
"name": "Testnet4",
"wif": b"\xEF",
"p2pkh": b"\x6F",
"p2sh": b"\xC4",
"bech32": "tb",
"xprv": b"\x04\x35\x83\x94",
"xpub": b"\x04\x35\x87\xcf",
"yprv": b"\x04\x4a\x4e\x28",
"zprv": b"\x04\x5f\x18\xbc",
"Yprv": b"\x02\x42\x85\xb5",
"Zprv": b"\x02\x57\x50\x48",
"ypub": b"\x04\x4a\x52\x62",
"zpub": b"\x04\x5f\x1c\xf6",
"Ypub": b"\x02\x42\x89\xef",
"Zpub": b"\x02\x57\x54\x83",
"bip32": const(1),
},
}
65 changes: 65 additions & 0 deletions tests/tests/test_networks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""Tests for network definitions and get_network function."""

from embit import networks
from unittest import TestCase


class TestNetworks(TestCase):
"""Test Bitcoin network definitions."""

def test_all_networks_present(self):
"""All expected Bitcoin networks should be defined."""
expected = ["main", "test", "regtest", "signet", "testnet4"]
for name in expected:
self.assertIn(name, networks.NETWORKS)

def test_testnet4_parameters(self):
"""testnet4 should have same parameters as testnet3."""
test = networks.NETWORKS["test"]
testnet4 = networks.NETWORKS["testnet4"]

# Same address parameters
self.assertEqual(testnet4["bech32"], test["bech32"])
self.assertEqual(testnet4["bech32"], "tb")
self.assertEqual(testnet4["p2pkh"], test["p2pkh"])
self.assertEqual(testnet4["p2sh"], test["p2sh"])
self.assertEqual(testnet4["wif"], test["wif"])

# Same xpub/xprv versions
self.assertEqual(testnet4["xpub"], test["xpub"])
self.assertEqual(testnet4["xprv"], test["xprv"])
self.assertEqual(testnet4["zpub"], test["zpub"])
self.assertEqual(testnet4["zprv"], test["zprv"])

# Same coin type
self.assertEqual(testnet4["bip32"], test["bip32"])

def test_get_network_direct(self):
"""get_network should return network by name."""
main = networks.get_network("main")
self.assertEqual(main["name"], "Mainnet")
self.assertEqual(main["bech32"], "bc")

test = networks.get_network("test")
self.assertEqual(test["name"], "Testnet")
self.assertEqual(test["bech32"], "tb")

def test_get_network_testnet4(self):
"""get_network should handle testnet4."""
testnet4 = networks.get_network("testnet4")
self.assertIsNotNone(testnet4)
self.assertEqual(testnet4["name"], "Testnet4")
self.assertEqual(testnet4["bech32"], "tb")

def test_get_network_unknown(self):
"""get_network should return None for unknown networks."""
result = networks.get_network("unknown_network")
self.assertIsNone(result)

def test_mainnet_bech32(self):
"""Mainnet should use 'bc' prefix."""
self.assertEqual(networks.NETWORKS["main"]["bech32"], "bc")

def test_regtest_bech32(self):
"""Regtest should use 'bcrt' prefix."""
self.assertEqual(networks.NETWORKS["regtest"]["bech32"], "bcrt")