generateCache¶
| Source (upstream pre-purge) | Current | |
|---|---|---|
| File | consensus/ethash/algorithm.go |
algorithm.go |
| Symbol | generateCache |
generateCache |
| Ref | dde2da0ef~1 |
etc/v1.17.3-full-node |
ECIP-1099 epoch params + ADAPT:
reflect.SliceHeader→unsafe.Slice(#29067)
3-way merge — purge → getc ← upstream¶
pre-purgecore-geth
↗// generateCache creates a verification cache of a given size for an input seed.
// The cache production process involves first sequentially filling up 32 MB of
// memory, then performing two passes of Sergio Demian Lerner's RandMemoHash
// algorithm from Strict Memory Hard Hashing Functions (2014). The output is a
// set of 524288 64-byte values.
// This method places the result into dest in machine byte order.
↗func generateCache(dest []uint32, epoch uint64, epochLength uint64, seed []byte) {
↗ // Print some debug logs to allow analysis on low end devices
logger := log.New("epoch", epoch)
↗ start := time.Now()
defer func() {
elapsed := time.Since(start)
↗ logFn := logger.Debug
if elapsed > 3*time.Second {
logFn = logger.Info
}
↗ logFn("Generated ethash verification cache", "epochLength", epochLength, "elapsed", common.PrettyDuration(elapsed))
↗ }()
// Convert our destination slice to a byte buffer
↗ cache := unsafe.Slice((*byte)(unsafe.Pointer(&dest[0])), len(dest)*4)
↗ // Calculate the number of theoretical rows (we'll store in one buffer nonetheless)
size := uint64(len(cache))
rows := int(size) / hashBytes
↗ // Start a monitoring goroutine to report progress on low end devices
var progress atomic.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", "epochLength", epochLength, "percentage", progress.Load()*100/uint32(rows)/(cacheRounds+1), "elapsed", common.PrettyDuration(time.Since(start)))
↗ }
}
}()
// Create a hasher to reuse between invocations
keccak512 := makeHasher(sha3.NewLegacyKeccak512())
↗ // Sequentially produce the initial dataset
keccak512(cache, seed)
for offset := uint64(hashBytes); offset < size; offset += hashBytes {
keccak512(cache[offset:], cache[offset-hashBytes:offset])
progress.Add(1)
}
// Use a low-round version of randmemohash
temp := make([]byte, hashBytes)
↗ for i := 0; i < cacheRounds; i++ {
for j := 0; j < rows; j++ {
var (
srcOff = ((j - 1 + rows) % rows) * hashBytes
dstOff = j * hashBytes
xorOff = (binary.LittleEndian.Uint32(cache[dstOff:]) % uint32(rows)) * hashBytes
)
bitutil.XORBytes(temp, cache[srcOff:srcOff+hashBytes], cache[xorOff:xorOff+hashBytes])
keccak512(cache[dstOff:], temp)
↗ progress.Add(1)
}
}
// Swap the byte order on big endian systems and return
if !isLittleEndian() {
swap(cache)
}
}