Skip to content

Commit

Permalink
Use bitcoinj payment protocol classes rather than our own.
Browse files Browse the repository at this point in the history
  • Loading branch information
schildbach committed Oct 2, 2014
1 parent aa1ae8d commit 5bdf226
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 266 deletions.
10 changes: 0 additions & 10 deletions wallet/res/layout/send_coins_fragment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,6 @@
android:textColor="@color/fg_significant"
android:textSize="@dimen/font_size_normal" />

<TextView
android:id="@+id/send_coins_payee_organization"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/list_entry_padding_horizontal_lax"
android:layout_marginRight="@dimen/list_entry_padding_horizontal_lax"
android:singleLine="true"
android:textColor="@color/fg_significant"
android:textSize="@dimen/font_size_normal" />

<TextView
android:id="@+id/send_coins_payee_verified_by"
android:layout_width="match_parent"
Expand Down
38 changes: 23 additions & 15 deletions wallet/src/de/schildbach/wallet/data/PaymentIntent.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ScriptException;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.Wallet.SendRequest;
import org.bitcoinj.core.WrongNetworkException;
import org.bitcoinj.protocols.payments.PaymentProtocol;
import org.bitcoinj.protocols.payments.PaymentProtocolException;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.uri.BitcoinURI;
Expand Down Expand Up @@ -67,6 +70,19 @@ public Output(final Coin amount, final Script script)
this.script = script;
}

public static Output valueOf(final PaymentProtocol.Output output) throws PaymentProtocolException.InvalidOutputs
{
try
{
final Script script = new Script(output.scriptData);
return new PaymentIntent.Output(output.amount, script);
}
catch (final ScriptException x)
{
throw new PaymentProtocolException.InvalidOutputs("unparseable script in output: " + Arrays.toString(output.scriptData));
}
}

public boolean hasAmount()
{
return amount != null && amount.signum() != 0;
Expand Down Expand Up @@ -143,9 +159,6 @@ private Output(final Parcel in)
@CheckForNull
public final String payeeName;

@CheckForNull
public final String payeeOrganization;

@CheckForNull
public final String payeeVerifiedBy;

Expand All @@ -169,13 +182,12 @@ private Output(final Parcel in)

private static final Logger log = LoggerFactory.getLogger(PaymentIntent.class);

public PaymentIntent(@Nullable final Standard standard, @Nullable final String payeeName, @Nullable final String payeeOrganization,
@Nullable final String payeeVerifiedBy, @Nullable final Output[] outputs, @Nullable final String memo, @Nullable final String paymentUrl,
@Nullable final byte[] payeeData, @Nullable final String paymentRequestUrl, @Nullable final byte[] paymentRequestHash)
public PaymentIntent(@Nullable final Standard standard, @Nullable final String payeeName, @Nullable final String payeeVerifiedBy,
@Nullable final Output[] outputs, @Nullable final String memo, @Nullable final String paymentUrl, @Nullable final byte[] payeeData,
@Nullable final String paymentRequestUrl, @Nullable final byte[] paymentRequestHash)
{
this.standard = standard;
this.payeeName = payeeName;
this.payeeOrganization = payeeOrganization;
this.payeeVerifiedBy = payeeVerifiedBy;
this.outputs = outputs;
this.memo = memo;
Expand All @@ -187,12 +199,12 @@ public PaymentIntent(@Nullable final Standard standard, @Nullable final String p

private PaymentIntent(@Nonnull final Address address, @Nullable final String addressLabel)
{
this(null, null, null, null, buildSimplePayTo(Coin.ZERO, address), addressLabel, null, null, null, null);
this(null, null, null, buildSimplePayTo(Coin.ZERO, address), addressLabel, null, null, null, null);
}

public static PaymentIntent blank()
{
return new PaymentIntent(null, null, null, null, null, null, null, null, null, null);
return new PaymentIntent(null, null, null, null, null, null, null, null, null);
}

public static PaymentIntent fromAddress(@Nonnull final Address address, @Nullable final String addressLabel)
Expand All @@ -214,7 +226,7 @@ public static PaymentIntent fromBitcoinUri(@Nonnull final BitcoinURI bitcoinUri)
final String paymentRequestHashStr = (String) bitcoinUri.getParameterByName("h");
final byte[] paymentRequestHash = paymentRequestHashStr != null ? base64UrlDecode(paymentRequestHashStr) : null;

return new PaymentIntent(PaymentIntent.Standard.BIP21, null, null, null, outputs, bitcoinUri.getLabel(), bluetoothMac != null ? "bt:"
return new PaymentIntent(PaymentIntent.Standard.BIP21, null, null, outputs, bitcoinUri.getLabel(), bluetoothMac != null ? "bt:"
+ bluetoothMac : null, null, bitcoinUri.getPaymentRequestUrl(), paymentRequestHash);
}

Expand Down Expand Up @@ -261,7 +273,7 @@ public PaymentIntent mergeWithEditedValues(@Nullable final Coin editedAmount, @N
outputs = buildSimplePayTo(editedAmount, editedAddress);
}

return new PaymentIntent(standard, payeeName, payeeOrganization, payeeVerifiedBy, outputs, memo, null, payeeData, null, null);
return new PaymentIntent(standard, payeeName, payeeVerifiedBy, outputs, memo, null, payeeData, null, null);
}

public SendRequest toSendRequest()
Expand Down Expand Up @@ -440,8 +452,6 @@ public String toString()
if (hasPayee())
{
builder.append(payeeName);
if (payeeOrganization != null)
builder.append("/").append(payeeOrganization);
if (payeeVerifiedBy != null)
builder.append("/").append(payeeVerifiedBy);
builder.append(',');
Expand Down Expand Up @@ -481,7 +491,6 @@ public void writeToParcel(final Parcel dest, final int flags)
dest.writeSerializable(standard);

dest.writeString(payeeName);
dest.writeString(payeeOrganization);
dest.writeString(payeeVerifiedBy);

if (outputs != null)
Expand Down Expand Up @@ -541,7 +550,6 @@ private PaymentIntent(final Parcel in)
standard = (Standard) in.readSerializable();

payeeName = in.readString();
payeeOrganization = in.readString();
payeeVerifiedBy = in.readString();

final int outputsLength = in.readInt();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.bitcoin.protocols.payments.Protos.PaymentACK;
import org.bitcoinj.core.ProtocolException;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.protocols.payments.PaymentProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -37,7 +38,6 @@
import android.bluetooth.BluetoothSocket;
import de.schildbach.wallet.Constants;
import de.schildbach.wallet.util.Bluetooth;
import de.schildbach.wallet.util.PaymentProtocol;

/**
* @author Shahar Livne
Expand Down Expand Up @@ -185,7 +185,7 @@ public void run()

log.debug("got payment message");

for (final Transaction tx : PaymentProtocol.parsePaymentMessage(payment))
for (final Transaction tx : PaymentProtocol.parseTransactionsFromPaymentMessage(Constants.NETWORK_PARAMETERS, payment))
{
if (!handleTx(tx))
ack = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import org.bitcoin.protocols.payments.Protos;
import org.bitcoin.protocols.payments.Protos.Payment;
import org.bitcoinj.protocols.payments.PaymentProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -41,7 +42,6 @@
import android.os.Looper;
import de.schildbach.wallet.Constants;
import de.schildbach.wallet.util.Bluetooth;
import de.schildbach.wallet.util.PaymentProtocol;
import de.schildbach.wallet_test.R;

/**
Expand Down Expand Up @@ -130,7 +130,7 @@ public void run()

final Protos.PaymentACK paymentAck = Protos.PaymentACK.parseFrom(is);

final boolean ack = !"nack".equals(PaymentProtocol.parsePaymentAck(paymentAck));
final boolean ack = !"nack".equals(PaymentProtocol.parsePaymentAck(paymentAck).getMemo());

log.info("received {} via http", ack ? "ack" : "nack");

Expand Down Expand Up @@ -235,7 +235,7 @@ public void run()

final Protos.PaymentACK paymentAck = Protos.PaymentACK.parseDelimitedFrom(is);

final boolean ack = "ack".equals(PaymentProtocol.parsePaymentAck(paymentAck));
final boolean ack = "ack".equals(PaymentProtocol.parsePaymentAck(paymentAck).getMemo());

log.info("received {} via bluetooth", ack ? "ack" : "nack");

Expand Down
94 changes: 89 additions & 5 deletions wallet/src/de/schildbach/wallet/ui/InputParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@
package de.schildbach.wallet.ui;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.util.ArrayList;
import java.util.Date;
import java.util.regex.Pattern;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.bitcoin.protocols.payments.Protos;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Base58;
Expand All @@ -34,19 +40,26 @@
import org.bitcoinj.core.ProtocolException;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.VerificationException;
import org.bitcoinj.crypto.TrustStoreLoader;
import org.bitcoinj.protocols.payments.PaymentProtocol;
import org.bitcoinj.protocols.payments.PaymentProtocol.PkiVerificationData;
import org.bitcoinj.protocols.payments.PaymentProtocolException;
import org.bitcoinj.protocols.payments.PaymentProtocolException.PkiVerificationException;
import org.bitcoinj.protocols.payments.PaymentSession;
import org.bitcoinj.uri.BitcoinURI;
import org.bitcoinj.uri.BitcoinURIParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.content.Context;
import android.content.DialogInterface.OnClickListener;

import com.google.common.hash.Hashing;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.UninitializedMessageException;

import de.schildbach.wallet.Constants;
import de.schildbach.wallet.data.PaymentIntent;
import de.schildbach.wallet.util.Io;
import de.schildbach.wallet.util.PaymentProtocol;
import de.schildbach.wallet.util.Qr;
import de.schildbach.wallet_test.R;

Expand Down Expand Up @@ -83,7 +96,7 @@ public void parse()

error(R.string.input_parser_io_error, x.getMessage());
}
catch (final PkiVerificationException x)
catch (final PaymentProtocolException.PkiVerificationException x)
{
log.info("got unverifyable payment request", x);

Expand Down Expand Up @@ -269,7 +282,7 @@ public void parse()

error(R.string.input_parser_io_error, x.getMessage());
}
catch (final PkiVerificationException x)
catch (final PaymentProtocolException.PkiVerificationException x)
{
log.info("got unverifyable payment request", x);

Expand Down Expand Up @@ -326,11 +339,82 @@ protected final void handleDirectTransaction(@Nonnull final Transaction transact

protected final void parseAndHandlePaymentRequest(@Nonnull final byte[] serializedPaymentRequest) throws PaymentProtocolException
{
final PaymentIntent paymentIntent = PaymentProtocol.parsePaymentRequest(serializedPaymentRequest);
final PaymentIntent paymentIntent = parsePaymentRequest(serializedPaymentRequest);

handlePaymentIntent(paymentIntent);
}

public static PaymentIntent parsePaymentRequest(@Nonnull final byte[] serializedPaymentRequest) throws PaymentProtocolException
{
try
{
if (serializedPaymentRequest.length > 50000)
throw new PaymentProtocolException("payment request too big: " + serializedPaymentRequest.length);

final Protos.PaymentRequest paymentRequest = Protos.PaymentRequest.parseFrom(serializedPaymentRequest);

final String pkiName;
final String pkiCaName;
if (!"none".equals(paymentRequest.getPkiType()))
{
final KeyStore keystore = new TrustStoreLoader.DefaultTrustStoreLoader().getKeyStore();
final PkiVerificationData verificationData = PaymentProtocol.verifyPaymentRequestPki(paymentRequest, keystore);
pkiName = verificationData.displayName;
pkiCaName = verificationData.rootAuthorityName;
}
else
{
pkiName = null;
pkiCaName = null;
}

final PaymentSession paymentSession = PaymentProtocol.parsePaymentRequest(paymentRequest);

if (paymentSession.isExpired())
throw new PaymentProtocolException.Expired("payment details expired: current time " + new Date() + " after expiry time "
+ paymentSession.getExpires());

if (!paymentSession.getNetworkParameters().equals(Constants.NETWORK_PARAMETERS))
throw new PaymentProtocolException.InvalidNetwork("cannot handle payment request network: " + paymentSession.getNetworkParameters());

final ArrayList<PaymentIntent.Output> outputs = new ArrayList<PaymentIntent.Output>(1);
for (final PaymentProtocol.Output output : paymentSession.getOutputs())
outputs.add(PaymentIntent.Output.valueOf(output));

final String memo = paymentSession.getMemo();

final String paymentUrl = paymentSession.getPaymentUrl();

final byte[] merchantData = paymentSession.getMerchantData();

final byte[] paymentRequestHash = Hashing.sha256().hashBytes(serializedPaymentRequest).asBytes();

final PaymentIntent paymentIntent = new PaymentIntent(PaymentIntent.Standard.BIP70, pkiName, pkiCaName,
outputs.toArray(new PaymentIntent.Output[0]), memo, paymentUrl, merchantData, null, paymentRequestHash);

if (paymentIntent.hasPaymentUrl() && !paymentIntent.isSupportedPaymentUrl())
throw new PaymentProtocolException.InvalidPaymentURL("cannot handle payment url: " + paymentIntent.paymentUrl);

return paymentIntent;
}
catch (final InvalidProtocolBufferException x)
{
throw new PaymentProtocolException(x);
}
catch (final UninitializedMessageException x)
{
throw new PaymentProtocolException(x);
}
catch (final FileNotFoundException x)
{
throw new RuntimeException(x);
}
catch (final KeyStoreException x)
{
throw new RuntimeException(x);
}
}

protected abstract void handlePaymentIntent(@Nonnull PaymentIntent paymentIntent);

protected void handlePrivateKey(@Nonnull final ECKey key)
Expand Down
6 changes: 3 additions & 3 deletions wallet/src/de/schildbach/wallet/ui/RequestCoinsFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Wallet;
import org.bitcoinj.protocols.payments.PaymentProtocol;
import org.bitcoinj.uri.BitcoinURI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -67,7 +68,6 @@
import de.schildbach.wallet.util.BitmapFragment;
import de.schildbach.wallet.util.Bluetooth;
import de.schildbach.wallet.util.Nfc;
import de.schildbach.wallet.util.PaymentProtocol;
import de.schildbach.wallet.util.Qr;
import de.schildbach.wallet_test.R;

Expand Down Expand Up @@ -419,8 +419,8 @@ private byte[] determinePaymentRequest(final boolean includeBluetoothMac)
{
final Address address = application.determineSelectedAddress();
final Coin amount = amountCalculatorLink.getAmount();
final String paymentUrl = includeBluetoothMac && bluetoothMac != null ? "bt:" + bluetoothMac : null;

return PaymentProtocol.createPaymentRequest(amount, address, null, includeBluetoothMac && bluetoothMac != null ? "bt:" + bluetoothMac : null)
.toByteArray();
return PaymentProtocol.createPaymentRequest(Constants.NETWORK_PARAMETERS, amount, address, null, paymentUrl, null).build().toByteArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.bitcoinj.protocols.payments.PaymentProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -43,7 +44,6 @@
import de.schildbach.wallet.data.PaymentIntent;
import de.schildbach.wallet.ui.InputParser;
import de.schildbach.wallet.util.Bluetooth;
import de.schildbach.wallet.util.PaymentProtocol;
import de.schildbach.wallet_test.R;

/**
Expand Down
Loading

0 comments on commit 5bdf226

Please sign in to comment.