Exemple #1
0
  private void loadWalletFromProtobuf() {
    if (walletFile.exists()) {
      final long start = System.currentTimeMillis();

      FileInputStream walletStream = null;

      try {
        walletStream = new FileInputStream(walletFile);

        wallet = new WalletProtobufSerializer().readWallet(walletStream);

        log.info(
            "wallet loaded from: '"
                + walletFile
                + "', took "
                + (System.currentTimeMillis() - start)
                + "ms");
      } catch (final FileNotFoundException x) {
        log.error("problem loading wallet", x);

        Toast.makeText(WalletApplication.this, x.getClass().getName(), Toast.LENGTH_LONG).show();

        wallet = restoreWalletFromBackup();
      } catch (final UnreadableWalletException x) {
        log.error("problem loading wallet", x);

        Toast.makeText(WalletApplication.this, x.getClass().getName(), Toast.LENGTH_LONG).show();

        wallet = restoreWalletFromBackup();
      } finally {
        if (walletStream != null) {
          try {
            walletStream.close();
          } catch (final IOException x) {
            // swallow
          }
        }
      }

      if (!wallet.isConsistent()) {
        Toast.makeText(this, "inconsistent wallet: " + walletFile, Toast.LENGTH_LONG).show();

        wallet = restoreWalletFromBackup();
      }

      if (!wallet.getParams().equals(Constants.NETWORK_PARAMETERS))
        throw new Error("bad wallet network parameters: " + wallet.getParams().getId());
    } else {
      wallet = new Wallet(Constants.NETWORK_PARAMETERS);

      log.info("new wallet created");
    }

    // this check is needed so encrypted wallets won't get their private keys removed accidently
    for (final ECKey key : wallet.getKeys())
      if (key.getPrivKeyBytes() == null)
        throw new Error(
            "found read-only key, but wallet is likely an encrypted wallet from the future");
  }
 private synchronized Transaction makeUnsignedChannelContract(Coin valueToMe)
     throws ValueOutOfRangeException {
   Transaction tx = new Transaction(wallet.getParams());
   tx.addInput(multisigContract.getOutput(0));
   // Our output always comes first.
   // TODO: We should drop myKey in favor of output key + multisig key separation
   // (as its always obvious who the client is based on T2 output order)
   tx.addOutput(valueToMe, myKey.toAddress(wallet.getParams()));
   return tx;
 }
 @Override
 public void deserializeWalletExtension(Wallet containingWallet, byte[] data) throws Exception {
   lock.lock();
   try {
     checkState(this.containingWallet == null || this.containingWallet == containingWallet);
     this.containingWallet = containingWallet;
     NetworkParameters params = containingWallet.getParams();
     ClientState.StoredClientPaymentChannels states =
         ClientState.StoredClientPaymentChannels.parseFrom(data);
     for (ClientState.StoredClientPaymentChannel storedState : states.getChannelsList()) {
       Transaction refundTransaction =
           new Transaction(params, storedState.getRefundTransaction().toByteArray());
       refundTransaction.getConfidence().setSource(TransactionConfidence.Source.SELF);
       StoredClientChannel channel =
           new StoredClientChannel(
               new Sha256Hash(storedState.getId().toByteArray()),
               new Transaction(params, storedState.getContractTransaction().toByteArray()),
               refundTransaction,
               new ECKey(new BigInteger(1, storedState.getMyKey().toByteArray()), null, true),
               BigInteger.valueOf(storedState.getValueToMe()),
               BigInteger.valueOf(storedState.getRefundFees()),
               false);
       if (storedState.hasCloseTransactionHash())
         channel.close =
             containingWallet.getTransaction(new Sha256Hash(storedState.toByteArray()));
       putChannel(channel, false);
     }
   } finally {
     lock.unlock();
   }
 }
 /**
  * Creates the initial multisig contract and incomplete refund transaction which can be requested
  * at the appropriate time using {@link PaymentChannelClientState#getIncompleteRefundTransaction}
  * and {@link PaymentChannelClientState#getMultisigContract()}. The way the contract is crafted
  * can be adjusted by overriding {@link
  * PaymentChannelClientState#editContractSendRequest(com.google.bitcoin.core.Wallet.SendRequest)}.
  * By default unconfirmed coins are allowed to be used, as for micropayments the risk should be
  * relatively low.
  *
  * @throws ValueOutOfRangeException if the value being used is too small to be accepted by the
  *     network
  * @throws InsufficientMoneyException if the wallet doesn't contain enough balance to initiate
  */
 public synchronized void initiate() 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, serverMultisigKey);
   // 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.getMinNonDustValue().compareTo(totalValue) > 0)
     throw new ValueOutOfRangeException("totalValue too small to use");
   Wallet.SendRequest req = Wallet.SendRequest.forTx(template);
   req.coinSelector = AllowUnconfirmedCoinSelector.get();
   editContractSendRequest(req);
   req.shuffleOutputs = false; // TODO: Fix things so shuffling is usable.
   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);
   refundTx
       .addInput(multisigOutput)
       .setSequenceNumber(0); // Allow replacement when it's eventually reactivated.
   refundTx.setLockTime(expiryTime);
   if (totalValue.compareTo(Coin.CENT) < 0) {
     // 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, myKey.toAddress(params));
     refundFees = multisigFee.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
   } else {
     refundTx.addOutput(totalValue, myKey.toAddress(params));
     refundFees = multisigFee;
   }
   refundTx.getConfidence().setSource(TransactionConfidence.Source.SELF);
   log.info(
       "initiated channel with multi-sig contract {}, refund {}",
       multisigContract.getHashAsString(),
       refundTx.getHashAsString());
   state = State.INITIATED;
   // Client should now call getIncompleteRefundTransaction() and send it to the server.
 }
  /**
   * Loads wallet data from the given protocol buffer and inserts it into the given Wallet object.
   * This is primarily useful when you wish to pre-register extension objects. Note that if loading
   * fails the provided Wallet object may be in an indeterminate state and should be thrown away.
   *
   * @throws IOException if there is a problem reading the stream.
   * @throws IllegalArgumentException if the wallet is corrupt.
   */
  public void readWallet(Protos.Wallet walletProto, Wallet wallet) throws IOException {
    // TODO: This method should throw more specific exception types than IllegalArgumentException.
    // Read the scrypt parameters that specify how encryption and decryption is performed.
    if (walletProto.hasEncryptionParameters()) {
      Protos.ScryptParameters encryptionParameters = walletProto.getEncryptionParameters();
      wallet.setKeyCrypter(new KeyCrypterScrypt(encryptionParameters));
    }

    if (walletProto.hasDescription()) {
      wallet.setDescription(walletProto.getDescription());
    }

    // Read all keys
    for (Protos.Key keyProto : walletProto.getKeyList()) {
      if (!(keyProto.getType() == Protos.Key.Type.ORIGINAL
          || keyProto.getType() == Protos.Key.Type.ENCRYPTED_SCRYPT_AES)) {
        throw new IllegalArgumentException(
            "Unknown key type in wallet, type = " + keyProto.getType());
      }

      byte[] privKey = keyProto.hasPrivateKey() ? keyProto.getPrivateKey().toByteArray() : null;
      EncryptedPrivateKey encryptedPrivateKey = null;
      if (keyProto.hasEncryptedPrivateKey()) {
        Protos.EncryptedPrivateKey encryptedPrivateKeyProto = keyProto.getEncryptedPrivateKey();
        encryptedPrivateKey =
            new EncryptedPrivateKey(
                encryptedPrivateKeyProto.getInitialisationVector().toByteArray(),
                encryptedPrivateKeyProto.getEncryptedPrivateKey().toByteArray());
      }

      byte[] pubKey = keyProto.hasPublicKey() ? keyProto.getPublicKey().toByteArray() : null;

      ECKey ecKey;
      final KeyCrypter keyCrypter = wallet.getKeyCrypter();
      if (keyCrypter != null
          && keyCrypter.getUnderstoodEncryptionType() != EncryptionType.UNENCRYPTED) {
        // If the key is encrypted construct an ECKey using the encrypted private key bytes.
        ecKey = new ECKey(encryptedPrivateKey, pubKey, keyCrypter);
      } else {
        // Construct an unencrypted private key.
        ecKey = new ECKey(privKey, pubKey);
      }
      ecKey.setCreationTimeSeconds((keyProto.getCreationTimestamp() + 500) / 1000);
      wallet.addKey(ecKey);
    }

    // Read all transactions and insert into the txMap.
    for (Protos.Transaction txProto : walletProto.getTransactionList()) {
      readTransaction(txProto, wallet.getParams());
    }

    // Update transaction outputs to point to inputs that spend them
    for (Protos.Transaction txProto : walletProto.getTransactionList()) {
      WalletTransaction wtx = connectTransactionOutputs(txProto);
      wallet.addWalletTransaction(wtx);
    }

    // Update the lastBlockSeenHash.
    if (!walletProto.hasLastSeenBlockHash()) {
      wallet.setLastBlockSeenHash(null);
    } else {
      wallet.setLastBlockSeenHash(byteStringToHash(walletProto.getLastSeenBlockHash()));
    }
    if (!walletProto.hasLastSeenBlockHeight()) {
      wallet.setLastBlockSeenHeight(-1);
    } else {
      wallet.setLastBlockSeenHeight(walletProto.getLastSeenBlockHeight());
    }

    loadExtensions(wallet, walletProto);

    if (walletProto.hasVersion()) {
      wallet.setVersion(walletProto.getVersion());
    }

    // Make sure the object can be re-used to read another wallet without corruption.
    txMap.clear();
  }