@Test(expected = UnreadableWalletException.FutureVersion.class)
 public void versions() throws Exception {
   Protos.Wallet.Builder proto =
       Protos.Wallet.newBuilder(new WalletProtobufSerializer().walletToProto(myWallet));
   proto.setVersion(2);
   new WalletProtobufSerializer().readWallet(params, null, proto.build());
 }
 private static void populateExtensions(Wallet wallet, Protos.Wallet.Builder walletBuilder) {
   for (WalletExtension extension : wallet.getExtensions().values()) {
     Protos.Extension.Builder proto = Protos.Extension.newBuilder();
     proto.setId(extension.getWalletExtensionID());
     proto.setMandatory(extension.isWalletExtensionMandatory());
     proto.setData(ByteString.copyFrom(extension.serializeWalletExtension()));
     walletBuilder.addExtension(proto);
   }
 }
  /**
   * Converts the given wallet to the object representation of the protocol buffers. This can be
   * modified, or additional data fields set, before serialization takes place.
   */
  public Protos.Wallet walletToProto(Wallet wallet) {
    Protos.Wallet.Builder walletBuilder = Protos.Wallet.newBuilder();
    walletBuilder.setNetworkIdentifier(wallet.getNetworkParameters().getId());
    if (wallet.getDescription() != null) {
      walletBuilder.setDescription(wallet.getDescription());
    }

    for (WalletTransaction wtx : wallet.getWalletTransactions()) {
      Protos.Transaction txProto = makeTxProto(wtx);
      walletBuilder.addTransaction(txProto);
    }

    walletBuilder.addAllKey(wallet.serializeKeychainToProtobuf());

    for (Script script : wallet.getWatchedScripts()) {
      Protos.Script protoScript =
          Protos.Script.newBuilder()
              .setProgram(ByteString.copyFrom(script.getProgram()))
              .setCreationTimestamp(script.getCreationTimeSeconds() * 1000)
              .build();

      walletBuilder.addWatchedScript(protoScript);
    }

    // Populate the lastSeenBlockHash field.
    Sha256Hash lastSeenBlockHash = wallet.getLastBlockSeenHash();
    if (lastSeenBlockHash != null) {
      walletBuilder.setLastSeenBlockHash(hashToByteString(lastSeenBlockHash));
      walletBuilder.setLastSeenBlockHeight(wallet.getLastBlockSeenHeight());
    }
    if (wallet.getLastBlockSeenTimeSecs() > 0)
      walletBuilder.setLastSeenBlockTimeSecs(wallet.getLastBlockSeenTimeSecs());

    // Populate the scrypt parameters.
    KeyCrypter keyCrypter = wallet.getKeyCrypter();
    if (keyCrypter == null) {
      // The wallet is unencrypted.
      walletBuilder.setEncryptionType(EncryptionType.UNENCRYPTED);
    } else {
      // The wallet is encrypted.
      walletBuilder.setEncryptionType(keyCrypter.getUnderstoodEncryptionType());
      if (keyCrypter instanceof KeyCrypterScrypt) {
        KeyCrypterScrypt keyCrypterScrypt = (KeyCrypterScrypt) keyCrypter;
        walletBuilder.setEncryptionParameters(keyCrypterScrypt.getScryptParameters());
      } else {
        // Some other form of encryption has been specified that we do not know how to persist.
        throw new RuntimeException(
            "The wallet has encryption of type '"
                + keyCrypter.getUnderstoodEncryptionType()
                + "' but this WalletProtobufSerializer does not know how to persist this.");
      }
    }

    if (wallet.getKeyRotationTime() != null) {
      long timeSecs = wallet.getKeyRotationTime().getTime() / 1000;
      walletBuilder.setKeyRotationTime(timeSecs);
    }

    populateExtensions(wallet, walletBuilder);

    for (Map.Entry<String, ByteString> entry : wallet.getTags().entrySet()) {
      Protos.Tag.Builder tag =
          Protos.Tag.newBuilder().setTag(entry.getKey()).setData(entry.getValue());
      walletBuilder.addTags(tag);
    }

    for (TransactionSigner signer : wallet.getTransactionSigners()) {
      // do not serialize LocalTransactionSigner as it's being added implicitly
      if (signer instanceof LocalTransactionSigner) continue;
      Protos.TransactionSigner.Builder protoSigner = Protos.TransactionSigner.newBuilder();
      protoSigner.setClassName(signer.getClass().getName());
      protoSigner.setData(ByteString.copyFrom(signer.serialize()));
      walletBuilder.addTransactionSigners(protoSigner);
    }

    walletBuilder.setSigsRequiredToSpend(wallet.getSigsRequiredToSpend());

    // Populate the wallet version.
    walletBuilder.setVersion(wallet.getVersion());

    return walletBuilder.build();
  }
  /**
   * Converts the given wallet to the object representation of the protocol buffers. This can be
   * modified, or additional data fields set, before serialization takes place.
   */
  public Protos.Wallet walletToProto(Wallet wallet) {
    Protos.Wallet.Builder walletBuilder = Protos.Wallet.newBuilder();
    walletBuilder.setNetworkIdentifier(wallet.getNetworkParameters().getId());
    if (wallet.getDescription() != null) {
      walletBuilder.setDescription(wallet.getDescription());
    }

    for (WalletTransaction wtx : wallet.getWalletTransactions()) {
      Protos.Transaction txProto = makeTxProto(wtx);
      walletBuilder.addTransaction(txProto);
    }

    for (ECKey key : wallet.getKeys()) {
      Protos.Key.Builder keyBuilder =
          Protos.Key.newBuilder()
              .setCreationTimestamp(key.getCreationTimeSeconds() * 1000)
              // .setLabel() TODO
              .setType(Protos.Key.Type.ORIGINAL);
      if (key.getPrivKeyBytes() != null)
        keyBuilder.setPrivateKey(ByteString.copyFrom(key.getPrivKeyBytes()));

      EncryptedPrivateKey encryptedPrivateKey = key.getEncryptedPrivateKey();
      if (encryptedPrivateKey != null) {
        // Key is encrypted.
        Protos.EncryptedPrivateKey.Builder encryptedKeyBuilder =
            Protos.EncryptedPrivateKey.newBuilder()
                .setEncryptedPrivateKey(
                    ByteString.copyFrom(encryptedPrivateKey.getEncryptedBytes()))
                .setInitialisationVector(
                    ByteString.copyFrom(encryptedPrivateKey.getInitialisationVector()));

        if (key.getKeyCrypter() == null) {
          throw new IllegalStateException(
              "The encrypted key " + key.toString() + " has no KeyCrypter.");
        } else {
          // If it is a Scrypt + AES encrypted key, set the persisted key type.
          if (key.getKeyCrypter().getUnderstoodEncryptionType()
              == Protos.Wallet.EncryptionType.ENCRYPTED_SCRYPT_AES) {
            keyBuilder.setType(Protos.Key.Type.ENCRYPTED_SCRYPT_AES);
          } else {
            throw new IllegalArgumentException(
                "The key "
                    + key.toString()
                    + " is encrypted with a KeyCrypter of type "
                    + key.getKeyCrypter().getUnderstoodEncryptionType()
                    + ". This WalletProtobufSerialiser does not understand that type of encryption.");
          }
        }
        keyBuilder.setEncryptedPrivateKey(encryptedKeyBuilder);
      }

      // We serialize the public key even if the private key is present for speed reasons: we don't
      // want to do
      // lots of slow EC math to load the wallet, we prefer to store the redundant data instead. It
      // matters more
      // on mobile platforms.
      keyBuilder.setPublicKey(ByteString.copyFrom(key.getPubKey()));
      walletBuilder.addKey(keyBuilder);
    }

    // Populate the lastSeenBlockHash field.
    Sha256Hash lastSeenBlockHash = wallet.getLastBlockSeenHash();
    if (lastSeenBlockHash != null) {
      walletBuilder.setLastSeenBlockHash(hashToByteString(lastSeenBlockHash));
      walletBuilder.setLastSeenBlockHeight(wallet.getLastBlockSeenHeight());
    }

    // Populate the scrypt parameters.
    KeyCrypter keyCrypter = wallet.getKeyCrypter();
    if (keyCrypter == null) {
      // The wallet is unencrypted.
      walletBuilder.setEncryptionType(EncryptionType.UNENCRYPTED);
    } else {
      // The wallet is encrypted.
      walletBuilder.setEncryptionType(keyCrypter.getUnderstoodEncryptionType());
      if (keyCrypter instanceof KeyCrypterScrypt) {
        KeyCrypterScrypt keyCrypterScrypt = (KeyCrypterScrypt) keyCrypter;
        walletBuilder.setEncryptionParameters(keyCrypterScrypt.getScryptParameters());
      } else {
        // Some other form of encryption has been specified that we do not know how to persist.
        throw new RuntimeException(
            "The wallet has encryption of type '"
                + keyCrypter.getUnderstoodEncryptionType()
                + "' but this WalletProtobufSerializer does not know how to persist this.");
      }
    }

    populateExtensions(wallet, walletBuilder);

    // Populate the wallet version.
    walletBuilder.setVersion(wallet.getVersion());

    return walletBuilder.build();
  }