Target calculation & chunk verification
This commit adds the code for FLO chunk verification. FLO chunk verification is different from Bitcoin chunk verification as the blockchain has 3 different target recalculation times. ie. 90 blocks at height 0->208440, 15 blocks at height 208440->426000 & 1 block further down Changes have also been maded such that chunk doesnt need to be saved in parts
This commit is contained in:
parent
d418976f5e
commit
085cd9919e
@ -31,7 +31,7 @@ from .util import bfh, bh2u
|
||||
|
||||
|
||||
HEADER_SIZE = 80 # bytes
|
||||
MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
|
||||
MAX_TARGET = 0x00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
|
||||
|
||||
class MissingHeader(Exception):
|
||||
@ -176,19 +176,26 @@ class Blockchain(util.PrintError):
|
||||
|
||||
def verify_chunk(self, index: int, data: bytes) -> None:
|
||||
num = len(data) // HEADER_SIZE
|
||||
start_height = index * 2016
|
||||
current_header = start_height = index * 2016
|
||||
prev_hash = self.get_hash(start_height - 1)
|
||||
target = self.get_target(index-1)
|
||||
headerLast = None
|
||||
headerFirst = None
|
||||
for i in range(num):
|
||||
target = self.get_target(current_header - 1, headerLast, headerFirst)
|
||||
height = start_height + i
|
||||
try:
|
||||
expected_header_hash = self.get_hash(height)
|
||||
except MissingHeader:
|
||||
expected_header_hash = None
|
||||
raw_header = data[i*HEADER_SIZE : (i+1)*HEADER_SIZE]
|
||||
header = deserialize_header(raw_header, index*2016 + i)
|
||||
header = deserialize_header(raw_header, current_header)
|
||||
self.verify_header(header, prev_hash, target, expected_header_hash)
|
||||
prev_hash = hash_header(header)
|
||||
headerLast = header
|
||||
difficulty_interval = self.DifficultyAdjustmentInterval(height)
|
||||
if height % difficulty_interval == 0:
|
||||
headerFirst = header
|
||||
current_header = current_header + 1
|
||||
|
||||
def path(self):
|
||||
d = util.get_headers_dir(self.config)
|
||||
@ -330,33 +337,65 @@ class Blockchain(util.PrintError):
|
||||
raise MissingHeader(height)
|
||||
return hash_header(header)
|
||||
|
||||
def get_target(self, index: int) -> int:
|
||||
def get_target(self, index: int, headerLast: dict=None, headerFirst: dict=None) -> int:
|
||||
# compute target from chunk x, used in chunk x+1
|
||||
if constants.net.TESTNET:
|
||||
return 0
|
||||
if index == -1:
|
||||
return MAX_TARGET
|
||||
# The range is first 90 blocks because FLO's block time was 90 blocks when it started
|
||||
if -1 <= index <= 88:
|
||||
return 0x1e0ffff0
|
||||
if index < len(self.checkpoints):
|
||||
h, t = self.checkpoints[index]
|
||||
return t
|
||||
# new target
|
||||
first = self.read_header(index * 2016)
|
||||
last = self.read_header(index * 2016 + 2015)
|
||||
if not first or not last:
|
||||
raise MissingHeader()
|
||||
bits = last.get('bits')
|
||||
target = self.bits_to_target(bits)
|
||||
nActualTimespan = last.get('timestamp') - first.get('timestamp')
|
||||
nTargetTimespan = 14 * 24 * 60 * 60
|
||||
nActualTimespan = max(nActualTimespan, nTargetTimespan // 4)
|
||||
nActualTimespan = min(nActualTimespan, nTargetTimespan * 4)
|
||||
new_target = min(MAX_TARGET, (target * nActualTimespan) // nTargetTimespan)
|
||||
return new_target
|
||||
if headerLast is None:
|
||||
headerLast = self.read_header(index)
|
||||
height = headerLast["block_height"]
|
||||
# check if the height passes is in range for retargeting
|
||||
if (height + 1) % self.DifficultyAdjustmentInterval(height + 1) != 0:
|
||||
return int(headerLast["bits"])
|
||||
if headerFirst is None:
|
||||
averagingInterval = self.AveragingInterval(height + 1)
|
||||
blockstogoback = averagingInterval - 1
|
||||
# print("Blocks to go back = " + str(blockstogoback))
|
||||
if (height + 1) != averagingInterval:
|
||||
blockstogoback = averagingInterval
|
||||
firstHeight = height - blockstogoback
|
||||
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: int) -> int:
|
||||
bitsN = (bits >> 24) & 0xff
|
||||
if not (bitsN >= 0x03 and bitsN <= 0x1d):
|
||||
raise Exception("First part of bits should be in [0x03, 0x1d]")
|
||||
if not (bitsN >= 0x03 and bitsN <= 0x1e):
|
||||
raise BaseException("First part of bits should be in [0x03, 0x1e]")
|
||||
bitsBase = bits & 0xffffff
|
||||
if not (bitsBase >= 0x8000 and bitsBase <= 0x7fffff):
|
||||
raise Exception("Second part of bits should be in [0x8000, 0x7fffff]")
|
||||
@ -418,6 +457,61 @@ class Blockchain(util.PrintError):
|
||||
cp.append((h, target))
|
||||
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 check_header(header: dict) -> Optional[Blockchain]:
|
||||
if type(header) is not dict:
|
||||
|
||||
@ -18,7 +18,6 @@ _sym_db = _symbol_database.Default()
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='paymentrequest.proto',
|
||||
package='payments',
|
||||
syntax='proto2',
|
||||
serialized_pb=_b('\n\x14paymentrequest.proto\x12\x08payments\"+\n\x06Output\x12\x11\n\x06\x61mount\x18\x01 \x01(\x04:\x01\x30\x12\x0e\n\x06script\x18\x02 \x02(\x0c\"\xa3\x01\n\x0ePaymentDetails\x12\x15\n\x07network\x18\x01 \x01(\t:\x04main\x12!\n\x07outputs\x18\x02 \x03(\x0b\x32\x10.payments.Output\x12\x0c\n\x04time\x18\x03 \x02(\x04\x12\x0f\n\x07\x65xpires\x18\x04 \x01(\x04\x12\x0c\n\x04memo\x18\x05 \x01(\t\x12\x13\n\x0bpayment_url\x18\x06 \x01(\t\x12\x15\n\rmerchant_data\x18\x07 \x01(\x0c\"\x95\x01\n\x0ePaymentRequest\x12\"\n\x17payment_details_version\x18\x01 \x01(\r:\x01\x31\x12\x16\n\x08pki_type\x18\x02 \x01(\t:\x04none\x12\x10\n\x08pki_data\x18\x03 \x01(\x0c\x12\"\n\x1aserialized_payment_details\x18\x04 \x02(\x0c\x12\x11\n\tsignature\x18\x05 \x01(\x0c\"\'\n\x10X509Certificates\x12\x13\n\x0b\x63\x65rtificate\x18\x01 \x03(\x0c\"i\n\x07Payment\x12\x15\n\rmerchant_data\x18\x01 \x01(\x0c\x12\x14\n\x0ctransactions\x18\x02 \x03(\x0c\x12#\n\trefund_to\x18\x03 \x03(\x0b\x32\x10.payments.Output\x12\x0c\n\x04memo\x18\x04 \x01(\t\">\n\nPaymentACK\x12\"\n\x07payment\x18\x01 \x02(\x0b\x32\x11.payments.Payment\x12\x0c\n\x04memo\x18\x02 \x01(\tB(\n\x1eorg.bitcoin.protocols.paymentsB\x06Protos')
|
||||
)
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
@ -55,7 +54,6 @@ _OUTPUT = _descriptor.Descriptor(
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto2',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
@ -128,7 +126,6 @@ _PAYMENTDETAILS = _descriptor.Descriptor(
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto2',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
@ -187,7 +184,6 @@ _PAYMENTREQUEST = _descriptor.Descriptor(
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto2',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
@ -218,7 +214,6 @@ _X509CERTIFICATES = _descriptor.Descriptor(
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto2',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
@ -270,7 +265,6 @@ _PAYMENT = _descriptor.Descriptor(
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto2',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
@ -308,7 +302,6 @@ _PAYMENTACK = _descriptor.Descriptor(
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto2',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
|
||||
@ -362,8 +362,7 @@ if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
|
||||
# config is an object passed to the various constructors (wallet, interface, gui)
|
||||
#if is_android:
|
||||
if True:
|
||||
if is_android:
|
||||
config_options = {
|
||||
'verbosity': '',
|
||||
'cmd': 'gui',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user