loadprecomputed.go raw

   1  // Copyright 2015 The btcsuite developers
   2  // Copyright (c) 2015-2022 The Decred developers
   3  // Use of this source code is governed by an ISC
   4  // license that can be found in the LICENSE file.
   5  
   6  package secp256k1
   7  
   8  import (
   9  	"compress/zlib"
  10  	"encoding/base64"
  11  	"io"
  12  	"strings"
  13  	"sync"
  14  )
  15  
  16  //go:generate go run genprecomps.go
  17  
  18  // bytePointTable describes a table used to house pre-computed values for
  19  // accelerating scalar base multiplication.
  20  type bytePointTable [32][256]JacobianPoint
  21  
  22  // compressedBytePointsFn is set to a real function by the code generation to
  23  // return the compressed pre-computed values for accelerating scalar base
  24  // multiplication.
  25  var compressedBytePointsFn func() string
  26  
  27  // s256BytePoints houses pre-computed values used to accelerate scalar base
  28  // multiplication such that they are only loaded on first use.
  29  var s256BytePoints = func() func() *bytePointTable {
  30  	// mustLoadBytePoints decompresses and deserializes the pre-computed byte
  31  	// points used to accelerate scalar base multiplication for the secp256k1
  32  	// curve.
  33  	//
  34  	// This approach is used since it allows the compile to use significantly
  35  	// less ram and be performed much faster than it is with hard-coding the
  36  	// final in-memory data structure.  At the same time, it is quite fast to
  37  	// generate the in-memory data structure on first use with this approach
  38  	// versus computing the table.
  39  	//
  40  	// It will panic on any errors because the data is hard coded and thus any
  41  	// errors means something is wrong in the source code.
  42  	var data *bytePointTable
  43  	mustLoadBytePoints := func() {
  44  		// There will be no byte points to load when generating them.
  45  		if compressedBytePointsFn == nil {
  46  			return
  47  		}
  48  		bp := compressedBytePointsFn()
  49  		// Decompress the pre-computed table used to accelerate scalar base
  50  		// multiplication.
  51  		decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(bp))
  52  		r, err := zlib.NewReader(decoder)
  53  		if err != nil {
  54  			panic(err)
  55  		}
  56  		serialized, err := io.ReadAll(r)
  57  		if err != nil {
  58  			panic(err)
  59  		}
  60  		// Deserialize the precomputed byte points and set the memory table to
  61  		// them.
  62  		offset := 0
  63  		var bytePoints bytePointTable
  64  		for byteNum := 0; byteNum < len(bytePoints); byteNum++ {
  65  			// All points in this window.
  66  			for i := 0; i < len(bytePoints[byteNum]); i++ {
  67  				p := &bytePoints[byteNum][i]
  68  				p.X.SetByteSlice(serialized[offset:])
  69  				offset += 32
  70  				p.Y.SetByteSlice(serialized[offset:])
  71  				offset += 32
  72  				p.Z.SetInt(1)
  73  			}
  74  		}
  75  		data = &bytePoints
  76  	}
  77  	// Return a closure that initializes the data on first access.  This is done
  78  	// because the table takes a non-trivial amount of memory and initializing
  79  	// it unconditionally would cause anything that imports the package, either
  80  	// directly, or indirectly via transitive deps, to use that memory even if
  81  	// the caller never accesses any parts of the package that actually needs
  82  	// access to it.
  83  	var loadBytePointsOnce sync.Once
  84  	return func() *bytePointTable {
  85  		loadBytePointsOnce.Do(mustLoadBytePoints)
  86  		return data
  87  	}
  88  }()
  89