/** * Convert mnemonic word list to seed. */ public static byte[] toSeed(List<String> words, String passphrase) { // To create binary seed from mnemonic, we use PBKDF2 function // with mnemonic sentence (in UTF-8) used as a password and // string "mnemonic" + passphrase (again in UTF-8) used as a // salt. Iteration count is set to 4096 and HMAC-SHA512 is // used as a pseudo-random function. Desired length of the // derived key is 512 bits (= 64 bytes). // String pass = Utils.SPACE_JOINER.join(words); String salt = "mnemonic" + passphrase; final Stopwatch watch = Stopwatch.createStarted(); byte[] seed = PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64); watch.stop(); log.info("PBKDF2 took {}", watch); return seed; }
public String toString(boolean includePrivateKeys, @Nullable KeyParameter aesKey, NetworkParameters params) { final DeterministicKey watchingKey = getWatchingKey(); final StringBuilder builder = new StringBuilder(); if (seed != null) { if (includePrivateKeys) { DeterministicSeed decryptedSeed = seed.isEncrypted() ? seed.decrypt(getKeyCrypter(), DEFAULT_PASSPHRASE_FOR_MNEMONIC, aesKey) : seed; final List<String> words = decryptedSeed.getMnemonicCode(); builder.append("Seed as words: ").append(Utils.SPACE_JOINER.join(words)).append('\n'); builder.append("Seed as hex: ").append(decryptedSeed.toHexString()).append('\n'); } else { if (seed.isEncrypted()) builder.append("Seed is encrypted\n"); } builder.append("Seed birthday: ").append(seed.getCreationTimeSeconds()).append(" [") .append(Utils.dateTimeFormat(seed.getCreationTimeSeconds() * 1000)).append("]\n"); } else { builder.append("Key birthday: ").append(watchingKey.getCreationTimeSeconds()).append(" [") .append(Utils.dateTimeFormat(watchingKey.getCreationTimeSeconds() * 1000)).append("]\n"); } builder.append("Key to watch: ").append(watchingKey.serializePubB58(params)).append('\n'); formatAddresses(includePrivateKeys, aesKey, params, builder); return builder.toString(); }
private void initAddresses() throws CoinbleskException { loadAddresses(); try { // new address: no address yet or current receive address expires soon. boolean needToCreateNewAddress = false; if (addresses.isEmpty()) { Log.d(TAG, "No address yet. Create new address."); needToCreateNewAddress = true; } else { long nowSec = org.bitcoinj.core.Utils.currentTimeSeconds(); long currentExpiresInSec = addresses.last().getLockTime() - nowSec; if (currentExpiresInSec < Constants.MIN_LOCKTIME_SPAN_SECONDS) { Log.d(TAG, "Current address expires soon (in "+currentExpiresInSec+" seconds). Create new address."); needToCreateNewAddress = true; } } if (needToCreateNewAddress) { createTimeLockedAddress(); } } catch (Exception e) { Log.w(TAG, "Could not initialize addresses. ", e); throw new CoinbleskException("Could not initialize addresses: " + e.getMessage(), e); } }
private TimeLockedAddress addAddress(LockTime lockTime) { // Note: do not use in loop, adding to wallet is slow! TimeLockedAddress address = new TimeLockedAddress( multisigClientKey.getPubKey(), multisigServerKey.getPubKey(), lockTime.getLockTime()); addresses.add(lockTime); addressHashes.put( Utils.HEX.encode(address.getAddressHash()), address); Script pubKeyScript = address.createPubkeyScript(); pubKeyScript.setCreationTimeSeconds(lockTime.getTimeCreatedSeconds()); wallet.addWatchedScripts(ImmutableList.of(pubKeyScript)); Log.d(TAG, "Added address: " + address.toString(getNetworkParameters())); return address; }
private List<TransactionOutput> getUnlockedUnspentOutputs() { final List<TransactionOutput> outputs = new ArrayList<>(); final List<TransactionOutput> candidates = wallet.calculateAllSpendCandidates(false, false); final long currentTimeSec = org.bitcoinj.core.Utils.currentTimeSeconds(); for (TransactionOutput txOut : candidates) { byte[] addressHash = txOut.getScriptPubKey().getPubKeyHash(); TimeLockedAddress tla = findTimeLockedAddressByHash(addressHash); if (tla != null) { long lockTime = tla.getLockTime(); if (BitcoinUtils.isAfterLockTime(currentTimeSec, lockTime)) { outputs.add(txOut); Log.d(TAG, "getUnlockedUnspentOutputs - unlocked output: " + txOut); } } } return outputs; }
private List<TransactionSignature> signTransaction(Transaction tx) throws CoinbleskException { final List<TransactionInput> inputs = tx.getInputs(); final List<TransactionSignature> signatures = new ArrayList<>(inputs.size()); for (int i = 0; i < inputs.size(); ++i) { TransactionInput txIn = inputs.get(i); TransactionOutput prevTxOut = txIn.getConnectedOutput(); byte[] sentToHash = prevTxOut.getScriptPubKey().getPubKeyHash(); TimeLockedAddress tla = findTimeLockedAddressByHash(sentToHash); if (tla == null) { throw new CoinbleskException(String.format(Locale.US, "Could not sign input (index=%d, pubKeyHash=%s)", i, org.bitcoinj.core.Utils.HEX.encode(sentToHash))); } byte[] redeemScript = tla.createRedeemScript().getProgram(); TransactionSignature signature = tx.calculateSignature( i, multisigClientKey, redeemScript, Transaction.SigHash.ALL, false); signatures.add(signature); } return signatures; }
/** * Convert mnemonic word list to seed. */ public static byte[] toSeed(List<String> words, String passphrase) { // To create binary seed from mnemonic, we use PBKDF2 function // with mnemonic sentence (in UTF-8) used as a password and // string "mnemonic" + passphrase (again in UTF-8) used as a // salt. Iteration count is set to 4096 and HMAC-SHA512 is // used as a pseudo-random function. Desired length of the // derived key is 512 bits (= 64 bytes). // String pass = Utils.join(words); String salt = "mnemonic" + passphrase; final Stopwatch watch = Stopwatch.createStarted(); byte[] seed = PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64); watch.stop(); log.info("PBKDF2 took {}", watch); return seed; }
public String toString(boolean includePrivateKeys, NetworkParameters params) { final DeterministicKey watchingKey = getWatchingKey(); final StringBuilder builder = new StringBuilder(); if (seed != null) { if (seed.isEncrypted()) { builder.append("Seed is encrypted\n"); } else if (includePrivateKeys) { final List<String> words = seed.getMnemonicCode(); builder.append("Seed as words: ").append(Utils.join(words)).append('\n'); builder.append("Seed as hex: ").append(seed.toHexString()).append('\n'); } builder.append("Seed birthday: ").append(seed.getCreationTimeSeconds()).append(" [") .append(Utils.dateTimeFormat(seed.getCreationTimeSeconds() * 1000)).append("]\n"); } else { builder.append("Key birthday: ").append(watchingKey.getCreationTimeSeconds()).append(" [") .append(Utils.dateTimeFormat(watchingKey.getCreationTimeSeconds() * 1000)).append("]\n"); } builder.append("Key to watch: ").append(watchingKey.serializePubB58(params)).append('\n'); formatAddresses(includePrivateKeys, params, builder); return builder.toString(); }
@Test public void shouldAcceptDefaultTimeWindow() { final TwoWayChannelMessage message = createClientVersionMessage(); final Capture<TwoWayChannelMessage> initiateCapture = new Capture<TwoWayChannelMessage>(); connection.sendToClient(capture(initiateCapture)); replay(connection); dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, connection); dut.connectionOpen(); dut.receiveMessage(message); long expectedExpire = Utils.currentTimeSeconds() + 24 * 60 * 60 - 60; // This the default defined in paymentchannel.proto assertServerVersion(); assertExpireTime(expectedExpire, initiateCapture); }
@Test public void shouldTruncateTooLargeTimeWindow() { final int maxTimeWindow = 40000; final int timeWindow = maxTimeWindow + 1; final TwoWayChannelMessage message = createClientVersionMessage(timeWindow); final Capture<TwoWayChannelMessage> initiateCapture = new Capture<TwoWayChannelMessage>(); connection.sendToClient(capture(initiateCapture)); replay(connection); dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties(){ @Override public long getMaxTimeWindow() { return maxTimeWindow; } @Override public long getMinTimeWindow() { return 20000; } }, connection); dut.connectionOpen(); dut.receiveMessage(message); long expectedExpire = Utils.currentTimeSeconds() + maxTimeWindow; assertServerVersion(); assertExpireTime(expectedExpire, initiateCapture); }
@Test public void shouldAllowExactTimeWindow() { final TwoWayChannelMessage message = createClientVersionMessage(); final Capture<TwoWayChannelMessage> initiateCapture = new Capture<TwoWayChannelMessage>(); connection.sendToClient(capture(initiateCapture)); replay(connection); final int expire = 24 * 60 * 60 - 60; // This the default defined in paymentchannel.proto dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties(){ @Override public long getMaxTimeWindow() { return expire; } @Override public long getMinTimeWindow() { return expire; } }, connection); dut.connectionOpen(); long expectedExpire = Utils.currentTimeSeconds() + expire; dut.receiveMessage(message); assertServerVersion(); assertExpireTime(expectedExpire, initiateCapture); }
@Test public void testVectors() throws Exception { for (int ii = 0; ii < vectors.length; ii += 3) { String vecData = vectors[ii]; String vecCode = vectors[ii+1]; String vecSeed = vectors[ii+2]; List<String> code = mc.toMnemonic(HEX.decode(vecData)); byte[] seed = MnemonicCode.toSeed(code, "TREZOR"); byte[] entropy = mc.toEntropy(split(vecCode)); assertEquals(vecData, HEX.encode(entropy)); assertEquals(vecCode, Utils.join(code)); assertEquals(vecSeed, HEX.encode(seed)); } }
/** * Test with random plain text strings and random passwords. * UUIDs are used and hence will only cover hex characters (and the separator hyphen). * @throws KeyCrypterException */ @Test public void testKeyCrypterGood2() { KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); // Trying random UUIDs for plainText and passwords. int numberOfTests = 16; for (int i = 0; i < numberOfTests; i++) { // Create a UUID as the plaintext and use another for the password. String plainText = UUID.randomUUID().toString(); CharSequence password = UUID.randomUUID().toString(); EncryptedData data = keyCrypter.encrypt(plainText.getBytes(), keyCrypter.deriveKey(password)); assertNotNull(data); byte[] reconstructedPlainBytes = keyCrypter.decrypt(data,keyCrypter.deriveKey(password)); assertEquals(Utils.HEX.encode(plainText.getBytes()), Utils.HEX.encode(reconstructedPlainBytes)); } }
@Test public void testEncryptDecryptBytes2() throws KeyCrypterException { KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); // Encrypt random bytes of various lengths up to length 50. Random random = new Random(); for (int i = 0; i < 50; i++) { byte[] plainBytes = new byte[i]; random.nextBytes(plainBytes); EncryptedData data = keyCrypter.encrypt(plainBytes, keyCrypter.deriveKey(PASSWORD1)); assertNotNull(data); //log.debug("\nEncrypterDecrypterTest: cipherBytes = \nlength = " + cipherBytes.length + "\n---------------\n" + Utils.HEX.encode(cipherBytes) + "\n---------------\n"); byte[] rebornPlainBytes = keyCrypter.decrypt(data, keyCrypter.deriveKey(PASSWORD1)); log.debug("Original: (" + i + ") " + Utils.HEX.encode(plainBytes)); log.debug("Reborn1 : (" + i + ") " + Utils.HEX.encode(rebornPlainBytes)); assertEquals(Utils.HEX.encode(plainBytes), Utils.HEX.encode(rebornPlainBytes)); } }
@Test public void serializationUnencrypted() throws UnreadableWalletException { Utils.setMockClock(); Date now = Utils.now(); final ECKey key1 = new ECKey(); Utils.rollMockClock(5000); final ECKey key2 = new ECKey(); chain.importKeys(ImmutableList.of(key1, key2)); List<Protos.Key> keys = chain.serializeToProtobuf(); assertEquals(2, keys.size()); assertArrayEquals(key1.getPubKey(), keys.get(0).getPublicKey().toByteArray()); assertArrayEquals(key2.getPubKey(), keys.get(1).getPublicKey().toByteArray()); assertArrayEquals(key1.getPrivKeyBytes(), keys.get(0).getSecretBytes().toByteArray()); assertArrayEquals(key2.getPrivKeyBytes(), keys.get(1).getSecretBytes().toByteArray()); long normTime = (long) (Math.floor(now.getTime() / 1000) * 1000); assertEquals(normTime, keys.get(0).getCreationTimestamp()); assertEquals(normTime + 5000 * 1000, keys.get(1).getCreationTimestamp()); chain = BasicKeyChain.fromProtobufUnencrypted(keys); assertEquals(2, chain.getKeys().size()); assertEquals(key1, chain.getKeys().get(0)); assertEquals(key2, chain.getKeys().get(1)); }
@Test public void keysBeforeAndAfter() throws Exception { Utils.setMockClock(); long now = Utils.currentTimeSeconds(); final ECKey key1 = new ECKey(); Utils.rollMockClock(86400); final ECKey key2 = new ECKey(); final List<ECKey> keys = Lists.newArrayList(key1, key2); assertEquals(2, chain.importKeys(keys)); assertNull(chain.findOldestKeyAfter(now + 86400 * 2)); assertEquals(key1, chain.findOldestKeyAfter(now - 1)); assertEquals(key2, chain.findOldestKeyAfter(now + 86400 - 1)); assertEquals(2, chain.findKeysBefore(now + 86400 * 2).size()); assertEquals(1, chain.findKeysBefore(now + 1).size()); assertEquals(0, chain.findKeysBefore(now - 1).size()); }
@Test public void keyRotationHD() throws Exception { // Test that if we rotate an HD chain, a new one is created and all arrivals on the old keys are moved. Utils.setMockClock(); wallet = new Wallet(PARAMS); ECKey key1 = wallet.freshReceiveKey(); ECKey key2 = wallet.freshReceiveKey(); sendMoneyToWallet(wallet, AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, key1.toAddress(PARAMS)); sendMoneyToWallet(wallet, AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, key2.toAddress(PARAMS)); DeterministicKey watchKey1 = wallet.getWatchingKey(); // A day later, we get compromised. Utils.rollMockClock(86400); wallet.setKeyRotationTime(Utils.currentTimeSeconds()); List<Transaction> txns = wallet.doMaintenance(null, false).get(); assertEquals(1, txns.size()); DeterministicKey watchKey2 = wallet.getWatchingKey(); assertNotEquals(watchKey1, watchKey2); }
@Override public boolean broadcastTxSync(final BitTransaction tx) { Preconditions.checkNotNull(stratumClient); CallMessage message = new CallMessage("blockchain.transaction.broadcast", Arrays.asList(Utils.HEX.encode(tx.bitcoinSerialize()))); try { ResultMessage result = stratumClient.call(message).get(); String txId = result.getResult().getString(0); // FIXME could return {u'message': u'', u'code': -25} log.info("got tx {} =?= {}", txId, tx.getHash()); Preconditions.checkState(tx.getHash().toString().equals(txId)); return true; } catch (Exception e) { log.error("Could not get reply for blockchain.transaction.broadcast", e); } return false; }
public boolean broadcastNxtTxSync(NxtTransaction tx) throws TransactionBroadcastException { if (isConnected()) { if (log.isInfoEnabled()) { log.info("Broadcasting tx {}", Utils.HEX.encode(tx.getRawTransaction().getBytes())); } boolean success = blockchainConnection.broadcastTxSync(tx); if (success) { onTransactionBroadcast(tx); } else { onTransactionBroadcastError(tx); } return success; } else { throw new TransactionBroadcastException("No connection available"); } }
private boolean broadcastBitTxSync(BitTransaction tx) throws TransactionBroadcastException { if (isConnected()) { lock.lock(); try { if (log.isInfoEnabled()) { log.info("Broadcasting tx {}", Utils.HEX.encode(tx.bitcoinSerialize())); } boolean success = blockchainConnection.broadcastTxSync(tx); if (success) { onTransactionBroadcast(tx); } else { onTransactionBroadcastError(tx); } return success; } finally { lock.unlock(); } } else { throw new TransactionBroadcastException("No connection available"); } }
private void broadcastTx(BitTransaction tx, TransactionEventListener<BitTransaction> listener) throws TransactionBroadcastException { if (isConnected()) { lock.lock(); try { if (log.isInfoEnabled()) { log.info("Broadcasting tx {}", Utils.HEX.encode(tx.bitcoinSerialize())); } blockchainConnection.broadcastTx(tx, listener != null ? listener : this); } finally { lock.unlock(); } } else { throw new TransactionBroadcastException("No connection available"); } }
@Override protected void run() { while (isRunning()) { try { connect(); setConnected(true); runClient(); setConnected(false); disconnect(); createSocket(); } catch (IOException e) { logger.error("IOException", e); setConnected(false); if (isRunning()) { logger.error("will reconnect"); disconnect(); createSocket(); Utils.sleep(3000 + new Random().nextInt(4000)); } } catch (Throwable t) { logger.error("client", t); Throwables.propagate(t); } } }
@Override public String toString() { StringBuilder buf = new StringBuilder(); if (isOpCode()) { buf.append(getOpCodeName(opcode)); } else if (data != null) { // Data chunk buf.append(getPushDataName(opcode)); buf.append("["); buf.append(Utils.HEX.encode(data)); buf.append("]"); } else { // Small num buf.append(Script.decodeFromOpN(opcode)); } return buf.toString(); }
/** * Generates a new deterministic key from the given seed, which can be any arbitrary byte array. However resist * the temptation to use a string as the seed - any key derived from a password is likely to be weak and easily * broken by attackers (this is not theoretical, people have had money stolen that way). This method checks * that the given seed is at least 64 bits long. * * @throws HDDerivationException if generated master key is invalid (private key 0 or >= n). * @throws IllegalArgumentException if the seed is less than 8 bytes and could be brute forced. */ public static DeterministicKey createMasterPrivateKey(byte[] seed) throws HDDerivationException { checkArgument(seed.length > 8, "Seed is too short and could be brute forced"); // Calculate I = HMAC-SHA512(key="Bitcoin seed", msg=S) byte[] i = HDUtils.hmacSha512(MASTER_HMAC_SHA512, seed); // Split I into two 32-byte sequences, Il and Ir. // Use Il as master secret key, and Ir as master chain code. checkState(i.length == 64, i.length); byte[] il = Arrays.copyOfRange(i, 0, 32); byte[] ir = Arrays.copyOfRange(i, 32, 64); Arrays.fill(i, (byte)0); DeterministicKey masterPrivKey = createMasterPrivKeyFromBytes(il, ir); Arrays.fill(il, (byte)0); Arrays.fill(ir, (byte)0); // Child deterministic keys will chain up to their parents to find the keys. masterPrivKey.setCreationTimeSeconds(Utils.currentTimeSeconds()); return masterPrivKey; }
@Test public void shouldTruncateTooSmallTimeWindow() { final int minTimeWindow = 20000; final int timeWindow = minTimeWindow - 1; final TwoWayChannelMessage message = createClientVersionMessage(timeWindow); final Capture<TwoWayChannelMessage> initiateCapture = new Capture<TwoWayChannelMessage>(); connection.sendToClient(capture(initiateCapture)); replay(connection); dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, minTimeWindow, 40000, connection); dut.connectionOpen(); dut.receiveMessage(message); long expectedExpire = Utils.currentTimeSeconds() + minTimeWindow; assertServerVersion(); assertExpireTime(expectedExpire, initiateCapture); }
@Test public void shouldTruncateTooLargeTimeWindow() { final int maxTimeWindow = 40000; final int timeWindow = maxTimeWindow + 1; final TwoWayChannelMessage message = createClientVersionMessage(timeWindow); final Capture<TwoWayChannelMessage> initiateCapture = new Capture<TwoWayChannelMessage>(); connection.sendToClient(capture(initiateCapture)); replay(connection); dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, 20000, maxTimeWindow, connection); dut.connectionOpen(); dut.receiveMessage(message); long expectedExpire = Utils.currentTimeSeconds() + maxTimeWindow; assertServerVersion(); assertExpireTime(expectedExpire, initiateCapture); }
@Test public void testEncryptDecryptBytes2() throws KeyCrypterException { KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); // Encrypt random bytes of various lengths up to length 50. Random random = new Random(); for (int i = 0; i < 50; i++) { byte[] plainBytes = new byte[i]; random.nextBytes(plainBytes); EncryptedData encryptedPrivateKey = keyCrypter.encrypt(plainBytes, keyCrypter.deriveKey(PASSWORD1)); assertNotNull(encryptedPrivateKey); //log.debug("\nEncrypterDecrypterTest: cipherBytes = \nlength = " + cipherBytes.length + "\n---------------\n" + Utils.HEX.encode(cipherBytes) + "\n---------------\n"); byte[] rebornPlainBytes = keyCrypter.decrypt(encryptedPrivateKey, keyCrypter.deriveKey(PASSWORD1)); log.debug("Original: (" + i + ") " + Utils.HEX.encode(plainBytes)); log.debug("Reborn1 : (" + i + ") " + Utils.HEX.encode(rebornPlainBytes)); assertEquals(Utils.HEX.encode(plainBytes), Utils.HEX.encode(rebornPlainBytes)); } }
private static AltcoinBlock createGenesis(NetworkParameters params) { AltcoinBlock genesisBlock = new AltcoinBlock(params, Block.BLOCK_VERSION_GENESIS); Transaction t = new Transaction(params); try { byte[] bytes = Utils.HEX.decode ("04ffff001d0104084e696e746f6e646f"); t.addInput(new TransactionInput(params, t, bytes)); ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream(); Script.writeBytes(scriptPubKeyBytes, Utils.HEX.decode ("040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9")); scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG); t.addOutput(new TransactionOutput(params, t, COIN.multiply(88), scriptPubKeyBytes.toByteArray())); } catch (Exception e) { // Cannot happen. throw new RuntimeException(e); } genesisBlock.addTransaction(t); return genesisBlock; }
private static AltcoinBlock createGenesis(NetworkParameters params) { AltcoinBlock genesisBlock = new AltcoinBlock(params, Block.BLOCK_VERSION_GENESIS); Transaction t = new Transaction(params); try { // "... choose what comes next. Lives of your own, or a return to chains. -- V" byte[] bytes = Utils.HEX.decode ("04ff7f001c020a024b2e2e2e2063686f6f7365207768617420636f6d6573206e6578742e20204c69766573206f6620796f7572206f776e2c206f7220612072657475726e20746f20636861696e732e202d2d2056"); t.addInput(new TransactionInput(params, t, bytes)); ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream(); Script.writeBytes(scriptPubKeyBytes, Utils.HEX.decode ("04b620369050cd899ffbbc4e8ee51e8c4534a855bb463439d63d235d4779685d8b6f4870a238cf365ac94fa13ef9a2a22cd99d0d5ee86dcabcafce36c7acf43ce5")); scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG); t.addOutput(new TransactionOutput(params, t, COIN.multiply(50), scriptPubKeyBytes.toByteArray())); } catch (Exception e) { // Cannot happen. throw new RuntimeException(e); } genesisBlock.addTransaction(t); return genesisBlock; }
@Test public void shouldAllowExactTimeWindow() { final TwoWayChannelMessage message = createClientVersionMessage(); final Capture<TwoWayChannelMessage> initiateCapture = new Capture<TwoWayChannelMessage>(); connection.sendToClient(capture(initiateCapture)); replay(connection); final int expire = 24 * 60 * 60 - 60; // This the default defined in paymentchannel.proto dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, expire, expire, connection); dut.connectionOpen(); long expectedExpire = Utils.currentTimeSeconds() + expire; dut.receiveMessage(message); assertServerVersion(); assertExpireTime(expectedExpire, initiateCapture); }