Replace Keccak256 in oPoW with CSHAKE256 with domain seperation (#1842)

* 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 <orinewman1@gmail.com>
This commit is contained in:
Elichai Turkel 2021-11-07 18:36:30 +02:00 committed by GitHub
parent a2173ef80a
commit e3463b7268
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 171 additions and 71 deletions

View File

@ -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"},
@ -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: {
@ -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"},
@ -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: {
@ -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"},
},
},
}

View File

@ -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
}

View File

@ -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",

View File

@ -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

View File

@ -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"
)
@ -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)
}

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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

View File

@ -19,8 +19,27 @@ var genesisTxPayload = []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Blue score
0x00, 0xE1, 0xF5, 0x05, 0x00, 0x00, 0x00, 0x00, // Subsidy
0x00, 0x00, //script version
0x01, // Varint
0x00, // OP-FALSE
0x01, // Varint
0x00, // OP-FALSE
0xd7, 0x95, 0xd7, 0x9e, 0xd7, 0x94, 0x20, 0xd7, // ומה די עליך ועל אחיך ייטב בשאר כספא ודהבה למעבד כרעות אלהכם תעבדון
0x93, 0xd7, 0x99, 0x20, 0xd7, 0xa2, 0xd7, 0x9c,
0xd7, 0x99, 0xd7, 0x9a, 0x20, 0xd7, 0x95, 0xd7,
0xa2, 0xd7, 0x9c, 0x20, 0xd7, 0x90, 0xd7, 0x97,
0xd7, 0x99, 0xd7, 0x9a, 0x20, 0xd7, 0x99, 0xd7,
0x99, 0xd7, 0x98, 0xd7, 0x91, 0x20, 0xd7, 0x91,
0xd7, 0xa9, 0xd7, 0x90, 0xd7, 0xa8, 0x20, 0xd7,
0x9b, 0xd7, 0xa1, 0xd7, 0xa4, 0xd7, 0x90, 0x20,
0xd7, 0x95, 0xd7, 0x93, 0xd7, 0x94, 0xd7, 0x91,
0xd7, 0x94, 0x20, 0xd7, 0x9c, 0xd7, 0x9e, 0xd7,
0xa2, 0xd7, 0x91, 0xd7, 0x93, 0x20, 0xd7, 0x9b,
0xd7, 0xa8, 0xd7, 0xa2, 0xd7, 0x95, 0xd7, 0xaa,
0x20, 0xd7, 0x90, 0xd7, 0x9c, 0xd7, 0x94, 0xd7,
0x9b, 0xd7, 0x9d, 0x20, 0xd7, 0xaa, 0xd7, 0xa2,
0xd7, 0x91, 0xd7, 0x93, 0xd7, 0x95, 0xd7, 0x9f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Bitcoin block hash 00000000000000000001733c62adb19f1b77fa0735d0e11f25af36fc9ca908a5
0x00, 0x01, 0x73, 0x3c, 0x62, 0xad, 0xb1, 0x9f,
0x1b, 0x77, 0xfa, 0x07, 0x35, 0xd0, 0xe1, 0x1f,
0x25, 0xaf, 0x36, 0xfc, 0x9c, 0xa9, 0x08, 0xa5,
}
// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
@ -31,19 +50,19 @@ var genesisCoinbaseTx = transactionhelper.NewSubnetworkTransaction(0, []*externa
// genesisHash is the hash of the first block in the block DAG for the main
// network (genesis block).
var genesisHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
0x16, 0x57, 0xf4, 0x2c, 0x8e, 0x19, 0x81, 0xe7,
0x7a, 0x02, 0x66, 0x42, 0x7c, 0xb4, 0x15, 0x59,
0x88, 0x17, 0x50, 0xb9, 0x5b, 0xf9, 0x9d, 0xe1,
0xfe, 0x89, 0x08, 0x44, 0xba, 0x0d, 0x00, 0xc4,
0xca, 0xeb, 0x97, 0x96, 0x0a, 0x16, 0x0c, 0x21,
0x1a, 0x6b, 0x21, 0x96, 0xbd, 0x78, 0x39, 0x9f,
0xd4, 0xc4, 0xcc, 0x5b, 0x50, 0x9f, 0x55, 0xc1,
0x2c, 0x8a, 0x7d, 0x81, 0x5f, 0x75, 0x36, 0xea,
})
// genesisMerkleRoot is the hash of the first transaction in the genesis block
// for the main network.
var genesisMerkleRoot = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
0xea, 0xb2, 0xe3, 0xfa, 0xd6, 0x72, 0x95, 0x38,
0x04, 0xe0, 0x43, 0x1a, 0xf2, 0x2d, 0xc5, 0xeb,
0x27, 0xd8, 0x0a, 0xdd, 0x4c, 0xc5, 0xd9, 0x80,
0x90, 0xc7, 0x6c, 0x43, 0xce, 0x81, 0x23, 0x68,
0xca, 0xed, 0xaf, 0x7d, 0x4a, 0x08, 0xbb, 0xe8,
0x90, 0x11, 0x64, 0x0c, 0x48, 0x41, 0xb6, 0x6d,
0x5b, 0xba, 0x67, 0xd7, 0x28, 0x8c, 0xe6, 0xd6,
0x72, 0x28, 0xdb, 0x00, 0x09, 0x66, 0xe9, 0x74,
})
// genesisBlock defines the genesis block of the block DAG which serves as the
@ -55,9 +74,9 @@ var genesisBlock = externalapi.DomainBlock{
genesisMerkleRoot,
&externalapi.DomainHash{},
externalapi.NewDomainHashFromByteArray(muhash.EmptyMuHashHash.AsArray()),
0x17c5f0c90e6,
0x17cfb020c02,
0x1e7fffff,
0x165c5,
0x3392c,
0,
0,
big.NewInt(0),
@ -86,10 +105,10 @@ var devnetGenesisCoinbaseTx = transactionhelper.NewSubnetworkTransaction(0,
// devGenesisHash is the hash of the first block in the block DAG for the development
// network (genesis block).
var devnetGenesisHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
0x3f, 0x4e, 0x3d, 0x83, 0x06, 0x9a, 0x19, 0x2b,
0xe4, 0x32, 0xe9, 0x2b, 0x81, 0x6b, 0x27, 0xce,
0xb0, 0x29, 0xea, 0x0a, 0x15, 0xb3, 0xba, 0x3f,
0x73, 0x35, 0xfe, 0x9a, 0x78, 0x74, 0x60, 0x3c,
0x34, 0x3b, 0x53, 0xb7, 0xad, 0x82, 0x8a, 0x33,
0x4b, 0x03, 0xb1, 0xbb, 0x13, 0x15, 0xb0, 0x75,
0xf3, 0xf0, 0x40, 0xfb, 0x64, 0x0c, 0xca, 0x19,
0xc9, 0x61, 0xe7, 0x69, 0xdb, 0x98, 0x98, 0x3e,
})
// devnetGenesisMerkleRoot is the hash of the first transaction in the genesis block
@ -112,7 +131,7 @@ var devnetGenesisBlock = externalapi.DomainBlock{
externalapi.NewDomainHashFromByteArray(muhash.EmptyMuHashHash.AsArray()),
0x11e9db49828,
0x1e7fffff,
0x38244,
0x48e5e,
0,
0,
big.NewInt(0),
@ -140,10 +159,10 @@ var simnetGenesisCoinbaseTx = transactionhelper.NewSubnetworkTransaction(0,
// simnetGenesisHash is the hash of the first block in the block DAG for
// the simnet (genesis block).
var simnetGenesisHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
0x9b, 0x66, 0xc8, 0x1a, 0xd5, 0xb8, 0xc2, 0xf7,
0x45, 0x45, 0x47, 0xd6, 0x83, 0x83, 0xf8, 0x27,
0x45, 0x46, 0xcf, 0x98, 0x1c, 0x74, 0x46, 0xcf,
0x25, 0x5e, 0xd3, 0xe7, 0xe4, 0x68, 0x47, 0x6f,
0x41, 0x1f, 0x8c, 0xd2, 0x6f, 0x3d, 0x41, 0xae,
0xa3, 0x9e, 0x78, 0x57, 0x39, 0x27, 0xda, 0x24,
0xd2, 0x39, 0x95, 0x70, 0x5b, 0x57, 0x9f, 0x30,
0x95, 0x9b, 0x91, 0x27, 0xe9, 0x6b, 0x79, 0xe3,
})
// simnetGenesisMerkleRoot is the hash of the first transaction in the genesis block
@ -164,9 +183,9 @@ var simnetGenesisBlock = externalapi.DomainBlock{
simnetGenesisMerkleRoot,
&externalapi.DomainHash{},
externalapi.NewDomainHashFromByteArray(muhash.EmptyMuHashHash.AsArray()),
0x17c2df94abb,
0x17c5f62fbb6,
0x207fffff,
0x0,
0x2,
0,
0,
big.NewInt(0),
@ -194,10 +213,10 @@ var testnetGenesisCoinbaseTx = transactionhelper.NewSubnetworkTransaction(0,
// testnetGenesisHash is the hash of the first block in the block DAG for the test
// network (genesis block).
var testnetGenesisHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
0xe0, 0xf5, 0xec, 0xd0, 0x3a, 0x46, 0xc5, 0xc9,
0x55, 0xd7, 0xba, 0x3d, 0x56, 0xb8, 0x85, 0xe2,
0xdb, 0x37, 0xe4, 0xd9, 0x0e, 0xf3, 0xab, 0xae,
0x04, 0xac, 0x35, 0x6b, 0x7a, 0x30, 0x89, 0xa8,
0xf8, 0x96, 0xa3, 0x03, 0x48, 0x73, 0xbe, 0x17,
0x39, 0xfc, 0x43, 0x59, 0x23, 0x68, 0x99, 0xfd,
0x3d, 0x65, 0xd2, 0xbc, 0x94, 0xf9, 0x78, 0x0d,
0xf0, 0xd0, 0xda, 0x3e, 0xb1, 0xcc, 0x43, 0x70,
})
// testnetGenesisMerkleRoot is the hash of the first transaction in the genesis block
@ -218,9 +237,9 @@ var testnetGenesisBlock = externalapi.DomainBlock{
testnetGenesisMerkleRoot,
&externalapi.DomainHash{},
externalapi.NewDomainHashFromByteArray(muhash.EmptyMuHashHash.AsArray()),
0x17c5f0d12b2,
0x17c5f62fbb6,
0x1e7fffff,
0x52c45,
0x14582,
0,
0,
big.NewInt(0),