Exemplo n.º 1
0
 def test_example_1(self):
     node = SimpleNode('btc.programmingblockchain.com', testnet=False)
     node.handshake()
     last_block_hash = GENESIS_BLOCK_HASH
     first_epoch_block = None
     expected_bits = None
     count = 1
     want = ('ffff001d', 'ffff001d', 'ffff001d', 'ffff001d', 'ffff001d',
             'ffff001d', 'ffff001d', 'ffff001d', 'ffff001d', 'ffff001d',
             'ffff001d', 'ffff001d', 'ffff001d', 'ffff001d', 'ffff001d',
             'ffff001d', '6ad8001d', '28c4001d')
     for bits in want:
         getheaders = GetHeadersMessage(start_block=last_block_hash)
         node.send(getheaders.command, getheaders.serialize())
         headers_envelope = node.wait_for_commands([b'headers'])
         headers_message = HeadersMessage.parse(headers_envelope.stream())
         for block in headers_message.blocks:
             if not block.check_pow():
                 raise RuntimeError(
                     'bad proof of work at block {}'.format(count))
             if last_block_hash != GENESIS_BLOCK_HASH and block.prev_block != last_block_hash:
                 raise RuntimeError(
                     'discontinuous block at {}'.format(count))
             if expected_bits and block.bits != expected_bits:
                 raise RuntimeError('bad bits at block {} {} vs {}'.format(
                     count, block.bits.hex(), expected_bits.hex()))
             if first_epoch_block and count % 2016 == 2015:
                 expected_bits = calculate_new_bits(
                     expected_bits,
                     block.timestamp - first_epoch_block.timestamp)
                 self.assertEqual(expected_bits.hex(), bits)
             elif first_epoch_block is None:
                 expected_bits = block.bits
             if count % 2016 == 0 or not first_epoch_block:
                 first_epoch_block = block
             count += 1
             last_block_hash = block.hash()
Exemplo n.º 2
0
    def validate(self, prev_block, prev_block_height, first_block_difficulty_period, total_fees=None, logger_handler=None):

        if prev_block.hash() != self.prev_block:
            if logger_handler is not None:
                logger_handler("[*] FAILED VALIDATION ==> Block previous hash does not match previous block's hash in database")
            return False
        if logger_handler is not None:
            logger_handler("[*] PASSED VALIDATION ==> Block previous hash matches previous block's hash in database: {}".format(self.prev_block.hex()))

        expected_block_reward = self.expected_block_reward(prev_block_height + 1)

        if logger_handler is not None:
            logger_handler("[*] Expected block reward in BTC: {}".format(expected_block_reward / 100000000))

        if total_fees is not None:
            actual_block_reward = self.block_reward(total_fees)
        else:
            actual_block_reward = None

        if actual_block_reward is not None:
            logger_handler("[*] Actual block reward in BTC: {}".format(actual_block_reward / 100000000))
            if actual_block_reward > expected_block_reward:
                if logger_handler is not None:
                    logger_handler("[*] FAILED VALIDATION ==> Actual block reward should not be greater than the expected block reward")
                return False
            if logger_handler is not None:
                logger_handler("[*] PASSED VALIDATION ==> Actual block reward less than or equal to the expected block reward")

        if (prev_block_height + 1) % BLOCK_DIFFICULTY_ADJUSTMENT_PERIOD == 0:
            if logger_handler is not None:
                logger_handler("[*] This block started with a new block difficulty period")
            time_differential = prev_block.timestamp - first_block_difficulty_period.timestamp
            new_bits = calculate_new_bits(first_block_difficulty_period.bits, time_differential)
            if new_bits != self.bits:
                if logger_handler is not None:
                    logger_handler("[*] FAILED VALIDATION ==> Expected dfficulty bits: {} Block difficulty bits: {}".format(new_bits.hex(), self.bits.hex()))
                return False
        else:
            if logger_handler is not None:
                logger_handler("[*] This block does not start with a new block difficulty period")
            if self.bits != first_block_difficulty_period.bits:
                if logger_handler is not None:
                    logger_handler("[*] FAILED VALIDATION ==> Expected dfficulty bits: {} Block difficulty bits: {}".format(first_block_difficulty_period.bits.hex(), self.bits.hex()))
                return False
        if logger_handler is not None:
            logger_handler("[*] PASSED VALIDATION ==> Block difficulty bits are valid")

        if not self.check_pow():
            if logger_handler is not None:
                logger_handler("[*] FAILED VALIDATION ==> Block proof of work is not valid")
            return False
        if logger_handler is not None:
            logger_handler("[*] PASSED VALIDATION ==> Block proof of work is valid")

        if not self.validate_merkle_root():
            if logger_handler is not None:
                logger_handler("[*] FAILED VALIDATION ==> Block merkle root is not valid")
            return False
        if logger_handler is not None:
                logger_handler("[*] PASSED VALIDATION ==> Block merkle root is valid")

        return True
Exemplo n.º 3
0
# run(network.GetHeadersMessageTest("test_serialize"))

previous = Block.parse(BytesIO(GENESIS_BLOCK))
first_epoch_timestamp = previous.timestamp
expected_bits = LOWEST_BITS
count = 1
node = SimpleNode('mainnet.programmingbitcoin.com', testnet=False)
node.handshake()
for _ in range(19):
    
    getheaders = GetHeadersMessage(start_block=previous.hash())
    node.send(getheaders)
    headers = node.wait_for(HeadersMessage)
    for header in headers.blocks:
        if not header.check_pow():
            raise RuntimeError('bad PoW at block {}'.format(count))
        if header.prev_block != previous.hash():
            raise RuntimeError('discontinuous block at {}'.format(count))
        
        if count % 2016 == 0:
            time_diff = previous.timestamp - first_epoch_timestamp
            expected_bits = calculate_new_bits(previous.bits, time_diff)
            print("Range: {}.  Expected Bits: {}.".format(_, expected_bits.hex()))
            first_epoch_timestamp = header.timestamp
        
        if header.bits != expected_bits:
            raise RuntimeError('bad bits at block {}'.format(count))

        previous = header
        count += 1
Exemplo n.º 4
0
 def update(self, stop_height=None):
     if stop_height is None:
         stop_height = self.node.latest_block
     start_height = self.store_height()
     previous = self.headers[-1]
     # find the first epoch timestamp
     epoch_first_block = self.headers[(len(self.headers) // 2016) * 2016]
     epoch_first_timestamp = epoch_first_block.timestamp
     epoch_bits = epoch_first_block.bits
     # download blocks we don't have
     current_height = start_height + 1
     while current_height <= stop_height:
         LOGGER.info('downloading headers {} to {}'.format(current_height, current_height+1999))
         # ask for headers
         getheaders = GetHeadersMessage(start_block=previous.hash())
         self.node.send(getheaders)
         headers = self.node.wait_for(HeadersMessage)
         # validate headers
         for header in headers.blocks:
             if not header.check_pow():
                 raise RuntimeError('{}: PoW is invalid'.format(current_height))
             if header.prev_block != previous.hash():
                 raise RuntimeError('{}: not sequential'.format(current_height))
             if current_height % 2016 == 0:
                 # difficulty adjustment
                 time_diff = previous.timestamp - epoch_first_timestamp
                 epoch_bits = calculate_new_bits(previous.bits, time_diff)
                 expected_bits = epoch_bits
                 epoch_first_timestamp = header.timestamp
             elif self.testnet and header.timestamp > previous.timestamp + 20 * 60:
                 expected_bits = LOWEST_BITS
             else:
                 expected_bits = epoch_bits
             if header.bits != expected_bits:
                 raise RuntimeError('{}: bad bits {} vs {}'.format(
                     current_height, header.bits.hex(), expected_bits.hex()))
             previous = header
             current_height += 1
             self.headers.append(header)
             self.headers_lookup[header.hash()] = header
             if current_height > stop_height:
                 break
     LOGGER.info('downloaded headers to {} inclusive'.format(stop_height))
     # download compact filter checkpoints
     getcfcheckpoint = GetCFCheckPointMessage(stop_hash=self.headers[-1].hash())
     self.node.send(getcfcheckpoint)
     cfcheckpoint = self.node.wait_for(CFCheckPointMessage)
     # download all cfheaders
     prev_filter_header = b'\x00' * 32
     cfcheckpoint.filter_headers.append(None)
     for i, cpfilter_header in enumerate(cfcheckpoint.filter_headers):
         if i == 0:
             start = 0
         else:
             start = i * 1000 + 1
         end = (i+1) * 1000
         if end <= start_height:
             prev_filter_header = cpfilter_header
             continue
         if end > stop_height:
             end = stop_height
             stop_hash = self.headers[-1].hash()
         else:
             stop_hash = self.header_by_height(end).hash()
         LOGGER.info('getting filters range {} to {}'.format(start, end))
         getcfheaders = GetCFHeadersMessage(
             start_height=start, stop_hash=stop_hash)
         self.node.send(getcfheaders)
         cfheaders = self.node.wait_for(CFHeadersMessage)
         if prev_filter_header != cfheaders.previous_filter_header:
             raise RuntimeError('Non-sequential CF headers')
         # validate all cfheaders
         for j, filter_hash in enumerate(cfheaders.filter_hashes):
             header = self.header_by_height(start + j)
             filter_header = hash256(filter_hash[::-1] + prev_filter_header[::-1])[::-1]
             prev_filter_header = filter_header
             header.cfhash = filter_hash
             header.cfheader = filter_header
         if cpfilter_header is not None and cpfilter_header != prev_filter_header:
             raise RuntimeError('CF header not what we expected')
Exemplo n.º 5
0
if time_differential < TWO_WEEKS // 4:
    time_differential = TWO_WEEKS // 4
new_target = last_block.target() * time_differential // TWO_WEEKS
new_bits = helper.target_to_bits(new_target)
print(new_bits.hex())

print("Doing the calculate_new_bits way:  *******************")
block1_hex = '000000203471101bbda3fe307664b3283a9ef0e97d9a38a7eacd8800000000000000000010c8aba8479bbaa5e0848152fd3c2289ca50e1c3e58c9a4faaafbdf5803c5448ddb845597e8b0118e43a81d3'
block2_hex = '02000020f1472d9db4b563c35f97c428ac903f23b7fc055d1cfc26000000000000000000b3f449fcbe1bc4cfbcb8283a0d2c037f961a3fdf2b8bedc144973735eea707e1264258597e8b0118e5f00474'

block1_stream = BytesIO(bytes.fromhex(block1_hex))
block2_stream = BytesIO(bytes.fromhex(block2_hex))
block1 = Block.parse(block1_stream)
block2 = Block.parse(block2_stream)
time_differential = block2.time_differential(block1)
new_bits = helper.calculate_new_bits(block1.bits, time_differential)
print(new_bits.hex())

# parse both blocks
# get the time differential
# if the differential > 8 weeks, set to 8 weeks
# if the differential < 1/2 week, set to 1/2 week
# new target is last target * differential / 2 weeks
# convert new target to bits
# print the new bits hex

# exponent = 4
# coefficient = 1
# target = coefficient * 256**(exponent-3)
# print("target: ", target)
# bits = helper.target_to_bits(target)