Skip to content

Commit

Permalink
BIP49 derivation scheme changes: ypub, yprv, upub, uprv
Browse files Browse the repository at this point in the history
  • Loading branch information
SamouraiDev committed Oct 27, 2017
1 parent 2ac4785 commit 52f03cf
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 29 deletions.
22 changes: 18 additions & 4 deletions core/src/main/java/org/bitcoinj/core/NetworkParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ public abstract class NetworkParameters {
protected byte[] alertSigningKey;
protected int bip32HeaderPub;
protected int bip32HeaderPriv;
protected int bip49HeaderPub;
protected int bip49HeaderPriv;
protected int bip141HeaderPub;
protected int bip141HeaderPriv;

/** Used to check majorities for block version upgrade */
protected int majorityEnforceBlockUpgrade;
Expand All @@ -107,7 +111,7 @@ public abstract class NetworkParameters {
*/
protected int spendableCoinbaseDepth;
protected int subsidyDecreaseBlockCount;

protected int[] acceptableAddressCodes;
protected String[] dnsSeeds;
protected int[] addrSeeds;
Expand Down Expand Up @@ -146,7 +150,7 @@ private static Block createGenesis(NetworkParameters n) {
public static final int TARGET_TIMESPAN = 14 * 24 * 60 * 60; // 2 weeks per difficulty cycle, on average.
public static final int TARGET_SPACING = 10 * 60; // 10 minutes per block.
public static final int INTERVAL = TARGET_TIMESPAN / TARGET_SPACING;

/**
* Blocks with a timestamp after this should enforce BIP 16, aka "Pay to script hash". This BIP changed the
* network rules in a soft-forking manner, that is, blocks that don't follow the rules are accepted but not
Expand Down Expand Up @@ -406,6 +410,16 @@ public int getBip32HeaderPriv() {
return bip32HeaderPriv;
}

/** Returns the 4 byte header for BIP49 (HD) wallet - public key part. */
public int getBip49HeaderPub() {
return bip49HeaderPub;
}

/** Returns the 4 byte header for BIP49 (HD) wallet - private key part. */
public int getBip49HeaderPriv() {
return bip49HeaderPriv;
}

/**
* Returns the number of coins that will be produced in total, on this
* network. Where not applicable, a very large number of coins is returned
Expand Down Expand Up @@ -438,7 +452,7 @@ public int getBip32HeaderPriv() {

/**
* Return the default serializer for this network. This is a shared serializer.
* @return
* @return
*/
public final MessageSerializer getDefaultSerializer() {
// Construct a default serializer if we don't have one
Expand Down Expand Up @@ -493,7 +507,7 @@ public int getMajorityWindow() {
* The flags indicating which block validation tests should be applied to
* the given block. Enables support for alternative blockchains which enable
* tests based on different criteria.
*
*
* @param block block to determine flags for.
* @param height height of the block, if known, null otherwise. Returned
* tests should be a safe subset if block height is unknown.
Expand Down
35 changes: 24 additions & 11 deletions core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public DeterministicKey(ImmutableList<ChildNumber> childNumberPath,
this.parentFingerprint = ascertainParentFingerprint(parent, parentFingerprint);
}


/** Clones the key */
public DeterministicKey(DeterministicKey keyToClone, DeterministicKey newParent) {
super(keyToClone.priv, keyToClone.pub.get());
Expand Down Expand Up @@ -461,17 +461,22 @@ public BigInteger getPrivKey() {
return key;
}

public byte[] serializePublic(NetworkParameters params) {
return serialize(params, true);
public byte[] serializePublic(NetworkParameters params, boolean bip49) {
return serialize(params, true, bip49);
}

public byte[] serializePrivate(NetworkParameters params) {
return serialize(params, false);
public byte[] serializePrivate(NetworkParameters params, boolean bip49) {
return serialize(params, false, bip49);
}

private byte[] serialize(NetworkParameters params, boolean pub) {
private byte[] serialize(NetworkParameters params, boolean pub, boolean bip49) {
ByteBuffer ser = ByteBuffer.allocate(78);
ser.putInt(pub ? params.getBip32HeaderPub() : params.getBip32HeaderPriv());
if(bip49) {
ser.putInt(pub ? params.getBip49HeaderPub() : params.getBip49HeaderPriv());
}
else {
ser.putInt(pub ? params.getBip32HeaderPub() : params.getBip32HeaderPriv());
}
ser.put((byte) getDepth());
ser.putInt(getParentFingerprint());
ser.putInt(getChildNumber().i());
Expand All @@ -482,11 +487,19 @@ private byte[] serialize(NetworkParameters params, boolean pub) {
}

public String serializePubB58(NetworkParameters params) {
return toBase58(serialize(params, true));
return toBase58(serialize(params, true, false));
}

public String serializePrivB58(NetworkParameters params) {
return toBase58(serialize(params, false));
return toBase58(serialize(params, false, false));
}

public String serializePubB58(NetworkParameters params, boolean bip49) {
return toBase58(serialize(params, true, bip49));
}

public String serializePrivB58(NetworkParameters params, boolean bip49) {
return toBase58(serialize(params, false, bip49));
}

static String toBase58(byte[] ser) {
Expand Down Expand Up @@ -521,9 +534,9 @@ public static DeterministicKey deserialize(NetworkParameters params, byte[] seri
public static DeterministicKey deserialize(NetworkParameters params, byte[] serializedKey, @Nullable DeterministicKey parent) {
ByteBuffer buffer = ByteBuffer.wrap(serializedKey);
int header = buffer.getInt();
if (header != params.getBip32HeaderPriv() && header != params.getBip32HeaderPub())
if (header != params.getBip32HeaderPriv() && header != params.getBip32HeaderPub() && header != params.getBip49HeaderPriv() && header != params.getBip49HeaderPub())
throw new IllegalArgumentException("Unknown header bytes: " + toBase58(serializedKey).substring(0, 4));
boolean pub = header == params.getBip32HeaderPub();
boolean pub = (header == params.getBip32HeaderPub() || header == params.getBip49HeaderPub());
int depth = buffer.get() & 0xFF; // convert signed byte to positive int since depth cannot be negative
final int parentFingerprint = buffer.getInt();
final int i = buffer.getInt();
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/org/bitcoinj/params/MainNetParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ public MainNetParams() {
packetMagic = 0xf9beb4d9L;
bip32HeaderPub = 0x0488B21E; //The 4 byte header that serializes in base58 to "xpub".
bip32HeaderPriv = 0x0488ADE4; //The 4 byte header that serializes in base58 to "xprv"
bip49HeaderPub = 0x049D7CB2; //The 4 byte header that serializes in base58 to "ypub".
bip49HeaderPriv = 0x049D7878; //The 4 byte header that serializes in base58 to "yprv"
bip141HeaderPub = 0x04B24746; //The 4 byte header that serializes in base58 to "zpub".
bip141HeaderPriv = 0x04B2430C; //The 4 byte header that serializes in base58 to "zprv"

majorityEnforceBlockUpgrade = MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE;
majorityRejectBlockOutdated = MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED;
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/org/bitcoinj/params/TestNet3Params.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ public TestNet3Params() {
addrSeeds = null;
bip32HeaderPub = 0x043587CF;
bip32HeaderPriv = 0x04358394;
bip49HeaderPub = 0x044A5262;
bip49HeaderPriv = 0x044A4E28;
bip141HeaderPub = 0x045F1CF6;
bip141HeaderPriv = 0x045F18BC;

majorityEnforceBlockUpgrade = TestNet2Params.TESTNET_MAJORITY_ENFORCE_BLOCK_UPGRADE;
majorityRejectBlockOutdated = TestNet2Params.TESTNET_MAJORITY_REJECT_BLOCK_OUTDATED;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* 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
Expand Down Expand Up @@ -92,7 +92,7 @@
* keys, you can request 33 keys before more keys will be calculated and the Bloom filter rebuilt and rebroadcast.
* But even when you are requesting the 33rd key, you will still be looking 100 keys ahead.
* </p>
*
*
* @author Andreas Schildbach
*/
@SuppressWarnings("PublicStaticCollectionField")
Expand Down Expand Up @@ -1328,7 +1328,8 @@ public String toString(boolean includePrivateKeys, NetworkParameters params) {
builder.append("Key birthday: ").append(watchingKey.getCreationTimeSeconds()).append(" [")
.append(Utils.dateTimeFormat(watchingKey.getCreationTimeSeconds() * 1000)).append("]\n");
}
builder.append("Key to watch: ").append(watchingKey.serializePubB58(params)).append('\n');
builder.append("Key to watch: ").append(watchingKey.serializePubB58(params, false)).append('\n');
builder.append("Key to watch: ").append(watchingKey.serializePubB58(params, true)).append('\n');
formatAddresses(includePrivateKeys, params, builder);
return builder.toString();
}
Expand Down
11 changes: 7 additions & 4 deletions core/src/main/java/org/bitcoinj/wallet/MarriedKeyChain.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* 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
Expand Down Expand Up @@ -235,9 +235,12 @@ public List<Protos.Key> serializeToProtobuf() {

@Override
protected void formatAddresses(boolean includePrivateKeys, NetworkParameters params, StringBuilder builder2) {
for (DeterministicKeyChain followingChain : followingKeyChains)
builder2.append("Following chain: ").append(followingChain.getWatchingKey().serializePubB58(params))
.append('\n');
for (DeterministicKeyChain followingChain : followingKeyChains) {
builder2.append("Following chain: ").append(followingChain.getWatchingKey().serializePubB58(params, false))
.append('\n');
builder2.append("Following chain: ").append(followingChain.getWatchingKey().serializePubB58(params, true))
.append('\n');
}
builder2.append('\n');
for (RedeemData redeemData : marriedKeysRedeemData.values())
formatScript(ScriptBuilder.createP2SHOutputScript(redeemData.redeemScript), builder2, params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ public void serializeToTextAndBytes() {
{
final String pub58 = key1.serializePubB58(params);
final String priv58 = key1.serializePrivB58(params);
final byte[] pub = key1.serializePublic(params);
final byte[] priv = key1.serializePrivate(params);
final byte[] pub = key1.serializePublic(params, false);
final byte[] priv = key1.serializePrivate(params, false);
assertEquals("xpub661MyMwAqRbcF7mq7Aejj5xZNzFfgi3ABamE9FedDHVmViSzSxYTgAQGcATDo2J821q7Y9EAagjg5EP3L7uBZk11PxZU3hikL59dexfLkz3", pub58);
assertEquals("xprv9s21ZrQH143K2dhN197jMx1ppxRBHFKJpMqdLsF1ewxncv7quRED8N5nksxphju3W7naj1arF56L5PUEWfuSk8h73Sb2uh7bSwyXNrjzhAZ", priv58);
assertArrayEquals(new byte[]{4, -120, -78, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, -68, 93, -104, -97, 31, -105, -18, 109, 112, 104, 45, -77, -77, 18, 85, -29, -120, 86, -113, 26, 48, -18, -79, -110, -6, -27, 87, 86, 24, 124, 99, 3, 96, -33, -14, 67, -19, -47, 16, 76, -49, -11, -30, -123, 7, 56, 101, 91, 74, 125, -127, 61, 42, -103, 90, -93, 66, -36, 2, -126, -107, 30, 24, -111}, pub);
Expand All @@ -230,8 +230,8 @@ public void serializeToTextAndBytes() {
{
final String pub58 = key2.serializePubB58(params);
final String priv58 = key2.serializePrivB58(params);
final byte[] pub = key2.serializePublic(params);
final byte[] priv = key2.serializePrivate(params);
final byte[] pub = key2.serializePublic(params, false);
final byte[] priv = key2.serializePrivate(params, false);
assertEquals(DeterministicKey.deserializeB58(key1, priv58, params), key2);
assertEquals(DeterministicKey.deserializeB58(key1, pub58, params).getPubKeyPoint(), key2.getPubKeyPoint());
assertEquals(DeterministicKey.deserialize(params, priv, key1), key2);
Expand All @@ -247,9 +247,9 @@ public void parentlessDeserialization() {
DeterministicKey key3 = HDKeyDerivation.deriveChildKey(key2, ChildNumber.ZERO_HARDENED);
DeterministicKey key4 = HDKeyDerivation.deriveChildKey(key3, ChildNumber.ZERO_HARDENED);
assertEquals(key4.getPath().size(), 3);
assertEquals(DeterministicKey.deserialize(params, key4.serializePrivate(params), key3).getPath().size(), 3);
assertEquals(DeterministicKey.deserialize(params, key4.serializePrivate(params), null).getPath().size(), 1);
assertEquals(DeterministicKey.deserialize(params, key4.serializePrivate(params)).getPath().size(), 1);
assertEquals(DeterministicKey.deserialize(params, key4.serializePrivate(params, false), key3).getPath().size(), 3);
assertEquals(DeterministicKey.deserialize(params, key4.serializePrivate(params, false), null).getPath().size(), 1);
assertEquals(DeterministicKey.deserialize(params, key4.serializePrivate(params, false)).getPath().size(), 1);
}

/** Reserializing a deserialized key should yield the original input */
Expand Down

1 comment on commit 52f03cf

@SamouraiDev
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.