diff --git a/electrum/blockchain.py b/electrum/blockchain.py index 08fa0805..6143f98a 100644 --- a/electrum/blockchain.py +++ b/electrum/blockchain.py @@ -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: diff --git a/electrum/paymentrequest_pb2.py b/electrum/paymentrequest_pb2.py index 20cb32c2..f596128c 100644 --- a/electrum/paymentrequest_pb2.py +++ b/electrum/paymentrequest_pb2.py @@ -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=[ ], diff --git a/run_electrum b/run_electrum index 6558d960..a254fabb 100755 --- a/run_electrum +++ b/run_electrum @@ -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',