aboutsummaryrefslogtreecommitdiffstats
path: root/pow
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2017-03-06 17:37:32 +0800
committerFelix Lange <fjl@users.noreply.github.com>2017-03-09 22:50:14 +0800
commit023670f6bafcfed28c01857da215217a5dadfaa1 (patch)
tree2ced9d0178bfd8d101356a64522cf0225f98362e /pow
parent567d41d9363706b4b13ce0903804e8acf214af49 (diff)
downloaddexon-023670f6bafcfed28c01857da215217a5dadfaa1.tar.gz
dexon-023670f6bafcfed28c01857da215217a5dadfaa1.tar.zst
dexon-023670f6bafcfed28c01857da215217a5dadfaa1.zip
cmd, eth, les, node, pow: disk caching and progress reports
Diffstat (limited to 'pow')
-rw-r--r--pow/ethash.go121
-rw-r--r--pow/ethash_algo.go36
2 files changed, 120 insertions, 37 deletions
diff --git a/pow/ethash.go b/pow/ethash.go
index b22c65e0b..602f9324f 100644
--- a/pow/ethash.go
+++ b/pow/ethash.go
@@ -17,12 +17,19 @@
package pow
import (
+ "bufio"
"bytes"
"errors"
+ "fmt"
+ "io/ioutil"
"math/big"
+ "os"
+ "path/filepath"
"sync"
"time"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
metrics "github.com/rcrowley/go-metrics"
)
@@ -36,6 +43,15 @@ var (
var (
// maxUint256 is a big integer representing 2^256-1
maxUint256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
+
+ // sharedEthash is a full instance that can be shared between multiple users.
+ sharedEthash = NewFullEthash("", 3, 0, "", 0)
+
+ // algorithmRevision is the data structure version used for file naming.
+ algorithmRevision = 23
+
+ // dumpMagic is a dataset dump header to sanity check a data dump.
+ dumpMagic = hexutil.MustDecode("0xfee1deadbaddcafe")
)
// cache wraps an ethash cache with some metadata to allow easier concurrent use.
@@ -48,21 +64,65 @@ type cache struct {
}
// generate ensures that the cache content is generates.
-func (c *cache) generate(test bool) {
+func (c *cache) generate(dir string, limit int, test bool) {
c.once.Do(func() {
- cacheSize := cacheSize(c.epoch*epochLength + 1)
+ // If we have a testing cache, generate and return
if test {
- cacheSize = 1024
+ rawCache := generateCache(1024, seedHash(c.epoch*epochLength+1))
+ c.cache = prepare(uint64(len(rawCache)), bytes.NewReader(rawCache))
+ return
+ }
+ // Full cache generation is needed, check cache dir for existing data
+ size := cacheSize(c.epoch*epochLength + 1)
+ seed := seedHash(c.epoch*epochLength + 1)
+
+ path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x", algorithmRevision, seed))
+ logger := log.New("seed", hexutil.Bytes(seed))
+
+ if dir != "" {
+ dump, err := os.Open(path)
+ if err == nil {
+ logger.Info("Loading ethash cache from disk")
+ start := time.Now()
+ c.cache = prepare(size, bufio.NewReader(dump))
+ logger.Info("Loaded ethash cache from disk", "elapsed", common.PrettyDuration(time.Since(start)))
+
+ dump.Close()
+ return
+ }
+ }
+ // No previous disk cache was available, generate on the fly
+ rawCache := generateCache(size, seed)
+ c.cache = prepare(size, bytes.NewReader(rawCache))
+
+ // If a cache directory is given, attempt to serialize for next time
+ if dir != "" {
+ // Store the ethash cache to disk
+ start := time.Now()
+ if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
+ logger.Error("Failed to create ethash cache dir", "err", err)
+ } else if err := ioutil.WriteFile(path, rawCache, os.ModePerm); err != nil {
+ logger.Error("Failed to write ethash cache to disk", "err", err)
+ } else {
+ logger.Info("Stored ethash cache to disk", "elapsed", common.PrettyDuration(time.Since(start)))
+ }
+ // Iterate over all previous instances and delete old ones
+ for ep := int(c.epoch) - limit; ep >= 0; ep-- {
+ seed := seedHash(uint64(ep)*epochLength + 1)
+ path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x", algorithmRevision, seed))
+ os.Remove(path)
+ }
}
- rawCache := generateCache(cacheSize, seedHash(c.epoch*epochLength+1))
- c.cache = prepare(uint64(len(rawCache)), bytes.NewReader(rawCache))
})
}
// Ethash is a PoW data struture implementing the ethash algorithm.
type Ethash struct {
- cachedir string // Data directory to store the verification caches
- dagdir string // Data directory to store full mining datasets
+ cachedir string // Data directory to store the verification caches
+ cachesinmem int // Number of caches to keep in memory
+ cachesondisk int // Number of caches to keep on disk
+ dagdir string // Data directory to store full mining datasets
+ dagsondisk int // Number of mining datasets to keep on disk
caches map[uint64]*cache // In memory caches to avoid regenerating too often
future *cache // Pre-generated cache for the estimated future epoch
@@ -71,15 +131,27 @@ type Ethash struct {
hashrate *metrics.StandardMeter // Meter tracking the average hashrate
tester bool // Flag whether to use a smaller test dataset
- shared bool // Flag whether to use a global chared dataset
}
// NewFullEthash creates a full sized ethash PoW scheme.
-func NewFullEthash(cachedir, dagdir string) PoW {
+func NewFullEthash(cachedir string, cachesinmem, cachesondisk int, dagdir string, dagsondisk int) PoW {
+ if cachesinmem <= 0 {
+ log.Warn("One ethash cache must alwast be in memory", "requested", cachesinmem)
+ cachesinmem = 1
+ }
+ if cachedir != "" && cachesondisk > 0 {
+ log.Info("Disk storage enabled for ethash caches", "dir", cachedir, "count", cachesondisk)
+ }
+ if dagdir != "" && dagsondisk > 0 {
+ log.Info("Disk storage enabled for ethash DAGs", "dir", dagdir, "count", dagsondisk)
+ }
return &Ethash{
- cachedir: cachedir,
- dagdir: dagdir,
- caches: make(map[uint64]*cache),
+ cachedir: cachedir,
+ cachesinmem: cachesinmem,
+ cachesondisk: cachesondisk,
+ dagdir: dagdir,
+ dagsondisk: dagsondisk,
+ caches: make(map[uint64]*cache),
}
}
@@ -87,18 +159,16 @@ func NewFullEthash(cachedir, dagdir string) PoW {
// purposes.
func NewTestEthash() PoW {
return &Ethash{
- caches: make(map[uint64]*cache),
- tester: true,
+ cachesinmem: 1,
+ caches: make(map[uint64]*cache),
+ tester: true,
}
}
// NewSharedEthash creates a full sized ethash PoW shared between all requesters
// running in the same process.
func NewSharedEthash() PoW {
- return &Ethash{
- caches: make(map[uint64]*cache),
- shared: true,
- }
+ return sharedEthash
}
// Verify implements PoW, checking whether the given block satisfies the PoW
@@ -140,7 +210,7 @@ func (ethash *Ethash) cache(block uint64) []uint32 {
current, future := ethash.caches[epoch], (*cache)(nil)
if current == nil {
// No in-memory cache, evict the oldest if the cache limit was reached
- for len(ethash.caches) >= 3 {
+ for len(ethash.caches) >= ethash.cachesinmem {
var evict *cache
for _, cache := range ethash.caches {
if evict == nil || evict.used.After(cache.used) {
@@ -149,21 +219,21 @@ func (ethash *Ethash) cache(block uint64) []uint32 {
}
delete(ethash.caches, evict.epoch)
- log.Debug("Evictinged ethash cache", "old", evict.epoch, "used", evict.used)
+ log.Debug("Evicted ethash cache", "epoch", evict.epoch, "used", evict.used)
}
// If we have the new cache pre-generated, use that, otherwise create a new one
if ethash.future != nil && ethash.future.epoch == epoch {
log.Debug("Using pre-generated cache", "epoch", epoch)
current, ethash.future = ethash.future, nil
} else {
- log.Debug("Generating new ethash cache", "epoch", epoch)
+ log.Debug("Requiring new ethash cache", "epoch", epoch)
current = &cache{epoch: epoch}
}
ethash.caches[epoch] = current
// If we just used up the future cache, or need a refresh, regenerate
if ethash.future == nil || ethash.future.epoch <= epoch {
- log.Debug("Pre-generating cache for the future", "epoch", epoch+1)
+ log.Debug("Requiring new future ethash cache", "epoch", epoch+1)
future = &cache{epoch: epoch + 1}
ethash.future = future
}
@@ -172,16 +242,15 @@ func (ethash *Ethash) cache(block uint64) []uint32 {
ethash.lock.Unlock()
// Wait for generation finish, bump the timestamp and finalize the cache
- current.once.Do(func() {
- current.generate(ethash.tester)
- })
+ current.generate(ethash.cachedir, ethash.cachesondisk, ethash.tester)
+
current.lock.Lock()
current.used = time.Now()
current.lock.Unlock()
// If we exhusted the future cache, now's a goot time to regenerate it
if future != nil {
- go future.generate(ethash.tester)
+ go future.generate(ethash.cachedir, ethash.cachesondisk, ethash.tester)
}
return current.cache
}
diff --git a/pow/ethash_algo.go b/pow/ethash_algo.go
index fcd6af995..f6d05880a 100644
--- a/pow/ethash_algo.go
+++ b/pow/ethash_algo.go
@@ -45,12 +45,6 @@ const (
loopAccesses = 64 // Number of accesses in hashimoto loop
)
-var (
- // Metadata fields to be compatible with the C++ ethash
- ethashRevision = 23 // Data structure version
- ethashMagic = hexutil.MustDecode("0xfee1deadbaddcafe") // Dataset dump magic number
-)
-
// cacheSize calculates and returns the size of the ethash verification cache that
// belongs to a certain block number. The cache size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
@@ -108,16 +102,33 @@ func seedHash(block uint64) []byte {
// set of 524288 64-byte values.
func generateCache(size uint64, seed []byte) []byte {
// Print some debug logs to allow analysis on low end devices
- logger := log.New("size", size, "seed", hexutil.Bytes(seed))
- logger.Debug("Generating ethash cache")
+ logger := log.New("seed", hexutil.Bytes(seed))
+ logger.Debug("Generating ethash verification cache")
- defer func(start time.Time) {
- logger.Debug("Generated ethash cache", "elapsed", common.PrettyDuration(time.Since(start)))
- }(time.Now())
+ start := time.Now()
+ defer func() {
+ logger.Info("Generated ethash verification cache", "elapsed", common.PrettyDuration(time.Since(start)))
+ }()
// Calculate the number of thoretical rows (we'll store in one buffer nonetheless)
rows := int(size) / hashBytes
+ // Start a monitoring goroutine to report progress on low end devices
+ var progress uint32
+
+ done := make(chan struct{})
+ defer close(done)
+
+ go func() {
+ for {
+ select {
+ case <-done:
+ return
+ case <-time.After(3 * time.Second):
+ logger.Info("Generating ethash verification cache", "percentage", atomic.LoadUint32(&progress)*100/uint32(rows)/4, "elapsed", common.PrettyDuration(time.Since(start)))
+ }
+ }
+ }()
// Create a hasher to reuse between invocations
keccak512 := crypto.Keccak512Hasher()
@@ -126,6 +137,7 @@ func generateCache(size uint64, seed []byte) []byte {
copy(cache, keccak512(seed))
for offset := uint64(hashBytes); offset < size; offset += hashBytes {
copy(cache[offset:], keccak512(cache[offset-hashBytes:offset]))
+ atomic.AddUint32(&progress, 1)
}
// Use a low-round version of randmemohash
temp := make([]byte, hashBytes)
@@ -139,6 +151,8 @@ func generateCache(size uint64, seed []byte) []byte {
)
xorBytes(temp, cache[srcOff:srcOff+hashBytes], cache[xorOff:xorOff+hashBytes])
copy(cache[dstOff:], keccak512(temp))
+
+ atomic.AddUint32(&progress, 1)
}
}
return cache
an>3-3/+5 * - Remove p5-SQL-Tokenizer from 'lang' categoryculot2011-02-135-38/+0 * SQL::Tokenizer is a simple tokenizer for SQL queries. It does not claimculot2011-02-135-0/+38 * Update to the 20110210 snapshot of GCC 4.5.3.gerald2011-02-112-11/+11 * - Update to 1.003000sunpoet2011-02-102-7/+6 * Omit the libquadmath info file on sparc64 and ia64.gerald2011-02-104-8/+16 * Update to the 20110208 snapshot of GCC 4.4.6.gerald2011-02-102-9/+9 * - Update to 1.69.31miwi2011-02-082-4/+3 * Update to the 20110205 snapshot of GCC 4.6.0. This finally addressesgerald2011-02-068-56/+44 * Update to the 20110203 snapshot of GCC 4.5.3.gerald2011-02-062-11/+11 * Remove expired ports:rene2011-02-058-390/+0 * - Update to 05_20110203gahr2011-02-044-9/+10 * Update to the 20110201 snapshot of GCC 4.4.6.gerald2011-02-032-9/+9 * - Fix pkg-plist with TZDATA option onmm2011-02-022-2/+8 * Refactor and make X11 support optional.olgeni2011-02-026-72/+80 * Update to January 2011 release of "Rakudo Star"az2011-01-313-393/+392 * Update to the 20110129 snapshot of GCC 4.6.0.gerald2011-01-308-44/+44 * Fix a typo and pet portlint.linimon2011-01-291-1/+1 * Fix fetch by postpending '.old' to masterdir.linimon2011-01-291-3/+1 * Update to the 20110125 snapshot of GCC 4.4.6.gerald2011-01-282-9/+9 * Update to the 20110127 snapshot of GCC 4.5.3.gerald2011-01-282-11/+11 * - Is not MAKE_JOBS_SAFE:pav2011-01-283-3/+3 * - Disallow from 7.x builds on pointyhat due to hangspav2011-01-271-2/+3 * Update to 5.12.3skv2011-01-2512-18/+15 * Update algol68g to 2.1.2 (bugfix release)johans2011-01-252-5/+3 * Update to the 20110122 snapshot of GCC 4.6.0. Among others, thisgerald2011-01-248-44/+44 * Update to 1.3.1 and simplify the conditionalsahil2011-01-242-18/+17 * Update Algol 68 Genie compiler to 2.1.1johans2011-01-224-94/+16 * Update to the 20110118 snapshot of GCC 4.4.6.gerald2011-01-212-9/+9 * Update to the 20110120 snapshot of GCC 4.5.3.gerald2011-01-212-11/+11 * Mark DEPRECATED and set EXPIRATION_DATE: port has been broken for nearlyarved2011-01-201-0/+2 * Update to the 20110115 snapshot of GCC 4.6.0.gerald2011-01-178-52/+52 * Update to the 20110108 snapshot of GCC 4.6.0.gerald2011-01-158-44/+44 * - Update to 20101225 snapshotswills2011-01-153-5/+12 * Update to the 20110113 snapshot of 4.5.3.gerald2011-01-152-11/+11 * Update to the 20110111 snapshot of GCC 4.4.6.gerald2011-01-152-9/+9 * Update afnix to 2.0.0johans2011-01-145-46/+91 * - update to 1.1dinoex2011-01-143-9/+15 * Correct /usr/local/include -> ${LOCALBASE}/includejohans2011-01-121-1/+1 * Set CPPFLAGS and pass it to configure explicitly.johans2011-01-121-1/+2 * - Fix pkg-plist in case when gdbm is not present.stas2011-01-122-1/+10 * dmd2 fails to build under FreeBSD 7.cy2011-01-121-0/+4 * Enable MYSQLND by default.ale2011-01-122-6/+6 * Mark BROKEN: does not fetch.erwin2011-01-111-0/+2 * Update to 0.09.tobez2011-01-112-4/+4 * - Update to 1.2.stas2011-01-118-1460/+2045 * Update to 5.2.17mm2011-01-102-4/+4 * Update to 5.3.5 release to fix CVE-2010-4645.ale2011-01-094-10/+14 * Update to the 20110106 snapshot of GCC 4.5.3.gerald2011-01-072-11/+11 * - Update to use a newer gcc as base, this reduced the size of the patches.alepulver2011-01-074-343/+12 * - Use bundled parrot and unbreakpav2011-01-062-26/+777 * - Update to 4.181tabthorpe2011-01-053-8/+4 * - Update to 1.7.6wen2011-01-042-7/+5 * Update 1.065 --> 1.066cy2011-01-042-3/+3 * Add Digital Mars D version 2 to ports.cy2011-01-041-0/+1 * Welcome to the new Digital Mars D Version 2 port.cy2011-01-046-221/+374 * Remove the port lang/squeak-dev since the port is brokenbsam2011-01-0316-517/+0 * Update to the 20101221 snapshot of GCC 4.4.6.gerald2011-01-032-14/+11 * Update to 1.01makc2011-01-022-4/+3 * Update to the 20110101 snapshot of GCC 4.6.0.gerald2011-01-028-44/+44 * Garbage-collect expired ports:rene2011-01-0115-433/+0 * - Update suhosin patchmm2010-12-312-3/+3 * Update suhosin patch. This is a NOP, so don't bump PORTREVISION.ale2010-12-314-22/+6 * Update to the 20101230 snapshot of GCC 4.5.3.gerald2010-12-312-11/+11 * Update to 1.5.6.knu2010-12-304-3266/+1643 * - DISTNAME= ${PORTNAME}-${PORTVERSION} is the default and not needed.pgollucci2010-12-303-3/+0 * - Update GNU awk to 3.1.8johans2010-12-295-22/+10 * Update to snapshot 101228lme2010-12-282-3/+3 * Reset krion@FreeBSD.org due to 6 months of inactivity and maintainer-linimon2010-12-281-1/+1 * Take over maintainership.cy2010-12-283-3/+3 * - change MAINTAINER to my @FreeBSD.org addressflo2010-12-281-1/+1 * Update to the 20101225 snapshot of GCC 4.6.0. This addresses thegerald2010-12-2712-56/+52 * Update to the 20101223 snapshot of GCC 4.5.3. This is nearly the samegerald2010-12-272-11/+11 * - Use canonical format for FreeBSD.org MAINTAINER addressessunpoet2010-12-262-4/+1 * - Update to 05_20101219gahr2010-12-244-14/+24 * In FreeBSD jails, the source and destination address of connectionsolgeni2010-12-234-0/+68 * Fix man page section for fpm.ale2010-12-232-2/+2 * Sync to final (for now) bsd.autotools.mkade2010-12-233-3/+6 * Update to 2.11.0skv2010-12-224-7/+9 * Update to 5.3.4 release.ale2010-12-206-54/+48 * - Update to 1.11gahr2010-12-202-4/+3 * Clean up ruby pkg-plists:pgollucci2010-12-202-15/+15 * Unbreak by adding build dependency on perlmakc2010-12-192-3/+1 * - Update smalltalk to 3.2.3johans2010-12-194-20/+5 * - Update to 1.00pgollucci2010-12-182-3/+3 * - Pass to perl@pgollucci2010-12-182-2/+2 * Unbreak on amd64.linimon2010-12-181-4/+2 * - Update to 5.2.16mm2010-12-173-16/+3 * Use the Makevar HAVE_COMPAT_IA32_LIBS instead of hard-coding thelinimon2010-12-171-1/+1 * - Support tcl/tk 8.5gahr2010-12-162-2/+1 * Upgrade to version R14B01.olgeni2010-12-1514-252/+98 * Chase devel/icu upgradebapt2010-12-152-2/+3 * - Update to 0.14wen2010-12-153-4/+64 * - Update 1.69.29wen2010-12-152-3/+3 * - Update e17 applications suite to the recent snapshot.stas2010-12-145-14/+25 * - Update to 3.1.3wen2010-12-1412-222/+204 * - Update to 1.69.28pgollucci2010-12-132-4/+3 * - Support = in hostnames compatiable with misc/149510.pgollucci2010-12-124-4/+4 * - Update to 8.1pgollucci2010-12-112-14/+9 * - Support = in hostnames compatiable with misc/149510.pgollucci2010-12-111-1/+1 * - Add patch that fixes PHP bug #53516 (open_basedir not working)mm2010-12-112-0/+13 * Rather than relying on some hackish patches use thre --disable-docsbrooks2010-12-103-33/+1 * - Update to 5.2.15mm2010-12-102-10/+5 * Update to r121368.brooks2010-12-103-13/+15 * Remove obsolete patch: use autoconf-wrapper.ale2010-12-093-39/+0 * - Unbreak build with clangmm2010-12-092-11/+20 * Clean up "current" versions of autotools components to further reduceade2010-12-091-1/+1 * - update to November 2010 release of "Rakudo Star"pgollucci2010-12-083-3/+12 * - Update to 2.10.1pgollucci2010-12-084-4/+10 * - Fix build with new tixpav2010-12-071-4/+5 * Do not touch /etc/manpath.config on -CURRENT after man.d/perl.conf is usedgarga2010-12-065-18/+42 * - add experimental option GNUSTEP_WITH_LIBOBJC2dinoex2010-12-053-0/+67 * Sync to new bsd.autotools.mkade2010-12-04