docs: @Override
      public synchronized void initiate(@Nullable KeyParameter userKey, ClientChannelProperties clientChannelProperties) throws ValueOutOfRangeException, InsufficientMoneyException {
          final NetworkParameters params = wallet.getParams();
          Transaction template = new Transaction(params);
          // We always place the client key before the server key because, if either side wants some privacy, they can
          // use a fresh key for the the multisig contract and nowhere else
          List<ECKey> keys = Lists.newArrayList(myKey, serverKey);
          // There is also probably a change output, but we don't bother shuffling them as it's obvious from the
          // format which one is the change. If we start obfuscating the change output better in future this may
          // be worth revisiting.
          TransactionOutput multisigOutput = template.addOutput(totalValue, ScriptBuilder.createMultiSigOutputScript(2, keys));
          if (multisigOutput.isDust())
              throw new ValueOutOfRangeException("totalValue too small to use");
          SendRequest req = SendRequest.forTx(template);
          req.coinSelector = AllowUnconfirmedCoinSelector.get();
          req.shuffleOutputs = false;   // TODO: Fix things so shuffling is usable.
          req = clientChannelProperties.modifyContractSendRequest(req);
          if (userKey != null) req.aesKey = userKey;
          wallet.completeTx(req);
          Coin multisigFee = req.tx.getFee();
          multisigContract = req.tx;
          // Build a refund transaction that protects us in the case of a bad server that's just trying to cause havoc
          // by locking up peoples money (perhaps as a precursor to a ransom attempt). We time lock it so the server
          // has an assurance that we cannot take back our money by claiming a refund before the channel closes - this
          // relies on the fact that since Bitcoin 0.8 time locked transactions are non-final. This will need to change
          // in future as it breaks the intended design of timelocking/tx replacement, but for now it simplifies this
          // specific protocol somewhat.
          refundTx = new Transaction(params);
          // don't disable lock time. the sequence will be included in the server's signature and thus won't be changeable.
          // by using this sequence value, we avoid extra full replace-by-fee and relative lock time processing.
          refundTx.addInput(multisigOutput).setSequenceNumber(TransactionInput.NO_SEQUENCE - 1L);
          refundTx.setLockTime(expiryTime);
          if (Context.get().isEnsureMinRequiredFee()) {
              // Must pay min fee.
              final Coin valueAfterFee = totalValue.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
              if (Transaction.MIN_NONDUST_OUTPUT.compareTo(valueAfterFee) > 0)
                  throw new ValueOutOfRangeException("totalValue too small to use");
              refundTx.addOutput(valueAfterFee, LegacyAddress.fromKey(params, myKey));
              refundFees = multisigFee.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
          } else {
              refundTx.addOutput(totalValue, LegacyAddress.fromKey(params, myKey));
              refundFees = multisigFee;
          }
          refundTx.getConfidence().setSource(TransactionConfidence.Source.SELF);
          log.info("initiated channel with multi-sig contract {}, refund {}", multisigContract.getTxId(),
                  refundTx.getTxId());
          stateMachine.transition(State.INITIATED);
          // Client should now call getIncompleteRefundTransaction() and send it to the server.
      }