Skip to content

Commit

Permalink
Replace Keccak256 in oPoW with CSHAKE256 with domain seperation (#1842)
Browse files Browse the repository at this point in the history
* Replace keccak with CSHAKE256 in oPoW

* Add benchmarks to hash writers to compare blake2b to the CSHAKE

* Update genesis blocks

* Update tests

* Define genesis's block level to be the maximal one

* Add message to genesis coinbase

* Add comments to genesis coinbase

* Fix tests

Co-authored-by: Ori Newman <[email protected]>
  • Loading branch information
elichai and someone235 authored Nov 7, 2021
1 parent a2173ef commit e3463b7
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 71 deletions.
50 changes: 25 additions & 25 deletions domain/consensus/processes/dagtraversalmanager/window_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ func TestBlockWindow(t *testing.T) {
{
parents: []string{"C", "D"},
id: "E",
expectedWindow: []string{"D", "C", "B"},
expectedWindow: []string{"C", "D", "B"},
},
{
parents: []string{"C", "D"},
id: "F",
expectedWindow: []string{"D", "C", "B"},
expectedWindow: []string{"C", "D", "B"},
},
{
parents: []string{"A"},
Expand All @@ -132,37 +132,37 @@ func TestBlockWindow(t *testing.T) {
{
parents: []string{"H", "F"},
id: "I",
expectedWindow: []string{"F", "D", "C", "H", "G", "B"},
expectedWindow: []string{"F", "H", "C", "D", "G", "B"},
},
{
parents: []string{"I"},
id: "J",
expectedWindow: []string{"I", "F", "D", "C", "H", "G", "B"},
expectedWindow: []string{"I", "F", "H", "C", "D", "G", "B"},
},
{
parents: []string{"J"},
id: "K",
expectedWindow: []string{"J", "I", "F", "D", "C", "H", "G", "B"},
expectedWindow: []string{"J", "I", "F", "H", "C", "D", "G", "B"},
},
{
parents: []string{"K"},
id: "L",
expectedWindow: []string{"K", "J", "I", "F", "D", "C", "H", "G", "B"},
expectedWindow: []string{"K", "J", "I", "F", "H", "C", "D", "G", "B"},
},
{
parents: []string{"L"},
id: "M",
expectedWindow: []string{"L", "K", "J", "I", "F", "D", "C", "H", "G", "B"},
expectedWindow: []string{"L", "K", "J", "I", "F", "H", "C", "D", "G", "B"},
},
{
parents: []string{"M"},
id: "N",
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "D", "C", "H", "G"},
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "H", "C", "D", "G"},
},
{
parents: []string{"N"},
id: "O",
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "D", "C", "H"},
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "H", "C", "D"},
},
},
dagconfig.DevnetParams.Name: {
Expand All @@ -184,12 +184,12 @@ func TestBlockWindow(t *testing.T) {
{
parents: []string{"C", "D"},
id: "E",
expectedWindow: []string{"C", "D", "B"},
expectedWindow: []string{"D", "C", "B"},
},
{
parents: []string{"C", "D"},
id: "F",
expectedWindow: []string{"C", "D", "B"},
expectedWindow: []string{"D", "C", "B"},
},
{
parents: []string{"A"},
Expand All @@ -204,37 +204,37 @@ func TestBlockWindow(t *testing.T) {
{
parents: []string{"H", "F"},
id: "I",
expectedWindow: []string{"F", "C", "H", "D", "G", "B"},
expectedWindow: []string{"F", "D", "C", "H", "B", "G"},
},
{
parents: []string{"I"},
id: "J",
expectedWindow: []string{"I", "F", "C", "H", "D", "G", "B"},
expectedWindow: []string{"I", "F", "D", "C", "H", "B", "G"},
},
{
parents: []string{"J"},
id: "K",
expectedWindow: []string{"J", "I", "F", "C", "H", "D", "G", "B"},
expectedWindow: []string{"J", "I", "F", "D", "C", "H", "B", "G"},
},
{
parents: []string{"K"},
id: "L",
expectedWindow: []string{"K", "J", "I", "F", "C", "H", "D", "G", "B"},
expectedWindow: []string{"K", "J", "I", "F", "D", "C", "H", "B", "G"},
},
{
parents: []string{"L"},
id: "M",
expectedWindow: []string{"L", "K", "J", "I", "F", "C", "H", "D", "G", "B"},
expectedWindow: []string{"L", "K", "J", "I", "F", "D", "C", "H", "B", "G"},
},
{
parents: []string{"M"},
id: "N",
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "C", "H", "D", "G"},
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "D", "C", "H", "B"},
},
{
parents: []string{"N"},
id: "O",
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "C", "H", "D"},
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "D", "C", "H"},
},
},
dagconfig.SimnetParams.Name: {
Expand Down Expand Up @@ -276,37 +276,37 @@ func TestBlockWindow(t *testing.T) {
{
parents: []string{"H", "F"},
id: "I",
expectedWindow: []string{"F", "H", "D", "C", "G", "B"},
expectedWindow: []string{"F", "D", "H", "C", "G", "B"},
},
{
parents: []string{"I"},
id: "J",
expectedWindow: []string{"I", "F", "H", "D", "C", "G", "B"},
expectedWindow: []string{"I", "F", "D", "H", "C", "G", "B"},
},
{
parents: []string{"J"},
id: "K",
expectedWindow: []string{"J", "I", "F", "H", "D", "C", "G", "B"},
expectedWindow: []string{"J", "I", "F", "D", "H", "C", "G", "B"},
},
{
parents: []string{"K"},
id: "L",
expectedWindow: []string{"K", "J", "I", "F", "H", "D", "C", "G", "B"},
expectedWindow: []string{"K", "J", "I", "F", "D", "H", "C", "G", "B"},
},
{
parents: []string{"L"},
id: "M",
expectedWindow: []string{"L", "K", "J", "I", "F", "H", "D", "C", "G", "B"},
expectedWindow: []string{"L", "K", "J", "I", "F", "D", "H", "C", "G", "B"},
},
{
parents: []string{"M"},
id: "N",
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "H", "D", "C", "G"},
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "D", "H", "C", "G"},
},
{
parents: []string{"N"},
id: "O",
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "H", "D", "C"},
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "D", "H", "C"},
},
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import (

func TestDifficulty(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
// Mainnet's genesis is too new, so if we'll build on it we'll get to the future very quickly.
// TODO: Once it gets older, we should unskip this test.
if consensusConfig.Name == "kaspa-mainnet" {
return
}

if consensusConfig.DisableDifficultyAdjustment {
return
}
Expand Down
2 changes: 1 addition & 1 deletion domain/consensus/processes/pruningmanager/pruning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func TestPruning(t *testing.T) {
dagconfig.SimnetParams.Name: "1582",
},
"dag-for-test-pruning.json": {
dagconfig.MainnetParams.Name: "503",
dagconfig.MainnetParams.Name: "502",
dagconfig.TestnetParams.Name: "502",
dagconfig.DevnetParams.Name: "502",
dagconfig.SimnetParams.Name: "502",
Expand Down
20 changes: 6 additions & 14 deletions domain/consensus/utils/hashes/domains.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,15 @@ func NewBlockHashWriter() HashWriter {
}

// NewPoWHashWriter Returns a new HashWriter used for the PoW function
func NewPoWHashWriter() HashWriter {
keccak256 := sha3.NewLegacyKeccak256()
_, err := keccak256.Write([]byte(proofOfWorkDomain))
if err != nil {
panic(errors.Wrap(err, "this should never happen"))
}
return HashWriter{keccak256}
func NewPoWHashWriter() ShakeHashWriter {
shake256 := sha3.NewCShake256(nil, []byte(proofOfWorkDomain))
return ShakeHashWriter{shake256}
}

// NewHeavyHashWriter Returns a new HashWriter used for the HeavyHash function
func NewHeavyHashWriter() HashWriter {
keccak256 := sha3.NewLegacyKeccak256()
_, err := keccak256.Write([]byte(heavyHashDomain))
if err != nil {
panic(errors.Wrap(err, "this should never happen"))
}
return HashWriter{keccak256}
func NewHeavyHashWriter() ShakeHashWriter {
shake256 := sha3.NewCShake256(nil, []byte(heavyHashDomain))
return ShakeHashWriter{shake256}
}

// NewMerkleBranchHashWriter Returns a new HashWriter used for a merkle tree branch
Expand Down
27 changes: 27 additions & 0 deletions domain/consensus/utils/hashes/writers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package hashes
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/pkg/errors"
"golang.org/x/crypto/sha3"
"hash"
)

Expand Down Expand Up @@ -30,3 +31,29 @@ func (h HashWriter) Finalize() *externalapi.DomainHash {
copy(sum[:], h.Sum(sum[:0]))
return externalapi.NewDomainHashFromByteArray(&sum)
}

// ShakeHashWriter is exactly the same as HashWriter but for CShake256
type ShakeHashWriter struct {
sha3.ShakeHash
}

// InfallibleWrite is just like write but doesn't return anything
func (h *ShakeHashWriter) InfallibleWrite(p []byte) {
// This write can never return an error, this is part of the hash.Hash interface contract.
_, err := h.Write(p)
if err != nil {
panic(errors.Wrap(err, "this should never happen. sha3.ShakeHash interface promises to not return errors."))
}
}

// Finalize returns the resulting hash
func (h *ShakeHashWriter) Finalize() *externalapi.DomainHash {
var sum [externalapi.DomainHashSize]byte
// This should prevent `Sum` for allocating an output buffer, by using the DomainHash buffer. we still copy because we don't want to rely on that.
_, err := h.Read(sum[:])
if err != nil {
panic(errors.Wrap(err, "this should never happen. sha3.ShakeHash interface promises to not return errors."))
}
h.ShakeHash = nil // prevent double reading as it will return a different hash
return externalapi.NewDomainHashFromByteArray(&sum)
}
50 changes: 50 additions & 0 deletions domain/consensus/utils/hashes/writers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package hashes

import (
"math/rand"
"testing"
)

func BenchmarkNewBlockHashWriterSmall(b *testing.B) {
r := rand.New(rand.NewSource(0))
var someBytes [32]byte
r.Read(someBytes[:])
for i := 0; i < b.N; i++ {
hasher := NewBlockHashWriter()
hasher.InfallibleWrite(someBytes[:])
hasher.Finalize()
}
}

func BenchmarkNewBlockHashWriterBig(b *testing.B) {
r := rand.New(rand.NewSource(0))
var someBytes [1024]byte
r.Read(someBytes[:])
for i := 0; i < b.N; i++ {
hasher := NewBlockHashWriter()
hasher.InfallibleWrite(someBytes[:])
hasher.Finalize()
}
}

func BenchmarkNewHeavyHashWriterSmall(b *testing.B) {
r := rand.New(rand.NewSource(0))
var someBytes [32]byte
r.Read(someBytes[:])
for i := 0; i < b.N; i++ {
hasher := NewHeavyHashWriter()
hasher.InfallibleWrite(someBytes[:])
hasher.Finalize()
}
}

func BenchmarkNewHeavyHashWriterBig(b *testing.B) {
r := rand.New(rand.NewSource(0))
var someBytes [1024]byte
r.Read(someBytes[:])
for i := 0; i < b.N; i++ {
hasher := NewHeavyHashWriter()
hasher.InfallibleWrite(someBytes[:])
hasher.Finalize()
}
}
2 changes: 1 addition & 1 deletion domain/consensus/utils/pow/heavyhash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func BenchmarkMatrix_HeavyHash(b *testing.B) {
}

func TestMatrix_HeavyHash(t *testing.T) {
expected, err := hex.DecodeString("288fd7da3f7970c9bb2f8f0c802603a4750aab70afb39ba4174c1c3d57b7f6fe")
expected, err := hex.DecodeString("87689f379943eaf9b7475ca95325687772bfcc68fc7899caeb4409ec4590c325")
if err != nil {
t.Fatal(err)
}
Expand Down
6 changes: 6 additions & 0 deletions domain/consensus/utils/pow/pow.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ func toBig(hash *externalapi.DomainHash) *big.Int {

// BlockLevel returns the block level of the given header.
func BlockLevel(header externalapi.BlockHeader) int {
// Genesis is defined to be the root of all blocks at all levels, so we define it to be the maximal
// block level.
if len(header.DirectParents()) == 0 {
return constants.MaxBlockLevel
}

proofOfWorkValue := CalculateProofOfWorkValue(header.ToMutable())
for blockLevel := 0; ; blockLevel++ {
if blockLevel == constants.MaxBlockLevel || proofOfWorkValue.Bit(blockLevel+1) != 0 {
Expand Down
2 changes: 1 addition & 1 deletion domain/dagconfig/consensus_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
//

const (
defaultMaxCoinbasePayloadLength = 150
defaultMaxCoinbasePayloadLength = 172
// defaultMaxBlockMass is a bound on the mass of a block, larger values increase the bound d
// on the round trip time of a block, which affects the other parameters as described below
defaultMaxBlockMass = 500_000
Expand Down
Loading

0 comments on commit e3463b7

Please sign in to comment.