@Test public void testRoundTripNormalWallet() throws Exception { Wallet wallet1 = roundTrip(myWallet); assertEquals(0, wallet1.getTransactions(true).size()); assertEquals(Coin.ZERO, wallet1.getBalance()); assertArrayEquals( myKey.getPubKey(), wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPubKey()); assertArrayEquals( myKey.getPrivKeyBytes(), wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPrivKeyBytes()); assertEquals( myKey.getCreationTimeSeconds(), wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getCreationTimeSeconds()); }
@Test public void empty() throws Exception { // Check the base case of a wallet with one key and no transactions. Wallet wallet1 = roundTrip(myWallet); assertEquals(0, wallet1.getTransactions(true).size()); assertEquals(Coin.ZERO, wallet1.getBalance()); assertArrayEquals( myKey.getPubKey(), wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPubKey()); assertArrayEquals( myKey.getPrivKeyBytes(), wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPrivKeyBytes()); assertEquals( myKey.getCreationTimeSeconds(), wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getCreationTimeSeconds()); assertEquals(mScriptCreationTime, wallet1.getWatchedScripts().get(0).getCreationTimeSeconds()); assertEquals(1, wallet1.getWatchedScripts().size()); assertEquals( ScriptBuilder.createOutputScript(myWatchedKey.toAddress(params)), wallet1.getWatchedScripts().get(0)); assertEquals(WALLET_DESCRIPTION, wallet1.getDescription()); }
@Override public CoinSelection select(Coin target, List<TransactionOutput> candidates) { try { LinkedList<TransactionOutput> gathered = Lists.newLinkedList(); Coin valueGathered = Coin.ZERO; for (TransactionOutput output : candidates) { if (ignorePending && !isConfirmed(output)) continue; // Find the key that controls output, assuming it's a regular pay-to-pubkey or // pay-to-address output. // We ignore any other kind of exotic output on the assumption we can't spend it ourselves. final Script scriptPubKey = output.getScriptPubKey(); ECKey controllingKey; if (scriptPubKey.isSentToRawPubKey()) { controllingKey = wallet.findKeyFromPubKey(scriptPubKey.getPubKey()); } else if (scriptPubKey.isSentToAddress()) { controllingKey = wallet.findKeyFromPubHash(scriptPubKey.getPubKeyHash()); } else { log.info("Skipping tx output {} because it's not of simple form.", output); continue; } checkNotNull( controllingKey, "Coin selector given output as candidate for which we lack the key"); if (controllingKey.getCreationTimeSeconds() >= unixTimeSeconds) continue; // It's older than the cutoff time so select. valueGathered = valueGathered.add(output.getValue()); gathered.push(output); if (gathered.size() >= MAX_SIMULTANEOUS_INPUTS) { log.warn( "Reached {} inputs, going further would yield a tx that is too large, stopping here.", gathered.size()); break; } } return new CoinSelection(valueGathered, gathered); } catch (ScriptException e) { throw new RuntimeException( e); // We should never have problems understanding scripts in our wallet. } }
/** * 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(); }