def test_transaction_v2_invalid_signature(self): # noinspection PyDictCreation tx_dumped = { 'from': 'hx48cd6eb32339d5c719dcc0af21e9bc3b67d733e6', 'to': 'hx22f72e44141bedd50d1e536455682863d3d8a484', 'value': '0x186a0', 'fee': '0xf4240', 'timestamp': '1558679280067963', 'nonce': '1', 'tx_hash': '34477b3bc76fa73aad0258ba9fd36f28a3c4b26956c1e5eb92ddda7d98df4e32', # valid hash 'signature': 'W/hW/PAo+ExeSsreD//yJVgNqmnkWKs+m0VUqE11O7Ek82yEINuczLRXtj1k515q8Ep4OLsRPPiPNjDM9vuhsgE=' } tx_dumped['signature'] = Signature(os.urandom( Signature.size)).to_base64str() # invalid signature tx_version, tx_type = self.tx_versioner.get_version(tx_dumped) ts = TransactionSerializer.new(tx_version, tx_type, self.tx_versioner) tx = ts.from_(tx_dumped) tv = TransactionVerifier.new(tx_version, tx_type, self.tx_versioner) self.assertRaises(TransactionInvalidSignatureError, lambda: tv.verify(tx)) self.assertRaises(TransactionInvalidSignatureError, lambda: tv.pre_verify(tx))
def test_block_invalid_vote(self): ratio = 0.67 block_hash = Hash32(os.urandom(Hash32.size)) block_votes = BlockVotes(self.reps, ratio, 0, 0, block_hash) invalid_block_vote = BlockVote.new(self.signers[0], 0, 0, 1, block_hash) self.assertRaises(RuntimeError, block_votes.add_vote, invalid_block_vote) invalid_block_vote = BlockVote.new(self.signers[0], 0, 1, 0, block_hash) self.assertRaises(RuntimeError, block_votes.add_vote, invalid_block_vote) invalid_block_vote = BlockVote.new(self.signers[0], 0, 0, 0, Hash32(os.urandom(32))) self.assertRaises(RuntimeError, block_votes.add_vote, invalid_block_vote) invalid_block_vote = BlockVote(rep=self.reps[0], timestamp=0, signature=Signature(os.urandom(65)), block_height=0, round_=0, block_hash=block_hash) self.assertRaises(RuntimeError, block_votes.add_vote, invalid_block_vote) block_vote = BlockVote.new(self.signers[0], 0, 0, 0, block_hash) block_votes.add_vote(block_vote) duplicate_block_vote = BlockVote.new(self.signers[0], 0, 0, 0, Hash32.empty()) self.assertRaises(votes.VoteDuplicateError, block_votes.add_vote, duplicate_block_vote)
def _deserialize_header_data(self, json_data: dict): hash_ = Hash32.fromhex(json_data["hash"]) prev_hash = json_data.get('prevHash') prev_hash = Hash32.fromhex(prev_hash) if prev_hash else None peer_id = json_data.get('leader') peer_id = ExternalAddress.fromhex(peer_id) if peer_id else None signature = json_data.get('signature') signature = Signature.from_base64str(signature) if signature else None next_leader = json_data.get("nextLeader") next_leader = ExternalAddress.fromhex( next_leader) if next_leader else None transactions_hash = json_data["transactionsHash"] transactions_hash = Hash32.fromhex(transactions_hash) receipts_hash = json_data["receiptsHash"] receipts_hash = Hash32.fromhex(receipts_hash) state_hash = json_data["stateHash"] state_hash = Hash32.fromhex(state_hash) reps_hash = json_data["repsHash"] reps_hash = Hash32.fromhex(reps_hash) next_reps_hash = json_data["nextRepsHash"] next_reps_hash = Hash32.fromhex(next_reps_hash) leader_votes_hash = json_data["leaderVotesHash"] leader_votes_hash = Hash32.fromhex(leader_votes_hash) prev_votes_hash = json_data["prevVotesHash"] prev_votes_hash = Hash32.fromhex(prev_votes_hash) height = json_data["height"] height = int(height, 16) timestamp = json_data["timestamp"] timestamp = int(timestamp, 16) return { "hash": hash_, "prev_hash": prev_hash, "height": height, "timestamp": timestamp, "peer_id": peer_id, "signature": signature, "next_leader": next_leader, "transactions_hash": transactions_hash, "receipts_hash": receipts_hash, "state_hash": state_hash, "reps_hash": reps_hash, "next_reps_hash": next_reps_hash, "leader_votes_hash": leader_votes_hash, "prev_votes_hash": prev_votes_hash, "logs_bloom": BloomFilter.fromhex(json_data["logsBloom"]) }
def from_(self, tx_data: dict) -> 'Transaction': tx_data_copied = dict(tx_data) tx_data_copied.pop('method', None) hash = tx_data_copied.pop('tx_hash', None) signature = tx_data_copied.pop('signature', None) timestamp = tx_data_copied.pop('timestamp', None) from_address = tx_data_copied.pop('from', None) to_address = tx_data_copied.pop('to', None) value = tx_data_copied.pop('value', None) fee = tx_data_copied.pop('fee', None) nonce = tx_data_copied.pop('nonce', None) extra = tx_data_copied value = int_fromhex(value) fee = int_fromhex(fee) if nonce is not None: nonce = int_fromstr(nonce) return Transaction( raw_data=tx_data, hash=Hash32.fromhex(hash, ignore_prefix=True, allow_malformed=False), signature=Signature.from_base64str(signature), timestamp=int(timestamp) if timestamp is not None else None, from_address=ExternalAddress.fromhex(from_address, ignore_prefix=False, allow_malformed=True), to_address=ExternalAddress.fromhex(to_address, ignore_prefix=False, allow_malformed=True), value=value, fee=fee, nonce=nonce, extra=extra, )
def sign(self): if self.signature is not None: return self.signature if self.hash is None: raise RuntimeError self.signature = Signature(self.signer.sign_hash(self.hash)) return self.signature
def sign_transaction(self, tx: 'Transaction'): if self.signer.address != tx.signer_address.hex_hx(): raise RuntimeError( f"Signer not match. {self.signer.address} != {tx.signer_address.hex_hx()}" ) signature = Signature(self.signer.sign_hash(tx.hash)) raw_data = dict(tx.raw_data) raw_data["signature"] = signature.to_base64str() return Transaction(raw_data=raw_data, hash=tx.hash, signature=signature, timestamp=tx.timestamp, from_address=tx.from_address, to_address=tx.to_address, value=tx.value, fee=tx.fee, nonce=tx.nonce)
def _header(hash_: Hash32 = Hash32.new(), prev_hash: Hash32 = Hash32.new(), height: int = 0, timestamp: int = 0, peer_id: ExternalAddress = ExternalAddress.new(), signature: Signature = Signature.new(), next_leader: Address = Address.new(), merkle_tree_root_hash: Hash32 = Hash32.new(), commit_state: dict = dict()) -> BlockHeader_v0_1a: return BlockHeader_v0_1a(hash_, prev_hash, height, timestamp, peer_id, signature, next_leader, merkle_tree_root_hash, commit_state)
def _header(hash_: Hash32 = Hash32.new(), prev_hash: Hash32 = Hash32.new(), height: int = 0, timestamp: int = 0, peer_id: ExternalAddress = ExternalAddress.new(), signature: Signature = Signature.new(), next_leader: ExternalAddress = ExternalAddress.new(), logs_bloom: BloomFilter = BloomFilter.new(), transactions_hash: Hash32 = Hash32.new(), state_hash: Hash32 = Hash32.new(), receipts_hash: Hash32 = Hash32.new(), reps_hash: Hash32 = Hash32.new(), next_reps_hash: Hash32 = Hash32.new(), leader_votes_hash: Hash32 = Hash32.new(), prev_votes_hash: Hash32 = Hash32.new()) -> BlockHeader_v0_4: return BlockHeader_v0_4(hash_, prev_hash, height, timestamp, peer_id, signature, next_leader, logs_bloom, transactions_hash, state_hash, receipts_hash, reps_hash, next_reps_hash, leader_votes_hash, prev_votes_hash)
def test_leader_invalid_vote(self): ratio = 0.67 old_leader = self.reps[0] new_leader = self.reps[1] leader_votes = LeaderVotes(self.reps, ratio, 0, 0, old_leader) invalid_leader_vote = LeaderVote.new(self.signers[0], 0, 1, 0, old_leader, new_leader) self.assertRaises(RuntimeError, leader_votes.add_vote, invalid_leader_vote) invalid_leader_vote = LeaderVote.new(self.signers[0], 0, 0, 1, old_leader, new_leader) self.assertRaises(RuntimeError, leader_votes.add_vote, invalid_leader_vote) invalid_leader_vote = LeaderVote.new(self.signers[0], 0, 0, 0, new_leader, new_leader) self.assertRaises(RuntimeError, leader_votes.add_vote, invalid_leader_vote) invalid_leader_vote = LeaderVote(rep=self.reps[0], timestamp=0, signature=Signature(os.urandom(65)), block_height=0, round_=0, new_leader=new_leader, old_leader=old_leader) self.assertRaises(RuntimeError, leader_votes.add_vote, invalid_leader_vote) leader_vote = LeaderVote.new(self.signers[0], 0, 0, 0, old_leader, new_leader) leader_votes.add_vote(leader_vote) duplicate_leader_vote = LeaderVote.new(self.signers[0], 0, 0, 0, old_leader, self.reps[2]) self.assertRaises(votes.VoteDuplicateError, leader_votes.add_vote, duplicate_leader_vote)
def test_transaction_v3_invalid_signature(self): # noinspection PyDictCreation tx_dumped = { 'version': '0x3', 'from': 'hx48cd6eb32339d5c719dcc0af21e9bc3b67d733e6', 'to': 'hxe0a231fa5c80e45f51d7df5f7d127954320df829', 'stepLimit': '0xf4240', 'timestamp': '0x5899c717f92f8', 'nid': '0x3', 'value': '0x186a0', 'nonce': '0x64', 'data': 'test', 'dataType': 'message', 'signature': 'J84KdBtQR4w1bcBdBGF8g6aNoCXjsY/5T6vGV4RXeMwEvafj9xVRDVjzF+vN1JVYvXrAzjlYPCiiBXBQe6+tRAE=' } tx_dumped['signature'] = Signature(os.urandom( Signature.size)).to_base64str() # invalid signature tx_version, tx_type = self.tx_versioner.get_version(tx_dumped) ts = TransactionSerializer.new(tx_version, tx_type, self.tx_versioner) tx = ts.from_(tx_dumped) tv = TransactionVerifier.new(tx_version, tx_type, self.tx_versioner) self.assertRaises(TransactionInvalidSignatureError, lambda: tv.verify(tx)) self.assertRaises(TransactionInvalidSignatureError, lambda: tv.pre_verify(tx, nid=3))
def _deserialize_header_data(self, json_data: dict): prev_hash = json_data.get('prev_block_hash') prev_hash = Hash32.fromhex(prev_hash, ignore_prefix=True) if prev_hash else None peer_id = json_data.get('peer_id') peer_id = ExternalAddress.fromhex(peer_id) if peer_id else None signature = json_data.get('signature') signature = Signature.from_base64str(signature) if signature else None next_leader = json_data.get("next_leader") next_leader = ExternalAddress.fromhex(next_leader) if next_leader else None return { "hash": Hash32.fromhex(json_data["block_hash"], ignore_prefix=True), "prev_hash": prev_hash, "height": json_data["height"], "timestamp": json_data["time_stamp"], "peer_id": peer_id, "signature": signature, "next_leader": next_leader, "merkle_tree_root_hash": Hash32.fromhex(json_data["merkle_tree_root_hash"], ignore_prefix=True), "commit_state": json_data["commit_state"] }
def empty(cls, rep: ExternalAddress, block_height: int, round_: int, old_leader: ExternalAddress): return cls(rep, 0, Signature.empty(), block_height, round_, old_leader, ExternalAddress.empty())
def _deserialize(cls, data: dict): return { "rep": ExternalAddress.fromhex_address(data["rep"]), "timestamp": int(data["timestamp"], 16), "signature": Signature.from_base64str(data["signature"]) }
def new(cls, signer: Signer, timestamp: int, **kwargs): rep_id: ExternalAddress = ExternalAddress.fromhex(signer.address) hash_ = cls.to_hash(rep_id, timestamp, **kwargs) signature = Signature(signer.sign_hash(hash_)) return cls(rep_id, timestamp, signature, **kwargs)
def empty(cls, rep: ExternalAddress, block_height: int): return cls(rep, 0, Signature.empty(), block_height, Hash32.empty())
class TestTxCache: target_attrs = ["hash", "signature"] @pytest.mark.parametrize("target_attr", target_attrs) def test_no_cache_attr_in_tx_if_not_verified(self, tx_version, tx_factory: 'TxFactory', target_attr): """Check that Transaction has no attribute until the verification func has been invoked""" tx = tx_factory(tx_version) if any("cache" in attr for attr in dir(tx)): raise AttributeError( "Something wrong. Transaction has cached value when initialized." ) with pytest.raises(AttributeError): getattr(tx, f"_cache_verify_{target_attr}") @pytest.mark.parametrize("raise_exception", [True, False]) @pytest.mark.parametrize("target_attr", ["hash", "signature"]) def test_has_cache_attr_when_verified_successfully(self, tx_version, tx_factory: 'TxFactory', raise_exception, target_attr): """Check that the verification result has been cached successfully""" tx = tx_factory(tx_version) tv = TransactionVerifier.new(tx.version, tx.type(), tx_versioner, raise_exceptions=raise_exception) verify_func_name = f"verify_{target_attr}" verify_func = getattr(tv, verify_func_name) verify_func(tx) assert getattr(tx, f"_cache_{verify_func_name}") @pytest.mark.parametrize("raise_exception", [True, False]) @pytest.mark.parametrize("expected_exc, target_attr, fake_value", [ (TransactionInvalidHashError, "hash", Hash32.new()), (TransactionInvalidSignatureError, "signature", Signature.new()), ]) def test_exception_cached_when_raised_exception_while_verification( self, tx_version, tx_factory: 'TxFactory', raise_exception, expected_exc, target_attr, fake_value, monkeypatch): """Check that the exception successfully cached when raised any exception while verification step""" tx = tx_factory(tx_version) tv = TransactionVerifier.new(tx.version, tx.type(), tx_versioner, raise_exceptions=raise_exception) verify_func = getattr(tv, f"verify_{target_attr}") orig_value = getattr(tx, target_attr) test_values = (fake_value, orig_value) for test_value in test_values: # Monkeypatch object.__setattr__(tx, target_attr, test_value) assert getattr(tx, target_attr) == test_value # Verification test if raise_exception: with pytest.raises(expected_exc): verify_func(tx) else: tv.exceptions.clear() assert not tv.exceptions verify_func(tx) assert isinstance(tv.exceptions[0], expected_exc) @pytest.mark.parametrize("raise_exception", [True, False]) @pytest.mark.parametrize("expected_exc, target_attr, fake_value", [ (TransactionInvalidHashError, "hash", Hash32.new()), (TransactionInvalidSignatureError, "signature", Signature.new()), ]) def test_verify_success_and_no_exc_with_fake_value_at_second( self, tx_version, tx_factory: 'TxFactory', raise_exception, expected_exc, target_attr, fake_value, monkeypatch): """Check that the result is successfully cached and bypasses further verifications which could raise exceptions. Do not apply this usecase in code! This test aims the reliablity of cache logic, not for the usefulness of this case. """ tx = tx_factory(tx_version) tv = TransactionVerifier.new(tx.version, tx.type(), tx_versioner, raise_exceptions=raise_exception) verify_func = getattr(tv, f"verify_{target_attr}") # First verification verify_func(tx) # Monkeypatch with fake value object.__setattr__(tx, target_attr, fake_value) assert getattr(tx, target_attr) == fake_value # Verify again with fake value and ensure no exceptions raised if raise_exception: verify_func(tx) else: tv.exceptions.clear() assert not tv.exceptions verify_func(tx) assert not tv.exceptions @pytest.mark.parametrize("tag", ["before_cache", "after_cache"]) @pytest.mark.parametrize("target_attr", ["hash", "signature"]) def test_benchmark_verify(self, benchmark, tx_version, tx_factory: 'TxFactory', target_attr, tag): """Benchmark the elapsed time of verification func in various cases.""" tx = tx_factory(tx_version) tv = TransactionVerifier.new(tx.version, tx.type(), tx_versioner) verify_func = getattr(tv, f"verify_{target_attr}") if tag == "first": benchmark(verify_func, tx) else: verify_func(tx) benchmark(verify_func, tx)
def sign(self): if self.hash is None: self.build_hash() self.signature = Signature(self.signer.sign_hash(self.hash)) return self.signature