private void readTransaction(Protos.Transaction txProto, NetworkParameters params) {
    Transaction tx = new Transaction(params);
    if (txProto.hasUpdatedAt()) {
      tx.setUpdateTime(new Date(txProto.getUpdatedAt()));
    }

    for (Protos.TransactionOutput outputProto : txProto.getTransactionOutputList()) {
      BigInteger value = BigInteger.valueOf(outputProto.getValue());
      byte[] scriptBytes = outputProto.getScriptBytes().toByteArray();
      TransactionOutput output = new TransactionOutput(params, tx, value, scriptBytes);
      tx.addOutput(output);
    }

    for (Protos.TransactionInput transactionInput : txProto.getTransactionInputList()) {
      byte[] scriptBytes = transactionInput.getScriptBytes().toByteArray();
      TransactionOutPoint outpoint =
          new TransactionOutPoint(
              params,
              transactionInput.getTransactionOutPointIndex(),
              byteStringToHash(transactionInput.getTransactionOutPointHash()));
      TransactionInput input = new TransactionInput(params, tx, scriptBytes, outpoint);
      if (transactionInput.hasSequence()) {
        input.setSequenceNumber(transactionInput.getSequence());
      }
      tx.addInput(input);
    }

    for (ByteString blockHash : txProto.getBlockHashList()) {
      tx.addBlockAppearance(byteStringToHash(blockHash));
    }

    if (txProto.hasLockTime()) {
      tx.setLockTime(0xffffffffL & txProto.getLockTime());
    }

    // Transaction should now be complete.
    Sha256Hash protoHash = byteStringToHash(txProto.getHash());
    Preconditions.checkState(
        tx.getHash().equals(protoHash),
        "Transaction did not deserialize completely: %s vs %s",
        tx.getHash(),
        protoHash);
    Preconditions.checkState(
        !txMap.containsKey(txProto.getHash()),
        "Wallet contained duplicate transaction %s",
        byteStringToHash(txProto.getHash()));
    txMap.put(txProto.getHash(), tx);
  }