From 8e89ab0b1892294916ba453221e7b4a39b063dad Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 18 Oct 2023 16:10:09 +0200 Subject: [PATCH] sol: Schnorr signature Signed-off-by: Vincenzo Palazzo --- session/complete/session.ipynb | 557 ++++++++++++++++++++++++++++----- session/ecc.py | 35 ++- session/hash.py | 3 +- session/session.ipynb | 273 ++++++++++++---- 4 files changed, 721 insertions(+), 147 deletions(-) diff --git a/session/complete/session.ipynb b/session/complete/session.ipynb index 893e142..a867765 100644 --- a/session/complete/session.ipynb +++ b/session/complete/session.ipynb @@ -19,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "8dadf5e6", "metadata": { "slideshow": { @@ -41,14 +41,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "1e49bfe0", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "233a1e9353c5f782c96c1c08323fe9fca47ad161ee69d008846b68625c221113\n" + ] + } + ], "source": [ "# Example Tagged Hashes\n", "from hash import sha256\n", @@ -76,10 +84,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "f5b2d003", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1d721a19d161e978e7436d9e73bb810a0a32cbdffc7a9b29e11713b1940a4126\n" + ] + } + ], "source": [ "# Exercise 1\n", "\n", @@ -113,10 +129,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "24346452", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.000s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 2\n", "\n", @@ -143,14 +171,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "87fa62d1", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "f01d6b9018ab421dd410404cb869072065522bf85734008f105cf385a023a80f\n", + "True\n" + ] + } + ], "source": [ "# Example X-only pubkey\n", "from ecc import PrivateKey, S256Point\n", @@ -178,10 +215,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "1d4b0f3b", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "e79c4eb45764bd015542f6779cc70fef44b7a2432f839264768288efab886291\n" + ] + } + ], "source": [ "# Exercise 3\n", "\n", @@ -215,10 +260,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "c3c8ebf9", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.000s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 4\n", "\n", @@ -243,14 +300,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "fc90c449", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "from ecc import S256Point, SchnorrSignature, G, N\n", "from helper import sha256, big_endian_to_int\n", @@ -293,10 +358,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "1f3c774d", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "# Exercise 5\n", "\n", @@ -335,10 +408,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "9d511cfd", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.111s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 6\n", "\n", @@ -364,14 +449,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "3eb5a3c9", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "f3626c99fe36167e5fef6b95e5ed6e5687caa4dc828986a7de8f9423c0f77f9bc73091ed86085ce43de0e255b3d0afafc7eee41ddc9970c3dc8472acfcdfd39a\n" + ] + } + ], "source": [ "# Example Signing\n", "from ecc import PrivateKey, N, G\n", @@ -413,10 +506,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "id": "7914103c", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5ad2703f5b4f4b9dea4c28fa30d86d3781d28e09dd51aae1208de80bb6155bee7d9dee36de5540efd633445a8d743816cbbc15fb8a1c7768984190d5b873a341\n" + ] + } + ], "source": [ "# Exercise 7\n", "\n", @@ -475,10 +576,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "b6b3bc50", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.230s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 8\n", "\n", @@ -530,10 +643,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "0af3fd1e", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "# Exercise 9\n", "\n", @@ -587,14 +708,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "8792cf7d", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OP_1 578444b411276eee17e2f69988d192b7e728f4375525a868f4a9c2b78e12af16\n" + ] + } + ], "source": [ "# Example Q calculation for a single-key\n", "from ecc import S256Point, G\n", @@ -626,10 +755,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "2b2607d1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OP_1 a6b9f4b7999f9c6de76165342c9feac354d5d3062a41761ed1616eaf9e3c38ec\n" + ] + } + ], "source": [ "# Exercise 10\n", "\n", @@ -669,10 +806,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "a64dbec3", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.001s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 11\n", "\n", @@ -699,10 +848,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "id": "677255f7", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.057s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 12\n", "\n", @@ -729,10 +890,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "id": "b6de5731", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.057s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 13\n", "\n", @@ -760,14 +933,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "id": "cb17ebfd", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "bc1p27zyfdq3yahwu9lz76vc35vjklnj3aph25j6s68548pt0rsj4utql46j72\n", + "tb1p27zyfdq3yahwu9lz76vc35vjklnj3aph25j6s68548pt0rsj4utqgavay9\n" + ] + } + ], "source": [ "# Example of getting a p2tr address\n", "from ecc import S256Point\n", @@ -796,10 +978,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "id": "fb7d63c5", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tb1pfx2ys8pzcg0mdufk9v25hphv85zgjpv5kyn6uevdmfmvdsw0ea0qyvv87u\n" + ] + } + ], "source": [ "# Exercise 14\n", "\n", @@ -832,14 +1022,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "id": "f4442ec9", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "010000000001019569365ed633a71a12ad889235f2f0e7509ffa53e510c25f46241063d76418870000000000ffffffff012c4c0000000000002251205f401f8ef325e3601e93f357ba33014416b7e5877a6ae69e68c8438bd522e1f601403697a0f0f49a451668b9b0361ec7c3b857299f0f80b8ce8c50e1d3cc87f44382de2b6eeccabe0efda3b1639841c342fce64ba28a2a018d4a9a69f5e7a0d43f6b00000000\n" + ] + } + ], "source": [ "# Spending from a p2tr\n", "from ecc import PrivateKey, N\n", @@ -883,10 +1081,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "id": "adececcf", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.104s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 15\n", "\n", @@ -915,10 +1125,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "id": "76abb9c7", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "01000000000101376afebcc53dd694c99c5674846909881f790145948cb820b1f61f89486309250000000000ffffffff02409c000000000000160014f5a74a3131dedb57a092ae86aad3ee3f9b8d72146ce80000000000002251204994481c22c21fb6f1362b154b86ec3d04890594b127ae658dda76c6c1cfcf5e014002de2a8a88783937f10742235dfdf6a0f9526f4e8eee9d3d4cd11d5813269a0d1b56b5028b81735dae9d3dd9b9f2fe2193474dba0569cff087c2575f0f8f5b5f00000000\n" + ] + } + ], "source": [ "# Exercise 16\n", "\n", @@ -989,10 +1207,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "id": "54fcb4e1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.666s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 17\n", "\n", @@ -1018,14 +1248,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "id": "d2e4b852", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "331a8f6a14e1b41a6b523ddb505fbc0662a6446bd42408692497297d3474aeec OP_CHECKSIG\n", + "331a8f6a14e1b41a6b523ddb505fbc0662a6446bd42408692497297d3474aeec OP_CHECKSIGVERIFY 158a49d62c384c539a453e41a70214cfb85184954ae5c8df4b47eb74d58ff16f OP_CHECKSIG\n", + "331a8f6a14e1b41a6b523ddb505fbc0662a6446bd42408692497297d3474aeec OP_CHECKSIGVERIFY 158a49d62c384c539a453e41a70214cfb85184954ae5c8df4b47eb74d58ff16f OP_CHECKSIGADD 582662e8e47df59489d6756615aa3db3fa3bbaa75a424b9c78036265858f5544 OP_CHECKSIGADD OP_2 OP_EQUAL\n", + "40d10c OP_CHECKLOCKTIMEVERIFY OP_DROP 331a8f6a14e1b41a6b523ddb505fbc0662a6446bd42408692497297d3474aeec OP_CHECKSIG\n" + ] + } + ], "source": [ "# Example TapScripts\n", "from ecc import PrivateKey\n", @@ -1065,10 +1306,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "id": "24f90fb9", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "134ba4d9c35a66017e9d525a879700a9fb9209a3f43a651fdaf71f3a085a77d3 OP_CHECKSIGVERIFY 027aa71d9cdb31cd8fe037a6f441e624fe478a2deece7affa840312b14e971a4 OP_CHECKSIGVERIFY 165cfd87a31d8fab4431c955b0462804f1ba79b41970ab7e8b0e4e4686f5f8b4 OP_CHECKSIGVERIFY 9e5f5a5c29d33c32185a3dc0a9ccb3e72743744dd869dd40b6265a23fd84a402 OP_CHECKSIG\n" + ] + } + ], "source": [ "# Exercise 18\n", "\n", @@ -1102,14 +1351,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "id": "b5262af4", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d1b3ee8e8c175e5db7e2ff7a87435e8f751d148b77fb1f00e14ff8ffa1c09a40\n" + ] + } + ], "source": [ "# Example of making a TapLeaf and calculating the hash\n", "from ecc import PrivateKey\n", @@ -1141,10 +1398,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "id": "65e33b7f", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0787f5aba506f118a90cefaf00ccfdb2785cf5998d40c3d43ebfaa5b4c6bcb7d\n" + ] + } + ], "source": [ "# Exercise 19\n", "\n", @@ -1185,10 +1450,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "id": "bfb6dad1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.000s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 20\n", "\n", @@ -1214,14 +1491,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "id": "aeee7a5a", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "60f57015577d9cc2326d980355bc0896c80a9f94dc692d8738069bc05895634c\n" + ] + } + ], "source": [ "from ecc import PrivateKey\n", "from hash import hash_tapbranch\n", @@ -1262,10 +1547,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "id": "b1719cda", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "f938d6fa5e3335e540f07a4007ee296640a977c89178aca79f15f2ec6acc14b6\n" + ] + } + ], "source": [ "# Exercise 21\n", "\n", @@ -1316,10 +1609,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "id": "90998eb1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.001s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 22\n", "\n", @@ -1345,14 +1650,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "id": "e6775fbc", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "f53fab2e9cf0a458609226b4c42d5c0264700cdf33850c2b1423543a44ad4234\n" + ] + } + ], "source": [ "# Example of Comupting the Merkle Root\n", "from ecc import PrivateKey\n", @@ -1391,10 +1704,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "id": "59b96bf1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "8b9f09cd4a33e62b0c9d086056bbdeb7a218c1e4830291b9be56841b31d94ccb\n" + ] + } + ], "source": [ "# Exercise 23\n", "\n", @@ -1462,14 +1783,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "id": "4eba16fa", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "tb1pe0jrx2y2u8hdu8eysx8ssprdfej8lmuq3namllrazrey56vwan7s5j2wr8\n" + ] + } + ], "source": [ "# Example of Control Block Validation\n", "from ecc import PrivateKey, S256Point\n", @@ -1510,10 +1840,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "id": "9f876c91", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "# Exercise 24\n", "\n", @@ -1562,10 +1900,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "id": "b689f58b", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.043s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 25\n", "\n", @@ -1597,10 +1947,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "id": "a14e3ae5", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tb1pxh7kypwsvxnat0z6588pufhx43r2fnqjyn846qj5kx8mgqcamvjsyn5cjg\n" + ] + } + ], "source": [ "# Exercise 26\n", "\n", @@ -1651,10 +2009,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "id": "2062d3a2", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "010000000001019c5a863b077030fd35b0c8170ae698d23c9a2f1e0873c466621739c4954480690100000000ffffffff0178e600000000000022512035fd6205d061a7d5bc5aa1ce1e26e6ac46a4cc1224cf5d0254b18fb4031ddb250140b33905727e316ab7fc8c2816761d61af9f1c535cee632a210642f07d619af632c6df51d63099be31e6d12ecd2a465543861eab6e53feb09ccd49288bda1cb8f600000000\n" + ] + } + ], "source": [ "# Exercise 27\n", "\n", @@ -1706,10 +2072,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "id": "fb786518", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "01000000000101a2b85a1372be6bc9a3d53110a4f142819d653fa6d07fbd4367138145030914200000000000ffffffff0184e4000000000000160014f5a74a3131dedb57a092ae86aad3ee3f9b8d721403403b1681a67f40e6767b2db64744ad3f005d3971645135d58a3e1826d5c960bc281ce187bc9270c51ed7833fcf5e8415501862d51b0ebd051917d9878104778f292220cd04c1bf88ca891af152fc57c36523ab59efb16b7ec07caca0cfc4a1f2051d9eac61c0cd04c1bf88ca891af152fc57c36523ab59efb16b7ec07caca0cfc4a1f2051d9e76f5c1cdfc8b07dc8edca5bef2b4991201c5a0e18b1dbbcfe00ef2295b8f6dffaf5548715217f7a892c7c5ff787a97b6e2f123287a1a354fe3ccda09c39d5d7300000000\n" + ] + } + ], "source": [ "# Exercise 28\n", "\n", @@ -1761,7 +2136,25 @@ ] } ], - "metadata": {}, + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.5" + } + }, "nbformat": 4, "nbformat_minor": 5 } diff --git a/session/ecc.py b/session/ecc.py index e80317a..1371568 100644 --- a/session/ecc.py +++ b/session/ecc.py @@ -271,9 +271,12 @@ class S256Point(Point): def xonly(self): """returns the binary version of X-only pubkey""" # if x is None, return 32 0 bytes + if self.x is None: + return b"\x00" * 32 # otherwise, convert the x coordinate to Big Endian 32 bytes - raise NotImplementedError + return int_to_big_endian(self.x.num, 32) + def tweak(self, merkle_root=b""): """returns the tweak for use in p2tr if there's no script path""" # take the hash_taptweak of the xonly and the merkle root @@ -657,7 +660,35 @@ class PrivateKey: # create a SchnorrSignature object using the R and s # check that this schnorr signature verifies # return the signature - raise NotImplementedError + # set d (working secret) to N - secret if odd, secret otherwise + if self.point.parity: + d = N - self.secret + else: + d = self.secret + # get k using the self.bip340_k method + k = self.bip340_k(msg, aux) + # get the resulting R=kG point + r = k * G + # if R's y coordinate is odd, flip the k + if r.parity: + # set k to N - k + k = N - k + # recalculate R + r = k * G + # calculate the commitment which is: R || P || msg + commitment = r.xonly() + self.point.xonly() + msg + # hash_challenge the result and interpret as a big endian integer + # mod the result by N and this is your e + e = big_endian_to_int(hash_challenge(commitment)) % N + # calculate s which is (k+ed) mod N + s = (k + e * d) % N + # create a SchnorrSignature object using the R and s + sig = SchnorrSignature(r, s) + # check that this schnorr signature verifies + if not self.point.verify_schnorr(msg, sig): + raise RuntimeError("Bad Signature") + # return the signature + return sig def deterministic_k(self, z): k = b"\x00" * 32 diff --git a/session/hash.py b/session/hash.py index f816844..909ee10 100644 --- a/session/hash.py +++ b/session/hash.py @@ -10,7 +10,8 @@ def sha256(x): def tagged_hash(tag, msg): # compute the sha256 of the tag using hashlib.sha256 # compute the tagged hash by getting the sha256 of the tag hash + tag hash + message - raise NotImplementedError + challenge_hash = sha256(tag) + return sha256(challenge_hash + challenge_hash + msg) def hash_aux(msg): diff --git a/session/session.ipynb b/session/session.ipynb index 80da910..770cfd5 100644 --- a/session/session.ipynb +++ b/session/session.ipynb @@ -19,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "id": "8dadf5e6", "metadata": { "slideshow": { @@ -41,14 +41,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "id": "1e49bfe0", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "233a1e9353c5f782c96c1c08323fe9fca47ad161ee69d008846b68625c221113\n" + ] + } + ], "source": [ "# Example Tagged Hashes\n", "from hash import sha256\n", @@ -76,21 +84,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "id": "2787e08e", - "metadata": {}, - "outputs": [], + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1d721a19d161e978e7436d9e73bb810a0a32cbdffc7a9b29e11713b1940a4126\n" + ] + } + ], "source": [ "# Exercise 1\n", "\n", "from hash import sha256\n", - "# define the challenge tag and the message\n", - "\n", - "\n", - "# calculate the challenge tag hash using sha256\n", - "\n", - "# calculate the hash of the challenge\n", - "\n" + "challenge_hash = sha256(b\"BIP0340/aux\")\n", + "msg = b\"hello world\"\n", + "hash_challenge = sha256(challenge_hash + challenge_hash + msg)\n", + "print(hash_challenge.hex())\n" ] }, { @@ -112,10 +127,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "id": "24346452", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.001s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 2\n", "\n", @@ -142,18 +169,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "id": "87fa62d1", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "f01d6b9018ab421dd410404cb869072065522bf85734008f105cf385a023a80f\n", + "True\n" + ] + } + ], "source": [ "# Example X-only pubkey\n", "from ecc import PrivateKey, S256Point\n", "from helper import int_to_big_endian\n", + "\n", "pubkey = PrivateKey(12345).point\n", "xonly = int_to_big_endian(pubkey.x.num, 32)\n", "print(xonly.hex())\n", @@ -177,19 +214,32 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 42, "id": "7179e7c0", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "e79c4eb45764bd015542f6779cc70fef44b7a2432f839264768288efab886291\n", + "True\n" + ] + } + ], "source": [ "# Exercise 3\n", "\n", - "from ecc import PrivateKey\n", + "from ecc import PrivateKey, S256Point\n", + "from helper import int_to_big_endian\n", "secret = 21000000\n", "# create a private key with the secret\n", - "\n", + "pubkey = PrivateKey(secret).point\n", "# get the public point for the private key\n", - "\n", + "xonly = int_to_big_endian(pubkey.x.num, 32)\n", + "print(xonly.hex())\n", + "pubkey2 = S256Point.parse(xonly)\n", + "print(pubkey.xonly() == pubkey2.xonly())\n", "# convert the x coordinate to a big-endian integer 32 bytes\n", "\n" ] @@ -213,10 +263,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "id": "c3c8ebf9", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.001s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 4\n", "\n", @@ -241,14 +303,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 43, "id": "fc90c449", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "from ecc import S256Point, SchnorrSignature, G, N\n", "from helper import sha256, big_endian_to_int\n", @@ -291,10 +361,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 44, "id": "9e5fb728", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "# Exercise 5\n", "\n", @@ -307,10 +385,11 @@ "sig = SchnorrSignature.parse(sig_raw)\n", "msg = bytes.fromhex(\"1a84547db188f0b1d2c9f0beac230afebbd5e6e6c1a46fc69841815194bf8612\")\n", "# create the commitment: R || P || m (points should be xonly)\n", - "\n", + "commitment = sig.r.xonly() + pubkey.xonly() + msg\n", "# hash the commitment with hash_challenge and then big_endian_to_int it\n", - "\n", - "# check that -hP+sG=R\n" + "h = big_endian_to_int(hash_challenge(commitment))\n", + "# check that -hP+sG=R\n", + "print(-h*pubkey + sig.s*G == sig.r)" ] }, { @@ -332,10 +411,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 45, "id": "9d511cfd", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.071s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 6\n", "\n", @@ -361,14 +452,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 46, "id": "3eb5a3c9", "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "f3626c99fe36167e5fef6b95e5ed6e5687caa4dc828986a7de8f9423c0f77f9bc73091ed86085ce43de0e255b3d0afafc7eee41ddc9970c3dc8472acfcdfd39a\n" + ] + } + ], "source": [ "# Example Signing\n", "from ecc import PrivateKey, N, G\n", @@ -410,46 +509,56 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "id": "6adb7529", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5ad2703f5b4f4b9dea4c28fa30d86d3781d28e09dd51aae1208de80bb6155bee7d9dee36de5540efd633445a8d743816cbbc15fb8a1c7768984190d5b873a341\n" + ] + } + ], "source": [ "# Exercise 7\n", "\n", "from ecc import PrivateKey, N, G\n", "from helper import sha256\n", "# create the private key\n", - "\n", + "priv = PrivateKey(21000000)\n", "# calculate d (working secret) based on whether the y is even or odd\n", "if priv.point.y.num % 2 == 1:\n", " d = N - priv.secret\n", "else:\n", " d = priv.secret\n", "# create the message\n", - "\n", + "msg = sha256(b\"I'm learning Taproot!\")\n", "# We'll learn more about k later, for now use 987654321\n", "k = 987654321\n", "# get the resulting R=kG point\n", - "\n", + "r = k * G\n", "# if R's y coordinate is odd, flip the k\n", - "\n", + "if r.y.num % 2 == 1:\n", " # set k to N - k\n", - "\n", + " k = N - k\n", " # recalculate R\n", + " r = k * G\n", "\n", "# calculate the commitment which is: R || P || msg\n", - "\n", + "commitment = r.xonly() + priv.point.xonly() + msg\n", "# hash_challenge the result and interpret as a big endian integer mod the result by N and this is your e\n", - "\n", + "e = big_endian_to_int(hash_challenge(commitment)) % N\n", "# calculate s which is (k+ed) mod N\n", - "\n", + "s = (k + e * d) % N\n", "# create a SchnorrSignature object using the R and s\n", - "\n", + "sig = SchnorrSignature(r, s)\n", "# check that this schnorr signature verifies\n", - "\n", - "\n", - "# print the serialized hex of the signature\n" + "if not priv.point.verify_schnorr(msg, sig):\n", + " raise RuntimeError(\"Bad Signature\")\n", + "# print the serialized hex of the signature\n", + "print(sig.serialize().hex())" ] }, { @@ -471,10 +580,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 49, "id": "b6b3bc50", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.137s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 8\n", "\n", @@ -526,16 +647,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 51, "id": "501b6994", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "# Exercise 9\n", "\n", "from ecc import SchnorrSignature, S256Point, N, G\n", "from hash import hash_challenge\n", "from helper import big_endian_to_int\n", + "\n", "p1_raw = bytes.fromhex(\"cbaa648dbfe734646ce958e2f14a874149fae4010fdeabde4bae6a732537fd91\")\n", "p2_raw = bytes.fromhex(\"e79c4eb45764bd015542f6779cc70fef44b7a2432f839264768288efab886291\")\n", "p1 = S256Point.parse(p1_raw)\n", @@ -547,18 +677,19 @@ "msg1 = bytes.fromhex(\"1a84547db188f0b1d2c9f0beac230afebbd5e6e6c1a46fc69841815194bf8612\")\n", "msg2 = bytes.fromhex(\"af1c325abcb0cced3a4166ce67be1db659ae1dd574fe49b0f2941d8d4882d62c\")\n", "# define s as the s_i sum (make sure to mod by N)\n", - "\n", + "s = (sig1.s + sig2.s) % N\n", "# define r as the signatures' r sum\n", - "\n", + "r = sig1.r + sig2.r\n", "# create the commitments: R_i||P_i||m_i\n", - "\n", - "\n", + "commitment_1 = sig1.r.xonly() + p1.xonly() + msg1\n", + "commitment_2 = sig2.r.xonly() + p2.xonly() + msg2\n", "# define the h's as the hash_challenge of the commitment as a big endian integer\n", - "\n", - "\n", + "h1 = big_endian_to_int(hash_challenge(commitment_1))\n", + "h2 = big_endian_to_int(hash_challenge(commitment_2))\n", "# compute the sum of the h_i P_i's\n", - "\n", - "# check that sG=R+h\n" + "h = h1*p1 + h2*p2\n", + "# check that sG=R+h\n", + "print(s*G == r+h)" ] }, { @@ -1747,7 +1878,25 @@ ] } ], - "metadata": {}, + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.5" + } + }, "nbformat": 4, "nbformat_minor": 5 }