Skip to content
This repository has been archived by the owner on May 28, 2019. It is now read-only.

Conform Bitcoin to BIP49 #53

Merged
merged 6 commits into from
Sep 12, 2018
Merged
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
16 changes: 16 additions & 0 deletions Sources/Bitcoin/Bitcoin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ open class Bitcoin: Blockchain {
return .bitcoin
}

override open var coinPurpose: Purpose {
return .bip49
}

/// Public key hash address prefix.
open var publicKeyHashAddressPrefix: UInt8 {
return 0x00
Expand All @@ -39,6 +43,18 @@ open class Bitcoin: Blockchain {
open override func address(data: Data) -> Address? {
return BitcoinAddress(data: data)
}

open func legacyAddress(for publicKey: PublicKey, prefix: UInt8) -> Address {
return publicKey.legacyBitcoinAddress(prefix: prefix)
}

open func legacyAddress(string: String) -> Address? {
return BitcoinAddress(string: string)
}

open func legacyAddress(data: Data) -> Address? {
return BitcoinAddress(data: data)
}
}

public final class Litecoin: Bitcoin {
Expand Down
11 changes: 9 additions & 2 deletions Sources/Bitcoin/PublicKey+Bitcoin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@ import Foundation

public extension PublicKey {
/// Returns the public key address with the given prefix.
public func bitcoinAddress(prefix: UInt8) -> BitcoinAddress {
let hash = Data([prefix]) + Crypto.sha256ripemd160(data)
public func legacyBitcoinAddress(prefix: UInt8) -> BitcoinAddress {
let hash = Data([prefix]) + bitcoinKeyHash
return BitcoinAddress(data: hash)!
}

public func bitcoinAddress(prefix: UInt8) -> BitcoinAddress {
let witnessVersion = Data(bytes: [0x00, 0x14])
let redeemScript = Crypto.sha256ripemd160(witnessVersion + bitcoinKeyHash)
let address = Crypto.base58Encode([prefix] + redeemScript)
return BitcoinAddress(string: address)!
}

/// Returns the public key hash.
public var bitcoinKeyHash: Data {
return Crypto.sha256ripemd160(data)
Expand Down
6 changes: 5 additions & 1 deletion Sources/Blockchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ open class Blockchain: Hashable {
fatalError("Use a specific Blockchain subclass")
}

open var coinPurpose: Purpose {
return .bip44
}

public init() {}

/// Returns the address associated with a public key.
Expand Down Expand Up @@ -45,6 +49,6 @@ open class Blockchain: Hashable {

public extension Blockchain {
func derivationPath(at index: Int) -> DerivationPath {
return DerivationPath(purpose: 44, coinType: self.coinType.rawValue, account: 0, change: 0, address: index)
return DerivationPath(purpose: coinPurpose.rawValue, coinType: coinType.rawValue, account: 0, change: 0, address: index)
}
}
4 changes: 4 additions & 0 deletions Sources/Ethereum/Ethereum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ open class Ethereum: Blockchain {
return .ethereum
}

open override var coinPurpose: Purpose {
return .bip44
}

open override func address(for publicKey: PublicKey) -> Address {
return publicKey.ethereumAddress
}
Expand Down
14 changes: 14 additions & 0 deletions Sources/Purpose.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright © 2017-2018 Trust.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

import Foundation

// See https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki

public enum Purpose: Int {
case bip44 = 44
case bip49 = 49
}
17 changes: 12 additions & 5 deletions Tests/Bitcoin/BitcoinAddressTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@ class BitcoinAddressTests: XCTestCase {
}

func testFromPrivateKey() {
let data = Crypto.base58Decode("5K6EwEiKWKNnWGYwbNtrXjA8KKNntvxNKvepNqNeeLpfW7FSG1v")!
let privateKey = PrivateKey(data: data.dropFirst())!
let privateKey = PrivateKey(wif: "L5XECLxq1MDvBeYXjZwz5tTYsFZRWmaYziY3Wvc2bqSRAuRcBqhg")!
let publicKey = privateKey.publicKey(compressed: true)
let address = Bitcoin().address(for: publicKey)

XCTAssertEqual(address.description, "3EpNJiTASbZ6DeNA7QZ7bPEz82Y42W8Rd7")
XCTAssertEqual(address.description, "3Hv6oV8BYCoocW4eqZaEXsaR5tHhCxiMSk")
}

func testFromPrivateKeySegwitAddress() {
let privateKey = PrivateKey(wif: "KxZX6Jv3to6RWnhsffTcLLryRnNyyc8Ng2G8P9LFkbCdzGDEhNy1")!
let publicKey = privateKey.publicKey(compressed: true)
let address = Bitcoin().legacyAddress(for: publicKey, prefix: 0x0)

XCTAssertEqual(address.description, "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1")
}

func testIsValid() {
Expand All @@ -41,13 +48,13 @@ class BitcoinAddressTests: XCTestCase {
// compressed public key starting with 0x03 (greater than midpoint of curve)
let compressedPK = PublicKey(data: Data(hexString: "030589ee559348bd6a7325994f9c8eff12bd5d73cc683142bd0dd1a17abc99b0dc")!)!
XCTAssertTrue(compressedPK.isCompressed)
XCTAssertEqual(compressedPK.bitcoinAddress(prefix: 0).description, "1KbUJ4x8epz6QqxkmZbTc4f79JbWWz6g37")
XCTAssertEqual(compressedPK.legacyBitcoinAddress(prefix: 0).description, "1KbUJ4x8epz6QqxkmZbTc4f79JbWWz6g37")
}

func testUncompressedPublicKey() {
// uncompressed public key, starting with 0x04. Contains both X and Y encoded
let uncompressed = PublicKey(data: Data(hexString: "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8")!)!
XCTAssertFalse(uncompressed.isCompressed)
XCTAssertEqual(uncompressed.bitcoinAddress(prefix: 0).description, "1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm")
XCTAssertEqual(uncompressed.legacyBitcoinAddress(prefix: 0).description, "1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm")
}
}
9 changes: 9 additions & 0 deletions Tests/HDWalletTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ class HDWalletTests: XCTestCase {
XCTAssertEqual(key1.publicKey().ethereumAddress.description, "0x98f5438cDE3F0Ff6E11aE47236e93481899d1C47")
}

func testDeriveBitcoin() {
let wallet = HDWallet(mnemonic: words, passphrase: passphrase)
let key = wallet.getKey(at: Bitcoin().derivationPath(at: 0))
let publicKey = key.publicKey(compressed: true)

XCTAssertEqual("026fc80db3a34e7c6bfc6d7d9a53aeba9e706b309c9cf7ee96fa9c36ff7fd92a20", publicKey.description)
XCTAssertEqual("3FJjnZNXC6FWQ2UJAaKL3Vme2EJavfgnXe", publicKey.bitcoinAddress(prefix: Bitcoin().payToScriptHashAddressPrefix).description)
}

func testSignHash() {
let wallet = HDWallet(mnemonic: words, passphrase: passphrase)
let key = wallet.getKey(at: Ethereum().derivationPath(at: 0))
Expand Down
21 changes: 21 additions & 0 deletions Tests/PurposeTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright © 2017-2018 Trust.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

import XCTest
import TrustCore

class PurposeTests: XCTestCase {

func testPurpose() {
XCTAssertEqual(.bip49, Bitcoin().coinPurpose)
XCTAssertEqual(.bip49, Litecoin().coinPurpose)
XCTAssertEqual(.bip49, Dash().coinPurpose)
XCTAssertEqual(.bip44, Ethereum().coinPurpose)
XCTAssertEqual(.bip44, Wanchain().coinPurpose)
XCTAssertEqual(.bip44, Vechain().coinPurpose)
XCTAssertEqual(.bip44, Go().coinPurpose)
}
}
8 changes: 8 additions & 0 deletions TrustCore.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
77F80A892134A5220011D949 /* VechainTransactionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F80A882134A5220011D949 /* VechainTransactionTests.swift */; };
77F80A9421363D5C0011D949 /* CryptoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F80A9321363D5C0011D949 /* CryptoTests.swift */; };
77F8D3B02141CBB900E80CD6 /* DashTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F8D3AF2141CBB900E80CD6 /* DashTests.swift */; };
77F8D3DB214860D200E80CD6 /* Purpose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F8D3DA214860D200E80CD6 /* Purpose.swift */; };
77F8D3DF2148625100E80CD6 /* PurposeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F8D3DE2148625100E80CD6 /* PurposeTests.swift */; };
887FD54919AD2FA39E88EAF0 /* Pods_TrustCore_TrustCoreTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C9DA59AE410E04E68E1F89 /* Pods_TrustCore_TrustCoreTests.framework */; };
BB06149A920620338B3323D1 /* Pods_TrustCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 682B6374F9856982618A8501 /* Pods_TrustCore.framework */; };
BB1215D021390A4D0092FC68 /* ERC721EncoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1215CF21390A4D0092FC68 /* ERC721EncoderTests.swift */; };
Expand Down Expand Up @@ -172,6 +174,8 @@
77F80A882134A5220011D949 /* VechainTransactionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VechainTransactionTests.swift; sourceTree = "<group>"; };
77F80A9321363D5C0011D949 /* CryptoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoTests.swift; sourceTree = "<group>"; };
77F8D3AF2141CBB900E80CD6 /* DashTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashTests.swift; sourceTree = "<group>"; };
77F8D3DA214860D200E80CD6 /* Purpose.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Purpose.swift; sourceTree = "<group>"; };
77F8D3DE2148625100E80CD6 /* PurposeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurposeTests.swift; sourceTree = "<group>"; };
99258028638D3902B177E80C /* Pods-TrustCore.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustCore.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TrustCore/Pods-TrustCore.debug.xcconfig"; sourceTree = "<group>"; };
A1C9DA59AE410E04E68E1F89 /* Pods_TrustCore_TrustCoreTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TrustCore_TrustCoreTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BB1215CF21390A4D0092FC68 /* ERC721EncoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ERC721EncoderTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -252,6 +256,7 @@
615AE1D720F1D66000A5A0ED /* PublicKey.swift */,
61A728D220F46ECF0005045D /* String+Clear.swift */,
7749DFEE2141105800411ED8 /* Slip.swift */,
77F8D3DA214860D200E80CD6 /* Purpose.swift */,
615AE1D120F1D1BA00A5A0ED /* Bitcoin */,
615AE1D420F1D56C00A5A0ED /* Ethereum */,
61A728E620F487F50005045D /* HDWallet */,
Expand Down Expand Up @@ -286,6 +291,7 @@
77F80A872134A50A0011D949 /* VeChain */,
7749DFEC21410F1B00411ED8 /* SlipTests.swift */,
77F8D3AF2141CBB900E80CD6 /* DashTests.swift */,
77F8D3DE2148625100E80CD6 /* PurposeTests.swift */,
);
path = Tests;
sourceTree = "<group>";
Expand Down Expand Up @@ -689,6 +695,7 @@
61D0BA292075D771004F71E1 /* ABIEncoder.swift in Sources */,
612B85D4212BC35100F5BCF5 /* BitcoinTransaction+Signing.swift in Sources */,
BB3864C3209569CE00CBF773 /* ReverseResolverEncoder.swift in Sources */,
77F8D3DB214860D200E80CD6 /* Purpose.swift in Sources */,
6167556C207ABBEF00018DC8 /* ABIType.swift in Sources */,
77044FAF212F9F8E00CC20D1 /* WanchainSigner.swift in Sources */,
61675588207C745700018DC8 /* ERC20Encoder.swift in Sources */,
Expand Down Expand Up @@ -716,6 +723,7 @@
buildActionMask = 2147483647;
files = (
77044FBE2131378F00CC20D1 /* TronTests.swift in Sources */,
77F8D3DF2148625100E80CD6 /* PurposeTests.swift in Sources */,
BB3864C720956F8B00CBF773 /* ReverseResolverEncoderTests.swift in Sources */,
61555865208ED04F00FB241E /* TransactionSigningTests.swift in Sources */,
6149A6D0201E4B6B00A2F35C /* EthereumAddressTests.swift in Sources */,
Expand Down