aboutsummaryrefslogtreecommitdiffstats
path: root/point_test.go
diff options
context:
space:
mode:
authorClawd <ai@clawd.bot>2026-02-19 20:55:43 -0800
committerClawd <ai@clawd.bot>2026-02-19 20:55:43 -0800
commit56a59d990f865a6e40e58ee335ee2efe2c0a8a40 (patch)
tree1e42693006633a48d8169bf1fba3d4892f99af8e /point_test.go
parentaa8684e440c619cbff36bba1a9a96c9a503941ab (diff)
Add Point type with addition, doubling, scalar multiplication
Diffstat (limited to 'point_test.go')
-rw-r--r--point_test.go195
1 files changed, 195 insertions, 0 deletions
diff --git a/point_test.go b/point_test.go
new file mode 100644
index 0000000..ba12977
--- /dev/null
+++ b/point_test.go
@@ -0,0 +1,195 @@
1package main
2
3import (
4 "math/big"
5 "testing"
6)
7
8func TestGeneratorIsOnCurve(t *testing.T) {
9 if !G.IsOnCurve() {
10 t.Error("generator point G should be on the curve")
11 }
12}
13
14func TestInfinityIsOnCurve(t *testing.T) {
15 inf := Infinity()
16 if !inf.IsOnCurve() {
17 t.Error("point at infinity should be considered on curve")
18 }
19}
20
21func TestPointNotOnCurve(t *testing.T) {
22 // (1, 1) is not on y² = x³ + 7
23 // y² = 1, x³ + 7 = 8, so 1 ≠ 8
24 x := NewFieldElementFromInt64(1)
25 y := NewFieldElementFromInt64(1)
26 _, err := NewPoint(x, y)
27 if err == nil {
28 t.Error("(1, 1) should not be on the curve")
29 }
30}
31
32func TestAddInfinity(t *testing.T) {
33 inf := Infinity()
34
35 // G + infinity = G
36 result := G.Add(inf)
37 if !result.Equal(G) {
38 t.Error("G + infinity should equal G")
39 }
40
41 // infinity + G = G
42 result = inf.Add(G)
43 if !result.Equal(G) {
44 t.Error("infinity + G should equal G")
45 }
46
47 // infinity + infinity = infinity
48 result = inf.Add(inf)
49 if !result.IsInfinity() {
50 t.Error("infinity + infinity should be infinity")
51 }
52}
53
54func TestAddInverseGivesInfinity(t *testing.T) {
55 // G + (-G) = infinity
56 negG := G.Negate()
57 result := G.Add(negG)
58 if !result.IsInfinity() {
59 t.Error("G + (-G) should be infinity")
60 }
61}
62
63func TestDoubleGenerator(t *testing.T) {
64 // 2G should be on the curve
65 twoG := G.Double()
66 if !twoG.IsOnCurve() {
67 t.Error("2G should be on the curve")
68 }
69
70 // 2G should not be infinity
71 if twoG.IsInfinity() {
72 t.Error("2G should not be infinity")
73 }
74
75 // 2G should not equal G
76 if twoG.Equal(G) {
77 t.Error("2G should not equal G")
78 }
79}
80
81func TestAddEqualsDouble(t *testing.T) {
82 // G + G should equal G.Double()
83 sum := G.Add(G)
84 doubled := G.Double()
85 if !sum.Equal(doubled) {
86 t.Error("G + G should equal 2G")
87 }
88}
89
90func TestScalarMulByOne(t *testing.T) {
91 // 1 * G = G
92 one := big.NewInt(1)
93 result := G.ScalarMul(one)
94 if !result.Equal(G) {
95 t.Error("1 * G should equal G")
96 }
97}
98
99func TestScalarMulByTwo(t *testing.T) {
100 // 2 * G = G + G
101 two := big.NewInt(2)
102 result := G.ScalarMul(two)
103 expected := G.Double()
104 if !result.Equal(expected) {
105 t.Error("2 * G should equal G.Double()")
106 }
107}
108
109func TestScalarMulByThree(t *testing.T) {
110 // 3 * G = G + G + G
111 three := big.NewInt(3)
112 result := G.ScalarMul(three)
113 expected := G.Double().Add(G)
114 if !result.Equal(expected) {
115 t.Error("3 * G should equal 2G + G")
116 }
117
118 // Result should be on curve
119 if !result.IsOnCurve() {
120 t.Error("3G should be on the curve")
121 }
122}
123
124func TestScalarMulByN(t *testing.T) {
125 // N * G = infinity (curve order)
126 result := G.ScalarMul(N)
127 if !result.IsInfinity() {
128 t.Error("N * G should be infinity (curve order)")
129 }
130}
131
132func TestScalarMulAssociative(t *testing.T) {
133 // (a * b) * G = a * (b * G)
134 a := big.NewInt(7)
135 b := big.NewInt(11)
136 ab := new(big.Int).Mul(a, b) // 77
137
138 left := G.ScalarMul(ab)
139 right := G.ScalarMul(b).ScalarMul(a)
140
141 if !left.Equal(right) {
142 t.Error("scalar multiplication should be associative")
143 }
144}
145
146func TestNegateOnCurve(t *testing.T) {
147 negG := G.Negate()
148 if !negG.IsOnCurve() {
149 t.Error("-G should be on the curve")
150 }
151}
152
153func TestDoubleNegateEqualsOriginal(t *testing.T) {
154 // -(-G) = G
155 result := G.Negate().Negate()
156 if !result.Equal(G) {
157 t.Error("-(-G) should equal G")
158 }
159}
160
161func TestPointEquality(t *testing.T) {
162 // Same point should be equal
163 if !G.Equal(G) {
164 t.Error("G should equal itself")
165 }
166
167 // Different points should not be equal
168 twoG := G.Double()
169 if G.Equal(twoG) {
170 t.Error("G should not equal 2G")
171 }
172
173 // Infinity equals infinity
174 inf1 := Infinity()
175 inf2 := Infinity()
176 if !inf1.Equal(inf2) {
177 t.Error("infinity should equal infinity")
178 }
179}
180
181// Known test vector from Bitcoin
182func TestKnownVector2G(t *testing.T) {
183 // 2G has known coordinates (verified against bitcoin wiki)
184 expectedX, _ := new(big.Int).SetString("C6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3CA7ABAC09B95C709EE5", 16)
185 expectedY, _ := new(big.Int).SetString("1AE168FEA63DC339A3C58419466CEAEEF7F632653266D0E1236431A950CFE52A", 16)
186
187 twoG := G.Double()
188
189 if twoG.x.value.Cmp(expectedX) != 0 {
190 t.Errorf("2G.x = %s, want %x", twoG.x.String(), expectedX)
191 }
192 if twoG.y.value.Cmp(expectedY) != 0 {
193 t.Errorf("2G.y = %s, want %x", twoG.y.String(), expectedY)
194 }
195}