diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..26d33521
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml
new file mode 100644
index 00000000..a110605c
--- /dev/null
+++ b/.idea/dbnavigator.xml
@@ -0,0 +1,457 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..0b59a6af
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 00000000..105ce2da
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..184fcba9
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..97742c21
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/programmingbitcoin.iml b/.idea/programmingbitcoin.iml
new file mode 100644
index 00000000..8e5446ac
--- /dev/null
+++ b/.idea/programmingbitcoin.iml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..94a25f7f
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/code-ch01/Chapter1.ipynb b/code-ch01/Chapter1.ipynb
index f8055f46..3f4e7a5a 100644
--- a/code-ch01/Chapter1.ipynb
+++ b/code-ch01/Chapter1.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 45,
"metadata": {},
"outputs": [],
"source": [
@@ -19,9 +19,18 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 46,
"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,30 +52,56 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 47,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.001s\n",
+ "\n",
+ "OK\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 1\n",
- "\n",
- "reload(ecc)\n",
"run(ecc.FieldElementTest(\"test_ne\"))"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 48,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "1\n"
+ ]
+ }
+ ],
"source": [
"print(7 % 3)"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 49,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "12\n"
+ ]
+ }
+ ],
"source": [
"print(-27 % 13)"
]
@@ -87,25 +122,49 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 50,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "FieldElement_57(20)\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 2\n",
"\n",
"# remember that % is the modulo operator\n",
"prime = 57\n",
"# 44+33\n",
+ "a = ecc.FieldElement(44, 57)\n",
+ "b = ecc.FieldElement(33, 57)\n",
+ "print(a.__add__(b))\n",
"# 9-29\n",
"# 17+42+49\n",
"# 52-30-38"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": []
+ },
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 51,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "True\n"
+ ]
+ }
+ ],
"source": [
"from ecc import FieldElement\n",
"a = FieldElement(7, 13)\n",
@@ -127,9 +186,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 52,
"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,13 +223,33 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 67,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.002s\n",
+ "\n",
+ "OK\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "final is FieldElement_31(22)\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 4\n",
"\n",
- "prime = 97\n",
+ "# prime = 97\n",
+ "run(ecc.FieldElementTest(\"test_mul\"))\n",
"\n",
"# 95*45*31\n",
"# 17*13*19*44\n",
@@ -180,9 +271,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 81,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]\n",
+ "[0, 3, 6, 9, 12, 15, 18, 2, 5, 8, 11, 14, 17, 1, 4, 7, 10, 13, 16]\n",
+ "[0, 7, 14, 2, 9, 16, 4, 11, 18, 6, 13, 1, 8, 15, 3, 10, 17, 5, 12]\n",
+ "[0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 16, 10, 4, 17, 11, 5, 18, 12, 6]\n",
+ "[0, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 5\n",
"\n",
@@ -190,15 +293,25 @@
"k = 1 # 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",
+ "for k in (1, 3, 7, 13, 18):\n",
+ " print([k * i % prime for i in range(prime)])\n",
"# Hint - sort!"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 65,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "final is FieldElement_13(10)\n",
+ "True\n"
+ ]
+ }
+ ],
"source": [
"from ecc import FieldElement\n",
"a = FieldElement(3, 13)\n",
@@ -220,9 +333,28 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 63,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.001s\n",
+ "\n",
+ "OK\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "final is FieldElement_31(22)\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 6\n",
"\n",
@@ -232,9 +364,17 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 64,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "True\n"
+ ]
+ }
+ ],
"source": [
"from ecc import FieldElement\n",
"a = FieldElement(3, 13)\n",
@@ -255,13 +395,27 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 84,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[1, 1, 1, 1, 1, 1]\n",
+ "[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n",
+ "[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n",
+ "[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",
+ "for prime in (7, 11, 17, 31):\n",
+ " print([pow(i, prime - 1, prime) for i in range(1, prime)])\n"
]
},
{
@@ -279,15 +433,30 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 85,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "4\n",
+ "29\n",
+ "13\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 8\n",
"\n",
"# 3/24\n",
"# 17**-3\n",
- "# 4**-4*11"
+ "# 4**-4*11\n",
+ "prime = 31\n",
+ "print(3 * pow(24, prime - 2, prime) % prime)\n",
+ "print(pow(17, prime - 4, prime))\n",
+ "print(pow(4, prime - 5, prime) * 11 % prime)\n",
+ "\n"
]
},
{
@@ -305,9 +474,28 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 80,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.001s\n",
+ "\n",
+ "OK\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "final is FieldElement_31(13)\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 9\n",
"\n",
@@ -317,9 +505,17 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 61,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "True\n"
+ ]
+ }
+ ],
"source": [
"from ecc import FieldElement\n",
"a = FieldElement(7, 13)\n",
@@ -328,7 +524,28 @@
]
}
],
- "metadata": {},
+ "metadata": {
+ "interpreter": {
+ "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49"
+ },
+ "kernelspec": {
+ "display_name": "Python 3.10.2 64-bit",
+ "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.10.2"
+ }
+ },
"nbformat": 4,
"nbformat_minor": 2
}
diff --git a/code-ch01/ecc.py b/code-ch01/ecc.py
index b5bf616e..174ea298 100644
--- a/code-ch01/ecc.py
+++ b/code-ch01/ecc.py
@@ -22,8 +22,9 @@ def __eq__(self, other):
# end::source1[]
def __ne__(self, other):
- # this should be the inverse of the == operator
- raise NotImplementedError
+ if other is None:
+ return True
+ return self.num != other.num or self.prime != other.prime
# tag::source2[]
def __add__(self, other):
@@ -36,18 +37,20 @@ def __add__(self, other):
def __sub__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot subtract 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)
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
+ original = self.num
+ counter = 1
+ while counter < other.num:
+ original = original + self.num
+ counter+=1
+ final = original % self.prime
+ print(f'final is {self.__class__(final, self.prime)}')
+ return self.__class__(final, self.prime)
# tag::source3[]
def __pow__(self, exponent):
@@ -59,12 +62,8 @@ def __pow__(self, exponent):
def __truediv__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot divide two numbers in different Fields')
- # use fermat's little theorem:
- # self.num**(p-1) % p == 1
- # this means:
- # 1/n == pow(n, p-2, p)
- # We return an element of the same class
- raise NotImplementedError
+ num = self.num * pow(other.num, self.prime - 2, self.prime) % self.prime
+ return self.__class__(num, self.prime)
class FieldElementTest(TestCase):
diff --git a/code-ch03/Chapter3.ipynb b/code-ch03/Chapter3.ipynb
index bf0ffe20..75c1628c 100644
--- a/code-ch03/Chapter3.ipynb
+++ b/code-ch03/Chapter3.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -30,24 +30,64 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 13,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "False\n",
+ "False\n",
+ "False\n",
+ "False\n",
+ "False\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 1\n",
+ "is_on_curve = lambda y, x: y**2 == x**3 + (a*x) + b\n",
"\n",
"prime = 223\n",
- "a = FieldElement(0, prime)\n",
- "b = FieldElement(7, prime)\n",
+ "\n",
+ "a = FieldElement(192, prime)\n",
+ "b = FieldElement(105, prime)\n",
+ "print(is_on_curve(a, b))\n",
+ "\n",
+ "a = FieldElement(17, prime)\n",
+ "b = FieldElement(56, prime)\n",
+ "print(is_on_curve(a, b))\n",
+ "\n",
+ "\n",
+ "a = FieldElement(200, prime)\n",
+ "b = FieldElement(119, prime)\n",
+ "print(is_on_curve(a, b))\n",
+ "\n",
+ "a = FieldElement(1, prime)\n",
+ "b = FieldElement(193, prime)\n",
+ "print(is_on_curve(a, b))\n",
+ "\n",
+ "a = FieldElement(42, prime)\n",
+ "b = FieldElement(99, prime)\n",
+ "print(is_on_curve(a, b))\n",
"\n",
"# (192,105), (17,56), (200,119), (1,193), (42,99)"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 14,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Point(192,105)_0_7 FieldElement(223)\n"
+ ]
+ }
+ ],
"source": [
"from ecc import FieldElement, Point\n",
"a = FieldElement(num=0, prime=223)\n",
@@ -396,7 +436,28 @@
]
}
],
- "metadata": {},
+ "metadata": {
+ "interpreter": {
+ "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49"
+ },
+ "kernelspec": {
+ "display_name": "Python 3.10.2 64-bit",
+ "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.10.2"
+ }
+ },
"nbformat": 4,
"nbformat_minor": 2
}
diff --git a/code-ch04/Chapter4.ipynb b/code-ch04/Chapter4.ipynb
index 68289b9f..8c8d3dfa 100644
--- a/code-ch04/Chapter4.ipynb
+++ b/code-ch04/Chapter4.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -30,16 +30,29 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 2,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "04ffe558e388852f0120e46af2d1b370f85854a8eb0841811ece0e3e03d282d57c315dc72890a4f10a1481c031b03b351b0dc79901ca18a00cf009dbdb157a1d10\n",
+ "04027f3da1918455e03c46f659266a1bb5204e959db7364d2f473bdf8f0a13cc9dff87647fd023c13b4a4994f17691895806e1b40b57f4fd22581a4f46851f3b06\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 1\n",
"\n",
"from ecc import PrivateKey\n",
- "\n",
"# 5000\n",
+ "private_key = PrivateKey(5000)\n",
+ "print(private_key.point.sec(compressed=False).hex())\n",
"# 2018**5\n",
+ "private_key = PrivateKey(2018**5)\n",
+ "print(private_key.point.sec(compressed=False).hex())\n",
+ "\n",
"# 0xdeadbeef12345\n",
"# privatekey.point is the public key for a private key"
]
@@ -59,16 +72,29 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 6,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0357a4f368868a8a6d572991e484e664810ff14c05c0fa023275251151fe0e53d1\n",
+ "02933ec2d2b111b92737ec12f1c5d20f3233a0ad21cd8b36d0bca7a0cfa5cb8701\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 2\n",
"\n",
"from ecc import PrivateKey\n",
"\n",
"# 5001\n",
+ "private_key = PrivateKey(5001)\n",
+ "print(private_key.point.sec(compressed=True).hex())\n",
"# 2019**5\n",
+ "private_key = PrivateKey(2019**5)\n",
+ "print(private_key.point.sec(compressed=True).hex())\n",
"# 0xdeadbeef54321"
]
},
@@ -91,16 +117,25 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 7,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "3045022037206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c60221008ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 3\n",
"\n",
"from ecc import Signature\n",
"\n",
"r = 0x37206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6\n",
- "s = 0x8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec"
+ "s = 0x8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec\n",
+ "print(Signature(r,s).der().hex())"
]
},
{
@@ -118,17 +153,30 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "9MA8fRQrT4u8Zj8ZRd6MAiiyaxb2Y1CMpvVkHQu5hVM6\n",
+ "4fE3H2E6XMp4SsxtwinF7w9a34ooUrwWe4WsW1458Pd\n",
+ "EQJsjkd6JaGwxrjEhfeqPenqHwrBmPQZjJGNSCHBkcF7\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 4\n",
"\n",
"from helper import encode_base58\n",
"\n",
"# 7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d\n",
- "# eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c\n",
- "# c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6"
+ "print(encode_base58(bytes.fromhex('7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d')))\n",
+ "#eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c\n",
+ "print(encode_base58(bytes.fromhex('eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c')))\n",
+ "# c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6\n",
+ "print(encode_base58(bytes.fromhex('c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6')))\n"
]
},
{
@@ -146,17 +194,31 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 4,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "mmTPbXQFxboEtNRkwfh6K51jvdtHLxGeMA\n",
+ "muC5h84joNRi2Aiw1FzvS9RkPEXGwaoxGE\n",
+ "mg2MoJnGVV7JKFEawp6zKrKL6gDsoXJJRA\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 5\n",
"\n",
"from ecc import PrivateKey\n",
"\n",
"# 5002 (use uncompressed SEC, on testnet)\n",
+ "print(PrivateKey(5002).point.address(compressed=False, testnet=True))\n",
"# 2020**5 (use compressed SEC, on testnet)\n",
- "# 0x12345deadbeef (use compressed SEC on mainnet)"
+ "print(PrivateKey(2020**5).point.address(compressed=False, testnet=True))\n",
+ "# 0x12345deadbeef (use compressed SEC on mainnet)\n",
+ "print(PrivateKey(0x12345deadbeef).point.address(compressed=False, testnet=True))\n",
+ "\n"
]
},
{
@@ -174,13 +236,26 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 19,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN8rFTv2sfUK\n",
+ "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjpWAxgzczjbCwxic\n",
+ "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgiuQJv1h8Ytr2S53a\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 6\n",
"\n",
"from ecc import PrivateKey\n",
+ "print(PrivateKey(5003).wif(compressed=True, testnet=True))\n",
+ "print(PrivateKey(2021**5).wif(compressed=False, testnet=True))\n",
+ "print(PrivateKey(0x54321deadbeef).wif(compressed=True, testnet=False))\n",
"\n",
"# 5003\n",
"# 2021**5\n",
@@ -200,9 +275,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 7,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.001s\n",
+ "\n",
+ "OK\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 7\n",
"\n",
@@ -223,9 +310,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 15,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.001s\n",
+ "\n",
+ "OK\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 8\n",
"\n",
@@ -244,9 +343,18 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 17,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "secret: 70828496956310515876653719000055877884684432812736799231962040068791188138200\n",
+ "private key: cSq6WBCbDw6ygiRendBbrpa7gq4Fphi4W9Bp1CT51g35NVvsooc8\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 9\n",
"\n",
@@ -254,14 +362,38 @@
"from helper import hash256, little_endian_to_int\n",
"\n",
"# select a passphrase here, add your email address into the passphrase for security\n",
- "# passphrase = b'your@email.address some secret only you know'\n",
- "# secret = little_endian_to_int(hash256(passphrase))\n",
+ "passphrase = b'email.address some secret only you know'\n",
+ "secret = little_endian_to_int(hash256(passphrase))\n",
+ "print(f'secret: {secret}')\n",
"# create a private key using your secret\n",
- "# print an address from the public point of the private key with testnet=True"
+ "private_key = PrivateKey(secret).wif(compressed=True, testnet=True)\n",
+ "# print an address from the public point of the private key with testnet=True\n",
+ "print(f'private key: {private_key}')"
]
}
],
- "metadata": {},
+ "metadata": {
+ "interpreter": {
+ "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49"
+ },
+ "kernelspec": {
+ "display_name": "Python 3.10.2 64-bit",
+ "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.10.2"
+ }
+ },
"nbformat": 4,
"nbformat_minor": 2
}
diff --git a/code-ch04/helper.py b/code-ch04/helper.py
index 56e1d53e..deb2de10 100644
--- a/code-ch04/helper.py
+++ b/code-ch04/helper.py
@@ -63,17 +63,12 @@ def decode_base58(s):
def little_endian_to_int(b):
- '''little_endian_to_int takes byte sequence as a little-endian number.
- Returns an integer'''
- # use int.from_bytes()
- raise NotImplementedError
+ return int.from_bytes(b, 'little')
def int_to_little_endian(n, length):
- '''endian_to_little_endian takes an integer and returns the little-endian
- byte sequence of length'''
- # use n.to_bytes()
- raise NotImplementedError
+
+ return n.to_bytes(length, 'little')
class HelperTest(TestCase):
diff --git a/code-ch05/Chapter5.ipynb b/code-ch05/Chapter5.ipynb
index 16f1aaf1..a2ebe063 100644
--- a/code-ch05/Chapter5.ipynb
+++ b/code-ch05/Chapter5.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -11,10 +11,7 @@
"# import everything and define a test runner function\n",
"from importlib import reload\n",
"from helper import run\n",
- "import ecc\n",
- "import helper\n",
- "import script\n",
- "import tx"
+ "import ecc\n"
]
},
{
@@ -30,21 +27,41 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 7,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.005s\n",
+ "\n",
+ "OK\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 1\n",
- "\n",
- "reload(tx)\n",
+ "import tx\n",
+ "# reload(tx)\n",
"run(tx.TxTest(\"test_parse_version\"))"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "3045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01 0349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a\n"
+ ]
+ }
+ ],
"source": [
"from io import BytesIO\n",
"from script import Script\n",
@@ -67,9 +84,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 4,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.003s\n",
+ "\n",
+ "OK\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 2\n",
"\n",
@@ -90,9 +119,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 5,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.003s\n",
+ "\n",
+ "OK\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 3\n",
"\n",
@@ -100,6 +141,11 @@
"run(tx.TxTest(\"test_parse_outputs\"))"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": []
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -187,9 +233,35 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 13,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "E\n",
+ "======================================================================\n",
+ "ERROR: test_fee (tx.TxTest)\n",
+ "----------------------------------------------------------------------\n",
+ "Traceback (most recent call last):\n",
+ " File \"/Users/laurenceadams/programming/programmingbitcoin/code-ch05/tx.py\", line 290, in test_fee\n",
+ " self.assertEqual(tx.fee(), 40000)\n",
+ " File \"/Users/laurenceadams/programming/programmingbitcoin/code-ch05/tx.py\", line 138, in fee\n",
+ " tx_in_value += tx_in.value(testnet=self.testnet)\n",
+ " File \"/Users/laurenceadams/programming/programmingbitcoin/code-ch05/tx.py\", line 199, in value\n",
+ " tx = self.fetch_tx(testnet=testnet)\n",
+ " File \"/Users/laurenceadams/programming/programmingbitcoin/code-ch05/tx.py\", line 193, in fetch_tx\n",
+ " return TxFetcher.fetch(self.prev_tx.hex(), testnet=testnet)\n",
+ "AttributeError: type object 'TxFetcher' has no attribute 'fetch'\n",
+ "\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.010s\n",
+ "\n",
+ "FAILED (errors=1)\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 6\n",
"\n",
@@ -198,7 +270,28 @@
]
}
],
- "metadata": {},
+ "metadata": {
+ "interpreter": {
+ "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
+ },
+ "kernelspec": {
+ "display_name": "Python 3.8.9 64-bit",
+ "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.9"
+ }
+ },
"nbformat": 4,
"nbformat_minor": 2
}
diff --git a/code-ch05/tx.py b/code-ch05/tx.py
index 276e866c..01179375 100644
--- a/code-ch05/tx.py
+++ b/code-ch05/tx.py
@@ -2,7 +2,7 @@
from unittest import TestCase
import json
-import requests
+# import requests
from helper import (
encode_varint,
@@ -25,27 +25,27 @@ def get_url(cls, testnet=False):
else:
return 'https://blockstream.info/api/'
- @classmethod
- def fetch(cls, tx_id, testnet=False, fresh=False):
- if fresh or (tx_id not in cls.cache):
- url = '{}/tx/{}/hex'.format(cls.get_url(testnet), tx_id)
- response = requests.get(url)
- try:
- raw = bytes.fromhex(response.text.strip())
- except ValueError:
- raise ValueError('unexpected response: {}'.format(response.text))
- if raw[4] == 0:
- raw = raw[:4] + raw[6:]
- tx = Tx.parse(BytesIO(raw), testnet=testnet)
- tx.locktime = little_endian_to_int(raw[-4:])
- else:
- tx = Tx.parse(BytesIO(raw), testnet=testnet)
- if tx.id() != tx_id: # <1>
- raise ValueError('not the same id: {} vs {}'.format(tx.id(),
- tx_id))
- cls.cache[tx_id] = tx
- cls.cache[tx_id].testnet = testnet
- return cls.cache[tx_id]
+ # @classmethod
+ # def fetch(cls, tx_id, testnet=False, fresh=False):
+ # if fresh or (tx_id not in cls.cache):
+ # url = '{}/tx/{}/hex'.format(cls.get_url(testnet), tx_id)
+ # response = requests.get(url)
+ # try:
+ # raw = bytes.fromhex(response.text.strip())
+ # except ValueError:
+ # raise ValueError('unexpected response: {}'.format(response.text))
+ # if raw[4] == 0:
+ # raw = raw[:4] + raw[6:]
+ # tx = Tx.parse(BytesIO(raw), testnet=testnet)
+ # tx.locktime = little_endian_to_int(raw[-4:])
+ # else:
+ # tx = Tx.parse(BytesIO(raw), testnet=testnet)
+ # if tx.id() != tx_id: # <1>
+ # raise ValueError('not the same id: {} vs {}'.format(tx.id(),
+ # tx_id))
+ # cls.cache[tx_id] = tx
+ # cls.cache[tx_id].testnet = testnet
+ # return cls.cache[tx_id]
# end::source7[]
@classmethod
@@ -105,18 +105,16 @@ def hash(self): # <4>
@classmethod
def parse(cls, s, testnet=False):
- '''Takes a byte stream and parses the transaction at the start
- return a Tx object
- '''
- # s.read(n) will return n bytes
- # version is an integer in 4 bytes, little-endian
- # num_inputs is a varint, use read_varint(s)
- # parse num_inputs number of TxIns
- # num_outputs is a varint, use read_varint(s)
- # parse num_outputs number of TxOuts
- # locktime is an integer in 4 bytes, little-endian
- # return an instance of the class (see __init__ for args)
- raise NotImplementedError
+ version = little_endian_to_int(s.read(4))
+ num_inputs = read_varint(s)
+ inputs = []
+ for _ in range(num_inputs):
+ inputs.append(TxIn.parse(s))
+ num_outputs = read_varint(s)
+ outputs = []
+ for _ in range(num_outputs):
+ outputs.append(TxOut.parse(s))
+ return cls(version, inputs, outputs, None, testnet=testnet)
# tag::source6[]
def serialize(self):
@@ -134,11 +132,18 @@ def serialize(self):
def fee(self):
'''Returns the fee of this transaction in satoshi'''
+ tx_in_value = 0
+ tx_out_amount = 0
+ for tx_in in self.tx_ins:
+ tx_in_value += tx_in.value(testnet=self.testnet)
+ for tx_out in self.tx_outs:
+ tx_out_amount += tx_out.amount
+ return tx_in_value - tx_out_amount
# initialize input sum and output sum
# use TxIn.value() to sum up the input amounts
# use TxOut.amount to sum up the output amounts
# fee is input sum - output sum
- raise NotImplementedError
+
# tag::source2[]
@@ -161,15 +166,16 @@ def __repr__(self):
@classmethod
def parse(cls, s):
- '''Takes a byte stream and parses the tx_input at the start
- return a TxIn object
- '''
# prev_tx is 32 bytes, little endian
# prev_index is an integer in 4 bytes, little endian
# use Script.parse to get the ScriptSig
# sequence is an integer in 4 bytes, little-endian
# return an instance of the class (see __init__ for args)
- raise NotImplementedError
+ prev_tx = s.read(32)[::-1]
+ prev_index = little_endian_to_int(s.read(4))
+ script_sig = Script.parse(s)
+ sequence = little_endian_to_int(s.read(4))
+ return cls(prev_tx, prev_index, script_sig, sequence)
# tag::source5[]
def serialize(self):
@@ -178,6 +184,7 @@ def serialize(self):
result += int_to_little_endian(self.prev_index, 4)
result += self.script_sig.serialize()
result += int_to_little_endian(self.sequence, 4)
+ print(result)
return result
# end::source5[]
@@ -217,10 +224,9 @@ def parse(cls, s):
'''Takes a byte stream and parses the tx_output at the start
return a TxOut object
'''
- # amount is an integer in 8 bytes, little endian
- # use Script.parse to get the ScriptPubKey
- # return an instance of the class (see __init__ for args)
- raise NotImplementedError
+ amount = little_endian_to_int(s.read(8))
+ script_pubkey = Script.parse(s)
+ return cls(amount, script_pubkey)
# tag::source4[]
def serialize(self): # <1>
diff --git a/code-ch06/Chapter6.ipynb b/code-ch06/Chapter6.ipynb
index 13f4830e..eae7bbfc 100644
--- a/code-ch06/Chapter6.ipynb
+++ b/code-ch06/Chapter6.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
@@ -28,9 +28,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 17,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.001s\n",
+ "\n",
+ "OK\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 1\n",
"\n",
@@ -40,9 +52,23 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 18,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "ename": "NotImplementedError",
+ "evalue": "",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mNotImplementedError\u001b[0m Traceback (most recent call last)",
+ "\u001b[1;32m/Users/laurenceadams/programming/programmingbitcoin/code-ch06/Chapter6.ipynb Cell 4'\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 6\u001b[0m script_sig \u001b[39m=\u001b[39m Script([sig])\n\u001b[1;32m 7\u001b[0m combined_script \u001b[39m=\u001b[39m script_sig \u001b[39m+\u001b[39m script_pubkey\n\u001b[0;32m----> 8\u001b[0m \u001b[39mprint\u001b[39m(combined_script\u001b[39m.\u001b[39;49mevaluate(z))\n",
+ "File \u001b[0;32m~/programming/programmingbitcoin/code-ch06/script.py:123\u001b[0m, in \u001b[0;36mScript.evaluate\u001b[0;34m(self, z)\u001b[0m\n\u001b[1;32m 121\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mFalse\u001b[39;00m\n\u001b[1;32m 122\u001b[0m \u001b[39melif\u001b[39;00m cmd \u001b[39min\u001b[39;00m (\u001b[39m172\u001b[39m, \u001b[39m173\u001b[39m, \u001b[39m174\u001b[39m, \u001b[39m175\u001b[39m): \u001b[39m# <6>\u001b[39;00m\n\u001b[0;32m--> 123\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m operation(stack, z):\n\u001b[1;32m 124\u001b[0m LOGGER\u001b[39m.\u001b[39minfo(\u001b[39m'\u001b[39m\u001b[39mbad op: \u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m'\u001b[39m\u001b[39m.\u001b[39mformat(OP_CODE_NAMES[cmd]))\n\u001b[1;32m 125\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mFalse\u001b[39;00m\n",
+ "File \u001b[0;32m~/programming/programmingbitcoin/code-ch06/op.py:676\u001b[0m, in \u001b[0;36mop_checksig\u001b[0;34m(stack, z)\u001b[0m\n\u001b[1;32m 668\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mop_checksig\u001b[39m(stack, z):\n\u001b[1;32m 669\u001b[0m \u001b[39m# check that there are at least 2 elements on the stack\u001b[39;00m\n\u001b[1;32m 670\u001b[0m \u001b[39m# the top element of the stack is the SEC pubkey\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 674\u001b[0m \u001b[39m# verify the signature using S256Point.verify()\u001b[39;00m\n\u001b[1;32m 675\u001b[0m \u001b[39m# push an encoded 1 or 0 depending on whether the signature verified\u001b[39;00m\n\u001b[0;32m--> 676\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mNotImplementedError\u001b[39;00m\n",
+ "\u001b[0;31mNotImplementedError\u001b[0m: "
+ ]
+ }
+ ],
"source": [
"from script import Script\n",
"z = 0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d\n",
@@ -67,9 +93,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 20,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.149s\n",
+ "\n",
+ "OK\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 2\n",
"\n",
@@ -96,16 +134,31 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 23,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "ename": "TypeError",
+ "evalue": "'function' object is not subscriptable",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
+ "\u001b[1;32m/Users/laurenceadams/programming/programmingbitcoin/code-ch06/Chapter6.ipynb Cell 8'\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 6\u001b[0m script_sig \u001b[39m=\u001b[39m Script([OP_CODE_FUNCTIONS[\u001b[39m118\u001b[39m],OP_CODE_FUNCTIONS[\u001b[39m118\u001b[39m],OP_CODE_FUNCTIONS[\u001b[39m149\u001b[39m],OP_CODE_FUNCTIONS[\u001b[39m147\u001b[39m], OP_CODE_FUNCTIONS[\u001b[39m86\u001b[39m], OP_CODE_FUNCTIONS[\u001b[39m135\u001b[39m]]) \u001b[39m# FILL THIS IN\u001b[39;00m\n\u001b[1;32m 7\u001b[0m combined_script \u001b[39m=\u001b[39m script_sig \u001b[39m+\u001b[39m script_pubkey\n\u001b[0;32m----> 8\u001b[0m \u001b[39mprint\u001b[39m(combined_script\u001b[39m.\u001b[39;49mevaluate(\u001b[39m0\u001b[39;49m))\n",
+ "File \u001b[0;32m~/programming/programmingbitcoin/code-ch06/script.py:127\u001b[0m, in \u001b[0;36mScript.evaluate\u001b[0;34m(self, z)\u001b[0m\n\u001b[1;32m 125\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mFalse\u001b[39;00m\n\u001b[1;32m 126\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m--> 127\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m operation(stack):\n\u001b[1;32m 128\u001b[0m LOGGER\u001b[39m.\u001b[39minfo(\u001b[39m'\u001b[39m\u001b[39mbad op: \u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m'\u001b[39m\u001b[39m.\u001b[39mformat(OP_CODE_NAMES[cmd]))\n\u001b[1;32m 129\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mFalse\u001b[39;00m\n",
+ "File \u001b[0;32m~/programming/programmingbitcoin/code-ch06/op.py:479\u001b[0m, in \u001b[0;36mop_mul\u001b[0;34m(stack)\u001b[0m\n\u001b[1;32m 477\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mlen\u001b[39m(stack) \u001b[39m<\u001b[39m \u001b[39m2\u001b[39m:\n\u001b[1;32m 478\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mFalse\u001b[39;00m\n\u001b[0;32m--> 479\u001b[0m element1 \u001b[39m=\u001b[39m decode_num(stack\u001b[39m.\u001b[39;49mpop())\n\u001b[1;32m 480\u001b[0m element2 \u001b[39m=\u001b[39m decode_num(stack\u001b[39m.\u001b[39mpop())\n\u001b[1;32m 481\u001b[0m stack\u001b[39m.\u001b[39mappend(encode_num(element2 \u001b[39m*\u001b[39m element1))\n",
+ "File \u001b[0;32m~/programming/programmingbitcoin/code-ch06/op.py:39\u001b[0m, in \u001b[0;36mdecode_num\u001b[0;34m(element)\u001b[0m\n\u001b[1;32m 37\u001b[0m \u001b[39mif\u001b[39;00m element \u001b[39m==\u001b[39m \u001b[39mb\u001b[39m\u001b[39m'\u001b[39m\u001b[39m'\u001b[39m:\n\u001b[1;32m 38\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39m0\u001b[39m\n\u001b[0;32m---> 39\u001b[0m big_endian \u001b[39m=\u001b[39m element[::\u001b[39m-\u001b[39;49m\u001b[39m1\u001b[39;49m]\n\u001b[1;32m 40\u001b[0m \u001b[39mif\u001b[39;00m big_endian[\u001b[39m0\u001b[39m] \u001b[39m&\u001b[39m \u001b[39m0x80\u001b[39m:\n\u001b[1;32m 41\u001b[0m negative \u001b[39m=\u001b[39m \u001b[39mTrue\u001b[39;00m\n",
+ "\u001b[0;31mTypeError\u001b[0m: 'function' object is not subscriptable"
+ ]
+ }
+ ],
"source": [
"# Exercise 3\n",
"\n",
"from script import Script\n",
- "\n",
+ "from op import OP_CODE_FUNCTIONS\n",
"script_pubkey = Script([0x76, 0x76, 0x95, 0x93, 0x56, 0x87])\n",
- "script_sig = Script([]) # FILL THIS IN\n",
+ "script_sig = Script([OP_CODE_FUNCTIONS[118],OP_CODE_FUNCTIONS[118],OP_CODE_FUNCTIONS[149],OP_CODE_FUNCTIONS[147], OP_CODE_FUNCTIONS[86], OP_CODE_FUNCTIONS[135]]) # FILL THIS IN\n",
"combined_script = script_sig + script_pubkey\n",
"print(combined_script.evaluate(0))"
]
@@ -147,7 +200,28 @@
]
}
],
- "metadata": {},
+ "metadata": {
+ "interpreter": {
+ "hash": "ddf34bd14ab601ab5ba1afcb9bdcdce15fa952f799e72bd09b477fea761a4e6b"
+ },
+ "kernelspec": {
+ "display_name": "Python 3.10.2 ('venv': venv)",
+ "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.10.2"
+ }
+ },
"nbformat": 4,
"nbformat_minor": 2
}
diff --git a/code-ch06/ecc.py b/code-ch06/ecc.py
index a3c357cd..3596a177 100644
--- a/code-ch06/ecc.py
+++ b/code-ch06/ecc.py
@@ -10,7 +10,7 @@
class FieldElement:
- def __init__(self, num, prime):
+ def __init__(self, num, prime):
if num >= prime or num < 0:
error = 'Num {} not in field range 0 to {}'.format(
num, prime - 1)
diff --git a/code-ch06/op.py b/code-ch06/op.py
index 7c17be32..ad7b8b66 100644
--- a/code-ch06/op.py
+++ b/code-ch06/op.py
@@ -644,10 +644,15 @@ def op_sha256(stack):
def op_hash160(stack):
+ if len(stack) < 1:
+ return False
+ else:
+ stack.append(hash160(stack.pop()))
+ return True
# check that there's at least 1 element on the stack
# pop off the top element from the stack
# push a hash160 of the popped off element to the stack
- raise NotImplementedError
+
# tag::source2[]
@@ -668,7 +673,20 @@ def op_checksig(stack, z):
# parse the serialized pubkey and signature into objects
# verify the signature using S256Point.verify()
# push an encoded 1 or 0 depending on whether the signature verified
- raise NotImplementedError
+ if len(stack) < 2:
+ return False
+ pubkey = stack.pop()
+ der_signature = stack.pop()
+ hash_type = der_signature[:-1]
+
+ parsed_pub_key = S256Point.parse(pubkey)
+ signature = Signature.parse(hash_type)
+
+ if parsed_pub_key.verify(z, signature):
+ stack.append(encode_num(1))
+ else:
+ stack.append(encode_num(0))
+ return True
def op_checksigverify(stack, z):
diff --git a/code-ch07/Chapter7.ipynb b/code-ch07/Chapter7.ipynb
index bb7f7867..238542ef 100644
--- a/code-ch07/Chapter7.ipynb
+++ b/code-ch07/Chapter7.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -19,9 +19,17 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "True\n"
+ ]
+ }
+ ],
"source": [
"from tx import Tx\n",
"from io import BytesIO\n",
@@ -33,9 +41,17 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 4,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "True\n"
+ ]
+ }
+ ],
"source": [
"from ecc import S256Point, Signature\n",
"sec = bytes.fromhex('0349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a')\n",
@@ -48,9 +64,17 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 5,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0x27e0c5994dec7824e56dec6b2fcb342eb7cdb0d0957c2fce9882f715e85d81a6\n"
+ ]
+ }
+ ],
"source": [
"from helper import hash256\n",
"modified_tx = bytes.fromhex('0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000001976a914a802fc56c704ce87c42d7c92eb75e7896bdc41ae88acfeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac1943060001000000')\n",
@@ -61,9 +85,20 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 6,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"from ecc import S256Point, Signature\n",
"sec = bytes.fromhex('0349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a')\n",
@@ -87,9 +122,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 7,
"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",
@@ -110,9 +157,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 9,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.302s\n",
+ "\n",
+ "OK\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 2\n",
"\n",
@@ -122,9 +181,24 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 11,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "tx: cd30a8da777d28ef0e61efe68a9f7c559c1d3e5bcd7b265c850ccb4068598d11\n",
+ "version: 1\n",
+ "tx_ins:\n",
+ "0d6fe5213c0b3291f208cba8bfb59b7476dffacc4e5cb66f6eb20a080843a299:13\n",
+ "tx_outs:\n",
+ "33000000:OP_DUP OP_HASH160 d52ad7ca9b3d096a38e752c2018e6fbc40cdf26f OP_EQUALVERIFY OP_CHECKSIG\n",
+ "10000000:OP_DUP OP_HASH160 507b27411ccf7f16f10297de6cef3f291623eddf OP_EQUALVERIFY OP_CHECKSIG\n",
+ "locktime: 0\n"
+ ]
+ }
+ ],
"source": [
"from helper import decode_base58, SIGHASH_ALL\n",
"from script import p2pkh_script, Script\n",
@@ -147,9 +221,17 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 20,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000006a47304402207db2402a3311a3b845b038885e3dd889c08126a8570f26a844e3e4049c482a11022010178cdca4129eacbeab7c44648bf5ac1f9cac217cd609d216ec2ebc8d242c0a012103935581e52c354cd2f484fe8ed83af7a3097005b2f9c60bff71d35bd795f54b67feffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600\n"
+ ]
+ }
+ ],
"source": [
"from ecc import PrivateKey\n",
"from helper import SIGHASH_ALL\n",
@@ -165,9 +247,17 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 21,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "mn81594PzKZa9K3Jyy1ushpuEzrnTnxhVg\n"
+ ]
+ }
+ ],
"source": [
"from ecc import PrivateKey\n",
"from helper import hash256, little_endian_to_int\n",
@@ -189,14 +279,42 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 9,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ ".\n",
+ "----------------------------------------------------------------------\n",
+ "Ran 1 test in 0.250s\n",
+ "\n",
+ "OK\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "n38rkhz4Wt3EALiXUsT825QqD4sBZjmMMV\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 3\n",
"\n",
"reload(tx)\n",
- "run(tx.TxTest(\"test_sign_input\"))"
+ "run(tx.TxTest(\"test_sign_input\"))\n",
+ "\n",
+ "# Not an Exercise\n",
+ "from ecc import PrivateKey\n",
+ "from helper import hash256, little_endian_to_int\n",
+ "\n",
+ "secret = little_endian_to_int(hash256(b'what who where do we go?'))\n",
+ "private_key = PrivateKey(secret)\n",
+ "print(private_key.point.address(testnet=True))\n",
+ "\n"
]
},
{
@@ -212,9 +330,20 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 15,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "prev_text b'\\x9d\\x98\\x9a\\x0b\\x081Z\\xbd\\xb1\\xbd\\xa8\\xb7\\n\\x05\\x0c\\xec\\xe2R\\xcc\\xae\\x0e\\xceg\\xe6\\xed\\x80=\\x8f|8\\xaaV'\n",
+ "priv \n",
+ "True\n",
+ "010000000156aa387c8f3d80ede667ce0eaecc52e2ec0c050ab7a8bdb1bd5a31080b9a989d010000006b483045022100ece9798beac1bda1b073f18593e662db5138014f7938f22488960eba272d8cfa022046dc69a3a783044effebf73555b89ed3d8bd780a35635472f6ce90165709df0c012103935581e52c354cd2f484fe8ed83af7a3097005b2f9c60bff71d35bd795f54b67ffffffff0270170000000000001976a914ad346f8eb57dee9a37981716e498120ae80e44f788ac90010000000000001976a914ad346f8eb57dee9a37981716e498120ae80e44f788ac00000000\n"
+ ]
+ }
+ ],
"source": [
"# Exercise 4\n",
"\n",
@@ -223,30 +352,46 @@
"from script import p2pkh_script, Script\n",
"from tx import TxIn, TxOut, Tx\n",
"\n",
- "# create 1 TxIn and 2 TxOuts\n",
+ "# get the prev_tx and prev_index from the transaction where you got\n",
+ "# some testnet coins\n",
+ "prev_tx = bytes.fromhex('9d989a0b08315abdb1bda8b70a050cece252ccae0ece67e6ed803d8f7c38aa56')\n",
+ "print(f'prev_text {prev_tx}')\n",
+ "prev_index = 1\n",
"# 1 of the TxOuts should be back to your address\n",
"# the other TxOut should be to this address\n",
"target_address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'\n",
- "\n",
- "# get the private key from the exercise in Chapter 4\n",
- "# change address should be the address generated from Chapter 4\n",
- "\n",
- "# get the prev_tx and prev_index from the transaction where you got\n",
- "# some testnet coins\n",
- "# create a transaction input for the previous transaction with\n",
- "# the default ScriptSig and sequence\n",
- "\n",
"# target amount should be 60% of the output amount\n",
+ "target_amount = .00006000\n",
"# set the fee to some reasonable amount\n",
+ "# change address should be the address generated from Chapter 4\n",
+ "change_address = 'mmgSjGVNNHBqbmM3UjbPySeZFL9NLmcAhd'\n",
"# change amount = amount from the prev tx - target amount - fee\n",
- "\n",
+ "change_amount = .000030820\n",
+ "# get the private key from the exercise in Chapter 4\n",
+ "secret = 8675309\n",
+ "priv = PrivateKey(secret=secret)\n",
+ "print(f'priv {priv}')\n",
+ "# create 1 TxIn and 2 TxOuts\n",
+ "tx_ins = []\n",
+ "# create a transaction input for the previous transaction with\n",
+ "# the default ScriptSig and sequence\n",
+ "tx_ins.append(TxIn(prev_tx, prev_index))\n",
+ "tx_outs = []\n",
+ "h160 = decode_base58(target_address)\n",
+ "script_pubkey = p2pkh_script(h160)\n",
+ "target_satoshis = int(target_amount * 100000000)\n",
"# create a transaction output for the target amount and address\n",
+ "tx_outs.append(TxOut(target_satoshis, script_pubkey))\n",
"# create a transaction output for the change amount and address\n",
+ "change_satoshis = int(change_amount*10000000)\n",
+ "tx_outs.append(TxOut(change_satoshis, script_pubkey))\n",
"# create the transaction object\n",
- "\n",
+ "tx_obj = Tx(1, tx_ins, tx_outs, 0, testnet=True)\n",
"# sign the one input in the transaction object using the private key\n",
+ "print(tx_obj.sign_input(0, priv))\n",
"# print the transaction's serialization in hex\n",
- "# broadcast at http://testnet.blockchain.info/pushtx"
+ "print(tx_obj.serialize().hex())\n",
+ "# broadcast at http://testnet.blockchain.info/pushtx\n"
]
},
{
@@ -299,7 +444,28 @@
]
}
],
- "metadata": {},
+ "metadata": {
+ "interpreter": {
+ "hash": "ddf34bd14ab601ab5ba1afcb9bdcdce15fa952f799e72bd09b477fea761a4e6b"
+ },
+ "kernelspec": {
+ "display_name": "Python 3.10.2 ('venv': venv)",
+ "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.10.2"
+ }
+ },
"nbformat": 4,
"nbformat_minor": 2
}
diff --git a/code-ch07/tx.py b/code-ch07/tx.py
index d3beba9c..df544014 100644
--- a/code-ch07/tx.py
+++ b/code-ch07/tx.py
@@ -162,28 +162,54 @@ def sig_hash(self, input_index):
signed for index input_index'''
# start the serialization with version
# use int_to_little_endian in 4 bytes
+ s = int_to_little_endian(self.version, 4)
# add how many inputs there are using encode_varint
+ s += encode_varint(len(self.tx_ins))
# loop through each input using enumerate, so we have the input index
+ for i, tx_in in enumerate(self.tx_ins):
# if the input index is the one we're signing
- # the previous tx's ScriptPubkey is the ScriptSig
+ if i == input_index:
+ # the previous tx's ScriptPubkey is the ScriptSig
+ s += TxIn(
+ prev_tx=tx_in.prev_tx,
+ prev_index=tx_in.prev_index,
+ script_sig=tx_in.script_pubkey(self.testnet),
+ sequence=tx_in.sequence
+ ).serialize() # add the serialization of the input with the ScriptSig we want
# Otherwise, the ScriptSig is empty
- # add the serialization of the input with the ScriptSig we want
+ else:
+ s += TxIn(
+ prev_tx=tx_in.prev_tx,
+ prev_index=tx_in.prev_index,
+ sequence=tx_in.sequence,
+ ).serialize()# add the serialization of the input with the ScriptSig we want
+
# add how many outputs there are using encode_varint
- # add the serialization of each output
+ s += encode_varint(len(self.tx_outs))
+ for tx_out in self.tx_outs:
+ # add the serialization of each output
+ s += tx_out.serialize()
# add the locktime using int_to_little_endian in 4 bytes
+ s += int_to_little_endian(self.locktime, 4)
# add SIGHASH_ALL using int_to_little_endian in 4 bytes
+ s += int_to_little_endian(SIGHASH_ALL, 4)
# hash256 the serialization
+ h256 = hash256(s)
# convert the result to an integer using int.from_bytes(x, 'big')
- raise NotImplementedError
+ return int.from_bytes(h256, 'big')
def verify_input(self, input_index):
'''Returns whether the input has a valid signature'''
# get the relevant input
+ current_input = self.tx_ins[input_index]
# grab the previous ScriptPubKey
+ script_pub_key = current_input.script_pubkey(self.testnet)
# get the signature hash (z)
+ z = self.sig_hash(input_index)
# combine the current ScriptSig and the previous ScriptPubKey
+ combined = current_input.script_sig + script_pub_key
# evaluate the combined script
- raise NotImplementedError
+ return combined.evaluate(z)
# tag::source2[]
def verify(self):
@@ -198,13 +224,19 @@ def verify(self):
def sign_input(self, input_index, private_key):
# get the signature hash (z)
+ z = self.sig_hash(input_index)
# get der signature of z from private key
+ der = private_key.sign(z).der()
# append the SIGHASH_ALL to der (use SIGHASH_ALL.to_bytes(1, 'big'))
+ appended_der = der + SIGHASH_ALL.to_bytes(1, 'big')
# calculate the sec
+ sec = private_key.point.sec()
# initialize a new script with [sig, sec] as the cmds
+ new_script = Script([appended_der, sec])
# change input's script_sig to new script
+ self.tx_ins[input_index].script_sig = new_script
# return whether sig is valid using self.verify_input
- raise NotImplementedError
+ return self.verify_input(input_index)
class TxIn: