From e0f87d3752edd792b7993d64622c216ebda225f4 Mon Sep 17 00:00:00 2001 From: Clawd Date: Thu, 19 Feb 2026 20:21:41 -0800 Subject: Add field arithmetic (mod p operations) --- field.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 +++ main.go | 33 +++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 field.go create mode 100644 go.mod create mode 100644 main.go diff --git a/field.go b/field.go new file mode 100644 index 0000000..8b865ad --- /dev/null +++ b/field.go @@ -0,0 +1,92 @@ +package main + +import ( + "fmt" + "math/big" +) + +// The prime for secp256k1: 2^256 - 2^32 - 977 +// All field arithmetic happens mod this number +var P, _ = new(big.Int).SetString( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", + 16, +) + +// FieldElement represents a number in our finite field (mod P) +type FieldElement struct { + value *big.Int +} + +// NewFieldElement creates a field element from a big.Int +// It automatically reduces mod P +func NewFieldElement(v *big.Int) *FieldElement { + result := new(big.Int).Mod(v, P) + return &FieldElement{value: result} +} + +// NewFieldElementFromInt64 is a convenience for small numbers +func NewFieldElementFromInt64(v int64) *FieldElement { + return NewFieldElement(big.NewInt(v)) +} + +// Add returns (a + b) mod P +func (a *FieldElement) Add(b *FieldElement) *FieldElement { + result := new(big.Int).Add(a.value, b.value) + return NewFieldElement(result) +} + +// Sub returns (a - b) mod P +func (a *FieldElement) Sub(b *FieldElement) *FieldElement { + result := new(big.Int).Sub(a.value, b.value) + return NewFieldElement(result) +} + +// Mul returns (a * b) mod P +func (a *FieldElement) Mul(b *FieldElement) *FieldElement { + result := new(big.Int).Mul(a.value, b.value) + return NewFieldElement(result) +} + +// Div returns (a / b) mod P +// Division in a field = multiply by the inverse +func (a *FieldElement) Div(b *FieldElement) *FieldElement { + // a / b = a * b^(-1) + // b^(-1) mod P = b^(P-2) mod P (Fermat's little theorem) + inverse := b.Inverse() + return a.Mul(inverse) +} + +// Inverse returns a^(-1) mod P using Fermat's little theorem +// a^(-1) = a^(P-2) mod P +func (a *FieldElement) Inverse() *FieldElement { + // P - 2 + exp := new(big.Int).Sub(P, big.NewInt(2)) + // a^(P-2) mod P + result := new(big.Int).Exp(a.value, exp, P) + return &FieldElement{value: result} +} + +// Square returns a² mod P (convenience method) +func (a *FieldElement) Square() *FieldElement { + return a.Mul(a) +} + +// Equal checks if two field elements are the same +func (a *FieldElement) Equal(b *FieldElement) bool { + return a.value.Cmp(b.value) == 0 +} + +// IsZero checks if the element is zero +func (a *FieldElement) IsZero() bool { + return a.value.Sign() == 0 +} + +// String returns hex representation +func (a *FieldElement) String() string { + return fmt.Sprintf("%064x", a.value) +} + +// Clone returns a copy +func (a *FieldElement) Clone() *FieldElement { + return &FieldElement{value: new(big.Int).Set(a.value)} +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f8eed0f --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module secp256k1-learn + +go 1.23.5 diff --git a/main.go b/main.go new file mode 100644 index 0000000..9e19d35 --- /dev/null +++ b/main.go @@ -0,0 +1,33 @@ +package main + +import "fmt" + +func main() { + fmt.Println("=== secp256k1 from scratch ===") + fmt.Println() + + // Test field arithmetic with small numbers first + fmt.Println("Testing field arithmetic:") + + a := NewFieldElementFromInt64(7) + b := NewFieldElementFromInt64(5) + + fmt.Printf("a = %d\n", 7) + fmt.Printf("b = %d\n", 5) + fmt.Printf("a + b = %s (should end in ...c)\n", a.Add(b).String()) + fmt.Printf("a - b = %s (should end in ...2)\n", a.Sub(b).String()) + fmt.Printf("a * b = %s (should end in ...23, which is 35)\n", a.Mul(b).String()) + + // Test division: 10 / 5 = 2 + ten := NewFieldElementFromInt64(10) + five := NewFieldElementFromInt64(5) + fmt.Printf("10 / 5 = %s (should end in ...2)\n", ten.Div(five).String()) + + // Test inverse: 5 * inverse(5) should = 1 + inv := five.Inverse() + product := five.Mul(inv) + fmt.Printf("5 * inverse(5) = %s (should end in ...1)\n", product.String()) + + fmt.Println() + fmt.Println("Field arithmetic works!") +} -- cgit v1.2.3