diff options
| -rw-r--r-- | field.go | 92 | ||||
| -rw-r--r-- | go.mod | 3 | ||||
| -rw-r--r-- | main.go | 33 |
3 files changed, 128 insertions, 0 deletions
diff --git a/field.go b/field.go new file mode 100644 index 0000000..8b865ad --- /dev/null +++ b/field.go | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | package main | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "fmt" | ||
| 5 | "math/big" | ||
| 6 | ) | ||
| 7 | |||
| 8 | // The prime for secp256k1: 2^256 - 2^32 - 977 | ||
| 9 | // All field arithmetic happens mod this number | ||
| 10 | var P, _ = new(big.Int).SetString( | ||
| 11 | "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", | ||
| 12 | 16, | ||
| 13 | ) | ||
| 14 | |||
| 15 | // FieldElement represents a number in our finite field (mod P) | ||
| 16 | type FieldElement struct { | ||
| 17 | value *big.Int | ||
| 18 | } | ||
| 19 | |||
| 20 | // NewFieldElement creates a field element from a big.Int | ||
| 21 | // It automatically reduces mod P | ||
| 22 | func NewFieldElement(v *big.Int) *FieldElement { | ||
| 23 | result := new(big.Int).Mod(v, P) | ||
| 24 | return &FieldElement{value: result} | ||
| 25 | } | ||
| 26 | |||
| 27 | // NewFieldElementFromInt64 is a convenience for small numbers | ||
| 28 | func NewFieldElementFromInt64(v int64) *FieldElement { | ||
| 29 | return NewFieldElement(big.NewInt(v)) | ||
| 30 | } | ||
| 31 | |||
| 32 | // Add returns (a + b) mod P | ||
| 33 | func (a *FieldElement) Add(b *FieldElement) *FieldElement { | ||
| 34 | result := new(big.Int).Add(a.value, b.value) | ||
| 35 | return NewFieldElement(result) | ||
| 36 | } | ||
| 37 | |||
| 38 | // Sub returns (a - b) mod P | ||
| 39 | func (a *FieldElement) Sub(b *FieldElement) *FieldElement { | ||
| 40 | result := new(big.Int).Sub(a.value, b.value) | ||
| 41 | return NewFieldElement(result) | ||
| 42 | } | ||
| 43 | |||
| 44 | // Mul returns (a * b) mod P | ||
| 45 | func (a *FieldElement) Mul(b *FieldElement) *FieldElement { | ||
| 46 | result := new(big.Int).Mul(a.value, b.value) | ||
| 47 | return NewFieldElement(result) | ||
| 48 | } | ||
| 49 | |||
| 50 | // Div returns (a / b) mod P | ||
| 51 | // Division in a field = multiply by the inverse | ||
| 52 | func (a *FieldElement) Div(b *FieldElement) *FieldElement { | ||
| 53 | // a / b = a * b^(-1) | ||
| 54 | // b^(-1) mod P = b^(P-2) mod P (Fermat's little theorem) | ||
| 55 | inverse := b.Inverse() | ||
| 56 | return a.Mul(inverse) | ||
| 57 | } | ||
| 58 | |||
| 59 | // Inverse returns a^(-1) mod P using Fermat's little theorem | ||
| 60 | // a^(-1) = a^(P-2) mod P | ||
| 61 | func (a *FieldElement) Inverse() *FieldElement { | ||
| 62 | // P - 2 | ||
| 63 | exp := new(big.Int).Sub(P, big.NewInt(2)) | ||
| 64 | // a^(P-2) mod P | ||
| 65 | result := new(big.Int).Exp(a.value, exp, P) | ||
| 66 | return &FieldElement{value: result} | ||
| 67 | } | ||
| 68 | |||
| 69 | // Square returns a² mod P (convenience method) | ||
| 70 | func (a *FieldElement) Square() *FieldElement { | ||
| 71 | return a.Mul(a) | ||
| 72 | } | ||
| 73 | |||
| 74 | // Equal checks if two field elements are the same | ||
| 75 | func (a *FieldElement) Equal(b *FieldElement) bool { | ||
| 76 | return a.value.Cmp(b.value) == 0 | ||
| 77 | } | ||
| 78 | |||
| 79 | // IsZero checks if the element is zero | ||
| 80 | func (a *FieldElement) IsZero() bool { | ||
| 81 | return a.value.Sign() == 0 | ||
| 82 | } | ||
| 83 | |||
| 84 | // String returns hex representation | ||
| 85 | func (a *FieldElement) String() string { | ||
| 86 | return fmt.Sprintf("%064x", a.value) | ||
| 87 | } | ||
| 88 | |||
| 89 | // Clone returns a copy | ||
| 90 | func (a *FieldElement) Clone() *FieldElement { | ||
| 91 | return &FieldElement{value: new(big.Int).Set(a.value)} | ||
| 92 | } | ||
| @@ -0,0 +1,3 @@ | |||
| 1 | module secp256k1-learn | ||
| 2 | |||
| 3 | go 1.23.5 | ||
| @@ -0,0 +1,33 @@ | |||
| 1 | package main | ||
| 2 | |||
| 3 | import "fmt" | ||
| 4 | |||
| 5 | func main() { | ||
| 6 | fmt.Println("=== secp256k1 from scratch ===") | ||
| 7 | fmt.Println() | ||
| 8 | |||
| 9 | // Test field arithmetic with small numbers first | ||
| 10 | fmt.Println("Testing field arithmetic:") | ||
| 11 | |||
| 12 | a := NewFieldElementFromInt64(7) | ||
| 13 | b := NewFieldElementFromInt64(5) | ||
| 14 | |||
| 15 | fmt.Printf("a = %d\n", 7) | ||
| 16 | fmt.Printf("b = %d\n", 5) | ||
| 17 | fmt.Printf("a + b = %s (should end in ...c)\n", a.Add(b).String()) | ||
| 18 | fmt.Printf("a - b = %s (should end in ...2)\n", a.Sub(b).String()) | ||
| 19 | fmt.Printf("a * b = %s (should end in ...23, which is 35)\n", a.Mul(b).String()) | ||
| 20 | |||
| 21 | // Test division: 10 / 5 = 2 | ||
| 22 | ten := NewFieldElementFromInt64(10) | ||
| 23 | five := NewFieldElementFromInt64(5) | ||
| 24 | fmt.Printf("10 / 5 = %s (should end in ...2)\n", ten.Div(five).String()) | ||
| 25 | |||
| 26 | // Test inverse: 5 * inverse(5) should = 1 | ||
| 27 | inv := five.Inverse() | ||
| 28 | product := five.Mul(inv) | ||
| 29 | fmt.Printf("5 * inverse(5) = %s (should end in ...1)\n", product.String()) | ||
| 30 | |||
| 31 | fmt.Println() | ||
| 32 | fmt.Println("Field arithmetic works!") | ||
| 33 | } | ||
