## 2019年4月4日木曜日

### Python - Elliptic Curves - Coding Coding Point Addition for When P1 = P2、特殊メソッド、__add__メソッド

Programming Bitcoin: Learn How to Program Bitcoin from Scratch (Jimmy Song(著)、O'Reilly Media)のChapter 2(Elliptic Curves)、Coding Point Addition for When P1 = P2、Exercises 7(39)の解答を求めてみる。

コード

Python 3

ecc_test.py

```#!/usr/bin/env python3
from unittest import TestCase, main
from ecc import FieldElement, Point

class PointTest(TestCase):
def setUp(self):
pass

def tearDown(self):
pass

def test_ne1(self):
p1 = Point(0, 0, 0, 0)
p2 = Point(1, 1, 0, 0)
self.assertNotEqual(p1, p2)

def test_ne2(self):
p1 = Point(0, 0, 0, 0)
p2 = Point(1, -1, 0, 0)
self.assertNotEqual(p1, p2)

def test_ne_none(self):
self.assertNotEqual(Point(0, 0, 0, 0), None)
self.assertNotEqual(None, Point(0, 0, 0, 0))

p1 = Point(0, 0, 0, 0)
p2 = Point(1, -1, 0, 0)
inf = Point(None, None, 0, 0)
self.assertEqual(p1, p1 + inf)
self.assertEqual(p1, inf + p1)
self.assertEqual(p2, p2 + inf)
self.assertEqual(p2, inf + p2)

p1 = Point(1, 1, 0, 0)
p2 = Point(1, -1, 0, 0)
inf = Point(None, None, 0, 0)
self.assertEqual(inf, p1 + p2)

p1 = Point(2, 5, 5, 7)
p2 = Point(-1, -1, 5, 7)
self.assertEqual(Point(3, -7, 5, 7), p1 + p2)

p = Point(-1, -1, 5, 7)
self.assertEqual(Point(18, 77, 5, 7), p + p)

class FieldElementTest(TestCase):
def setUp(self):
self.a = FieldElement(6, 13)
self.b = FieldElement(7, 13)
self.c = FieldElement(6, 17)

def tearDown(self):
pass

def test_ne(self):
self.assertNotEqual(self.a, None)
self.assertNotEqual(self.a, self.b)
self.assertNotEqual(self.a, self.c)

def test_neg(self):
self.assertEqual(-self.a, FieldElement(7, 13))

def test_sub(self):
self.assertEqual(self.a - self.a, FieldElement(0, 13))
self.assertEqual(self.a - self.b, FieldElement(12, 13))
self.assertEqual(self.b - self.a, FieldElement(1, 13))

def test_mul(self):
self.assertEqual(FieldElement(3, 13), self.a * self.b)

def test_mul_exc(self):
with self.assertRaises(TypeError):
self.a + self.c

def test_true_div1(self):
prime = 31
actual = FieldElement(3, prime) / FieldElement(24, prime)
self.assertEqual(FieldElement(4, prime), actual)

def test_true_div2(self):
prime = 31
actual = FieldElement(1, prime) / FieldElement(17, prime) ** 3
self.assertEqual(FieldElement(29, prime), actual)

def test_true_div3(self):
prime = 31
actual = (
FieldElement(1, prime) /
FieldElement(4, prime) ** 4 *
FieldElement(11, prime)
)
self.assertEqual(FieldElement(13, prime), actual)

if __name__ == '__main__':
main()
```

ecc.py

```#!/usr/bin/env python3
class Point:
def __init__(self, x, y, a, b):
self.a = a
self.b = b
self.x = x
self.y = y
if self.x is None and self.y is None:
return
if y ** 2 != x ** 3 + a * x + b:
raise ValueError(f'({x}, {y}) is not on the curve')

def __eq__(self, other):
if other is None:
return False
return (self.x == other.x and
self.y == other.y and
self.a == other.a and
self.b == other.b)

def __ne__(self, other):
return not (self == other)

if self.a != other.a or self.b != other.b:
raise TypeError(f'Points {self}, {other} are no on the same curve')
if self.x is None:
return other
if other.x is None:
return self
if self.x == other.x and self.y != other.y:
return self.__class__(None, None, self.a, self.b)
if self.x != other.x:
s = (other.y - self.y) / (other.x - self.x)
x = s ** 2 - self.x - other.x
y = s * (self.x - x) - self.y
return self.__class__(x, y, self.a, self.b)
slope = (3 * self.x ** 2 + self.a) / (2 * self.y)
x = slope ** 2 - 2 * self.x
y = slope * (self.x - x) - self.y
return Point(x, y, self.a, self.b)

class FieldElement:
def __init__(self, num: int, prime: int):
if num < 0 or prime <= num:
raise ValueError(f'Num {num} not in field range 0 to {prime - 1}')
self.num = num
self.prime = prime

def __repr__(self) -> str:
return f'FieldElement_{self.prime}({self.num})'

def __eq__(self, other) -> bool:
if other is None:
return False
return self.num == other.num and self.prime == other.prime

def __ne__(self, other) -> bool:
if other is None:
return True
return not self == other

def __neg__(self):
return self.__class__(-self.num % self.prime, self.prime)

if self.prime != other.prime:
raise TypeError('Cannot add two numbers in different Fields')
return self.__class__((self.num + other.num) % self.prime, self.prime)

def __sub__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot subtract two numbers in different Fields')
return self + (- other)

def __mul__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot multiply two numbers in different Fields')
return self.__class__((self.num * other.num) % self.prime, self.prime)

def __pow__(self, exponent):
exponent %= (self.prime - 1)
return self.__class__(pow(self.num, exponent, self.prime), self.prime)

def __truediv__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot divide two numbers in different Fields')
num = (self.num *
pow(other.num, other.prime - 2, other.prime) %
self.prime)
prime = self.prime
return self.__class__(num, prime)
```

```C:\Users\...> py ecc_test.py -v
..........E....
======================================================================
----------------------------------------------------------------------
Traceback (most recent call last):
File "ecc_test.py", line 49, in test_add_p1_eq_p2
self.assertEqual(Point(18, 77, 5, 7), p + p)
File "...\Programming Bitcoin\ch2\ecc.py", line 42, in __add__

----------------------------------------------------------------------
Ran 15 tests in 0.001s

FAILED (errors=1)

C:\Users\...> py ecc_test.py -v
test_mul (__main__.FieldElementTest) ... ok
test_mul_exc (__main__.FieldElementTest) ... ok
test_ne (__main__.FieldElementTest) ... ok
test_neg (__main__.FieldElementTest) ... ok
test_sub (__main__.FieldElementTest) ... ok
test_true_div1 (__main__.FieldElementTest) ... ok
test_true_div2 (__main__.FieldElementTest) ... ok
test_true_div3 (__main__.FieldElementTest) ... ok
test_ne1 (__main__.PointTest) ... ok
test_ne2 (__main__.PointTest) ... ok
test_ne_none (__main__.PointTest) ... ok

----------------------------------------------------------------------
Ran 15 tests in 0.001s

OK

C:\Users\...>
```