Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions dcrec/secp256k1/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down
65 changes: 65 additions & 0 deletions dcrec/secp256k1/field_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
67 changes: 67 additions & 0 deletions dcrec/secp256k1/field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down