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

Commit

Permalink
Conform Bitcoin to BIP49 (#53)
Browse files Browse the repository at this point in the history
* Conform Bitcoin to BIP49

* Test derive Bitcoin

* Using sewgit native address

* Use bitcoinKeyHash

* Support old and new addresses

* Init with bytes
  • Loading branch information
vikmeup committed Sep 12, 2018
1 parent 71b241b commit 87d446d
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 8 deletions.
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

0 comments on commit 87d446d

Please sign in to comment.