From 681e303ceca58c780cf726576a4acc35f8981a5d Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Wed, 17 Dec 2025 09:17:14 +0100 Subject: [PATCH 1/2] Add Python tests (Python and C interface) --- .github/workflows/build_python.yml | 4 ++ samples/python/sample_secret.sec | 1 + samples/python/test_lib.py | 35 +++++++++++ samples/python/test_lib_c.py | 97 ++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 samples/python/sample_secret.sec create mode 100644 samples/python/test_lib.py create mode 100644 samples/python/test_lib_c.py diff --git a/.github/workflows/build_python.yml b/.github/workflows/build_python.yml index 7090768..5ce0011 100644 --- a/.github/workflows/build_python.yml +++ b/.github/workflows/build_python.yml @@ -28,3 +28,7 @@ jobs: run: cd lib-py && VIRTUAL_ENV='../venv' ../venv/bin/maturin develop && cd .. - name: Run the Python sample app run: cd samples/python && ../../venv/bin/python main.py && cd ../.. + - name: Run the Python test (Python interface) + run: cd samples/python && ../../venv/bin/python test_lib.py && cd ../.. + - name: Run the Python test (C interface) + run: cd samples/python && ../../venv/bin/python test_lib_c.py && cd ../.. diff --git a/samples/python/sample_secret.sec b/samples/python/sample_secret.sec new file mode 100644 index 0000000..1cc6c1a --- /dev/null +++ b/samples/python/sample_secret.sec @@ -0,0 +1 @@ +d37ca643f810e9d76bd608463fa1216cdef6b2 \ No newline at end of file diff --git a/samples/python/test_lib.py b/samples/python/test_lib.py new file mode 100644 index 0000000..a03ddc1 --- /dev/null +++ b/samples/python/test_lib.py @@ -0,0 +1,35 @@ +# Copyright (c) 2025-present Cadena Bitcoin +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +import dlccryptlib_oracle + +xpub = dlccryptlib_oracle.init('sample_secret.sec', 'password') +print('Library initialized, xpub', xpub) + +xpub = dlccryptlib_oracle.get_xpub() +print('Xpub', xpub) +pubkey0 = dlccryptlib_oracle.get_public_key(0) +print('Pubkey 0', pubkey0) +address0 = dlccryptlib_oracle.get_address(0) +print('Address 0', address0) + +event_id = "event001" +nonce0_arr = dlccryptlib_oracle.create_deterministic_nonce(event_id, 0) +nonce0_pub = nonce0_arr[1] +print('Nonce 0 (pub)', nonce0_pub) +nonce1_arr = dlccryptlib_oracle.create_deterministic_nonce(event_id, 1) +nonce1_sec = nonce1_arr[0] +nonce1_pub = nonce1_arr[1] +print('Nonce 1 (pub, sec)', nonce1_pub, nonce1_sec) +nonce2_arr = dlccryptlib_oracle.create_deterministic_nonce(event_id, 2) + +# Sign the event id with nonce1 +sig = dlccryptlib_oracle.sign_schnorr_with_nonce(event_id, nonce1_sec, 0) +print('Signature: ', sig) + +# Sign again (same nonce) +print('Sign again: ', dlccryptlib_oracle.sign_schnorr_with_nonce(event_id, nonce1_sec, 0)) + +# Sign with different nonce +print('Sign with other nonce: ', dlccryptlib_oracle.sign_schnorr_with_nonce(event_id, nonce2_arr[0], 0)) diff --git a/samples/python/test_lib_c.py b/samples/python/test_lib_c.py new file mode 100644 index 0000000..a4b752c --- /dev/null +++ b/samples/python/test_lib_c.py @@ -0,0 +1,97 @@ +# Copyright (c) 2025-present Cadena Bitcoin +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Test the dlccryptlib library through its C-style interface +# Test the methods used by the app + +import os +import sys +from ctypes import cdll, c_char_p, c_uint32, c_uint8 + +class RustInterface: + def __init__(self): + try: + if hasattr(sys, 'getandroidapilevel'): + # Running on Android + self.lib = cdll.LoadLibrary("libdlccryptlib_py.so") + else: + # Local dev environment + dev_path = "../../lib-py/target/debug/libdlccryptlib_py.so" + if os.path.exists(dev_path): + self.lib = cdll.LoadLibrary(os.path.abspath(dev_path)) + else: + self.lib = cdll.LoadLibrary("libdlccryptlib_py.so") + except OSError as e: + raise RuntimeError(f"Could not load Rust shared library: {e}") + + # Define Rust method signatures + self.lib.init_with_entropy_c.argtypes = [c_char_p, c_char_p] + self.lib.init_with_entropy_c.restype = c_char_p + self.lib.get_public_key_c.argtypes = [c_uint32] + self.lib.get_public_key_c.restype = c_char_p + self.lib.sign_hash_ecdsa_c.argtypes = [c_char_p, c_uint32, c_char_p] + self.lib.sign_hash_ecdsa_c.restype = c_char_p + self.lib.create_cet_adaptor_sigs_c.argtypes = [c_uint8, c_uint32, c_char_p, c_char_p, c_uint32, c_char_p, c_char_p, c_char_p, c_char_p] + self.lib.create_cet_adaptor_sigs_c.restype = c_char_p + self.lib.create_deterministic_nonce_c.argtypes = [c_char_p, c_char_p] + self.lib.create_deterministic_nonce_c.restype = c_char_p + + # Initialize the library, provide the secret as parameter. Return the XPUB. + def init_with_entropy(self, entropy, network): + # Call the Rust function (init_with_entropy_c) from the .so library + return self.lib.init_with_entropy_c(entropy.encode('utf-8'), network.encode('utf-8')).decode("utf-8") + + # Sign a hash with a child private key (specified by index). + def sign_hash_ecdsa(self, hash, signer_index, signer_pubkey): + # Call the Rust function (sign_hash_ecdsa_c) from the .so library + return self.lib.sign_hash_ecdsa_c(hash.encode('utf-8'), signer_index, signer_pubkey.encode('utf-8')).decode('utf-8') + + # Create adaptor signatures for a number of CETs + def create_cet_adaptor_sigs(self, num_digits: int, num_cets: int, digit_string_template: str, oracle_pubkey: str, signing_key_index: int, signing_pubkey: str, nonces: str, interval_wildcards: str, sighashes: str): + # Call the Rust function (create_cet_adaptor_sigs_c) from the .so library + return self.lib.create_cet_adaptor_sigs_c(num_digits, num_cets, digit_string_template.encode('utf-8'), oracle_pubkey.encode('utf-8'), signing_key_index, signing_pubkey.encode('utf-8'), nonces.encode('utf-8'), interval_wildcards.encode('utf-8'), sighashes.encode('utf-8')).decode('utf-8') + + # Return a child public key. + def get_public_key(self, index): + # Call the Rust function (get_public_key_c) from the .so library + return self.lib.get_public_key_c(index).decode("utf-8") + + def create_deterministic_nonce(self, event_id, index): + # Call the Rust function (create_deterministic_nonce_c) from the .so library + return self.lib.create_deterministic_nonce_c(event_id.encode('utf-8'), str(index).encode('utf-8')).decode("utf-8") + + +rust_interface = None +try: + rust_interface = RustInterface() +except OSError: + print("ELHASALTUNK") + +entropy_hex = "00000000000000000000000000000001" # abandon x 11 actual +network = "signet" + +xpub = rust_interface.init_with_entropy(entropy_hex, network) +print("xpub:", xpub) +assert(xpub == "tpubDCxVvuZwEu4oZypCT3pzos1MUoVJyjTHjfrhKFXNBkAEqBmkkzEb2dUgzpZmBWbd6wZnNmm3Ex2suMnEFUMmayH2a6S49R4pTnoQttGrxUm") + +pubkey0 = rust_interface.get_public_key(0) +print("pubkey 0:", pubkey0) +assert(pubkey0 == "031941e84b8d111e094aefc46e7181757c93a1da87c93ab519a40d9d765176e704") + +hash = "0001020300000000000000000000000000000000000000000000000000010203" +sig = rust_interface.sign_hash_ecdsa(hash, 0, pubkey0) +print("sig:", sig) + + +digit_string_template = "Outcome:btcusd1747220520:{digit_index}:{digit_outcome}" +oracle_pubkey = "0292892b831077bc87f7767215ab631ff56d881986119ff03f1b64362e9abc70cd" +nonces = "03bf8272fd77ac83400e8b7f1af5899ab96ce81871ca26d31fa3b80db08bdc412e 03ec14b379b5db0c5305a452ee04d4b82b5a1db90f8eddc55f1f94d5947b341ed4 0325642feb3db37b3ffa88b0754d59ad1c3116e035ee9e5557e107fd3d914fb3fb 02d597f9bd84cb925ade7efa04edf46c33a7d96cc4252647204a6961a34838d00d 03d11c778b1c4f1f7710a4b17816f02d049325220b2fb8007efd84248f08fd75dc 038fb0dbd6eb0e970c75c28ca02b614523fe59b5da000f815fcfbfcf4a4ecdd192 023f0eadc3b9c3337d31e38a9238a3c59505cc8004fa7ca6facdd3c853d824ca0d" +interval_wildcards = "0000*** 0001*** 0002***" +sighashes = "0001020300000000000000000000000000000000000000000000000000010200 0001020300000000000000000000000000000000000000000000000000010201 0001020300000000000000000000000000000000000000000000000000010202" +sigs = rust_interface.create_cet_adaptor_sigs(7, 3, digit_string_template, oracle_pubkey, 0, pubkey0, nonces, interval_wildcards, sighashes) +print("signatures:", sigs) + + +det_nonce0 = rust_interface.create_deterministic_nonce("event001", 0) +print("nonce:", det_nonce0) From 3656d9211beb9dc94ccadd0f71680caf7a20449a Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Wed, 17 Dec 2025 09:19:46 +0100 Subject: [PATCH 2/2] Test fix, lib name fix --- samples/python/test_lib.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/samples/python/test_lib.py b/samples/python/test_lib.py index a03ddc1..30834d8 100644 --- a/samples/python/test_lib.py +++ b/samples/python/test_lib.py @@ -2,34 +2,34 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -import dlccryptlib_oracle +import dlccryptlib_py -xpub = dlccryptlib_oracle.init('sample_secret.sec', 'password') +xpub = dlccryptlib_py.init('sample_secret.sec', 'password') print('Library initialized, xpub', xpub) -xpub = dlccryptlib_oracle.get_xpub() +xpub = dlccryptlib_py.get_xpub() print('Xpub', xpub) -pubkey0 = dlccryptlib_oracle.get_public_key(0) +pubkey0 = dlccryptlib_py.get_public_key(0) print('Pubkey 0', pubkey0) -address0 = dlccryptlib_oracle.get_address(0) +address0 = dlccryptlib_py.get_address(0) print('Address 0', address0) event_id = "event001" -nonce0_arr = dlccryptlib_oracle.create_deterministic_nonce(event_id, 0) +nonce0_arr = dlccryptlib_py.create_deterministic_nonce(event_id, 0) nonce0_pub = nonce0_arr[1] print('Nonce 0 (pub)', nonce0_pub) -nonce1_arr = dlccryptlib_oracle.create_deterministic_nonce(event_id, 1) +nonce1_arr = dlccryptlib_py.create_deterministic_nonce(event_id, 1) nonce1_sec = nonce1_arr[0] nonce1_pub = nonce1_arr[1] print('Nonce 1 (pub, sec)', nonce1_pub, nonce1_sec) -nonce2_arr = dlccryptlib_oracle.create_deterministic_nonce(event_id, 2) +nonce2_arr = dlccryptlib_py.create_deterministic_nonce(event_id, 2) # Sign the event id with nonce1 -sig = dlccryptlib_oracle.sign_schnorr_with_nonce(event_id, nonce1_sec, 0) +sig = dlccryptlib_py.sign_schnorr_with_nonce(event_id, nonce1_sec, 0) print('Signature: ', sig) # Sign again (same nonce) -print('Sign again: ', dlccryptlib_oracle.sign_schnorr_with_nonce(event_id, nonce1_sec, 0)) +print('Sign again: ', dlccryptlib_py.sign_schnorr_with_nonce(event_id, nonce1_sec, 0)) # Sign with different nonce -print('Sign with other nonce: ', dlccryptlib_oracle.sign_schnorr_with_nonce(event_id, nonce2_arr[0], 0)) +print('Sign with other nonce: ', dlccryptlib_py.sign_schnorr_with_nonce(event_id, nonce2_arr[0], 0))