@Test
 public void watching() throws UnreadableWalletException {
   ECKey key1 = new ECKey();
   ECKey pub = ECKey.fromPublicOnly(key1.getPubKeyPoint());
   chain.importKeys(pub);
   assertEquals(1, chain.numKeys());
   List<Protos.Key> keys = chain.serializeToProtobuf();
   assertEquals(1, keys.size());
   assertTrue(keys.get(0).hasPublicKey());
   assertFalse(keys.get(0).hasSecretBytes());
   chain = BasicKeyChain.fromProtobufUnencrypted(keys);
   assertEquals(1, chain.numKeys());
   assertFalse(chain.findKeyFromPubKey(pub.getPubKey()).hasPrivKey());
 }
  /**
   * Called when the client provides the refund transaction. The refund transaction must have one
   * input from the multisig contract (that we don't have yet) and one output that the client
   * creates to themselves. This object will later be modified when we start getting paid.
   *
   * @param refundTx The refund transaction, this object will be mutated when payment is
   *     incremented.
   * @param clientMultiSigPubKey The client's pubkey which is required for the multisig output
   * @return Our signature that makes the refund transaction valid
   * @throws VerificationException If the transaction isnt valid or did not meet the requirements of
   *     a refund transaction.
   */
  public synchronized byte[] provideRefundTransaction(
      Transaction refundTx, byte[] clientMultiSigPubKey) throws VerificationException {
    checkNotNull(refundTx);
    checkNotNull(clientMultiSigPubKey);
    checkState(state == State.WAITING_FOR_REFUND_TRANSACTION);
    log.info("Provided with refund transaction: {}", refundTx);
    // Do a few very basic syntax sanity checks.
    refundTx.verify();
    // Verify that the refund transaction has a single input (that we can fill to sign the multisig
    // output).
    if (refundTx.getInputs().size() != 1)
      throw new VerificationException("Refund transaction does not have exactly one input");
    // Verify that the refund transaction has a time lock on it and a sequence number of zero.
    if (refundTx.getInput(0).getSequenceNumber() != 0)
      throw new VerificationException("Refund transaction's input's sequence number is non-0");
    if (refundTx.getLockTime() < minExpireTime)
      throw new VerificationException("Refund transaction has a lock time too soon");
    // Verify the transaction has one output (we don't care about its contents, its up to the
    // client)
    // Note that because we sign with SIGHASH_NONE|SIGHASH_ANYOENCANPAY the client can later add
    // more outputs and
    // inputs, but we will need only one output later to create the paying transactions
    if (refundTx.getOutputs().size() != 1)
      throw new VerificationException("Refund transaction does not have exactly one output");

    refundTransactionUnlockTimeSecs = refundTx.getLockTime();

    // Sign the refund tx with the scriptPubKey and return the signature. We don't have the spending
    // transaction
    // so do the steps individually.
    clientKey = ECKey.fromPublicOnly(clientMultiSigPubKey);
    Script multisigPubKey =
        ScriptBuilder.createMultiSigOutputScript(2, ImmutableList.of(clientKey, serverKey));
    // We are really only signing the fact that the transaction has a proper lock time and don't
    // care about anything
    // else, so we sign SIGHASH_NONE and SIGHASH_ANYONECANPAY.
    TransactionSignature sig =
        refundTx.calculateSignature(0, serverKey, multisigPubKey, Transaction.SigHash.NONE, true);
    log.info("Signed refund transaction.");
    this.clientOutput = refundTx.getOutput(0);
    state = State.WAITING_FOR_MULTISIG_CONTRACT;
    return sig.encodeToBitcoin();
  }
 PaymentChannelServerState(
     StoredServerChannel storedServerChannel, Wallet wallet, TransactionBroadcaster broadcaster)
     throws VerificationException {
   synchronized (storedServerChannel) {
     this.wallet = checkNotNull(wallet);
     this.broadcaster = checkNotNull(broadcaster);
     this.multisigContract = checkNotNull(storedServerChannel.contract);
     this.multisigScript = multisigContract.getOutput(0).getScriptPubKey();
     this.clientKey = ECKey.fromPublicOnly(multisigScript.getChunks().get(1).data);
     this.clientOutput = checkNotNull(storedServerChannel.clientOutput);
     this.refundTransactionUnlockTimeSecs = storedServerChannel.refundTransactionUnlockTimeSecs;
     this.serverKey = checkNotNull(storedServerChannel.myKey);
     this.totalValue = multisigContract.getOutput(0).getValue();
     this.bestValueToMe = checkNotNull(storedServerChannel.bestValueToMe);
     this.bestValueSignature = storedServerChannel.bestValueSignature;
     checkArgument(bestValueToMe.equals(Coin.ZERO) || bestValueSignature != null);
     this.storedServerChannel = storedServerChannel;
     storedServerChannel.state = this;
     this.state = State.READY;
   }
 }
  private void doMigrateFrom_v1_0_262(Context context, NetworkParameters migrationParams)
      throws IOException {
    Log.d(TAG, "********* MIGRATION FROM v1.0.262 - " + migrationParams.getId() + "*********");

    final File rootDir = context.getFilesDir();
    final File storageDir;
    final File archiveDir = new File(rootDir, "archive_" + System.currentTimeMillis());
    final File walletFile;
    final File chainFile;

    final File newWalletFile;
    final File newChainFile;

    if (ClientUtils.isMainNet(migrationParams)) {
      String walletFilesPrefix = AppConfig.MainNetConfig.get().getWalletFilesPrefix();
      storageDir = new File(rootDir, "mainnet_wallet__uuid_object_storage");
      walletFile = new File(rootDir, "mainnet_wallet_.wallet");
      newWalletFile = new File(rootDir, walletFilesPrefix + ".wallet");
      chainFile = new File(rootDir, "mainnet_wallet_.spvchain");
      newChainFile = new File(rootDir, walletFilesPrefix + ".spvchain");
    } else if (ClientUtils.isTestNet(migrationParams)) {
      String walletFilesPrefix = AppConfig.TestNetConfig.get().getWalletFilesPrefix();
      storageDir = new File(rootDir, "testnet_wallet__uuid_object_storage");
      walletFile = new File(rootDir, "testnet_wallet_.wallet");
      newWalletFile = new File(rootDir, walletFilesPrefix + ".wallet");
      chainFile = new File(rootDir, "testnet_wallet_.spvchain");
      newChainFile = new File(rootDir, walletFilesPrefix + ".spvchain");
    } else {
      throw new RuntimeException(
          "Network params not supported (unknown): " + migrationParams.toString());
    }

    final File keyFile = new File(storageDir, "ECKeyWrapper.json");

    if (keyFile.exists() && walletFile.exists()) {

      // Keys: stored in ECKeyWrapper.json
      /* Key format (JSON):
        {
          "...uuid1...": {
              "isPublicOnly": true,
              "keyPayload": [...bytes (integers)...],
              "name": "remote_server_public_key",
              "uuid": "...uuid1..."
          },
          "...uuid2...": {
              "isPublicOnly": false,
              "keyPayload": [...bytes (integers)...],
              "name": "remote_client_public_key",
              "uuid": "...uuid2..."
          }
        }
      */

      Log.d(TAG, "Key file found: " + keyFile);
      String keyFileJson = FileUtils.readFileToString(keyFile);
      Type type = new TypeToken<Map<String, ECKeyWrapper>>() {}.getType();
      // Note: do not use gson from serializeutils (key is not stored in base64).
      Map<String, ECKeyWrapper> keys = new Gson().fromJson(keyFileJson, type);
      ECKey serverKey = null;
      ECKey clientKey = null;
      for (ECKeyWrapper key : keys.values()) {
        if (key.isPublicOnly && key.name.equals("remote_server_public_key")) {
          serverKey = ECKey.fromPublicOnly(key.keyPayload);
        } else if (!key.isPublicOnly && key.name.equals("remote_client_public_key")) {
          clientKey = ECKey.fromPrivate(key.keyPayload);
        } else {
          Log.d(TAG, "Unknown key name: " + key.name);
        }
      }

      if (clientKey != null && serverKey != null) {
        Log.d(TAG, "Found client and server keys - store in shared preferences.");
        try {

          /** ******** Actual Migration Code ********* */
          SharedPrefUtils.setClientKey(context, migrationParams, clientKey);
          SharedPrefUtils.setServerKey(context, migrationParams, serverKey, "n/a - migration");
          Log.d(
              TAG,
              "Migrated keys:"
                  + " clientPubKey="
                  + clientKey.getPublicKeyAsHex()
                  + ", serverPubKey="
                  + serverKey.getPublicKeyAsHex());

          // move wallet file
          Log.d(
              TAG,
              "Migrate wallet file: " + walletFile.toString() + " -> " + newWalletFile.toString());
          FileUtils.copyFile(walletFile, newWalletFile);
          Log.d(
              TAG,
              "Migrate chain file: " + chainFile.toString() + " -> " + newChainFile.toString());
          FileUtils.copyFile(chainFile, newChainFile);

          SharedPrefUtils.enableMultisig2of2ToCltvForwarder(context);

          // move everything to an archive file.
          Log.d(TAG, "Move old files to archive dir: " + archiveDir.toString());
          FileUtils.moveToDirectory(storageDir, archiveDir, true);
          FileUtils.moveToDirectory(walletFile, archiveDir, true);
          FileUtils.moveToDirectory(chainFile, archiveDir, true);

        } catch (Exception e) {
          Log.d(TAG, "Exception: ", e);
          // clear the changes made.
          SharedPrefUtils.setClientKey(context, migrationParams, null);
          SharedPrefUtils.setServerKey(context, migrationParams, null, "");
          if (newWalletFile.exists()) {
            newWalletFile.delete();
          }
        }
      }
    } else {
      Log.d(
          TAG,
          "Key file or wallet file not found - no migration required - "
              + String.format(
                  "keyFile: %s (exists: %s), walletFile: %s (exists: %s)",
                  keyFile.toString(),
                  keyFile.exists(),
                  walletFile.toString(),
                  walletFile.exists()));
    }

    Log.d(TAG, "********* MIGRATION FROM v1.0.262 FINISHED *********");
  }