From 56a59d990f865a6e40e58ee335ee2efe2c0a8a40 Mon Sep 17 00:00:00 2001 From: Clawd Date: Thu, 19 Feb 2026 20:55:43 -0800 Subject: Add Point type with addition, doubling, scalar multiplication --- point_test.go | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 point_test.go (limited to 'point_test.go') 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 @@ +package main + +import ( + "math/big" + "testing" +) + +func TestGeneratorIsOnCurve(t *testing.T) { + if !G.IsOnCurve() { + t.Error("generator point G should be on the curve") + } +} + +func TestInfinityIsOnCurve(t *testing.T) { + inf := Infinity() + if !inf.IsOnCurve() { + t.Error("point at infinity should be considered on curve") + } +} + +func TestPointNotOnCurve(t *testing.T) { + // (1, 1) is not on y² = x³ + 7 + // y² = 1, x³ + 7 = 8, so 1 ≠ 8 + x := NewFieldElementFromInt64(1) + y := NewFieldElementFromInt64(1) + _, err := NewPoint(x, y) + if err == nil { + t.Error("(1, 1) should not be on the curve") + } +} + +func TestAddInfinity(t *testing.T) { + inf := Infinity() + + // G + infinity = G + result := G.Add(inf) + if !result.Equal(G) { + t.Error("G + infinity should equal G") + } + + // infinity + G = G + result = inf.Add(G) + if !result.Equal(G) { + t.Error("infinity + G should equal G") + } + + // infinity + infinity = infinity + result = inf.Add(inf) + if !result.IsInfinity() { + t.Error("infinity + infinity should be infinity") + } +} + +func TestAddInverseGivesInfinity(t *testing.T) { + // G + (-G) = infinity + negG := G.Negate() + result := G.Add(negG) + if !result.IsInfinity() { + t.Error("G + (-G) should be infinity") + } +} + +func TestDoubleGenerator(t *testing.T) { + // 2G should be on the curve + twoG := G.Double() + if !twoG.IsOnCurve() { + t.Error("2G should be on the curve") + } + + // 2G should not be infinity + if twoG.IsInfinity() { + t.Error("2G should not be infinity") + } + + // 2G should not equal G + if twoG.Equal(G) { + t.Error("2G should not equal G") + } +} + +func TestAddEqualsDouble(t *testing.T) { + // G + G should equal G.Double() + sum := G.Add(G) + doubled := G.Double() + if !sum.Equal(doubled) { + t.Error("G + G should equal 2G") + } +} + +func TestScalarMulByOne(t *testing.T) { + // 1 * G = G + one := big.NewInt(1) + result := G.ScalarMul(one) + if !result.Equal(G) { + t.Error("1 * G should equal G") + } +} + +func TestScalarMulByTwo(t *testing.T) { + // 2 * G = G + G + two := big.NewInt(2) + result := G.ScalarMul(two) + expected := G.Double() + if !result.Equal(expected) { + t.Error("2 * G should equal G.Double()") + } +} + +func TestScalarMulByThree(t *testing.T) { + // 3 * G = G + G + G + three := big.NewInt(3) + result := G.ScalarMul(three) + expected := G.Double().Add(G) + if !result.Equal(expected) { + t.Error("3 * G should equal 2G + G") + } + + // Result should be on curve + if !result.IsOnCurve() { + t.Error("3G should be on the curve") + } +} + +func TestScalarMulByN(t *testing.T) { + // N * G = infinity (curve order) + result := G.ScalarMul(N) + if !result.IsInfinity() { + t.Error("N * G should be infinity (curve order)") + } +} + +func TestScalarMulAssociative(t *testing.T) { + // (a * b) * G = a * (b * G) + a := big.NewInt(7) + b := big.NewInt(11) + ab := new(big.Int).Mul(a, b) // 77 + + left := G.ScalarMul(ab) + right := G.ScalarMul(b).ScalarMul(a) + + if !left.Equal(right) { + t.Error("scalar multiplication should be associative") + } +} + +func TestNegateOnCurve(t *testing.T) { + negG := G.Negate() + if !negG.IsOnCurve() { + t.Error("-G should be on the curve") + } +} + +func TestDoubleNegateEqualsOriginal(t *testing.T) { + // -(-G) = G + result := G.Negate().Negate() + if !result.Equal(G) { + t.Error("-(-G) should equal G") + } +} + +func TestPointEquality(t *testing.T) { + // Same point should be equal + if !G.Equal(G) { + t.Error("G should equal itself") + } + + // Different points should not be equal + twoG := G.Double() + if G.Equal(twoG) { + t.Error("G should not equal 2G") + } + + // Infinity equals infinity + inf1 := Infinity() + inf2 := Infinity() + if !inf1.Equal(inf2) { + t.Error("infinity should equal infinity") + } +} + +// Known test vector from Bitcoin +func TestKnownVector2G(t *testing.T) { + // 2G has known coordinates (verified against bitcoin wiki) + expectedX, _ := new(big.Int).SetString("C6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3CA7ABAC09B95C709EE5", 16) + expectedY, _ := new(big.Int).SetString("1AE168FEA63DC339A3C58419466CEAEEF7F632653266D0E1236431A950CFE52A", 16) + + twoG := G.Double() + + if twoG.x.value.Cmp(expectedX) != 0 { + t.Errorf("2G.x = %s, want %x", twoG.x.String(), expectedX) + } + if twoG.y.value.Cmp(expectedY) != 0 { + t.Errorf("2G.y = %s, want %x", twoG.y.String(), expectedY) + } +} -- cgit v1.2.3