diff --git a/dcrec/secp256k1/field.go b/dcrec/secp256k1/field.go index 36c287d43..f633b46df 100644 --- a/dcrec/secp256k1/field.go +++ b/dcrec/secp256k1/field.go @@ -773,12 +773,82 @@ func (f *FieldVal) Add2(val *FieldVal, val2 *FieldVal) *FieldVal { return f } +// MulBy2 multiplies the field value by 2 and stores the result in f in constant +// time. Note that this function can overflow if multiplying the value by any +// of the individual words exceeds a max uint32. Therefore it is important that +// the caller ensures no overflows will occur before using this function. +// +// The field value is returned to support chaining. This enables syntax like: +// f.MulBy2().Add(f2) so that f = 2 * f + f2. +// +// Preconditions: +// - The field value magnitude multiplied by 2 val MUST be a max of 32 +// Output Normalized: No +// Output Max Magnitude: Existing field magnitude times 2 +func (f *FieldVal) MulBy2() *FieldVal { + return f.MulInt(2) +} + +// MulBy3 multiplies the field value by 3 and stores the result in f in constant +// time. Note that this function can overflow if multiplying the value by any +// of the individual words exceeds a max uint32. Therefore it is important that +// the caller ensures no overflows will occur before using this function. +// +// The field value is returned to support chaining. This enables syntax like: +// f.MulBy3().Add(f2) so that f = 3 * f + f2. +// +// Preconditions: +// - The field value magnitude multiplied by 3 val MUST be a max of 32 +// Output Normalized: No +// Output Max Magnitude: Existing field magnitude times 3 +func (f *FieldVal) MulBy3() *FieldVal { + return f.MulInt(3) +} + +// MulBy4 multiplies the field value by 4 and stores the result in f in constant +// time. Note that this function can overflow if multiplying the value by any +// of the individual words exceeds a max uint32. Therefore it is important that +// the caller ensures no overflows will occur before using this function. +// +// The field value is returned to support chaining. This enables syntax like: +// f.MulBy4().Add(f2) so that f = 4 * f + f2. +// +// Preconditions: +// - The field value magnitude multiplied by 4 val MUST be a max of 32 +// Output Normalized: No +// Output Max Magnitude: Existing field magnitude times 4 +func (f *FieldVal) MulBy4() *FieldVal { + return f.MulInt(4) +} + +// MulBy8 multiplies the field value by 8 and stores the result in f in constant +// time. Note that this function can overflow if multiplying the value by any +// of the individual words exceeds a max uint32. Therefore it is important that +// the caller ensures no overflows will occur before using this function. +// +// The field value is returned to support chaining. This enables syntax like: +// f.MulBy8().Add(f2) so that f = 8 * f + f2. +// +// Preconditions: +// - The field value magnitude multiplied by 8 val MUST be a max of 32 +// Output Normalized: No +// Output Max Magnitude: Existing field magnitude times 8 +func (f *FieldVal) MulBy8() *FieldVal { + return f.MulInt(8) +} + // MulInt multiplies the field value by the passed int and stores the result in // f in constant time. Note that this function can overflow if multiplying the // value by any of the individual words exceeds a max uint32. Therefore it is // important that the caller ensures no overflows will occur before using this // function. // +// Callers should prefer using the specialized methods for multiplying by 2, 3, +// 4, and 8, as they are commonly used in curve equations. +// +// See [FieldVal.MulBy2], [FieldVal.MulBy3], [FieldVal.MulBy4], and +// [FieldVal.MulBy8] for the aforementioned specialized methods. +// // The field value is returned to support chaining. This enables syntax like: // f.MulInt(2).Add(f2) so that f = 2 * f + f2. // diff --git a/dcrec/secp256k1/field_bench_test.go b/dcrec/secp256k1/field_bench_test.go index 5f4e851b5..bdf2c16b1 100644 --- a/dcrec/secp256k1/field_bench_test.go +++ b/dcrec/secp256k1/field_bench_test.go @@ -88,6 +88,71 @@ func BenchmarkFieldAdd(b *testing.B) { } } +// BenchmarkFieldMulBy2 benchmarks multiplying an unsigned 256-bit big-endian +// integer by 2 with [FieldVal.MulBy2]. +func BenchmarkFieldMulBy2(b *testing.B) { + fHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" + f := mustFieldVal(fHex) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + f.MulBy2() + } +} + +// BenchmarkFieldMulBy3 benchmarks multiplying an unsigned 256-bit big-endian +// integer by 3 with [FieldVal.MulBy3]. +func BenchmarkFieldMulBy3(b *testing.B) { + fHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" + f := mustFieldVal(fHex) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + f.MulBy3() + } +} + +// BenchmarkFieldMulBy4 benchmarks multiplying an unsigned 256-bit big-endian +// integer by 4 with [FieldVal.MulBy4]. +func BenchmarkFieldMulBy4(b *testing.B) { + fHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" + f := mustFieldVal(fHex) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + f.MulBy4() + } +} + +// BenchmarkFieldMulBy8 benchmarks multiplying an unsigned 256-bit big-endian +// integer by 8 with [FieldVal.MulBy8]. +func BenchmarkFieldMulBy8(b *testing.B) { + fHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" + f := mustFieldVal(fHex) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + f.MulBy8() + } +} + +// BenchmarkFieldMulInt benchmarks multiplying an unsigned 256-bit big-endian +// integer by small integers with [FieldVal.MulInt]. +func BenchmarkFieldMulInt(b *testing.B) { + fHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" + f := mustFieldVal(fHex) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + f.MulInt(2) + } +} + // BenchmarkBigIntMulModP benchmarks multiplying two unsigned 256-bit big-endian // integers modulo the field prime with stdlib big integers. func BenchmarkBigIntMulModP(b *testing.B) { diff --git a/dcrec/secp256k1/field_test.go b/dcrec/secp256k1/field_test.go index 974145a20..a1200b8dd 100644 --- a/dcrec/secp256k1/field_test.go +++ b/dcrec/secp256k1/field_test.go @@ -1178,6 +1178,73 @@ func TestFieldAdd(t *testing.T) { } } +// TestFieldMulByX ensures [FieldVal.MulBy2], [FieldVal.MulBy3], +// [FieldVal.MulBy4], and [FieldVal.MulBy8] produce the same results as the +// equivalent [FieldVal.MulInt]. +func TestFieldMulByX(t *testing.T) { + mulByFuncs := []struct { + factor uint8 + fn func(*FieldVal) *FieldVal + }{ + {2, (*FieldVal).MulBy2}, + {3, (*FieldVal).MulBy3}, + {4, (*FieldVal).MulBy4}, + {8, (*FieldVal).MulBy8}, + } + + tests := []struct { + name string // test description + in string // hex encoded value + }{{ + name: "zero", + in: "0", + }, { + name: "one", + in: "1", + }, { + name: "two", + in: "2", + }, { + name: "secp256k1 prime-1", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + }, { + name: "secp256k1 prime / 2", + in: "7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffe17", + }, { + name: "secp256k1 prime / 3", + in: "55555555555555555555555555555555555555555555555555555554fffffeba", + }, { + name: "secp256k1 prime / 4", + in: "3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0b", + }, { + name: "secp256k1 prime / 8", + in: "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffff85", + }, { + name: "random sampling #1", + in: "e8655154d386119205b642e6cf03b2e7d03ff59301d98bfebcf350a778014efa", + }, { + name: "random sampling #2", + in: "bb38f58fddc9bf462d4b9ba6503c142be374ecd423525c590ab8de77a6265676", + }, { + name: "random sampling #3", + in: "bdacc1032b224a43e0dd6ac5452037d9cdfbc3a8041e0ce8eee9d42516a1e3b1", + }, { + name: "random sampling #4", + in: "e3cbe002cc93029190181a906ed41af401c9726546dc19389a06290efdf563f1", + }} + for _, test := range tests { + in := mustFieldVal(test.in) + for _, m := range mulByFuncs { + want := new(FieldVal).Set(in).MulInt(m.factor) + got := m.fn(new(FieldVal).Set(in)) + if !got.Equals(want) { + t.Errorf("%q: MulBy%d: wrong result -- got: %v, want: %v", + test.name, m.factor, got, want) + } + } + } +} + // TestFieldMulInt ensures that multiplying an integer to field values via // [FieldVal.MulInt] works as expected. func TestFieldMulInt(t *testing.T) {