@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 *********"); }