def test_serialize_contract_send_subclass(chain_state): """Serializing must preserve class Regression test for https://github.com/raiden-network/raiden/issues/6075 """ canonical_identifier = CanonicalIdentifier( chain_identifier=ChainID(1), token_network_address=TokenNetworkAddress(factories.make_address()), channel_identifier=factories.make_channel_identifier(), ) chain_state.pending_transactions = [ ContractSendChannelClose( canonical_identifier=canonical_identifier, triggered_by_block_hash=factories.make_block_hash(), balance_proof=None, ) ] serialized_chain_state = JSONSerializer.serialize(chain_state) deserialized_chain_state = JSONSerializer.deserialize( serialized_chain_state) assert ( chain_state.pending_transactions[0].__class__.__name__ == deserialized_chain_state.pending_transactions[0].__class__.__name__) assert chain_state == deserialized_chain_state
def test_actioninitchain_restore(): """ ActionInitChain *must* restore the previous pseudo random generator state. Message identifiers are used for confirmation messages, e.g. delivered and processed messages, therefore it's important for each message identifier to not collide with a previous identifier, for this reason the PRNG is used. Additionally, during restarts the state changes are reapplied, and it's really important for the re-execution of the state changes to be deterministic, otherwise undefined behavior may happen. For this reason the state of the PRNG must be restored. If the above is not respected, the message ids generated during restart will not match the previous IDs and the message queues won't be properly cleared up. """ pseudo_random_generator = random.Random() block_number = 577 our_address = factories.make_address() chain_id = 777 original_obj = state_change.ActionInitChain( pseudo_random_generator=pseudo_random_generator, block_number=block_number, block_hash=factories.make_block_hash(), our_address=our_address, chain_id=chain_id, ) decoded_obj = JSONSerializer.deserialize( JSONSerializer.serialize(original_obj)) assert original_obj == decoded_obj
def test_restore_queueids_to_queues(chain_state, netting_channel_state): """Test that withdraw messages are restorable if they exist in chain_state.queueids_to_queues. """ recipient = netting_channel_state.partner_state.address queue_identifier = QueueIdentifier( recipient=recipient, canonical_identifier=netting_channel_state.canonical_identifier) msg_args = dict( recipient=recipient, canonical_identifier=netting_channel_state.canonical_identifier, message_identifier=factories.make_message_identifier(), total_withdraw=WithdrawAmount(1), participant=recipient, expiration=BlockExpiration(10), nonce=Nonce(15), ) messages = [ SendWithdrawRequest(**msg_args), SendWithdrawConfirmation(**msg_args), SendWithdrawExpired(**msg_args), ] chain_state.queueids_to_queues[queue_identifier] = messages serialized_chain_state = JSONSerializer.serialize(chain_state) deserialized_chain_state = JSONSerializer.deserialize( serialized_chain_state) assert chain_state == deserialized_chain_state
def test_message_identical() -> None: """ Will fail if the messages changed since the committed version If you intend to change the serialized messages, then update the messages on disc (see comment inside test). This test exists only to prevent accidental breaking of compatibility. If many values change in unexpected ways, that might have to do with the pseudo-random initialization of the messages (see random.seed() above). """ signer = LocalSigner(bytes(range(32))) for message in messages: # The messages contain only random signatures. We don't want to test # only the serialization itself, but also prevent accidental changes of # the signature. To do this, we have to create proper signatures. message.sign(signer) filename = os.path.join( os.path.dirname(__file__), "serialized_messages", message.__class__.__name__ + ".json" ) # Uncomment this for one run if you intentionally changed the message # with open(filename, "w") as f: # json_msg = MessageSerializer.serialize(message) # # pretty print for more readable diffs # json_msg = json.dumps(json.loads(json_msg), indent=4, sort_keys=True) # f.write(json_msg) with open(filename) as f: saved_message_dict = JSONSerializer.deserialize(f.read()) # The assert output is more readable when we used dicts than with plain JSON message_dict = JSONSerializer.deserialize(MessageSerializer.serialize(message)) assert message_dict == saved_message_dict
def test_mediated_transfer_min_max(amount, payment_identifier, nonce, transferred_amount): mediated_transfer = factories.create( factories.LockedTransferProperties( amount=amount, payment_identifier=payment_identifier, nonce=nonce, transferred_amount=transferred_amount, ) ) mediated_transfer.sign(signer) data = JSONSerializer.serialize(mediated_transfer) assert JSONSerializer.deserialize(data) == mediated_transfer
def test_send_refund_transfer_contains_balance_proof(): recipient = factories.make_address() transfer = factories.create(factories.LockedTransferUnsignedStateProperties()) message_identifier = 1 event = SendRefundTransfer( recipient=recipient, message_identifier=message_identifier, transfer=transfer, canonical_identifier=factories.make_canonical_identifier(), ) assert hasattr(event, "balance_proof") assert JSONSerializer.deserialize(JSONSerializer.serialize(event)) == event
def test_serialization_networkx_graph(): p1 = to_canonical_address("0x5522070585a1a275631ba69c444ac0451AA9Fe4C") p2 = to_canonical_address("0x5522070585a1a275631ba69c444ac0451AA9Fe4D") p3 = to_canonical_address("0x5522070585a1a275631ba69c444ac0451AA9Fe4E") p4 = to_canonical_address("0x5522070585a1a275631ba69c444ac0451AA9Fe4F") e = [(p1, p2), (p2, p3), (p3, p4)] graph = Graph(e) instance = ClassWithGraphObject(graph) data = JSONSerializer.serialize(instance) restored_instance = JSONSerializer.deserialize(data) assert instance.graph.edges == restored_instance.graph.edges
def test_refund_transfer_min_max(amount, payment_identifier, nonce, transferred_amount): refund_transfer = factories.create( factories.RefundTransferProperties( amount=amount, payment_identifier=payment_identifier, nonce=nonce, transferred_amount=transferred_amount, ) ) refund_transfer.sign(signer) data = JSONSerializer.serialize(refund_transfer) assert JSONSerializer.deserialize(data) == refund_transfer
def test_events_loaded_from_storage_should_deserialize(tmp_path): filename = f"{tmp_path}/v{RAIDEN_DB_VERSION}_log.db" storage = SerializedSQLiteStorage(filename, serializer=JSONSerializer()) # Satisfy the foreign-key constraint for state change ID ids = storage.write_state_changes([ ActionInitChain( pseudo_random_generator=random.Random(), block_number=1, block_hash=b"", our_address=factories.make_address(), chain_id=1, ) ]) canonical_identifier = factories.make_canonical_identifier() recipient = factories.make_address() participant = factories.make_address() event = SendWithdrawRequest( recipient=recipient, canonical_identifier=canonical_identifier, message_identifier=factories.make_message_identifier(), total_withdraw=1, participant=participant, expiration=10, nonce=15, ) storage.write_events([(ids[0], event)]) stored_events = storage.get_events() assert stored_events[0] == event
def main(db_file, token_network_address, partner_address, names_translator): translator: Optional[Translator] if names_translator: translator = Translator(json.load(names_translator)) lookup = {v: k for k, v in translator.items()} token_network_address = lookup.get(token_network_address, token_network_address) partner_address = lookup.get(partner_address, partner_address) else: translator = None assert is_checksum_address( token_network_address), "token_network_address must be provided" assert is_checksum_address( partner_address), "partner_address must be provided" with closing(SerializedSQLiteStorage(db_file, JSONSerializer())) as storage: replay_wal( storage=storage, token_network_address=token_network_address, partner_address=partner_address, translator=translator, )
def test_events_loaded_from_storage_should_deserialize(tmp_path): filename = Path(f"{tmp_path}/v{RAIDEN_DB_VERSION}_log.db") storage = SerializedSQLiteStorage(filename, serializer=JSONSerializer()) # Satisfy the foreign-key constraint for state change ID ids = storage.write_state_changes([ Block( block_number=BlockNumber(1), gas_limit=BlockGasLimit(1), block_hash=factories.make_block_hash(), ) ]) canonical_identifier = factories.make_canonical_identifier() recipient = factories.make_address() participant = factories.make_address() event = SendWithdrawRequest( recipient=recipient, canonical_identifier=canonical_identifier, message_identifier=factories.make_message_identifier(), total_withdraw=WithdrawAmount(1), participant=participant, expiration=BlockExpiration(10), nonce=Nonce(15), ) storage.write_events([(ids[0], event)]) stored_events = storage.get_events() assert stored_events[0] == event
def new_wal(state_transition: Callable, state: State = None) -> WriteAheadLog: serializer = JSONSerializer() state_manager = StateManager(state_transition, state) storage = SerializedSQLiteStorage(":memory:", serializer) wal = WriteAheadLog(state_manager, storage) return wal
def test_chainstate_restore(): pseudo_random_generator = random.Random() block_number = 577 our_address = factories.make_address() chain_id = 777 original_obj = state.ChainState( pseudo_random_generator=pseudo_random_generator, block_number=block_number, block_hash=factories.make_block_hash(), our_address=our_address, chain_id=chain_id, ) decoded_obj = JSONSerializer.deserialize(JSONSerializer.serialize(original_obj)) assert original_obj == decoded_obj
def new_wal(state_transition: Callable, state: State = None) -> WriteAheadLog: serializer = JSONSerializer() state = state or Empty() storage = SerializedSQLiteStorage(":memory:", serializer) storage.write_first_state_snapshot(state) return WriteAheadLog(state, storage, state_transition)
def test_initial_state_snapshotting(): serializer = JSONSerializer() state = Empty() storage = SerializedSQLiteStorage(":memory:", serializer) assert not storage.database.has_snapshot() assert not storage.get_snapshot_before_state_change(LOW_STATECHANGE_ULID) storage.write_first_state_snapshot(state) assert storage.database.has_snapshot() assert storage.get_snapshot_before_state_change(LOW_STATECHANGE_ULID)
def test_get_state_change_with_transfer_by_secrethash(): serializer = JSONSerializer() storage = SerializedSQLiteStorage(":memory:", serializer) mediator_secret, mediator_secrethash = factories.make_secret_with_hash() channels = factories.mediator_make_channel_pair() mediator_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=mediator_secret, target=channels.partner_address(1), initiator=channels.partner_address(0), ) ) mediator_state_change = factories.mediator_make_init_action(channels, mediator_transfer) target_secret, target_secrethash = factories.make_secret_with_hash() from_channel = factories.create( factories.NettingChannelStateProperties( partner_state=factories.NettingChannelEndStateProperties( balance=100, address=factories.make_address() ) ) ) target_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=target_secret, target=channels.our_address(0), initiator=channels.partner_address(1), ) ) target_state_change = ActionInitTarget( from_hop=HopState( node_address=from_channel.partner_state.address, channel_identifier=from_channel.canonical_identifier.channel_identifier, ), transfer=target_transfer, balance_proof=target_transfer.balance_proof, sender=target_transfer.balance_proof.sender, # pylint: disable=no-member ) assert storage.count_state_changes() == 0 storage.write_state_changes([mediator_state_change, target_state_change]) assert storage.count_state_changes() == 2 restored = get_state_change_with_transfer_by_secrethash(storage, mediator_secrethash) assert isinstance(restored.data, ActionInitMediator) assert restored.data.from_transfer == mediator_transfer restored = get_state_change_with_transfer_by_secrethash(storage, target_secrethash) assert isinstance(restored.data, ActionInitTarget) assert restored.data.transfer == target_transfer
def test_log_run(): with patch("raiden.storage.sqlite.get_system_spec") as get_speck_mock: get_speck_mock.return_value = dict(raiden="1.2.3") serializer = JSONSerializer() store = SerializedSQLiteStorage(":memory:", serializer) store.log_run() cursor = store.database.conn.cursor() cursor.execute("SELECT started_at, raiden_version FROM runs") run = cursor.fetchone() now = datetime.utcnow() assert now - timedelta(seconds=2) <= run[0] <= now, f"{run[0]} not right before {now}" assert run[1] == "1.2.3" store.close()
def __init__(self, message_handler=None, state_transition=None, private_key=None): if private_key is None: self.privkey, self.address = factories.make_privkey_address() else: self.privkey = private_key self.address = privatekey_to_address(private_key) self.rpc_client = MockJSONRPCClient(self.address) self.proxy_manager = MockProxyManager(node_address=self.address) self.signer = LocalSigner(self.privkey) self.message_handler = message_handler self.routing_mode = RoutingMode.PRIVATE self.config = RaidenConfig(chain_id=self.rpc_client.chain_id, environment_type=Environment.DEVELOPMENT) self.default_user_deposit = Mock() self.default_registry = Mock() self.default_registry.address = factories.make_address() self.default_one_to_n_address = factories.make_address() self.default_msc_address = factories.make_address() self.targets_to_identifiers_to_statuses: Dict[Address, dict] = defaultdict(dict) self.route_to_feedback_token: dict = {} if state_transition is None: state_transition = node.state_transition serializer = JSONSerializer() state_manager = StateManager(state_transition, None) storage = SerializedSQLiteStorage(":memory:", serializer) self.wal = WriteAheadLog(state_manager, storage) state_change = ActionInitChain( pseudo_random_generator=random.Random(), block_number=BlockNumber(0), block_hash=factories.make_block_hash(), our_address=self.rpc_client.address, chain_id=self.rpc_client.chain_id, ) with self.wal.process_state_change_atomically() as dispatcher: dispatcher.dispatch(state_change) self.transport = Mock()
def test_decode_with_unknown_type(): test_str = """{"_type": "some.non.existent.package"}""" with pytest.raises(SerializationError): JSONSerializer.deserialize(test_str) test_str = """{"_type": "raiden.tests.NonExistentClass"}""" with pytest.raises(SerializationError): JSONSerializer.deserialize(test_str) test_str = """{"_type": "NonExistentClass"}""" with pytest.raises(SerializationError): JSONSerializer.deserialize(test_str)
def test_upgrade_manager_restores_backup(tmp_path, monkeypatch): db_path = tmp_path / Path("v17_log.db") old_db_filename = tmp_path / Path("v16_log.db") with patch("raiden.storage.sqlite.RAIDEN_DB_VERSION", new=16), SQLiteStorage(str(old_db_filename)) as storage: state_change = ActionInitChain( chain_id=1, our_address=factories.make_address(), block_number=1, block_hash=factories.make_block_hash(), pseudo_random_generator=random.Random(), ) action_init_chain_data = JSONSerializer.serialize(state_change) storage.write_state_changes(state_changes=[action_init_chain_data]) storage.update_version() upgrade_functions = [UpgradeRecord(from_version=16, function=Mock())] upgrade_functions[0].function.return_value = 17 web3, _ = create_fake_web3_for_block_hash(number_of_blocks=1) with monkeypatch.context() as m: m.setattr(raiden.utils.upgrades, "UPGRADES_LIST", upgrade_functions) m.setattr(raiden.utils.upgrades, "RAIDEN_DB_VERSION", 19) UpgradeManager(db_filename=db_path, web3=web3).run() # Once restored, the state changes written above should be # in the restored database with SQLiteStorage(str(db_path)) as storage: state_change_record = storage.get_latest_state_change_by_data_field( FilteredDBQuery( filters=[{ "_type": "raiden.transfer.state_change.ActionInitChain" }], main_operator=Operator.NONE, inner_operator=Operator.NONE, )) assert state_change_record.data is not None
def test_deserialize_missing_attribute(): test_input = json.dumps( {"_type": f"{ClassWithInt.__module__}.ClassWithInt"}) with pytest.raises(SerializationError): JSONSerializer.deserialize(test_input)
def test_storage_close(): storage = SerializedSQLiteStorage(":memory:", JSONSerializer()) storage.close() with pytest.raises( RuntimeError): # attempt to close an already closed database storage.close()
def test_get_event_with_balance_proof(): """ All events which contain a balance proof must be found by when querying the database. """ serializer = JSONSerializer() storage = SerializedSQLiteStorage(":memory:", serializer) counter = itertools.count(1) partner_address = factories.make_address() balance_proof = make_balance_proof_from_counter(counter) lock_expired = SendLockExpired( recipient=partner_address, message_identifier=MessageID(next(counter)), balance_proof=balance_proof, secrethash=factories.make_secret_hash(next(counter)), canonical_identifier=balance_proof.canonical_identifier, ) locked_transfer = SendLockedTransfer( recipient=partner_address, message_identifier=MessageID(next(counter)), transfer=make_transfer_from_counter(counter), canonical_identifier=factories.make_canonical_identifier(), ) send_balance_proof = SendBalanceProof( recipient=partner_address, message_identifier=MessageID(next(counter)), payment_identifier=factories.make_payment_id(), token_address=factories.make_token_address(), secret=factories.make_secret(next(counter)), balance_proof=make_balance_proof_from_counter(counter), canonical_identifier=factories.make_canonical_identifier(), ) refund_transfer = SendRefundTransfer( recipient=partner_address, message_identifier=MessageID(next(counter)), transfer=make_transfer_from_counter(counter), canonical_identifier=factories.make_canonical_identifier(), ) events_balanceproofs = [ (lock_expired, lock_expired.balance_proof), (locked_transfer, locked_transfer.balance_proof), (send_balance_proof, send_balance_proof.balance_proof), (refund_transfer, refund_transfer.transfer.balance_proof), ] state_change = Block(BlockNumber(1), BlockGasLimit(1), factories.make_block_hash()) for event, _ in events_balanceproofs: state_change_identifiers = storage.write_state_changes([state_change]) storage.write_events(events=[(state_change_identifiers[0], event)]) for event, balance_proof in events_balanceproofs: event_record = get_event_with_balance_proof_by_balance_hash( storage=storage, canonical_identifier=balance_proof.canonical_identifier, balance_hash=balance_proof.balance_hash, recipient=partner_address, ) assert event_record assert event_record.data == event event_record = get_event_with_balance_proof_by_locksroot( storage=storage, canonical_identifier=balance_proof.canonical_identifier, recipient=event.recipient, locksroot=balance_proof.locksroot, ) assert event_record assert event_record.data == event # Checking that balance proof attribute can be accessed for all events. # Issue https://github.com/raiden-network/raiden/issues/3179 assert event_record.data.balance_proof == event.balance_proof storage.close()
def test_get_state_change_with_balance_proof(): """ All state changes which contain a balance proof must be found when querying the database. """ serializer = JSONSerializer() storage = SerializedSQLiteStorage(":memory:", serializer) counter = itertools.count() balance_proof = make_signed_balance_proof_from_counter(counter) lock_expired = ReceiveLockExpired( sender=balance_proof.sender, balance_proof=balance_proof, secrethash=factories.make_secret_hash(next(counter)), message_identifier=MessageID(next(counter)), ) received_balance_proof = make_signed_balance_proof_from_counter(counter) unlock = ReceiveUnlock( sender=received_balance_proof.sender, message_identifier=MessageID(next(counter)), secret=factories.make_secret(next(counter)), balance_proof=received_balance_proof, ) transfer = make_signed_transfer_from_counter(counter) transfer_refund = ReceiveTransferRefund( transfer=transfer, balance_proof=transfer.balance_proof, sender=transfer.balance_proof.sender, # pylint: disable=no-member ) transfer = make_signed_transfer_from_counter(counter) transfer_reroute = ActionTransferReroute( transfer=transfer, balance_proof=transfer.balance_proof, sender=transfer.balance_proof.sender, # pylint: disable=no-member secret=sha3(factories.make_secret(next(counter))), ) mediator_from_route, mediator_signed_transfer = make_from_route_from_counter( counter) action_init_mediator = ActionInitMediator( route_states=[ RouteState( route=[factories.make_address(), factories.make_address()], forward_channel_id=factories.make_channel_identifier(), ) ], from_hop=mediator_from_route, from_transfer=mediator_signed_transfer, balance_proof=mediator_signed_transfer.balance_proof, sender=mediator_signed_transfer.balance_proof.sender, # pylint: disable=no-member ) target_from_route, target_signed_transfer = make_from_route_from_counter( counter) action_init_target = ActionInitTarget( from_hop=target_from_route, transfer=target_signed_transfer, balance_proof=target_signed_transfer.balance_proof, sender=target_signed_transfer.balance_proof.sender, # pylint: disable=no-member ) statechanges_balanceproofs = [ (lock_expired, lock_expired.balance_proof), (unlock, unlock.balance_proof), (transfer_refund, transfer_refund.transfer.balance_proof), (transfer_reroute, transfer_reroute.transfer.balance_proof), (action_init_mediator, action_init_mediator.from_transfer.balance_proof), (action_init_target, action_init_target.transfer.balance_proof), ] assert storage.count_state_changes() == 0 state_change_ids = storage.write_state_changes( [state_change for state_change, _ in statechanges_balanceproofs]) assert storage.count_state_changes() == len(statechanges_balanceproofs) msg_in_order = "Querying must return state changes in order" stored_statechanges_records = storage.get_statechanges_records_by_range( RANGE_ALL_STATE_CHANGES) assert len(stored_statechanges_records) == 6, msg_in_order pair_elements = zip(statechanges_balanceproofs, state_change_ids, stored_statechanges_records) for statechange_balanceproof, statechange_id, record in pair_elements: assert record.data == statechange_balanceproof[0], msg_in_order assert record.state_change_identifier == statechange_id, msg_in_order # Make sure state changes are returned in the correct order in which they were stored stored_statechanges = storage.get_statechanges_by_range( Range( stored_statechanges_records[1].state_change_identifier, stored_statechanges_records[2].state_change_identifier, )) assert len(stored_statechanges) == 2 assert isinstance(stored_statechanges[0], ReceiveUnlock) assert isinstance(stored_statechanges[1], ReceiveTransferRefund) for state_change, balance_proof in statechanges_balanceproofs: state_change_record = get_state_change_with_balance_proof_by_balance_hash( storage=storage, canonical_identifier=balance_proof.canonical_identifier, sender=balance_proof.sender, balance_hash=balance_proof.balance_hash, ) assert state_change_record assert state_change_record.data == state_change state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=storage, canonical_identifier=balance_proof.canonical_identifier, sender=balance_proof.sender, locksroot=balance_proof.locksroot, ) assert state_change_record assert state_change_record.data == state_change storage.close()
def test_serialize_missing_attribute(): instance = ClassWithInt(1) instance.value = b"a" with pytest.raises(SerializationError): JSONSerializer.serialize(instance)
def test_serialize_wrong_type(): with pytest.raises(SerializationError): JSONSerializer.serialize([])
def test_deserialize_invalid_json(input_value): with pytest.raises(SerializationError): JSONSerializer.deserialize(input_value)
def test_deserialize_wrong_type(): with pytest.raises(SerializationError): JSONSerializer.deserialize("[]")