640 lines
14 KiB
Plaintext
640 lines
14 KiB
Plaintext
{
|
|
"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
|
|
}
|