From b6d44d01a36c902d2d6b58dc461688fd1edca12d Mon Sep 17 00:00:00 2001 From: Brian Jeffreys Date: Mon, 15 Jan 2024 16:30:54 -0600 Subject: [PATCH] Chapter 1 completed --- code-ch01/Chapter1.ipynb | 349 ++++++++++++++++++++++++++++++++++----- code-ch01/ecc.py | 16 +- 2 files changed, 314 insertions(+), 51 deletions(-) diff --git a/code-ch01/Chapter1.ipynb b/code-ch01/Chapter1.ipynb index f8055f46..07ce5b12 100644 --- a/code-ch01/Chapter1.ipynb +++ b/code-ch01/Chapter1.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -19,9 +19,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n", + "True\n" + ] + } + ], "source": [ "from ecc import FieldElement\n", "a = FieldElement(7, 13)\n", @@ -43,9 +52,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.006s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 1\n", "\n", @@ -55,18 +76,34 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + } + ], "source": [ "print(7 % 3)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "12\n" + ] + } + ], "source": [ "print(-27 % 13)" ] @@ -87,25 +124,54 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "44 +f 33 = 20\n", + "9 -f 29 = 37\n", + "17 +f 42 +f 49 = 51\n", + "52 -f 30 -f 38 = 41\n" + ] + } + ], "source": [ "# Exercise 2\n", "\n", "# remember that % is the modulo operator\n", + "# Define our prime for the finite field\n", "prime = 57\n", - "# 44+33\n", - "# 9-29\n", - "# 17+42+49\n", - "# 52-30-38" + "\n", + "# We will need the field arithmetic functions\n", + "def field_add(a, b, prime):\n", + " return (a + b) % prime\n", + "\n", + "def field_sub(a, b, prime):\n", + " return (a - b) % prime\n", + "\n", + "# Calculate and print the results\n", + "print(f\"44 +f 33 = {field_add(44, 33, prime)}\")\n", + "print(f\"9 -f 29 = {field_sub(9, 29, prime)}\")\n", + "print(f\"17 +f 42 +f 49 = {field_add(field_add(17, 42, prime), 49, prime)}\")\n", + "print(f\"52 -f 30 -f 38 = {field_sub(field_sub(52, 30, prime), 38, prime)}\")\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "from ecc import FieldElement\n", "a = FieldElement(7, 13)\n", @@ -127,9 +193,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.002s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 3\n", "\n", @@ -152,17 +230,40 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "95 * 45 * 31 in F97 = 23\n", + "17 * 13 * 19 * 44 in F97 = 68\n", + "12^7 * 77^49 in F97 = 63\n" + ] + } + ], "source": [ "# Exercise 4\n", "\n", + "# Define the prime for finite field F97\n", "prime = 97\n", "\n", + "# Defining the finite field multiplication and exponentiation operations that we need\n", + "def field_multiply(a, b, prime):\n", + " return (a * b) % prime\n", + "\n", + "def field_pow(base, exponent, prime):\n", + " return pow(base, exponent, prime)\n", + "\n", + "# Calculate and print the results:\n", + "\n", "# 95*45*31\n", + "print(f\"95 * 45 * 31 in F97 = {field_multiply(field_multiply(95, 45, prime), 31, prime)}\")\n", "# 17*13*19*44\n", - "# 12**7*77**49" + "print(f\"17 * 13 * 19 * 44 in F97 = {field_multiply(field_multiply(field_multiply(17, 13, prime), 19, prime), 44, prime)}\")\n", + "# 12**7*77**49\n", + "print(f\"12^7 * 77^49 in F97 = {field_multiply(field_pow(12, 7, prime), field_pow(77, 49, prime), prime)}\")" ] }, { @@ -180,25 +281,68 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "k = 0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n", + "k = 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 2: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 6: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 7: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 8: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 9: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 10: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 11: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 12: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 13: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 14: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 15: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 16: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 17: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n", + "k = 18: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n" + ] + } + ], "source": [ "# Exercise 5\n", "\n", + "# the output for each k in F 19 is the same and is a sorted list of all numbers from 0 to 18. \n", + "# This is because multiplication in a finite field of a prime order (like 19) with any element of the field\n", + "# (except 0) will produce all other elements of the field in some order. Since we are ​​sorting the results, \n", + "# the output for each k (except when k = 0) is the sequence of numbers from 0 to 18, regardless of the value of k.\n", + "# k=0 is a special case where all products are 0, as 0 times any number is 0.\n", + "\n", "prime = 19\n", - "k = 1 # 3, 7, 13 and 18 are the other possibilities\n", + "# k = 1 \n", + "# 3, 7, 13 and 18 are the other possibilities\n", "# loop through all possible k's 0 up to prime-1\n", "# calculate k*iterator % prime\n", "\n", - "# Hint - sort!" + "for k in range(prime):\n", + " results = sorted([k * i % prime for i in range(prime)])\n", + " print(f\"k = {k}: {results}\")\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "from ecc import FieldElement\n", "a = FieldElement(3, 13)\n", @@ -220,9 +364,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ".\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.003s\n", + "\n", + "OK\n" + ] + } + ], "source": [ "# Exercise 6\n", "\n", @@ -232,9 +388,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "from ecc import FieldElement\n", "a = FieldElement(3, 13)\n", @@ -255,13 +419,35 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "For p = 7, the set is: [1, 1, 1, 1, 1, 1]\n", + "For p = 11, the set is: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n", + "For p = 17, the set is: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n", + "For p = 31, the set is: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n", + "For p = 43, the set is: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n" + ] + } + ], "source": [ "# Exercise 7\n", "\n", - "primes = [7, 11, 17, 31, 43]" + "primes = [7, 11, 17, 31, 43]\n", + "\n", + "def calculate_set_for_prime(prime):\n", + " return [pow(i, prime - 1, prime) for i in range(1, prime)]\n", + "\n", + "# Calculate and store the sets for each prime\n", + "sets_for_primes = {p: calculate_set_for_prime(p) for p in primes}\n", + "\n", + "# Print the results\n", + "for p in sets_for_primes:\n", + " print(f\"For p = {p}, the set is: {sets_for_primes[p]}\")" ] }, { @@ -279,15 +465,40 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3/24 in F31 = 4\n", + "17^-3 in F31 = 29\n", + "4^-4 * 11 in F31 = 6\n" + ] + } + ], "source": [ "# Exercise 8\n", "\n", - "# 3/24\n", + "# Create FieldElement objects for the calculations in F31\n", + "a = FieldElement(3, 31)\n", + "b = FieldElement(24, 31)\n", + "c = FieldElement(17, 31)\n", + "d = FieldElement(4, 31)\n", + "e = FieldElement(11, 31)\n", + "\n", + "# Now we can use the overloaded methods in our FieldElement class\n", + " # 3/24\n", + "result1 = a / b \n", "# 17**-3\n", - "# 4**-4*11" + "result2 = c ** -3\n", + "# 4**-4*11\n", + "result3 = (d ** -4) * e \n", + "\n", + "print(f\"3/24 in F31 = {result1}\")\n", + "print(f\"17^-3 in F31 = {result2}\")\n", + "print(f\"4^-4 * 11 in F31 = {result3}\")\n" ] }, { @@ -305,9 +516,31 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "E\n", + "======================================================================\n", + "ERROR: test_div (ecc.FieldElementTest)\n", + "----------------------------------------------------------------------\n", + "Traceback (most recent call last):\n", + " File \"/Users/bjeffreys/Projects/ProgrammingBitcoin/programmingbitcoin/code-ch01/ecc.py\", line 113, in test_div\n", + " self.assertEqual(a / b, FieldElement(4, 31))\n", + " File \"/Users/bjeffreys/Projects/ProgrammingBitcoin/programmingbitcoin/code-ch01/ecc.py\", line 69, in __truediv__\n", + " raise NotImplementedError\n", + "NotImplementedError\n", + "\n", + "----------------------------------------------------------------------\n", + "Ran 1 test in 0.004s\n", + "\n", + "FAILED (errors=1)\n" + ] + } + ], "source": [ "# Exercise 9\n", "\n", @@ -317,9 +550,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], "source": [ "from ecc import FieldElement\n", "a = FieldElement(7, 13)\n", @@ -328,7 +569,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.8.18" + } + }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/code-ch01/ecc.py b/code-ch01/ecc.py index b5bf616e..5315412c 100644 --- a/code-ch01/ecc.py +++ b/code-ch01/ecc.py @@ -22,8 +22,8 @@ def __eq__(self, other): # end::source1[] def __ne__(self, other): - # this should be the inverse of the == operator - raise NotImplementedError + # We can call the __eq__ method and negate the result. + return not self.__eq__(other) # tag::source2[] def __add__(self, other): @@ -39,15 +39,17 @@ def __sub__(self, other): # self.num and other.num are the actual values # self.prime is what we need to mod against # We return an element of the same class - raise NotImplementedError - + num = (self.num - other.num) % self.prime + return self.__class__(num, self.prime) + def __mul__(self, other): if self.prime != other.prime: raise TypeError('Cannot multiply two numbers in different Fields') # self.num and other.num are the actual values # self.prime is what we need to mod against # We return an element of the same class - raise NotImplementedError + num = (self.num * other.num) % self.prime + return self.__class__(num, self.prime) # tag::source3[] def __pow__(self, exponent): @@ -64,7 +66,9 @@ def __truediv__(self, other): # this means: # 1/n == pow(n, p-2, p) # We return an element of the same class - raise NotImplementedError + # Use Fermat's Little Theorem for the inverse: other.num**(prime-2) is the modular inverse of other.num + num = (self.num * pow(other.num, self.prime - 2, self.prime)) % self.prime + return self.__class__(num, self.prime) class FieldElementTest(TestCase):