Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# configuration for https://travis-ci.org/bitcoincash-wallet/bitcoincashj
sudo: false
sudo: sudo
dist: trusty
language: java
jdk: oraclejdk8
jdk: openjdk8
addons:
postgresql: "9.3" # min supported version
cache:
Expand All @@ -14,6 +14,14 @@ services:

install: true # disable default because no need to do the mvn install before mvn verify

before_install:
- wget https://oss.sonatype.org/content/repositories/releases/org/javafxports/dalvik-sdk/8.60.9/dalvik-sdk-8.60.9.zip && unzip dalvik-sdk-8.60.9.zip
- sudo cp dalvik-sdk/rt/lib/ext/jfxrt.jar $JAVA_HOME/jre/lib/ext
- sudo cp dalvik-sdk/rt/lib/jfxswt.jar $JAVA_HOME/jre/lib/
- sudo cp dalvik-sdk/rt/lib/javafx.properties $JAVA_HOME/jre/lib
- sudo cp dalvik-sdk/rt/lib/javafx.platform.properties $JAVA_HOME/jre/lib
- sudo cp dalvik-sdk/lib/javafx-mx.jar $JAVA_HOME/lib

before_script:
- psql -c "create user bitcoinj with password 'password';" -U postgres
- psql -c 'create database bitcoinj_test owner bitcoinj;' -U postgres
Expand Down
22 changes: 21 additions & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,26 @@
<version>1.8</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>com.madgag.spongycastle</groupId>
<artifactId>prov</artifactId>
<version>1.51.0.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>

<!-- test dependencies -->
<dependency>
Expand Down Expand Up @@ -487,4 +507,4 @@
<scope>test</scope>
</dependency>
</dependencies>
</project>
</project>
2 changes: 1 addition & 1 deletion core/src/main/java/org/bitcoinj/core/BlockChain.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader)
}

@Override
protected void rollbackBlockStore(int height) throws BlockStoreException {
public void rollbackBlockStore(int height) throws BlockStoreException {
lock.lock();
try {
int currentHeight = getBestChainHeight();
Expand Down
98 changes: 98 additions & 0 deletions core/src/main/java/org/bitcoinj/core/bip47/BIP47Account.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* Copyright (c) 2017 Stash
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.bitcoinj.core.bip47;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDKeyDerivation;

import static org.bitcoinj.core.bip47.BIP47PaymentCode.createMasterPubKeyFromPaymentCode;

/**
* Created by jimmy on 8/4/17.
*/

/**
* p>A {@link BIP47Account} is necessary for BIP47 payment channels. It holds the notification key used to derive the
* notification address and the key to derive payment addresses in a channel.</p>
*
* <p>The BIP47 account is at the derivation path <pre>m / 47' / coin_type' / account_id'.</pre>. </p>
*
* <p>Properties:</p>
* <ul>
* <li>The coin_type' should be chosen as in BIP43. </li>
* <li>The account_id is any integer (from 0 to 2147483647)</li>
* <li>The notification key is derived at: <pre>m / 47' / coin_type' / account_id' / 0 </pre> (non hardened)</li>
* <li>The payment keys are derived at: <pre>m / 47' / coin_type' / account_id' / idx' </pre> (hardened). </li>
* </ul>
*/
public class BIP47Account {
private NetworkParameters mNetworkParameters;
private DeterministicKey mKey;
private int mIndex;
private BIP47PaymentCode mBIP47PaymentCode;
private String mXPub;

/**
* Constructor expecting a coin_type' derivation path key and the identity number.
*/
public BIP47Account(NetworkParameters parameters, DeterministicKey deterministicKey, int index) {
mNetworkParameters = parameters;
mIndex = index;
mKey = HDKeyDerivation.deriveChildKey(deterministicKey, mIndex | ChildNumber.HARDENED_BIT);
mBIP47PaymentCode = new BIP47PaymentCode(mKey.getPubKey(), mKey.getChainCode());
mXPub = mKey.serializePubB58(parameters);
}

/**
* Constructor expecting a Base58Check encoded payment code.
* @throws AddressFormatException if the payment code is invalid
*/
public BIP47Account(NetworkParameters parameters, String strPaymentCode) {
mNetworkParameters = parameters;
mIndex = 0;
mKey = createMasterPubKeyFromPaymentCode(strPaymentCode);
mBIP47PaymentCode = new BIP47PaymentCode(strPaymentCode);
mXPub = mKey.serializePubB58(parameters);
}

/** Return the Base58Check representation of the payment code*/
public String getStringPaymentCode() {
return mBIP47PaymentCode.toString();
}

public String getXPub() {
return mXPub;
}

/** Returns the P2PKH address associated with the 0th public key */
public Address getNotificationAddress() {
return HDKeyDerivation.deriveChildKey(mKey, ChildNumber.ZERO).toAddress(mNetworkParameters);
}

/** Returns the 0th derivation key */
public ECKey getNotificationKey() {
return HDKeyDerivation.deriveChildKey(mKey, ChildNumber.ZERO);
}

/** Return the payment code as is */
public BIP47PaymentCode getPaymentCode() {
return mBIP47PaymentCode;
}

/** Returns the nth key.
* @param idx must be between 0 and 2147483647
*/
public ECKey keyAt(int idx) {
return HDKeyDerivation.deriveChildKey(mKey, new ChildNumber(idx, false));
}

public byte[] getPrivKey(int index) {
return HDKeyDerivation.deriveChildKey(mKey, index).getPrivKeyBytes();
}
}
50 changes: 50 additions & 0 deletions core/src/main/java/org/bitcoinj/core/bip47/BIP47Address.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* Copyright (c) 2017 Stash
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.bitcoinj.core.bip47;

/**
* Created by jimmy on 9/29/17.
*/

public class BIP47Address {

private String address;
private int index = 0;
private boolean seen = false;

public BIP47Address() {}

public BIP47Address(String address, int index) {
this.address = address;
this.index = index;
}

public BIP47Address(String address, int index, boolean seen) {
this(address, index);
this.seen = seen;
}

public String getAddress() {
return address;
}

public int getIndex() {
return index;
}

public boolean isSeen() {
return seen;
}

public void setSeen(boolean seen) {
this.seen = seen;
}

@Override
public String toString() {
return address;
}
}
140 changes: 140 additions & 0 deletions core/src/main/java/org/bitcoinj/core/bip47/BIP47Channel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/* Copyright (c) 2017 Stash
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.bitcoinj.core.bip47;

import org.bitcoinj.core.Sha256Hash;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.kits.BIP47AppKit;
import org.bitcoinj.wallet.bip47.NotSecp256k1Exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;

import static org.bitcoinj.utils.BIP47Util.getReceiveAddress;

public class BIP47Channel {
private static final String TAG = "BIP47Channel";

private static final int STATUS_NOT_SENT = -1;
private static final int STATUS_SENT_CFM = 1;

private static final int LOOKAHEAD = 10;

private String paymentCode;
private String label = "";
private List<BIP47Address> incomingAddresses = new ArrayList<BIP47Address>();
private List<String> outgoingAddresses = new ArrayList<String>();
private int status = STATUS_NOT_SENT;
private int currentOutgoingIndex = 0;
private int currentIncomingIndex = -1;
private Sha256Hash ntxHash;

private static final Logger log = LoggerFactory.getLogger(BIP47Channel.class);
public BIP47Channel() {}

public BIP47Channel(String paymentCode) {
this.paymentCode = paymentCode;
}

public BIP47Channel(String paymentCode, String label) {
this(paymentCode);
this.label = label;
}

public String getPaymentCode() {
return paymentCode;
}

public void setPaymentCode(String pc) {
paymentCode = pc;
}

public List<BIP47Address> getIncomingAddresses() {
return incomingAddresses;
}

public int getCurrentIncomingIndex() {
return currentIncomingIndex;
}

public void generateKeys(BIP47AppKit wallet) throws NotSecp256k1Exception, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
for (int i = 0; i < LOOKAHEAD; i++) {
ECKey key = getReceiveAddress(wallet, paymentCode, i).getReceiveECKey();
Address address = wallet.getAddressOfKey(key);

log.debug("New address generated");
log.debug(address.toString());
wallet.importKey(key);
incomingAddresses.add(i, new BIP47Address(address.toString(), i));
}

currentIncomingIndex = LOOKAHEAD - 1;
}

public BIP47Address getIncomingAddress(String address) {
for (BIP47Address bip47Address: incomingAddresses) {
if (bip47Address.getAddress().equals(address)) {
return bip47Address;
}
}
return null;
}

public void addNewIncomingAddress(String newAddress, int nextIndex) {
incomingAddresses.add(nextIndex, new BIP47Address(newAddress, nextIndex));
currentIncomingIndex = nextIndex;
}

public String getLabel() {
return label;
}

public void setLabel(String l) {
label = l;
}

public List<String> getOutgoingAddresses() {
return outgoingAddresses;
}

public boolean isNotificationTransactionSent() {
return status == STATUS_SENT_CFM;
}

public void setStatusSent() {
status = STATUS_SENT_CFM;
}

public int getCurrentOutgoingIndex() {
return currentOutgoingIndex;
}

public void incrementOutgoingIndex() {
currentOutgoingIndex++;
}

public void addAddressToOutgoingAddresses(String address) {
outgoingAddresses.add(address);
}

public void setStatusNotSent() {
status = STATUS_NOT_SENT;
}

public Sha256Hash getNtxHash() { return ntxHash; }

public void setNtxHash(Sha256Hash ntxHash) {
this.ntxHash = ntxHash;
}
}
Loading