def handle_block(chain_state: ChainState, state_change: Block) -> TransitionResult[ChainState]: block_number = state_change.block_number chain_state.block_number = block_number chain_state.block_hash = state_change.block_hash # Subdispatch Block state change channels_result = subdispatch_to_all_channels( chain_state=chain_state, state_change=state_change, block_number=block_number, block_hash=chain_state.block_hash, ) transfers_result = subdispatch_to_all_lockedtransfers(chain_state, state_change) events = channels_result.events + transfers_result.events return TransitionResult(chain_state, events)
def handle_update_transport_authdata( chain_state: ChainState, state_change: ActionUpdateTransportAuthData, ) -> TransitionResult[ChainState]: assert chain_state is not None, 'chain_state must be set' chain_state.last_transport_authdata = state_change.auth_data return TransitionResult(chain_state, list())
def initialize(self, block_number, random, random_seed): self.random_seed = random_seed self.block_number = block_number self.block_hash = factories.make_block_hash() self.random = random self.private_key, self.address = factories.make_privkey_address() self.chain_state = ChainState( pseudo_random_generator=self.random, block_number=self.block_number, block_hash=self.block_hash, our_address=self.address, chain_id=factories.UNIT_CHAIN_ID, ) self.token_network_id = factories.make_address() self.token_id = factories.make_address() self.token_network_state = TokenNetworkState(self.token_network_id, self.token_id) self.payment_network_id = factories.make_payment_network_identifier() self.payment_network_state = PaymentNetworkState( self.payment_network_id, [self.token_network_state], ) self.chain_state.identifiers_to_paymentnetworks[ self.payment_network_id] = self.payment_network_state return self.new_channel_with_transaction()
def initialize(self, block_number, random, random_seed): self.random_seed = random_seed self.block_number = block_number self.block_hash = factories.make_block_hash() self.random = random self.private_key, self.address = factories.make_privkey_address() self.chain_state = ChainState( pseudo_random_generator=self.random, block_number=self.block_number, block_hash=self.block_hash, our_address=self.address, chain_id=factories.UNIT_CHAIN_ID, ) self.token_network_id = factories.UNIT_TOKEN_NETWORK_ADDRESS self.token_id = factories.UNIT_TOKEN_ADDRESS self.token_network_state = TokenNetworkState(self.token_network_id, self.token_id) self.payment_network_id = factories.make_payment_network_identifier() self.payment_network_state = PaymentNetworkState( self.payment_network_id, [self.token_network_state]) self.chain_state.identifiers_to_paymentnetworks[ self.payment_network_id] = self.payment_network_state self.chain_state.tokennetworkaddresses_to_paymentnetworkaddresses[ self.token_network_id] = self.payment_network_id channels = [ self.new_channel_with_transaction() for _ in range(self.initial_number_of_channels) ] return multiple(*channels)
def handle_chain_init(chain_state, state_change): chain_state = ChainState( state_change.pseudo_random_generator, state_change.block_number, state_change.chain_id, ) events = list() return TransitionResult(chain_state, events)
def chain_state(our_address): block_number = 1 return ChainState( random.Random(), block_number, our_address, UNIT_CHAIN_ID, )
def chain_state(our_address): block_number = BlockNumber(1) return ChainState( pseudo_random_generator=random.Random(), block_number=block_number, block_hash=factories.make_block_hash(), our_address=our_address, chain_id=UNIT_CHAIN_ID, )
def handle_node_change_network_state( chain_state: ChainState, state_change: ActionChangeNodeNetworkState, ) -> TransitionResult: events = list() node_address = state_change.node_address network_state = state_change.network_state chain_state.nodeaddresses_to_networkstates[node_address] = network_state return TransitionResult(chain_state, events)
def handle_chain_init( chain_state: ChainState, state_change: ActionInitChain, ) -> TransitionResult: chain_state = ChainState( state_change.pseudo_random_generator, state_change.block_number, state_change.our_address, state_change.chain_id, ) events = list() return TransitionResult(chain_state, events)
def handle_new_payment_network( chain_state: ChainState, state_change: ContractReceiveNewPaymentNetwork, ) -> TransitionResult: events = list() payment_network = state_change.payment_network payment_network_identifier = payment_network.address if payment_network_identifier not in chain_state.identifiers_to_paymentnetworks: chain_state.identifiers_to_paymentnetworks[payment_network_identifier] = payment_network return TransitionResult(chain_state, events)
def handle_contract_receive_new_token_network_registry( chain_state: ChainState, state_change: ContractReceiveNewTokenNetworkRegistry ) -> TransitionResult[ChainState]: events: List[Event] = list() token_network_registry = state_change.token_network_registry token_network_registry_address = TokenNetworkRegistryAddress(token_network_registry.address) if token_network_registry_address not in chain_state.identifiers_to_tokennetworkregistries: chain_state.identifiers_to_tokennetworkregistries[ token_network_registry_address ] = token_network_registry return TransitionResult(chain_state, events)
def handle_chain_init( chain_state: ChainState, state_change: ActionInitChain) -> TransitionResult[ChainState]: if chain_state is None: chain_state = ChainState( pseudo_random_generator=state_change.pseudo_random_generator, block_number=state_change.block_number, block_hash=state_change.block_hash, our_address=state_change.our_address, chain_id=state_change.chain_id, ) events: List[Event] = list() return TransitionResult(chain_state, events)
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() initial_state = ChainState( 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, ) wal = WriteAheadLog( state=initial_state, storage=SerializedSQLiteStorage(":memory:", serializer), state_transition=state_transition, ) self.wal = wal self.transport = Mock()
def _initialize_monitoring_services_queue(self, chain_state: ChainState): """Send the monitoring requests for all current balance proofs. Note: The node must always send the *received* balance proof to the monitoring service, *before* sending its own locked transfer forward. If the monitoring service is updated after, then the following can happen: For a transfer A-B-C where this node is B - B receives T1 from A and processes it - B forwards its T2 to C * B crashes (the monitoring service is not updated) For the above scenario, the monitoring service would not have the latest balance proof received by B from A available with the lock for T1, but C would. If the channel B-C is closed and B does not come back online in time, the funds for the lock L1 can be lost. During restarts the rationale from above has to be replicated. Because the initialization code *is not* the same as the event handler. This means the balance proof updates must be done prior to the processing of the message queues. """ msg = ( 'Transport was started before the monitoring service queue was updated. ' 'This can lead to safety issue. node:{self!r}') assert not self.transport, msg msg = ( 'The node state was not yet recovered, cant read balance proofs. node:{self!r}' ) assert self.wal, msg current_balance_proofs = views.detect_balance_proof_change( old_state=ChainState( pseudo_random_generator=chain_state.pseudo_random_generator, block_number=GENESIS_BLOCK_NUMBER, block_hash=constants.EMPTY_HASH, our_address=chain_state.our_address, chain_id=chain_state.chain_id, ), current_state=chain_state, ) for balance_proof in current_balance_proofs: update_services_from_balance_proof(self, chain_state, balance_proof)
def initialize_all(self, block_number, random, random_seed): self.random_seed = random_seed self.block_number = block_number self.block_hash = factories.make_block_hash() self.random = random self.token_network_address = factories.UNIT_TOKEN_NETWORK_ADDRESS self.token_id = factories.UNIT_TOKEN_ADDRESS self.token_network_state = TokenNetworkState( address=self.token_network_address, token_address=self.token_id, network_graph=TokenNetworkGraphState(self.token_network_address), ) self.token_network_registry_address = factories.make_token_network_registry_address() self.token_network_registry_state = TokenNetworkRegistryState( self.token_network_registry_address, [self.token_network_state] ) channels = [] for _ in range(self.number_of_clients): private_key, address = factories.make_privkey_address() self.address_to_privkey[address] = private_key chain_state = ChainState( pseudo_random_generator=self.random, block_number=self.block_number, block_hash=self.block_hash, our_address=address, chain_id=factories.UNIT_CHAIN_ID, ) chain_state.identifiers_to_tokennetworkregistries[ self.token_network_registry_address ] = self.token_network_registry_state chain_state.tokennetworkaddresses_to_tokennetworkregistryaddresses[ self.token_network_address ] = self.token_network_registry_address self.address_to_client[address] = Client(chain_state=chain_state) channels.extend( self.new_channel_with_transaction(client_address=address) for _ in range(self.initial_number_of_channels) ) return multiple(*channels)
def inplace_delete_message_queue( chain_state: ChainState, state_change: Union[ReceiveDelivered, ReceiveProcessed], queueid: QueueIdentifier, ) -> None: """ Filter messages from queue, if the queue becomes empty, cleanup the queue itself. """ queue = chain_state.queueids_to_queues.get(queueid) if not queue: return inplace_delete_message(message_queue=queue, state_change=state_change) if len(queue) == 0: del chain_state.queueids_to_queues[queueid] else: chain_state.queueids_to_queues[queueid] = queue
def initialize(self, block_number, random, random_seed): self.random_seed = random_seed self.block_number = block_number self.random = random self.private_key, self.address = factories.make_privkey_address() self.chain_state = ChainState( self.random, self.block_number, self.address, factories.UNIT_CHAIN_ID, ) self.token_network_id = factories.make_address() self.token_id = factories.make_address() self.token_network_state = TokenNetworkState(self.token_network_id, self.token_id) self.payment_network_id = factories.make_payment_network_identifier() self.payment_network_state = PaymentNetworkState( self.payment_network_id, [self.token_network_state], ) self.chain_state.identifiers_to_paymentnetworks[ self.payment_network_id] = self.payment_network_state self.channels = list() for partner_address in self.channels_with: channel = factories.make_channel( our_balance=1000, partner_balance=1000, token_network_identifier=self.token_network_id, our_address=self.address, partner_address=partner_address, ) channel_new_state_change = ContractReceiveChannelNew( factories.make_transaction_hash(), self.token_network_id, channel, self.block_number, ) node.state_transition(self.chain_state, channel_new_state_change) self.channels.append(channel)
def inplace_delete_message_queue( chain_state: ChainState, state_change: StateChange, queueid: QueueIdentifier, ): """ Filter messages from queue, if the queue becomes empty, cleanup the queue itself. """ queue = chain_state.queueids_to_queues.get(queueid) if not queue: return inplace_delete_message( queue, state_change, ) if len(queue) == 0: del chain_state.queueids_to_queues[queueid] else: chain_state.queueids_to_queues[queueid] = queue
def handle_block( chain_state: ChainState, state_change: Block, ) -> TransitionResult: block_number = state_change.block_number chain_state.block_number = block_number # Subdispatch Block state change channels_result = subdispatch_to_all_channels( chain_state, state_change, block_number, ) transfers_result = subdispatch_to_all_lockedtransfers( chain_state, state_change, ) events = channels_result.events + transfers_result.events return TransitionResult(chain_state, events)
def handle_delivered(chain_state: ChainState, state_change: ReceiveDelivered) -> TransitionResult: # TODO: improve the complexity of this algorithm queueids_to_remove = [] for queueid, queue in chain_state.queueids_to_queues.items(): if queueid.channel_identifier == CHANNEL_IDENTIFIER_GLOBAL_QUEUE: filtered_queue = [ message for message in queue if message.message_identifier != state_change.message_identifier ] if not filtered_queue: queueids_to_remove.append(queueid) else: chain_state.queueids_to_queues[queueid] = filtered_queue for queueid in queueids_to_remove: del chain_state.queueids_to_queues[queueid] return TransitionResult(chain_state, [])
def handle_node_change_network_state( chain_state: ChainState, state_change: ActionChangeNodeNetworkState, ) -> TransitionResult[ChainState]: events: List[Event] = list() node_address = state_change.node_address network_state = state_change.network_state chain_state.nodeaddresses_to_networkstates[node_address] = network_state for secrethash, subtask in chain_state.payment_mapping.secrethashes_to_task.items( ): result = subdispatch_mediatortask( chain_state=chain_state, state_change=state_change, secrethash=secrethash, token_network_identifier=subtask.token_network_identifier, ) events.extend(result.events) return TransitionResult(chain_state, events)
def handle_action_change_node_network_state( chain_state: ChainState, state_change: ActionChangeNodeNetworkState ) -> TransitionResult[ChainState]: events: List[Event] = list() node_address = state_change.node_address network_state = state_change.network_state chain_state.nodeaddresses_to_networkstates[node_address] = network_state for secrethash, subtask in list(chain_state.payment_mapping.secrethashes_to_task.items()): # This typecheck would not have been needed if token_network_address, a common attribute # for all TransferTasks was part of the TransferTasks superclass. typecheck(subtask, (InitiatorTask, MediatorTask, TargetTask)) result = subdispatch_mediatortask( chain_state=chain_state, state_change=state_change, token_network_address=subtask.token_network_address, secrethash=secrethash, ) events.extend(result.events) return TransitionResult(chain_state, events)
def test_detect_balance_proof_change(): prng = random.Random() block_hash = factories.make_block_hash() old = ChainState( pseudo_random_generator=prng, block_number=1, block_hash=block_hash, our_address=2, chain_id=3, ) new = ChainState( pseudo_random_generator=prng, block_number=1, block_hash=block_hash, our_address=2, chain_id=3, ) def diff(): return list(detect_balance_proof_change(old, new)) assert len(diff()) == 0 payment_network = PaymentNetworkState(b'x', []) payment_network_copy = deepcopy(payment_network) new.identifiers_to_paymentnetworks['a'] = payment_network assert len(diff()) == 0 token_network = TokenNetworkState(b'a', b'a') token_network_copy = deepcopy(token_network) payment_network.tokenidentifiers_to_tokennetworks['a'] = token_network assert len(diff()) == 0 channel = NettingChannelState( canonical_identifier=factories.make_canonical_identifier(), token_address=b'a', payment_network_identifier=1, reveal_timeout=1, settle_timeout=2, mediation_fee=0, our_state=None, partner_state=None, open_transaction=TransactionExecutionStatus(result='success'), settle_transaction=None, update_transaction=None, close_transaction=None, ) channel_copy = deepcopy(channel) token_network.channelidentifiers_to_channels['a'] = channel our_state = NettingChannelEndState(address=b'b', balance=1) our_state_copy = deepcopy(our_state) partner_state = NettingChannelEndState(address=b'a', balance=0) partner_state_copy = deepcopy(partner_state) channel.our_state = our_state channel.partner_state = partner_state assert len(diff()) == 0 balance_proof = object() partner_state.balance_proof = balance_proof assert len(diff()) == 1 old.identifiers_to_paymentnetworks['a'] = payment_network_copy assert len(diff()) == 1 payment_network_copy.tokenidentifiers_to_tokennetworks[ 'a'] = token_network_copy assert len(diff()) == 1 token_network_copy.channelidentifiers_to_channels['a'] = channel_copy channel_copy.partner_state = partner_state_copy assert len(diff()) == 1 channel_copy.partner_state.balance_proof = balance_proof assert len(diff()) == 0 channel_copy.partner_state.balance_proof = object() assert len(diff()) == 1 assert diff() == [balance_proof] # check our_state BP changes channel_copy.partner_state.balance_proof = balance_proof assert len(diff()) == 0 channel.our_state.balance_proof = object() channel_copy.our_state = our_state_copy assert len(diff()) == 1 assert diff() == [channel.our_state.balance_proof] channel_copy.our_state.balance_proof = channel.our_state.balance_proof assert len(diff()) == 0
def handle_update_transport_authdata( chain_state: ChainState, state_change: ActionUpdateTransportAuthData, ) -> TransitionResult: chain_state.last_transport_authdata = state_change.auth_data return TransitionResult(chain_state, list())
def handle_update_transport_synctoken( chain_state: ChainState, state_change: ActionUpdateTransportSyncToken, ) -> TransitionResult: chain_state.last_transport_synctoken = state_change.sync_token return TransitionResult(chain_state, list())
def test_detect_balance_proof_change(): prng = random.Random() block_hash = make_block_hash() our_address = make_address() empty_chain = ChainState( pseudo_random_generator=prng, block_number=1, block_hash=block_hash, our_address=our_address, chain_id=3, ) assert empty(detect_balance_proof_change(empty_chain, empty_chain)), MSG_NO_CHANGE assert empty( detect_balance_proof_change(empty_chain, deepcopy(empty_chain))), MSG_NO_CHANGE token_network_registry_address = make_address() chain_with_registry_no_bp = deepcopy(empty_chain) chain_with_registry_no_bp.identifiers_to_tokennetworkregistries[ token_network_registry_address] = TokenNetworkRegistryState( token_network_registry_address, []) assert empty( detect_balance_proof_change(empty_chain, chain_with_registry_no_bp)), MSG_NO_CHANGE assert empty( detect_balance_proof_change( chain_with_registry_no_bp, deepcopy(chain_with_registry_no_bp))), MSG_NO_CHANGE token_network_address = make_address() token_address = make_address() chain_with_token_network_no_bp = deepcopy(chain_with_registry_no_bp) chain_with_token_network_no_bp.identifiers_to_tokennetworkregistries[ token_network_registry_address].tokennetworkaddresses_to_tokennetworks[ token_network_address] = TokenNetworkState( address=token_network_address, token_address=token_address, network_graph=TokenNetworkGraphState(token_network_address), ) assert empty( detect_balance_proof_change( empty_chain, chain_with_token_network_no_bp)), MSG_NO_CHANGE assert empty( detect_balance_proof_change( chain_with_registry_no_bp, chain_with_token_network_no_bp)), MSG_NO_CHANGE assert empty( detect_balance_proof_change( chain_with_token_network_no_bp, deepcopy(chain_with_token_network_no_bp))), MSG_NO_CHANGE partner_address = make_address() canonical_identifier = make_canonical_identifier() channel_no_bp = NettingChannelState( canonical_identifier=canonical_identifier, token_address=token_address, token_network_registry_address=token_network_registry_address, reveal_timeout=1, settle_timeout=2, our_state=NettingChannelEndState(address=our_address, contract_balance=1), partner_state=NettingChannelEndState(address=partner_address, contract_balance=0), open_transaction=TransactionExecutionStatus(result="success"), settle_transaction=None, update_transaction=None, close_transaction=None, fee_schedule=FeeScheduleState(), ) chain_with_channel_no_bp = deepcopy(chain_with_token_network_no_bp) chain_with_token_network_no_bp.identifiers_to_tokennetworkregistries[ token_network_registry_address].tokennetworkaddresses_to_tokennetworks[ token_network_address].channelidentifiers_to_channels[ canonical_identifier.channel_identifier] = channel_no_bp assert empty( detect_balance_proof_change(empty_chain, chain_with_channel_no_bp)), MSG_NO_CHANGE assert empty( detect_balance_proof_change(chain_with_registry_no_bp, chain_with_channel_no_bp)), MSG_NO_CHANGE assert empty( detect_balance_proof_change(chain_with_token_network_no_bp, chain_with_channel_no_bp)), MSG_NO_CHANGE assert empty( detect_balance_proof_change( chain_with_channel_no_bp, deepcopy(chain_with_channel_no_bp))), MSG_NO_CHANGE channel_with_sent_bp = deepcopy(channel_no_bp) channel_with_sent_bp.our_state.balance_proof = create( BalanceProofUnsignedState) chain_with_sent_bp = deepcopy(chain_with_token_network_no_bp) chain_with_sent_bp.identifiers_to_tokennetworkregistries[ token_network_registry_address].tokennetworkaddresses_to_tokennetworks[ token_network_address].channelidentifiers_to_channels[ canonical_identifier.channel_identifier] = channel_with_sent_bp assert not empty( detect_balance_proof_change( empty_chain, chain_with_sent_bp)), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change( chain_with_registry_no_bp, chain_with_sent_bp)), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change( chain_with_token_network_no_bp, chain_with_sent_bp)), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change( chain_with_channel_no_bp, chain_with_sent_bp)), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert empty( detect_balance_proof_change( chain_with_sent_bp, deepcopy(chain_with_sent_bp))), MSG_NO_CHANGE channel_with_received_bp = deepcopy(channel_no_bp) channel_with_received_bp.partner_state.balance_proof = create( BalanceProofUnsignedState) chain_with_received_bp = deepcopy(chain_with_token_network_no_bp) chain_with_received_bp.identifiers_to_tokennetworkregistries[ token_network_registry_address].tokennetworkaddresses_to_tokennetworks[ token_network_address].channelidentifiers_to_channels[ canonical_identifier.channel_identifier] = channel_with_sent_bp # asserting with `channel_with_received_bp` and `channel_with_sent_bp` # doesn't make sense, because one of the balance proofs would have to # disappear (which is a bug) assert not empty( detect_balance_proof_change( empty_chain, chain_with_received_bp)), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change( chain_with_registry_no_bp, chain_with_received_bp)), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change( chain_with_token_network_no_bp, chain_with_received_bp)), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change( chain_with_channel_no_bp, chain_with_received_bp)), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert empty( detect_balance_proof_change( chain_with_received_bp, deepcopy(chain_with_received_bp))), MSG_NO_CHANGE chain_with_sent_and_received_bp = deepcopy(chain_with_token_network_no_bp) ta_to_tn = chain_with_sent_and_received_bp.identifiers_to_tokennetworkregistries channel_with_sent_and_recived_bp = ( ta_to_tn[token_network_registry_address]. tokennetworkaddresses_to_tokennetworks[token_network_address]. channelidentifiers_to_channels[canonical_identifier.channel_identifier] ) channel_with_sent_and_recived_bp.partner_state.balance_proof = deepcopy( channel_with_received_bp.partner_state.balance_proof) channel_with_sent_and_recived_bp.our_state.balance_proof = deepcopy( channel_with_received_bp.our_state.balance_proof) assert not empty( detect_balance_proof_change(empty_chain, chain_with_sent_and_received_bp) ), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change(chain_with_registry_no_bp, chain_with_sent_and_received_bp) ), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change(chain_with_token_network_no_bp, chain_with_sent_and_received_bp) ), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change(chain_with_channel_no_bp, chain_with_sent_and_received_bp) ), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change(chain_with_received_bp, chain_with_sent_and_received_bp) ), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert not empty( detect_balance_proof_change(chain_with_sent_bp, chain_with_sent_and_received_bp) ), MSG_BALANCE_PROOF_SHOULD_BE_DETECTED assert empty( detect_balance_proof_change( chain_with_sent_and_received_bp, deepcopy(chain_with_sent_and_received_bp))), MSG_NO_CHANGE
def test_detect_balance_proof_change(): prng = random.Random() block_hash = factories.make_block_hash() old = ChainState( pseudo_random_generator=prng, block_number=1, block_hash=block_hash, our_address=2, chain_id=3, ) new = ChainState( pseudo_random_generator=prng, block_number=1, block_hash=block_hash, our_address=2, chain_id=3, ) def diff(): return list(detect_balance_proof_change(old, new)) assert len(diff()) == 0 payment_network = PaymentNetworkState(b'x', []) payment_network_copy = deepcopy(payment_network) new.identifiers_to_paymentnetworks['a'] = payment_network assert len(diff()) == 0 token_network = TokenNetworkState(b'a', b'a') token_network_copy = deepcopy(token_network) payment_network.tokenidentifiers_to_tokennetworks['a'] = token_network assert len(diff()) == 0 channel = NettingChannelState( 1, 0, b'a', 1, 1, 1, 2, None, None, TransactionExecutionStatus(result='success'), ) channel_copy = deepcopy(channel) token_network.channelidentifiers_to_channels['a'] = channel partner_state = NettingChannelEndState(b'a', 0) partner_state_copy = deepcopy(partner_state) channel.partner_state = partner_state assert len(diff()) == 0 balance_proof = object() partner_state.balance_proof = balance_proof assert len(diff()) == 1 old.identifiers_to_paymentnetworks['a'] = payment_network_copy assert len(diff()) == 1 payment_network_copy.tokenidentifiers_to_tokennetworks[ 'a'] = token_network_copy assert len(diff()) == 1 token_network_copy.channelidentifiers_to_channels['a'] = channel_copy channel_copy.partner_state = partner_state_copy assert len(diff()) == 1 channel_copy.partner_state.balance_proof = balance_proof assert len(diff()) == 0 channel_copy.partner_state.balance_proof = object() assert len(diff()) == 1 assert diff() == [balance_proof]