diff --git a/consensus/ethash/algorithm.go b/consensus/ethash/algorithm.go index 905a7b1ea714..f592c53f9a4c 100644 --- a/consensus/ethash/algorithm.go +++ b/consensus/ethash/algorithm.go @@ -18,7 +18,6 @@ package ethash import ( "encoding/binary" - "hash" "math/big" "reflect" "runtime" @@ -94,17 +93,6 @@ func calcDatasetSize(epoch int) uint64 { // reused between hash runs instead of requiring new ones to be created. type hasher func(dest []byte, data []byte) -// makeHasher creates a repetitive hasher, allowing the same hash data structures -// to be reused between hash runs instead of requiring new ones to be created. -// The returned function is not thread safe! -func makeHasher(h hash.Hash) hasher { - return func(dest []byte, data []byte) { - h.Write(data) - h.Sum(dest[:0]) - h.Reset() - } -} - // seedHash is the seed to use for generating a verification cache and the mining // dataset. func seedHash(block uint64) []byte { @@ -112,7 +100,7 @@ func seedHash(block uint64) []byte { if block < epochLength { return seed } - keccak256 := makeHasher(sha3.NewKeccak256()) + keccak256 := sha3.KeccakFast256 for i := 0; i < int(block/epochLength); i++ { keccak256(seed, seed) } @@ -166,7 +154,7 @@ func generateCache(dest []uint32, epoch uint64, seed []byte) { } }() // Create a hasher to reuse between invocations - keccak512 := makeHasher(sha3.NewKeccak512()) + keccak512 := sha3.KeccakFast512 // Sequentially produce the initial dataset keccak512(cache, seed) @@ -299,7 +287,7 @@ func generateDataset(dest []uint32, epoch uint64, cache []uint32) { defer pend.Done() // Create a hasher to reuse between invocations - keccak512 := makeHasher(sha3.NewKeccak512()) + keccak512 := sha3.KeccakFast512 // Calculate the data segment this thread should generate batch := uint32((size + hashBytes*uint64(threads) - 1) / (hashBytes * uint64(threads))) @@ -373,7 +361,7 @@ func hashimoto(hash []byte, nonce uint64, size uint64, lookup func(index uint32) // in-memory cache) in order to produce our final value for a particular header // hash and nonce. func hashimotoLight(size uint64, cache []uint32, hash []byte, nonce uint64) ([]byte, []byte) { - keccak512 := makeHasher(sha3.NewKeccak512()) + keccak512 := sha3.KeccakFast512 lookup := func(index uint32) []uint32 { rawData := generateDatasetItem(cache, index, keccak512) diff --git a/crypto/sha3/sha3.go b/crypto/sha3/sha3.go index b12a35c87fc8..1140a4d266eb 100644 --- a/crypto/sha3/sha3.go +++ b/crypto/sha3/sha3.go @@ -4,6 +4,8 @@ package sha3 +import "encoding/binary" + // spongeDirection indicates the direction bytes are flowing through the sponge. type spongeDirection int @@ -190,3 +192,60 @@ func (d *state) Sum(in []byte) []byte { dup.Read(hash) return append(in, hash...) } + + + +func keccakFast(out []byte, bits int, data []byte) { + const wordSize = 8 + hashSize := bits / 8 + blockSize := (1600 - bits * 2) / 8 + + var state [25]uint64 + + dataIndex := 0 + + dataLen := len(data) + for dataLen >= blockSize { + for i := 0; i < (blockSize / wordSize); i++ { + state[i] ^= binary.LittleEndian.Uint64(data[dataIndex:]) + dataIndex += wordSize + } + keccakF1600(&state) + dataLen -= blockSize + } + + stateIndex := 0 + for dataLen >= wordSize { + state[stateIndex] ^= binary.LittleEndian.Uint64(data[dataIndex:]) + stateIndex++ + dataIndex += wordSize + dataLen -= wordSize + } + + var lastWord [8]byte + lastWordIndex := 0 + for dataLen > 0 { + lastWord[lastWordIndex] = data[dataIndex] + lastWordIndex++ + dataIndex++ + dataLen-- + } + lastWord[lastWordIndex] = 0x01 + state[stateIndex] ^= binary.LittleEndian.Uint64(lastWord[:]) + + state[(blockSize/wordSize) - 1] ^= 0x8000000000000000 + + keccakF1600(&state) + + for i := 0; i < (hashSize / wordSize); i++ { + binary.LittleEndian.PutUint64(out[i * 8:], state[i]) + } +} + +func KeccakFast256(dest []byte, data []byte) { + keccakFast(dest, 256, data) +} + +func KeccakFast512(dest []byte, data []byte) { + keccakFast(dest, 512, data) +}