Changes for target calculation

All the changes required for target calculation, verification and saving parts of chunks for FLO
@akhil2015

Co-authored-by: Akhil Bharti <akhil2015@users.noreply.github.com>
This commit is contained in:
Vivek Teegalapally 2018-08-05 23:18:30 +05:30
parent ca30705c69
commit 85bae50e62
2 changed files with 194 additions and 36 deletions

View File

@ -28,7 +28,14 @@ from .bitcoin import Hash, hash_encode, int_to_hex, rev_hex
from . import constants from . import constants
from .util import bfh, bh2u from .util import bfh, bh2u
MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 try:
import scrypt
getPoWHash = lambda x: scrypt.hash(x, x, N=1024, r=1, p=1, buflen=32)
except ImportError:
util.print_msg("Warning: package scrypt not available; synchronization could be very slow")
from .scrypt import scrypt_1024_1_1_80 as getPoWHash
MAX_TARGET = 0x00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
class MissingHeader(Exception): class MissingHeader(Exception):
@ -69,6 +76,8 @@ def hash_header(header):
header['prev_block_hash'] = '00'*32 header['prev_block_hash'] = '00'*32
return hash_encode(Hash(bfh(serialize_header(header)))) return hash_encode(Hash(bfh(serialize_header(header))))
def pow_hash_header(header):
return hash_encode(getPoWHash(bfh(serialize_header(header))))
blockchains = {} blockchains = {}
@ -168,25 +177,36 @@ class Blockchain(util.PrintError):
def verify_header(self, header, prev_hash, target): def verify_header(self, header, prev_hash, target):
_hash = hash_header(header) _hash = hash_header(header)
#if prev_hash != header.get('prev_block_hash'): _powhash = pow_hash_header(header)
# raise Exception("prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash'))) if prev_hash != header.get('prev_block_hash'):
#if constants.net.TESTNET: raise Exception("prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash')))
# return if constants.net.TESTNET:
bits = self.target_to_bits(target) return
#if bits != header.get('bits'): print("I'm inside verify_header")
# raise Exception("bits mismatch: %s vs %s" % (bits, header.get('bits'))) #bits = self.target_to_bits(target)
#if int('0x' + _hash, 16) > target: bits = target
# raise Exception("insufficient proof of work: %s vs target %s" % (int('0x' + _hash, 16), target)) if bits != header.get('bits'):
raise Exception("bits mismatch: %s vs %s" % (bits, header.get('bits')))
block_hash = int('0x' + _hash, 16)
target_val = self.bits_to_target(bits)
if int('0x' + _powhash, 16) > target_val:
raise Exception("insufficient proof of work: %s vs target %s" % (int('0x' + _hash, 16), target_val))
print("I passed verify_header(). Calc target values have been matched")
def verify_chunk(self, index, data): def verify_chunk(self, index, data):
num = len(data) // 80 num = len(data) // 80
prev_hash = self.get_hash(index * 2016 - 1) current_header = (index * 2016)
target = self.get_target(index-1) # last = (index * 2016 + 2015)
prev_hash = self.get_hash(current_header - 1)
for i in range(num): for i in range(num):
target = self.get_target(current_header - 1)
raw_header = data[i*80:(i+1) * 80] raw_header = data[i*80:(i+1) * 80]
header = deserialize_header(raw_header, index*2016 + i) header = deserialize_header(raw_header, current_header)
print(i)
self.verify_header(header, prev_hash, target) self.verify_header(header, prev_hash, target)
self.save_chunk_part(header)
prev_hash = hash_header(header) prev_hash = hash_header(header)
current_header = current_header + 1
def path(self): def path(self):
d = util.get_headers_dir(self.config) d = util.get_headers_dir(self.config)
@ -202,15 +222,15 @@ class Blockchain(util.PrintError):
main_chain.save_chunk(index, chunk) main_chain.save_chunk(index, chunk)
return return
delta_height = (index * 2016 - self.forkpoint) #delta_height = (index * 2016 - self.forkpoint)
delta_bytes = delta_height * 80 delta_bytes = delta_height * 80
# if this chunk contains our forkpoint, only save the part after forkpoint # if this chunk contains our forkpoint, only save the part after forkpoint
# (the part before is the responsibility of the parent) # (the part before is the responsibility of the parent)
if delta_bytes < 0: #if delta_bytes < 0:
chunk = chunk[-delta_bytes:] # chunk = chunk[-delta_bytes:]
delta_bytes = 0 # delta_bytes = 0
truncate = not chunk_within_checkpoint_region #truncate = not chunk_within_checkpoint_region
self.write(chunk, delta_bytes, truncate) #self.write(chunk, delta_bytes, truncate)
self.swap_with_parent() self.swap_with_parent()
@with_lock @with_lock
@ -319,29 +339,58 @@ class Blockchain(util.PrintError):
# compute target from chunk x, used in chunk x+1 # compute target from chunk x, used in chunk x+1
if constants.net.TESTNET: if constants.net.TESTNET:
return 0 return 0
if index == -1: # The range is first 90 blocks because FLO's block time was 90 blocks when it started
return MAX_TARGET if -1 <= index <= 88:
return 0x1e0ffff0
if index < len(self.checkpoints): if index < len(self.checkpoints):
h, t = self.checkpoints[index] h, t = self.checkpoints[index]
return t return t
# new target # new target
first = self.read_header(index * 2016) headerLast = self.read_header(index)
last = self.read_header(index * 2016 + 2015) height = headerLast["block_height"]
if not first or not last: # check if the height passes is in range for retargeting
raise MissingHeader() if (height + 1) % self.DifficultyAdjustmentInterval(height + 1) != 0:
bits = last.get('bits') return int(headerLast["bits"])
target = self.bits_to_target(bits) averagingInterval = self.AveragingInterval(height + 1)
nActualTimespan = last.get('timestamp') - first.get('timestamp') blockstogoback = averagingInterval - 1
nTargetTimespan = 14 * 24 * 60 * 60 # print("Blocks to go back = " + str(blockstogoback))
nActualTimespan = max(nActualTimespan, nTargetTimespan // 4) if (height + 1) != averagingInterval:
nActualTimespan = min(nActualTimespan, nTargetTimespan * 4) blockstogoback = averagingInterval
new_target = min(MAX_TARGET, (target * nActualTimespan) // nTargetTimespan) firstHeight = height - blockstogoback
return new_target headerFirst = self.read_header(int(firstHeight))
firstBlockTime = headerFirst["timestamp"]
nMinActualTimespan = int(self.MinActualTimespan(int(headerLast["block_height"]) + 1))
nMaxActualTimespan = int(self.MaxActualTimespan(int(headerLast["block_height"]) + 1))
# Limit adjustment step
nActualTimespan = headerLast["timestamp"] - firstBlockTime
if nActualTimespan < nMinActualTimespan:
nActualTimespan = nMinActualTimespan
if nActualTimespan > nMaxActualTimespan:
nActualTimespan = nMaxActualTimespan
# Retarget
bnNewBits = int(headerLast["bits"])
bnNew = self.bits_to_target(bnNewBits)
bnOld = bnNew
# FLO: intermediate uint256 can overflow by 1 bit
# const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
fShift = bnNew > MAX_TARGET - 1
if (fShift):
bnNew = bnNew >> 1
bnNew = bnNew * nActualTimespan
bnNew = bnNew / self.TargetTimespan(headerLast["block_height"] + 1)
if fShift:
bnNew = bnNew << 1
if bnNew > MAX_TARGET:
bnNew = MAX_TARGET
bnNew = self.target_to_bits(int(bnNew))
return bnNew
def bits_to_target(self, bits): def bits_to_target(self, bits):
bitsN = (bits >> 24) & 0xff bitsN = (bits >> 24) & 0xff
if not (bitsN >= 0x03 and bitsN <= 0x1d): if not (bitsN >= 0x03 and bitsN <= 0x1e):
raise Exception("First part of bits should be in [0x03, 0x1d]") raise BaseException("First part of bits should be in [0x03, 0x1e]")
bitsBase = bits & 0xffffff bitsBase = bits & 0xffffff
if not (bitsBase >= 0x8000 and bitsBase <= 0x7fffff): if not (bitsBase >= 0x8000 and bitsBase <= 0x7fffff):
raise Exception("Second part of bits should be in [0x8000, 0x7fffff]") raise Exception("Second part of bits should be in [0x8000, 0x7fffff]")
@ -373,7 +422,7 @@ class Blockchain(util.PrintError):
if prev_hash != header.get('prev_block_hash'): if prev_hash != header.get('prev_block_hash'):
return False return False
try: try:
target = self.get_target(height // 2016 - 1) target = self.get_target(height - 1)
except MissingHeader: except MissingHeader:
return False return False
try: try:
@ -402,3 +451,67 @@ class Blockchain(util.PrintError):
target = self.get_target(index) target = self.get_target(index)
cp.append((h, target)) cp.append((h, target))
return cp return cp
def AveragingInterval(self, height):
# V1
if height < constants.net.nHeight_Difficulty_Version2:
return constants.net.nAveragingInterval_Version1
# V2
elif height < constants.net.nHeight_Difficulty_Version3:
return constants.net.nAveragingInterval_Version2
# V3
else:
return constants.net.nAveragingInterval_Version3
def MinActualTimespan(self, height):
averagingTargetTimespan = self.AveragingInterval(height) * constants.net.nPowTargetSpacing
# V1
if height < constants.net.nHeight_Difficulty_Version2:
return int(averagingTargetTimespan * (100 - constants.net.nMaxAdjustUp_Version1) / 100)
# V2
elif height < constants.net.nHeight_Difficulty_Version3:
return int(averagingTargetTimespan * (100 - constants.net.nMaxAdjustUp_Version2) / 100)
# V3
else:
return int(averagingTargetTimespan * (100 - constants.net.nMaxAdjustUp_Version3) / 100)
def MaxActualTimespan(self, height):
averagingTargetTimespan = self.AveragingInterval(height) * constants.net.nPowTargetSpacing
# V1
if height < constants.net.nHeight_Difficulty_Version2:
return int(averagingTargetTimespan * (100 + constants.net.nMaxAdjustDown_Version1) / 100)
# V2
elif height < constants.net.nHeight_Difficulty_Version3:
return int(averagingTargetTimespan * (100 + constants.net.nMaxAdjustDown_Version2) / 100)
# V3
else:
return int(averagingTargetTimespan * (100 + constants.net.nMaxAdjustDown_Version3) / 100)
def TargetTimespan(self, height):
# V1
if height < constants.net.nHeight_Difficulty_Version2:
return constants.net.nTargetTimespan_Version1
# V2
if height < constants.net.nHeight_Difficulty_Version3:
return constants.net.nAveragingInterval_Version2 * constants.net.nPowTargetSpacing
# V3
return constants.net.nAveragingInterval_Version3 * constants.net.nPowTargetSpacing
def DifficultyAdjustmentInterval(self, height):
# V1
if height < constants.net.nHeight_Difficulty_Version2:
return constants.net.nInterval_Version1
# V2
if height < constants.net.nHeight_Difficulty_Version3:
return constants.net.nInterval_Version2
# V3
return constants.net.nInterval_Version3
def save_chunk_part(self, header):
filename = self.path()
delta = header.get('block_height') - self.checkpoint
data = bfh(serialize_header(header))
# assert delta == self.size()
assert len(data) == 80
self.write(data, delta * 80)
# self.swap_with_parent()

View File

@ -64,6 +64,31 @@ class BitcoinMainnet:
'p2wsh': 0x02aa7ed3, # Zpub 'p2wsh': 0x02aa7ed3, # Zpub
} }
BIP44_COIN_TYPE = 2 BIP44_COIN_TYPE = 2
# FLO Network constants
fPowAllowMinDifficultyBlocks = False
fPowNoRetargeting = False
nRuleChangeActivationThreshold = 6048 # 75% of 8064
nMinerConfirmationWindow = 8064
# Difficulty adjustments
nPowTargetSpacing = 40 # 40s block time
# V1
nTargetTimespan_Version1 = 60 * 60
nInterval_Version1 = nTargetTimespan_Version1 / nPowTargetSpacing
nMaxAdjustUp_Version1 = 75
nMaxAdjustDown_Version1 = 300
nAveragingInterval_Version1 = nInterval_Version1
# V2
nHeight_Difficulty_Version2 = 208440
nInterval_Version2 = 15
nMaxAdjustDown_Version2 = 300
nMaxAdjustUp_Version2 = 75
nAveragingInterval_Version2 = nInterval_Version2
# V3
nHeight_Difficulty_Version3 = 426000
nInterval_Version3 = 1
nMaxAdjustDown_Version3 = 3
nMaxAdjustUp_Version3 = 2
nAveragingInterval_Version3 = 6
class BitcoinTestnet: class BitcoinTestnet:
@ -93,6 +118,26 @@ class BitcoinTestnet:
'p2wsh': 0x02575483, # Vpub 'p2wsh': 0x02575483, # Vpub
} }
BIP44_COIN_TYPE = 1 BIP44_COIN_TYPE = 1
#Difficulty adjustments
nPowTargetSpacing = 40 # 40 block time
# V1
nTargetTimespan_Version1 = 60 * 60
nInterval_Version1 = nTargetTimespan_Version1 / nPowTargetSpacing;
nMaxAdjustUp_Version1 = 75
nMaxAdjustDown_Version1 = 300
nAveragingInterval_Version1 = nInterval_Version1
# V2
nHeight_Difficulty_Version2 = 50000
nInterval_Version2 = 15
nMaxAdjustDown_Version2 = 300
nMaxAdjustUp_Version2 = 75
nAveragingInterval_Version2 = nInterval_Version2
# V3
nHeight_Difficulty_Version3 = 60000
nInterval_Version3 = 1
nMaxAdjustDown_Version3 = 3
nMaxAdjustUp_Version3 = 2
nAveragingInterval_Version3 = 6
class BitcoinRegtest(BitcoinTestnet): class BitcoinRegtest(BitcoinTestnet):