From 8c28b14f89b67052e7d2ff9a2b2e9ad4832ed1f1 Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Thu, 27 Feb 2025 22:22:23 +0100 Subject: [PATCH] PaymentProtocol, PaymentSession: remove BIP70 from bitcoinj It had its time, but nowadays nobody is using it any more. Let's leave it behind. --- .../org/bitcoinj/core/NetworkParameters.java | 38 +- .../org/bitcoinj/crypto/TrustStoreLoader.java | 109 ---- .../java/org/bitcoinj/crypto/X509Utils.java | 108 ---- .../bitcoinj/params/BitcoinNetworkParams.java | 10 - .../org/bitcoinj/protocols/package-info.java | 21 - .../protocols/payments/PaymentProtocol.java | 491 ------------------ .../payments/PaymentProtocolException.java | 107 ---- .../protocols/payments/PaymentSession.java | 438 ---------------- .../protocols/payments/package-info.java | 21 - .../testing/MockAltNetworkParams.java | 5 - .../java/org/bitcoinj/uri/BitcoinURI.java | 31 +- .../java/org/bitcoinj/wallet/SendRequest.java | 8 - core/src/main/proto/paymentrequest.proto | 73 --- .../org/bitcoinj/crypto/X509UtilsTest.java | 40 -- .../payments/PaymentProtocolTest.java | 162 ------ .../payments/PaymentSessionTest.java | 238 --------- .../java/org/bitcoinj/uri/BitcoinURITest.java | 33 -- .../org/bitcoinj/crypto/comodo-smime.crt | 30 -- .../org/bitcoinj/crypto/startssl-client.crt | 41 -- .../org/bitcoinj/crypto/startssl-smime.crt | 40 -- .../org/bitcoinj/protocols/payments/README | 6 - .../payments/pki_test.bitcoinpaymentrequest | Bin 4106 -> 0 bytes .../protocols/payments/test-cacert.key | 54 -- .../bitcoinj/protocols/payments/test-cacerts | Bin 1496 -> 0 bytes .../protocols/payments/test-expired-cert | Bin 3936 -> 0 bytes .../protocols/payments/test-valid-cert | Bin 3934 -> 0 bytes .../bitcoinj/tools/PaymentProtocolTool.java | 111 ---- .../org/bitcoinj/wallettool/WalletTool.java | 153 +----- 28 files changed, 5 insertions(+), 2363 deletions(-) delete mode 100644 core/src/main/java/org/bitcoinj/crypto/TrustStoreLoader.java delete mode 100644 core/src/main/java/org/bitcoinj/crypto/X509Utils.java delete mode 100644 core/src/main/java/org/bitcoinj/protocols/package-info.java delete mode 100644 core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java delete mode 100644 core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocolException.java delete mode 100644 core/src/main/java/org/bitcoinj/protocols/payments/PaymentSession.java delete mode 100644 core/src/main/java/org/bitcoinj/protocols/payments/package-info.java delete mode 100644 core/src/main/proto/paymentrequest.proto delete mode 100644 core/src/test/java/org/bitcoinj/crypto/X509UtilsTest.java delete mode 100644 core/src/test/java/org/bitcoinj/protocols/payments/PaymentProtocolTest.java delete mode 100644 core/src/test/java/org/bitcoinj/protocols/payments/PaymentSessionTest.java delete mode 100644 core/src/test/resources/org/bitcoinj/crypto/comodo-smime.crt delete mode 100644 core/src/test/resources/org/bitcoinj/crypto/startssl-client.crt delete mode 100644 core/src/test/resources/org/bitcoinj/crypto/startssl-smime.crt delete mode 100644 core/src/test/resources/org/bitcoinj/protocols/payments/README delete mode 100644 core/src/test/resources/org/bitcoinj/protocols/payments/pki_test.bitcoinpaymentrequest delete mode 100644 core/src/test/resources/org/bitcoinj/protocols/payments/test-cacert.key delete mode 100644 core/src/test/resources/org/bitcoinj/protocols/payments/test-cacerts delete mode 100644 core/src/test/resources/org/bitcoinj/protocols/payments/test-expired-cert delete mode 100644 core/src/test/resources/org/bitcoinj/protocols/payments/test-valid-cert delete mode 100644 tools/src/main/java/org/bitcoinj/tools/PaymentProtocolTool.java diff --git a/core/src/main/java/org/bitcoinj/core/NetworkParameters.java b/core/src/main/java/org/bitcoinj/core/NetworkParameters.java index d4f1ff02375..b67ded63cb6 100644 --- a/core/src/main/java/org/bitcoinj/core/NetworkParameters.java +++ b/core/src/main/java/org/bitcoinj/core/NetworkParameters.java @@ -26,7 +26,6 @@ import org.bitcoinj.crypto.DumpedPrivateKey; import org.bitcoinj.params.BitcoinNetworkParams; import org.bitcoinj.params.Networks; -import org.bitcoinj.protocols.payments.PaymentProtocol; import org.bitcoinj.script.Script; import org.bitcoinj.store.BlockStore; import org.bitcoinj.store.BlockStoreException; @@ -50,23 +49,7 @@ * them, you are encouraged to call the static get() methods on each specific params class directly.

*/ public abstract class NetworkParameters { - - /** The string used by the payment protocol to represent the main net. */ - @Deprecated - public static final String PAYMENT_PROTOCOL_ID_MAINNET = PaymentProtocol.PAYMENT_PROTOCOL_ID_MAINNET; - /** The string used by the payment protocol to represent the test net. */ - @Deprecated - public static final String PAYMENT_PROTOCOL_ID_TESTNET = PaymentProtocol.PAYMENT_PROTOCOL_ID_TESTNET; - /** The string used by the payment protocol to represent signet (note that this is non-standard). */ - @Deprecated - public static final String PAYMENT_PROTOCOL_ID_SIGNET = PaymentProtocol.PAYMENT_PROTOCOL_ID_SIGNET; - /** The string used by the payment protocol to represent unit testing (note that this is non-standard). */ - @Deprecated - public static final String PAYMENT_PROTOCOL_ID_UNIT_TESTS = PaymentProtocol.PAYMENT_PROTOCOL_ID_UNIT_TESTS; - @Deprecated - public static final String PAYMENT_PROTOCOL_ID_REGTEST = PaymentProtocol.PAYMENT_PROTOCOL_ID_REGTEST; - - // TODO: Seed nodes should be here as well. + // TODO: Seed nodes should be here as well. protected BigInteger maxTarget; protected int port; @@ -149,13 +132,6 @@ public Network network() { return network; } - /** - * @return the payment protocol network id string - * @deprecated Use {@link PaymentProtocol#protocolIdFromParams(NetworkParameters)} - */ - @Deprecated - public abstract String getPaymentProtocolId(); - @Override public boolean equals(Object o) { if (this == o) return true; @@ -194,18 +170,6 @@ public static NetworkParameters of(Network network) { : Networks.find(network).orElseThrow(() -> new IllegalArgumentException("Unknown network")); } - /** - * Return network parameters for a paymentProtocol ID string - * @param pmtProtocolId paymentProtocol ID string - * @return network parameters for the given string paymentProtocolID or NULL if not recognized - * @deprecated Use {@link PaymentProtocol#paramsFromPmtProtocolID(String)} (String)} - */ - @Nullable - @Deprecated - public static NetworkParameters fromPmtProtocolID(String pmtProtocolId) { - return PaymentProtocol.paramsFromPmtProtocolID(pmtProtocolId); - } - public int getSpendableCoinbaseDepth() { return spendableCoinbaseDepth; } diff --git a/core/src/main/java/org/bitcoinj/crypto/TrustStoreLoader.java b/core/src/main/java/org/bitcoinj/crypto/TrustStoreLoader.java deleted file mode 100644 index f9cc67e7467..00000000000 --- a/core/src/main/java/org/bitcoinj/crypto/TrustStoreLoader.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2014 Andreas Schildbach - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoinj.crypto; - -import javax.annotation.Nonnull; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.KeyStoreException; - -/** - * An implementation of TrustStoreLoader handles fetching a KeyStore from the operating system, a file, etc. It's - * necessary because the Java {@link KeyStore} abstraction is not completely seamless and for example - * we sometimes need slightly different techniques to load the key store on different versions of Android, MacOS, - * Windows, etc. - */ -public interface TrustStoreLoader { - KeyStore getKeyStore() throws FileNotFoundException, KeyStoreException; - - String DEFAULT_KEYSTORE_TYPE = KeyStore.getDefaultType(); - String DEFAULT_KEYSTORE_PASSWORD = "changeit"; - - class DefaultTrustStoreLoader implements TrustStoreLoader { - @Override - public KeyStore getKeyStore() throws FileNotFoundException, KeyStoreException { - - String keystorePath = null; - String keystoreType = DEFAULT_KEYSTORE_TYPE; - try { - // Check if we are on Android. - Class version = Class.forName("android.os.Build$VERSION"); - // Build.VERSION_CODES.ICE_CREAM_SANDWICH is 14. - if (version.getDeclaredField("SDK_INT").getInt(version) >= 14) { - return loadIcsKeyStore(); - } else { - keystoreType = "BKS"; - keystorePath = System.getProperty("java.home") - + "/etc/security/cacerts.bks".replace('/', File.separatorChar); - } - } catch (ClassNotFoundException e) { - // NOP. android.os.Build is not present, so we are not on Android. Fall through. - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); // Should never happen. - } - if (keystorePath == null) { - keystorePath = System.getProperty("javax.net.ssl.trustStore"); - } - if (keystorePath == null) { - return loadFallbackStore(); - } - try { - return X509Utils.loadKeyStore(keystoreType, DEFAULT_KEYSTORE_PASSWORD, - new FileInputStream(keystorePath)); - } catch (FileNotFoundException e) { - // If we failed to find a system trust store, load our own fallback trust store. This can fail on - // Android but we should never reach it there. - return loadFallbackStore(); - } - } - - private KeyStore loadIcsKeyStore() throws KeyStoreException { - try { - // After ICS, Android provided this nice method for loading the keystore, - // so we don't have to specify the location explicitly. - KeyStore keystore = KeyStore.getInstance("AndroidCAStore"); - keystore.load(null, null); - return keystore; - } catch (IOException | GeneralSecurityException x) { - throw new KeyStoreException(x); - } - } - - private KeyStore loadFallbackStore() throws FileNotFoundException, KeyStoreException { - return X509Utils.loadKeyStore("JKS", DEFAULT_KEYSTORE_PASSWORD, getClass().getResourceAsStream("cacerts")); - } - } - - class FileTrustStoreLoader implements TrustStoreLoader { - private final File path; - - public FileTrustStoreLoader(@Nonnull File path) throws FileNotFoundException { - if (!path.exists()) - throw new FileNotFoundException(path.toString()); - this.path = path; - } - - @Override - public KeyStore getKeyStore() throws FileNotFoundException, KeyStoreException { - return X509Utils.loadKeyStore(DEFAULT_KEYSTORE_TYPE, DEFAULT_KEYSTORE_PASSWORD, new FileInputStream(path)); - } - } -} diff --git a/core/src/main/java/org/bitcoinj/crypto/X509Utils.java b/core/src/main/java/org/bitcoinj/crypto/X509Utils.java deleted file mode 100644 index ca622604662..00000000000 --- a/core/src/main/java/org/bitcoinj/crypto/X509Utils.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2014 The bitcoinj authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoinj.crypto; - -import org.bitcoinj.protocols.payments.PaymentSession; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1String; -import org.bouncycastle.asn1.x500.AttributeTypeAndValue; -import org.bouncycastle.asn1.x500.RDN; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x500.style.RFC4519Style; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.IOException; -import java.io.InputStream; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.cert.CertificateParsingException; -import java.security.cert.X509Certificate; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * X509Utils provides tools for working with X.509 certificates and keystores, as used in the BIP 70 payment protocol. - * For more details on this, see {@link PaymentSession}, the article "Working with - * the payment protocol" on the bitcoinj website, or the Bitcoin developer guide. - */ -public class X509Utils { - /** - * Returns either a string that "sums up" the certificate for humans, in a similar manner to what you might see - * in a web browser, or null if one cannot be extracted. This will typically be the common name (CN) field, but - * can also be the org (O) field, org+location+country if withLocation is set, or the email - * address for S/MIME certificates. - */ - @Nullable - public static String getDisplayNameFromCertificate(@Nonnull X509Certificate certificate, boolean withLocation) throws CertificateParsingException { - X500Name name = new X500Name(certificate.getSubjectX500Principal().getName()); - String commonName = null, org = null, location = null, country = null; - for (RDN rdn : name.getRDNs()) { - AttributeTypeAndValue pair = rdn.getFirst(); - String val = ((ASN1String) pair.getValue()).getString(); - ASN1ObjectIdentifier type = pair.getType(); - if (type.equals(RFC4519Style.cn)) - commonName = val; - else if (type.equals(RFC4519Style.o)) - org = val; - else if (type.equals(RFC4519Style.l)) - location = val; - else if (type.equals(RFC4519Style.c)) - country = val; - } - String altName = null; - try { - final Collection> subjectAlternativeNames = certificate.getSubjectAlternativeNames(); - if (subjectAlternativeNames != null) - for (final List subjectAlternativeName : subjectAlternativeNames) - if ((Integer) subjectAlternativeName.get(0) == 1) // rfc822name - altName = (String) subjectAlternativeName.get(1); - } catch (CertificateParsingException e) { - // swallow - } - - if (org != null) { - return withLocation ? Stream.of(org, location, country).filter(Objects::nonNull).collect(Collectors.joining()) : org; - } else if (commonName != null) { - return commonName; - } else { - return altName; - } - } - - /** Returns a key store loaded from the given stream. Just a convenience around the Java APIs. */ - public static KeyStore loadKeyStore(String keystoreType, @Nullable String keystorePassword, InputStream is) - throws KeyStoreException { - try { - KeyStore keystore = KeyStore.getInstance(keystoreType); - keystore.load(is, keystorePassword != null ? keystorePassword.toCharArray() : null); - return keystore; - } catch (IOException | GeneralSecurityException x) { - throw new KeyStoreException(x); - } finally { - try { - is.close(); - } catch (IOException x) { - // Ignored. - } - } - } -} diff --git a/core/src/main/java/org/bitcoinj/params/BitcoinNetworkParams.java b/core/src/main/java/org/bitcoinj/params/BitcoinNetworkParams.java index c3d2d27334c..bb22dc825dc 100644 --- a/core/src/main/java/org/bitcoinj/params/BitcoinNetworkParams.java +++ b/core/src/main/java/org/bitcoinj/params/BitcoinNetworkParams.java @@ -29,7 +29,6 @@ import org.bitcoinj.core.ProtocolVersion; import org.bitcoinj.core.StoredBlock; import org.bitcoinj.core.VerificationException; -import org.bitcoinj.protocols.payments.PaymentProtocol; import org.bitcoinj.store.BlockStore; import org.bitcoinj.store.BlockStoreException; import org.bitcoinj.base.utils.MonetaryFormat; @@ -117,15 +116,6 @@ public static BitcoinNetworkParams of(BitcoinNetwork network) { } } - /** - * @return the payment protocol network id string - * @deprecated Use {@link PaymentProtocol#protocolIdFromParams(NetworkParameters)} - */ - @Deprecated - public String getPaymentProtocolId() { - return PaymentProtocol.protocolIdFromParams(this); - } - /** * Checks if we are at a reward halving point. * @param previousHeight The height of the previous stored block diff --git a/core/src/main/java/org/bitcoinj/protocols/package-info.java b/core/src/main/java/org/bitcoinj/protocols/package-info.java deleted file mode 100644 index 28f791bc0e4..00000000000 --- a/core/src/main/java/org/bitcoinj/protocols/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright by the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * High level protocols that build on top of Bitcoin go here: we have the payment protocol for sending transactions - * from sender to receiver with metadata, and a micropayment channels implementation. - */ -package org.bitcoinj.protocols; \ No newline at end of file diff --git a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java b/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java deleted file mode 100644 index 5bb1ebd800b..00000000000 --- a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java +++ /dev/null @@ -1,491 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoinj.protocols.payments; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import org.bitcoinj.protobuf.payments.Protos; -import org.bitcoinj.base.Address; -import org.bitcoinj.base.Coin; -import org.bitcoinj.base.internal.TimeUtils; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.crypto.X509Utils; -import org.bitcoinj.params.BitcoinNetworkParams; -import org.bitcoinj.params.MainNetParams; -import org.bitcoinj.params.RegTestParams; -import org.bitcoinj.params.SigNetParams; -import org.bitcoinj.params.TestNet3Params; -import org.bitcoinj.params.UnitTestParams; -import org.bitcoinj.script.ScriptBuilder; - -import javax.annotation.Nullable; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.Signature; -import java.security.SignatureException; -import java.security.cert.CertPath; -import java.security.cert.CertPathValidator; -import java.security.cert.CertPathValidatorException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.CertificateParsingException; -import java.security.cert.PKIXCertPathValidatorResult; -import java.security.cert.PKIXParameters; -import java.security.cert.TrustAnchor; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - *

Utility methods and constants for working with - * BIP 70 aka the payment protocol. These are low level wrappers around the protocol buffers. If you're implementing - * a wallet app, look at {@link PaymentSession} for a higher level API that should simplify working with the protocol.

- * - *

BIP 70 defines a binary, protobuf based protocol that runs directly between sender and receiver of funds. Payment - * protocol data does not flow over the Bitcoin P2P network or enter the block chain. It's instead for data that is only - * of interest to the parties involved but isn't otherwise needed for consensus.

- */ -public class PaymentProtocol { - - // MIME types as defined in BIP71. - public static final String MIMETYPE_PAYMENTREQUEST = "application/bitcoin-paymentrequest"; - public static final String MIMETYPE_PAYMENT = "application/bitcoin-payment"; - public static final String MIMETYPE_PAYMENTACK = "application/bitcoin-paymentack"; - - /** The string used by the payment protocol to represent the main net. */ - public static final String PAYMENT_PROTOCOL_ID_MAINNET = "main"; - /** The string used by the payment protocol to represent the test net. */ - public static final String PAYMENT_PROTOCOL_ID_TESTNET = "test"; - /** The string used by the payment protocol to represent signet (note that this is non-standard). */ - public static final String PAYMENT_PROTOCOL_ID_SIGNET = "signet"; - /** The string used by the payment protocol to represent unit testing (note that this is non-standard). */ - public static final String PAYMENT_PROTOCOL_ID_UNIT_TESTS = "unittest"; - public static final String PAYMENT_PROTOCOL_ID_REGTEST = "regtest"; - - /** - * Create a payment request with one standard pay to address output. 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 amount amount of coins to request, or null - * @param toAddress address 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, - @Nullable Coin amount, Address toAddress, @Nullable String memo, @Nullable String paymentUrl, - @Nullable byte[] merchantData) { - return createPaymentRequest(params, Collections.singletonList(createPayToAddressOutput(amount, toAddress)), memo, - paymentUrl, merchantData); - } - - /** - * 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 outputs, @Nullable String memo, @Nullable String paymentUrl, - @Nullable byte[] merchantData) { - final Protos.PaymentDetails.Builder paymentDetails = Protos.PaymentDetails.newBuilder(); - paymentDetails.setNetwork(protocolIdFromParams(params)); - 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(TimeUtils.currentTime().getEpochSecond()); - - final Protos.PaymentRequest.Builder paymentRequest = Protos.PaymentRequest.newBuilder(); - paymentRequest.setSerializedPaymentDetails(paymentDetails.build().toByteString()); - return paymentRequest; - } - - /** - * Parse a payment request. - * - * @param paymentRequest payment request to parse - * @return instance of {@link PaymentSession}, used as a value object - * @throws PaymentProtocolException - */ - public static PaymentSession parsePaymentRequest(Protos.PaymentRequest paymentRequest) - throws PaymentProtocolException { - return new PaymentSession(paymentRequest, false, null); - } - - /** - * Sign the provided payment request. - * - * @param paymentRequest Payment request to sign, in its builder form. - * @param certificateChain Certificate chain to send with the payment request, ordered from client certificate to root - * certificate. The root certificate itself may be omitted. - * @param privateKey The key to sign with. Must match the public key from the first certificate of the certificate chain. - */ - public static void signPaymentRequest(Protos.PaymentRequest.Builder paymentRequest, - X509Certificate[] certificateChain, PrivateKey privateKey) { - try { - final Protos.X509Certificates.Builder certificates = Protos.X509Certificates.newBuilder(); - for (final Certificate certificate : certificateChain) - certificates.addCertificate(ByteString.copyFrom(certificate.getEncoded())); - - paymentRequest.setPkiType("x509+sha256"); - paymentRequest.setPkiData(certificates.build().toByteString()); - paymentRequest.setSignature(ByteString.EMPTY); - final Protos.PaymentRequest paymentRequestToSign = paymentRequest.build(); - - final String algorithm; - if ("RSA".equalsIgnoreCase(privateKey.getAlgorithm())) - algorithm = "SHA256withRSA"; - else - throw new IllegalStateException(privateKey.getAlgorithm()); - - final Signature signature = Signature.getInstance(algorithm); - signature.initSign(privateKey); - signature.update(paymentRequestToSign.toByteArray()); - - paymentRequest.setSignature(ByteString.copyFrom(signature.sign())); - } catch (final GeneralSecurityException x) { - // Should never happen so don't make users have to think about it. - throw new RuntimeException(x); - } - } - - /** - * Uses the provided PKI method to find the corresponding public key and verify the provided signature. - * - * @param paymentRequest Payment request to verify. - * @param trustStore KeyStore of trusted root certificate authorities. - * @return verification data, or null if no PKI method was specified in the {@link Protos.PaymentRequest}. - * @throws PaymentProtocolException if payment request could not be verified. - */ - @Nullable - public static PkiVerificationData verifyPaymentRequestPki(Protos.PaymentRequest paymentRequest, KeyStore trustStore) - throws PaymentProtocolException { - List certs = null; - try { - final String pkiType = paymentRequest.getPkiType(); - if ("none".equals(pkiType)) - // Nothing to verify. Everything is fine. Move along. - return null; - - String algorithm; - if ("x509+sha256".equals(pkiType)) - algorithm = "SHA256withRSA"; - else if ("x509+sha1".equals(pkiType)) - algorithm = "SHA1withRSA"; - else - throw new PaymentProtocolException.InvalidPkiType("Unsupported PKI type: " + pkiType); - - Protos.X509Certificates protoCerts = Protos.X509Certificates.parseFrom(paymentRequest.getPkiData()); - if (protoCerts.getCertificateCount() == 0) - throw new PaymentProtocolException.InvalidPkiData("No certificates provided in message: server config error"); - - // Parse the certs and turn into a certificate chain object. Cert factories can parse both DER and base64. - // The ordering of certificates is defined by the payment protocol spec to be the same as what the Java - // crypto API requires - convenient! - CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); - certs = new ArrayList<>(); - for (ByteString bytes : protoCerts.getCertificateList()) - certs.add((X509Certificate) certificateFactory.generateCertificate(bytes.newInput())); - CertPath path = certificateFactory.generateCertPath(certs); - - // Retrieves the most-trusted CAs from keystore. - PKIXParameters params = new PKIXParameters(trustStore); - // Revocation not supported in the current version. - params.setRevocationEnabled(false); - - // Now verify the certificate chain is correct and trusted. This let's us get an identity linked pubkey. - CertPathValidator validator = CertPathValidator.getInstance("PKIX"); - PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator.validate(path, params); - PublicKey publicKey = result.getPublicKey(); - // OK, we got an identity, now check it was used to sign this message. - Signature signature = Signature.getInstance(algorithm); - // Note that we don't use signature.initVerify(certs.get(0)) here despite it being the most obvious - // way to set it up, because we don't care about the constraints specified on the certificates: any - // cert that links a key to a domain name or other identity will do for us. - signature.initVerify(publicKey); - Protos.PaymentRequest.Builder reqToCheck = paymentRequest.toBuilder(); - reqToCheck.setSignature(ByteString.EMPTY); - signature.update(reqToCheck.build().toByteArray()); - if (!signature.verify(paymentRequest.getSignature().toByteArray())) - throw new PaymentProtocolException.PkiVerificationException("Invalid signature, this payment request is not valid."); - - // Signature verifies, get the names from the identity we just verified for presentation to the user. - final X509Certificate cert = certs.get(0); - String displayName = X509Utils.getDisplayNameFromCertificate(cert, true); - if (displayName == null) - throw new PaymentProtocolException.PkiVerificationException("Could not extract name from certificate"); - // Everything is peachy. Return some useful data to the caller. - return new PkiVerificationData(displayName, publicKey, result.getTrustAnchor()); - } catch (InvalidProtocolBufferException e) { - // Data structures are malformed. - throw new PaymentProtocolException.InvalidPkiData(e); - } catch (CertificateException | SignatureException | InvalidKeyException e) { - // CertificateException: The X.509 certificate data didn't parse correctly. - // SignatureException: Something went wrong during hashing (yes, despite the name, this does not mean the sig was invalid). - // InvalidKeyException: Shouldn't happen if the certs verified correctly. - throw new PaymentProtocolException.PkiVerificationException(e); - } catch (NoSuchAlgorithmException | KeyStoreException | InvalidAlgorithmParameterException e) { - // NoSuchAlgorithmException: Should never happen so don't make users have to think about it. PKIX is always present. - throw new RuntimeException(e); - } catch (CertPathValidatorException e) { - // The certificate chain isn't known or trusted, probably, the server is using an SSL root we don't - // know about and the user needs to upgrade to a new version of the software (or import a root cert). - throw new PaymentProtocolException.PkiVerificationException(e, certs); - } - } - - /** - * Information about the X.509 signature's issuer and subject. - */ - public static class PkiVerificationData { - /** Display name of the payment requestor, could be a domain name, email address, legal name, etc */ - public final String displayName; - /** SSL public key that was used to sign. */ - public final PublicKey merchantSigningKey; - /** Object representing the CA that verified the merchant's ID */ - public final TrustAnchor rootAuthority; - /** String representing the display name of the CA that verified the merchant's ID */ - public final String rootAuthorityName; - - private PkiVerificationData(@Nullable String displayName, PublicKey merchantSigningKey, - TrustAnchor rootAuthority) throws PaymentProtocolException.PkiVerificationException { - try { - this.displayName = displayName; - this.merchantSigningKey = merchantSigningKey; - this.rootAuthority = rootAuthority; - this.rootAuthorityName = X509Utils.getDisplayNameFromCertificate(rootAuthority.getTrustedCert(), true); - } catch (CertificateParsingException x) { - throw new PaymentProtocolException.PkiVerificationException(x); - } - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("displayName", displayName) - .add("rootAuthorityName", rootAuthorityName) - .add("merchantSigningKey", merchantSigningKey) - .add("rootAuthority", rootAuthority) - .toString(); - } - } - - /** - * Create a payment message with one standard pay to address output. - * - * @param transactions one or more transactions that satisfy the requested outputs. - * @param refundAmount amount of coins to request as a refund, or null if no refund. - * @param refundAddress address to refund coins to - * @param memo arbitrary, user readable memo, or null if none - * @param merchantData arbitrary merchant data, or null if none - * @return created payment message - */ - public static Protos.Payment createPaymentMessage(List transactions, - @Nullable Coin refundAmount, @Nullable Address refundAddress, @Nullable String memo, - @Nullable byte[] merchantData) { - if (refundAddress != null) { - if (refundAmount == null) - throw new IllegalArgumentException("Specify refund amount if refund address is specified."); - return createPaymentMessage(transactions, - Collections.singletonList(createPayToAddressOutput(refundAmount, refundAddress)), memo, merchantData); - } else { - return createPaymentMessage(transactions, null, memo, merchantData); - } - } - - /** - * Create a payment message. This wraps up transaction data along with anything else useful for making a payment. - * - * @param transactions transactions to include with the payment message - * @param refundOutputs list of outputs to refund coins to, or null - * @param memo arbitrary, user readable memo, or null if none - * @param merchantData arbitrary merchant data, or null if none - * @return created payment message - */ - public static Protos.Payment createPaymentMessage(List transactions, - @Nullable List refundOutputs, @Nullable String memo, @Nullable byte[] merchantData) { - Protos.Payment.Builder builder = Protos.Payment.newBuilder(); - for (Transaction transaction : transactions) { - builder.addTransactions(ByteString.copyFrom(transaction.serialize())); - } - if (refundOutputs != null) { - for (Protos.Output output : refundOutputs) - builder.addRefundTo(output); - } - if (memo != null) - builder.setMemo(memo); - if (merchantData != null) - builder.setMerchantData(ByteString.copyFrom(merchantData)); - return builder.build(); - } - - /** - * 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 parseTransactionsFromPaymentMessage(NetworkParameters params, - Protos.Payment paymentMessage) { - final List transactions = new ArrayList<>(paymentMessage.getTransactionsCount()); - for (final ByteString transaction : paymentMessage.getTransactionsList()) - transactions.add(params.getDefaultSerializer().makeTransaction(ByteBuffer.wrap(transaction.toByteArray()))); - return transactions; - } - - /** - * Message returned by the merchant in response to a Payment message. - */ - public static class Ack { - @Nullable private final String memo; - - Ack(@Nullable String memo) { - this.memo = memo; - } - - /** - * Returns the memo included by the merchant in the payment ack. This message is typically displayed to the user - * as a notification (e.g. "Your payment was received and is being processed"). If none was provided, returns - * null. - */ - @Nullable public String getMemo() { - return memo; - } - } - - /** - * Create a payment ack. - * - * @param paymentMessage payment message to send with the ack - * @param memo arbitrary, user readable memo, or null if none - * @return created payment ack - */ - public static Protos.PaymentACK createPaymentAck(Protos.Payment paymentMessage, @Nullable String memo) { - final Protos.PaymentACK.Builder builder = Protos.PaymentACK.newBuilder(); - builder.setPayment(paymentMessage); - if (memo != null) - builder.setMemo(memo); - return builder.build(); - } - - /** - * Parse payment ack into an object. - */ - public static Ack parsePaymentAck(Protos.PaymentACK paymentAck) { - final String memo = paymentAck.hasMemo() ? paymentAck.getMemo() : null; - return new Ack(memo); - } - - /** - * 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 = NetworkParameters.of(address.network()); - if (params.network().exceedsMaxMoney(amount)) - throw new IllegalArgumentException("Amount too big: " + amount); - output.setAmount(amount.value); - } else { - output.setAmount(0); - } - output.setScript(ByteString.copyFrom(ScriptBuilder.createOutputScript(address).program())); - return output.build(); - } - - /** - * Return network parameters for a paymentProtocol ID string - * @param pmtProtocolId paymentProtocol ID string - * @return network parameters for the given string paymentProtocolID or NULL if not recognized - */ - @Nullable - public static BitcoinNetworkParams paramsFromPmtProtocolID(String pmtProtocolId) { - if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_MAINNET)) { - return MainNetParams.get(); - } else if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_TESTNET)) { - return TestNet3Params.get(); - } else if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_SIGNET)) { - return SigNetParams.get(); - } else if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_REGTEST)) { - return RegTestParams.get(); - } else if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_UNIT_TESTS)) { - return UnitTestParams.get(); - } else { - return null; - } - } - - public static String protocolIdFromParams(NetworkParameters params) { - if (params instanceof MainNetParams) { - return PAYMENT_PROTOCOL_ID_MAINNET; - } else if (params instanceof TestNet3Params) { - return PAYMENT_PROTOCOL_ID_TESTNET; - } else if (params instanceof SigNetParams) { - return PAYMENT_PROTOCOL_ID_SIGNET; - } else if (params instanceof RegTestParams) { - return PAYMENT_PROTOCOL_ID_REGTEST; - } else if (params instanceof UnitTestParams) { - return PAYMENT_PROTOCOL_ID_UNIT_TESTS; - } else { - throw new IllegalArgumentException("Unknown network"); - } - } - - /** - * Value object to hold amount/script pairs. - */ - public static class Output { - @Nullable public final Coin amount; - public final byte[] scriptData; - - public Output(@Nullable Coin amount, byte[] scriptData) { - this.amount = amount; - this.scriptData = scriptData; - } - } -} diff --git a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocolException.java b/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocolException.java deleted file mode 100644 index 63f34897a0c..00000000000 --- a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocolException.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoinj.protocols.payments; - -import java.security.cert.X509Certificate; -import java.util.List; - -public class PaymentProtocolException extends Exception { - public PaymentProtocolException(String msg) { - super(msg); - } - - public PaymentProtocolException(Exception e) { - super(e); - } - - public static class Expired extends PaymentProtocolException { - public Expired(String msg) { - super(msg); - } - } - - public static class InvalidPaymentRequestURL extends PaymentProtocolException { - public InvalidPaymentRequestURL(String msg) { - super(msg); - } - - public InvalidPaymentRequestURL(Exception e) { - super(e); - } - } - - public static class InvalidPaymentURL extends PaymentProtocolException { - public InvalidPaymentURL(Exception e) { - super(e); - } - - public InvalidPaymentURL(String msg) { - super(msg); - } - } - - public static class InvalidOutputs extends PaymentProtocolException { - public InvalidOutputs(String msg) { - super(msg); - } - } - - public static class InvalidVersion extends PaymentProtocolException { - public InvalidVersion(String msg) { - super(msg); - } - } - - public static class InvalidNetwork extends PaymentProtocolException { - public InvalidNetwork(String msg) { - super(msg); - } - } - - public static class InvalidPkiType extends PaymentProtocolException { - public InvalidPkiType(String msg) { - super(msg); - } - } - - public static class InvalidPkiData extends PaymentProtocolException { - public InvalidPkiData(String msg) { - super(msg); - } - - public InvalidPkiData(Exception e) { - super(e); - } - } - - public static class PkiVerificationException extends PaymentProtocolException { - public List certificates; - - public PkiVerificationException(String msg) { - super(msg); - } - - public PkiVerificationException(Exception e) { - super(e); - } - - public PkiVerificationException(Exception e, List certificates) { - super(e); - this.certificates = certificates; - } - } -} diff --git a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentSession.java b/core/src/main/java/org/bitcoinj/protocols/payments/PaymentSession.java deleted file mode 100644 index d47089433c0..00000000000 --- a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentSession.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright by the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoinj.protocols.payments; - -import com.google.common.annotations.VisibleForTesting; -import com.google.protobuf.InvalidProtocolBufferException; -import org.bitcoinj.protobuf.payments.Protos; -import org.bitcoinj.base.Address; -import org.bitcoinj.base.Coin; -import org.bitcoinj.base.internal.TimeUtils; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.core.TransactionOutput; -import org.bitcoinj.crypto.TrustStoreLoader; -import org.bitcoinj.params.MainNetParams; -import org.bitcoinj.protocols.payments.PaymentProtocol.PkiVerificationData; -import org.bitcoinj.uri.BitcoinURI; -import org.bitcoinj.base.internal.FutureUtils; -import org.bitcoinj.utils.Threading; -import org.bitcoinj.wallet.SendRequest; - -import javax.annotation.Nullable; -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.security.KeyStoreException; -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; - -/** - *

Provides a standard implementation of the Payment Protocol (BIP 0070)

- * - *

A PaymentSession can be initialized from one of the following:

- * - *
    - *
  • A {@link BitcoinURI} object that conforms to BIP 0072
  • - *
  • A url where the {@link Protos.PaymentRequest} can be fetched
  • - *
  • Directly with a {@link Protos.PaymentRequest} object
  • - *
- * - *

If initialized with a BitcoinURI or a url, a network request is made for the payment request object and a - * {@link CompletableFuture} is returned that will be notified with the PaymentSession object after it is downloaded.

- * - *

Once the PaymentSession is initialized, typically a wallet application will prompt the user to confirm that the - * amount and recipient are correct, perform any additional steps, and then construct a list of transactions to pass to - * the sendPayment method.

- * - *

Call sendPayment with a list of transactions that will be broadcast. A {@link Protos.Payment} message will be sent - * to the merchant if a payment url is provided in the PaymentRequest. NOTE: sendPayment does NOT broadcast the - * transactions to the bitcoin network. Instead it returns a CompletableFuture that will be notified when a - * {@link Protos.PaymentACK} is received from the merchant. Typically a wallet will show the message to the user - * as a confirmation message that the payment is now "processing" or that an error occurred, and then broadcast the - * tx itself later if needed.

- * - * @see BIP 0070 - */ -public class PaymentSession { - private final static ExecutorService executor = Threading.THREAD_POOL; - private NetworkParameters params; - private Protos.PaymentRequest paymentRequest; - private Protos.PaymentDetails paymentDetails; - private Coin totalValue = Coin.ZERO; - - /** - * Stores the calculated PKI verification data, or null if none is available. - * Only valid after the session is created with the verifyPki parameter set to true. - */ - @Nullable public final PkiVerificationData pkiVerificationData; - - /** - *

Returns a future that will be notified with a PaymentSession object after it is fetched using the provided uri. - * uri is a BIP-72-style BitcoinURI object that specifies where the {@link Protos.PaymentRequest} object may - * be fetched in the r= parameter.

- * - *

If the payment request object specifies a PKI method, then the system trust store will be used to verify - * the signature provided by the payment request. An exception is thrown by the future if the signature cannot - * be verified.

- */ - public static CompletableFuture createFromBitcoinUri(final BitcoinURI uri) throws PaymentProtocolException { - return createFromBitcoinUri(uri, true, null); - } - - /** - * Returns a future that will be notified with a PaymentSession object after it is fetched using the provided uri. - * uri is a BIP-72-style BitcoinURI object that specifies where the {@link Protos.PaymentRequest} object may - * be fetched in the r= parameter. - * If verifyPki is specified and the payment request object specifies a PKI method, then the system trust store will - * be used to verify the signature provided by the payment request. An exception is thrown by the future if the - * signature cannot be verified. - */ - public static CompletableFuture createFromBitcoinUri(final BitcoinURI uri, final boolean verifyPki) - throws PaymentProtocolException { - return createFromBitcoinUri(uri, verifyPki, null); - } - - /** - * Returns a future that will be notified with a PaymentSession object after it is fetched using the provided uri. - * uri is a BIP-72-style BitcoinURI object that specifies where the {@link Protos.PaymentRequest} object may - * be fetched in the r= parameter. - * If verifyPki is specified and the payment request object specifies a PKI method, then the system trust store will - * be used to verify the signature provided by the payment request. An exception is thrown by the future if the - * signature cannot be verified. - * If trustStoreLoader is null, the system default trust store is used. - */ - public static CompletableFuture createFromBitcoinUri(final BitcoinURI uri, final boolean verifyPki, @Nullable final TrustStoreLoader trustStoreLoader) - throws PaymentProtocolException { - String url = uri.getPaymentRequestUrl(); - if (url == null) - throw new PaymentProtocolException.InvalidPaymentRequestURL("No payment request URL (r= parameter) in BitcoinURI " + uri); - try { - return fetchPaymentRequest(new URI(url), verifyPki, trustStoreLoader); - } catch (URISyntaxException e) { - throw new PaymentProtocolException.InvalidPaymentRequestURL(e); - } - } - - /** - * Returns a future that will be notified with a PaymentSession object after it is fetched using the provided url. - * url is an address where the {@link Protos.PaymentRequest} object may be fetched. - * If verifyPki is specified and the payment request object specifies a PKI method, then the system trust store will - * be used to verify the signature provided by the payment request. An exception is thrown by the future if the - * signature cannot be verified. - */ - public static CompletableFuture createFromUrl(final String url) throws PaymentProtocolException { - return createFromUrl(url, true, null); - } - - /** - * Returns a future that will be notified with a PaymentSession object after it is fetched using the provided url. - * url is an address where the {@link Protos.PaymentRequest} object may be fetched. - * If the payment request object specifies a PKI method, then the system trust store will - * be used to verify the signature provided by the payment request. An exception is thrown by the future if the - * signature cannot be verified. - */ - public static CompletableFuture createFromUrl(final String url, final boolean verifyPki) - throws PaymentProtocolException { - return createFromUrl(url, verifyPki, null); - } - - /** - * Returns a future that will be notified with a PaymentSession object after it is fetched using the provided url. - * url is an address where the {@link Protos.PaymentRequest} object may be fetched. - * If the payment request object specifies a PKI method, then the system trust store will - * be used to verify the signature provided by the payment request. An exception is thrown by the future if the - * signature cannot be verified. - * If trustStoreLoader is null, the system default trust store is used. - */ - public static CompletableFuture createFromUrl(final String url, final boolean verifyPki, @Nullable final TrustStoreLoader trustStoreLoader) - throws PaymentProtocolException { - if (url == null) - throw new PaymentProtocolException.InvalidPaymentRequestURL("null paymentRequestUrl"); - try { - return fetchPaymentRequest(new URI(url), verifyPki, trustStoreLoader); - } catch(URISyntaxException e) { - throw new PaymentProtocolException.InvalidPaymentRequestURL(e); - } - } - - private static CompletableFuture fetchPaymentRequest(final URI uri, final boolean verifyPki, @Nullable final TrustStoreLoader trustStoreLoader) { - return CompletableFuture.supplyAsync((FutureUtils.ThrowingSupplier) () -> { - HttpURLConnection connection = (HttpURLConnection)uri.toURL().openConnection(); - connection.setRequestProperty("Accept", PaymentProtocol.MIMETYPE_PAYMENTREQUEST); - connection.setUseCaches(false); - Protos.PaymentRequest paymentRequest = Protos.PaymentRequest.parseFrom(connection.getInputStream()); - return new PaymentSession(paymentRequest, verifyPki, trustStoreLoader); - }, executor); - } - - /** - * Creates a PaymentSession from the provided {@link Protos.PaymentRequest}. - * Verifies PKI by default. - */ - public PaymentSession(Protos.PaymentRequest request) throws PaymentProtocolException { - this(request, true, null); - } - - /** - * Creates a PaymentSession from the provided {@link Protos.PaymentRequest}. - * If verifyPki is true, also validates the signature and throws an exception if it fails. - */ - public PaymentSession(Protos.PaymentRequest request, boolean verifyPki) throws PaymentProtocolException { - this(request, verifyPki, null); - } - - /** - * Creates a PaymentSession from the provided {@link Protos.PaymentRequest}. - * If verifyPki is true, also validates the signature and throws an exception if it fails. - * If trustStoreLoader is null, the system default trust store is used. - */ - public PaymentSession(Protos.PaymentRequest request, boolean verifyPki, @Nullable final TrustStoreLoader trustStoreLoader) throws PaymentProtocolException { - TrustStoreLoader nonNullTrustStoreLoader = trustStoreLoader != null ? trustStoreLoader : new TrustStoreLoader.DefaultTrustStoreLoader(); - parsePaymentRequest(request); - if (verifyPki) { - try { - pkiVerificationData = PaymentProtocol.verifyPaymentRequestPki(request, nonNullTrustStoreLoader.getKeyStore()); - } catch (IOException | KeyStoreException x) { - throw new PaymentProtocolException(x); - } - } else { - pkiVerificationData = null; - } - } - - /** - * Returns the outputs of the payment request. - */ - public List getOutputs() { - List outputs = new ArrayList<>(paymentDetails.getOutputsCount()); - for (Protos.Output output : paymentDetails.getOutputsList()) { - Coin amount = output.hasAmount() ? Coin.valueOf(output.getAmount()) : null; - outputs.add(new PaymentProtocol.Output(amount, output.getScript().toByteArray())); - } - return outputs; - } - - /** - * Returns the memo included by the merchant in the payment request, or null if not found. - */ - @Nullable public String getMemo() { - if (paymentDetails.hasMemo()) - return paymentDetails.getMemo(); - else - return null; - } - - /** - * Returns the total amount of bitcoins requested. - */ - public Coin getValue() { - return totalValue; - } - - /** - * Returns the time that the payment request was generated. - */ - public Instant time() { - return Instant.ofEpochSecond(paymentDetails.getTime()); - } - - /** - * Returns the expires time of the payment request, or empty if none. - */ - public Optional expires() { - if (paymentDetails.hasExpires()) - return Optional.of(Instant.ofEpochSecond(paymentDetails.getExpires())); - else - return Optional.empty(); - } - - /** - * This should always be called before attempting to call sendPayment. - */ - public boolean isExpired() { - return expires() - .map(time -> TimeUtils.currentTime().isAfter(time)) - .orElse(false); - } - - /** - * Returns the payment url where the Payment message should be sent. - * Returns null if no payment url was provided in the PaymentRequest. - */ - @Nullable - public String getPaymentUrl() { - if (paymentDetails.hasPaymentUrl()) - return paymentDetails.getPaymentUrl(); - return null; - } - - /** - * Returns the merchant data included by the merchant in the payment request, or null if none. - */ - @Nullable public byte[] getMerchantData() { - if (paymentDetails.hasMerchantData()) - return paymentDetails.getMerchantData().toByteArray(); - else - return null; - } - - /** - * Returns a {@link SendRequest} suitable for broadcasting to the network. - */ - public SendRequest getSendRequest() { - Transaction tx = new Transaction(); - for (Protos.Output output : paymentDetails.getOutputsList()) - tx.addOutput(new TransactionOutput(tx, Coin.valueOf(output.getAmount()), output.getScript().toByteArray())); - return SendRequest.forTx(tx).fromPaymentDetails(paymentDetails); - } - - /** - * Generates a Payment message and sends the payment to the merchant who sent the PaymentRequest. - * Provide transactions built by the wallet. - * NOTE: This does not broadcast the transactions to the bitcoin network, it merely sends a Payment message to the - * merchant confirming the payment. - * Returns an object wrapping PaymentACK once received. - * If the PaymentRequest did not specify a payment_url, completes exceptionally. - * @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. - * @return a future for the PaymentACK - */ - public CompletableFuture sendPayment(List txns, @Nullable Address refundAddr, @Nullable String memo) { - Protos.Payment payment = null; - try { - payment = getPayment(txns, refundAddr, memo); - } catch (IOException e) { - return FutureUtils.failedFuture(e); - } - if (payment == null) - return FutureUtils.failedFuture(new PaymentProtocolException.InvalidPaymentRequestURL("Missing Payment URL")); - if (isExpired()) - return FutureUtils.failedFuture(new PaymentProtocolException.Expired("PaymentRequest is expired")); - URL url; - try { - url = new URL(paymentDetails.getPaymentUrl()); - } catch (MalformedURLException e) { - return FutureUtils.failedFuture(new PaymentProtocolException.InvalidPaymentURL(e)); - } - return sendPayment(url, payment); - } - - /** - * Generates a Payment message based on the information in the PaymentRequest. - * Provide transactions built by the wallet. - * @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. - * @return Payment message or null (if the PaymentRequest did not specify a payment_url) - */ - @Nullable - public Protos.Payment getPayment(List txns, @Nullable Address refundAddr, @Nullable String memo) - throws IOException { - if (paymentDetails.hasPaymentUrl()) { - return PaymentProtocol.createPaymentMessage(txns, totalValue, refundAddr, memo, getMerchantData()); - } else { - return null; - } - } - - @VisibleForTesting - protected CompletableFuture sendPayment(final URL url, final Protos.Payment payment) { - return CompletableFuture.supplyAsync((FutureUtils.ThrowingSupplier) () -> { - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", PaymentProtocol.MIMETYPE_PAYMENT); - connection.setRequestProperty("Accept", PaymentProtocol.MIMETYPE_PAYMENTACK); - connection.setRequestProperty("Content-Length", Integer.toString(payment.getSerializedSize())); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(true); - - // Send request. - DataOutputStream outStream = new DataOutputStream(connection.getOutputStream()); - payment.writeTo(outStream); - outStream.flush(); - outStream.close(); - - // Get response. - Protos.PaymentACK paymentAck = Protos.PaymentACK.parseFrom(connection.getInputStream()); - return PaymentProtocol.parsePaymentAck(paymentAck); - }, executor); - } - - 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 = PaymentProtocol.paramsFromPmtProtocolID(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.network().exceedsMaxMoney(totalValue)) - throw new PaymentProtocolException.InvalidOutputs("The outputs are way too big."); - } catch (InvalidProtocolBufferException e) { - throw new PaymentProtocolException(e); - } - } - - /** Returns the value of pkiVerificationData or null if it wasn't verified at construction time. */ - @Nullable public PkiVerificationData verifyPki() { - return pkiVerificationData; - } - - /** Gets the params as read from the PaymentRequest.network field: main is the default if missing. */ - public NetworkParameters getNetworkParameters() { - return params; - } - - /** Returns the protobuf that this object was instantiated with. */ - public Protos.PaymentRequest getPaymentRequest() { - return paymentRequest; - } - - /** Returns the protobuf that describes the payment to be made. */ - public Protos.PaymentDetails getPaymentDetails() { - return paymentDetails; - } -} diff --git a/core/src/main/java/org/bitcoinj/protocols/payments/package-info.java b/core/src/main/java/org/bitcoinj/protocols/payments/package-info.java deleted file mode 100644 index 72cf934ca5a..00000000000 --- a/core/src/main/java/org/bitcoinj/protocols/payments/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright by the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * The BIP70 payment protocol wraps Bitcoin transactions and adds various useful features like memos, refund addresses - * and authentication. - */ -package org.bitcoinj.protocols.payments; \ No newline at end of file diff --git a/core/src/main/java/org/bitcoinj/testing/MockAltNetworkParams.java b/core/src/main/java/org/bitcoinj/testing/MockAltNetworkParams.java index 20f68736f4d..b71e1289d25 100644 --- a/core/src/main/java/org/bitcoinj/testing/MockAltNetworkParams.java +++ b/core/src/main/java/org/bitcoinj/testing/MockAltNetworkParams.java @@ -39,11 +39,6 @@ public MockAltNetworkParams() { p2shHeader = 5; } - @Override - public String getPaymentProtocolId() { - return null; - } - @Override public void checkDifficultyTransitions(StoredBlock storedPrev, Block next, BlockStore blockStore) throws VerificationException, BlockStoreException { diff --git a/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java b/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java index 2e9dc36aedb..250a8575e4b 100644 --- a/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java +++ b/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java @@ -82,7 +82,6 @@ public class BitcoinURI { public static final String FIELD_LABEL = "label"; public static final String FIELD_AMOUNT = "amount"; public static final String FIELD_ADDRESS = "address"; - public static final String FIELD_PAYMENT_REQUEST_URL = "r"; /** * URI Scheme for Bitcoin network. @@ -188,8 +187,8 @@ private BitcoinURI(String input, @Nullable Network network) throws BitcoinURIPar } } - if (addressToken.isEmpty() && getPaymentRequestUrl() == null) { - throw new BitcoinURIParseException("No address and no r= parameter found"); + if (addressToken.isEmpty()) { + throw new BitcoinURIParseException("No address found"); } } @@ -287,32 +286,6 @@ public String getMessage() { return (String) parameterMap.get(FIELD_MESSAGE); } - /** - * @return The URL where a payment request (as specified in BIP 70) may - * be fetched. - */ - public final String getPaymentRequestUrl() { - return (String) parameterMap.get(FIELD_PAYMENT_REQUEST_URL); - } - - /** - * Returns the URLs where a payment request (as specified in BIP 70) may be fetched. The first URL is the main URL, - * all subsequent URLs are fallbacks. - */ - public List getPaymentRequestUrls() { - ArrayList urls = new ArrayList<>(); - while (true) { - int i = urls.size(); - String paramName = FIELD_PAYMENT_REQUEST_URL + (i > 0 ? Integer.toString(i) : ""); - String url = (String) parameterMap.get(paramName); - if (url == null) - break; - urls.add(url); - } - Collections.reverse(urls); - return urls; - } - /** * @param name The name of the parameter * @return The parameter value, or null if not present diff --git a/core/src/main/java/org/bitcoinj/wallet/SendRequest.java b/core/src/main/java/org/bitcoinj/wallet/SendRequest.java index 9a67d34fda3..bc92791dd1f 100644 --- a/core/src/main/java/org/bitcoinj/wallet/SendRequest.java +++ b/core/src/main/java/org/bitcoinj/wallet/SendRequest.java @@ -18,7 +18,6 @@ package org.bitcoinj.wallet; import com.google.common.base.MoreObjects; -import org.bitcoinj.protobuf.payments.Protos.PaymentDetails; import org.bitcoinj.base.Address; import org.bitcoinj.base.Coin; import org.bitcoinj.crypto.AesKey; @@ -228,13 +227,6 @@ public static SendRequest childPaysForParent(Wallet wallet, Transaction parentTr return req; } - /** Copy data from payment request. */ - public SendRequest fromPaymentDetails(PaymentDetails paymentDetails) { - if (paymentDetails.hasMemo()) - this.memo = paymentDetails.getMemo(); - return this; - } - @Override public String toString() { // print only the user-settable fields diff --git a/core/src/main/proto/paymentrequest.proto b/core/src/main/proto/paymentrequest.proto deleted file mode 100644 index d961ea542e1..00000000000 --- a/core/src/main/proto/paymentrequest.proto +++ /dev/null @@ -1,73 +0,0 @@ -/** Copyright 2013 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Authors: Mike Hearn, Gavin Andresen - */ - -/* Notes: - * - Endianness: All byte arrays that represent numbers (such as hashes and private keys) are Big Endian - * - To regenerate after editing, run gradle generateProto - */ - -// -// Simple Bitcoin Payment Protocol messages -// -// Use fields 100+ for extensions; -// to avoid conflicts, register extensions at: -// https://en.bitcoin.it/wiki/Payment_Request -// - -syntax = "proto2"; - -package payments; - -option java_package = "org.bitcoinj.protobuf.payments"; -option java_outer_classname = "Protos"; - -// Generalized form of "send payment to this/these bitcoin addresses" -message Output { - optional uint64 amount = 1 [default = 0]; // amount is integer-number-of-satoshis - required bytes script = 2; // usually one of the standard Script forms -} -message PaymentDetails { - optional string network = 1 [default = "main"]; // "main" or "test" - repeated Output outputs = 2; // Where payment should be sent - required uint64 time = 3; // Timestamp; when payment request created - optional uint64 expires = 4; // Timestamp; when this request should be considered invalid - optional string memo = 5; // Human-readable description of request for the customer - optional string payment_url = 6; // URL to send Payment and get PaymentACK - optional bytes merchant_data = 7; // Arbitrary data to include in the Payment message -} -message PaymentRequest { - optional uint32 payment_details_version = 1 [default = 1]; - optional string pki_type = 2 [default = "none"]; // none / x509+sha256 / x509+sha1 - optional bytes pki_data = 3; // depends on pki_type - required bytes serialized_payment_details = 4; // PaymentDetails - optional bytes signature = 5; // pki-dependent signature -} -message X509Certificates { - repeated bytes certificate = 1; // DER-encoded X.509 certificate chain -} -message Payment { - optional bytes merchant_data = 1; // From PaymentDetails.merchant_data - repeated bytes transactions = 2; // Signed transactions that satisfy PaymentDetails.outputs - repeated Output refund_to = 3; // Where to send refunds, if a refund is necessary - optional string memo = 4; // Human-readable message for the merchant -} -message PaymentACK { - required Payment payment = 1; // Payment message that triggered this ACK - optional string memo = 2; // human-readable message for customer -} \ No newline at end of file diff --git a/core/src/test/java/org/bitcoinj/crypto/X509UtilsTest.java b/core/src/test/java/org/bitcoinj/crypto/X509UtilsTest.java deleted file mode 100644 index 782311d1767..00000000000 --- a/core/src/test/java/org/bitcoinj/crypto/X509UtilsTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2014 Andreas Schildbach - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoinj.crypto; - -import org.junit.Test; - -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; - -import static org.junit.Assert.assertEquals; - -public class X509UtilsTest { - - @Test - public void testDisplayName() throws Exception { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - - X509Certificate clientCert = (X509Certificate) cf.generateCertificate(getClass().getResourceAsStream( - "startssl-client.crt")); - assertEquals("Andreas Schildbach", X509Utils.getDisplayNameFromCertificate(clientCert, false)); - - X509Certificate comodoCert = (X509Certificate) cf.generateCertificate(getClass().getResourceAsStream( - "comodo-smime.crt")); - assertEquals("comodo.com@schildbach.de", X509Utils.getDisplayNameFromCertificate(comodoCert, true)); - } -} diff --git a/core/src/test/java/org/bitcoinj/protocols/payments/PaymentProtocolTest.java b/core/src/test/java/org/bitcoinj/protocols/payments/PaymentProtocolTest.java deleted file mode 100644 index f61403b3462..00000000000 --- a/core/src/test/java/org/bitcoinj/protocols/payments/PaymentProtocolTest.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2014 Andreas Schildbach - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoinj.protocols.payments; - -import org.bitcoinj.protobuf.payments.Protos; -import org.bitcoinj.protobuf.payments.Protos.Payment; -import org.bitcoinj.protobuf.payments.Protos.PaymentACK; -import org.bitcoinj.protobuf.payments.Protos.PaymentRequest; -import org.bitcoinj.base.BitcoinNetwork; -import org.bitcoinj.base.ScriptType; -import org.bitcoinj.base.Address; -import org.bitcoinj.base.Coin; -import org.bitcoinj.base.internal.TimeUtils; -import org.bitcoinj.crypto.ECKey; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.crypto.X509Utils; -import org.bitcoinj.params.TestNet3Params; -import org.bitcoinj.protocols.payments.PaymentProtocol.Output; -import org.bitcoinj.protocols.payments.PaymentProtocol.PkiVerificationData; -import org.bitcoinj.protocols.payments.PaymentProtocolException.PkiVerificationException; -import org.bitcoinj.script.ScriptBuilder; -import org.bitcoinj.testing.FakeTxBuilder; -import org.junit.Before; -import org.junit.Test; - -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.LinkedList; -import java.util.List; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class PaymentProtocolTest { - private static final NetworkParameters TESTNET = TestNet3Params.get(); - - // static test data - private static final Coin AMOUNT = Coin.SATOSHI; - private static final Address TO_ADDRESS = new ECKey().toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET); - private static final String MEMO = "memo"; - private static final String PAYMENT_URL = "https://example.com"; - private static final byte[] MERCHANT_DATA = { 0, 1, 2 }; - - private KeyStore caStore; - private X509Certificate caCert; - - @Before - public void setUp() throws Exception { - caStore = X509Utils.loadKeyStore("JKS", "password", getClass().getResourceAsStream("test-cacerts")); - caCert = (X509Certificate) caStore.getCertificate("test-cacert"); - } - - @Test - public void testSignAndVerifyValid() throws Exception { - Protos.PaymentRequest.Builder paymentRequest = minimalPaymentRequest().toBuilder(); - - // Sign - KeyStore keyStore = X509Utils - .loadKeyStore("JKS", "password", getClass().getResourceAsStream("test-valid-cert")); - PrivateKey privateKey = (PrivateKey) keyStore.getKey("test-valid", "password".toCharArray()); - X509Certificate clientCert = (X509Certificate) keyStore.getCertificate("test-valid"); - PaymentProtocol.signPaymentRequest(paymentRequest, new X509Certificate[]{clientCert}, privateKey); - - // Verify - PkiVerificationData verificationData = PaymentProtocol.verifyPaymentRequestPki(paymentRequest.build(), caStore); - assertNotNull(verificationData); - assertEquals(caCert, verificationData.rootAuthority.getTrustedCert()); - } - - @Test(expected = PkiVerificationException.class) - public void testSignAndVerifyExpired() throws Exception { - Protos.PaymentRequest.Builder paymentRequest = minimalPaymentRequest().toBuilder(); - - // Sign - KeyStore keyStore = X509Utils.loadKeyStore("JKS", "password", - getClass().getResourceAsStream("test-expired-cert")); - PrivateKey privateKey = (PrivateKey) keyStore.getKey("test-expired", "password".toCharArray()); - X509Certificate clientCert = (X509Certificate) keyStore.getCertificate("test-expired"); - PaymentProtocol.signPaymentRequest(paymentRequest, new X509Certificate[]{clientCert}, privateKey); - - // Verify - PaymentProtocol.verifyPaymentRequestPki(paymentRequest.build(), caStore); - } - - private Protos.PaymentRequest minimalPaymentRequest() { - Protos.PaymentDetails.Builder paymentDetails = Protos.PaymentDetails.newBuilder(); - paymentDetails.setTime(TimeUtils.currentTime().getEpochSecond()); - Protos.PaymentRequest.Builder paymentRequest = Protos.PaymentRequest.newBuilder(); - paymentRequest.setSerializedPaymentDetails(paymentDetails.build().toByteString()); - return paymentRequest.build(); - } - - @Test - public void testPaymentRequest() throws Exception { - // Create - PaymentRequest paymentRequest = PaymentProtocol.createPaymentRequest(TESTNET, AMOUNT, TO_ADDRESS, MEMO, - PAYMENT_URL, MERCHANT_DATA).build(); - byte[] paymentRequestBytes = paymentRequest.toByteArray(); - - // Parse - PaymentSession parsedPaymentRequest = PaymentProtocol.parsePaymentRequest(PaymentRequest - .parseFrom(paymentRequestBytes)); - final List parsedOutputs = parsedPaymentRequest.getOutputs(); - assertEquals(1, parsedOutputs.size()); - assertEquals(AMOUNT, parsedOutputs.get(0).amount); - assertArrayEquals(ScriptBuilder.createOutputScript(TO_ADDRESS).program(), parsedOutputs.get(0).scriptData); - assertEquals(MEMO, parsedPaymentRequest.getMemo()); - assertEquals(PAYMENT_URL, parsedPaymentRequest.getPaymentUrl()); - assertArrayEquals(MERCHANT_DATA, parsedPaymentRequest.getMerchantData()); - } - - @Test - public void testPaymentMessage() throws Exception { - // Create - List transactions = new LinkedList<>(); - transactions.add(FakeTxBuilder.createFakeTx(TESTNET.network(), AMOUNT, TO_ADDRESS)); - Coin refundAmount = Coin.SATOSHI; - Address refundAddress = new ECKey().toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET); - Payment payment = PaymentProtocol.createPaymentMessage(transactions, refundAmount, refundAddress, MEMO, - MERCHANT_DATA); - byte[] paymentBytes = payment.toByteArray(); - - // Parse - Payment parsedPayment = Payment.parseFrom(paymentBytes); - List parsedTransactions = PaymentProtocol.parseTransactionsFromPaymentMessage(TESTNET, - parsedPayment); - assertEquals(transactions, parsedTransactions); - assertEquals(1, parsedPayment.getRefundToCount()); - assertEquals(MEMO, parsedPayment.getMemo()); - assertArrayEquals(MERCHANT_DATA, parsedPayment.getMerchantData().toByteArray()); - } - - @Test - public void testPaymentAck() throws Exception { - // Create - Payment paymentMessage = Protos.Payment.newBuilder().build(); - PaymentACK paymentAck = PaymentProtocol.createPaymentAck(paymentMessage, MEMO); - byte[] paymentAckBytes = paymentAck.toByteArray(); - - // Parse - PaymentACK parsedPaymentAck = PaymentACK.parseFrom(paymentAckBytes); - assertEquals(paymentMessage, parsedPaymentAck.getPayment()); - assertEquals(MEMO, parsedPaymentAck.getMemo()); - } -} diff --git a/core/src/test/java/org/bitcoinj/protocols/payments/PaymentSessionTest.java b/core/src/test/java/org/bitcoinj/protocols/payments/PaymentSessionTest.java deleted file mode 100644 index 18969b77aef..00000000000 --- a/core/src/test/java/org/bitcoinj/protocols/payments/PaymentSessionTest.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoinj.protocols.payments; - -import com.google.protobuf.ByteString; -import org.bitcoinj.protobuf.payments.Protos; -import org.bitcoinj.base.BitcoinNetwork; -import org.bitcoinj.base.ScriptType; -import org.bitcoinj.base.Address; -import org.bitcoinj.base.Coin; -import org.bitcoinj.base.internal.TimeUtils; -import org.bitcoinj.core.Context; -import org.bitcoinj.core.TransactionOutPoint; -import org.bitcoinj.crypto.ECKey; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.core.TransactionInput; -import org.bitcoinj.core.TransactionOutput; -import org.bitcoinj.crypto.TrustStoreLoader; -import org.bitcoinj.params.MainNetParams; -import org.bitcoinj.params.TestNet3Params; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import java.io.InputStream; -import java.net.URL; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import static org.bitcoinj.base.Coin.COIN; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -public class PaymentSessionTest { - private static final NetworkParameters TESTNET = TestNet3Params.get(); - private static final NetworkParameters MAINNET = MainNetParams.get(); - - private static final String simplePaymentUrl = "http://a.simple.url.com/"; - private static final String paymentRequestMemo = "send coinz noa plz kthx"; - private static final String paymentMemo = "take ze coinz"; - private static final ByteString merchantData = ByteString.copyFromUtf8("merchant data"); - private static final Instant time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS); - private ECKey serverKey; - private Transaction tx; - private TransactionOutput outputToMe; - private final Coin amount = COIN; - - @Before - public void setUp() { - Context.propagate(new Context()); - serverKey = new ECKey(); - tx = new Transaction(); - outputToMe = new TransactionOutput(tx, amount, serverKey); - tx.addOutput(outputToMe); - } - - @Test - public void testSimplePayment() throws Exception { - // Create a PaymentRequest and make sure the correct values are parsed by the PaymentSession. - MockPaymentSession paymentSession = new MockPaymentSession(newSimplePaymentRequest("test")); - assertEquals(paymentRequestMemo, paymentSession.getMemo()); - assertEquals(amount, paymentSession.getValue()); - assertEquals(simplePaymentUrl, paymentSession.getPaymentUrl()); - assertEquals(time, paymentSession.time()); - assertTrue(paymentSession.getSendRequest().tx.equals(tx)); - assertFalse(paymentSession.isExpired()); - - // Send the payment and verify that the correct information is sent. - // Add a dummy input to tx so it is considered valid. - tx.addInput(new TransactionInput(tx, outputToMe.getScriptBytes(), TransactionOutPoint.UNCONNECTED)); - ArrayList txns = new ArrayList<>(); - txns.add(tx); - Address refundAddr = serverKey.toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET); - paymentSession.sendPayment(txns, refundAddr, paymentMemo); - assertEquals(1, paymentSession.getPaymentLog().size()); - assertEquals(simplePaymentUrl, paymentSession.getPaymentLog().get(0).getUrl().toString()); - Protos.Payment payment = paymentSession.getPaymentLog().get(0).getPayment(); - assertEquals(paymentMemo, payment.getMemo()); - assertEquals(merchantData, payment.getMerchantData()); - assertEquals(1, payment.getRefundToCount()); - assertEquals(amount.value, payment.getRefundTo(0).getAmount()); - TransactionOutput refundOutput = new TransactionOutput(null, amount, refundAddr); - ByteString refundScript = ByteString.copyFrom(refundOutput.getScriptBytes()); - assertTrue(refundScript.equals(payment.getRefundTo(0).getScript())); - } - - @Test - public void testDefaults() throws Exception { - Protos.Output.Builder outputBuilder = Protos.Output.newBuilder() - .setScript(ByteString.copyFrom(outputToMe.getScriptBytes())); - Protos.PaymentDetails paymentDetails = Protos.PaymentDetails.newBuilder() - .setTime(time.getEpochSecond()) - .addOutputs(outputBuilder) - .build(); - Protos.PaymentRequest paymentRequest = Protos.PaymentRequest.newBuilder() - .setSerializedPaymentDetails(paymentDetails.toByteString()) - .build(); - MockPaymentSession paymentSession = new MockPaymentSession(paymentRequest); - assertEquals(Coin.ZERO, paymentSession.getValue()); - assertNull(paymentSession.getPaymentUrl()); - assertNull(paymentSession.getMemo()); - } - - @Test - public void testExpiredPaymentRequest() throws PaymentProtocolException { - MockPaymentSession paymentSession = new MockPaymentSession(newExpiredPaymentRequest()); - assertTrue(paymentSession.isExpired()); - // Send the payment and verify that an exception is thrown. - // Add a dummy input to tx so it is considered valid. - tx.addInput(new TransactionInput(tx, outputToMe.getScriptBytes(), TransactionOutPoint.UNCONNECTED)); - ArrayList txns = new ArrayList<>(); - txns.add(tx); - - CompletableFuture ack = paymentSession.sendPayment(txns, null, null); - try { - ack.get(); - } catch (ExecutionException e) { - if (e.getCause() instanceof PaymentProtocolException.Expired) { - PaymentProtocolException.Expired cause = (PaymentProtocolException.Expired) e.getCause(); - assertEquals(0, paymentSession.getPaymentLog().size()); - assertEquals(cause.getMessage(), "PaymentRequest is expired"); - return; - } - } catch (InterruptedException e) { - // Ignore - } - fail("Expected exception due to expired PaymentRequest"); - } - - @Test - @Ignore("certificate expired") - public void testPkiVerification() throws Exception { - InputStream in = getClass().getResourceAsStream("pki_test.bitcoinpaymentrequest"); - Protos.PaymentRequest paymentRequest = Protos.PaymentRequest.newBuilder().mergeFrom(in).build(); - PaymentProtocol.PkiVerificationData pkiData = PaymentProtocol.verifyPaymentRequestPki(paymentRequest, - new TrustStoreLoader.DefaultTrustStoreLoader().getKeyStore()); - assertEquals("www.bitcoincore.org", pkiData.displayName); - assertEquals("The USERTRUST Network, Salt Lake City, US", pkiData.rootAuthorityName); - } - - private Protos.PaymentRequest newSimplePaymentRequest(String netID) { - Protos.Output.Builder outputBuilder = Protos.Output.newBuilder() - .setAmount(amount.value) - .setScript(ByteString.copyFrom(outputToMe.getScriptBytes())); - Protos.PaymentDetails paymentDetails = Protos.PaymentDetails.newBuilder() - .setNetwork(netID) - .setTime(time.getEpochSecond()) - .setPaymentUrl(simplePaymentUrl) - .addOutputs(outputBuilder) - .setMemo(paymentRequestMemo) - .setMerchantData(merchantData) - .build(); - return Protos.PaymentRequest.newBuilder() - .setPaymentDetailsVersion(1) - .setPkiType("none") - .setSerializedPaymentDetails(paymentDetails.toByteString()) - .build(); - } - - private Protos.PaymentRequest newExpiredPaymentRequest() { - Protos.Output.Builder outputBuilder = Protos.Output.newBuilder() - .setAmount(amount.value) - .setScript(ByteString.copyFrom(outputToMe.getScriptBytes())); - Protos.PaymentDetails paymentDetails = Protos.PaymentDetails.newBuilder() - .setNetwork("test") - .setTime(time.minusSeconds(10).getEpochSecond()) - .setExpires(time.minusSeconds(1).getEpochSecond()) - .setPaymentUrl(simplePaymentUrl) - .addOutputs(outputBuilder) - .setMemo(paymentRequestMemo) - .setMerchantData(merchantData) - .build(); - return Protos.PaymentRequest.newBuilder() - .setPaymentDetailsVersion(1) - .setPkiType("none") - .setSerializedPaymentDetails(paymentDetails.toByteString()) - .build(); - } - - private static class MockPaymentSession extends PaymentSession { - private final ArrayList paymentLog = new ArrayList<>(); - - public MockPaymentSession(Protos.PaymentRequest request) throws PaymentProtocolException { - super(request); - } - - public ArrayList getPaymentLog() { - return paymentLog; - } - - @Override - protected CompletableFuture sendPayment(final URL url, final Protos.Payment payment) { - paymentLog.add(new PaymentLogItem(url, payment)); - // Return a completed future that has a `null` value. This will satisfy the current tests. - return CompletableFuture.completedFuture(null); - } - - public static class PaymentLogItem { - private final URL url; - private final Protos.Payment payment; - - PaymentLogItem(final URL url, final Protos.Payment payment) { - this.url = url; - this.payment = payment; - } - - public URL getUrl() { - return url; - } - - public Protos.Payment getPayment() { - return payment; - } - } - } -} diff --git a/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java b/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java index 7cb083a8ed9..632a5b95084 100644 --- a/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java +++ b/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java @@ -427,37 +427,4 @@ public void testBad_TooLargeAmount() throws BitcoinURIParseException { BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=" + tooLargeByOne.movePointLeft(8), MAINNET); } - - @Test - public void testPaymentProtocolReq() throws Exception { - // Non-backwards compatible form ... - BitcoinURI uri = BitcoinURI.of("bitcoin:?r=https%3A%2F%2Fbitcoincore.org%2F%7Egavin%2Ff.php%3Fh%3Db0f02e7cea67f168e25ec9b9f9d584f9", TESTNET); - assertEquals("https://bitcoincore.org/~gavin/f.php?h=b0f02e7cea67f168e25ec9b9f9d584f9", uri.getPaymentRequestUrl()); - assertEquals(Collections.singletonList("https://bitcoincore.org/~gavin/f.php?h=b0f02e7cea67f168e25ec9b9f9d584f9"), - uri.getPaymentRequestUrls()); - assertNull(uri.getAddress()); - } - - @Test - public void testMultiplePaymentProtocolReq() throws Exception { - BitcoinURI uri = BitcoinURI.of("bitcoin:?r=https%3A%2F%2Fbitcoincore.org%2F%7Egavin&r1=bt:112233445566", MAINNET); - assertEquals(Arrays.asList("bt:112233445566", "https://bitcoincore.org/~gavin"), uri.getPaymentRequestUrls()); - assertEquals("https://bitcoincore.org/~gavin", uri.getPaymentRequestUrl()); - } - - @Test - public void testNoPaymentProtocolReq() throws Exception { - BitcoinURI uri = BitcoinURI.of("bitcoin:" + MAINNET_GOOD_ADDRESS, MAINNET); - assertNull(uri.getPaymentRequestUrl()); - assertEquals(Collections.emptyList(), uri.getPaymentRequestUrls()); - assertNotNull(uri.getAddress()); - } - - @Test - public void testUnescapedPaymentProtocolReq() throws Exception { - BitcoinURI uri = BitcoinURI.of("bitcoin:?r=https://merchant.example.com/pay?h%3D2a8628fc2fbe", TESTNET); - assertEquals("https://merchant.example.com/pay?h=2a8628fc2fbe", uri.getPaymentRequestUrl()); - assertEquals(Collections.singletonList("https://merchant.example.com/pay?h=2a8628fc2fbe"), uri.getPaymentRequestUrls()); - assertNull(uri.getAddress()); - } } diff --git a/core/src/test/resources/org/bitcoinj/crypto/comodo-smime.crt b/core/src/test/resources/org/bitcoinj/crypto/comodo-smime.crt deleted file mode 100644 index 8f6d782abca..00000000000 --- a/core/src/test/resources/org/bitcoinj/crypto/comodo-smime.crt +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFMTCCBBmgAwIBAgIQV2O4gJQ5NHgn/PPvDsU1djANBgkqhkiG9w0BAQUFADCB -kzELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxOTA3BgNV -BAMTMENPTU9ETyBDbGllbnQgQXV0aGVudGljYXRpb24gYW5kIFNlY3VyZSBFbWFp -bCBDQTAeFw0xNDAzMjkwMDAwMDBaFw0xNTAzMjkyMzU5NTlaMCkxJzAlBgkqhkiG -9w0BCQEWGGNvbW9kby5jb21Ac2NoaWxkYmFjaC5kZTCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAM61nbZo3ZN0Ojzn7UzoHBf07ZyTDm3KnwK4BdLKLgNS -NbGAJtgaYN91qKRbXf97VAFIN6FGhoXT+7MXSzlHgQHn7RkForMyREsD6F32TtyV -ZY9RuMGWjmPtABPRgeCVfNJNh9Hu87Uhhkj3Ma+H//ykfkJdDiOyBWIOJdjBFSZZ -M6bsZnyH8JCHqmxvK2qHpk+qNqpsNOZV83GYPA2gTFWd1AHjo5+A7W1Bo/qyJMrz -tpab0i+ieJPJdi6eJkMt3+nfr57Q2o4A3ZxH0Axq2D1a2dElhMK/JQilh2D+IDUp -VjoKkHgV9yji9UGOc3VHq+Sx8bNTumL7OFLCFYky9J8CAwEAAaOCAegwggHkMB8G -A1UdIwQYMBaAFHoTTgB0W8Z4Y2QnwS/ioFu8ecV7MB0GA1UdDgQWBBRWUNGbH8V3 -av0ESrIUwnvmq4aEEDAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH/BAIwADAgBgNV -HSUEGTAXBggrBgEFBQcDBAYLKwYBBAGyMQEDBQIwEQYJYIZIAYb4QgEBBAQDAgUg -MEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQEBMCswKQYIKwYBBQUHAgEWHWh0dHBz -Oi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMFcGA1UdHwRQME4wTKBKoEiGRmh0dHA6 -Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET0NsaWVudEF1dGhlbnRpY2F0aW9uYW5k -U2VjdXJlRW1haWxDQS5jcmwwgYgGCCsGAQUFBwEBBHwwejBSBggrBgEFBQcwAoZG -aHR0cDovL2NydC5jb21vZG9jYS5jb20vQ09NT0RPQ2xpZW50QXV0aGVudGljYXRp -b25hbmRTZWN1cmVFbWFpbENBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au -Y29tb2RvY2EuY29tMCMGA1UdEQQcMBqBGGNvbW9kby5jb21Ac2NoaWxkYmFjaC5k -ZTANBgkqhkiG9w0BAQUFAAOCAQEAMFhmP1Zy16m5L9gaGCDy847tJI3btBFFZMu/ -MMqamC5515QayLfwf9K2nmu1W63nehEAKqNw+PR1xTYnhPT4fopw5zFndiNg0L5u -blEbRgSdQYBh1I2dkzzPRDRJig4LfxVzRzL66FbllLEiJ6oR/XgdsH+JFgyjhk3Y -uJt+29sXoZ+ZR29d7l07OikQGI0HWCmp/UiwBcQ4dcTrDB72JYLHyli+OTAkcu9I -rBpsIbWJq+7NjaQ/8CJjvQ2neTgDS1Dq5DzMqqRlhxQwRl4dhfCSCcF81Vf0as4S -vVDNR8vJ9puGlYyGVJHhQ6mEoFEIvpetS7E9ELHnybSC9ev8CA== ------END CERTIFICATE----- diff --git a/core/src/test/resources/org/bitcoinj/crypto/startssl-client.crt b/core/src/test/resources/org/bitcoinj/crypto/startssl-client.crt deleted file mode 100644 index 44bed530654..00000000000 --- a/core/src/test/resources/org/bitcoinj/crypto/startssl-client.crt +++ /dev/null @@ -1,41 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIHPjCCBiagAwIBAgICH8wwDQYJKoZIhvcNAQEFBQAwgYwxCzAJBgNVBAYTAklM -MRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSswKQYDVQQLEyJTZWN1cmUgRGlnaXRh -bCBDZXJ0aWZpY2F0ZSBTaWduaW5nMTgwNgYDVQQDEy9TdGFydENvbSBDbGFzcyAx -IFByaW1hcnkgSW50ZXJtZWRpYXRlIENsaWVudCBDQTAeFw0wODA2MjIyMzA4MTJa -Fw0wOTA2MjIyMzA4MTJaMIHEMQswCQYDVQQGEwJERTEPMA0GA1UECBMGQmF5ZXJu -MREwDwYDVQQHEwhNdWVuY2hlbjEbMBkGA1UEChMSQW5kcmVhcyBTY2hpbGRiYWNo -MR4wHAYDVQQLExVQZXJzb25hIG5vdCB2YWxpZGF0ZWQxKTAnBgNVBAMTIFN0YXJ0 -Q29tIEZyZWUgQ2VydGlmaWNhdGUgTWVtYmVyMSkwJwYJKoZIhvcNAQkBFhpzdGFy -dHNzbC5jb21Ac2NoaWxkYmFjaC5kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBAOhtSryPHR3gB5E48JZS9y8R3A9I2gRGGon+kRLNh+LCAJJ1hm28Lr41 -leH0uApWs1WP//qzkXaFUoLlilu/XzkDU48J6HCeUq+7zkxFhq7UxK2lq7J1P8fH -tbKYSBWMfzZuVmwhrbaurggfCmq/o5/angbhN7Pn+aV1aPegjAKd8n94HVvVgzkp -DDYTA5vFbX/3241MIeKRU5InYw9KzXAC1aE0BYVM21f5Z/UQ+V4PEfXrSH9OHPVW -3GWasmjzR9h+/u1omJzejkXY6Ygd15tnmatqMoxRRVMWhWS9Hg7D+AgeiZNlEQZV -NTZxIiPA8618x51Wlq0XAiV5UvkMImECAwEAAaOCA24wggNqMAwGA1UdEwQFMAMC -AQAwCwYDVR0PBAQDAgSwMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAd -BgNVHQ4EFgQUeMIc15HHtjl2TDpGnc9X2UkUV2EwgagGA1UdIwSBoDCBnYAUU3Lt -kpzg2ssBXHx+ljVO8tS4UYKhgYGkfzB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN -U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNh -dGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRo -b3JpdHmCAQ0wggFHBgNVHSAEggE+MIIBOjCCATYGCysGAQQBgbU3AQEFMIIBJTAu -BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0 -BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl -LnBkZjCBvAYIKwYBBQUHAgIwga8wFBYNU3RhcnRDb20gTHRkLjADAgEBGoGWTGlt -aXRlZCBMaWFiaWxpdHksIHJlYWQgdGhlIHNlY3Rpb24gKkxlZ2FsIExpbWl0YXRp -b25zKiBvZiB0aGUgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUG9s -aWN5IGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3ku -cGRmMGMGA1UdHwRcMFowK6ApoCeGJWh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2Ny -dHUxLWNybC5jcmwwK6ApoCeGJWh0dHA6Ly9jcmwuc3RhcnRzc2wuY29tL2NydHUx -LWNybC5jcmwwgY4GCCsGAQUFBwEBBIGBMH8wOQYIKwYBBQUHMAGGLWh0dHA6Ly9v -Y3NwLnN0YXJ0c3NsLmNvbS9zdWIvY2xhc3MxL2NsaWVudC9jYTBCBggrBgEFBQcw -AoY2aHR0cDovL3d3dy5zdGFydHNzbC5jb20vY2VydHMvc3ViLmNsYXNzMS5jbGll -bnQuY2EuY3J0MCMGA1UdEgQcMBqGGGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tLzAN -BgkqhkiG9w0BAQUFAAOCAQEAIsKaduVCShmIKRsUCqTSeJSDIjgSdRiRmvDig+vT -NkRrfhZhtpkp03lfe71agFlaV6UWtFF2nrgFaoUmq2KxMnCD1gkQPQu01TqrDDwi -+dKFkh4tSGyj++BRCX4jpYgY7pDzh0Dtb261ovpzYB3e36mMO4AiLHby10VHir+k -AUI87JVffsgsKCEEEkywA//KcXqyVfgW3FgicNczCiwXdWCLJcnBAq8aundebdIH -hTFoWB/5BuRRCY2Je9XFR8vb1EUC5SuTL+wT0mGdx2T+qNskNtbZKyHLQSp9fCoD -yupR1THhr7iqF4zRI6r5r8tRuu8jr55NgN5ZA+LCisEJ7A== ------END CERTIFICATE----- diff --git a/core/src/test/resources/org/bitcoinj/crypto/startssl-smime.crt b/core/src/test/resources/org/bitcoinj/crypto/startssl-smime.crt deleted file mode 100644 index ac8ae2711fb..00000000000 --- a/core/src/test/resources/org/bitcoinj/crypto/startssl-smime.crt +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIHFDCCBfygAwIBAgIDAILwMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ -TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0 -YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg -MSBQcmltYXJ5IEludGVybWVkaWF0ZSBDbGllbnQgQ0EwHhcNMDkwNjA5MDAwMDAx -WhcNMTAwNjA5MjM1OTU5WjB2MR4wHAYDVQQKExVQZXJzb25hIE5vdCBWYWxpZGF0 -ZWQxKTAnBgNVBAMTIFN0YXJ0Q29tIEZyZWUgQ2VydGlmaWNhdGUgTWVtYmVyMSkw -JwYJKoZIhvcNAQkBFhpzdGFydHNzbC5jb21Ac2NoaWxkYmFjaC5kZTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBALA4dtu1FlTbhZQ+M9pyTFte40zotk3J -fqvEkDpWLBz3orN4SkMAUDOgTWdvNm+PakX2tEsZGD+nnWzkO3NI8x5ZhrOF3HwW -zeCaYzaDjhhRw4G1K2FKVBHK6TUkZ/LoLimVMsV8AbsAWWlmxTCXB1vyoiOMISiK -rMFsRFAQdtB5wHVuZdtVnO1++yLfQo+ckuTT35RBztpcP63GkVyo0ucFC8DxNQOA -+k8cEIVrfsr9PrLUlhTx+P5jQAaURqcVf0IAR6bNV7WdJmli7yjlWeQm7ymh8YFE -6xsy16TO24GQWR5waBFRqGaJPqRnpAdhiUc+1nbNGuaOYYCD6kATVycCAwEAAaOC -A5IwggOOMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdJQQWMBQGCCsGAQUF -BwMCBggrBgEFBQcDBDAdBgNVHQ4EFgQUY819Gv9zSrHpLGP3e9WbCJfzEgAwJQYD -VR0RBB4wHIEac3RhcnRzc2wuY29tQHNjaGlsZGJhY2guZGUwgagGA1UdIwSBoDCB -nYAUU3Ltkpzg2ssBXHx+ljVO8tS4UYKhgYGkfzB9MQswCQYDVQQGEwJJTDEWMBQG -A1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2Vy -dGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlv -biBBdXRob3JpdHmCAQ0wggFHBgNVHSAEggE+MIIBOjCCATYGCysGAQQBgbU3AQIA -MIIBJTAuBggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5 -LnBkZjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJt -ZWRpYXRlLnBkZjCBvAYIKwYBBQUHAgIwga8wFBYNU3RhcnRDb20gTHRkLjADAgEB -GoGWTGltaXRlZCBMaWFiaWxpdHksIHJlYWQgdGhlIHNlY3Rpb24gKkxlZ2FsIExp -bWl0YXRpb25zKiBvZiB0aGUgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkgUG9saWN5IGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9w -b2xpY3kucGRmMGMGA1UdHwRcMFowK6ApoCeGJWh0dHA6Ly93d3cuc3RhcnRzc2wu -Y29tL2NydHUxLWNybC5jcmwwK6ApoCeGJWh0dHA6Ly9jcmwuc3RhcnRzc2wuY29t -L2NydHUxLWNybC5jcmwwgY4GCCsGAQUFBwEBBIGBMH8wOQYIKwYBBQUHMAGGLWh0 -dHA6Ly9vY3NwLnN0YXJ0c3NsLmNvbS9zdWIvY2xhc3MxL2NsaWVudC9jYTBCBggr -BgEFBQcwAoY2aHR0cDovL3d3dy5zdGFydHNzbC5jb20vY2VydHMvc3ViLmNsYXNz -MS5jbGllbnQuY2EuY3J0MCMGA1UdEgQcMBqGGGh0dHA6Ly93d3cuc3RhcnRzc2wu -Y29tLzANBgkqhkiG9w0BAQUFAAOCAQEAaJgOEPjkRcKMVbbofA+GVlc1iMR+kJHk -bQNmojAmgDL1pXabFuNZqx7FVUBk7MQQOUaC1vd3RbyOE+AzdXaq7/pFk5/Zxalv -xn4gSA/wGHDB0oAi+efWQy7ZsskIOWkjg7tKqy0KCRlA6ZlQhL0aTFZe2X6fu/fI -eebFb6gQ3vhOwIgAGz7CZMjRBqPjiqpPrD/Uac2LORUdLw/wowTV1YBnNwsZGnzJ -/WquZB7n/yJjaSqhSL0s37AOg3TvXEXYS2GpoA02lQKfq3Lo86piAxSh7aJf7dpT -JMVnE6/+5FyjpP8Hpl8FARv1m51c9n788cDzS4/qFibKf9s6yt1/0A== ------END CERTIFICATE----- diff --git a/core/src/test/resources/org/bitcoinj/protocols/payments/README b/core/src/test/resources/org/bitcoinj/protocols/payments/README deleted file mode 100644 index 024eb16d491..00000000000 --- a/core/src/test/resources/org/bitcoinj/protocols/payments/README +++ /dev/null @@ -1,6 +0,0 @@ -# Create key store for CA certificate -keytool -keystore test-cacerts -importcert -file test-cacert.pem -alias test-cacert -deststorepass password - -# Create key store for certificate and private key -openssl pkcs12 -export -in test-valid-cert.pem -inkey test-valid-key.pem -passin pass:password -out test-valid.p12 -passout pass:password -name test-valid -keytool -importkeystore -deststorepass password -destkeypass password -destkeystore test-valid-cert -srckeystore test-valid.p12 -srcstoretype PKCS12 -srcstorepass password -alias test-valid diff --git a/core/src/test/resources/org/bitcoinj/protocols/payments/pki_test.bitcoinpaymentrequest b/core/src/test/resources/org/bitcoinj/protocols/payments/pki_test.bitcoinpaymentrequest deleted file mode 100644 index a7390377dea7d0c30a7e1584516f7f71d23033e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4106 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 { " The output destination can also be a native segwit address.%n" + " If the output destination starts with 04 and is 65 or 33 bytes long it will be treated as a public key instead of an address and the send will use%n" + " CHECKSIG as the script.%n" + - " If --payment-request is specified, a transaction will be created using the provided payment request. A payment request can be a local file, a bitcoin uri, or url to download the payment request, e.g.:%n" + - " --payment-request=/path/to/my.bitcoinpaymentrequest%n" + - " --payment-request=bitcoin:?r=https://merchant.example.com/pay?123%n" + - " --payment-request=https://merchant.example.com/pay?123%n" + " Other options include:%n" + " --fee-per-vkb or --fee-sat-per-vbyte sets the network fee, see below%n" + " --select-addr or --select-output to select specific outputs%n" + @@ -221,8 +214,6 @@ public class WalletTool implements Callable { private boolean ignoreMandatoryExtensions = false; @CommandLine.Option(names = "--password", description = "For an encrypted wallet, the password is provided here.") private String password = null; - @CommandLine.Option(names = "--payment-request", description = "Specifies a payment request either by name of a local file, a bitcoin uri, or url to download the payment request.") - private String paymentRequestLocationStr; @CommandLine.Option(names = "--no-pki", description = "Disables pki verification for payment requests.") private boolean noPki = false; @CommandLine.Option(names = "--dump-privkeys", description = "Private keys and seed are printed.") @@ -239,7 +230,6 @@ public class WalletTool implements Callable { private static AbstractBlockChain chain; private static PeerGroup peerGroup; private static Wallet wallet; - private static org.bitcoinj.protobuf.payments.Protos.PaymentRequest paymentRequest; public static class Condition { public enum Type { @@ -408,10 +398,7 @@ public Integer call() throws IOException, BlockStoreException { case RESET: reset(); break; case SYNC: syncChain(); break; case SEND: - if (paymentRequestLocationStr != null && outputsStr != null) { - System.err.println("--payment-request and --output cannot be used together."); - return 1; - } else if (feePerVkbStr != null && feeSatPerVbyteStr != null) { + if (feePerVkbStr != null && feeSatPerVbyteStr != null) { System.err.println("--fee-per-kb and --fee-sat-per-byte cannot be used together."); return 1; } else if (outputsStr != null) { @@ -454,10 +441,8 @@ else if (feeSatPerVbyteStr != null) coinSelector = null; } send(coinSelector, outputsStr, feePerVkb, lockTimeStr, allowUnconfirmed); - } else if (paymentRequestLocationStr != null) { - sendPaymentRequest(paymentRequestLocationStr, !noPki); } else { - System.err.println("You must specify a --payment-request or at least one --output=addr:value."); + System.err.println("You must specify at least one --output=addr:value."); return 1; } break; @@ -764,140 +749,6 @@ private static long parseLockTimeStr(String lockTimeStr) throws ParseException { return Long.parseLong(lockTimeStr); } - private void sendPaymentRequest(String location, boolean verifyPki) { - if (location.startsWith("http") || location.startsWith("bitcoin")) { - try { - CompletableFuture future; - if (location.startsWith("http")) { - future = PaymentSession.createFromUrl(location, verifyPki); - } else { - BitcoinURI paymentRequestURI = BitcoinURI.of(location); - future = PaymentSession.createFromBitcoinUri(paymentRequestURI, verifyPki); - } - PaymentSession session = future.get(); - if (session != null) { - send(session); - } else { - System.err.println("Server returned null session"); - System.exit(1); - } - } catch (PaymentProtocolException e) { - System.err.println("Error creating payment session " + e.getMessage()); - System.exit(1); - } catch (BitcoinURIParseException e) { - System.err.println("Invalid bitcoin uri: " + e.getMessage()); - System.exit(1); - } catch (InterruptedException e) { - // Ignore. - } catch (ExecutionException e) { - throw new RuntimeException(e); - } - } else { - // Try to open the payment request as a file. - FileInputStream stream = null; - try { - File paymentRequestFile = new File(location); - stream = new FileInputStream(paymentRequestFile); - } catch (Exception e) { - System.err.println("Failed to open file: " + e.getMessage()); - System.exit(1); - } - try { - paymentRequest = org.bitcoinj.protobuf.payments.Protos.PaymentRequest.newBuilder().mergeFrom(stream).build(); - } catch(IOException e) { - System.err.println("Failed to parse payment request from file " + e.getMessage()); - System.exit(1); - } - PaymentSession session = null; - try { - session = new PaymentSession(paymentRequest, verifyPki); - } catch (PaymentProtocolException e) { - System.err.println("Error creating payment session " + e.getMessage()); - System.exit(1); - } - send(session); - } - } - - private void send(PaymentSession session) { - System.out.println("Payment Request"); - System.out.println("Coin: " + session.getValue().toFriendlyString()); - System.out.println("Date: " + session.time()); - System.out.println("Memo: " + session.getMemo()); - if (session.pkiVerificationData != null) { - System.out.println("Pki-Verified Name: " + session.pkiVerificationData.displayName); - System.out.println("PKI data verified by: " + session.pkiVerificationData.rootAuthorityName); - } - final SendRequest req = session.getSendRequest(); - if (password != null) { - req.aesKey = passwordToKey(true); - if (req.aesKey == null) - return; // Error message already printed. - } - - // Complete the transaction - try { - wallet.completeTx(req); // may throw InsufficientMoneyException. - } catch (InsufficientMoneyException e) { - System.err.println("Insufficient funds: have " + wallet.getBalance().toFriendlyString()); - System.exit(1); - } - if (offline) { - wallet.commitTx(req.tx); - return; - } - - // Setup network communication (but not PeerGroup) - try { - setup(); - } catch (BlockStoreException e) { - System.err.println("BlockStoreException: " + e.getMessage()); - System.exit(1); - } - - // Send the payment - try { - // No refund address specified, no user-specified memo field. - PaymentProtocol.Ack ack = session.sendPayment(Collections.singletonList(req.tx), null, null).get(); - wallet.commitTx(req.tx); - System.out.println("Memo from server: " + ack.getMemo()); - } catch (ExecutionException e) { - try { - throw e.getCause(); - } catch (PaymentProtocolException.InvalidPaymentRequestURL e1) { - System.out.println("Missing/Invalid Payment Request URL, broadcasting transaction with PeerGroup"); - broadcastPayment(req); - } catch (PaymentProtocolException e1) { - System.err.println("Failed to send payment " + e.getMessage()); - System.exit(1); - } catch (IOException e1) { - System.err.println("Invalid payment " + e.getMessage()); - System.exit(1); - } catch (Throwable t) { - System.err.println("Unexpected error " + e.getMessage()); - System.exit(1); - } - } catch (VerificationException e) { - System.err.println("Failed to send payment " + e.getMessage()); - System.exit(1); - } catch (InterruptedException e) { - System.err.println("Interrupted: " + e.getMessage()); - System.exit(1); - } - } - - private void broadcastPayment(SendRequest req) { - peerGroup.start(); - TransactionBroadcast broadcast = peerGroup.broadcastTransaction(req.tx); - try { - // Wait for broadcast to be sent - broadcast.awaitRelayed().get(); - } catch (InterruptedException | ExecutionException e) { - System.err.println("Failed to broadcast payment " + e.getMessage()); - System.exit(1); - } - } - /** * Wait for a condition to be satisfied * @param waitFor condition type to wait for