You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
4.9 KiB

// Copyright 2017-2018 DERO Project. All rights reserved.
// Use of this source code in any form is governed by RESEARCH license.
// license can be found in the LICENSE file.
// GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package difficulty
import "fmt"
import "math/big"
import "github.com/deroproject/derosuite/config"
import "github.com/deroproject/derosuite/crypto"
var (
// bigZero is 0 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigZero = big.NewInt(0)
// bigOne is 1 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigOne = big.NewInt(1)
// oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid
// the overhead of creating it multiple times.
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
// enabling this will simulation mode with hard coded difficulty set to 1
// the variable is knowingly not exported, so no one can tinker with it
simulation = false // simulation mode is disabled
)
// HashToBig converts a PoW has into a big.Int that can be used to
// perform math comparisons.
func HashToBig(buf crypto.Hash) *big.Int {
// A Hash is in little-endian, but the big package wants the bytes in
// big-endian, so reverse them.
blen := len(buf) // its hardcoded 32 bytes, so why do len but lets do it
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[:])
}
// this function calculates the difficulty in big num form
func ConvertDifficultyToBig(difficultyi uint64) *big.Int {
if difficultyi == 0 {
panic("difficulty can never be zero")
}
// (1 << 256) / (difficultyNum )
difficulty := new(big.Int).SetUint64(difficultyi)
denominator := new(big.Int).Add(difficulty, bigZero) // above 2 lines can be merged
return new(big.Int).Div(oneLsh256, denominator)
}
// this function check whether the pow hash meets difficulty criteria
func CheckPowHash(pow_hash crypto.Hash, difficulty uint64) bool {
big_difficulty := ConvertDifficultyToBig(difficulty)
big_pow_hash := HashToBig(pow_hash)
if big_pow_hash.Cmp(big_difficulty) <= 0 { // if work_pow is less than difficulty
return true
}
return false
}
/* this function calculates difficulty on the basis of previous timestamps and cumulative_difficulty */
func Next_Difficulty(timestamps []uint64, cumulative_difficulty []uint64, target_seconds uint64) (difficulty uint64) {
difficulty = 1 // default difficulty is 1 // for genesis block
if simulation == true { // simulation mode has difficulty set to 1
return 1
}
if len(timestamps) > config.DIFFICULTY_BLOCKS_COUNT_V2 {
panic("More timestamps provided than required")
}
if len(timestamps) != len(cumulative_difficulty) {
panic("Number of timestamps != Number of cumulative_difficulty")
}
if len(timestamps) <= 1 {
return difficulty // return 1
}
length := uint64(len(timestamps))
weighted_timespans := uint64(0)
for i := uint64(1); i < length; i++ {
timespan := uint64(0)
if timestamps[i-1] >= timestamps[i] {
timespan = 1
} else {
timespan = timestamps[i] - timestamps[i-1]
}
if timespan > (10 * target_seconds) {
timespan = 10 * target_seconds
}
weighted_timespans += i * timespan
}
minimum_timespan := (target_seconds * length) / 2
if weighted_timespans < minimum_timespan { // fix startup weirdness
weighted_timespans = minimum_timespan
}
total_work := cumulative_difficulty[length-1] - cumulative_difficulty[0]
// convert input for 128 bit multiply
var big_total_work, big_target, big_result big.Int
big_total_work.SetUint64(total_work)
target := (((length + 1) / 2) * target_seconds * 3) / 2
big_target.SetUint64(target)
big_result.Mul(&big_total_work, &big_target)
if big_result.IsUint64() {
if big_result.Uint64() > 0x000fffffffffffff { // this will give us atleast 1 year to fix the difficulty algorithm
fmt.Printf("Total work per target_time will soon cross 2^64, please fix the difficulty algorithm\n")
}
difficulty = big_result.Uint64() / weighted_timespans
} else {
panic("Total work per target_time crossing 2^64 , please fix the above")
}
return difficulty
}