def test_block_set_balance(block_factory): """ Test changing the 'balance' block attribute """ # Hex-formatted balance block = block_factory("send") block.signature = None block.balance = 100 assert json.loads(block.json())["balance"] == \ "00000000000000000000000000000064" with pytest.raises(InvalidBalance): # Pre-state blocks only accept hex balances when deserializing # from a dict block_data = BLOCKS["send"]["data"].copy() block_data["balance"] = str(2**128) Block.from_dict(block_data) # String-formatted balance block = block_factory("state_sendreceive") block.signature = None block.balance = 100 assert json.loads(block.json())["balance"] == "100" # Invalid balances with pytest.raises(InvalidBalance): block.balance = -1 with pytest.raises(InvalidBalance): block.balance = MAX_BALANCE + 1
def test_block_invalid_work(block_factory): """ Try to load a block with invalid work """ block_data = BLOCKS["receive"]["data"].copy() block_data["work"] = "a" * 16 with pytest.raises(InvalidWork): Block.from_dict(block_data)
def test_block_missing_parameters(): """ Try to load a block with a missing required parameter """ block_data = BLOCKS["receive"]["data"].copy() del block_data["previous"] with pytest.raises(InvalidBlock) as exc: Block.from_dict(block_data) assert "is missing required parameters: previous" in str(exc)
def test_block_prohibited_parameters(): """ Try to load a block with an additional prohibited parameter """ block_data = BLOCKS["change"]["data"].copy() block_data["balance"] = "10000" with pytest.raises(InvalidBlock) as exc: Block.from_dict(block_data) assert "has prohibited parameters: balance" in str(exc)
def test_block_legacy_send_from_dict(): """ When deserializing a legacy send block, the balance has to be hex-formatted """ block_data = BLOCKS["send"]["data"].copy() block_data["balance"] = "10000000" with pytest.raises(InvalidBalance) as exc: Block.from_dict(block_data) assert "needs to be a hex" in str(exc)
def test_block_invalid_difficulty(): """ Create a Block with different difficulties to make it either pass or fail """ block_data = BLOCKS["send"]["data"].copy() with pytest.raises(InvalidWork): Block.from_dict(block_data, difficulty="fffffffeb1249487") block = Block.from_dict(block_data, difficulty="fffffffeb1249486") # Difficulty is required with pytest.raises(ValueError): block.difficulty = None
def test_block_signature_with_missing_parameters(): """ Try to verify a signature for a block that's missing the 'account' or 'signature' parameters NANO's rai_node API returns JSON representations of blocks that may not contain the 'account' parameter """ block = Block.from_dict(BLOCKS["receive"]["data"]) with pytest.raises(ValueError): # Missing account block.verify_signature() block.signature = None block.account = BLOCKS["receive"]["account"] with pytest.raises(ValueError): # Missing signature block.verify_signature() # Add the missing parameters block.account = BLOCKS["receive"]["account"] block.signature = BLOCKS["receive"]["data"]["signature"] block.verify_signature()
def test_block_invalid_block_type(): """ Try creating a block with an invalid block type """ with pytest.raises(ValueError) as exc: Block(block_type="transactionate_funds") assert "block_type" in str(exc)
def test_block_tx_type(): """ Load different blocks and check that their tx_types match """ for block_data, tx_type in BLOCKS_AND_TYPES: block = Block.from_dict(block_data["data"]) assert block.tx_type == tx_type, \ "For block %s, expected tx_type %s, got %s" % ( block_data["hash"], tx_type, block.tx_type)
def test_block_json(name, block): """ Deserialize every block from JSON and ensure they can be serialized back into identical JSON """ test_block = block["data"] block = Block.from_json(json.dumps(test_block)) assert json.loads(block.json()) == test_block
def test_block_hash(name, block): """ Calculate block hash for every test block and check that they match with the test data """ correct_block_hash = block["hash"] block = Block.from_dict(block["data"]) assert block.block_hash == correct_block_hash
def test_block_is_complete(): """ Check if a block is complete using Block.is_complete """ block = Block.from_dict(BLOCKS["receive"]["data"]) assert not block.complete # Add the 'account', so that the block can be verified block.account = BLOCKS["receive"]["account"] assert block.complete
def test_block_sign(): """ Sign a block and verify it """ private_key = \ "587d4d70c1a3b66db89ad0e69e12bbd06774e8a161d2dca0c0c734556b8656ad" account_id = \ "xrb_3f3iy4xh3umniqzpisrbuj6s1scde3mj83ffwgr4ckq1q6oirez1yjwq9t3y" block = Block( block_type="state", account=account_id, representative=account_id, previous=None, balance=100000, link="A4647CEBA216FD004AAFE3F552BA98739E6C4AD75A8C3E6A12B93531725D9F3A" ) assert not block.has_valid_signature block.sign(private_key) assert block.has_valid_signature with pytest.raises(ValueError): # Can't sign again if the block already has a signature block.sign(private_key)
def test_block_invalid_signature(): """ Try to load a block with invalid signature """ # 'open' blocks are the only legacy blocks that can be verified as-is # Other blocks need to add the optional 'account' parameter block = BLOCKS["open"]["data"].copy() block["signature"] = "B" * 128 with pytest.raises(InvalidSignature) as exc: # Invalid signature (can't be verified) Block.from_dict(block) assert "couldn't be verified" in str(exc) block["signature"] = "A" with pytest.raises(InvalidSignature) as exc: # Invalid signature (incorrect length) Block.from_dict(block) assert "hexadecimal string" in str(exc)
def test_block_skip_verify(): """ Deserialize a Block by skipping the verification of signature and PoW """ block_data = BLOCKS["receive"]["data"].copy() block_data["work"] = "a" * 16 # The work is invalid, but it's OK Block.from_dict(block_data, verify=False) Block.from_json(json.dumps(block_data), verify=False) block_data["signature"] = "A" * 128 # The signature is invalid, but that's OK as well now Block.from_dict(block_data, verify=False) Block.from_json(json.dumps(block_data), verify=False)
def test_block_complete(name, block): """ Deserialize every block from JSON and check that they have valid PoW and signatures """ test_block = block["data"] block = Block.from_json(json.dumps(test_block)) assert block.has_valid_work if block.account: assert block.has_valid_signature assert block.complete else: # Nano reference wallet's RPC API doesn't return some legacy blocks # with 'account' fields set even if they're required to verify the # signature. try: assert not block.account block.verify_signature() except ValueError as e: assert "'account' not included" in str(e) assert not block.complete
def _create_func(name): return Block.from_dict(BLOCKS[name]["data"])