def create_sendmediatedtransfer(channel_state, initiator, target, amount, identifier, expiration, secrethash): our_state = channel_state.our_state partner_state = channel_state.partner_state our_balance_proof = our_state.balance_proof msg = 'caller must make sure there is enough balance' assert amount <= get_distributable(our_state, partner_state), msg msg = 'caller must make sure the channel is open' assert get_status(channel_state) == CHANNEL_STATE_OPENED, msg lock = HashTimeLockState( amount, expiration, secrethash, ) merkletree = compute_merkletree_with( channel_state.our_state.merkletree, lock.lockhash, ) # The caller must ensure the same lock is not being used twice assert merkletree, 'lock is already registered' locksroot = merkleroot(merkletree) if our_balance_proof: transferred_amount = our_balance_proof.transferred_amount else: transferred_amount = 0 token = channel_state.token_address nonce = get_next_nonce(channel_state.our_state) recipient = channel_state.partner_state.address balance_proof = BalanceProofUnsignedState( nonce, transferred_amount, locksroot, channel_state.identifier, ) locked_transfer = LockedTransferUnsignedState( identifier, token, balance_proof, lock, initiator, target, ) mediatedtransfer = SendMediatedTransfer( locked_transfer, recipient, ) return mediatedtransfer, merkletree
def try_new_route(state): assert state.route is None, 'cannot try a new route while one is being used' # TODO: # - Route ranking. An upper layer should rate each route to optimize # the fee price/quality of each route and add a rate from in the range # [0.0,1.0]. # - Add in a policy per route: # - filtering, e.g. so the user may have a per route maximum transfer # value based on fixed value or reputation. # - reveal time computation # - These policy details are better hidden from this implementation and # changes should be applied through the use of Route state changes. # Find a single route that may fulfill the request, this uses a single # route intentionally try_route = None while state.routes.available_routes: route = state.routes.available_routes.pop(0) if route.available_balance < state.transfer.amount: state.routes.ignored_routes.append(route) else: try_route = route break if try_route is None: # No available route has sufficient balance for the current transfer, # cancel it. # # At this point we can just discard all the state data, this is only # valid because we are the initiator and we know that the secret was # not released. cancel = EventTransferFailed( identifier=state.transfer.identifier, reason='no route available', ) iteration = TransitionResult(None, [cancel]) else: state.route = try_route secret = state.random_generator.next() hashlock = sha3(secret) # The initiator doesn't need to learn the secret, so there is no need # to decrement reveal_timeout from the lock timeout. # # A value larger than settle_timeout could be used but wouldn't # improve, since the next hop will take settle_timeout as an upper # limit for expiration. lock_expiration = state.block_number + try_route.settle_timeout identifier = state.transfer.identifier transfer = LockedTransferState( identifier, state.transfer.amount, state.transfer.token, state.transfer.target, lock_expiration, hashlock, secret, ) message = SendMediatedTransfer( transfer.identifier, transfer.token, transfer.amount, transfer.hashlock, transfer.target, lock_expiration, try_route.node_address, ) state.transfer = transfer state.message = message iteration = TransitionResult(state, [message]) return iteration