{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "d2c6b4cd", "metadata": {}, "outputs": [], "source": [ "# PoC using scaning and spending keys" ] }, { "cell_type": "code", "execution_count": 2, "id": "6bcea120", "metadata": {}, "outputs": [], "source": [ "import hashlib\n", "from py_ecc.secp256k1 import *\n", "import sha3\n", "from eth_account import Account" ] }, { "cell_type": "markdown", "id": "4e25cb04", "metadata": {}, "source": [ "## Sender" ] }, { "cell_type": "markdown", "id": "22ca0bf7", "metadata": {}, "source": [ "$S = G*s$" ] }, { "cell_type": "code", "execution_count": 3, "id": "bb9355a0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(22246744184454969143801186698733154500632648736073949898323976612504587645286,\n", " 110772761940586493986212935445517909380300793379795289150161960681985511655321)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# privkey: 0xd952fe0740d9d14011fc8ead3ab7de3c739d3aa93ce9254c10b0134d80d26a30\n", "# address: 0x3CB39EA2f14B16B69B451719A7BEd55e0aFEcE8F\n", "s = int(0xd952fe0740d9d14011fc8ead3ab7de3c739d3aa93ce9254c10b0134d80d26a30) # private key\n", "S = secp256k1.privtopub(s.to_bytes(32, \"big\")) # public key\n", "S" ] }, { "cell_type": "markdown", "id": "c8240f67", "metadata": {}, "source": [ "## Recipient" ] }, { "cell_type": "markdown", "id": "6895e603", "metadata": {}, "source": [ "$P = G*p$" ] }, { "cell_type": "code", "execution_count": 4, "id": "c8e2d6ad", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "((89565891926547004231252920425935692360644145829622209833684329913297188986597,\n", " 12158399299693830322967808612713398636155367887041628176798871954788371653930),\n", " (112711660439710606056748659173929673102114977341539408544630613555209775888121,\n", " 25583027980570883691656905877401976406448868254816295069919888960541586679410))" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# privkey: 0x0000000000000000000000000000000000000000000000000000000000000001\n", "# address: 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf\n", "p_scan = int(0x0000000000000000000000000000000000000000000000000000000000000002) # private key\n", "p_spend = int(0x0000000000000000000000000000000000000000000000000000000000000003) # private key\n", "\n", "P_scan = secp256k1.privtopub(p_scan.to_bytes(32, \"big\")) # public key\n", "P_spend = secp256k1.privtopub(p_spend.to_bytes(32, \"big\")) # public key\n", "P_scan, P_spend" ] }, { "cell_type": "markdown", "id": "174929d7", "metadata": {}, "source": [ "## Calculate Stealth Address: $P_{spend} + G*hash(Q)$" ] }, { "cell_type": "markdown", "id": "8b39ed39", "metadata": {}, "source": [ "$Q = S * p_{scan}$" ] }, { "cell_type": "code", "execution_count": 5, "id": "63a022d7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(65311808848028536848162101908966111079795231803322390815513763038079235257196,\n", " 43767810034999830518515787564234053904327508763526333662117780420755425490082)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Q = secp256k1.multiply(P_scan, s)\n", "Q" ] }, { "cell_type": "markdown", "id": "d79c69fc", "metadata": {}, "source": [ "$Q = S * p_{scan} = P_{scan} * s$" ] }, { "cell_type": "code", "execution_count": 6, "id": "5f5fbcf4", "metadata": {}, "outputs": [], "source": [ "assert Q == secp256k1.multiply(S, p_scan)" ] }, { "cell_type": "markdown", "id": "0d5803ff", "metadata": {}, "source": [ "$h(Q)$" ] }, { "cell_type": "code", "execution_count": 7, "id": "f1b38cb0", "metadata": {}, "outputs": [], "source": [ "Q_hex = sha3.keccak_256(Q[0].to_bytes(32, \"big\") \n", " + Q[1].to_bytes(32, \"big\")\n", " ).hexdigest()\n", "Q_hased = bytearray.fromhex(Q_hex)" ] }, { "cell_type": "markdown", "id": "a0647821", "metadata": {}, "source": [ "$ stA = h(Q) * G + P_{spend}$" ] }, { "cell_type": "markdown", "id": "865e7f72", "metadata": {}, "source": [ "#### Sender sends funds to..." ] }, { "cell_type": "code", "execution_count": 8, "id": "d9dd755f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'0xfed69df0a27f1dae0d7430ead82aaedfad6332bb'" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stP = secp256k1.add(P_spend, secp256k1.privtopub(Q_hased))\n", "stA = \"0x\"+ sha3.keccak_256(stP[0].to_bytes(32, \"big\")\n", " +stP[1].to_bytes(32, \"big\")\n", " ).hexdigest()[-40:]\n", "stA" ] }, { "cell_type": "markdown", "id": "38e69080", "metadata": {}, "source": [ "#### Sender broadcasts" ] }, { "cell_type": "code", "execution_count": 9, "id": "cdf57fef", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "((22246744184454969143801186698733154500632648736073949898323976612504587645286,\n", " 110772761940586493986212935445517909380300793379795289150161960681985511655321),\n", " '0xfed69df0a27f1dae0d7430ead82aaedfad6332bb')" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "S, stA" ] }, { "cell_type": "markdown", "id": "588ccc7c", "metadata": {}, "source": [ "## Parse received funds" ] }, { "cell_type": "markdown", "id": "462f8c8d", "metadata": {}, "source": [ "* Note that $p_{scan}$ and $P_{spend}$ can be shared with a trusted party\n", "* There may be many S to be parsed" ] }, { "cell_type": "markdown", "id": "8ba2a295", "metadata": {}, "source": [ "$h(p_{scan}*S)*G + P_{spend} => toAddress$" ] }, { "cell_type": "code", "execution_count": 10, "id": "50b63208", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'0xfed69df0a27f1dae0d7430ead82aaedfad6332bb'" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Q = secp256k1.multiply(S, p_scan)\n", "Q_hex = sha3.keccak_256(Q[0].to_bytes(32, \"big\")+Q[1].to_bytes(32, \"big\")).hexdigest()\n", "Q_hased = bytearray.fromhex(Q_hex)\n", "\n", "P_stealth = secp256k1.add(P_spend, secp256k1.privtopub(Q_hased))\n", "P_stealthAddress = \"0x\"+ sha3.keccak_256(stP[0].to_bytes(32, \"big\")\n", " + stP[1].to_bytes(32, \"big\")\n", " ).hexdigest()[-40:]\n", "P_stealthAddress" ] }, { "cell_type": "markdown", "id": "8055d075", "metadata": {}, "source": [ "logged stealth address $stA$ equals the derived stealth address $P_stealthAddress$" ] }, { "cell_type": "markdown", "id": "26758ea5", "metadata": {}, "source": [ "$stA==stA_d$" ] }, { "cell_type": "code", "execution_count": 11, "id": "3faed6a3", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "P_stealthAddress == stA" ] }, { "cell_type": "markdown", "id": "050e346c", "metadata": {}, "source": [ "## Derive private key" ] }, { "cell_type": "markdown", "id": "44801516", "metadata": {}, "source": [ "#### Only the recipient has access to $p_{spend}$" ] }, { "cell_type": "markdown", "id": "7673e439", "metadata": {}, "source": [ "$p_{stealth}=p_{spend}+hash(Q)$" ] }, { "cell_type": "code", "execution_count": 12, "id": "4013b57e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "39153944482575822531387237249775711740128993925789544779866399859639729033274" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Q = secp256k1.multiply(S, p_scan)\n", "Q_hex = sha3.keccak_256(Q[0].to_bytes(32, \"big\")+Q[1].to_bytes(32, \"big\")).hexdigest()\n", "p_stealth = p_spend + int(Q_hex, 16)\n", "p_stealth" ] }, { "cell_type": "markdown", "id": "dc31c1aa", "metadata": {}, "source": [ "$P_{stealth} = p_{stealth}*G$" ] }, { "cell_type": "code", "execution_count": 13, "id": "09b5ccc2", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(67663851387124608323744162645277269585638670865381831245083336172545348387042,\n", " 80449904826544093817252981338261706033086352950841917067356875711772573870404)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Recipient has private key to ...\n", "P_stealth = secp256k1.privtopub(p_stealth.to_bytes(32, \"big\"))\n", "P_stealth" ] }, { "cell_type": "code", "execution_count": 14, "id": "a3ead30e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'0xfed69df0a27f1dae0d7430ead82aaedfad6332bb'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "P_stealthAddress_d = \"0x\"+ sha3.keccak_256(P_stealth[0].to_bytes(32, \"big\")\n", " + P_stealth[1].to_bytes(32, \"big\")\n", " ).hexdigest()[-40:]\n", "P_stealthAddress_d" ] }, { "cell_type": "code", "execution_count": 15, "id": "2712c07b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'0xfEd69Df0a27F1daE0D7430EAd82aaEdfAD6332bb'" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Account.from_key((p_stealth).to_bytes(32, \"big\")).address" ] }, { "cell_type": "markdown", "id": "74f0325e", "metadata": {}, "source": [ "## Additionally add view tags" ] }, { "cell_type": "markdown", "id": "ac45bb87", "metadata": {}, "source": [ "In addition to S and stA, the sender also broadcasts the first byte of h(Q)" ] }, { "cell_type": "code", "execution_count": 16, "id": "9645b880", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "86" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Q_hased[0]" ] }, { "cell_type": "markdown", "id": "8788f2f5", "metadata": {}, "source": [ "The recipient can do the the same a before without one EC Multiplication, one EC Addition and on Public Key to Address Conversion in order to check being a potential recipient." ] }, { "cell_type": "code", "execution_count": 17, "id": "bb9f5852", "metadata": {}, "outputs": [], "source": [ "Q_derived = secp256k1.multiply(S, p_scan)\n", "Q_hex_derived = sha3.keccak_256(Q_derived[0].to_bytes(32, \"big\")\n", " +Q_derived[1].to_bytes(32, \"big\")\n", " ).hexdigest()\n", "Q_hashed_derived = bytearray.fromhex(Q_hex_derived)" ] }, { "cell_type": "markdown", "id": "f7dc4624", "metadata": {}, "source": [ "Check view tag" ] }, { "cell_type": "code", "execution_count": 18, "id": "953bf07d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "run = Q_hased[0] == Q_hashed_derived[0] \n", "run" ] }, { "cell_type": "code", "execution_count": 19, "id": "e11ec134", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'0xfed69df0a27f1dae0d7430ead82aaedfad6332bb'" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "if run:\n", " P_stealth = secp256k1.add(P_spend, secp256k1.privtopub(Q_hased))\n", " P_stealthAddress = \"0x\"+ sha3.keccak_256(stP[0].to_bytes(32, \"big\")\n", " + stP[1].to_bytes(32, \"big\")\n", " ).hexdigest()[-40:]\n", "P_stealthAddress" ] }, { "cell_type": "code", "execution_count": 20, "id": "bd06ffc5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "P_stealthAddress==stA" ] } ], "metadata": { "kernelspec": { "display_name": "hackathon", "language": "python", "name": "hackathon" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.8" } }, "nbformat": 4, "nbformat_minor": 5 }