def events_for_close(channel_state, block_number): events = list() if get_status(channel_state) in CHANNEL_STATES_PRIOR_TO_CLOSED: channel_state.close_transaction = TransactionExecutionStatus( block_number, None, None) close_event = ContractSendChannelClose( channel_state.identifier, channel_state.token_address, channel_state.partner_state.balance_proof, ) events.append(close_event) return events
def test_get_our_capacity_for_token_network(): test_state = factories.make_chain_state(number_of_channels=3) chain_state = test_state.chain_state test_state.channels[-1].close_transaction = TransactionExecutionStatus( started_block_number=chain_state.block_number, finished_block_number=chain_state.block_number, result=TransactionExecutionStatus.SUCCESS, ) expected_sum = sum(c.our_state.contract_balance for c in test_state.channels[:-1]) assert (views.get_our_deposits_for_token_network( chain_state=chain_state, token_network_registry_address=test_state. token_network_registry_address, token_address=test_state.token_address, ) == expected_sum)
def make_channel( our_balance=0, partner_balance=0, our_address=None, partner_address=None, token_address=None, token_network_identifier=None, channel_identifier=None, reveal_timeout=10, settle_timeout=50, ): our_address = our_address or make_address() partner_address = partner_address or make_address() token_address = token_address or make_address() token_network_identifier = token_network_identifier or make_address() channel_identifier = channel_identifier or make_channel_identifier() our_state = make_endstate(our_address, our_balance) partner_state = make_endstate(partner_address, partner_balance) opened_block_number = 10 open_transaction = TransactionExecutionStatus( None, opened_block_number, TransactionExecutionStatus.SUCCESS, ) close_transaction = None settle_transaction = None channel_state = NettingChannelState( identifier=channel_identifier, chain_id=UNIT_CHAIN_ID, token_address=token_address, token_network_identifier=token_network_identifier, reveal_timeout=reveal_timeout, settle_timeout=settle_timeout, our_state=our_state, partner_state=partner_state, open_transaction=open_transaction, close_transaction=close_transaction, settle_transaction=settle_transaction, ) return channel_state
def make_channel( our_balance=0, partner_balance=0, our_address=None, partner_address=None, token_address=None, channel_address=None, reveal_timeout=10, settle_timeout=50 ): our_address = our_address or make_address() partner_address = partner_address or make_address() token_address = token_address or make_address() channel_address = channel_address or make_address() our_state = make_endstate(our_address, our_balance) partner_state = make_endstate(partner_address, partner_balance) opened_block_number = 10 open_transaction = TransactionExecutionStatus( None, opened_block_number, TransactionExecutionStatus.SUCCESS, ) close_transaction = None settle_transaction = None channel_state = NettingChannelState( channel_address, token_address, reveal_timeout, settle_timeout, our_state, partner_state, open_transaction, close_transaction, settle_transaction, ) return channel_state
def handle_block(channel_state, state_change, block_number): assert state_change.block_number == block_number events = list() if get_status(channel_state) == CHANNEL_STATE_CLOSED: closed_block_number = channel_state.close_transaction.finished_block_number settlement_end = closed_block_number + channel_state.settle_timeout if state_change.block_number > settlement_end: channel_state.settle_transaction = TransactionExecutionStatus( state_change.block_number, None, None) event = ContractSendChannelSettle(channel_state.identifier) events.append(event) while is_deposit_confirmed(channel_state, block_number): order_deposit_transaction = heapq.heappop( channel_state.deposit_transaction_queue) apply_channel_newbalance( channel_state, order_deposit_transaction.transaction, ) return TransitionResult(channel_state, events)
def test_channelstate_filters(): test_state = factories.make_chain_state(number_of_channels=5) chain_state = test_state.chain_state token_network_registry_address = test_state.token_network_registry_address token_address = test_state.token_address channel_open, channel_closing, channel_closed, channel_settling, channel_settled = ( test_state.channels ) in_progress = TransactionExecutionStatus(started_block_number=chain_state.block_number) done = TransactionExecutionStatus( started_block_number=chain_state.block_number, finished_block_number=chain_state.block_number, result=TransactionExecutionStatus.SUCCESS, ) channel_closing.close_transaction = in_progress channel_closed.close_transaction = done channel_settling.close_transaction = done channel_settling.settle_transaction = in_progress channel_settled.close_transaction = done channel_settled.settle_transaction = done unknown_token = factories.make_address() assert ( views.get_channelstate_open( chain_state=chain_state, token_network_registry_address=token_network_registry_address, token_address=unknown_token, ) == [] ) opened = views.get_channelstate_open( chain_state=chain_state, token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert opened == [channel_open] closing = views.get_channelstate_closing( chain_state=chain_state, token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert closing == [channel_closing] closed = views.get_channelstate_closed( chain_state=chain_state, token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert closed == [channel_closed] settling = views.get_channelstate_settling( chain_state=chain_state, token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert settling == [channel_settling] settled = views.get_channelstate_settled( chain_state=chain_state, token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert settled == [channel_settled]
def test_invalid_timeouts(): token_address = factories.make_address() reveal_timeout = 5 settle_timeout = 10 identifier = factories.make_address() address1 = factories.make_address() address2 = factories.make_address() balance1 = 10 balance2 = 10 opened_transaction = TransactionExecutionStatus( None, 1, TransactionExecutionStatus.SUCCESS, ) closed_transaction = None settled_transaction = None our_state = NettingChannelEndState(address1, balance1) partner_state = NettingChannelEndState(address2, balance2) # do not allow a reveal timeout larger than the settle timeout with pytest.raises(ValueError): large_reveal_timeout = 50 small_settle_timeout = 49 NettingChannelState( identifier, token_address, large_reveal_timeout, small_settle_timeout, our_state, partner_state, opened_transaction, closed_transaction, settled_transaction, ) # TypeError: 'a', [], {} for invalid_value in (-1, 0, 1.1, 1.0): with pytest.raises(ValueError): NettingChannelState( identifier, token_address, invalid_value, settle_timeout, our_state, partner_state, opened_transaction, closed_transaction, settled_transaction, ) with pytest.raises(ValueError): NettingChannelState( identifier, token_address, reveal_timeout, invalid_value, our_state, partner_state, opened_transaction, closed_transaction, settled_transaction, )
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 _(properties, defaults=None) -> TransactionExecutionStatus: return TransactionExecutionStatus( **_properties_to_kwargs( properties, defaults or TRANSACTION_EXECUTION_STATUS_DEFAULTS), )
def get_channel_state( token_address, payment_network_identifier, token_network_address, reveal_timeout, payment_channel_proxy, opened_block_number, ): channel_details = payment_channel_proxy.detail() our_state = NettingChannelEndState( channel_details.participants_data.our_details.address, channel_details.participants_data.our_details.deposit, ) partner_state = NettingChannelEndState( channel_details.participants_data.partner_details.address, channel_details.participants_data.partner_details.deposit, ) identifier = payment_channel_proxy.channel_identifier settle_timeout = payment_channel_proxy.settle_timeout() closed_block_number = None # ignore bad open block numbers if opened_block_number <= 0: return None open_transaction = TransactionExecutionStatus( None, opened_block_number, TransactionExecutionStatus.SUCCESS, ) if closed_block_number: close_transaction = TransactionExecutionStatus( None, closed_block_number, TransactionExecutionStatus.SUCCESS, ) else: close_transaction = None # For the current implementation the channel is a smart contract that # will be killed on settle. settle_transaction = None channel = NettingChannelState( identifier=identifier, chain_id=channel_details.chain_id, token_address=token_address, payment_network_identifier=payment_network_identifier, token_network_identifier=token_network_address, reveal_timeout=reveal_timeout, settle_timeout=settle_timeout, our_state=our_state, partner_state=partner_state, open_transaction=open_transaction, close_transaction=close_transaction, settle_transaction=settle_transaction, ) return channel
def get_channel_state( token_address: TokenAddress, payment_network_identifier: PaymentNetworkID, token_network_address: TokenNetworkAddress, reveal_timeout: BlockTimeout, payment_channel_proxy, opened_block_number: BlockNumber, ): # Here we have to query the latest state because if we query with an older block # state (e.g. opened_block_number) the state may have been pruned which will # lead to an error. latest_block_hash = payment_channel_proxy.client.blockhash_from_blocknumber( "latest") channel_details = payment_channel_proxy.detail(latest_block_hash) our_state = NettingChannelEndState( channel_details.participants_data.our_details.address, channel_details.participants_data.our_details.deposit, ) partner_state = NettingChannelEndState( channel_details.participants_data.partner_details.address, channel_details.participants_data.partner_details.deposit, ) identifier = payment_channel_proxy.channel_identifier settle_timeout = payment_channel_proxy.settle_timeout() closed_block_number = payment_channel_proxy.close_block_number() # ignore bad open block numbers if opened_block_number <= 0: return None open_transaction = TransactionExecutionStatus( None, opened_block_number, TransactionExecutionStatus.SUCCESS) close_transaction: Optional[TransactionExecutionStatus] = None if closed_block_number: close_transaction = TransactionExecutionStatus( None, closed_block_number, TransactionExecutionStatus.SUCCESS) # For the current implementation the channel is a smart contract that # will be killed on settle. settle_transaction = None channel = NettingChannelState( canonical_identifier=CanonicalIdentifier( chain_identifier=channel_details.chain_id, token_network_address=token_network_address, channel_identifier=identifier, ), token_address=token_address, payment_network_identifier=payment_network_identifier, reveal_timeout=reveal_timeout, settle_timeout=settle_timeout, mediation_fee=MEDIATION_FEE, our_state=our_state, partner_state=partner_state, open_transaction=open_transaction, close_transaction=close_transaction, settle_transaction=settle_transaction, ) return channel
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 channel_from_nettingcontract(our_key, netting_contract, reveal_timeout): """ Create a `channel.Channel` for the `netting_contract`. Use this to make sure that both implementations (the smart contract and the python code) work in tandem.""" our_address = privatekey_to_address(our_key) token_address_hex = netting_contract.tokenAddress(sender=our_key) settle_timeout = netting_contract.settleTimeout(sender=our_key) address_balance = netting_contract.addressAndBalance(sender=our_key) address1_hex, balance1, address2_hex, balance2 = address_balance opened_block_number = netting_contract.opened(sender=our_key) closed_block_number = netting_contract.closed(sender=our_key) settled_block_number = None identifier = address_decoder(netting_contract.address) token_address = normalize_address(token_address_hex) address1 = normalize_address(address1_hex) address2 = normalize_address(address2_hex) if our_address == address1: our_balance = balance1 partner_address = address2 partner_balance = balance2 else: our_balance = balance2 partner_address = address1 partner_balance = balance1 our_state = NettingChannelEndState( our_address, our_balance, ) partner_state = NettingChannelEndState( partner_address, partner_balance, ) open_transaction = TransactionExecutionStatus( None, opened_block_number, TransactionExecutionStatus.SUCCESS, ) close_transaction = None if closed_block_number: close_transaction = TransactionExecutionStatus( None, closed_block_number, TransactionExecutionStatus.SUCCESS, ) settle_transaction = None if settled_block_number: settle_transaction = TransactionExecutionStatus( None, settled_block_number, TransactionExecutionStatus.SUCCESS, ) channel = NettingChannelState( identifier, token_address, reveal_timeout, settle_timeout, our_state, partner_state, open_transaction, close_transaction, settle_transaction, ) return channel
def get_channel_state( token_address, token_network_address, reveal_timeout, netting_channel_proxy, ): channel_details = netting_channel_proxy.detail() our_state = NettingChannelEndState( channel_details['our_address'], channel_details['our_balance'], ) partner_state = NettingChannelEndState( channel_details['partner_address'], channel_details['partner_balance'], ) identifier = netting_channel_proxy.address settle_timeout = channel_details['settle_timeout'] opened_block_number = netting_channel_proxy.opened() closed_block_number = netting_channel_proxy.closed() # ignore bad open block numbers if opened_block_number <= 0: return None # ignore negative closed block numbers if closed_block_number < 0: return None open_transaction = TransactionExecutionStatus( None, opened_block_number, TransactionExecutionStatus.SUCCESS, ) if closed_block_number: close_transaction = TransactionExecutionStatus( None, closed_block_number, TransactionExecutionStatus.SUCCESS, ) else: close_transaction = None # For the current implementation the channel is a smart contract that # will be killed on settle. settle_transaction = None channel = NettingChannelState( identifier, token_address, token_network_address, reveal_timeout, settle_timeout, our_state, partner_state, open_transaction, close_transaction, settle_transaction, ) return channel
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]