stasatdaglabs 28d0f1ea2e
Set MaxBlockLevels for non-mainnet networks to 250 (#1952)
* Make MaxBlockLevel a DAG params instead of a constant.

* Change the testnet network name to 9.

* Fix TestBlockWindow.

* Set MaxBlockLevels for non-mainnet networks to 250.

* Revert "Fix TestBlockWindow."

This reverts commit 30a7892f53e0bb8d0d24435a68f0561a8efab575.

* Fix TestPruning.
2022-02-20 13:43:42 +02:00

113 lines
3.8 KiB
Go

package pow
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
"github.com/kaspanet/kaspad/domain/consensus/utils/serialization"
"github.com/kaspanet/kaspad/util/difficulty"
"github.com/pkg/errors"
"math/big"
)
// State is an intermediate data structure with pre-computed values to speed up mining.
type State struct {
mat matrix
Timestamp int64
Nonce uint64
Target big.Int
prePowHash externalapi.DomainHash
}
// NewState creates a new state with pre-computed values to speed up mining
// It takes the target from the Bits field
func NewState(header externalapi.MutableBlockHeader) *State {
target := difficulty.CompactToBig(header.Bits())
// Zero out the time and nonce.
timestamp, nonce := header.TimeInMilliseconds(), header.Nonce()
header.SetTimeInMilliseconds(0)
header.SetNonce(0)
prePowHash := consensushashing.HeaderHash(header)
header.SetTimeInMilliseconds(timestamp)
header.SetNonce(nonce)
return &State{
Target: *target,
prePowHash: *prePowHash,
mat: *generateMatrix(prePowHash),
Timestamp: timestamp,
Nonce: nonce,
}
}
// CalculateProofOfWorkValue hashes the internal header and returns its big.Int value
func (state *State) CalculateProofOfWorkValue() *big.Int {
// PRE_POW_HASH || TIME || 32 zero byte padding || NONCE
writer := hashes.NewPoWHashWriter()
writer.InfallibleWrite(state.prePowHash.ByteSlice())
err := serialization.WriteElement(writer, state.Timestamp)
if err != nil {
panic(errors.Wrap(err, "this should never happen. Hash digest should never return an error"))
}
zeroes := [32]byte{}
writer.InfallibleWrite(zeroes[:])
err = serialization.WriteElement(writer, state.Nonce)
if err != nil {
panic(errors.Wrap(err, "this should never happen. Hash digest should never return an error"))
}
powHash := writer.Finalize()
heavyHash := state.mat.HeavyHash(powHash)
return toBig(heavyHash)
}
// IncrementNonce the nonce in State by 1
func (state *State) IncrementNonce() {
state.Nonce++
}
// CheckProofOfWork check's if the block has a valid PoW according to the provided target
// it does not check if the difficulty itself is valid or less than the maximum for the appropriate network
func (state *State) CheckProofOfWork() bool {
// The block pow must be less than the claimed target
powNum := state.CalculateProofOfWorkValue()
// The block hash must be less or equal than the claimed target.
return powNum.Cmp(&state.Target) <= 0
}
// CheckProofOfWorkByBits check's if the block has a valid PoW according to its Bits field
// it does not check if the difficulty itself is valid or less than the maximum for the appropriate network
func CheckProofOfWorkByBits(header externalapi.MutableBlockHeader) bool {
return NewState(header).CheckProofOfWork()
}
// ToBig converts a externalapi.DomainHash into a big.Int treated as a little endian string.
func toBig(hash *externalapi.DomainHash) *big.Int {
// We treat the Hash as little-endian for PoW purposes, but the big package wants the bytes in big-endian, so reverse them.
buf := hash.ByteSlice()
blen := len(buf)
for i := 0; i < blen/2; i++ {
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
}
return new(big.Int).SetBytes(buf)
}
// BlockLevel returns the block level of the given header.
func BlockLevel(header externalapi.BlockHeader, maxBlockLevel int) 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 maxBlockLevel
}
proofOfWorkValue := NewState(header.ToMutable()).CalculateProofOfWorkValue()
level := maxBlockLevel - proofOfWorkValue.BitLen()
// If the block has a level lower than genesis make it zero.
if level < 0 {
level = 0
}
return level
}