@Test public void testProofOfWork() throws Exception { // This params accepts any difficulty target. NetworkParameters params = UnitTestParams.get(); Block block = params.getDefaultSerializer().makeBlock(blockBytes); block.setNonce(12346); try { block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class)); fail(); } catch (VerificationException e) { // Expected. } // Blocks contain their own difficulty target. The BlockChain verification mechanism is what // stops real blocks // from containing artificially weak difficulties. block.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET); // Now it should pass. block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class)); // Break the nonce again at the lower difficulty level so we can try solving for it. block.setNonce(1); try { block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class)); fail(); } catch (VerificationException e) { // Expected to fail as the nonce is no longer correct. } // Should find an acceptable nonce. block.solve(); block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class)); assertEquals(block.getNonce(), 2); }
private void parsePaymentRequest(Protos.PaymentRequest request) throws PaymentProtocolException { try { if (request == null) throw new PaymentProtocolException("request cannot be null"); if (request.getPaymentDetailsVersion() != 1) throw new PaymentProtocolException.InvalidVersion( "Version 1 required. Received version " + request.getPaymentDetailsVersion()); paymentRequest = request; if (!request.hasSerializedPaymentDetails()) throw new PaymentProtocolException("No PaymentDetails"); paymentDetails = Protos.PaymentDetails.newBuilder() .mergeFrom(request.getSerializedPaymentDetails()) .build(); if (paymentDetails == null) throw new PaymentProtocolException("Invalid PaymentDetails"); if (!paymentDetails.hasNetwork()) params = MainNetParams.get(); else params = NetworkParameters.fromPmtProtocolID(paymentDetails.getNetwork()); if (params == null) throw new PaymentProtocolException.InvalidNetwork( "Invalid network " + paymentDetails.getNetwork()); if (paymentDetails.getOutputsCount() < 1) throw new PaymentProtocolException.InvalidOutputs("No outputs"); for (Protos.Output output : paymentDetails.getOutputsList()) { if (output.hasAmount()) totalValue = totalValue.add(Coin.valueOf(output.getAmount())); } // This won't ever happen in practice. It would only happen if the user provided outputs // that are obviously invalid. Still, we don't want to silently overflow. if (params.hasMaxMoney() && totalValue.compareTo(params.getMaxMoney()) > 0) throw new PaymentProtocolException.InvalidOutputs("The outputs are way too big."); } catch (InvalidProtocolBufferException e) { throw new PaymentProtocolException(e); } }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NetworkParameters other = (NetworkParameters) o; return getId().equals(other.getId()); }
@Test public void testHeaderParse() throws Exception { Block block = params.getDefaultSerializer().makeBlock(blockBytes); Block header = block.cloneAsHeader(); Block reparsed = params.getDefaultSerializer().makeBlock(header.bitcoinSerialize()); assertEquals(reparsed, header); }
@Test public void testGetOpenTransactionOutputs() throws Exception { final int UNDOABLE_BLOCKS_STORED = 10; store = createStore(params, UNDOABLE_BLOCKS_STORED); chain = new FullPrunedBlockChain(params, store); // Check that we aren't accidentally leaving any references // to the full StoredUndoableBlock's lying around (ie memory leaks) ECKey outKey = new ECKey(); int height = 1; // Build some blocks on genesis block to create a spendable output Block rollingBlock = params .getGenesisBlock() .createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++); chain.add(rollingBlock); Transaction transaction = rollingBlock.getTransactions().get(0); TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, transaction.getHash()); byte[] spendableOutputScriptPubKey = transaction.getOutputs().get(0).getScriptBytes(); for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { rollingBlock = rollingBlock.createNextBlockWithCoinbase( Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++); chain.add(rollingBlock); } rollingBlock = rollingBlock.createNextBlock(null); // Create bitcoin spend of 1 BTC. ECKey toKey = new ECKey(); Coin amount = Coin.valueOf(100000000); Address address = new Address(params, toKey.getPubKeyHash()); Coin totalAmount = Coin.ZERO; Transaction t = new Transaction(params); t.addOutput(new TransactionOutput(params, t, amount, toKey)); t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey); rollingBlock.addTransaction(t); rollingBlock.solve(); chain.add(rollingBlock); totalAmount = totalAmount.add(amount); List<UTXO> outputs = store.getOpenTransactionOutputs(Lists.newArrayList(address)); assertNotNull(outputs); assertEquals("Wrong Number of Outputs", 1, outputs.size()); UTXO output = outputs.get(0); assertEquals("The address is not equal", address.toString(), output.getAddress()); assertEquals("The amount is not equal", totalAmount, output.getValue()); outputs = null; output = null; try { store.close(); } catch (Exception e) { } }
/** Test that if the block height is missing from coinbase of a version 2 block, it's rejected. */ @Test public void missingHeightFromCoinbase() throws Exception { final int UNDOABLE_BLOCKS_STORED = params.getMajorityEnforceBlockUpgrade() + 1; store = createStore(params, UNDOABLE_BLOCKS_STORED); try { chain = new FullPrunedBlockChain(params, store); ECKey outKey = new ECKey(); int height = 1; Block chainHead = params.getGenesisBlock(); // Build some blocks on genesis block to create a spendable output. // Put in just enough v1 blocks to stop the v2 blocks from forming a majority for (height = 1; height <= (params.getMajorityWindow() - params.getMajorityEnforceBlockUpgrade()); height++) { chainHead = chainHead.createNextBlockWithCoinbase( Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height); chain.add(chainHead); } // Fill the rest of the window in with v2 blocks for (; height < params.getMajorityWindow(); height++) { chainHead = chainHead.createNextBlockWithCoinbase( Block.BLOCK_VERSION_BIP34, outKey.getPubKey(), height); chain.add(chainHead); } // Throw a broken v2 block in before we have a supermajority to enable // enforcement, which should validate as-is chainHead = chainHead.createNextBlockWithCoinbase( Block.BLOCK_VERSION_BIP34, outKey.getPubKey(), height * 2); chain.add(chainHead); height++; // Trying to add a broken v2 block should now result in rejection as // we have a v2 supermajority thrown.expect(VerificationException.CoinbaseHeightMismatch.class); chainHead = chainHead.createNextBlockWithCoinbase( Block.BLOCK_VERSION_BIP34, outKey.getPubKey(), height * 2); chain.add(chainHead); } catch (final VerificationException ex) { throw (Exception) ex.getCause(); } finally { try { store.close(); } catch (Exception e) { // Catch and drop any exception so a break mid-test doesn't result // in a new exception being thrown and the original lost } } }
private byte[] serialize(NetworkParameters params, boolean pub) { ByteBuffer ser = ByteBuffer.allocate(78); ser.putInt(pub ? params.getBip32HeaderPub() : params.getBip32HeaderPriv()); ser.put((byte) getDepth()); ser.putInt(getParentFingerprint()); ser.putInt(getChildNumber().i()); ser.put(getChainCode()); ser.put(pub ? getPubKey() : getPrivKeyBytes33()); checkState(ser.position() == 78); return ser.array(); }
public BitcoinCrypto(NetworkParameters networkParameters, DeterministicSeed seed) throws Exception, NoSuchAlgorithmException { this.params = NetworkParameters.fromID(networkParameters.getId()); this.keyChainGroup = new KeyChainGroup(networkParameters, seed); Security.insertProviderAt(new BouncyCastleProvider(), 1); crashIfJCEMissing(); // this.sr = SecureRandom.getInstance("SHA1PRNG", new BouncyCastleProvider()); this.sr = SecureRandom.getInstance("SHA1PRNG"); this.keyPG = KeyPairGenerator.getInstance("ECIES", new BouncyCastleProvider()); // this.kit = initKit(seed); // this.wallet = kit.wallet(); }
@Test public void testUpdateLength() { NetworkParameters params = UnitTestParams.get(); Block block = params .getGenesisBlock() .createNextBlockWithCoinbase( Block.BLOCK_VERSION_GENESIS, new ECKey().getPubKey(), Block.BLOCK_HEIGHT_GENESIS); assertEquals(block.bitcoinSerialize().length, block.length); final int origBlockLen = block.length; Transaction tx = new Transaction(params); // this is broken until the transaction has > 1 input + output (which is required anyway...) // assertTrue(tx.length == tx.bitcoinSerialize().length && tx.length == 8); byte[] outputScript = new byte[10]; Arrays.fill(outputScript, (byte) ScriptOpCodes.OP_FALSE); tx.addOutput(new TransactionOutput(params, null, Coin.SATOSHI, outputScript)); tx.addInput( new TransactionInput( params, null, new byte[] {(byte) ScriptOpCodes.OP_FALSE}, new TransactionOutPoint(params, 0, Sha256Hash.of(new byte[] {1})))); int origTxLength = 8 + 2 + 8 + 1 + 10 + 40 + 1 + 1; assertEquals(tx.bitcoinSerialize().length, tx.length); assertEquals(origTxLength, tx.length); block.addTransaction(tx); assertEquals(block.bitcoinSerialize().length, block.length); assertEquals(origBlockLen + tx.length, block.length); block .getTransactions() .get(1) .getInputs() .get(0) .setScriptBytes(new byte[] {(byte) ScriptOpCodes.OP_FALSE, (byte) ScriptOpCodes.OP_FALSE}); assertEquals(block.length, origBlockLen + tx.length); assertEquals(tx.length, origTxLength + 1); block.getTransactions().get(1).getInputs().get(0).setScriptBytes(new byte[] {}); assertEquals(block.length, block.bitcoinSerialize().length); assertEquals(block.length, origBlockLen + tx.length); assertEquals(tx.length, origTxLength - 1); block .getTransactions() .get(1) .addInput( new TransactionInput( params, null, new byte[] {(byte) ScriptOpCodes.OP_FALSE}, new TransactionOutPoint(params, 0, Sha256Hash.of(new byte[] {1})))); assertEquals(block.length, origBlockLen + tx.length); assertEquals(tx.length, origTxLength + 41); // - 1 + 40 + 1 + 1 }
/** * Create a standard pay to address output for usage in {@link #createPaymentRequest} and {@link * #createPaymentMessage}. * * @param amount amount to pay, or null * @param address address to pay to * @return output */ public static Protos.Output createPayToAddressOutput(@Nullable Coin amount, Address address) { Protos.Output.Builder output = Protos.Output.newBuilder(); if (amount != null) { final NetworkParameters params = address.getParameters(); if (params.hasMaxMoney() && amount.compareTo(params.getMaxMoney()) > 0) throw new IllegalArgumentException("Amount too big: " + amount); output.setAmount(amount.value); } else { output.setAmount(0); } output.setScript(ByteString.copyFrom(ScriptBuilder.createOutputScript(address).getProgram())); return output.build(); }
private static void verifyDifficulty( StoredBlock prevBlock, Block added, BigInteger calcDiff, NetworkParameters params) { if (calcDiff.compareTo(params.getMaxTarget()) > 0) { log.info("Difficulty hit proof of work limit: {}", calcDiff.toString(16)); calcDiff = params.getMaxTarget(); } int accuracyBytes = (int) (added.getDifficultyTarget() >>> 24) - 3; final BigInteger receivedDifficulty = added.getDifficultyTargetAsInteger(); // The calculated difficulty is to a higher precision than received, so reduce here. final BigInteger mask = BigInteger.valueOf(0xFFFFFFL).shiftLeft(accuracyBytes * 8); calcDiff = calcDiff.and(mask); if (CoinDefinition.TEST_NETWORK_STANDARD.equals(params.getStandardNetworkId())) { if (calcDiff.compareTo(receivedDifficulty) != 0) { throw new VerificationException( "Network provided difficulty bits do not match what was calculated: " + receivedDifficulty.toString(16) + " vs " + calcDiff.toString(16)); } } else { final int height = prevBlock.getHeight() + 1; if (height <= 68589) { long nBitsNext = added.getDifficultyTarget(); long calcDiffBits = (accuracyBytes + 3) << 24; calcDiffBits |= calcDiff.shiftRight(accuracyBytes * 8).longValue(); final double n1 = CommonUtils.convertBitsToDouble(calcDiffBits); final double n2 = CommonUtils.convertBitsToDouble(nBitsNext); if (Math.abs(n1 - n2) > n1 * 0.2) { throw new VerificationException( "Network provided difficulty bits do not match what was calculated: " + receivedDifficulty.toString(16) + " vs " + calcDiff.toString(16)); } } else { if (calcDiff.compareTo(receivedDifficulty) != 0) { throw new VerificationException( "Network provided difficulty bits do not match what was calculated: " + receivedDifficulty.toString(16) + " vs " + calcDiff.toString(16)); } } } }
/** * Deserialize an HD Key. * * @param parent The parent node in the given key's deterministic hierarchy. */ public static DeterministicKey deserialize( NetworkParameters params, byte[] serializedKey, @Nullable DeterministicKey parent) { ByteBuffer buffer = ByteBuffer.wrap(serializedKey); int header = buffer.getInt(); if (header != params.getBip32HeaderPriv() && header != params.getBip32HeaderPub()) throw new IllegalArgumentException( "Unknown header bytes: " + toBase58(serializedKey).substring(0, 4)); boolean pub = header == params.getBip32HeaderPub(); int depth = buffer.get() & 0xFF; // convert signed byte to positive int since depth cannot be negative final int parentFingerprint = buffer.getInt(); final int i = buffer.getInt(); final ChildNumber childNumber = new ChildNumber(i); ImmutableList<ChildNumber> path; if (parent != null) { if (parentFingerprint == 0) throw new IllegalArgumentException("Parent was provided but this key doesn't have one"); if (parent.getFingerprint() != parentFingerprint) throw new IllegalArgumentException("Parent fingerprints don't match"); path = HDUtils.append(parent.getPath(), childNumber); if (path.size() != depth) throw new IllegalArgumentException("Depth does not match"); } else { if (depth >= 1) // We have been given a key that is not a root key, yet we lack the object representing the // parent. // This can happen when deserializing an account key for a watching wallet. In this case, // we assume that // the client wants to conceal the key's position in the hierarchy. The path is truncated // at the // parent's node. path = ImmutableList.of(childNumber); else path = ImmutableList.of(); } byte[] chainCode = new byte[32]; buffer.get(chainCode); byte[] data = new byte[33]; buffer.get(data); checkArgument(!buffer.hasRemaining(), "Found unexpected data in key"); if (pub) { return new DeterministicKey( path, chainCode, new LazyECPoint(ECKey.CURVE.getCurve(), data), parent, depth, parentFingerprint); } else { return new DeterministicKey( path, chainCode, new BigInteger(1, data), parent, depth, parentFingerprint); } }
@Test public void testLastBlockSeenHash() throws Exception { // Test the lastBlockSeenHash field works. // LastBlockSeenHash should be empty if never set. Wallet wallet = new Wallet(params); Protos.Wallet walletProto = new WalletProtobufSerializer().walletToProto(wallet); ByteString lastSeenBlockHash = walletProto.getLastSeenBlockHash(); assertTrue(lastSeenBlockHash.isEmpty()); // Create a block. Block block = params.getDefaultSerializer().makeBlock(BlockTest.blockBytes); Sha256Hash blockHash = block.getHash(); wallet.setLastBlockSeenHash(blockHash); wallet.setLastBlockSeenHeight(1); // Roundtrip the wallet and check it has stored the blockHash. Wallet wallet1 = roundTrip(wallet); assertEquals(blockHash, wallet1.getLastBlockSeenHash()); assertEquals(1, wallet1.getLastBlockSeenHeight()); // Test the Satoshi genesis block (hash of all zeroes) is roundtripped ok. Block genesisBlock = MainNetParams.get().getGenesisBlock(); wallet.setLastBlockSeenHash(genesisBlock.getHash()); Wallet wallet2 = roundTrip(wallet); assertEquals(genesisBlock.getHash(), wallet2.getLastBlockSeenHash()); }
/** * Parse transactions from payment message. * * @param params network parameters (needed for transaction deserialization) * @param paymentMessage payment message to parse * @return list of transactions */ public static List<Transaction> parseTransactionsFromPaymentMessage( NetworkParameters params, Protos.Payment paymentMessage) { final List<Transaction> transactions = new ArrayList<Transaction>(paymentMessage.getTransactionsCount()); for (final ByteString transaction : paymentMessage.getTransactionsList()) transactions.add(params.getDefaultSerializer().makeTransaction(transaction.toByteArray())); return transactions; }
@Test public void testBlockVerification() throws Exception { Block block = params.getDefaultSerializer().makeBlock(blockBytes); block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class)); assertEquals( "00000000a6e5eb79dcec11897af55e90cd571a4335383a3ccfbc12ec81085935", block.getHashAsString()); }
@Test public void testReceiveCoinbaseTransaction() throws Exception { // Block 169482 (hash 0000000000000756935f1ee9d5987857b604046f846d3df56d024cdb5f368665) // contains coinbase transactions that are mining pool shares. // The private key MINERS_KEY is used to check transactions are received by a wallet correctly. // The address for this private key is 1GqtGtn4fctXuKxsVzRPSLmYWN1YioLi9y. final String MINING_PRIVATE_KEY = "5JDxPrBRghF1EvSBjDigywqfmAjpHPmTJxYtQTYJxJRHLLQA4mG"; final long BLOCK_NONCE = 3973947400L; final Coin BALANCE_AFTER_BLOCK = Coin.valueOf(22223642); final NetworkParameters PARAMS = MainNetParams.get(); Block block169482 = PARAMS .getDefaultSerializer() .makeBlock(ByteStreams.toByteArray(getClass().getResourceAsStream("block169482.dat"))); // Check block. assertNotNull(block169482); block169482.verify(169482, EnumSet.noneOf(Block.VerifyFlag.class)); assertEquals(BLOCK_NONCE, block169482.getNonce()); StoredBlock storedBlock = new StoredBlock(block169482, BigInteger.ONE, 169482); // Nonsense work - not used in test. // Create a wallet contain the miner's key that receives a spend from a coinbase. ECKey miningKey = DumpedPrivateKey.fromBase58(PARAMS, MINING_PRIVATE_KEY).getKey(); assertNotNull(miningKey); Context context = new Context(PARAMS); Wallet wallet = new Wallet(context); wallet.importKey(miningKey); // Initial balance should be zero by construction. assertEquals(Coin.ZERO, wallet.getBalance()); // Give the wallet the first transaction in the block - this is the coinbase tx. List<Transaction> transactions = block169482.getTransactions(); assertNotNull(transactions); wallet.receiveFromBlock(transactions.get(0), storedBlock, NewBlockType.BEST_CHAIN, 0); // Coinbase transaction should have been received successfully but be unavailable to spend (too // young). assertEquals(BALANCE_AFTER_BLOCK, wallet.getBalance(BalanceType.ESTIMATED)); assertEquals(Coin.ZERO, wallet.getBalance(BalanceType.AVAILABLE)); }
@Test public void testWork() throws Exception { BigInteger work = params.getGenesisBlock().getWork(); // This number is printed by Bitcoin Core at startup as the calculated value of chainWork on // testnet: // // SetBestChain: new best=00000007199508e34a9f height=0 work=536879104 assertEquals(BigInteger.valueOf(536879104L), work); }
@Override protected int backTill(NetworkParameters params, StoredBlock prevBlock, Block added) { final int height = prevBlock.getHeight(); final int interval = params.getInterval(prevBlock.getHeader(), height); // Dash: This fixes an issue where a 51% attack can change difficulty at will. // Go back the full period unless it's the first retarget after genesis. // Code courtesy of Art Forz. return (height + 1 != interval) ? interval : height; }
@Test public void skipScripts() throws Exception { store = createStore(params, 10); chain = new FullPrunedBlockChain(params, store); // Check that we aren't accidentally leaving any references // to the full StoredUndoableBlock's lying around (ie memory leaks) ECKey outKey = new ECKey(); int height = 1; // Build some blocks on genesis block to create a spendable output Block rollingBlock = params .getGenesisBlock() .createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++); chain.add(rollingBlock); TransactionOutput spendableOutput = rollingBlock.getTransactions().get(0).getOutput(0); for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { rollingBlock = rollingBlock.createNextBlockWithCoinbase( Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++); chain.add(rollingBlock); } rollingBlock = rollingBlock.createNextBlock(null); Transaction t = new Transaction(params); t.addOutput(new TransactionOutput(params, t, FIFTY_COINS, new byte[] {})); TransactionInput input = t.addInput(spendableOutput); // Invalid script. input.clearScriptBytes(); rollingBlock.addTransaction(t); rollingBlock.solve(); chain.setRunScripts(false); try { chain.add(rollingBlock); } catch (VerificationException e) { fail(); } try { store.close(); } catch (Exception e) { } }
@Test public void testBitcoinSerialization() throws Exception { // We have to be able to reserialize everything exactly as we found it for hashing to work. This // test also // proves that transaction serialization works, along with all its subobjects like scripts and // in/outpoints. // // NB: This tests the bitcoin serialization protocol. Block block = params.getDefaultSerializer().makeBlock(blockBytes); assertTrue(Arrays.equals(blockBytes, block.bitcoinSerialize())); }
@Override public void deserializeWalletExtension(Wallet containingWallet, byte[] data) throws Exception { lock.lock(); try { checkState(this.containingWallet == null || this.containingWallet == containingWallet); this.containingWallet = containingWallet; NetworkParameters params = containingWallet.getParams(); ClientState.StoredClientPaymentChannels states = ClientState.StoredClientPaymentChannels.parseFrom(data); for (ClientState.StoredClientPaymentChannel storedState : states.getChannelsList()) { Transaction refundTransaction = params .getDefaultSerializer() .makeTransaction(storedState.getRefundTransaction().toByteArray()); refundTransaction.getConfidence().setSource(TransactionConfidence.Source.SELF); ECKey myKey = (storedState.getMyKey().isEmpty()) ? containingWallet.findKeyFromPubKey(storedState.getMyPublicKey().toByteArray()) : ECKey.fromPrivate(storedState.getMyKey().toByteArray()); StoredClientChannel channel = new StoredClientChannel( Sha256Hash.wrap(storedState.getId().toByteArray()), params .getDefaultSerializer() .makeTransaction(storedState.getContractTransaction().toByteArray()), refundTransaction, myKey, Coin.valueOf(storedState.getValueToMe()), Coin.valueOf(storedState.getRefundFees()), false); if (storedState.hasCloseTransactionHash()) { Sha256Hash closeTxHash = Sha256Hash.wrap(storedState.getCloseTransactionHash().toByteArray()); channel.close = containingWallet.getTransaction(closeTxHash); } putChannel(channel, false); } } finally { lock.unlock(); } }
/** * @param params The network parameters or null * @param nameValuePairTokens The tokens representing the name value pairs (assumed to be * separated by '=' e.g. 'amount=0.2') */ private void parseParameters( @Nullable NetworkParameters params, String addressToken, String[] nameValuePairTokens) throws BitcoinURIParseException { // Attempt to decode the rest of the tokens into a parameter map. for (String nameValuePairToken : nameValuePairTokens) { final int sepIndex = nameValuePairToken.indexOf('='); if (sepIndex == -1) throw new BitcoinURIParseException( "Malformed Bitcoin URI - no separator in '" + nameValuePairToken + "'"); if (sepIndex == 0) throw new BitcoinURIParseException( "Malformed Bitcoin URI - empty name '" + nameValuePairToken + "'"); final String nameToken = nameValuePairToken.substring(0, sepIndex).toLowerCase(Locale.ENGLISH); final String valueToken = nameValuePairToken.substring(sepIndex + 1); // Parse the amount. if (FIELD_AMOUNT.equals(nameToken)) { // Decode the amount (contains an optional decimal component to 8dp). try { Coin amount = Coin.parseCoin(valueToken); if (params != null && amount.isGreaterThan(params.getMaxMoney())) throw new BitcoinURIParseException("Max number of coins exceeded"); if (amount.signum() < 0) throw new ArithmeticException("Negative coins specified"); putWithValidation(FIELD_AMOUNT, amount); } catch (IllegalArgumentException e) { throw new OptionalFieldValidationException( String.format(Locale.US, "'%s' is not a valid amount", valueToken), e); } catch (ArithmeticException e) { throw new OptionalFieldValidationException( String.format(Locale.US, "'%s' has too many decimal places", valueToken), e); } } else { if (nameToken.startsWith("req-")) { // A required parameter that we do not know about. throw new RequiredFieldValidationException( "'" + nameToken + "' is required but not known, this URI is not valid"); } else { // Known fields and unknown parameters that are optional. try { if (valueToken.length() > 0) putWithValidation(nameToken, URLDecoder.decode(valueToken, "UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); // can't happen } } } } // Note to the future: when you want to implement 'req-expires' have a look at commit // 410a53791841 // which had it in. }
/** * Parses a wallet from the given stream, using the provided Wallet instance to load data into. * This is primarily used when you want to register extensions. Data in the proto will be added * into the wallet where applicable and overwrite where not. * * <p>A wallet can be unreadable for various reasons, such as inability to open the file, corrupt * data, internally inconsistent data, a wallet extension marked as mandatory that cannot be * handled and so on. You should always handle {@link UnreadableWalletException} and communicate * failure to the user in an appropriate manner. * * @throws UnreadableWalletException thrown in various error conditions (see description). */ public Wallet readWallet(InputStream input) throws UnreadableWalletException { try { Protos.Wallet walletProto = parseToProto(input); final String paramsID = walletProto.getNetworkIdentifier(); NetworkParameters params = NetworkParameters.fromID(paramsID); if (params == null) throw new UnreadableWalletException("Unknown network parameters ID " + paramsID); return readWallet(params, null, walletProto); } catch (IOException e) { throw new UnreadableWalletException("Could not parse input stream to protobuf", e); } }
/** * Cheap test to see if input stream is a wallet. This checks for a magic value at the beginning * of the stream. * * @param is input stream to test * @return true if input stream is a wallet */ public static boolean isWallet(InputStream is) { try { final CodedInputStream cis = CodedInputStream.newInstance(is); final int tag = cis.readTag(); final int field = WireFormat.getTagFieldNumber(tag); if (field != 1) // network_identifier return false; final String network = cis.readString(); return NetworkParameters.fromID(network) != null; } catch (IOException x) { return false; } }
/** * Generates a Payment message based on the information in the PaymentRequest. Provide * transactions built by the wallet. If the PaymentRequest did not specify a payment_url, returns * null. * * @param txns list of transactions to be included with the Payment message. * @param refundAddr will be used by the merchant to send money back if there was a problem. * @param memo is a message to include in the payment message sent to the merchant. */ @Nullable public Protos.Payment getPayment( List<Transaction> txns, @Nullable Address refundAddr, @Nullable String memo) throws IOException, PaymentProtocolException.InvalidNetwork { if (paymentDetails.hasPaymentUrl()) { for (Transaction tx : txns) if (!tx.getParams().equals(params)) throw new PaymentProtocolException.InvalidNetwork(params.getPaymentProtocolId()); return PaymentProtocol.createPaymentMessage( txns, totalValue, refundAddr, memo, getMerchantData()); } else { return null; } }
@Test public void testBadTransactions() throws Exception { Block block = params.getDefaultSerializer().makeBlock(blockBytes); // Re-arrange so the coinbase transaction is not first. Transaction tx1 = block.transactions.get(0); Transaction tx2 = block.transactions.get(1); block.transactions.set(0, tx2); block.transactions.set(1, tx1); try { block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class)); fail(); } catch (VerificationException e) { // We should get here. } }
/** * Simple Bitcoin URI builder using known good fields. * * @param params The network parameters that determine which network the URI is for. * @param address The Bitcoin address * @param amount The amount * @param label A label * @param message A message * @return A String containing the Bitcoin URI */ public static String convertToBitcoinURI( NetworkParameters params, String address, @Nullable Coin amount, @Nullable String label, @Nullable String message) { checkNotNull(params); checkNotNull(address); if (amount != null && amount.signum() < 0) { throw new IllegalArgumentException("Coin must be positive"); } StringBuilder builder = new StringBuilder(); String scheme = params.getUriScheme(); builder.append(scheme).append(":").append(address); boolean questionMarkHasBeenOutput = false; if (amount != null) { builder.append(QUESTION_MARK_SEPARATOR).append(FIELD_AMOUNT).append("="); builder.append(amount.toPlainString()); questionMarkHasBeenOutput = true; } if (label != null && !"".equals(label)) { if (questionMarkHasBeenOutput) { builder.append(AMPERSAND_SEPARATOR); } else { builder.append(QUESTION_MARK_SEPARATOR); questionMarkHasBeenOutput = true; } builder.append(FIELD_LABEL).append("=").append(encodeURLString(label)); } if (message != null && !"".equals(message)) { if (questionMarkHasBeenOutput) { builder.append(AMPERSAND_SEPARATOR); } else { builder.append(QUESTION_MARK_SEPARATOR); } builder.append(FIELD_MESSAGE).append("=").append(encodeURLString(message)); } return builder.toString(); }
/** * Create a payment request. You may want to sign the request using {@link #signPaymentRequest}. * Use {@link Protos.PaymentRequest.Builder#build} to get the actual payment request. * * @param params network parameters * @param outputs list of outputs to request coins to * @param memo arbitrary, user readable memo, or null if none * @param paymentUrl URL to send payment message to, or null if none * @param merchantData arbitrary merchant data, or null if none * @return created payment request, in its builder form */ public static Protos.PaymentRequest.Builder createPaymentRequest( NetworkParameters params, List<Protos.Output> outputs, @Nullable String memo, @Nullable String paymentUrl, @Nullable byte[] merchantData) { final Protos.PaymentDetails.Builder paymentDetails = Protos.PaymentDetails.newBuilder(); paymentDetails.setNetwork(params.getPaymentProtocolId()); for (Protos.Output output : outputs) paymentDetails.addOutputs(output); if (memo != null) paymentDetails.setMemo(memo); if (paymentUrl != null) paymentDetails.setPaymentUrl(paymentUrl); if (merchantData != null) paymentDetails.setMerchantData(ByteString.copyFrom(merchantData)); paymentDetails.setTime(Utils.currentTimeSeconds()); final Protos.PaymentRequest.Builder paymentRequest = Protos.PaymentRequest.newBuilder(); paymentRequest.setSerializedPaymentDetails(paymentDetails.build().toByteString()); return paymentRequest; }
@Override public void verifyDifficultyTransitions( StoredBlock prevBlock, Block added, NetworkParameters params) { int diffMode = 1; final int heightInc = prevBlock.getHeight() + 1; if (CoinDefinition.TEST_NETWORK_STANDARD.equals(params.getStandardNetworkId())) { if (heightInc >= 2000) { diffMode = 4; } } else { if (heightInc >= 68589) { diffMode = 4; } else if (heightInc >= 34140) { diffMode = 3; } else if (heightInc >= 15200) { diffMode = 2; } } try { switch (diffMode) { case 4: darkGravityWave3Check( prevBlock, added, linearExtension.getBlockChain().getBlockStore(), params); return; case 1: linearExtension.verifyDifficultyTransitions(prevBlock, added, params); return; case 2: kimotoGravityWellCheck( prevBlock, added, linearExtension.getBlockChain().getBlockStore(), params); return; case 3: darkGravityWaveCheck( prevBlock, added, linearExtension.getBlockChain().getBlockStore(), params); return; default: throw new RuntimeException("Unreachable"); } } catch (BlockStoreException ex) { throw new VerificationException( "Block store exception during difficulty transitions check", ex); } }
@Test public void coinbaseTxns() throws Exception { // Covers issue 420 where the outpoint index of a coinbase tx input was being mis-serialized. Block b = params .getGenesisBlock() .createNextBlockWithCoinbase( Block.BLOCK_VERSION_GENESIS, myKey.getPubKey(), FIFTY_COINS, Block.BLOCK_HEIGHT_GENESIS); Transaction coinbase = b.getTransactions().get(0); assertTrue(coinbase.isCoinBase()); BlockChain chain = new BlockChain(params, myWallet, new MemoryBlockStore(params)); assertTrue(chain.add(b)); // Wallet now has a coinbase tx in it. assertEquals(1, myWallet.getTransactions(true).size()); assertTrue(myWallet.getTransaction(coinbase.getHash()).isCoinBase()); Wallet wallet2 = roundTrip(myWallet); assertEquals(1, wallet2.getTransactions(true).size()); assertTrue(wallet2.getTransaction(coinbase.getHash()).isCoinBase()); }