def test_validate_not_sane_zeroes(self): db = self.MockDatabase() block1 = MultiChainBlock() # Act block1.up = 0 block1.down = 0 result = block1.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Up and down are zero", result[1])
def test_validate_linked_double_pay_fraud(self): db = self.MockDatabase() (block1, _, _, _) = TestBlocks.setup_validate() # Act db.add_block(block1) db.add_block(MultiChainBlock.create(db, block1.link_public_key, block1)) block2 = MultiChainBlock.create(db, block1.link_public_key, block1) result = block2.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Double countersign fraud", result[1])
def test_validate_linked_valid(self): db = self.MockDatabase() (block1, block2, _, _) = TestBlocks.setup_validate() db.add_block(block2) # Act db.add_block(MultiChainBlock.create(db, block1.link_public_key, block1)) result = block1.validate(db) self.assertEqual(result[0], ValidationResult.valid)
def test_create_genesis(self): key = ECCrypto().generate_key(u"curve25519") db = self.MockDatabase() block = MultiChainBlock.create(db, key.pub().key_to_bin(), link=None) self.assertEqual(block.previous_hash, GENESIS_HASH) self.assertEqual(block.sequence_number, GENESIS_SEQ) self.assertEqual(block.public_key, key.pub().key_to_bin()) self.assertEqual(block.signature, EMPTY_SIG)
def test_create_next(self): db = self.MockDatabase() prev = TestBlock() prev.sequence_number = GENESIS_SEQ db.add_block(prev) block = MultiChainBlock.create(db, prev.public_key, link=None) self.assertEqual(block.previous_hash, prev.hash) self.assertEqual(block.sequence_number, 2) self.assertEqual(block.public_key, prev.public_key)
def test_validate_linked_down(self): db = self.MockDatabase() (block1, block2, _, _) = TestBlocks.setup_validate() db.add_block(block2) # Act db.add_block(MultiChainBlock.create(db, block1.link_public_key, block1)) block1.down -= 5 result = block1.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Down/up mismatch on linked block", result[1])
def test_validate_linked_mismatch(self): db = self.MockDatabase() (block1, block2, _, _) = TestBlocks.setup_validate() # Act # db.add_block(MultiChainBlock.create(db, block1.link_public_key, block1)) db.add_block(block1) block3 = MultiChainBlock.create(db, block2.link_public_key, block1) result = block3.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Public key mismatch on linked block", result[1])
def __init__(self, previous=None): crypto = ECCrypto() up = random.randint(201, 220) down = random.randint(221, 240) other = crypto.generate_key(u"curve25519").pub().key_to_bin() if previous: self.key = previous.key MultiChainBlock.__init__(self, ( up, down, previous.total_up + up, previous.total_down + down, previous.public_key, previous.sequence_number + 1, other, 0, previous.hash, 0, 0)) else: self.key = crypto.generate_key(u"curve25519") MultiChainBlock.__init__(self, ( up, down, random.randint(241, 260), random.randint(261, 280), self.key.pub().key_to_bin(), random.randint(50, 100), other, 0, sha256(str(random.randint(0, 100000))).digest(), 0, 0)) self.sign(self.key)
def test_create_link_genesis(self): key = ECCrypto().generate_key(u"curve25519") db = self.MockDatabase() link = TestBlock() db.add_block(link) block = MultiChainBlock.create(db, key.pub().key_to_bin(), link=link) self.assertEqual(block.previous_hash, GENESIS_HASH) self.assertEqual(block.sequence_number, GENESIS_SEQ) self.assertEqual(block.public_key, key.pub().key_to_bin()) self.assertEqual(block.link_public_key, link.public_key) self.assertEqual(block.link_sequence_number, link.sequence_number)
def _decode_half_block(placeholder, offset, data): """ Decode an incoming half block message. :param placeholder: :param offset: Start of the HalfBlock message in the data. :param data: ByteStream containing the message. :return: (offset, HalfBlockPayload.impl) """ if len(data) < offset + block_pack_size: raise DropPacket("Unable to decode the payload") return offset + block_pack_size, placeholder.meta.payload.implement( MultiChainBlock.unpack(data, offset))
def test_unpack(self): block = MultiChainBlock.unpack('- So long and thanks for all the fish, so sad that it should come to this. - ' 'We tried to warn you all but oh dear! - You may not share our intellect, which ' 'might explain your disrespect, for all the natural wonders that grow around ' 'you. - So long, so long, and thanks for all the fish') self.assertEqual(block.up, 3251690667711950702) self.assertEqual(block.down, 7431046511915463784) self.assertEqual(block.total_up, 7020667011326177138) self.assertEqual(block.total_down, 2333265293611395173) self.assertEqual(block.public_key, ' fish, so sad that it should come to this. - We tried to warn you all but ') self.assertEqual(block.sequence_number, 1869095012) self.assertEqual(block.link_public_key, 'ear! - You may not share our intellect, which might explain your disrespec') self.assertEqual(block.link_sequence_number, 1949048934) self.assertEqual(block.previous_hash, 'or all the natural wonders that ') self.assertEqual(block.signature, 'grow around you. - So long, so long, and thanks for all the fish')
def test_validate_not_sane_negatives(self): db = self.MockDatabase() block1 = MultiChainBlock() # Act block1.up = -10 block1.down = -20 block1.total_down = -10 block1.total_up = -20 result = block1.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Up field is negative", result[1]) self.assertIn("Down field is negative", result[1]) self.assertIn("Total up field is negative", result[1]) self.assertIn("Total down field is negative", result[1])
def test_validate_existing_link_public_key(self): # Arrange db = self.MockDatabase() (block1, block2, block3, _) = TestBlocks.setup_validate() db.add_block(block1) db.add_block(block2) db.add_block(block3) # Act block2 = MultiChainBlock(block2.pack_db_insert()) block2.link_public_key = EMPTY_PK block2.sign(db.get(block2.public_key, block2.sequence_number).key) result = block2.validate(db) # Assert self.assertEqual(result[0], ValidationResult.invalid) self.assertIn('Linked public key is not valid', result[1])
def test_validate_existing_hash(self): # Arrange db = self.MockDatabase() (block1, block2, block3, _) = TestBlocks.setup_validate() db.add_block(block1) db.add_block(block2) db.add_block(block3) # Act block2 = MultiChainBlock(block2.pack_db_insert()) block2.previous_hash = sha256(str(random.randint(0, 100000))).digest() block2.sign(db.get(block2.public_key, block2.sequence_number).key) result = block2.validate(db) # Assert self.assertEqual(result[0], ValidationResult.invalid) self.assertIn('Previous hash is not equal to the hash id of the previous block', result[1])
def sign_block(self, candidate, public_key=None, bytes_up=None, bytes_down=None, linked=None): """ Create, sign, persist and send a block signed message :param candidate: The peer with whom you have interacted, as a dispersy candidate :param bytes_up: The bytes you have uploaded to the peer in this interaction :param bytes_down: The bytes you have downloaded from the peer in this interaction :param linked: The block that the requester is asking us to sign """ # NOTE to the future: This method reads from the database, increments and then writes back. If in some future # this method is allowed to execute in parallel, be sure to lock from before .create up to after .add_block assert bytes_up is None and bytes_down is None and linked is not None or \ bytes_up is not None and bytes_down is not None and linked is None, \ "Either provide a linked block or byte counts, not both" assert linked is None or linked.link_public_key == self.my_member.public_key, \ "Cannot counter sign block not addressed to self" assert linked is None or linked.link_sequence_number == UNKNOWN_SEQ, \ "Cannot counter sign block that is not a request" block = MultiChainBlock.create(self.persistence, self.my_member.public_key, linked) if linked is None: block.up = bytes_up block.down = bytes_down block.total_up += bytes_up block.total_down += bytes_down block.link_public_key = public_key block.sign(self.my_member.private_key) validation = block.validate(self.persistence) self.logger.info("Signed block to %s (%s) validation result %s", block.link_public_key.encode("hex")[-8:], block, validation) if validation[0] != ValidationResult.partial_next and validation[ 0] != ValidationResult.valid: self.logger.error("Signed block did not validate?! Result %s", repr(validation)) else: self.persistence.add_block(block) self.send_block(candidate, block)
def test_validate_existing_fraud(self): # Arrange db = self.MockDatabase() (block1, block2, _, _) = TestBlocks.setup_validate() db.add_block(block1) db.add_block(block2) # Act block3 = MultiChainBlock(block2.pack_db_insert()) block3.previous_hash = sha256(str(random.randint(0, 100000))).digest() block3.sign(block2.key) result = block3.validate(db) # Assert self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Double sign fraud", result[1])
def test_validate_existing_total_down(self): # Arrange db = self.MockDatabase() (block1, block2, block3, _) = TestBlocks.setup_validate() db.add_block(block1) db.add_block(block2) db.add_block(block3) # Act block2 = MultiChainBlock(block2.pack_db_insert()) block2.total_down += 10 block2.sign(db.get(block2.public_key, block2.sequence_number).key) result = block2.validate(db) # Assert self.assertEqual(result[0], ValidationResult.invalid) self.assertIn('Total down is higher than expected compared to the next block', result[1])
def test_pack(self): block = MultiChainBlock() block.up = 3251690667711950702 block.down = 7431046511915463784 block.total_up = 7020667011326177138 block.total_down = 2333265293611395173 block.public_key = ' fish, so sad that it should come to this. - We tried to warn you all but ' block.sequence_number = 1869095012 block.link_public_key = 'ear! - You may not share our intellect, which might explain your disrespec' block.link_sequence_number = 1949048934 block.previous_hash = 'or all the natural wonders that ' block.signature = 'grow around you. - So long, so long, and thanks for all the fish' self.assertEqual(block.pack(), '- So long and thanks for all the fish, so sad that it should come to this. - We' ' tried to warn you all but oh dear! - You may not share our intellect, which ' 'might explain your disrespect, for all the natural wonders that grow around you' '. - So long, so long, and thanks for all the fish')
def test_hash(self): block = MultiChainBlock() self.assertEqual(block.hash, "\xa1c!\x14\x11\x14\xe4\xb1g\xebB\xae\xc1y-\x0eF\x1d\x94'\x1co\xc5\xe4g\x80\xf1" "\xc1z\xb0\x12\xd7")
def _get(self, query, params): db_result = self.execute(_header + query, params).fetchone() return MultiChainBlock(db_result) if db_result else None
def _getall(self, query, params): db_result = self.execute(_header + query, params).fetchall() return [MultiChainBlock(db_item) for db_item in db_result]
def test_get_statistics(self): """ Testing whether the API returns the correct statistics """ block = MultiChainBlock() block.public_key = self.member.public_key block.link_public_key = "deadbeef".decode("HEX") block.link_sequence_number = 21 block.up = 42 block.down = 8 block.total_up = 1024 block.total_down = 2048 block.sequence_number = 3 block.previous_hash = "babecafe".decode("HEX") block.signature = "babebeef".decode("HEX") self.mc_community.persistence.add_block(block) def verify_response(response): response_json = json.loads(response) self.assertTrue("statistics" in response_json) stats = response_json["statistics"] self.assertEqual(stats["id"], self.member.public_key.encode("HEX")) self.assertEqual(stats["total_blocks"], 3) self.assertEqual(stats["total_up"], 1024) self.assertEqual(stats["total_down"], 2048) self.assertEqual(stats["peers_that_pk_helped"], 1) self.assertEqual(stats["peers_that_helped_pk"], 1) self.assertIn("latest_block", stats) self.assertNotEqual(stats["latest_block"]["insert_time"], "") self.assertEqual(stats["latest_block"]["hash"], block.hash.encode("HEX")) self.assertEqual(stats["latest_block"]["link_public_key"], "deadbeef") self.assertEqual(stats["latest_block"]["link_sequence_number"], 21) self.assertEqual(stats["latest_block"]["up"], 42) self.assertEqual(stats["latest_block"]["down"], 8) self.should_check_equality = False return self.do_request('multichain/statistics', expected_code=200).addCallback(verify_response)