Update Godeps

This commit is contained in:
Gustav Simonsson 2015-04-24 12:23:29 +02:00
parent b6ec1c720f
commit 18eae16b29
35 changed files with 2919 additions and 2299 deletions

4
Godeps/Godeps.json generated
View file

@ -22,8 +22,8 @@
},
{
"ImportPath": "github.com/ethereum/ethash",
"Comment": "v23.1-82-g908aad3",
"Rev": "908aad345c9fbf3ab9bbb94031dc02d0d90df1b8"
"Comment": "v23.1-145-gcd4a47c",
"Rev": "cd4a47c0200433cb48447d49944e3ba74d06729b"
},
{
"ImportPath": "github.com/ethereum/serpent-go",

View file

@ -1,14 +1,27 @@
# making our travis.yml play well with C++11 by obtaining g++4.8
# Taken from this file:
# https://github.com/beark/ftl/blob/master/.travis.yml
language: go
go:
- 1.4.2
before_install:
# for g++4.8 and C++11
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
# for some GO dependencies
- sudo add-apt-repository ppa:beineri/opt-qt541 -y
- sudo apt-get update -y -qq
- sudo apt-get install -yqq libgmp3-dev libreadline6-dev
install:
# ${GOPATH%?} is to remove the ':' from the GOPATH that travis adds
- git clone https://github.com/ethereum/go-ethereum ${GOPATH%?}/src/github.com/ethereum/go-ethereum
- go get github.com/tools/godep
- go get
# go test specific packages
- go get gopkg.in/fatih/set.v0
- go get github.com/huin/goupnp
- go get github.com/jackpal/go-nat-pmp
# need to explicitly request version 1.48 since by default we get 1.46 which does not work with C++11
- sudo apt-get install -qq --yes --force-yes g++-4.8
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 50
# need to explicitly request version 1.48 since by default we get 1.46 which does not work with C++11
- sudo apt-get install -qq wget cmake bash libboost-test1.48-dev libboost-system1.48-dev libboost-filesystem1.48-dev nodejs python-pip python-dev
- sudo apt-get install -qq wget cmake bash libboost-test1.48-dev libboost-system1.48-dev libboost-filesystem1.48-dev nodejs python-pip python-dev valgrind
- sudo pip install virtualenv -q
script: "./test/test.sh"

View file

@ -1,7 +1,22 @@
[![Build Status](https://travis-ci.org/ethereum/ethash.svg?branch=master)](https://travis-ci.org/ethereum/ethash)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/debris/ethash?branch=master&svg=true)](https://ci.appveyor.com/project/debris/ethash-nr37r/branch/master)
# Ethash
For details on this project, please see the Ethereum wiki:
https://github.com/ethereum/wiki/wiki/Ethash
### Coding Style for C++ code:
Follow the same exact style as in [cpp-ethereum](https://github.com/ethereum/cpp-ethereum/blob/develop/CodingStandards.txt)
### Coding Style for C code:
The main thing above all is code consistency.
- Tabs for indentation. A tab is 4 spaces
- Try to stick to the [K&R](http://en.wikipedia.org/wiki/Indent_style#K.26R_style),
especially for the C code.
- Keep the line lengths reasonable. No hard limit on 80 characters but don't go further
than 110. Some people work with multiple buffers next to each other.
Make them like you :)

View file

@ -0,0 +1,43 @@
version: 1.0.0.{build}
environment:
BOOST_ROOT: "c:/projects/ethash/deps/boost"
branches:
only:
- master
- develop
os: Windows Server 2012 R2
clone_folder: c:\projects\ethash
#platform: Any CPU
#configuration: Debug
install:
# by default, all script lines are interpreted as batch
# scripts to run before build
before_build:
- echo "Downloading boost..."
- mkdir c:\projects\ethash\deps
- cd c:\projects\ethash\deps
- curl -O https://build.ethdev.com/builds/windows-precompiled/boost.tar.gz
- echo "Unzipping boost..."
- 7z x boost.tar.gz > nul
- 7z x boost.tar > nul
- ls
- echo "Running cmake..."
- cd c:\projects\ethash
- cmake .
build:
project: ALL_BUILD.vcxproj # path to Visual Studio solution or project
after_build:
- echo "Running tests..."
- cd c:\projects\ethash\test\c\Debug
- Test.exe
- echo "Finished!"

View file

@ -12,21 +12,28 @@ package ethash
/*
#cgo CFLAGS: -std=gnu99 -Wall
#cgo LDFLAGS: -lm
#include "src/libethash/util.c"
#include "src/libethash/internal.c"
#include "src/libethash/sha3.c"
#include "src/libethash/io.c"
#ifdef _WIN32
#include "src/libethash/io_win32.c"
#include "src/libethash/mmap_win32.c"
#else
#include "src/libethash/io_posix.c"
#endif
*/
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"io/ioutil"
"math/big"
"math/rand"
"os"
"path"
"os/user"
"path/filepath"
"runtime"
"sync"
"time"
"unsafe"
@ -38,238 +45,245 @@ import (
"github.com/ethereum/go-ethereum/pow"
)
const epochLength uint64 = 30000
var minDifficulty = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
type ParamsAndCache struct {
params *C.ethash_params
cache *C.ethash_cache
Epoch uint64
var (
DefaultDir string = defaultDir()
TheLight Light
)
const (
cacheSizeForTesting = C.uint64_t(1024)
dagSizeForTesting = C.uint64_t(1024 * 32)
)
func defaultDir() string {
home := os.Getenv("HOME")
if user, err := user.Current(); err == nil {
home = user.HomeDir
}
if runtime.GOOS == "windows" {
return filepath.Join(home, "AppData", "Ethash")
}
return filepath.Join(home, ".ethash")
}
type DAG struct {
dag unsafe.Pointer // full GB of memory for dag
file bool
paramsAndCache *ParamsAndCache
type cache struct {
light *C.struct_ethash_light
}
func freeCache(h *cache) {
C.ethash_light_delete(h.light)
}
func makeCache(blockNum uint64, test bool) *cache {
seedHash, _ := GetSeedHash(blockNum)
size := C.ethash_get_cachesize(C.uint64_t(blockNum))
if test {
size = cacheSizeForTesting
}
light := C.ethash_light_new(size, (*C.ethash_h256_t)(unsafe.Pointer(&seedHash[0])))
cache := &cache{light}
runtime.SetFinalizer(cache, freeCache)
return cache
}
// Light wraps an ethash cache for light client verification
// of block nonces.
type Light struct {
test bool // if set use a smaller cache size
// This is a one-element cache of caches.
// TODO: keep multiple caches for recent epochs.
mu sync.RWMutex
epoch uint64
cache *cache
}
func (l *Light) getCache(blockNum uint64) *cache {
l.mu.RLock()
epoch := blockNum / epochLength
if l.epoch == epoch && l.cache != nil {
l.mu.RUnlock()
return l.cache
}
l.mu.RUnlock()
// Create the actual cache for the epoch.
// No lock is being held, so multiple goroutines
// might perform the generation and fight for the lock below.
cache := makeCache(blockNum, l.test)
l.mu.Lock()
if l.epoch != epoch || l.cache == nil {
l.cache = cache
}
l.mu.Unlock()
return cache
}
func (l *Light) Verify(block pow.Block) bool {
return l.verify(block.HashNoNonce(), block.MixDigest(), block.Difficulty(), block.NumberU64(), block.Nonce())
}
func (l *Light) verify(hash common.Hash, mixDigest common.Hash, difficulty *big.Int, blockNum uint64, nonce uint64) bool {
// Make sure the block num is valid
if blockNum >= epochLength*2048 {
glog.V(logger.Info).Infoln(fmt.Sprintf("Block number exceeds limit, invalid (value is %v, limit is %v)",
blockNum, epochLength*2048))
return false
}
// First check: make sure header, mixDigest, nonce are correct without hitting the cache
// This is to prevent DOS attacks
chash := (*C.ethash_h256_t)(unsafe.Pointer(&hash[0]))
cnonce := C.uint64_t(nonce)
target := new(big.Int).Div(minDifficulty, difficulty)
cache := l.getCache(blockNum)
size := C.ethash_get_datasize(C.uint64_t(blockNum))
if l.test {
size = dagSizeForTesting
}
var ret C.ethash_return_value_t
C.ethash_light_compute(&ret, cache.light, size, chash, cnonce)
result := common.Bytes2Big(C.GoBytes(unsafe.Pointer(&ret.result), C.int(32)))
return result.Cmp(target) <= 0
}
type dag struct {
mu sync.Mutex // prevents double free
full *C.struct_ethash_full
}
func freeDAG(h *dag) {
h.mu.Lock()
defer h.mu.Unlock()
if h.full != nil {
C.ethash_full_delete(h.full)
h.full = nil
}
}
func makeDAG(blockNum uint64, test bool, dir string) *dag {
if dir == "" {
dir = DefaultDir
}
seedHash, _ := GetSeedHash(blockNum)
cache := makeCache(blockNum, test)
size := C.ethash_get_cachesize(C.uint64_t(blockNum))
if test {
size = dagSizeForTesting
}
full := C.ethash_full_new(
C.CString(dir),
(*C.ethash_h256_t)(unsafe.Pointer(&seedHash[0])),
size,
C.ethash_light_get_cache(cache.light),
nil,
)
dag := &dag{full: full}
runtime.SetFinalizer(dag, freeDAG)
return dag
}
type Full struct {
Dir string // use this to specify a non-default DAG directory
test bool // if set use a smaller DAG size
turbo bool
hashRate int64
mu sync.Mutex // protects the fields below
epoch uint64 // epoch number of current full dag
dag *dag // current full DAG
}
func (pow *Full) getDAG(blockNum uint64) *dag {
pow.mu.Lock()
defer pow.mu.Unlock()
epoch := blockNum / epochLength
if pow.epoch == epoch && pow.dag != nil {
return pow.dag // up to date
}
// Generate a new DAG.
// This computation is very very expensive.
// The lock should prevent more than one of them
// to run at the same time.
pow.dag = makeDAG(blockNum, pow.test, pow.Dir)
return pow.dag
}
func (pow *Full) Search(block pow.Block, stop <-chan struct{}) (nonce uint64, mixDigest, seedHash []byte) {
dag := pow.getDAG(block.NumberU64())
r := rand.New(rand.NewSource(time.Now().UnixNano()))
miningHash := block.HashNoNonce()
diff := block.Difficulty()
i := int64(0)
starti := i
start := time.Now().UnixNano()
nonce = uint64(r.Int63())
cMiningHash := (*C.ethash_h256_t)(unsafe.Pointer(&miningHash[0]))
target := new(big.Int).Div(minDifficulty, diff)
var ret C.ethash_return_value_t
for {
select {
case <-stop:
pow.hashRate = 0
return 0, nil, nil
default:
i++
elapsed := time.Now().UnixNano() - start
hashes := ((float64(1e9) / float64(elapsed)) * float64(i-starti)) / 1000
pow.hashRate = int64(hashes)
C.ethash_full_compute(&ret, dag.full, cMiningHash, C.uint64_t(nonce))
result := common.Bytes2Big(C.GoBytes(unsafe.Pointer(&ret.result), C.int(32)))
// TODO: disagrees with the spec https://github.com/ethereum/wiki/wiki/Ethash#mining
if result.Cmp(target) <= 0 {
mixDigest = C.GoBytes(unsafe.Pointer(&ret.mix_hash), C.int(32))
seedHash, _ = GetSeedHash(block.NumberU64()) // This seedhash is useless
return nonce, mixDigest, seedHash
}
nonce += 1
}
if !pow.turbo {
time.Sleep(20 * time.Microsecond)
}
}
}
func (pow *Full) GetHashrate() int64 {
return pow.hashRate
}
func (pow *Full) Turbo(on bool) {
pow.turbo = on
}
type Ethash struct {
turbo bool
HashRate int64
chainManager pow.ChainManager
dag *DAG
paramsAndCache *ParamsAndCache
ret *C.ethash_return_value
dagMutex *sync.RWMutex
cacheMutex *sync.RWMutex
Light
Full
}
func parseNonce(nonce []byte) (uint64, error) {
nonceBuf := bytes.NewBuffer(nonce)
nonceInt, err := binary.ReadUvarint(nonceBuf)
if err != nil {
return 0, err
}
return nonceInt, nil
func New() *Ethash {
return &Ethash{Light: TheLight}
}
const epochLength uint64 = 30000
func makeParamsAndCache(chainManager pow.ChainManager, blockNum uint64) (*ParamsAndCache, error) {
if blockNum >= epochLength*2048 {
return nil, fmt.Errorf("block number is out of bounds (value %v, limit is %v)", blockNum, epochLength*2048)
}
paramsAndCache := &ParamsAndCache{
params: new(C.ethash_params),
cache: new(C.ethash_cache),
Epoch: blockNum / epochLength,
}
C.ethash_params_init(paramsAndCache.params, C.uint32_t(uint32(blockNum)))
paramsAndCache.cache.mem = C.malloc(C.size_t(paramsAndCache.params.cache_size))
seedHash, err := GetSeedHash(blockNum)
if err != nil {
return nil, err
}
glog.V(logger.Info).Infof("Making cache for epoch: %d (%v) (%x)\n", paramsAndCache.Epoch, blockNum, seedHash)
start := time.Now()
C.ethash_mkcache(paramsAndCache.cache, paramsAndCache.params, (*C.ethash_blockhash_t)(unsafe.Pointer(&seedHash[0])))
if glog.V(logger.Info) {
glog.Infoln("Took:", time.Since(start))
}
return paramsAndCache, nil
}
func (pow *Ethash) UpdateCache(blockNum uint64, force bool) error {
pow.cacheMutex.Lock()
defer pow.cacheMutex.Unlock()
thisEpoch := blockNum / epochLength
if force || pow.paramsAndCache.Epoch != thisEpoch {
var err error
pow.paramsAndCache, err = makeParamsAndCache(pow.chainManager, blockNum)
if err != nil {
panic(err)
}
}
return nil
}
func makeDAG(p *ParamsAndCache) *DAG {
d := &DAG{
dag: C.malloc(C.size_t(p.params.full_size)),
file: false,
paramsAndCache: p,
}
donech := make(chan string)
go func() {
t := time.NewTicker(5 * time.Second)
tstart := time.Now()
done:
for {
select {
case <-t.C:
glog.V(logger.Info).Infof("... still generating DAG (%v) ...\n", time.Since(tstart).Seconds())
case str := <-donech:
glog.V(logger.Info).Infof("... %s ...\n", str)
break done
}
}
}()
C.ethash_compute_full_data(d.dag, p.params, p.cache)
donech <- "DAG generation completed"
return d
}
func (pow *Ethash) writeDagToDisk(dag *DAG, epoch uint64) *os.File {
if epoch > 2048 {
panic(fmt.Errorf("Epoch must be less than 2048 (is %v)", epoch))
}
data := C.GoBytes(unsafe.Pointer(dag.dag), C.int(dag.paramsAndCache.params.full_size))
file, err := os.Create("/tmp/dag")
if err != nil {
panic(err)
}
dataEpoch := make([]byte, 8)
binary.BigEndian.PutUint64(dataEpoch, epoch)
file.Write(dataEpoch)
file.Write(data)
return file
}
func (pow *Ethash) UpdateDAG() {
blockNum := pow.chainManager.CurrentBlock().NumberU64()
if blockNum >= epochLength*2048 {
// This will crash in the 2030s or 2040s
panic(fmt.Errorf("Current block number is out of bounds (value %v, limit is %v)", blockNum, epochLength*2048))
}
pow.dagMutex.Lock()
defer pow.dagMutex.Unlock()
thisEpoch := blockNum / epochLength
if pow.dag == nil || pow.dag.paramsAndCache.Epoch != thisEpoch {
if pow.dag != nil && pow.dag.dag != nil {
C.free(pow.dag.dag)
pow.dag.dag = nil
}
if pow.dag != nil && pow.dag.paramsAndCache.cache.mem != nil {
C.free(pow.dag.paramsAndCache.cache.mem)
pow.dag.paramsAndCache.cache.mem = nil
}
// Make the params and cache for the DAG
paramsAndCache, err := makeParamsAndCache(pow.chainManager, blockNum)
if err != nil {
panic(err)
}
// TODO: On non-SSD disks, loading the DAG from disk takes longer than generating it in memory
pow.paramsAndCache = paramsAndCache
path := path.Join("/", "tmp", "dag")
pow.dag = nil
glog.V(logger.Info).Infoln("Retrieving DAG")
start := time.Now()
file, err := os.Open(path)
if err != nil {
glog.V(logger.Info).Infof("No DAG found. Generating new DAG in '%s' (this takes a while)...\n", path)
pow.dag = makeDAG(paramsAndCache)
file = pow.writeDagToDisk(pow.dag, thisEpoch)
pow.dag.file = true
} else {
data, err := ioutil.ReadAll(file)
if err != nil {
glog.V(logger.Info).Infof("DAG load err: %v\n", err)
}
if len(data) < 8 {
glog.V(logger.Info).Infof("DAG in '%s' is less than 8 bytes, it must be corrupted. Generating new DAG (this takes a while)...\n", path)
pow.dag = makeDAG(paramsAndCache)
file = pow.writeDagToDisk(pow.dag, thisEpoch)
pow.dag.file = true
} else {
dataEpoch := binary.BigEndian.Uint64(data[0:8])
if dataEpoch < thisEpoch {
glog.V(logger.Info).Infof("DAG in '%s' is stale. Generating new DAG (this takes a while)...\n", path)
pow.dag = makeDAG(paramsAndCache)
file = pow.writeDagToDisk(pow.dag, thisEpoch)
pow.dag.file = true
} else if dataEpoch > thisEpoch {
// FIXME
panic(fmt.Errorf("Saved DAG in '%s' reports to be from future epoch %v (current epoch is %v)\n", path, dataEpoch, thisEpoch))
} else if len(data) != (int(paramsAndCache.params.full_size) + 8) {
glog.V(logger.Info).Infof("DAG in '%s' is corrupted. Generating new DAG (this takes a while)...\n", path)
pow.dag = makeDAG(paramsAndCache)
file = pow.writeDagToDisk(pow.dag, thisEpoch)
pow.dag.file = true
} else {
data = data[8:]
pow.dag = &DAG{
dag: unsafe.Pointer(&data[0]),
file: true,
paramsAndCache: paramsAndCache,
}
}
}
}
glog.V(logger.Info).Infoln("Took:", time.Since(start))
file.Close()
}
}
func New(chainManager pow.ChainManager) *Ethash {
paramsAndCache, err := makeParamsAndCache(chainManager, chainManager.CurrentBlock().NumberU64())
if err != nil {
panic(err)
}
func NewForTesting() *Ethash {
return &Ethash{
turbo: true,
paramsAndCache: paramsAndCache,
chainManager: chainManager,
dag: nil,
cacheMutex: new(sync.RWMutex),
dagMutex: new(sync.RWMutex),
Light{test: true},
Full{test: true},
}
}
func (pow *Ethash) DAGSize() uint64 {
return uint64(pow.dag.paramsAndCache.params.full_size)
}
func (pow *Ethash) CacheSize() uint64 {
return uint64(pow.paramsAndCache.params.cache_size)
}
func GetSeedHash(blockNum uint64) ([]byte, error) {
if blockNum >= epochLength*2048 {
return nil, fmt.Errorf("block number is out of bounds (value %v, limit is %v)", blockNum, epochLength*2048)
@ -278,9 +292,6 @@ func GetSeedHash(blockNum uint64) ([]byte, error) {
epoch := blockNum / epochLength
seedHash := make([]byte, 32)
var i uint64
for i = 0; i < 32; i++ {
seedHash[i] = 0
}
for i = 0; i < epoch; i++ {
seedHash = crypto.Sha3(seedHash)
}
@ -288,151 +299,10 @@ func GetSeedHash(blockNum uint64) ([]byte, error) {
}
func (pow *Ethash) Stop() {
pow.cacheMutex.Lock()
pow.dagMutex.Lock()
defer pow.dagMutex.Unlock()
defer pow.cacheMutex.Unlock()
if pow.paramsAndCache.cache != nil {
C.free(pow.paramsAndCache.cache.mem)
}
if pow.dag.dag != nil && !pow.dag.file {
C.free(pow.dag.dag)
}
if pow.dag != nil && pow.dag.paramsAndCache != nil && pow.dag.paramsAndCache.cache.mem != nil {
C.free(pow.dag.paramsAndCache.cache.mem)
pow.dag.paramsAndCache.cache.mem = nil
}
pow.dag.dag = nil
}
func (pow *Ethash) Search(block pow.Block, stop <-chan struct{}) (uint64, []byte, []byte) {
pow.UpdateDAG()
pow.dagMutex.RLock()
defer pow.dagMutex.RUnlock()
r := rand.New(rand.NewSource(time.Now().UnixNano()))
miningHash := block.HashNoNonce()
diff := block.Difficulty()
i := int64(0)
starti := i
start := time.Now().UnixNano()
nonce := uint64(r.Int63())
cMiningHash := (*C.ethash_blockhash_t)(unsafe.Pointer(&miningHash[0]))
target := new(big.Int).Div(minDifficulty, diff)
var ret C.ethash_return_value
for {
select {
case <-stop:
pow.HashRate = 0
return 0, nil, nil
default:
i++
elapsed := time.Now().UnixNano() - start
hashes := ((float64(1e9) / float64(elapsed)) * float64(i-starti)) / 1000
pow.HashRate = int64(hashes)
C.ethash_full(&ret, pow.dag.dag, pow.dag.paramsAndCache.params, cMiningHash, C.uint64_t(nonce))
result := common.Bytes2Big(C.GoBytes(unsafe.Pointer(&ret.result), C.int(32)))
// TODO: disagrees with the spec https://github.com/ethereum/wiki/wiki/Ethash#mining
if result.Cmp(target) <= 0 {
mixDigest := C.GoBytes(unsafe.Pointer(&ret.mix_hash), C.int(32))
seedHash, err := GetSeedHash(block.NumberU64()) // This seedhash is useless
if err != nil {
panic(err)
}
return nonce, mixDigest, seedHash
}
nonce += 1
}
if !pow.turbo {
time.Sleep(20 * time.Microsecond)
}
}
}
func (pow *Ethash) Verify(block pow.Block) bool {
return pow.verify(block.HashNoNonce(), block.MixDigest(), block.Difficulty(), block.NumberU64(), block.Nonce())
}
func (pow *Ethash) verify(hash common.Hash, mixDigest common.Hash, difficulty *big.Int, blockNum uint64, nonce uint64) bool {
// Make sure the block num is valid
if blockNum >= epochLength*2048 {
glog.V(logger.Info).Infoln(fmt.Sprintf("Block number exceeds limit, invalid (value is %v, limit is %v)",
blockNum, epochLength*2048))
return false
}
// First check: make sure header, mixDigest, nonce are correct without hitting the cache
// This is to prevent DOS attacks
chash := (*C.ethash_blockhash_t)(unsafe.Pointer(&hash[0]))
cnonce := C.uint64_t(nonce)
target := new(big.Int).Div(minDifficulty, difficulty)
var pAc *ParamsAndCache
// If its an old block (doesn't use the current cache)
// get the cache for it but don't update (so we don't need the mutex)
// Otherwise, it's the current block or a future block.
// If current, updateCache will do nothing.
if blockNum/epochLength < pow.paramsAndCache.Epoch {
var err error
// If we can't make the params for some reason, this block is invalid
pAc, err = makeParamsAndCache(pow.chainManager, blockNum)
if err != nil {
glog.V(logger.Info).Infoln("big fucking eror", err)
return false
}
} else {
pow.UpdateCache(blockNum, false)
pow.cacheMutex.RLock()
defer pow.cacheMutex.RUnlock()
pAc = pow.paramsAndCache
}
ret := new(C.ethash_return_value)
C.ethash_light(ret, pAc.cache, pAc.params, chash, cnonce)
result := common.Bytes2Big(C.GoBytes(unsafe.Pointer(&ret.result), C.int(32)))
return result.Cmp(target) <= 0
}
func (pow *Ethash) GetHashrate() int64 {
return pow.HashRate
}
func (pow *Ethash) Turbo(on bool) {
pow.turbo = on
}
func (pow *Ethash) FullHash(nonce uint64, miningHash []byte) []byte {
pow.UpdateDAG()
pow.dagMutex.Lock()
defer pow.dagMutex.Unlock()
cMiningHash := (*C.ethash_blockhash_t)(unsafe.Pointer(&miningHash[0]))
cnonce := C.uint64_t(nonce)
ret := new(C.ethash_return_value)
// pow.hash is the output/return of ethash_full
C.ethash_full(ret, pow.dag.dag, pow.paramsAndCache.params, cMiningHash, cnonce)
ghash_full := C.GoBytes(unsafe.Pointer(&ret.result), 32)
return ghash_full
}
func (pow *Ethash) LightHash(nonce uint64, miningHash []byte) []byte {
cMiningHash := (*C.ethash_blockhash_t)(unsafe.Pointer(&miningHash[0]))
cnonce := C.uint64_t(nonce)
ret := new(C.ethash_return_value)
C.ethash_light(ret, pow.paramsAndCache.cache, pow.paramsAndCache.params, cMiningHash, cnonce)
ghash_light := C.GoBytes(unsafe.Pointer(&ret.result), 32)
return ghash_light
pow.Light.mu.Lock()
pow.Full.mu.Lock()
defer pow.Full.mu.Unlock()
defer pow.Light.mu.Unlock()
pow.Full.dag = nil
pow.Light.cache = nil
}

View file

@ -0,0 +1,73 @@
package ethash
import (
"bytes"
"encoding/hex"
"log"
"math/big"
"testing"
"sync"
"github.com/ethereum/go-ethereum/common"
)
type testBlock struct {
difficulty *big.Int
hashNoNonce common.Hash
nonce uint64
mixDigest common.Hash
number uint64
}
func (b *testBlock) Difficulty() *big.Int { return b.difficulty }
func (b *testBlock) HashNoNonce() common.Hash { return b.hashNoNonce }
func (b *testBlock) Nonce() uint64 { return b.nonce }
func (b *testBlock) MixDigest() common.Hash { return b.mixDigest }
func (b *testBlock) NumberU64() uint64 { return b.number }
func TestEthash(t *testing.T) {
block := &testBlock{difficulty: big.NewInt(10)}
eth := NewForTesting()
nonce, _, _ := eth.Search(block, nil)
block.nonce = nonce
// Verify the block concurrently to check for data races.
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
if !eth.Verify(block) {
t.Error("Block could not be verified")
}
wg.Done()
}()
}
wg.Wait()
}
func TestGetSeedHash(t *testing.T) {
seed0, err := GetSeedHash(0)
if err != nil {
t.Errorf("Failed to get seedHash for block 0: %v", err)
}
if bytes.Compare(seed0, make([]byte, 32)) != 0 {
log.Printf("seedHash for block 0 should be 0s, was: %v\n", seed0)
}
seed1, err := GetSeedHash(30000)
if err != nil {
t.Error(err)
}
// From python:
// > from pyethash import get_seedhash
// > get_seedhash(30000)
expectedSeed1, err := hex.DecodeString("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
if err != nil {
t.Error(err)
}
if bytes.Compare(seed1, expectedSeed1) != 0 {
log.Printf("seedHash for block 1 should be: %v,\nactual value: %v\n", expectedSeed1, seed1)
}
}

View file

@ -1,23 +1,36 @@
#!/usr/bin/env python
import os
from distutils.core import setup, Extension
sources = [
'src/python/core.c',
'src/libethash/io.c',
'src/libethash/util.c',
'src/libethash/internal.c',
'src/libethash/sha3.c']
if os.name == 'nt':
sources += [
'src/libethash/io_win32.c',
'src/libethash/mmap_win32.c',
]
else:
sources += [
'src/libethash/io_posix.c'
]
depends = [
'src/libethash/ethash.h',
'src/libethash/compiler.h',
'src/libethash/data_sizes.h',
'src/libethash/endian.h',
'src/libethash/ethash.h',
'src/libethash/io.h',
'src/libethash/fnv.h',
'src/libethash/internal.h',
'src/libethash/sha3.h',
'src/libethash/util.h',
]
pyethash = Extension('pyethash',
sources=[
'src/python/core.c',
'src/libethash/util.c',
'src/libethash/internal.c',
'src/libethash/sha3.c'],
depends=[
'src/libethash/ethash.h',
'src/libethash/compiler.h',
'src/libethash/data_sizes.h',
'src/libethash/endian.h',
'src/libethash/ethash.h',
'src/libethash/fnv.h',
'src/libethash/internal.h',
'src/libethash/sha3.h',
'src/libethash/util.h'
],
sources=sources,
depends=depends,
extra_compile_args=["-Isrc/", "-std=gnu99", "-Wall"])
setup(

View file

@ -3,56 +3,56 @@ include_directories(..)
set(CMAKE_BUILD_TYPE Release)
if (MSVC)
add_definitions("/openmp")
add_definitions("/openmp")
endif()
# enable C++11, should probably be a bit more specific about compiler
if (NOT MSVC)
SET(CMAKE_CXX_FLAGS "-std=c++11")
SET(CMAKE_CXX_FLAGS "-std=c++11")
endif()
if (NOT MPI_FOUND)
find_package(MPI)
find_package(MPI)
endif()
if (NOT CRYPTOPP_FOUND)
find_package(CryptoPP 5.6.2)
find_package(CryptoPP 5.6.2)
endif()
if (CRYPTOPP_FOUND)
add_definitions(-DWITH_CRYPTOPP)
add_definitions(-DWITH_CRYPTOPP)
find_package (Threads REQUIRED)
endif()
if (NOT OpenCL_FOUND)
find_package(OpenCL)
find_package(OpenCL)
endif()
if (OpenCL_FOUND)
add_definitions(-DWITH_OPENCL)
include_directories(${OpenCL_INCLUDE_DIRS})
list(APPEND FILES ethash_cl_miner.cpp ethash_cl_miner.h)
add_definitions(-DWITH_OPENCL)
include_directories(${OpenCL_INCLUDE_DIRS})
list(APPEND FILES ethash_cl_miner.cpp ethash_cl_miner.h)
endif()
if (MPI_FOUND)
include_directories(${MPI_INCLUDE_PATH})
add_executable (Benchmark_MPI_FULL benchmark.cpp)
target_link_libraries (Benchmark_MPI_FULL ${ETHHASH_LIBS} ${MPI_LIBRARIES})
SET_TARGET_PROPERTIES(Benchmark_MPI_FULL PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} ${MPI_COMPILE_FLAGS} -DFULL -DMPI")
include_directories(${MPI_INCLUDE_PATH})
add_executable (Benchmark_MPI_FULL benchmark.cpp)
target_link_libraries (Benchmark_MPI_FULL ${ETHHASH_LIBS} ${MPI_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
SET_TARGET_PROPERTIES(Benchmark_MPI_FULL PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} ${MPI_COMPILE_FLAGS} -DFULL -DMPI")
add_executable (Benchmark_MPI_LIGHT benchmark.cpp)
target_link_libraries (Benchmark_MPI_LIGHT ${ETHHASH_LIBS} ${MPI_LIBRARIES})
SET_TARGET_PROPERTIES(Benchmark_MPI_LIGHT PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} ${MPI_COMPILE_FLAGS} -DMPI")
add_executable (Benchmark_MPI_LIGHT benchmark.cpp)
target_link_libraries (Benchmark_MPI_LIGHT ${ETHHASH_LIBS} ${MPI_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
SET_TARGET_PROPERTIES(Benchmark_MPI_LIGHT PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} ${MPI_COMPILE_FLAGS} -DMPI")
endif()
add_executable (Benchmark_FULL benchmark.cpp)
target_link_libraries (Benchmark_FULL ${ETHHASH_LIBS})
target_link_libraries (Benchmark_FULL ${ETHHASH_LIBS} ${CMAKE_THREAD_LIBS_INIT})
SET_TARGET_PROPERTIES(Benchmark_FULL PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -DFULL")
add_executable (Benchmark_LIGHT benchmark.cpp)
target_link_libraries (Benchmark_LIGHT ${ETHHASH_LIBS})
target_link_libraries (Benchmark_LIGHT ${ETHHASH_LIBS} ${CMAKE_THREAD_LIBS_INIT})
if (OpenCL_FOUND)
add_executable (Benchmark_CL benchmark.cpp)
target_link_libraries (Benchmark_CL ${ETHHASH_LIBS} ethash-cl)
SET_TARGET_PROPERTIES(Benchmark_CL PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -DOPENCL")
endif()
add_executable (Benchmark_CL benchmark.cpp)
target_link_libraries (Benchmark_CL ${ETHHASH_LIBS} ethash-cl ${CMAKE_THREAD_LIBS_INIT})
SET_TARGET_PROPERTIES(Benchmark_CL PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -DOPENCL")
endif()

View file

@ -96,6 +96,11 @@ static std::string bytesToHexString(uint8_t const* bytes, unsigned size)
return str;
}
static std::string bytesToHexString(ethash_h256_t const *hash, unsigned size)
{
return bytesToHexString((uint8_t*)hash, size);
}
extern "C" int main(void)
{
// params for ethash
@ -106,12 +111,12 @@ extern "C" int main(void)
//params.full_size = 8209 * 4096; // 8MBish;
//params.cache_size = 8209*4096;
//params.cache_size = 2053*4096;
ethash_blockhash_t seed;
ethash_blockhash_t previous_hash;
ethash_h256_t seed;
ethash_h256_t previous_hash;
memcpy(&seed, hexStringToBytes("9410b944535a83d9adf6bbdcc80e051f30676173c16ca0d32d6f1263fc246466").data(), 32);
memcpy(&previous_hash, hexStringToBytes("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").data(), 32);
// allocate page aligned buffer for dataset
#ifdef FULL
void* full_mem_buf = malloc(params.full_size + 4095);
@ -122,24 +127,24 @@ extern "C" int main(void)
ethash_cache cache;
cache.mem = cache_mem;
// compute cache or full data
{
auto startTime = high_resolution_clock::now();
ethash_mkcache(&cache, &params, seed);
ethash_mkcache(&cache, &params, &seed);
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now() - startTime).count();
ethash_blockhash_t cache_hash;
ethash_h256_t cache_hash;
SHA3_256(&cache_hash, (uint8_t const*)cache_mem, params.cache_size);
debugf("ethash_mkcache: %ums, sha3: %s\n", (unsigned)((time*1000)/CLOCKS_PER_SEC), bytesToHexString(cache_hash,sizeof(cache_hash)).data());
debugf("ethash_mkcache: %ums, sha3: %s\n", (unsigned)((time*1000)/CLOCKS_PER_SEC), bytesToHexString(&cache_hash, sizeof(cache_hash)).data());
// print a couple of test hashes
{
auto startTime = high_resolution_clock::now();
ethash_return_value hash;
ethash_light(&hash, &cache, &params, previous_hash, 0);
ethash_light(&hash, &cache, &params, &previous_hash, 0);
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now() - startTime).count();
debugf("ethash_light test: %ums, %s\n", (unsigned)time, bytesToHexString(hash.result, 32).data());
debugf("ethash_light test: %ums, %s\n", (unsigned)time, bytesToHexString(&hash.result, 32).data());
}
#ifdef FULL
@ -154,34 +159,34 @@ extern "C" int main(void)
ethash_cl_miner miner;
{
auto startTime = high_resolution_clock::now();
if (!miner.init(params, seed))
if (!miner.init(params, &seed))
exit(-1);
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now() - startTime).count();
debugf("ethash_cl_miner init: %ums\n", (unsigned)time);
debugf("ethash_cl_miner init: %ums\n", (unsigned)time);
}
#endif
#ifdef FULL
{
auto startTime = high_resolution_clock::now();
{
auto startTime = high_resolution_clock::now();
ethash_return_value hash;
ethash_full(&hash, full_mem, &params, previous_hash, 0);
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now() - startTime).count();
debugf("ethash_full test: %uns, %s\n", (unsigned)time);
}
ethash_full(&hash, full_mem, &params, &previous_hash, 0);
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now() - startTime).count();
debugf("ethash_full test: %uns\n", (unsigned)time);
}
#endif
#ifdef OPENCL
// validate 1024 hashes against CPU
miner.hash(g_hashes, previous_hash, 0, 1024);
miner.hash(g_hashes, (uint8_t*)&previous_hash, 0, 1024);
for (unsigned i = 0; i != 1024; ++i)
{
ethash_return_value hash;
ethash_light(&hash, &cache, &params, previous_hash, i);
if (memcmp(hash.result, g_hashes + 32*i, 32) != 0)
ethash_light(&hash, &cache, &params, &previous_hash, i);
if (memcmp(&hash.result, g_hashes + 32*i, 32) != 0)
{
debugf("nonce %u failed: %s %s\n", i, bytesToHexString(g_hashes + 32*i, 32).c_str(), bytesToHexString(hash.result, 32).c_str());
debugf("nonce %u failed: %s %s\n", i, bytesToHexString(g_hashes + 32*i, 32).c_str(), bytesToHexString(&hash.result, 32).c_str());
static unsigned c = 0;
if (++c == 16)
{
@ -189,14 +194,14 @@ extern "C" int main(void)
}
}
}
// ensure nothing else is going on
miner.finish();
#endif
auto startTime = high_resolution_clock::now();
unsigned hash_count = trials;
#ifdef OPENCL
{
struct search_hook : ethash_cl_miner::search_hook
@ -220,14 +225,14 @@ extern "C" int main(void)
search_hook hook;
hook.hash_count = 0;
miner.search(previous_hash, 0x000000ffffffffff, hook);
miner.search((uint8_t*)&previous_hash, 0x000000ffffffffff, hook);
for (unsigned i = 0; i != hook.nonce_vec.size(); ++i)
{
uint64_t nonce = hook.nonce_vec[i];
ethash_return_value hash;
ethash_light(&hash, &cache, &params, previous_hash, nonce);
debugf("found: %.8x%.8x -> %s\n", unsigned(nonce>>32), unsigned(nonce), bytesToHexString(hash.result, 32).c_str());
ethash_light(&hash, &cache, &params, &previous_hash, nonce);
debugf("found: %.8x%.8x -> %s\n", unsigned(nonce>>32), unsigned(nonce), bytesToHexString(&hash.result, 32).c_str());
}
hash_count = hook.hash_count;
@ -239,9 +244,9 @@ extern "C" int main(void)
{
ethash_return_value hash;
#ifdef FULL
ethash_full(&hash, full_mem, &params, previous_hash, nonce);
ethash_full(&hash, full_mem, &params, &previous_hash, nonce);
#else
ethash_light(&hash, &cache, &params, previous_hash, nonce);
ethash_light(&hash, &cache, &params, &previous_hash, nonce);
#endif // FULL
}
}

View file

@ -54,14 +54,20 @@ ethash_cl_miner::ethash_cl_miner()
{
}
bool ethash_cl_miner::init(ethash_params const& params, ethash_blockhash_t const *seed, unsigned workgroup_size)
void ethash_cl_miner::finish()
{
if (m_queue())
m_queue.finish();
}
bool ethash_cl_miner::init(ethash_params const& params, ethash_h256_t const *seed, unsigned workgroup_size)
{
// store params
m_params = params;
// get all platforms
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
{
debugf("No OpenCL platforms found.\n");
@ -71,10 +77,10 @@ bool ethash_cl_miner::init(ethash_params const& params, ethash_blockhash_t const
// use default platform
debugf("Using platform: %s\n", platforms[0].getInfo<CL_PLATFORM_NAME>().c_str());
// get GPU device of the default platform
std::vector<cl::Device> devices;
platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
// get GPU device of the default platform
std::vector<cl::Device> devices;
platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
debugf("No OpenCL devices found.\n");
return false;
@ -130,7 +136,7 @@ bool ethash_cl_miner::init(ethash_params const& params, ethash_blockhash_t const
// create buffer for dag
m_dag = cl::Buffer(m_context, CL_MEM_READ_ONLY, params.full_size);
// create buffer for header
m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32);
@ -143,7 +149,7 @@ bool ethash_cl_miner::init(ethash_params const& params, ethash_blockhash_t const
// if this throws then it's because we probably need to subdivide the dag uploads for compatibility
void* dag_ptr = m_queue.enqueueMapBuffer(m_dag, true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, params.full_size);
ethash_compute_full_data(dag_ptr, &params, &cache);
ethash_compute_full_data(dag_ptr, params.full_size, &cache);
m_queue.enqueueUnmapMemObject(m_dag, dag_ptr);
free(cache_mem);
@ -167,7 +173,7 @@ void ethash_cl_miner::hash(uint8_t* ret, uint8_t const* header, uint64_t nonce,
unsigned buf;
};
std::queue<pending_batch> pending;
// update header constant buffer
m_queue.enqueueWriteBuffer(m_header, true, 0, 32, header);
@ -205,7 +211,7 @@ void ethash_cl_miner::hash(uint8_t* ret, uint8_t const* header, uint64_t nonce,
cl::NDRange(m_workgroup_size)
);
m_queue.flush();
pending.push({i, this_count, buf});
i += this_count;
buf = (buf + 1) % c_num_buffers;
@ -284,7 +290,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
// execute it!
m_queue.enqueueNDRangeKernel(m_search_kernel, cl::NullRange, c_search_batch_size, m_workgroup_size);
pending.push({start_nonce, buf});
buf = (buf + 1) % c_num_buffers;
@ -302,9 +308,9 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
{
nonces[i] = batch.start_nonce + results[i+1];
}
m_queue.enqueueUnmapMemObject(m_search_buf[batch.buf], results);
bool exit = num_found && hook.found(nonces, num_found);
exit |= hook.searched(batch.start_nonce, c_search_batch_size); // always report searched before exit
if (exit)

View file

@ -19,7 +19,7 @@ public:
public:
ethash_cl_miner();
bool init(ethash_params const& params, ethash_blockhash_t const *seed, unsigned workgroup_size = 64);
bool init(ethash_params const& params, ethash_h256_t const *seed, unsigned workgroup_size = 64);
void finish();
void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count);

View file

@ -21,7 +21,7 @@ set(FILES util.c
data_sizes.h)
if (MSVC)
list(APPEND FILES io_win32.c)
list(APPEND FILES io_win32.c mmap_win32.c)
else()
list(APPEND FILES io_posix.c)
endif()

File diff suppressed because it is too large Load diff

View file

@ -3,38 +3,6 @@
#include <stdint.h>
#include "compiler.h"
static const uint8_t BitReverseTable256[] =
{
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
static inline uint32_t bitfn_swap32(uint32_t a) {
return (BitReverseTable256[a & 0xff] << 24) |
(BitReverseTable256[(a >> 8) & 0xff] << 16) |
(BitReverseTable256[(a >> 16) & 0xff] << 8) |
(BitReverseTable256[(a >> 24) & 0xff]);
}
static inline uint64_t bitfn_swap64(uint64_t a) {
return ((uint64_t) bitfn_swap32((uint32_t) (a >> 32))) |
(((uint64_t) bitfn_swap32((uint32_t) a)) << 32);
}
#if defined(__MINGW32__) || defined(_WIN32)
# define LITTLE_ENDIAN 1234
# define BYTE_ORDER LITTLE_ENDIAN
@ -53,22 +21,52 @@ static inline uint64_t bitfn_swap64(uint64_t a) {
# define BIG_ENDIAN 1234
# define BYTE_ORDER BIG_ENDIAN
#else
# include <endian.h>
#endif
#if defined(_WIN32)
#include <stdlib.h>
#define ethash_swap_u32(input_) _byteswap_ulong(input_)
#define ethash_swap_u64(input_) _byteswap_uint64(input_)
#elif defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define ethash_swap_u32(input_) OSSwapInt32(input_)
#define ethash_swap_u64(input_) OSSwapInt64(input_)
#else // posix
#include <byteswap.h>
#define ethash_swap_u32(input_) __bswap_32(input_)
#define ethash_swap_u64(input_) __bswap_64(input_)
#endif
#if LITTLE_ENDIAN == BYTE_ORDER
#define fix_endian32(x) (x)
#define fix_endian64(x) (x)
#define fix_endian32(dst_ ,src_) dst_ = src_
#define fix_endian32_same(val_)
#define fix_endian64(dst_, src_) dst_ = src_
#define fix_endian64_same(val_)
#define fix_endian_arr32(arr_, size_)
#define fix_endian_arr64(arr_, size_)
#elif BIG_ENDIAN == BYTE_ORDER
#define fix_endian32(x) bitfn_swap32(x)
#define fix_endian64(x) bitfn_swap64(x)
#define fix_endian32(dst_, src_) dst_ = ethash_swap_u32(src_)
#define fix_endian32_same(val_) val_ = ethash_swap_u32(val_)
#define fix_endian64(dst_, src_) dst_ = ethash_swap_u64(src_
#define fix_endian64_same(val_) val_ = ethash_swap_u64(val_)
#define fix_endian_arr32(arr_, size_) \
do { \
for (unsigned i_ = 0; i_ < (size_), ++i_) { \
arr_[i_] = ethash_swap_u32(arr_[i_]); \
} \
while (0)
#define fix_endian_arr64(arr_, size_) \
do { \
for (unsigned i_ = 0; i_ < (size_), ++i_) { \
arr_[i_] = ethash_swap_u64(arr_[i_]); \
} \
while (0) \
#else
# error "endian not supported"
#endif // BYTE_ORDER
#endif // BYTE_ORDER

View file

@ -42,108 +42,240 @@
extern "C" {
#endif
typedef struct ethash_params {
uint64_t full_size; // Size of full data set (in bytes, multiple of mix size (128)).
uint64_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
} ethash_params;
/// Type of a blockhash
typedef struct ethash_blockhash { uint8_t b[32]; } ethash_blockhash_t;
static inline uint8_t ethash_blockhash_get(ethash_blockhash_t const* hash, unsigned int i)
/// Type of a seedhash/blockhash e.t.c.
typedef struct ethash_h256 { uint8_t b[32]; } ethash_h256_t;
static inline uint8_t ethash_h256_get(ethash_h256_t const* hash, unsigned int i)
{
return hash->b[i];
return hash->b[i];
}
static inline void ethash_blockhash_set(ethash_blockhash_t *hash, unsigned int i, uint8_t v)
static inline void ethash_h256_set(ethash_h256_t* hash, unsigned int i, uint8_t v)
{
hash->b[i] = v;
hash->b[i] = v;
}
static inline void ethash_blockhash_reset(ethash_blockhash_t *hash)
static inline void ethash_h256_reset(ethash_h256_t* hash)
{
memset(hash, 0, 32);
memset(hash, 0, 32);
}
// convenience macro to statically initialize an h256_t
// usage:
// ethash_h256_t a = ethash_h256_static_init(1, 2, 3, ... )
// have to provide all 32 values. If you don't provide all the rest
// will simply be unitialized (not guranteed to be 0)
#define ethash_h256_static_init(...) \
{ {__VA_ARGS__} }
struct ethash_light;
typedef struct ethash_light* ethash_light_t;
struct ethash_full;
typedef struct ethash_full* ethash_full_t;
typedef int(*ethash_callback_t)(unsigned);
typedef struct ethash_return_value {
ethash_blockhash_t result;
ethash_blockhash_t mix_hash;
} ethash_return_value;
ethash_h256_t result;
ethash_h256_t mix_hash;
} ethash_return_value_t;
uint64_t ethash_get_datasize(const uint32_t block_number);
uint64_t ethash_get_cachesize(const uint32_t block_number);
// initialize the parameters
static inline void ethash_params_init(ethash_params *params, const uint32_t block_number) {
params->full_size = ethash_get_datasize(block_number);
params->cache_size = ethash_get_cachesize(block_number);
}
uint64_t ethash_get_datasize(uint64_t const block_number);
uint64_t ethash_get_cachesize(uint64_t const block_number);
typedef struct ethash_cache {
void *mem;
} ethash_cache;
void* mem;
uint64_t cache_size;
} ethash_cache_t;
void ethash_mkcache(ethash_cache *cache, ethash_params const *params, ethash_blockhash_t const *seed);
void ethash_compute_full_data(void *mem, ethash_params const *params, ethash_cache const *cache);
void ethash_full(ethash_return_value *ret,
void const *full_mem,
ethash_params const *params,
ethash_blockhash_t const *header_hash,
const uint64_t nonce);
void ethash_light(ethash_return_value *ret,
ethash_cache const *cache,
ethash_params const *params,
ethash_blockhash_t const *header_hash,
const uint64_t nonce);
void ethash_get_seedhash(ethash_blockhash_t *seedhash, const uint32_t block_number);
/**
* Allocate and initialize a new ethash_cache object
*
* @param cache_size The size of the cache in bytes
* @param seed Block seedhash to be used during the computation of the
* cache nodes
* @return Newly allocated ethash_cache on success or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes()
*/
ethash_cache_t* ethash_cache_new(uint64_t cache_size, ethash_h256_t const* seed);
/**
* Frees a previously allocated ethash_cache
* @param c The object to free
*/
void ethash_cache_delete(ethash_cache_t* c);
static inline void ethash_prep_light(void *cache, ethash_params const *params, ethash_blockhash_t const* seed)
{
ethash_cache c;
c.mem = cache;
ethash_mkcache(&c, params, seed);
}
/**
* Allocate and initialize a new ethash_light handler
*
* @param cache_size The size of the cache in bytes
* @param seed Block seedhash to be used during the computation of the
* cache nodes
* @return Newly allocated ethash_light handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes()
*/
ethash_light_t ethash_light_new(uint64_t cache_size, ethash_h256_t const* seed);
/**
* Frees a previously allocated ethash_light handler
* @param light The light handler to free
*/
void ethash_light_delete(ethash_light_t light);
/**
* Calculate the light client data
*
* @param ret An object of ethash_return_value to hold the return value
* @param light The light client handler
* @param full_size The size of the full data in bytes.
* @param header_hash The header hash to pack into the mix
* @param nonce The nonce to pack into the mix
* @return true if all went well and false if there were invalid
* parameters given.
*/
bool ethash_light_compute(
ethash_return_value_t* ret,
ethash_light_t light,
uint64_t full_size,
const ethash_h256_t* header_hash,
uint64_t const nonce
);
/**
* Get a pointer to the cache object held by the light client
*
* @param light The light client whose cache to request
* @return A pointer to the cache held by the light client or NULL if
* there was no cache in the first place
*/
ethash_cache_t* ethash_light_get_cache(ethash_light_t light);
/**
* Move the memory ownership of the cache somewhere else
*
* @param light The light client whose cache's memory ownership to acquire.
* After this function concludes it will no longer have a cache.
* @return A pointer to the moved cache or NULL if there was no cache in the first place
*/
ethash_cache_t* ethash_light_acquire_cache(ethash_light_t light);
static inline void ethash_compute_light(ethash_return_value *ret, void const *cache, ethash_params const *params, ethash_blockhash_t const *header_hash, const uint64_t nonce)
{
ethash_cache c;
c.mem = (void *) cache;
ethash_light(ret, &c, params, header_hash, nonce);
}
/**
* Allocate and initialize a new ethash_full handler
*
* @param dirname The directory in which to put the DAG file.
* @param seedhash The seed hash of the block. Used in the DAG file naming.
* @param full_size The size of the full data in bytes.
* @param cache A cache object to use that was allocated with @ref ethash_cache_new().
* Iff this function succeeds the ethash_full_t will take memory
* memory ownership of the cache and free it at deletion. If
* not then the user still has to handle freeing of the cache himself.
* @param callback A callback function with signature of @ref ethash_callback_t
* It accepts an unsigned with which a progress of DAG calculation
* can be displayed. If all goes well the callback should return 0.
* If a non-zero value is returned then DAG generation will stop.
* @return Newly allocated ethash_full handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data()
*/
ethash_full_t ethash_full_new(
char const* dirname,
ethash_h256_t const* seed_hash,
uint64_t full_size,
ethash_cache_t const* cache,
ethash_callback_t callback
);
/**
* Frees a previously allocated ethash_full handler
* @param full The light handler to free
*/
void ethash_full_delete(ethash_full_t full);
/**
* Calculate the full client data
*
* @param ret An object of ethash_return_value to hold the return value
* @param full The full client handler
* @param header_hash The header hash to pack into the mix
* @param nonce The nonce to pack into the mix
* @return true if all went well and false if there were invalid
* parameters given or if there was a callback given and
* at some point return a non-zero value
*/
bool ethash_full_compute(
ethash_return_value_t* ret,
ethash_full_t full,
ethash_h256_t const* header_hash,
uint64_t const nonce
);
/**
* Get a pointer to the full DAG data
*/
void *ethash_full_data(ethash_full_t full);
static inline void ethash_prep_full(void *full, ethash_params const *params, void const *cache)
{
ethash_cache c;
c.mem = (void *) cache;
ethash_compute_full_data(full, params, &c);
}
static inline void ethash_compute_full(ethash_return_value *ret,
void const *full,
ethash_params const *params,
ethash_blockhash_t const *header_hash,
const uint64_t nonce)
{
ethash_full(ret, full, params, header_hash, nonce);
}
void ethash_get_seedhash(ethash_h256_t *seedhash, const uint32_t block_number);
// Returns if hash is less than or equal to difficulty
static inline int ethash_check_difficulty(ethash_blockhash_t const *hash,
ethash_blockhash_t const *difficulty)
static inline int ethash_check_difficulty(
ethash_h256_t const* hash,
ethash_h256_t const* difficulty
)
{
// Difficulty is big endian
for (int i = 0; i < 32; i++) {
if (ethash_blockhash_get(hash, i) == ethash_blockhash_get(difficulty, i)) {
continue;
}
return ethash_blockhash_get(hash, i) < ethash_blockhash_get(difficulty, i);
}
return 1;
// Difficulty is big endian
for (int i = 0; i < 32; i++) {
if (ethash_h256_get(hash, i) == ethash_h256_get(difficulty, i)) {
continue;
}
return ethash_h256_get(hash, i) < ethash_h256_get(difficulty, i);
}
return 1;
}
int ethash_quick_check_difficulty(ethash_blockhash_t const *header_hash,
const uint64_t nonce,
ethash_blockhash_t const *mix_hash,
ethash_blockhash_t const *difficulty);
int ethash_quick_check_difficulty(
ethash_h256_t const* header_hash,
uint64_t const nonce,
ethash_h256_t const* mix_hash,
ethash_h256_t const* difficulty
);
/**
* Compute the memory data for a full node's memory
*
* @param mem A pointer to an ethash full's memory
* @param full_size The size of the full data in bytes
* @param cache A cache object to use in the calculation
* @return true if all went fine and false for invalid parameters
*/
bool ethash_compute_full_data(void* mem, uint64_t full_size, ethash_cache_t const* cache);
/**
* =========================
* = DEPRECATED API =
* =========================
*
* Kept for backwards compatibility with whoever still uses it. Please consider
* switching to the new API (look above)
*/
typedef struct ethash_params {
/// Size of full data set (in bytes, multiple of mix size (128)).
uint64_t full_size;
/// Size of compute cache (in bytes, multiple of node size (64)).
uint64_t cache_size;
} ethash_params;
// initialize the parameters
static inline void ethash_params_init(ethash_params* params, uint32_t const block_number)
{
params->full_size = ethash_get_datasize(block_number);
params->cache_size = ethash_get_cachesize(block_number);
}
void ethash_mkcache(ethash_cache_t* cache, ethash_params const* params, ethash_h256_t const* seed);
void ethash_full(
ethash_return_value_t* ret,
void const* full_mem,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce
);
void ethash_light(
ethash_return_value_t* ret,
ethash_cache_t const* cache,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce
);
#ifdef __cplusplus
}

View file

@ -29,10 +29,11 @@ extern "C" {
#define FNV_PRIME 0x01000193
static inline uint32_t fnv_hash(const uint32_t x, const uint32_t y) {
return x*FNV_PRIME ^ y;
static inline uint32_t fnv_hash(uint32_t const x, uint32_t const y)
{
return x * FNV_PRIME ^ y;
}
#ifdef __cplusplus
}
#endif
#endif

View file

@ -8,11 +8,11 @@
ethash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file internal.c
* @author Tim Hughes <tim@twistedfury.com>
@ -23,11 +23,15 @@
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <errno.h>
#include <math.h>
#include "mmap.h"
#include "ethash.h"
#include "fnv.h"
#include "endian.h"
#include "internal.h"
#include "data_sizes.h"
#include "io.h"
#ifdef WITH_CRYPTOPP
@ -37,274 +41,467 @@
#include "sha3.h"
#endif // WITH_CRYPTOPP
uint64_t ethash_get_datasize(const uint32_t block_number) {
assert(block_number / EPOCH_LENGTH < 2048);
return dag_sizes[block_number / EPOCH_LENGTH];
uint64_t ethash_get_datasize(uint64_t const block_number)
{
assert(block_number / EPOCH_LENGTH < 2048);
return dag_sizes[block_number / EPOCH_LENGTH];
}
uint64_t ethash_get_cachesize(const uint32_t block_number) {
assert(block_number / EPOCH_LENGTH < 2048);
return cache_sizes[block_number / EPOCH_LENGTH];
uint64_t ethash_get_cachesize(uint64_t const block_number)
{
assert(block_number / EPOCH_LENGTH < 2048);
return cache_sizes[block_number / EPOCH_LENGTH];
}
// Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014)
// https://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf
// SeqMemoHash(s, R, N)
void static ethash_compute_cache_nodes(node *const nodes,
ethash_params const *params,
ethash_blockhash_t const* seed)
bool static ethash_compute_cache_nodes(
node* const nodes,
uint64_t cache_size,
ethash_h256_t const* seed
)
{
assert((params->cache_size % sizeof(node)) == 0);
uint32_t const num_nodes = (uint32_t) (params->cache_size / sizeof(node));
if (cache_size % sizeof(node) != 0) {
return false;
}
uint32_t const num_nodes = (uint32_t) (cache_size / sizeof(node));
SHA3_512(nodes[0].bytes, (uint8_t*)seed, 32);
SHA3_512(nodes[0].bytes, (uint8_t*)seed, 32);
for (unsigned i = 1; i != num_nodes; ++i) {
SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
}
for (unsigned i = 1; i != num_nodes; ++i) {
SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
}
for (unsigned j = 0; j != CACHE_ROUNDS; j++) {
for (unsigned i = 0; i != num_nodes; i++) {
uint32_t const idx = nodes[i].words[0] % num_nodes;
node data;
data = nodes[(num_nodes - 1 + i) % num_nodes];
for (unsigned w = 0; w != NODE_WORDS; ++w) {
data.words[w] ^= nodes[idx].words[w];
}
SHA3_512(nodes[i].bytes, data.bytes, sizeof(data));
}
}
for (unsigned j = 0; j != CACHE_ROUNDS; j++) {
for (unsigned i = 0; i != num_nodes; i++) {
uint32_t const idx = nodes[i].words[0] % num_nodes;
node data;
data = nodes[(num_nodes - 1 + i) % num_nodes];
for (unsigned w = 0; w != NODE_WORDS; ++w) {
data.words[w] ^= nodes[idx].words[w];
}
SHA3_512(nodes[i].bytes, data.bytes, sizeof(data));
}
}
// now perform endian conversion
#if BYTE_ORDER != LITTLE_ENDIAN
for (unsigned w = 0; w != (num_nodes*NODE_WORDS); ++w)
{
nodes->words[w] = fix_endian32(nodes->words[w]);
}
// now perform endian conversion
fix_endian_arr32(nodes->words, num_nodes * NODE_WORDS);
return true;
}
ethash_cache_t* ethash_cache_new(uint64_t cache_size, ethash_h256_t const* seed)
{
ethash_cache_t* ret;
ret = malloc(sizeof(*ret));
if (!ret) {
return NULL;
}
ret->mem = malloc((size_t)cache_size);
if (!ret->mem) {
goto fail_free_cache;
}
node* nodes = (node*)ret->mem;
if (!ethash_compute_cache_nodes(nodes, cache_size, seed)) {
goto fail_free_cache_mem;
}
ret->cache_size = cache_size;
return ret;
fail_free_cache_mem:
free(ret->mem);
fail_free_cache:
free(ret);
return NULL;
}
void ethash_cache_delete(ethash_cache_t* c)
{
free(c->mem);
free(c);
}
void ethash_calculate_dag_item(
node* const ret,
const unsigned node_index,
ethash_cache_t const* cache
)
{
uint32_t num_parent_nodes = (uint32_t) (cache->cache_size / sizeof(node));
node const* cache_nodes = (node const *) cache->mem;
node const* init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node));
ret->words[0] ^= node_index;
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
#if defined(_M_X64) && ENABLE_SSE
__m128i const fnv_prime = _mm_set1_epi32(FNV_PRIME);
__m128i xmm0 = ret->xmm[0];
__m128i xmm1 = ret->xmm[1];
__m128i xmm2 = ret->xmm[2];
__m128i xmm3 = ret->xmm[3];
#endif
}
void ethash_mkcache(ethash_cache *cache,
ethash_params const *params,
ethash_blockhash_t const* seed)
{
node *nodes = (node *) cache->mem;
ethash_compute_cache_nodes(nodes, params, seed);
}
void ethash_calculate_dag_item(node *const ret,
const unsigned node_index,
const struct ethash_params *params,
const struct ethash_cache *cache)
{
uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node));
node const *cache_nodes = (node const *) cache->mem;
node const *init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node));
ret->words[0] ^= node_index;
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
for (unsigned i = 0; i != DATASET_PARENTS; ++i) {
uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes;
node const *parent = &cache_nodes[parent_index];
#if defined(_M_X64) && ENABLE_SSE
__m128i const fnv_prime = _mm_set1_epi32(FNV_PRIME);
__m128i xmm0 = ret->xmm[0];
__m128i xmm1 = ret->xmm[1];
__m128i xmm2 = ret->xmm[2];
__m128i xmm3 = ret->xmm[3];
#endif
{
xmm0 = _mm_mullo_epi32(xmm0, fnv_prime);
xmm1 = _mm_mullo_epi32(xmm1, fnv_prime);
xmm2 = _mm_mullo_epi32(xmm2, fnv_prime);
xmm3 = _mm_mullo_epi32(xmm3, fnv_prime);
xmm0 = _mm_xor_si128(xmm0, parent->xmm[0]);
xmm1 = _mm_xor_si128(xmm1, parent->xmm[1]);
xmm2 = _mm_xor_si128(xmm2, parent->xmm[2]);
xmm3 = _mm_xor_si128(xmm3, parent->xmm[3]);
for (unsigned i = 0; i != DATASET_PARENTS; ++i) {
uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes;
node const *parent = &cache_nodes[parent_index];
// have to write to ret as values are used to compute index
ret->xmm[0] = xmm0;
ret->xmm[1] = xmm1;
ret->xmm[2] = xmm2;
ret->xmm[3] = xmm3;
}
#else
{
for (unsigned w = 0; w != NODE_WORDS; ++w) {
ret->words[w] = fnv_hash(ret->words[w], parent->words[w]);
}
}
#endif
}
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
}
bool ethash_compute_full_data(void* mem, uint64_t full_size, ethash_cache_t const* cache)
{
if (full_size % (sizeof(uint32_t) * MIX_WORDS) != 0 ||
(full_size % sizeof(node)) != 0) {
return false;
}
node* full_nodes = mem;
// now compute full nodes
for (unsigned n = 0; n != (full_size / sizeof(node)); ++n) {
ethash_calculate_dag_item(&(full_nodes[n]), n, cache);
}
return true;
}
static bool ethash_hash(
ethash_return_value_t* ret,
node const* full_nodes,
ethash_cache_t const* cache,
uint64_t full_size,
ethash_h256_t const* header_hash,
uint64_t const nonce,
ethash_callback_t callback
)
{
if (full_size % MIX_WORDS != 0) {
return false;
}
// pack hash and nonce together into first 40 bytes of s_mix
assert(sizeof(node) * 8 == 512);
node s_mix[MIX_NODES + 1];
memcpy(s_mix[0].bytes, header_hash, 32);
fix_endian64(s_mix[0].double_words[4], nonce);
// compute sha3-512 hash and replicate across mix
SHA3_512(s_mix->bytes, s_mix->bytes, 40);
fix_endian_arr32(s_mix[0].words, 16);
node* const mix = s_mix + 1;
for (unsigned w = 0; w != MIX_WORDS; ++w) {
mix->words[w] = s_mix[0].words[w % NODE_WORDS];
}
unsigned const page_size = sizeof(uint32_t) * MIX_WORDS;
unsigned const num_full_pages = (unsigned) (full_size / page_size);
double const progress_change = 1.0f / ACCESSES / MIX_NODES;
double progress = 0.0f;
for (unsigned i = 0; i != ACCESSES; ++i) {
uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages;
for (unsigned n = 0; n != MIX_NODES; ++n) {
node const* dag_node;
if (callback &&
callback((unsigned int)(ceil(progress * 100.0f))) != 0) {
return false;
}
progress += progress_change;
if (full_nodes) {
dag_node = &full_nodes[MIX_NODES * index + n];
} else {
node tmp_node;
ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, cache);
dag_node = &tmp_node;
}
#if defined(_M_X64) && ENABLE_SSE
{
xmm0 = _mm_mullo_epi32(xmm0, fnv_prime);
xmm1 = _mm_mullo_epi32(xmm1, fnv_prime);
xmm2 = _mm_mullo_epi32(xmm2, fnv_prime);
xmm3 = _mm_mullo_epi32(xmm3, fnv_prime);
xmm0 = _mm_xor_si128(xmm0, parent->xmm[0]);
xmm1 = _mm_xor_si128(xmm1, parent->xmm[1]);
xmm2 = _mm_xor_si128(xmm2, parent->xmm[2]);
xmm3 = _mm_xor_si128(xmm3, parent->xmm[3]);
// have to write to ret as values are used to compute index
ret->xmm[0] = xmm0;
ret->xmm[1] = xmm1;
ret->xmm[2] = xmm2;
ret->xmm[3] = xmm3;
}
#else
{
for (unsigned w = 0; w != NODE_WORDS; ++w) {
ret->words[w] = fnv_hash(ret->words[w], parent->words[w]);
}
}
{
__m128i fnv_prime = _mm_set1_epi32(FNV_PRIME);
__m128i xmm0 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[0]);
__m128i xmm1 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[1]);
__m128i xmm2 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[2]);
__m128i xmm3 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[3]);
mix[n].xmm[0] = _mm_xor_si128(xmm0, dag_node->xmm[0]);
mix[n].xmm[1] = _mm_xor_si128(xmm1, dag_node->xmm[1]);
mix[n].xmm[2] = _mm_xor_si128(xmm2, dag_node->xmm[2]);
mix[n].xmm[3] = _mm_xor_si128(xmm3, dag_node->xmm[3]);
}
#else
{
for (unsigned w = 0; w != NODE_WORDS; ++w) {
mix[n].words[w] = fnv_hash(mix[n].words[w], dag_node->words[w]);
}
}
#endif
}
}
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
}
// compress mix
for (unsigned w = 0; w != MIX_WORDS; w += 4) {
uint32_t reduction = mix->words[w + 0];
reduction = reduction * FNV_PRIME ^ mix->words[w + 1];
reduction = reduction * FNV_PRIME ^ mix->words[w + 2];
reduction = reduction * FNV_PRIME ^ mix->words[w + 3];
mix->words[w / 4] = reduction;
}
fix_endian_arr32(mix->words, MIX_WORDS / 4);
memcpy(&ret->mix_hash, mix->bytes, 32);
// final Keccak hash
SHA3_256(&ret->result, s_mix->bytes, 64 + 32); // Keccak-256(s + compressed_mix)
return true;
}
void ethash_compute_full_data(
void *mem,
ethash_params const *params,
ethash_cache const *cache) {
assert((params->full_size % (sizeof(uint32_t) * MIX_WORDS)) == 0);
assert((params->full_size % sizeof(node)) == 0);
node *full_nodes = mem;
// now compute full nodes
for (unsigned n = 0; n != (params->full_size / sizeof(node)); ++n) {
ethash_calculate_dag_item(&(full_nodes[n]), n, params, cache);
}
void ethash_quick_hash(
ethash_h256_t* return_hash,
ethash_h256_t const* header_hash,
uint64_t const nonce,
ethash_h256_t const* mix_hash
)
{
uint8_t buf[64 + 32];
memcpy(buf, header_hash, 32);
fix_endian64_same(nonce);
memcpy(&(buf[32]), &nonce, 8);
SHA3_512(buf, buf, 40);
memcpy(&(buf[64]), mix_hash, 32);
SHA3_256(return_hash, buf, 64 + 32);
}
static void ethash_hash(ethash_return_value *ret,
node const *full_nodes,
ethash_cache const *cache,
ethash_params const *params,
ethash_blockhash_t const *header_hash,
const uint64_t nonce)
void ethash_get_seedhash(ethash_h256_t* seedhash, const uint32_t block_number)
{
ethash_h256_reset(seedhash);
const uint32_t epochs = block_number / EPOCH_LENGTH;
for (uint32_t i = 0; i < epochs; ++i)
SHA3_256(seedhash, (uint8_t*)seedhash, 32);
}
int ethash_quick_check_difficulty(
ethash_h256_t const* header_hash,
uint64_t const nonce,
ethash_h256_t const* mix_hash,
ethash_h256_t const* difficulty
)
{
assert((params->full_size % MIX_WORDS) == 0);
// pack hash and nonce together into first 40 bytes of s_mix
assert(sizeof(node) * 8 == 512);
node s_mix[MIX_NODES + 1];
memcpy(s_mix[0].bytes, header_hash, 32);
#if BYTE_ORDER != LITTLE_ENDIAN
s_mix[0].double_words[4] = fix_endian64(nonce);
#else
s_mix[0].double_words[4] = nonce;
#endif
// compute sha3-512 hash and replicate across mix
SHA3_512(s_mix->bytes, s_mix->bytes, 40);
#if BYTE_ORDER != LITTLE_ENDIAN
for (unsigned w = 0; w != 16; ++w) {
s_mix[0].words[w] = fix_endian32(s_mix[0].words[w]);
}
#endif
node *const mix = s_mix + 1;
for (unsigned w = 0; w != MIX_WORDS; ++w) {
mix->words[w] = s_mix[0].words[w % NODE_WORDS];
}
unsigned const
page_size = sizeof(uint32_t) * MIX_WORDS,
num_full_pages = (unsigned) (params->full_size / page_size);
for (unsigned i = 0; i != ACCESSES; ++i) {
uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages;
for (unsigned n = 0; n != MIX_NODES; ++n) {
const node *dag_node = &full_nodes[MIX_NODES * index + n];
if (!full_nodes) {
node tmp_node;
ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, params, cache);
dag_node = &tmp_node;
}
#if defined(_M_X64) && ENABLE_SSE
{
__m128i fnv_prime = _mm_set1_epi32(FNV_PRIME);
__m128i xmm0 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[0]);
__m128i xmm1 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[1]);
__m128i xmm2 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[2]);
__m128i xmm3 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[3]);
mix[n].xmm[0] = _mm_xor_si128(xmm0, dag_node->xmm[0]);
mix[n].xmm[1] = _mm_xor_si128(xmm1, dag_node->xmm[1]);
mix[n].xmm[2] = _mm_xor_si128(xmm2, dag_node->xmm[2]);
mix[n].xmm[3] = _mm_xor_si128(xmm3, dag_node->xmm[3]);
}
#else
{
for (unsigned w = 0; w != NODE_WORDS; ++w) {
mix[n].words[w] = fnv_hash(mix[n].words[w], dag_node->words[w]);
}
}
#endif
}
}
// compress mix
for (unsigned w = 0; w != MIX_WORDS; w += 4) {
uint32_t reduction = mix->words[w + 0];
reduction = reduction * FNV_PRIME ^ mix->words[w + 1];
reduction = reduction * FNV_PRIME ^ mix->words[w + 2];
reduction = reduction * FNV_PRIME ^ mix->words[w + 3];
mix->words[w / 4] = reduction;
}
#if BYTE_ORDER != LITTLE_ENDIAN
for (unsigned w = 0; w != MIX_WORDS/4; ++w) {
mix->words[w] = fix_endian32(mix->words[w]);
}
#endif
memcpy(&ret->mix_hash, mix->bytes, 32);
// final Keccak hash
SHA3_256(&ret->result, s_mix->bytes, 64 + 32); // Keccak-256(s + compressed_mix)
ethash_h256_t return_hash;
ethash_quick_hash(&return_hash, header_hash, nonce, mix_hash);
return ethash_check_difficulty(&return_hash, difficulty);
}
void ethash_quick_hash(ethash_blockhash_t *return_hash,
ethash_blockhash_t const *header_hash,
const uint64_t nonce,
ethash_blockhash_t const *mix_hash)
ethash_light_t ethash_light_new(uint64_t cache_size, ethash_h256_t const* seed)
{
struct ethash_light *ret;
ret = calloc(sizeof(*ret), 1);
if (!ret) {
return NULL;
}
ret->cache = ethash_cache_new(cache_size, seed);
if (!ret->cache) {
goto fail_free_light;
}
return ret;
uint8_t buf[64 + 32];
memcpy(buf, header_hash, 32);
#if BYTE_ORDER != LITTLE_ENDIAN
nonce = fix_endian64(nonce);
#endif
memcpy(&(buf[32]), &nonce, 8);
SHA3_512(buf, buf, 40);
memcpy(&(buf[64]), mix_hash, 32);
SHA3_256(return_hash, buf, 64 + 32);
fail_free_light:
free(ret);
return NULL;
}
void ethash_get_seedhash(ethash_blockhash_t *seedhash, const uint32_t block_number)
void ethash_light_delete(ethash_light_t light)
{
ethash_blockhash_reset(seedhash);
const uint32_t epochs = block_number / EPOCH_LENGTH;
for (uint32_t i = 0; i < epochs; ++i)
SHA3_256(seedhash, (uint8_t*)seedhash, 32);
if (light->cache) {
ethash_cache_delete(light->cache);
}
free(light);
}
int ethash_quick_check_difficulty(ethash_blockhash_t const *header_hash,
const uint64_t nonce,
ethash_blockhash_t const *mix_hash,
ethash_blockhash_t const *difficulty)
bool ethash_light_compute(
ethash_return_value_t* ret,
ethash_light_t light,
uint64_t full_size,
const ethash_h256_t* header_hash,
uint64_t const nonce
)
{
ethash_blockhash_t return_hash;
ethash_quick_hash(&return_hash, header_hash, nonce, mix_hash);
return ethash_check_difficulty(&return_hash, difficulty);
return ethash_hash(
ret,
NULL,
light->cache,
full_size,
header_hash,
nonce,
NULL
);
}
void ethash_full(ethash_return_value *ret,
void const *full_mem,
ethash_params const *params,
ethash_blockhash_t const *header_hash,
const uint64_t nonce)
ethash_cache_t *ethash_light_get_cache(ethash_light_t light)
{
ethash_hash(ret, (node const *) full_mem, NULL, params, header_hash, nonce);
return light->cache;
}
void ethash_light(ethash_return_value *ret,
ethash_cache const *cache,
ethash_params const *params,
ethash_blockhash_t const *header_hash,
const uint64_t nonce)
ethash_cache_t *ethash_light_acquire_cache(ethash_light_t light)
{
ethash_hash(ret, NULL, cache, params, header_hash, nonce);
ethash_cache_t* ret = light->cache;
light->cache = 0;
return ret;
}
ethash_full_t ethash_full_new(
char const* dirname,
ethash_h256_t const* seed_hash,
uint64_t full_size,
ethash_cache_t const* cache,
ethash_callback_t callback
)
{
struct ethash_full* ret;
int fd;
FILE *f = NULL;
bool match = false;
ret = calloc(sizeof(*ret), 1);
if (!ret) {
return NULL;
}
ret->file_size = (size_t)full_size;
switch (ethash_io_prepare(dirname, *seed_hash, &f, (size_t)full_size)) {
case ETHASH_IO_FAIL:
case ETHASH_IO_MEMO_SIZE_MISMATCH:
goto fail_free_full;
case ETHASH_IO_MEMO_MATCH:
match = true;
case ETHASH_IO_MEMO_MISMATCH:
ret->file = f;
if ((fd = ethash_fileno(ret->file)) == -1) {
goto fail_free_full;
}
ret->data = mmap(
NULL,
(size_t)full_size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0
);
if (ret->data == MAP_FAILED) {
goto fail_close_file;
}
if (match) {
return ret;
}
break;
}
if (!ethash_compute_full_data(ret->data, full_size, cache)) {
goto fail_free_full_data;
}
ret->callback = callback;
return ret;
fail_free_full_data:
// could check that munmap(..) == 0 but even if it did not can't really do anything here
munmap(ret->data, (size_t)full_size);
fail_close_file:
fclose(ret->file);
fail_free_full:
free(ret);
return NULL;
}
void ethash_full_delete(ethash_full_t full)
{
// could check that munmap(..) == 0 but even if it did not can't really do anything here
munmap(full->data, full->file_size);
if (full->file) {
fclose(full->file);
}
free(full);
}
bool ethash_full_compute(
ethash_return_value_t* ret,
ethash_full_t full,
ethash_h256_t const* header_hash,
uint64_t const nonce
)
{
return ethash_hash(
ret,
(node const*)full->data,
NULL,
full->file_size,
header_hash,
nonce,
full->callback
);
}
void *ethash_full_data(ethash_full_t full)
{
return full->data;
}
/**
* =========================
* = DEPRECATED API =
* =========================
*
* Kept for backwards compatibility with whoever still uses it. Please consider
* switching to the new API (look above)
*/
void ethash_mkcache(
ethash_cache_t* cache,
ethash_params const* params,
ethash_h256_t const* seed
)
{
node* nodes = (node*) cache->mem;
ethash_compute_cache_nodes(nodes, params->cache_size, seed);
}
void ethash_full(
ethash_return_value_t* ret,
void const* full_mem,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce
)
{
ethash_hash(ret, (node const *) full_mem, NULL, params->full_size, header_hash, nonce, NULL);
}
void ethash_light(
ethash_return_value_t* ret,
ethash_cache_t const* cache,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce
)
{
ethash_hash(ret, NULL, cache, params->full_size, header_hash, nonce, NULL);
}

View file

@ -2,6 +2,7 @@
#include "compiler.h"
#include "endian.h"
#include "ethash.h"
#include <stdio.h>
#define ENABLE_SSE 0
@ -20,9 +21,9 @@ extern "C" {
#include <stdint.h>
typedef union node {
uint8_t bytes[NODE_WORDS * 4];
uint32_t words[NODE_WORDS];
uint64_t double_words[NODE_WORDS / 2];
uint8_t bytes[NODE_WORDS * 4];
uint32_t words[NODE_WORDS];
uint64_t double_words[NODE_WORDS / 2];
#if defined(_M_X64) && ENABLE_SSE
__m128i xmm[NODE_WORDS/4];
@ -30,15 +31,29 @@ typedef union node {
} node;
void ethash_calculate_dag_item(node *const ret,
const unsigned node_index,
ethash_params const *params,
ethash_cache const *cache);
struct ethash_light {
ethash_cache_t* cache;
};
void ethash_quick_hash(ethash_blockhash_t *return_hash,
ethash_blockhash_t const *header_hash,
const uint64_t nonce,
ethash_blockhash_t const *mix_hash);
struct ethash_full {
FILE* file;
uint64_t file_size;
node* data;
ethash_callback_t callback;
};
void ethash_calculate_dag_item(
node* const ret,
const unsigned node_index,
ethash_cache_t const* cache
);
void ethash_quick_hash(
ethash_h256_t* return_hash,
ethash_h256_t const* header_hash,
const uint64_t nonce,
ethash_h256_t const* mix_hash
);
#ifdef __cplusplus
}

View file

@ -22,68 +22,62 @@
#include <string.h>
#include <stdio.h>
// silly macro to save some typing
#define PASS_ARR(c_) (c_), sizeof(c_)
static bool ethash_io_write_file(char const *dirname,
char const* filename,
size_t filename_length,
void const* data,
size_t data_size)
enum ethash_io_rc ethash_io_prepare(
char const* dirname,
ethash_h256_t const seedhash,
FILE** output_file,
size_t file_size
)
{
bool ret = false;
char *fullname = ethash_io_create_filename(dirname, filename, filename_length);
if (!fullname) {
return false;
}
FILE *f = fopen(fullname, "wb");
if (!f) {
goto free_name;
}
if (data_size != fwrite(data, 1, data_size, f)) {
goto close;
}
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
ret = true;
close:
fclose(f);
free_name:
free(fullname);
return ret;
}
// assert directory exists
if (!ethash_mkdir(dirname)) {
goto end;
}
bool ethash_io_write(char const *dirname,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
uint64_t *data_size)
{
char info_buffer[DAG_MEMO_BYTESIZE];
// allocate the bytes
uint8_t *temp_data_ptr = malloc((size_t)params->full_size);
if (!temp_data_ptr) {
goto end;
}
ethash_compute_full_data(temp_data_ptr, params, cache);
ethash_io_mutable_name(REVISION, &seedhash, mutable_name);
char* tmpfile = ethash_io_create_filename(dirname, mutable_name, strlen(mutable_name));
if (!tmpfile) {
goto end;
}
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, (size_t)params->full_size)) {
goto fail_free;
}
// try to open the file
FILE* f = ethash_fopen(tmpfile, "rb+");
if (f) {
size_t found_size;
if (!ethash_file_size(f, &found_size)) {
fclose(f);
goto free_memo;
}
if (file_size != found_size) {
fclose(f);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo;
}
} else {
// file does not exist, will need to be created
f = ethash_fopen(tmpfile, "wb+");
if (!f) {
goto free_memo;
}
// make sure it's of the proper size
if (fseek(f, file_size - 1, SEEK_SET) != 0) {
fclose(f);
goto free_memo;
}
fputc('\n', f);
fflush(f);
ret = ETHASH_IO_MEMO_MISMATCH;
goto set_file;
}
ethash_io_serialize_info(REVISION, seedhash, info_buffer);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_MEMO_NAME), info_buffer, DAG_MEMO_BYTESIZE)) {
goto fail_free;
}
*data = temp_data_ptr;
*data_size = params->full_size;
return true;
fail_free:
free(temp_data_ptr);
ret = ETHASH_IO_MEMO_MATCH;
set_file:
*output_file = f;
free_memo:
free(tmpfile);
end:
return false;
return ret;
}
#undef PASS_ARR

View file

@ -22,91 +22,150 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#ifdef __cplusplus
#define __STDC_FORMAT_MACROS 1
#endif
#include <inttypes.h>
#include "endian.h"
#include "ethash.h"
#ifdef __cplusplus
extern "C" {
#endif
static const char DAG_FILE_NAME[] = "full";
static const char DAG_MEMO_NAME[] = "full.info";
// MSVC thinks that "static const unsigned int" is not a compile time variable. Sorry for the #define :(
#define DAG_MEMO_BYTESIZE 36
// Maximum size for mutable part of DAG file name
// 10 is for maximum number of digits of a uint32_t (for REVISION)
// 1 is for _ and 16 is for the first 16 hex digits for first 8 bytes of
// the seedhash and last 1 is for the null terminating character
// Reference: https://github.com/ethereum/wiki/wiki/Ethash-DAG
#define DAG_MUTABLE_NAME_MAX_SIZE (10 + 1 + 16 + 1)
/// Possible return values of @see ethash_io_prepare
enum ethash_io_rc {
ETHASH_IO_FAIL = 0, ///< There has been an IO failure
ETHASH_IO_MEMO_MISMATCH, ///< Memo file either did not exist or there was content mismatch
ETHASH_IO_MEMO_MATCH, ///< Memo file existed and contents matched. No need to do anything
ETHASH_IO_FAIL = 0, ///< There has been an IO failure
ETHASH_IO_MEMO_SIZE_MISMATCH, ///< DAG with revision/hash match, but file size was wrong.
ETHASH_IO_MEMO_MISMATCH, ///< The DAG file did not exist or there was revision/hash mismatch
ETHASH_IO_MEMO_MATCH, ///< DAG file existed and revision/hash matched. No need to do anything
};
// small hack for windows. I don't feel I should use va_args and forward just
// to have this one function properly cross-platform abstracted
#if defined(_WIN32)
#define snprintf(...) sprintf_s(__VA_ARGS__)
#endif
/**
* Prepares io for ethash
*
* Create the DAG directory if it does not exist, and check if the memo file matches.
* If it does not match then it's deleted to pave the way for @ref ethash_io_write()
*
* @param dirname A null terminated c-string of the path of the ethash
* data directory. If it does not exist it's created.
* @param seedhash The seedhash of the current block number
* @return For possible return values @see enum ethash_io_rc
*/
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash);
/**
* Fully computes data and writes it to the file on disk.
*
* This function should be called after @see ethash_io_prepare() and only if
* its return value is @c ETHASH_IO_MEMO_MISMATCH. Will write both the full data
* and the memo file.
* Create the DAG directory and the DAG file if they don't exist.
*
* @param[in] dirname A null terminated c-string of the path of the ethash
* data directory. Has to exist.
* @param[in] params An ethash_params object containing the full size
* and the cache size
* @param[in] seedhash The seedhash of the current block number
* @param[in] cache The cache data. Would have usually been calulated by
* @see ethash_prep_light().
* @param[out] data Pass a pointer to uint8_t by reference here. If the
* function is succesfull then this point to the allocated
* data calculated by @see ethash_prep_full(). Memory
* ownership is transfered to the callee. Remember that
* you eventually need to free this with a call to free().
* @param[out] data_size Pass a uint64_t by value. If the function is succesfull
* then this will contain the number of bytes allocated
* for @a data.
* @return True for success and false in case of failure.
* data directory. If it does not exist it's created.
* @param[in] seedhash The seedhash of the current block number, used in the
* naming of the file as can be seen from the spec at:
* https://github.com/ethereum/wiki/wiki/Ethash-DAG
* @param[out] output_file If there was no failure then this will point to an open
* file descriptor. User is responsible for closing it.
* In the case of memo match then the file is open on read
* mode, while on the case of mismatch a new file is created
* on write mode
* @param[in] file_size The size that the DAG file should have on disk
* @return For possible return values @see enum ethash_io_rc
*/
bool ethash_io_write(char const *dirname,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
uint64_t *data_size);
enum ethash_io_rc ethash_io_prepare(
char const* dirname,
ethash_h256_t const seedhash,
FILE** output_file,
size_t file_size
);
static inline void ethash_io_serialize_info(uint32_t revision,
ethash_blockhash_t seed_hash,
char *output)
/**
* An fopen wrapper for no-warnings crossplatform fopen.
*
* Msvc compiler considers fopen to be insecure and suggests to use their
* alternative. This is a wrapper for this alternative. Another way is to
* #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does
* not sound like a good idea.
*
* @param file_name The path to the file to open
* @param mode Opening mode. Check fopen()
* @return The FILE* or NULL in failure
*/
FILE* ethash_fopen(char const* file_name, char const* mode);
/**
* An strncat wrapper for no-warnings crossplatform strncat.
*
* Msvc compiler considers strncat to be insecure and suggests to use their
* alternative. This is a wrapper for this alternative. Another way is to
* #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does
* not sound like a good idea.
*
* @param des Destination buffer
* @param dest_size Maximum size of the destination buffer. This is the
* extra argument for the MSVC secure strncat
* @param src Souce buffer
* @param count Number of bytes to copy from source
* @return If all is well returns the dest buffer. If there is an
* error returns NULL
*/
char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count);
/**
* A cross-platform mkdir wrapper to create a directory or assert it's there
*
* @param dirname The full path of the directory to create
* @return true if the directory was created or if it already
* existed
*/
bool ethash_mkdir(char const* dirname);
/**
* Get a file's size
*
* @param[in] f The open file stream whose size to get
* @param[out] size Pass a size_t by reference to contain the file size
* @return true in success and false if there was a failure
*/
bool ethash_file_size(FILE* f, size_t* ret_size);
/**
* Get a file descriptor number from a FILE stream
*
* @param f The file stream whose fd to get
* @return Platform specific fd handler
*/
int ethash_fileno(FILE* f);
static inline bool ethash_io_mutable_name(
uint32_t revision,
ethash_h256_t const* seed_hash,
char* output
)
{
// if .info is only consumed locally we don't really care about endianess
memcpy(output, &revision, 4);
memcpy(output + 4, &seed_hash, 32);
uint64_t hash = *((uint64_t*)seed_hash);
#if LITTLE_ENDIAN == BYTE_ORDER
hash = ethash_swap_u64(hash);
#endif
return snprintf(output, DAG_MUTABLE_NAME_MAX_SIZE, "%u_%016" PRIx64, revision, hash) >= 0;
}
static inline char *ethash_io_create_filename(char const *dirname,
char const* filename,
size_t filename_length)
static inline char* ethash_io_create_filename(
char const* dirname,
char const* filename,
size_t filename_length
)
{
// in C the cast is not needed, but a C++ compiler will complain for invalid conversion
char *name = (char*)malloc(strlen(dirname) + filename_length);
if (!name) {
return NULL;
}
size_t dirlen = strlen(dirname);
// in C the cast is not needed, but a C++ compiler will complain for invalid conversion
char* name = (char*)malloc(dirlen + filename_length + 1);
if (!name) {
return NULL;
}
name[0] = '\0';
strcat(name, dirname);
strcat(name, filename);
return name;
name[0] = '\0';
ethash_strncat(name, dirlen + filename_length + 1, dirname, dirlen);
ethash_strncat(name, dirlen + filename_length + 1, filename, filename_length);
return name;
}

View file

@ -27,50 +27,34 @@
#include <stdio.h>
#include <unistd.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
FILE* ethash_fopen(char const* file_name, char const* mode)
{
char read_buffer[DAG_MEMO_BYTESIZE];
char expect_buffer[DAG_MEMO_BYTESIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// assert directory exists, full owner permissions and read/search for others
int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (rc == -1 && errno != EEXIST) {
goto end;
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
if (!memofile) {
goto end;
}
// try to open memo file
FILE *f = fopen(memofile, "rb");
if (!f) {
// file does not exist, so no checking happens. All is fine.
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
}
if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
goto close;
}
ethash_io_serialize_info(REVISION, seedhash, expect_buffer);
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) {
// we have different memo contents so delete the memo file
if (unlink(memofile) != 0) {
goto close;
}
ret = ETHASH_IO_MEMO_MISMATCH;
}
ret = ETHASH_IO_MEMO_MATCH;
close:
fclose(f);
free_memo:
free(memofile);
end:
return ret;
return fopen(file_name, mode);
}
char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count)
{
return strlen(dest) + count + 1 <= dest_size ? strncat(dest, src, count) : NULL;
}
bool ethash_mkdir(char const* dirname)
{
int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
return rc != -1 || errno == EEXIST;
}
int ethash_fileno(FILE *f)
{
return fileno(f);
}
bool ethash_file_size(FILE* f, size_t* ret_size)
{
struct stat st;
int fd;
if ((fd = fileno(f)) == -1 || fstat(fd, &st) != 0) {
return false;
}
*ret_size = st.st_size;
return true;
}

View file

@ -23,51 +23,38 @@
#include <direct.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
FILE* ethash_fopen(char const* file_name, char const* mode)
{
char read_buffer[DAG_MEMO_BYTESIZE];
char expect_buffer[DAG_MEMO_BYTESIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// assert directory exists
int rc = _mkdir(dirname);
if (rc == -1 && errno != EEXIST) {
goto end;
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
if (!memofile) {
goto end;
}
// try to open memo file
FILE *f = fopen(memofile, "rb");
if (!f) {
// file does not exist, so no checking happens. All is fine.
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
}
if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
goto close;
}
ethash_io_serialize_info(REVISION, seedhash, expect_buffer);
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) {
// we have different memo contents so delete the memo file
if (_unlink(memofile) != 0) {
goto close;
}
ret = ETHASH_IO_MEMO_MISMATCH;
}
ret = ETHASH_IO_MEMO_MATCH;
close:
fclose(f);
free_memo:
free(memofile);
end:
return ret;
FILE* f;
return fopen_s(&f, file_name, mode) == 0 ? f : NULL;
}
char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count)
{
return strncat_s(dest, dest_size, src, count) == 0 ? dest : NULL;
}
bool ethash_mkdir(char const* dirname)
{
int rc = _mkdir(dirname);
return rc != -1 || errno == EEXIST;
}
int ethash_fileno(FILE* f)
{
return _fileno(f);
}
bool ethash_file_size(FILE* f, size_t* ret_size)
{
struct _stat st;
int fd;
if ((fd = _fileno(f)) == -1 || _fstat(fd, &st) != 0) {
return false;
}
*ret_size = st.st_size;
return true;
}

View file

@ -0,0 +1,47 @@
/*
This file is part of ethash.
ethash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file mmap.h
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#pragma once
#if defined(__MINGW32__) || defined(_WIN32)
#include <sys/types.h>
#define PROT_READ 0x1
#define PROT_WRITE 0x2
/* This flag is only available in WinXP+ */
#ifdef FILE_MAP_EXECUTE
#define PROT_EXEC 0x4
#else
#define PROT_EXEC 0x0
#define FILE_MAP_EXECUTE 0
#endif
#define MAP_SHARED 0x01
#define MAP_PRIVATE 0x02
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FAILED ((void *) -1)
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
void munmap(void* addr, size_t length);
#else // posix, yay! ^_^
#include <sys/mman.h>
#endif

View file

@ -0,0 +1,84 @@
/* mmap() replacement for Windows
*
* Author: Mike Frysinger <vapier@gentoo.org>
* Placed into the public domain
*/
/* References:
* CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
* CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
* MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
* UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
*/
#include <io.h>
#include <windows.h>
#include "mmap.h"
#ifdef __USE_FILE_OFFSET64
# define DWORD_HI(x) (x >> 32)
# define DWORD_LO(x) ((x) & 0xffffffff)
#else
# define DWORD_HI(x) (0)
# define DWORD_LO(x) (x)
#endif
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset)
{
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
return MAP_FAILED;
if (fd == -1) {
if (!(flags & MAP_ANON) || offset)
return MAP_FAILED;
} else if (flags & MAP_ANON)
return MAP_FAILED;
DWORD flProtect;
if (prot & PROT_WRITE) {
if (prot & PROT_EXEC)
flProtect = PAGE_EXECUTE_READWRITE;
else
flProtect = PAGE_READWRITE;
} else if (prot & PROT_EXEC) {
if (prot & PROT_READ)
flProtect = PAGE_EXECUTE_READ;
else if (prot & PROT_EXEC)
flProtect = PAGE_EXECUTE;
} else
flProtect = PAGE_READONLY;
off_t end = length + offset;
HANDLE mmap_fd, h;
if (fd == -1)
mmap_fd = INVALID_HANDLE_VALUE;
else
mmap_fd = (HANDLE)_get_osfhandle(fd);
h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL);
if (h == NULL)
return MAP_FAILED;
DWORD dwDesiredAccess;
if (prot & PROT_WRITE)
dwDesiredAccess = FILE_MAP_WRITE;
else
dwDesiredAccess = FILE_MAP_READ;
if (prot & PROT_EXEC)
dwDesiredAccess |= FILE_MAP_EXECUTE;
if (flags & MAP_PRIVATE)
dwDesiredAccess |= FILE_MAP_COPY;
void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length);
if (ret == NULL) {
ret = MAP_FAILED;
}
// since we are handling the file ourselves with fd, close the Windows Handle here
CloseHandle(h);
return ret;
}
void munmap(void* addr, size_t length)
{
UnmapViewOfFile(addr);
}
#undef DWORD_HI
#undef DWORD_LO

View file

@ -17,65 +17,65 @@
/*** Constants. ***/
static const uint8_t rho[24] = \
{ 1, 3, 6, 10, 15, 21,
28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43,
62, 18, 39, 61, 20, 44};
{ 1, 3, 6, 10, 15, 21,
28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43,
62, 18, 39, 61, 20, 44};
static const uint8_t pi[24] = \
{10, 7, 11, 17, 18, 3,
5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2,
20, 14, 22, 9, 6, 1};
{10, 7, 11, 17, 18, 3,
5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2,
20, 14, 22, 9, 6, 1};
static const uint64_t RC[24] = \
{1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL,
0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL,
0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL,
0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL};
{1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL,
0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL,
0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL,
0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL};
/*** Helper macros to unroll the permutation. ***/
#define rol(x, s) (((x) << s) | ((x) >> (64 - s)))
#define REPEAT6(e) e e e e e e
#define REPEAT24(e) REPEAT6(e e e e)
#define REPEAT5(e) e e e e e
#define FOR5(v, s, e) \
v = 0; \
REPEAT5(e; v += s;)
#define FOR5(v, s, e) \
v = 0; \
REPEAT5(e; v += s;)
/*** Keccak-f[1600] ***/
static inline void keccakf(void* state) {
uint64_t* a = (uint64_t*)state;
uint64_t b[5] = {0};
uint64_t t = 0;
uint8_t x, y;
uint64_t* a = (uint64_t*)state;
uint64_t b[5] = {0};
uint64_t t = 0;
uint8_t x, y;
for (int i = 0; i < 24; i++) {
// Theta
FOR5(x, 1,
b[x] = 0;
FOR5(y, 5,
b[x] ^= a[x + y]; ))
FOR5(x, 1,
FOR5(y, 5,
a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); ))
// Rho and pi
t = a[1];
x = 0;
REPEAT24(b[0] = a[pi[x]];
a[pi[x]] = rol(t, rho[x]);
t = b[0];
x++; )
// Chi
FOR5(y,
5,
FOR5(x, 1,
b[x] = a[y + x];)
FOR5(x, 1,
a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); ))
// Iota
a[0] ^= RC[i];
}
for (int i = 0; i < 24; i++) {
// Theta
FOR5(x, 1,
b[x] = 0;
FOR5(y, 5,
b[x] ^= a[x + y]; ))
FOR5(x, 1,
FOR5(y, 5,
a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); ))
// Rho and pi
t = a[1];
x = 0;
REPEAT24(b[0] = a[pi[x]];
a[pi[x]] = rol(t, rho[x]);
t = b[0];
x++; )
// Chi
FOR5(y,
5,
FOR5(x, 1,
b[x] = a[y + x];)
FOR5(x, 1,
a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); ))
// Iota
a[0] ^= RC[i];
}
}
/******** The FIPS202-defined functions. ********/
@ -83,20 +83,20 @@ static inline void keccakf(void* state) {
/*** Some helper macros. ***/
#define _(S) do { S } while (0)
#define FOR(i, ST, L, S) \
_(for (size_t i = 0; i < L; i += ST) { S; })
#define mkapply_ds(NAME, S) \
static inline void NAME(uint8_t* dst, \
const uint8_t* src, \
size_t len) { \
FOR(i, 1, len, S); \
}
#define mkapply_sd(NAME, S) \
static inline void NAME(const uint8_t* src, \
uint8_t* dst, \
size_t len) { \
FOR(i, 1, len, S); \
}
#define FOR(i, ST, L, S) \
_(for (size_t i = 0; i < L; i += ST) { S; })
#define mkapply_ds(NAME, S) \
static inline void NAME(uint8_t* dst, \
const uint8_t* src, \
size_t len) { \
FOR(i, 1, len, S); \
}
#define mkapply_sd(NAME, S) \
static inline void NAME(const uint8_t* src, \
uint8_t* dst, \
size_t len) { \
FOR(i, 1, len, S); \
}
mkapply_ds(xorin, dst[i] ^= src[i]) // xorin
mkapply_sd(setout, dst[i] = src[i]) // setout
@ -105,47 +105,47 @@ mkapply_sd(setout, dst[i] = src[i]) // setout
#define Plen 200
// Fold P*F over the full blocks of an input.
#define foldP(I, L, F) \
while (L >= rate) { \
F(a, I, rate); \
P(a); \
I += rate; \
L -= rate; \
}
#define foldP(I, L, F) \
while (L >= rate) { \
F(a, I, rate); \
P(a); \
I += rate; \
L -= rate; \
}
/** The sponge-based hash construction. **/
static inline int hash(uint8_t* out, size_t outlen,
const uint8_t* in, size_t inlen,
size_t rate, uint8_t delim) {
if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) {
return -1;
}
uint8_t a[Plen] = {0};
// Absorb input.
foldP(in, inlen, xorin);
// Xor in the DS and pad frame.
a[inlen] ^= delim;
a[rate - 1] ^= 0x80;
// Xor in the last block.
xorin(a, in, inlen);
// Apply P
P(a);
// Squeeze output.
foldP(out, outlen, setout);
setout(a, out, outlen);
memset(a, 0, 200);
return 0;
const uint8_t* in, size_t inlen,
size_t rate, uint8_t delim) {
if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) {
return -1;
}
uint8_t a[Plen] = {0};
// Absorb input.
foldP(in, inlen, xorin);
// Xor in the DS and pad frame.
a[inlen] ^= delim;
a[rate - 1] ^= 0x80;
// Xor in the last block.
xorin(a, in, inlen);
// Apply P
P(a);
// Squeeze output.
foldP(out, outlen, setout);
setout(a, out, outlen);
memset(a, 0, 200);
return 0;
}
#define defsha3(bits) \
int sha3_##bits(uint8_t* out, size_t outlen, \
const uint8_t* in, size_t inlen) { \
if (outlen > (bits/8)) { \
return -1; \
} \
return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \
}
#define defsha3(bits) \
int sha3_##bits(uint8_t* out, size_t outlen, \
const uint8_t* in, size_t inlen) { \
if (outlen > (bits/8)) { \
return -1; \
} \
return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \
}
/*** FIPS202 SHA3 FOFs ***/
defsha3(256)
defsha3(512)
defsha3(512)

View file

@ -8,22 +8,22 @@ extern "C" {
#include <stdint.h>
#include <stdlib.h>
struct ethash_blockhash;
struct ethash_h256;
#define decsha3(bits) \
int sha3_##bits(uint8_t*, size_t, const uint8_t*, size_t);
int sha3_##bits(uint8_t*, size_t, uint8_t const*, size_t);
decsha3(256)
decsha3(512)
static inline void SHA3_256(struct ethash_blockhash const* ret, uint8_t const *data, const size_t size)
static inline void SHA3_256(struct ethash_h256 const* ret, uint8_t const* data, size_t const size)
{
sha3_256((uint8_t*)ret, 32, data, size);
sha3_256((uint8_t*)ret, 32, data, size);
}
static inline void SHA3_512(uint8_t *ret, uint8_t const *data, const size_t size)
static inline void SHA3_512(uint8_t* ret, uint8_t const* data, size_t const size)
{
sha3_512(ret, 64, data, size);
sha3_512(ret, 64, data, size);
}
#ifdef __cplusplus

View file

@ -23,13 +23,15 @@
#include <cryptopp/sha3.h>
extern "C" {
struct ethash_blockhash;
typedef struct ethash_blockhash ethash_blockhash_t;
void SHA3_256(ethash_blockhash_t const* ret, const uint8_t *data, size_t size) {
CryptoPP::SHA3_256().CalculateDigest((uint8_t*)ret, data, size);
struct ethash_h256;
typedef struct ethash_h256 ethash_h256_t;
void SHA3_256(ethash_h256_t const* ret, uint8_t const* data, size_t size)
{
CryptoPP::SHA3_256().CalculateDigest((uint8_t*)ret, data, size);
}
void SHA3_512(uint8_t *const ret, const uint8_t *data, size_t size) {
CryptoPP::SHA3_512().CalculateDigest(ret, data, size);
void SHA3_512(uint8_t* const ret, uint8_t const* data, size_t size)
{
CryptoPP::SHA3_512().CalculateDigest(ret, data, size);
}
}

View file

@ -8,11 +8,11 @@
extern "C" {
#endif
struct ethash_blockhash;
typedef struct ethash_blockhash ethash_blockhash_t;
struct ethash_h256;
typedef struct ethash_h256 ethash_h256_t;
void SHA3_256(ethash_blockhash_t *const ret, const uint8_t *data, size_t size);
void SHA3_512(uint8_t *const ret, const uint8_t *data, size_t size);
void SHA3_256(ethash_h256_t const* ret, uint8_t const* data, size_t size);
void SHA3_512(uint8_t* const ret, uint8_t const* data, size_t size);
#ifdef __cplusplus
}

View file

@ -25,12 +25,12 @@
#ifdef _MSC_VER
// foward declare without all of Windows.h
__declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);
__declspec(dllimport) void __stdcall OutputDebugStringA(char const* lpOutputString);
void debugf(const char *str, ...)
void debugf(char const* str, ...)
{
va_list args;
va_start(args, str);
va_start(args, str);
char buf[1<<16];
_vsnprintf_s(buf, sizeof(buf), sizeof(buf), str, args);

View file

@ -27,7 +27,7 @@ extern "C" {
#endif
#ifdef _MSC_VER
void debugf(const char *str, ...);
void debugf(char const* str, ...);
#else
#define debugf printf
#endif

View file

@ -69,7 +69,7 @@ mkcache_bytes(PyObject *self, PyObject *args) {
params.cache_size = (size_t) cache_size;
ethash_cache cache;
cache.mem = malloc(cache_size);
ethash_mkcache(&cache, &params, (ethash_blockhash_t *) seed);
ethash_mkcache(&cache, &params, (ethash_h256_t *) seed);
PyObject * val = Py_BuildValue(PY_STRING_FORMAT, cache.mem, cache_size);
free(cache.mem);
return val;
@ -146,7 +146,7 @@ hashimoto_light(PyObject *self, PyObject *args) {
params.full_size = (size_t) full_size;
ethash_cache cache;
cache.mem = (void *) cache_bytes;
ethash_light(&out, &cache, &params, (ethash_blockhash_t *) header, nonce);
ethash_light(&out, &cache, &params, (ethash_h256_t *) header, nonce);
return Py_BuildValue("{" PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT "," PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT "}",
"mix digest", &out.mix_hash, 32,
"result", &out.result, 32);
@ -181,7 +181,7 @@ hashimoto_full(PyObject *self, PyObject *args) {
ethash_return_value out;
ethash_params params;
params.full_size = (size_t) full_size;
ethash_full(&out, (void *) full_bytes, &params, (ethash_blockhash_t *) header, nonce);
ethash_full(&out, (void *) full_bytes, &params, (ethash_h256_t *) header, nonce);
return Py_BuildValue("{" PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT ", " PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT "}",
"mix digest", &out.mix_hash, 32,
"result", &out.result, 32);
@ -227,9 +227,9 @@ mine(PyObject *self, PyObject *args) {
// TODO: Multi threading?
do {
ethash_full(&out, (void *) full_bytes, &params, (const ethash_blockhash_t *) header, nonce++);
ethash_full(&out, (void *) full_bytes, &params, (const ethash_h256_t *) header, nonce++);
// TODO: disagrees with the spec https://github.com/ethereum/wiki/wiki/Ethash#mining
} while (!ethash_check_difficulty(&out.result, (const ethash_blockhash_t *) difficulty));
} while (!ethash_check_difficulty(&out.result, (const ethash_h256_t *) difficulty));
return Py_BuildValue("{" PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT ", " PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT ", " PY_CONST_STRING_FORMAT ":K}",
"mix digest", &out.mix_hash, 32,
@ -250,7 +250,7 @@ get_seedhash(PyObject *self, PyObject *args) {
PyErr_SetString(PyExc_ValueError, error_message);
return 0;
}
ethash_blockhash_t seedhash;
ethash_h256_t seedhash;
ethash_get_seedhash(&seedhash, block_number);
return Py_BuildValue(PY_STRING_FORMAT, (char *) &seedhash, 32);
}

View file

@ -29,360 +29,510 @@ namespace fs = boost::filesystem;
#define our_alloca(param__) alloca((size_t)(param__))
std::string bytesToHexString(const uint8_t *str, const uint64_t s) {
std::ostringstream ret;
std::ostringstream ret;
for (size_t i = 0; i < s; ++i)
ret << std::hex << std::setfill('0') << std::setw(2) << std::nouppercase << (int) str[i];
for (size_t i = 0; i < s; ++i)
ret << std::hex << std::setfill('0') << std::setw(2) << std::nouppercase << (int) str[i];
return ret.str();
return ret.str();
}
std::string blockhashToHexString(ethash_blockhash_t *hash) {
return bytesToHexString((uint8_t*)hash, 32);
std::string blockhashToHexString(ethash_h256_t *hash) {
return bytesToHexString((uint8_t*)hash, 32);
}
BOOST_AUTO_TEST_CASE(fnv_hash_check) {
uint32_t x = 1235U;
const uint32_t
y = 9999999U,
expected = (FNV_PRIME * x) ^y;
uint32_t x = 1235U;
const uint32_t
y = 9999999U,
expected = (FNV_PRIME * x) ^y;
x = fnv_hash(x, y);
x = fnv_hash(x, y);
BOOST_REQUIRE_MESSAGE(x == expected,
"\nexpected: " << expected << "\n"
<< "actual: " << x << "\n");
BOOST_REQUIRE_MESSAGE(x == expected,
"\nexpected: " << expected << "\n"
<< "actual: " << x << "\n");
}
BOOST_AUTO_TEST_CASE(SHA256_check) {
ethash_blockhash_t input;
ethash_blockhash_t out;
memcpy(&input, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
SHA3_256(&out, (uint8_t*)&input, 32);
const std::string
expected = "2b5ddf6f4d21c23de216f44d5e4bdc68e044b71897837ea74c83908be7037cd7",
actual = bytesToHexString((uint8_t*)&out, 32);
BOOST_REQUIRE_MESSAGE(expected == actual,
"\nexpected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
ethash_h256_t input;
ethash_h256_t out;
memcpy(&input, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
SHA3_256(&out, (uint8_t*)&input, 32);
const std::string
expected = "2b5ddf6f4d21c23de216f44d5e4bdc68e044b71897837ea74c83908be7037cd7",
actual = bytesToHexString((uint8_t*)&out, 32);
BOOST_REQUIRE_MESSAGE(expected == actual,
"\nexpected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
BOOST_AUTO_TEST_CASE(SHA512_check) {
uint8_t input[64], out[64];
memcpy(input, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 64);
SHA3_512(out, input, 64);
const std::string
expected = "0be8a1d334b4655fe58c6b38789f984bb13225684e86b20517a55ab2386c7b61c306f25e0627c60064cecd6d80cd67a82b3890bd1289b7ceb473aad56a359405",
actual = bytesToHexString(out, 64);
BOOST_REQUIRE_MESSAGE(expected == actual,
"\nexpected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
uint8_t input[64], out[64];
memcpy(input, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 64);
SHA3_512(out, input, 64);
const std::string
expected = "0be8a1d334b4655fe58c6b38789f984bb13225684e86b20517a55ab2386c7b61c306f25e0627c60064cecd6d80cd67a82b3890bd1289b7ceb473aad56a359405",
actual = bytesToHexString(out, 64);
BOOST_REQUIRE_MESSAGE(expected == actual,
"\nexpected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
BOOST_AUTO_TEST_CASE(test_swap_endian32) {
uint32_t v32 = (uint32_t)0xBAADF00D;
v32 = ethash_swap_u32(v32);
BOOST_REQUIRE_EQUAL(v32, (uint32_t)0x0DF0ADBA);
}
BOOST_AUTO_TEST_CASE(test_swap_endian64) {
uint64_t v64 = (uint64_t)0xFEE1DEADDEADBEEF;
v64 = ethash_swap_u64(v64);
BOOST_REQUIRE_EQUAL(v64, (uint64_t)0xEFBEADDEADDEE1FE);
}
BOOST_AUTO_TEST_CASE(ethash_params_init_genesis_check) {
ethash_params params;
ethash_params_init(&params, 0);
BOOST_REQUIRE_MESSAGE(params.full_size < DATASET_BYTES_INIT,
"\nfull size: " << params.full_size << "\n"
<< "should be less than or equal to: " << DATASET_BYTES_INIT << "\n");
BOOST_REQUIRE_MESSAGE(params.full_size + 20 * MIX_BYTES >= DATASET_BYTES_INIT,
"\nfull size + 20*MIX_BYTES: " << params.full_size + 20 * MIX_BYTES << "\n"
<< "should be greater than or equal to: " << DATASET_BYTES_INIT << "\n");
BOOST_REQUIRE_MESSAGE(params.cache_size < DATASET_BYTES_INIT / 32,
"\ncache size: " << params.cache_size << "\n"
<< "should be less than or equal to: " << DATASET_BYTES_INIT / 32 << "\n");
ethash_params params;
ethash_params_init(&params, 0);
BOOST_REQUIRE_MESSAGE(params.full_size < DATASET_BYTES_INIT,
"\nfull size: " << params.full_size << "\n"
<< "should be less than or equal to: " << DATASET_BYTES_INIT << "\n");
BOOST_REQUIRE_MESSAGE(params.full_size + 20 * MIX_BYTES >= DATASET_BYTES_INIT,
"\nfull size + 20*MIX_BYTES: " << params.full_size + 20 * MIX_BYTES << "\n"
<< "should be greater than or equal to: " << DATASET_BYTES_INIT << "\n");
BOOST_REQUIRE_MESSAGE(params.cache_size < DATASET_BYTES_INIT / 32,
"\ncache size: " << params.cache_size << "\n"
<< "should be less than or equal to: " << DATASET_BYTES_INIT / 32 << "\n");
}
BOOST_AUTO_TEST_CASE(ethash_params_init_genesis_calcifide_check) {
ethash_params params;
ethash_params_init(&params, 0);
const uint32_t expected_full_size = 1073739904;
const uint32_t expected_cache_size = 16776896;
BOOST_REQUIRE_MESSAGE(params.full_size == expected_full_size,
"\nexpected: " << expected_cache_size << "\n"
<< "actual: " << params.full_size << "\n");
BOOST_REQUIRE_MESSAGE(params.cache_size == expected_cache_size,
"\nexpected: " << expected_cache_size << "\n"
<< "actual: " << params.cache_size << "\n");
}
BOOST_AUTO_TEST_CASE(light_and_full_client_checks) {
ethash_params params;
ethash_blockhash_t seed;
ethash_blockhash_t hash;
ethash_blockhash_t difficulty;
ethash_return_value light_out;
ethash_return_value full_out;
memcpy(&seed, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
memcpy(&hash, "~~~X~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
// Set the difficulty
ethash_blockhash_set(&difficulty, 0, 197);
ethash_blockhash_set(&difficulty, 1, 90);
for (int i = 2; i < 32; i++)
ethash_blockhash_set(&difficulty, i, 255);
ethash_params_init(&params, 0);
params.cache_size = 1024;
params.full_size = 1024 * 32;
ethash_cache cache;
cache.mem = our_alloca(params.cache_size);
ethash_mkcache(&cache, &params, &seed);
node *full_mem = (node *) our_alloca(params.full_size);
ethash_compute_full_data(full_mem, &params, &cache);
{
const std::string
expected = "2da2b506f21070e1143d908e867962486d6b0a02e31d468fd5e3a7143aafa76a14201f63374314e2a6aaf84ad2eb57105dea3378378965a1b3873453bb2b78f9a8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995ca8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995ca8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995c259440b89fa3481c2c33171477c305c8e1e421f8d8f6d59585449d0034f3e421808d8da6bbd0b6378f567647cc6c4ba6c434592b198ad444e7284905b7c6adaf70bf43ec2daa7bd5e8951aa609ab472c124cf9eba3d38cff5091dc3f58409edcc386c743c3bd66f92408796ee1e82dd149eaefbf52b00ce33014a6eb3e50625413b072a58bc01da28262f42cbe4f87d4abc2bf287d15618405a1fe4e386fcdafbb171064bd99901d8f81dd6789396ce5e364ac944bbbd75a7827291c70b42d26385910cd53ca535ab29433dd5c5714d26e0dce95514c5ef866329c12e958097e84462197c2b32087849dab33e88b11da61d52f9dbc0b92cc61f742c07dbbf751c49d7678624ee60dfbe62e5e8c47a03d8247643f3d16ad8c8e663953bcda1f59d7e2d4a9bf0768e789432212621967a8f41121ad1df6ae1fa78782530695414c6213942865b2730375019105cae91a4c17a558d4b63059661d9f108362143107babe0b848de412e4da59168cce82bfbff3c99e022dd6ac1e559db991f2e3f7bb910cefd173e65ed00a8d5d416534e2c8416ff23977dbf3eb7180b75c71580d08ce95efeb9b0afe904ea12285a392aff0c8561ff79fca67f694a62b9e52377485c57cc3598d84cac0a9d27960de0cc31ff9bbfe455acaa62c8aa5d2cce96f345da9afe843d258a99c4eaf3650fc62efd81c7b81cd0d534d2d71eeda7a6e315d540b4473c80f8730037dc2ae3e47b986240cfc65ccc565f0d8cde0bc68a57e39a271dda57440b3598bee19f799611d25731a96b5dbbbefdff6f4f656161462633030d62560ea4e9c161cf78fc96a2ca5aaa32453a6c5dea206f766244e8c9d9a8dc61185ce37f1fc804459c5f07434f8ecb34141b8dcae7eae704c950b55556c5f40140c3714b45eddb02637513268778cbf937a33e4e33183685f9deb31ef54e90161e76d969587dd782eaa94e289420e7c2ee908517f5893a26fdb5873d68f92d118d4bcf98d7a4916794d6ab290045e30f9ea00ca547c584b8482b0331ba1539a0f2714fddc3a0b06b0cfbb6a607b8339c39bcfd6640b1f653e9d70ef6c985b",
actual = bytesToHexString((uint8_t const *) cache.mem, params.cache_size);
BOOST_REQUIRE_MESSAGE(expected == actual,
"\nexpected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
{
node node;
ethash_calculate_dag_item(&node, 0, &params, &cache);
const std::string
actual = bytesToHexString((uint8_t const *) &node, sizeof(node)),
expected = "b1698f829f90b35455804e5185d78f549fcb1bdce2bee006d4d7e68eb154b596be1427769eb1c3c3e93180c760af75f81d1023da6a0ffbe321c153a7c0103597";
BOOST_REQUIRE_MESSAGE(actual == expected,
"\n" << "expected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
{
for (int i = 0; i < params.full_size / sizeof(node); ++i) {
for (uint32_t j = 0; j < 32; ++j) {
node expected_node;
ethash_calculate_dag_item(&expected_node, j, &params, &cache);
const std::string
actual = bytesToHexString((uint8_t const *) &(full_mem[j]), sizeof(node)),
expected = bytesToHexString((uint8_t const *) &expected_node, sizeof(node));
BOOST_REQUIRE_MESSAGE(actual == expected,
"\ni: " << j << "\n"
<< "expected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
}
}
{
uint64_t nonce = 0x7c7c597c;
ethash_full(&full_out, full_mem, &params, &hash, nonce);
ethash_light(&light_out, &cache, &params, &hash, nonce);
const std::string
light_result_string = blockhashToHexString(&light_out.result),
full_result_string = blockhashToHexString(&full_out.result);
BOOST_REQUIRE_MESSAGE(light_result_string == full_result_string,
"\nlight result: " << light_result_string.c_str() << "\n"
<< "full result: " << full_result_string.c_str() << "\n");
const std::string
light_mix_hash_string = blockhashToHexString(&light_out.mix_hash),
full_mix_hash_string = blockhashToHexString(&full_out.mix_hash);
BOOST_REQUIRE_MESSAGE(full_mix_hash_string == light_mix_hash_string,
"\nlight mix hash: " << light_mix_hash_string.c_str() << "\n"
<< "full mix hash: " << full_mix_hash_string.c_str() << "\n");
ethash_blockhash_t check_hash;
ethash_quick_hash(&check_hash, &hash, nonce, &full_out.mix_hash);
const std::string check_hash_string = blockhashToHexString(&check_hash);
BOOST_REQUIRE_MESSAGE(check_hash_string == full_result_string,
"\ncheck hash string: " << check_hash_string.c_str() << "\n"
<< "full result: " << full_result_string.c_str() << "\n");
}
{
ethash_full(&full_out, full_mem, &params, &hash, 5);
std::string
light_result_string = blockhashToHexString(&light_out.result),
full_result_string = blockhashToHexString(&full_out.result);
BOOST_REQUIRE_MESSAGE(light_result_string != full_result_string,
"\nlight result and full result should differ: " << light_result_string.c_str() << "\n");
ethash_light(&light_out, &cache, &params, &hash, 5);
light_result_string = blockhashToHexString(&light_out.result);
BOOST_REQUIRE_MESSAGE(light_result_string == full_result_string,
"\nlight result and full result should be the same\n"
<< "light result: " << light_result_string.c_str() << "\n"
<< "full result: " << full_result_string.c_str() << "\n");
std::string
light_mix_hash_string = blockhashToHexString(&light_out.mix_hash),
full_mix_hash_string = blockhashToHexString(&full_out.mix_hash);
BOOST_REQUIRE_MESSAGE(full_mix_hash_string == light_mix_hash_string,
"\nlight mix hash: " << light_mix_hash_string.c_str() << "\n"
<< "full mix hash: " << full_mix_hash_string.c_str() << "\n");
BOOST_REQUIRE_MESSAGE(ethash_check_difficulty(&full_out.result, &difficulty),
"ethash_check_difficulty failed"
);
BOOST_REQUIRE_MESSAGE(ethash_quick_check_difficulty(&hash, 5U, &full_out.mix_hash, &difficulty),
"ethash_quick_check_difficulty failed"
);
}
ethash_params params;
ethash_params_init(&params, 0);
const uint32_t expected_full_size = 1073739904;
const uint32_t expected_cache_size = 16776896;
BOOST_REQUIRE_MESSAGE(params.full_size == expected_full_size,
"\nexpected: " << expected_cache_size << "\n"
<< "actual: " << params.full_size << "\n");
BOOST_REQUIRE_MESSAGE(params.cache_size == expected_cache_size,
"\nexpected: " << expected_cache_size << "\n"
<< "actual: " << params.cache_size << "\n");
}
BOOST_AUTO_TEST_CASE(ethash_check_difficulty_check) {
ethash_blockhash_t hash;
ethash_blockhash_t target;
memcpy(&hash, "11111111111111111111111111111111", 32);
memcpy(&target, "22222222222222222222222222222222", 32);
BOOST_REQUIRE_MESSAGE(
ethash_check_difficulty(&hash, &target),
"\nexpected \"" << std::string((char *) &hash, 32).c_str() << "\" to have the same or less difficulty than \"" << std::string((char *) &target, 32).c_str() << "\"\n");
BOOST_REQUIRE_MESSAGE(
ethash_check_difficulty(&hash, &hash), "");
// "\nexpected \"" << hash << "\" to have the same or less difficulty than \"" << hash << "\"\n");
memcpy(&target, "11111111111111111111111111111112", 32);
BOOST_REQUIRE_MESSAGE(
ethash_check_difficulty(&hash, &target), "");
// "\nexpected \"" << hash << "\" to have the same or less difficulty than \"" << target << "\"\n");
memcpy(&target, "11111111111111111111111111111110", 32);
BOOST_REQUIRE_MESSAGE(
!ethash_check_difficulty(&hash, &target), "");
// "\nexpected \"" << hash << "\" to have more difficulty than \"" << target << "\"\n");
ethash_h256_t hash;
ethash_h256_t target;
memcpy(&hash, "11111111111111111111111111111111", 32);
memcpy(&target, "22222222222222222222222222222222", 32);
BOOST_REQUIRE_MESSAGE(
ethash_check_difficulty(&hash, &target),
"\nexpected \"" << std::string((char *) &hash, 32).c_str() << "\" to have the same or less difficulty than \"" << std::string((char *) &target, 32).c_str() << "\"\n");
BOOST_REQUIRE_MESSAGE(
ethash_check_difficulty(&hash, &hash), "");
// "\nexpected \"" << hash << "\" to have the same or less difficulty than \"" << hash << "\"\n");
memcpy(&target, "11111111111111111111111111111112", 32);
BOOST_REQUIRE_MESSAGE(
ethash_check_difficulty(&hash, &target), "");
// "\nexpected \"" << hash << "\" to have the same or less difficulty than \"" << target << "\"\n");
memcpy(&target, "11111111111111111111111111111110", 32);
BOOST_REQUIRE_MESSAGE(
!ethash_check_difficulty(&hash, &target), "");
// "\nexpected \"" << hash << "\" to have more difficulty than \"" << target << "\"\n");
}
BOOST_AUTO_TEST_CASE(test_ethash_io_mutable_name) {
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
// should have at least 8 bytes provided since this is what we test :)
ethash_h256_t seed1 = ethash_h256_static_init(0, 10, 65, 255, 34, 55, 22, 8);
ethash_io_mutable_name(1, &seed1, mutable_name);
BOOST_REQUIRE_EQUAL(0, strcmp(mutable_name, "1_000a41ff22371608"));
ethash_h256_t seed2 = ethash_h256_static_init(0, 0, 0, 0, 0, 0, 0, 0);
ethash_io_mutable_name(44, &seed2, mutable_name);
BOOST_REQUIRE_EQUAL(0, strcmp(mutable_name, "44_0000000000000000"));
}
BOOST_AUTO_TEST_CASE(test_ethash_dir_creation) {
ethash_blockhash_t seedhash;
memset(&seedhash, 0, 32);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash)
);
ethash_h256_t seedhash;
FILE *f = NULL;
memset(&seedhash, 0, 32);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash, &f, 64)
);
BOOST_REQUIRE(f);
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
// cleanup
fs::remove_all("./test_ethash_directory/");
}
BOOST_AUTO_TEST_CASE(test_ethash_io_write_files_are_created) {
ethash_blockhash_t seedhash;
static const int blockn = 0;
ethash_get_seedhash(&seedhash, blockn);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash)
);
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
ethash_cache cache;
ethash_params params;
uint8_t *data;
uint64_t size;
ethash_params_init(&params, blockn);
params.cache_size = 1024;
params.full_size = 1024 * 32;
cache.mem = our_alloca(params.cache_size);
ethash_mkcache(&cache, &params, &seedhash);
BOOST_REQUIRE(
ethash_io_write("./test_ethash_directory/", &params, seedhash, &cache, &data, &size)
);
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full")));
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full.info")));
// cleanup
fs::remove_all("./test_ethash_directory/");
free(data);
// cleanup
fclose(f);
fs::remove_all("./test_ethash_directory/");
}
BOOST_AUTO_TEST_CASE(test_ethash_io_memo_file_match) {
ethash_blockhash_t seedhash;
static const int blockn = 0;
ethash_get_seedhash(&seedhash, blockn);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash)
);
ethash_h256_t seedhash;
static const int blockn = 0;
FILE *f = NULL;
ethash_get_seedhash(&seedhash, blockn);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash, &f, 64)
);
BOOST_REQUIRE(f);
fclose(f);
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
// and check that we have a match when checking again
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash, &f, 64)
);
BOOST_REQUIRE(f);
ethash_cache cache;
ethash_params params;
uint8_t *data;
uint64_t size;
ethash_params_init(&params, blockn);
params.cache_size = 1024;
params.full_size = 1024 * 32;
cache.mem = our_alloca(params.cache_size);
ethash_mkcache(&cache, &params, &seedhash);
BOOST_REQUIRE(
ethash_io_write("./test_ethash_directory/", &params, seedhash, &cache, &data, &size)
);
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full")));
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full.info")));
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash)
);
// cleanup
fs::remove_all("./test_ethash_directory/");
free(data);
// cleanup
fclose(f);
fs::remove_all("./test_ethash_directory/");
}
// could have used dev::contentsNew but don't wanna try to import
// libdevcore just for one function
static std::vector<char> readFileIntoVector(char const* filename)
BOOST_AUTO_TEST_CASE(test_ethash_io_memo_file_size_mismatch) {
ethash_h256_t seedhash;
static const int blockn = 0;
FILE *f = NULL;
ethash_get_seedhash(&seedhash, blockn);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash, &f, 64)
);
BOOST_REQUIRE(f);
fclose(f);
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
// and check that we get the size mismatch detected if we request diffferent size
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_SIZE_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash, &f, 65)
);
// cleanup
fs::remove_all("./test_ethash_directory/");
}
BOOST_AUTO_TEST_CASE(light_and_full_client_checks) {
uint64_t full_size;
uint64_t cache_size;
ethash_h256_t seed;
ethash_h256_t hash;
ethash_h256_t difficulty;
ethash_return_value_t light_out;
ethash_return_value_t full_out;
memcpy(&seed, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
memcpy(&hash, "~~~X~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
// Set the difficulty
ethash_h256_set(&difficulty, 0, 197);
ethash_h256_set(&difficulty, 1, 90);
for (int i = 2; i < 32; i++)
ethash_h256_set(&difficulty, i, 255);
cache_size = 1024;
full_size = 1024 * 32;
ethash_light_t light = ethash_light_new(cache_size, &seed);
ethash_cache *cache = ethash_light_get_cache(light);
BOOST_ASSERT(light);
ethash_full_t full = ethash_full_new(
"./test_ethash_directory/",
&seed,
full_size,
ethash_light_get_cache(light),
NULL
);
BOOST_ASSERT(full);
{
const std::string
expected = "2da2b506f21070e1143d908e867962486d6b0a02e31d468fd5e3a7143aafa76a14201f63374314e2a6aaf84ad2eb57105dea3378378965a1b3873453bb2b78f9a8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995ca8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995ca8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995c259440b89fa3481c2c33171477c305c8e1e421f8d8f6d59585449d0034f3e421808d8da6bbd0b6378f567647cc6c4ba6c434592b198ad444e7284905b7c6adaf70bf43ec2daa7bd5e8951aa609ab472c124cf9eba3d38cff5091dc3f58409edcc386c743c3bd66f92408796ee1e82dd149eaefbf52b00ce33014a6eb3e50625413b072a58bc01da28262f42cbe4f87d4abc2bf287d15618405a1fe4e386fcdafbb171064bd99901d8f81dd6789396ce5e364ac944bbbd75a7827291c70b42d26385910cd53ca535ab29433dd5c5714d26e0dce95514c5ef866329c12e958097e84462197c2b32087849dab33e88b11da61d52f9dbc0b92cc61f742c07dbbf751c49d7678624ee60dfbe62e5e8c47a03d8247643f3d16ad8c8e663953bcda1f59d7e2d4a9bf0768e789432212621967a8f41121ad1df6ae1fa78782530695414c6213942865b2730375019105cae91a4c17a558d4b63059661d9f108362143107babe0b848de412e4da59168cce82bfbff3c99e022dd6ac1e559db991f2e3f7bb910cefd173e65ed00a8d5d416534e2c8416ff23977dbf3eb7180b75c71580d08ce95efeb9b0afe904ea12285a392aff0c8561ff79fca67f694a62b9e52377485c57cc3598d84cac0a9d27960de0cc31ff9bbfe455acaa62c8aa5d2cce96f345da9afe843d258a99c4eaf3650fc62efd81c7b81cd0d534d2d71eeda7a6e315d540b4473c80f8730037dc2ae3e47b986240cfc65ccc565f0d8cde0bc68a57e39a271dda57440b3598bee19f799611d25731a96b5dbbbefdff6f4f656161462633030d62560ea4e9c161cf78fc96a2ca5aaa32453a6c5dea206f766244e8c9d9a8dc61185ce37f1fc804459c5f07434f8ecb34141b8dcae7eae704c950b55556c5f40140c3714b45eddb02637513268778cbf937a33e4e33183685f9deb31ef54e90161e76d969587dd782eaa94e289420e7c2ee908517f5893a26fdb5873d68f92d118d4bcf98d7a4916794d6ab290045e30f9ea00ca547c584b8482b0331ba1539a0f2714fddc3a0b06b0cfbb6a607b8339c39bcfd6640b1f653e9d70ef6c985b",
actual = bytesToHexString((uint8_t const *) cache->mem, cache_size);
BOOST_REQUIRE_MESSAGE(expected == actual,
"\nexpected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
{
node node;
ethash_calculate_dag_item(&node, 0, cache);
const std::string
actual = bytesToHexString((uint8_t const *) &node, sizeof(node)),
expected = "b1698f829f90b35455804e5185d78f549fcb1bdce2bee006d4d7e68eb154b596be1427769eb1c3c3e93180c760af75f81d1023da6a0ffbe321c153a7c0103597";
BOOST_REQUIRE_MESSAGE(actual == expected,
"\n" << "expected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
{
for (int i = 0; i < full_size / sizeof(node); ++i) {
for (uint32_t j = 0; j < 32; ++j) {
node expected_node;
ethash_calculate_dag_item(&expected_node, j, cache);
const std::string
actual = bytesToHexString((uint8_t const *) &(full->data[j]), sizeof(node)),
expected = bytesToHexString((uint8_t const *) &expected_node, sizeof(node));
BOOST_REQUIRE_MESSAGE(actual == expected,
"\ni: " << j << "\n"
<< "expected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
}
}
{
uint64_t nonce = 0x7c7c597c;
BOOST_REQUIRE(ethash_full_compute(&full_out, full, &hash, nonce));
BOOST_REQUIRE(ethash_light_compute(&light_out, light, full_size, &hash, nonce));
const std::string
light_result_string = blockhashToHexString(&light_out.result),
full_result_string = blockhashToHexString(&full_out.result);
BOOST_REQUIRE_MESSAGE(light_result_string == full_result_string,
"\nlight result: " << light_result_string.c_str() << "\n"
<< "full result: " << full_result_string.c_str() << "\n");
const std::string
light_mix_hash_string = blockhashToHexString(&light_out.mix_hash),
full_mix_hash_string = blockhashToHexString(&full_out.mix_hash);
BOOST_REQUIRE_MESSAGE(full_mix_hash_string == light_mix_hash_string,
"\nlight mix hash: " << light_mix_hash_string.c_str() << "\n"
<< "full mix hash: " << full_mix_hash_string.c_str() << "\n");
ethash_h256_t check_hash;
ethash_quick_hash(&check_hash, &hash, nonce, &full_out.mix_hash);
const std::string check_hash_string = blockhashToHexString(&check_hash);
BOOST_REQUIRE_MESSAGE(check_hash_string == full_result_string,
"\ncheck hash string: " << check_hash_string.c_str() << "\n"
<< "full result: " << full_result_string.c_str() << "\n");
}
{
BOOST_REQUIRE(ethash_full_compute(&full_out, full, &hash, 5));
std::string
light_result_string = blockhashToHexString(&light_out.result),
full_result_string = blockhashToHexString(&full_out.result);
BOOST_REQUIRE_MESSAGE(light_result_string != full_result_string,
"\nlight result and full result should differ: " << light_result_string.c_str() << "\n");
BOOST_REQUIRE(ethash_light_compute(&light_out, light, full_size, &hash, 5));
light_result_string = blockhashToHexString(&light_out.result);
BOOST_REQUIRE_MESSAGE(light_result_string == full_result_string,
"\nlight result and full result should be the same\n"
<< "light result: " << light_result_string.c_str() << "\n"
<< "full result: " << full_result_string.c_str() << "\n");
std::string
light_mix_hash_string = blockhashToHexString(&light_out.mix_hash),
full_mix_hash_string = blockhashToHexString(&full_out.mix_hash);
BOOST_REQUIRE_MESSAGE(full_mix_hash_string == light_mix_hash_string,
"\nlight mix hash: " << light_mix_hash_string.c_str() << "\n"
<< "full mix hash: " << full_mix_hash_string.c_str() << "\n");
BOOST_REQUIRE_MESSAGE(ethash_check_difficulty(&full_out.result, &difficulty),
"ethash_check_difficulty failed"
);
BOOST_REQUIRE_MESSAGE(ethash_quick_check_difficulty(&hash, 5U, &full_out.mix_hash, &difficulty),
"ethash_quick_check_difficulty failed"
);
}
ethash_light_delete(light);
ethash_full_delete(full);
fs::remove_all("./test_ethash_directory/");
}
static bool g_executed = false;
static unsigned g_prev_progress = 0;
static int test_full_callback(unsigned _progress)
{
ifstream ifs(filename, ios::binary|ios::ate);
ifstream::pos_type pos = ifs.tellg();
std::vector<char> result((unsigned int)pos);
ifs.seekg(0, ios::beg);
ifs.read(&result[0], pos);
return result;
g_executed = true;
BOOST_CHECK(_progress >= g_prev_progress);
g_prev_progress = _progress;
return 0;
}
BOOST_AUTO_TEST_CASE(test_ethash_io_memo_file_contents) {
ethash_blockhash_t seedhash;
static const int blockn = 0;
ethash_get_seedhash(&seedhash, blockn);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash)
);
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
ethash_cache cache;
ethash_params params;
uint8_t *data;
uint64_t size;
ethash_params_init(&params, blockn);
params.cache_size = 1024;
params.full_size = 1024 * 32;
cache.mem = our_alloca(params.cache_size);
ethash_mkcache(&cache, &params, &seedhash);
BOOST_REQUIRE(
ethash_io_write("./test_ethash_directory/", &params, seedhash, &cache, &data, &size)
);
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full")));
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full.info")));
char expect_buffer[DAG_MEMO_BYTESIZE];
ethash_io_serialize_info(REVISION, seedhash, expect_buffer);
auto vec = readFileIntoVector("./test_ethash_directory/full.info");
BOOST_REQUIRE_EQUAL(vec.size(), DAG_MEMO_BYTESIZE);
BOOST_REQUIRE(memcmp(expect_buffer, &vec[0], DAG_MEMO_BYTESIZE) == 0);
// cleanup
fs::remove_all("./test_ethash_directory/");
free(data);
static int test_full_callback_that_fails(unsigned _progress)
{
return 1;
}
BOOST_AUTO_TEST_CASE(full_client_callback) {
uint64_t full_size;
uint64_t cache_size;
ethash_h256_t seed;
ethash_h256_t hash;
ethash_return_value_t full_out;
memcpy(&seed, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
memcpy(&hash, "~~~X~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
cache_size = 1024;
full_size = 1024 * 32;
ethash_cache *cache = ethash_cache_new(cache_size, &seed);
ethash_full_t full = ethash_full_new(
"./test_ethash_directory/",
&seed,
full_size,
cache,
test_full_callback
);
BOOST_ASSERT(full);
BOOST_REQUIRE(ethash_full_compute(&full_out, full, &hash, 5));
BOOST_CHECK(g_executed);
BOOST_REQUIRE_EQUAL(g_prev_progress, 100);
ethash_full_delete(full);
ethash_cache_delete(cache);
fs::remove_all("./test_ethash_directory/");
}
BOOST_AUTO_TEST_CASE(failing_full_client_callback) {
uint64_t full_size;
uint64_t cache_size;
ethash_h256_t seed;
ethash_h256_t hash;
ethash_return_value_t full_out;
memcpy(&seed, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
memcpy(&hash, "~~~X~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
cache_size = 1024;
full_size = 1024 * 32;
ethash_cache *cache = ethash_cache_new(cache_size, &seed);
ethash_full_t full = ethash_full_new(
"./test_ethash_directory/",
&seed,
full_size,
cache,
test_full_callback_that_fails
);
BOOST_ASSERT(full);
BOOST_REQUIRE(!ethash_full_compute(&full_out, full, &hash, 5));
ethash_cache_delete(cache);
ethash_full_delete(full);
fs::remove_all("./test_ethash_directory/");
}
/**
* =========================
* = DEPRECATED API TESTS =
* =========================
*
* Kept for backwards compatibility with whoever still uses it.
* Delete when deprecated API goes away
*/
BOOST_AUTO_TEST_CASE(light_and_full_client_checks_old) {
ethash_params params;
ethash_h256_t seed;
ethash_h256_t hash;
ethash_h256_t difficulty;
ethash_return_value_t light_out;
ethash_return_value_t full_out;
memcpy(&seed, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
memcpy(&hash, "~~~X~~~~~~~~~~~~~~~~~~~~~~~~~~~~", 32);
// Set the difficulty
ethash_h256_set(&difficulty, 0, 197);
ethash_h256_set(&difficulty, 1, 90);
for (int i = 2; i < 32; i++)
ethash_h256_set(&difficulty, i, 255);
ethash_params_init(&params, 0);
params.cache_size = 1024;
params.full_size = 1024 * 32;
ethash_cache cache;
cache.mem = our_alloca(params.cache_size);
cache.cache_size = params.cache_size;
ethash_mkcache(&cache, &params, &seed);
node *full_mem = (node *) our_alloca(params.full_size);
ethash_compute_full_data(full_mem, params.full_size, &cache);
{
const std::string
expected = "2da2b506f21070e1143d908e867962486d6b0a02e31d468fd5e3a7143aafa76a14201f63374314e2a6aaf84ad2eb57105dea3378378965a1b3873453bb2b78f9a8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995ca8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995ca8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995c259440b89fa3481c2c33171477c305c8e1e421f8d8f6d59585449d0034f3e421808d8da6bbd0b6378f567647cc6c4ba6c434592b198ad444e7284905b7c6adaf70bf43ec2daa7bd5e8951aa609ab472c124cf9eba3d38cff5091dc3f58409edcc386c743c3bd66f92408796ee1e82dd149eaefbf52b00ce33014a6eb3e50625413b072a58bc01da28262f42cbe4f87d4abc2bf287d15618405a1fe4e386fcdafbb171064bd99901d8f81dd6789396ce5e364ac944bbbd75a7827291c70b42d26385910cd53ca535ab29433dd5c5714d26e0dce95514c5ef866329c12e958097e84462197c2b32087849dab33e88b11da61d52f9dbc0b92cc61f742c07dbbf751c49d7678624ee60dfbe62e5e8c47a03d8247643f3d16ad8c8e663953bcda1f59d7e2d4a9bf0768e789432212621967a8f41121ad1df6ae1fa78782530695414c6213942865b2730375019105cae91a4c17a558d4b63059661d9f108362143107babe0b848de412e4da59168cce82bfbff3c99e022dd6ac1e559db991f2e3f7bb910cefd173e65ed00a8d5d416534e2c8416ff23977dbf3eb7180b75c71580d08ce95efeb9b0afe904ea12285a392aff0c8561ff79fca67f694a62b9e52377485c57cc3598d84cac0a9d27960de0cc31ff9bbfe455acaa62c8aa5d2cce96f345da9afe843d258a99c4eaf3650fc62efd81c7b81cd0d534d2d71eeda7a6e315d540b4473c80f8730037dc2ae3e47b986240cfc65ccc565f0d8cde0bc68a57e39a271dda57440b3598bee19f799611d25731a96b5dbbbefdff6f4f656161462633030d62560ea4e9c161cf78fc96a2ca5aaa32453a6c5dea206f766244e8c9d9a8dc61185ce37f1fc804459c5f07434f8ecb34141b8dcae7eae704c950b55556c5f40140c3714b45eddb02637513268778cbf937a33e4e33183685f9deb31ef54e90161e76d969587dd782eaa94e289420e7c2ee908517f5893a26fdb5873d68f92d118d4bcf98d7a4916794d6ab290045e30f9ea00ca547c584b8482b0331ba1539a0f2714fddc3a0b06b0cfbb6a607b8339c39bcfd6640b1f653e9d70ef6c985b",
actual = bytesToHexString((uint8_t const *) cache.mem, params.cache_size);
BOOST_REQUIRE_MESSAGE(expected == actual,
"\nexpected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
{
node node;
ethash_calculate_dag_item(&node, 0, &cache);
const std::string
actual = bytesToHexString((uint8_t const *) &node, sizeof(node)),
expected = "b1698f829f90b35455804e5185d78f549fcb1bdce2bee006d4d7e68eb154b596be1427769eb1c3c3e93180c760af75f81d1023da6a0ffbe321c153a7c0103597";
BOOST_REQUIRE_MESSAGE(actual == expected,
"\n" << "expected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
{
for (int i = 0; i < params.full_size / sizeof(node); ++i) {
for (uint32_t j = 0; j < 32; ++j) {
node expected_node;
ethash_calculate_dag_item(&expected_node, j, &cache);
const std::string
actual = bytesToHexString((uint8_t const *) &(full_mem[j]), sizeof(node)),
expected = bytesToHexString((uint8_t const *) &expected_node, sizeof(node));
BOOST_REQUIRE_MESSAGE(actual == expected,
"\ni: " << j << "\n"
<< "expected: " << expected.c_str() << "\n"
<< "actual: " << actual.c_str() << "\n");
}
}
}
{
uint64_t nonce = 0x7c7c597c;
ethash_full(&full_out, full_mem, &params, &hash, nonce);
ethash_light(&light_out, &cache, &params, &hash, nonce);
const std::string
light_result_string = blockhashToHexString(&light_out.result),
full_result_string = blockhashToHexString(&full_out.result);
BOOST_REQUIRE_MESSAGE(light_result_string == full_result_string,
"\nlight result: " << light_result_string.c_str() << "\n"
<< "full result: " << full_result_string.c_str() << "\n");
const std::string
light_mix_hash_string = blockhashToHexString(&light_out.mix_hash),
full_mix_hash_string = blockhashToHexString(&full_out.mix_hash);
BOOST_REQUIRE_MESSAGE(full_mix_hash_string == light_mix_hash_string,
"\nlight mix hash: " << light_mix_hash_string.c_str() << "\n"
<< "full mix hash: " << full_mix_hash_string.c_str() << "\n");
ethash_h256_t check_hash;
ethash_quick_hash(&check_hash, &hash, nonce, &full_out.mix_hash);
const std::string check_hash_string = blockhashToHexString(&check_hash);
BOOST_REQUIRE_MESSAGE(check_hash_string == full_result_string,
"\ncheck hash string: " << check_hash_string.c_str() << "\n"
<< "full result: " << full_result_string.c_str() << "\n");
}
{
ethash_full(&full_out, full_mem, &params, &hash, 5);
std::string
light_result_string = blockhashToHexString(&light_out.result),
full_result_string = blockhashToHexString(&full_out.result);
BOOST_REQUIRE_MESSAGE(light_result_string != full_result_string,
"\nlight result and full result should differ: " << light_result_string.c_str() << "\n");
ethash_light(&light_out, &cache, &params, &hash, 5);
light_result_string = blockhashToHexString(&light_out.result);
BOOST_REQUIRE_MESSAGE(light_result_string == full_result_string,
"\nlight result and full result should be the same\n"
<< "light result: " << light_result_string.c_str() << "\n"
<< "full result: " << full_result_string.c_str() << "\n");
std::string
light_mix_hash_string = blockhashToHexString(&light_out.mix_hash),
full_mix_hash_string = blockhashToHexString(&full_out.mix_hash);
BOOST_REQUIRE_MESSAGE(full_mix_hash_string == light_mix_hash_string,
"\nlight mix hash: " << light_mix_hash_string.c_str() << "\n"
<< "full mix hash: " << full_mix_hash_string.c_str() << "\n");
BOOST_REQUIRE_MESSAGE(ethash_check_difficulty(&full_out.result, &difficulty),
"ethash_check_difficulty failed"
);
BOOST_REQUIRE_MESSAGE(ethash_quick_check_difficulty(&hash, 5U, &full_out.mix_hash, &difficulty),
"ethash_quick_check_difficulty failed"
);
}
}

View file

@ -3,6 +3,13 @@
# Strict mode
set -e
VALGRIND_ARGS="--tool=memcheck"
VALGRIND_ARGS+=" --leak-check=yes"
VALGRIND_ARGS+=" --track-origins=yes"
VALGRIND_ARGS+=" --show-reachable=yes"
VALGRIND_ARGS+=" --num-callers=20"
VALGRIND_ARGS+=" --track-fds=yes"
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
@ -17,3 +24,9 @@ cd $TEST_DIR/build ;
cmake ../../.. > /dev/null
make Test
./test/c/Test
# If we have valgrind also run memory check tests
if hash valgrind 2>/dev/null; then
echo "======== Running tests under valgrind ========";
cd $TEST_DIR/build/ && valgrind $VALGRIND_ARGS ./test/c/Test
fi

View file

@ -1,82 +0,0 @@
package ethashTest
import (
"bytes"
"crypto/rand"
"encoding/hex"
"log"
"math/big"
"testing"
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/ethdb"
)
func TestEthash(t *testing.T) {
seedHash := make([]byte, 32)
_, err := rand.Read(seedHash)
if err != nil {
panic(err)
}
db, err := ethdb.NewMemDatabase()
if err != nil {
panic(err)
}
blockProcessor, err := core.NewCanonical(5, db)
if err != nil {
panic(err)
}
log.Println("Block Number: ", blockProcessor.ChainManager().CurrentBlock().Number())
e := ethash.New(blockProcessor.ChainManager())
miningHash := make([]byte, 32)
if _, err := rand.Read(miningHash); err != nil {
panic(err)
}
diff := big.NewInt(10000)
log.Println("difficulty", diff)
nonce := uint64(0)
ghash_full := e.FullHash(nonce, miningHash)
log.Printf("ethash full (on nonce): %x %x\n", ghash_full, nonce)
ghash_light := e.LightHash(nonce, miningHash)
log.Printf("ethash light (on nonce): %x %x\n", ghash_light, nonce)
if bytes.Compare(ghash_full, ghash_light) != 0 {
t.Errorf("full: %x, light: %x", ghash_full, ghash_light)
}
}
func TestGetSeedHash(t *testing.T) {
seed0, err := ethash.GetSeedHash(0)
if err != nil {
t.Errorf("Failed to get seedHash for block 0: %v", err)
}
if bytes.Compare(seed0, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) != 0 {
log.Printf("seedHash for block 0 should be 0s, was: %v\n", seed0)
}
seed1, err := ethash.GetSeedHash(30000)
if err != nil {
t.Error(err)
}
// From python:
// > from pyethash import get_seedhash
// > get_seedhash(30000)
expectedSeed1, err := hex.DecodeString("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
if err != nil {
t.Error(err)
}
if bytes.Compare(seed1, expectedSeed1) != 0 {
log.Printf("seedHash for block 1 should be: %v,\nactual value: %v\n", expectedSeed1, seed1)
}
}

View file

@ -24,8 +24,9 @@ fi
echo -e "\n################# Testing C ##################"
$TEST_DIR/c/test.sh
echo -e "\n################# Testing Python ##################"
$TEST_DIR/python/test.sh
# Temporarily commenting out python tests until they conform to the API
#echo -e "\n################# Testing Python ##################"
#$TEST_DIR/python/test.sh
#echo "################# Testing Go ##################"
#$TEST_DIR/go/test.sh
echo "################# Testing Go ##################"
cd $TEST_DIR/.. && go test -timeout 9999s