├── .gitignore ├── BXFunctions.md ├── Bitcoin.h ├── Bitcoin.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── Bitcoin.xcscheme ├── Bitcoin.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ ├── IDEWorkspaceChecks.plist │ ├── WorkspaceSettings.xcsettings │ └── swiftpm │ └── Package.resolved ├── BitcoinDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── BitcoinDemo.xcscheme ├── Docs ├── Derivations.pdf └── Derivations.xlogic ├── Info.plist ├── LICENSE.md ├── README.md ├── Sources ├── Bitcoin │ ├── .gitkeep │ ├── BitcoinError.swift │ ├── Chain │ │ ├── Fee.swift │ │ ├── Input.swift │ │ ├── Output.swift │ │ ├── OutputPoint.swift │ │ ├── RuleFork.swift │ │ ├── Script.swift │ │ └── Transaction.swift │ ├── Constants.swift │ ├── Formats │ │ ├── Base10.swift │ │ ├── Base16.swift │ │ ├── Base32.swift │ │ ├── Base58.swift │ │ ├── Base64.swift │ │ ├── Base85.swift │ │ ├── Bech32.swift │ │ ├── BitcoinHash.swift │ │ ├── SegwitAddress.swift │ │ └── TxRef.swift │ ├── Machine │ │ ├── ScriptOpcode.swift │ │ ├── ScriptOperation.swift │ │ ├── ScriptPattern.swift │ │ ├── ScriptVersion.swift │ │ └── SigHashAlgorithm.swift │ ├── Math │ │ ├── DERSignature.swift │ │ ├── ECSignature.swift │ │ ├── Endorsement.swift │ │ ├── Hash.swift │ │ └── Signing.swift │ ├── SSS │ │ ├── Crypto.swift │ │ ├── SSS.swift │ │ ├── SSSKeyShare.swift │ │ ├── SSSMessage.swift │ │ ├── SSSShare.swift │ │ └── SplitRecoveryPhrase.swift │ ├── Seed.swift │ ├── Util.swift │ └── Wallet │ │ ├── Asset.swift │ │ ├── BIP32Path.swift │ │ ├── ChainType.swift │ │ ├── CoinType.swift │ │ ├── Derivations.swift │ │ ├── ECKey.swift │ │ ├── ECPrivateKey.swift │ │ ├── ECPublicKey.swift │ │ ├── HDKey.swift │ │ ├── HDKeyPurpose.swift │ │ ├── HDKeyVersion.swift │ │ ├── HashDigest.swift │ │ ├── LongHash.swift │ │ ├── Mnemonic.swift │ │ ├── Network.swift │ │ ├── PaymentAddress.swift │ │ ├── ShortHash.swift │ │ ├── WIF.swift │ │ └── WrappedData.swift └── BitcoinDemo │ ├── AppDelegate.swift │ ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift └── Tests └── BitcoinDemo ├── Info.plist ├── TestBech32.swift ├── TestBech32Bis.swift ├── TestEC.swift ├── TestEllipticCurve.swift ├── TestFormats.swift ├── TestHD.swift ├── TestHash.swift ├── TestInput.swift ├── TestMnemonic.swift ├── TestOpcode.swift ├── TestOperation.swift ├── TestOutputPoint.swift ├── TestPaymentAddress.swift ├── TestRandom.swift ├── TestSSS.swift ├── TestScript.swift ├── TestTransaction.swift ├── TestTxRef.swift └── TestWrapped.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage/Build 26 | Pods/ 27 | -------------------------------------------------------------------------------- /BXFunctions.md: -------------------------------------------------------------------------------- 1 | # Libbitcoin Explorer Functions 2 | 3 | [https://github.com/libbitcoin/libbitcoin-explorer/wiki](https://github.com/libbitcoin/libbitcoin-explorer/wiki) 4 | 5 | ## Wallet Commands 6 | 7 | * ✅ ec-new (newECPrivateKey, toECPrivateKey) 8 | * ✅ ec-to-address (toECPaymentAddress) 9 | * ✅ ec-to-public (toECPublicKey) 10 | * ✅ ec-to-wif (toWIF) 11 | * ✅ hd-new (newHDPrivateKey) 12 | * ✅ hd-private (deriveHDPrivateKey) 13 | * ✅ hd-public (deriveHDPublicKey) 14 | * ✅ hd-to-ec (toECKey) 15 | * ✅ hd-to-public (toHDPublicKey) 16 | * ✅ mnemonic-new (newMnemonic) 17 | * ✅ mnemonic-to-seed (mnemonicToSeed) 18 | * ✅ seed (seed) 19 | * ✅ wif-to-ec (wifToECPrivateKey) 20 | * ✅ wif-to-public (wifToECPrivateKey |> toECPublicKey) 21 | * electrum-new 22 | * electrum-to-seed 23 | * qrcode 24 | * uri-decode 25 | * uri-encode 26 | 27 | ## Key Encryption Commands 28 | 29 | * ec-to-ek 30 | * ek-address 31 | * ek-new 32 | * ek-public 33 | * ek-public-to-address 34 | * ek-public-to-ec 35 | * ek-to-address 36 | * ek-to-ec 37 | * token-new 38 | 39 | ## Stealth Commands 40 | 41 | * stealth-decode 42 | * stealth-encode 43 | * stealth-public 44 | * stealth-secret 45 | * stealth-shared 46 | 47 | ## Messaging Commands 48 | 49 | * ✅ message-sign (messageSign) 50 | * ✅ message-validate (messageValidate) 51 | 52 | ## Transaction Commands 53 | 54 | * input-set 55 | * input-sign 56 | * input-validate 57 | * ✅ script-decode (scriptDecode) 58 | * ✅ script-encode (scriptEncode) 59 | * ✅ script-to-address (scriptToAddress) 60 | * ✅ tx-decode (transactionDecode) 61 | * tx-encode 62 | * tx-sign 63 | 64 | ## Online Commands 65 | 66 | * fetch-balance 67 | * fetch-block 68 | * fetch-header 69 | * fetch-height 70 | * fetch-history 71 | * fetch-public-key 72 | * fetch-stealth 73 | * fetch-tx 74 | * fetch-tx-index 75 | * fetch-utxo 76 | * send-tx 77 | * send-tx-node 78 | * send-tx-p2p 79 | * subscribe-block 80 | * subscribe-tx 81 | * validate-tx 82 | * watch-address 83 | * watch-stealth 84 | * watch-tx 85 | 86 | ## Encoding Commands 87 | 88 | * ✅ address-encode (addressEncode) 89 | * ✅ address-decode (addressDecode) 90 | * ✅ address-embed (addressEmbed) 91 | * ✅ (bitcoinHashEncode) 92 | * ✅ (bitcoinHashDecode) 93 | * ✅ (encodeBase10) 94 | * ✅ (decodeBase10) 95 | * ✅ base16-encode (base16Encode) 96 | * ✅ base16-decode (base16Decode) 97 | * ✅ (base32Encode) 98 | * ✅ (base32Decode) 99 | * ✅ base58-encode (base58Encode) 100 | * ✅ base58-decode (base58Decode) 101 | * ✅ base64-encode (base64Encode) 102 | * ✅ base64-decode (base64Decode) 103 | * ✅ (base85Encode) 104 | * ✅ (base85Decode) 105 | * ✅ base58check-encode (base58CheckEncode) 106 | * ✅ base58check-decode (base58CheckDecode) 107 | * ✅ wrap-encode (wrapEncode) 108 | * ✅ wrap-decode (wrapDecode) 109 | 110 | ## Hash Commands 111 | 112 | * ✅ bitcoin160 (bitcoin160) 113 | * ✅ bitcoin256 (bitcoin256) 114 | * ✅ ripemd160 (ripemd160) 115 | * ✅ sha160 (sha160) 116 | * ✅ sha256 (sha256) 117 | * ✅ sha512 (sha512) 118 | * ✅ (sha256HMAC) 119 | * ✅ (sha512HMAC) 120 | * ✅ (pkcs5PBKDF2HMACSHA512) 121 | 122 | ## Math Commands 123 | 124 | * ✅ btc-to-satoshi (btcToSatoshi) 125 | * ✅ satoshi-to-btc (satoshiToBTC) 126 | * cert-new 127 | * cert-public 128 | * ec-add 129 | * ec-add-secrets 130 | * ec-multiply 131 | * ec-multiply-secrets 132 | -------------------------------------------------------------------------------- /Bitcoin.h: -------------------------------------------------------------------------------- 1 | // 2 | // Bitcoin.h 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/20/19. 6 | // Copyright © 2019 Blockchain Commons. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Bitcoin. 12 | FOUNDATION_EXPORT double BitcoinVersionNumber; 13 | 14 | //! Project version string for Bitcoin. 15 | FOUNDATION_EXPORT const unsigned char BitcoinVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Bitcoin.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Bitcoin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Bitcoin.xcodeproj/xcshareddata/xcschemes/Bitcoin.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Bitcoin.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Bitcoin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Bitcoin.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Bitcoin.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "ExtensibleEnumeratedName", 6 | "repositoryURL": "https://github.com/wolfmcnally/ExtensibleEnumeratedName.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "76394ea0652f809d19c7edc22614e2e638f82394", 10 | "version": "2.0.2" 11 | } 12 | }, 13 | { 14 | "package": "WolfFoundation", 15 | "repositoryURL": "https://github.com/wolfmcnally/WolfFoundation.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "8370e79858c238b584600374bd13484414796c72", 19 | "version": "5.0.1" 20 | } 21 | }, 22 | { 23 | "package": "WolfNumerics", 24 | "repositoryURL": "https://github.com/wolfmcnally/WolfNumerics.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "40ecf0a68c642dcbe3db4eb8e2c5785da551175e", 28 | "version": "4.0.4" 29 | } 30 | }, 31 | { 32 | "package": "WolfOSBridge", 33 | "repositoryURL": "https://github.com/wolfmcnally/WolfOSBridge.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "b51f5fd34352ff8e4594301d9c0479a9e2e99ce4", 37 | "version": "2.0.3" 38 | } 39 | }, 40 | { 41 | "package": "WolfPipe", 42 | "repositoryURL": "https://github.com/wolfmcnally/WolfPipe.git", 43 | "state": { 44 | "branch": null, 45 | "revision": "c3431ea757cd45972805eddd5fbcb762e465b894", 46 | "version": "2.0.2" 47 | } 48 | }, 49 | { 50 | "package": "WolfStrings", 51 | "repositoryURL": "https://github.com/wolfmcnally/WolfStrings.git", 52 | "state": { 53 | "branch": null, 54 | "revision": "3f1831535ce6261d682ca1ae0d4b2f73bf38df44", 55 | "version": "2.2.1" 56 | } 57 | }, 58 | { 59 | "package": "WolfWith", 60 | "repositoryURL": "https://github.com/wolfmcnally/WolfWith.git", 61 | "state": { 62 | "branch": null, 63 | "revision": "d5aa204302d8250f533221a6bc4c5a4250f9d491", 64 | "version": "2.0.3" 65 | } 66 | } 67 | ] 68 | }, 69 | "version": 1 70 | } 71 | -------------------------------------------------------------------------------- /BitcoinDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BitcoinDemo.xcodeproj/xcshareddata/xcschemes/BitcoinDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 57 | 63 | 64 | 65 | 66 | 67 | 74 | 75 | 81 | 82 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 101 | 102 | 103 | 104 | 105 | 115 | 117 | 123 | 124 | 125 | 126 | 132 | 134 | 140 | 141 | 142 | 143 | 145 | 146 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /Docs/Derivations.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockchainCommons/iOS-Bitcoin/5dc82d5cbef5a07f15d595b1c62bc28d73da04ae/Docs/Derivations.pdf -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2019 Blockchain Commons. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bitcoin 2 | 3 | Swift bindings for libbitcoin. 4 | 5 | ## Blog Post 6 | 7 | On January 25, 2019 I published an announcement for this framework on my blog [here](https://wolfmcnally.com/125/announcing-open-source-bitcoin-framework-for-ios/). 8 | 9 | ## Requirements 10 | 11 | * Swift 5.1 12 | 13 | * Xcode 11.3 14 | 15 | * The `Bitcoin` framework depends on the [CBitcoin](https://github.com/BlockchainCommons/iOS-CBitcoin) framework, which includes a pre-made build of [libbitcoin](https://github.com/libbitcoin). To properly install this, you need to first install the latest version of Git and the Git Large File Storage handler: 16 | 17 | ```bash 18 | $ brew install git 19 | $ brew install git-lfs 20 | $ which git 21 | /usr/local/bin/git 22 | $ git --version 23 | git version 2.21.0 24 | ``` 25 | 26 | ## Installation 27 | 28 | `Bitcoin` no longer supports building via Cocoapods, but since it depends on the `CBitcoin` framework, which in turn embeds several third-party pre-built binary frameworks (libbitcoin etc.) it is also not suitable for distribution via the Swift Package Manager at this time. So for now, it is built directly as an Xcode project. 29 | 30 | The Bitcoin and CBitcoin project directories should be siblings in the same directory: 31 | 32 | ``` 33 | MyProjects 34 | | 35 | +—— CBitcoin 36 | | | 37 | | +—— CBitcoin.xcodeproj 38 | | 39 | +—— Bitcoin 40 | | 41 | +—— Bitcoin.xcworkspace 42 | ``` 43 | 44 | ```bash 45 | $ cd MyProjects 46 | $ git clone https://github.com/BlockchainCommons/iOS-CBitcoin.git CBitcoin 47 | $ git clone https://github.com/BlockchainCommons/iOS-Bitcoin.git Bitcoin 48 | $ cd CBitcoin/Sources 49 | $ unzip -q Frameworks.zip 50 | $ cd ../../Bitcoin 51 | $ open Bitcoin.xcworkspace/ 52 | ``` 53 | 54 | ⚠️Make sure you open `Bitcoin.xcworkspace` and not `Bitcoin.xcodeproj`. 55 | 56 | Within Xcode: 57 | 58 | * Wait for the required Swift Packages to resolve 59 | * Build the `Bitcoin` target for an available platform. 60 | 61 | ## Unit Tests 62 | 63 | The `BitcoinDemo` app is simply an iOS container for the test suite. To run the unit tests, select the `BitcoinDemo` target and then `Product > Test`. 64 | 65 | ## Author 66 | 67 | Wolf McNally, wolf@wolfmcnally.com 68 | 69 | ## License 70 | 71 | Bitcoin is available under the Apache 2 license. See the LICENSE file for more info. 72 | 73 | ## Changes 74 | 75 | ### 0.10.1 76 | 77 | * Added master key component to hdAccountPrivateKeyDerivationPath() 78 | 79 | ### 0.10.0 80 | 81 | * Improvements to BIP32Path to support Master key component. 82 | * Derivations now have a `path` attribute that retrieves the BIP32Path associated with the derivation. 83 | 84 | ### 0.9.0 85 | 86 | * Bech32 and TxRef functionality now support original Bech32 and Bech32Bis. 87 | -------------------------------------------------------------------------------- /Sources/Bitcoin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockchainCommons/iOS-Bitcoin/5dc82d5cbef5a07f15d595b1c62bc28d73da04ae/Sources/Bitcoin/.gitkeep -------------------------------------------------------------------------------- /Sources/Bitcoin/BitcoinError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BitcoinError.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 9/15/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import WolfFoundation 22 | 23 | public struct LibBitcoinResult: CodedError, CustomStringConvertible { 24 | public let code: Int 25 | 26 | public init(code: UInt32) { 27 | self.code = Int(code) 28 | } 29 | 30 | public var isSuccess: Bool { 31 | return code == 0 32 | } 33 | 34 | public var description: String { 35 | return "libbitcoin error \(code)" 36 | } 37 | } 38 | 39 | public enum BitcoinError: Int, Error, CustomStringConvertible, CaseIterable { 40 | private typealias `Self` = BitcoinError 41 | 42 | case errorCode = 1 43 | case invalidFormat = 2 44 | case invalidDataSize = 3 45 | case seedTooSmall = 4 46 | case invalidSeedSize = 5 47 | case invalidSeed = 6 48 | case unsupportedLanguage = 7 49 | case invalidVersion = 8 50 | case privateKeyRequired = 9 51 | case invalidKey = 10 52 | case invalidAddress = 11 53 | case invalidChecksum = 12 54 | case invalidScript = 13 55 | case invalidTransaction = 14 56 | case invalidData = 15 57 | case invalidOpcode = 16 58 | case invalidSignature = 17 59 | case signingFailed = 18 60 | case invalidDerivation = 19 61 | case noRandomNumberSource = 20 62 | case unknownAsset = 21 63 | 64 | private static let descriptions: [BitcoinError: String] = [ 65 | .errorCode: "Error code", 66 | .invalidFormat: "Invalid format", 67 | .invalidDataSize: "Invalid data size", 68 | .seedTooSmall: "Seed size too small", 69 | .invalidSeedSize: "Invalid seed size", 70 | .invalidSeed: "Invalid seed", 71 | .unsupportedLanguage: "Unsupported language", 72 | .invalidVersion: "Invalid version", 73 | .privateKeyRequired: "Private key required", 74 | .invalidKey: "Invalid key", 75 | .invalidAddress: "Invalid address", 76 | .invalidChecksum: "Invalid checksum", 77 | .invalidScript: "Invalid script", 78 | .invalidTransaction: "Invalid transaction", 79 | .invalidData: "Invalid data", 80 | .invalidOpcode: "Invalid opcode", 81 | .invalidSignature: "Invalid signature", 82 | .signingFailed: "Signing failed", 83 | .invalidDerivation: "Invalid derivation", 84 | .noRandomNumberSource: "No random number source available", 85 | .unknownAsset: "Unknown asset" 86 | ] 87 | 88 | public var description: String { 89 | assert(BitcoinError.allCases.count == Self.descriptions.count) 90 | return Self.descriptions[self]! 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Chain/Fee.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Fee.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/24/19. 6 | // Copyright © 2019 Blockchain Commons. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import WolfPipe 11 | 12 | public struct FeeRate: Codable { 13 | public let targetConfirmationMinutes: Int 14 | public let satoshisPerVbyte: Double 15 | 16 | public init(targetConfirmationMinutes: Int, satoshisPerVbyte: Double) { 17 | self.targetConfirmationMinutes = targetConfirmationMinutes 18 | self.satoshisPerVbyte = satoshisPerVbyte 19 | } 20 | } 21 | 22 | public struct FeeSchedule: Codable { 23 | // Array of fee estimates sorted ascending by confirmation time. 24 | public let feeRates: [FeeRate] 25 | 26 | public init(_ feeEstimates: [FeeRate]) { 27 | self.feeRates = feeEstimates.sorted { $0.targetConfirmationMinutes < $1.targetConfirmationMinutes } 28 | } 29 | 30 | public var fastest: FeeRate { 31 | return feeRates.first! 32 | } 33 | 34 | public var cheapest: FeeRate { 35 | return feeRates.last! 36 | } 37 | } 38 | 39 | extension Transaction { 40 | public func fee(with estimate: FeeRate) -> (fee: Satoshis, byteCount: Int) { 41 | let data = self |> serialize 42 | let byteCount = data.count 43 | let fee = Double(byteCount) * estimate.satoshisPerVbyte 44 | let roundedFee = fee.rounded() 45 | let satoshis = UInt64(roundedFee) |> tagSatoshis 46 | return (satoshis, byteCount) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Chain/Input.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Input.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/8/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | import WolfFoundation 24 | 25 | public struct Input: InstanceContainer, Encodable { 26 | var wrapped: WrappedInstance 27 | 28 | init(instance: OpaquePointer) { 29 | wrapped = WrappedInstance(instance) 30 | } 31 | 32 | public init() { 33 | self.init(instance: _inputNew()) 34 | } 35 | 36 | public static func deserialize(_ data: Data) throws -> Input { 37 | let instance = try data.withUnsafeBytes { dataBytes -> OpaquePointer in 38 | var instance: OpaquePointer! 39 | if let error = BitcoinError(rawValue: _inputDeserialize(dataBytes®, data.count, &instance)) { 40 | throw error 41 | } 42 | return instance 43 | } 44 | return Input(instance: instance) 45 | } 46 | 47 | public var serialized: Data { 48 | var dataBytes: UnsafeMutablePointer! 49 | var dataLength = 0 50 | _inputSerialize(wrapped.instance, &dataBytes, &dataLength) 51 | return receiveData(bytes: dataBytes, count: dataLength) 52 | } 53 | 54 | public init(previousOutput: OutputPoint? = nil, script: Script? = nil, sequence: UInt32? = nil) { 55 | self.init() 56 | if let previousOutput = previousOutput { 57 | self.previousOutput = previousOutput 58 | } 59 | if let script = script { 60 | self.script = script 61 | } 62 | self.sequence = sequence ?? 0xffffffff 63 | } 64 | 65 | public var previousOutput: OutputPoint { 66 | get { 67 | return OutputPoint(instance: _inputGetPreviousOutput(wrapped.instance)) 68 | } 69 | 70 | set { 71 | if !isKnownUniquelyReferenced(&wrapped) { 72 | wrapped = WrappedInstance(_inputCopy(wrapped.instance)) 73 | } 74 | _inputSetPreviousOutput(wrapped.instance, newValue.wrapped.instance) 75 | } 76 | } 77 | 78 | public var sequence: UInt32 { 79 | get { 80 | return _inputGetSequence(wrapped.instance) 81 | } 82 | 83 | set { 84 | if !isKnownUniquelyReferenced(&wrapped) { 85 | wrapped = WrappedInstance(_inputCopy(wrapped.instance)) 86 | } 87 | _inputSetSequence(wrapped.instance, newValue) 88 | } 89 | } 90 | 91 | public var script: Script { 92 | get { 93 | return Script(instance: _inputGetScript(wrapped.instance)) 94 | } 95 | 96 | set { 97 | if !isKnownUniquelyReferenced(&wrapped) { 98 | wrapped = WrappedInstance(_inputCopy(wrapped.instance)) 99 | } 100 | _inputSetScript(wrapped.instance, newValue.wrapped.instance) 101 | } 102 | } 103 | 104 | public var scriptString: String { 105 | var decoded: UnsafeMutablePointer! 106 | var decodedLength = 0 107 | _inputGetScriptString(wrapped.instance, RuleFork.allRules®, &decoded, &decodedLength) 108 | return receiveString(bytes: decoded, count: decodedLength) 109 | } 110 | 111 | public var isValid: Bool { 112 | return _inputIsValid(wrapped.instance) 113 | } 114 | 115 | public enum CodingKeys: String, CodingKey { 116 | case previousOutput 117 | case sequence 118 | case script 119 | } 120 | 121 | public func encode(to encoder: Encoder) throws { 122 | var container = encoder.container(keyedBy: CodingKeys.self) 123 | try container.encode(previousOutput, forKey: .previousOutput) 124 | try container.encode(sequence, forKey: .sequence) 125 | try container.encode(scriptString, forKey: .script) 126 | } 127 | } 128 | 129 | extension Input: CustomStringConvertible { 130 | public var description: String { 131 | return try! self |> toJSONString(outputFormatting: .sortedKeys) 132 | } 133 | } 134 | 135 | extension Input: Equatable { 136 | public static func == (lhs: Input, rhs: Input) -> Bool { 137 | return _inputEqual(lhs.wrapped.instance, rhs.wrapped.instance) 138 | } 139 | } 140 | 141 | // MARK: - Free functions 142 | 143 | public func serialize(_ input: Input) -> Data { 144 | return input.serialized 145 | } 146 | 147 | public func deserializeInput(_ data: Data) throws -> Input { 148 | return try Input.deserialize(data) 149 | } 150 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Chain/Output.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Output.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/8/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | import WolfFoundation 24 | 25 | public struct Output: InstanceContainer, Encodable { 26 | var wrapped: WrappedInstance 27 | 28 | init(instance: OpaquePointer) { 29 | wrapped = WrappedInstance(instance) 30 | } 31 | 32 | public init() { 33 | self.init(instance: _outputNew()) 34 | } 35 | 36 | public init(value: Satoshis) { 37 | self.init() 38 | self.value = value 39 | } 40 | 41 | public init(value: Satoshis, paymentAddress: PaymentAddress) throws { 42 | self.init(value: value) 43 | try setPaymentAddress(paymentAddress) 44 | } 45 | 46 | public init(value: Satoshis, script: Script) { 47 | self.init(value: value) 48 | self.script = script 49 | } 50 | 51 | public static func deserialize(_ data: Data) throws -> Output { 52 | let instance = try data.withUnsafeBytes { dataBytes -> OpaquePointer in 53 | var instance: OpaquePointer! 54 | if let error = BitcoinError(rawValue: _outputDeserialize(dataBytes®, data.count, &instance)) { 55 | throw error 56 | } 57 | return instance 58 | } 59 | return Output(instance: instance) 60 | } 61 | 62 | public var serialized: Data { 63 | var dataBytes: UnsafeMutablePointer! 64 | var dataLength = 0 65 | _outputSerialize(wrapped.instance, &dataBytes, &dataLength) 66 | return receiveData(bytes: dataBytes, count: dataLength) 67 | } 68 | 69 | public mutating func setPaymentAddress(_ paymentAddress: PaymentAddress) throws { 70 | if !isKnownUniquelyReferenced(&wrapped) { 71 | wrapped = WrappedInstance(_outputCopy(wrapped.instance)) 72 | } 73 | try paymentAddress®.withCString { paymentAddressBytes in 74 | if let error = BitcoinError(rawValue: _outputSetPaymentAddress(wrapped.instance, paymentAddressBytes)) { 75 | throw error; 76 | } 77 | } 78 | } 79 | 80 | public var value: Satoshis { 81 | get { 82 | return Satoshis(rawValue: _outputGetValue(wrapped.instance)) 83 | } 84 | 85 | set { 86 | if !isKnownUniquelyReferenced(&wrapped) { 87 | wrapped = WrappedInstance(_outputCopy(wrapped.instance)) 88 | } 89 | _outputSetValue(wrapped.instance, newValue®) 90 | } 91 | } 92 | 93 | public var scriptString: String { 94 | var decoded: UnsafeMutablePointer! 95 | var decodedLength = 0 96 | _outputGetScriptString(wrapped.instance, RuleFork.allRules®, &decoded, &decodedLength) 97 | return receiveString(bytes: decoded, count: decodedLength) 98 | } 99 | 100 | public var script: Script { 101 | get { 102 | return Script(instance: _outputGetScript(wrapped.instance)) 103 | } 104 | 105 | set { 106 | if !isKnownUniquelyReferenced(&wrapped) { 107 | wrapped = WrappedInstance(_outputCopy(wrapped.instance)) 108 | } 109 | _outputSetScript(wrapped.instance, newValue.wrapped.instance) 110 | } 111 | } 112 | 113 | public enum CodingKeys: String, CodingKey { 114 | case value 115 | case script 116 | } 117 | 118 | public func encode(to encoder: Encoder) throws { 119 | var container = encoder.container(keyedBy: CodingKeys.self) 120 | try container.encode(value, forKey: .value) 121 | try container.encode(scriptString, forKey: .script) 122 | } 123 | } 124 | 125 | extension Output: CustomStringConvertible { 126 | public var description: String { 127 | return try! self |> toJSONString(outputFormatting: .sortedKeys) 128 | } 129 | } 130 | 131 | extension Output: Equatable { 132 | public static func == (lhs: Output, rhs: Output) -> Bool { 133 | return _outputEqual(lhs.wrapped.instance, rhs.wrapped.instance) 134 | } 135 | } 136 | 137 | // MARK: - Free functions 138 | 139 | public func serialize(_ output: Output) -> Data { 140 | return output.serialized 141 | } 142 | 143 | public func deserializeOutput(_ data: Data) throws -> Output { 144 | return try Output.deserialize(data) 145 | } 146 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Chain/OutputPoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OutputPoint.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/8/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | import WolfFoundation 24 | 25 | /// An OutputPoint is a component of a transaction input, and specifies 26 | /// the output of the previous transaction that is being spent. 27 | public struct OutputPoint: InstanceContainer, Encodable { 28 | var wrapped: WrappedInstance 29 | 30 | init(instance: OpaquePointer) { 31 | wrapped = WrappedInstance(instance) 32 | } 33 | 34 | public init() { 35 | self.init(instance: _outputPointNew()) 36 | } 37 | 38 | public static func deserialize(_ data: Data) throws -> OutputPoint { 39 | let instance = try data.withUnsafeBytes { dataBytes -> OpaquePointer in 40 | var instance: OpaquePointer! 41 | if let error = BitcoinError(rawValue: _outputPointDeserialize(dataBytes®, data.count, &instance)) { 42 | throw error 43 | } 44 | return instance 45 | } 46 | return OutputPoint(instance: instance) 47 | } 48 | 49 | // public init(script: Script) { 50 | // let instance = _outputPointFromScript(script.wrapped.instance) 51 | // self.init(instance: instance) 52 | // } 53 | 54 | public var serialized: Data { 55 | var dataBytes: UnsafeMutablePointer! 56 | var dataLength = 0 57 | _outputPointSerialize(wrapped.instance, &dataBytes, &dataLength) 58 | return receiveData(bytes: dataBytes, count: dataLength) 59 | } 60 | 61 | public init(hash: HashDigest, index: Int) { 62 | self.init() 63 | self.hash = hash 64 | self.index = index 65 | } 66 | 67 | /// This is a sentinel used in `index` to indicate no output, e.g. coinbase. 68 | public static let nullIndex = Int(UInt32.max) 69 | 70 | public var index: Int { 71 | get { 72 | return Int(_outputPointGetIndex(wrapped.instance)) 73 | } 74 | 75 | set { 76 | if !isKnownUniquelyReferenced(&wrapped) { 77 | wrapped = WrappedInstance(_outputPointCopy(wrapped.instance)) 78 | } 79 | _outputPointSetIndex(wrapped.instance, UInt32(newValue)) 80 | } 81 | } 82 | 83 | public var hash: HashDigest { 84 | get { 85 | var hashBytes: UnsafeMutablePointer! 86 | var hashLength = 0 87 | _outputPointGetHash(wrapped.instance, &hashBytes, &hashLength) 88 | return try! receiveData(bytes: hashBytes, count: hashLength) |> tagHashDigest 89 | } 90 | 91 | set { 92 | if !isKnownUniquelyReferenced(&wrapped) { 93 | wrapped = WrappedInstance(_outputPointCopy(wrapped.instance)) 94 | } 95 | newValue®.withUnsafeBytes { hashBytes in 96 | _outputPointSetHash(wrapped.instance, hashBytes®) 97 | } 98 | } 99 | } 100 | 101 | public var isValid: Bool { 102 | return _outputPointIsValid(wrapped.instance) 103 | } 104 | 105 | public var isNull: Bool { 106 | return _outputPointIsNull(wrapped.instance) 107 | } 108 | 109 | public enum CodingKeys: String, CodingKey { 110 | case hash 111 | case index 112 | } 113 | 114 | public func encode(to encoder: Encoder) throws { 115 | var container = encoder.container(keyedBy: CodingKeys.self) 116 | try container.encode(hash® |> toBase16, forKey: .hash) 117 | try container.encode(index, forKey: .index) 118 | } 119 | } 120 | 121 | extension OutputPoint: CustomStringConvertible { 122 | public var description: String { 123 | return try! self |> toJSONString(outputFormatting: .sortedKeys) 124 | } 125 | } 126 | 127 | extension OutputPoint: Equatable { 128 | public static func == (lhs: OutputPoint, rhs: OutputPoint) -> Bool { 129 | return _outputPointEqual(lhs.wrapped.instance, rhs.wrapped.instance); 130 | } 131 | } 132 | 133 | // MARK: - Free functions 134 | 135 | public func serialize(_ outputPoint: OutputPoint) -> Data { 136 | return outputPoint.serialized 137 | } 138 | 139 | public func deserializeOutputPoint(_ data: Data) throws -> OutputPoint { 140 | return try OutputPoint.deserialize(data) 141 | } 142 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Chain/RuleFork.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RuleFork.swift 3 | // Pods 4 | // 5 | // Created by Wolf McNally on 11/12/18. 6 | // 7 | // Copyright © 2019 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | public struct RuleFork: OptionSet { 22 | public let rawValue: UInt32 23 | 24 | public init(rawValue: UInt32) { 25 | self.rawValue = rawValue 26 | } 27 | 28 | public static let noRules = RuleFork(rawValue: 0) 29 | 30 | /// Allow minimum difficulty blocks (hard fork, testnet). 31 | public static let difficult = RuleFork(rawValue: 1 << 0) 32 | 33 | /// Perform difficulty retargeting (hard fork, regtest). 34 | public static let retarget = RuleFork(rawValue: 1 << 1) 35 | 36 | /// Pay-to-script-hash enabled (soft fork, feature). 37 | public static let bip16Rule = RuleFork(rawValue: 1 << 2) 38 | 39 | /// No duplicated unspent transaction ids (soft fork, security). 40 | public static let bip30Rule = RuleFork(rawValue: 1 << 3) 41 | 42 | /// Coinbase must include height (soft fork, security). 43 | public static let bip34Rule = RuleFork(rawValue: 1 << 4) 44 | 45 | /// Strict DER signatures required (soft fork, security). 46 | public static let bip66Rule = RuleFork(rawValue: 1 << 5) 47 | 48 | /// Operation nop2 becomes check locktime verify (soft fork, feature). 49 | public static let bip65Rule = RuleFork(rawValue: 1 << 6) 50 | 51 | /// Hard code bip34-based activation heights (hard fork, optimization). 52 | public static let bip90Rule = RuleFork(rawValue: 1 << 7) 53 | 54 | /// Enforce relative locktime (soft fork, feature). 55 | public static let bip68Rule = RuleFork(rawValue: 1 << 8) 56 | 57 | /// Operation nop3 becomes check sequence verify (soft fork, feature). 58 | public static let bip112Rule = RuleFork(rawValue: 1 << 9) 59 | 60 | /// Use median time past for locktime (soft fork, feature). 61 | public static let bip113Rule = RuleFork(rawValue: 1 << 10) 62 | 63 | /// Segregated witness consensus layer (soft fork, feature). 64 | public static let bip141Rule = RuleFork(rawValue: 1 << 11) 65 | 66 | /// Segregated witness v0 verification (soft fork, feature). 67 | public static let bip143Rule = RuleFork(rawValue: 1 << 12) 68 | 69 | /// Prevent dummy value malleability (soft fork, feature). 70 | public static let bip147Rule = RuleFork(rawValue: 1 << 13) 71 | 72 | /// Fix Satoshi's time warp bug (hard fork, security). 73 | public static let timeWarpPatch = RuleFork(rawValue: 1 << 14) 74 | 75 | /// Fix target overflow for very low difficulty (hard fork, security). 76 | public static let retargetOverflowPatch = RuleFork(rawValue: 1 << 15) 77 | 78 | /// Use scrypt hashing for proof of work (hard fork, feature). 79 | public static let scryptProofOfWork = RuleFork(rawValue: 1 << 16) 80 | 81 | /// Sentinel bit to indicate tx has not been validated. 82 | public static let unverified = RuleFork(rawValue: 1 << 31) 83 | 84 | /// Rules that use bip34-based activation. 85 | public static let bip34Activations: RuleFork = [.bip34Rule, .bip65Rule, .bip66Rule] 86 | 87 | /// Rules that use BIP9 bit zero first time activation. 88 | public static let bip9Bit0Group: RuleFork = [.bip68Rule, .bip112Rule, .bip113Rule] 89 | 90 | /// Rules that use BIP9 bit one first time activation. 91 | public static let bip9Bit1Group: RuleFork = [.bip141Rule, .bip143Rule, .bip147Rule] 92 | 93 | /// Simple mask to set all bits. 94 | public static let allRules = RuleFork(rawValue: 0xffffffff) 95 | } 96 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/25/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | // Consensus sentinels 22 | public let maxInputSequence = UInt32.max 23 | 24 | // Various validation constants. 25 | public let locktimeThreshold = 500_000_000 26 | public let minCoinbaseSize = 2 27 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Formats/Base10.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Base10.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/24/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfFoundation 23 | import WolfPipe 24 | 25 | public enum Base10Tag { } 26 | public typealias Base10 = Tagged 27 | public func tagBase10(_ string: String) -> Base10 { return Base10(rawValue: string) } 28 | 29 | public enum FragmentsTag { } 30 | public typealias Fragments = Tagged 31 | public func tagFragments(_ value: UInt64) -> Fragments { return Fragments(rawValue: value) } 32 | 33 | public enum SatoshisTag { } 34 | public typealias Satoshis = Tagged 35 | public func tagSatoshis(_ amount: UInt64) -> Satoshis { return Satoshis(rawValue: amount) } 36 | 37 | /// The number of decimal places in a bitcoin. 38 | public let btcDecimalPlaces: Int = { 39 | return Int(_btcDecimalPlaces()) 40 | }() 41 | 42 | /// The number of decimal places in a milli-bitcoin. 43 | public let mbtcDecimalPlaces: Int = { 44 | return Int(_mbtcDecimalPlaces()) 45 | }() 46 | 47 | /// The number of decimal places in a micro-bitcoin. 48 | public let ubtcDecimalPlaces: Int = { 49 | return Int(_ubtcDecimalPlaces()) 50 | }() 51 | 52 | /// Converts a Bitcoin amount to a string, following the BIP 21 grammar. 53 | /// Avoids the rounding issues often seen with floating-point methods. 54 | /// 55 | /// This is a curried function suitable for use with the pipe operator. 56 | /// 57 | /// - parameter decimalPlaces: The location of the decimal point. The default 58 | /// is zero, which treats the input as a normal integer. 59 | public func toBase10(decimalPlaces: Int) -> (_ amount: Fragments) -> Base10 { 60 | return { amount in 61 | var bytes: UnsafeMutablePointer! 62 | var count: Int = 0 63 | _encodeBase10(amount®, &bytes, &count, UInt8(decimalPlaces)) 64 | return receiveString(bytes: bytes, count: count) |> tagBase10 65 | } 66 | } 67 | 68 | /// Converts a Bitcoin amount to a string, following the BIP 21 grammar. 69 | /// Avoids the rounding issues often seen with floating-point methods. 70 | /// 71 | /// This is a single-argument function suitable for use with the pipe operator. 72 | /// 73 | /// - parameter amount: The amount to be encoded. 74 | public func toBase10(_ amount: Fragments) -> Base10 { 75 | return amount |> toBase10(decimalPlaces: 0) 76 | } 77 | 78 | /// Converts a bitcoin amount (in satoshis) to a string. 79 | /// 80 | /// This is a single-argument function suitable for use with the pipe operator. 81 | /// 82 | /// - parameter satoshi: The amount (in satoshis) to be encoded. 83 | public func satoshiToBTC(_ satoshi: Satoshis) -> Base10 { 84 | return satoshi® |> tagFragments |> toBase10(decimalPlaces: btcDecimalPlaces) 85 | } 86 | 87 | /// Converts a string to a Bitcoin amount according to the BIP 21 grammar. 88 | /// 89 | /// This is a curried function suitable for use with the pipe operator. 90 | /// 91 | /// Throws if the string fails to validate. 92 | /// 93 | /// - parameter decimalPlaces: The location of the decimal point. The default 94 | /// is zero, which treats the input as a normal integer. 95 | /// - parameter strict: true to treat fractional results as an error, 96 | /// or false to round them upwards. 97 | public func toFragments(decimalPlaces: Int, strict: Bool = true) -> (_ base10: Base10) throws -> Fragments { 98 | return { base10 in 99 | var out: UInt64 = 0 100 | try base10®.withCString { (stringBytes) in 101 | try withUnsafeMutablePointer(to: &out) { (outPointer: UnsafeMutablePointer) in 102 | if let error = BitcoinError(rawValue: _decodeBase10(stringBytes, outPointer, decimalPlaces, strict)) { 103 | throw error 104 | } 105 | } 106 | } 107 | return out |> tagFragments 108 | } 109 | } 110 | 111 | /// Converts a string to a Bitcoin amount according to the BIP 21 grammar. 112 | /// 113 | /// This is a single-argument function suitable for use with the pipe operator. 114 | /// 115 | /// Throws if the string fails to validate. 116 | /// 117 | /// - parameter string: The amount to be decoded. 118 | /// - returns: The amount. 119 | public func toFragments(_ base10: Base10) throws -> Fragments { 120 | return try base10 |> toFragments(decimalPlaces: 0) 121 | } 122 | 123 | /// Converts a string in Bitcoin to an amount (in satoshis). 124 | /// 125 | /// This is a single-argument function suitable for use with the pipe operator. 126 | /// 127 | /// - parameter string: The amount (in BTC) to be decoded. 128 | /// - returns: The amount (in satoshis). 129 | public func btcToSatoshi(_ base10: Base10) throws -> Satoshis { 130 | return try base10 |> toFragments(decimalPlaces: btcDecimalPlaces) |> rawValue |> tagSatoshis 131 | } 132 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Formats/Base16.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Base16.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/24/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfFoundation 23 | import WolfPipe 24 | 25 | public enum Base16Tag { } 26 | public typealias Base16 = Tagged 27 | public func tagBase16(_ string: String) -> Base16 { return Base16(rawValue: string) } 28 | 29 | /// Encodes the data as a base16 (hex) string. 30 | public func toBase16(_ data: Data) -> Base16 { 31 | return data.withUnsafeBytes { dataBytes -> Base16 in 32 | var bytes: UnsafeMutablePointer! 33 | var count: Int = 0 34 | _encodeBase16(dataBytes®, data.count, &bytes, &count) 35 | return receiveString(bytes: bytes, count: count) |> tagBase16 36 | } 37 | } 38 | 39 | /// Decodes the base16 (hex) format string. 40 | /// 41 | /// Throws if the string is not valid base16. 42 | public func toData(_ base16: Base16) throws -> Data { 43 | return try base16®.withCString { (stringBytes) in 44 | var bytes: UnsafeMutablePointer! 45 | var count: Int = 0 46 | if let error = BitcoinError(rawValue: _decodeBase16(stringBytes, &bytes, &count)) { 47 | throw error 48 | } 49 | return receiveData(bytes: bytes, count: count) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Formats/Base32.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Base32.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/27/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfFoundation 23 | import WolfPipe 24 | 25 | public enum Base32Tag { } 26 | public typealias Base32 = Tagged 27 | public func tagBase32(_ string: String) -> Base32 { return Base32(rawValue: string) } 28 | 29 | /// Convert the data to Base32 with the provided prefix. 30 | public func toBase32(prefix: String, payload: Data) -> Base32 { 31 | return payload.withUnsafeBytes { dataBytes -> Base32 in 32 | prefix.withCString { prefixBytes in 33 | var bytes: UnsafeMutablePointer! 34 | var count: Int = 0 35 | _encodeBase32(prefixBytes, dataBytes®, payload.count, &bytes, &count) 36 | return receiveString(bytes: bytes, count: count) |> tagBase32 37 | } 38 | } 39 | } 40 | 41 | /// Convert the data to Base32 with the provided prefix. 42 | public func toBase32(prefix: String) -> (_ payload: Data) -> Base32 { 43 | return { payload in 44 | toBase32(prefix: prefix, payload: payload) 45 | } 46 | } 47 | 48 | /// Decodes the base32 format string. 49 | /// 50 | /// Throws if the string is not valid base32. 51 | public func toData(_ base32: Base32) throws -> (prefix: String, payload: Data) { 52 | return try base32®.withCString { (stringBytes) in 53 | var prefixBytes: UnsafeMutablePointer! 54 | var prefixCount: Int = 0 55 | var payloadBytes: UnsafeMutablePointer! 56 | var payloadCount: Int = 0 57 | if let error = BitcoinError(rawValue: _decodeBase32(stringBytes, &prefixBytes, &prefixCount, &payloadBytes, &payloadCount)) { 58 | throw error 59 | } 60 | let prefix = receiveString(bytes: prefixBytes, count: prefixCount) 61 | let payload = receiveData(bytes: payloadBytes, count: payloadCount) 62 | return (prefix, payload) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Formats/Base58.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Base58.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/23/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfFoundation 23 | import WolfPipe 24 | 25 | public enum Base58Tag { } 26 | public typealias Base58 = Tagged 27 | public func tagBase58(_ string: String) -> Base58 { return Base58(rawValue: string) } 28 | 29 | public enum Base58CheckTag { } 30 | public typealias Base58Check = Tagged 31 | public func tagBase58Check(_ string: String) -> Base58Check { return Base58Check(rawValue: string) } 32 | 33 | extension Character { 34 | /// Returns true if the character is a valid base58 character, false otherwise. 35 | public var isBase58: Bool { 36 | guard let a = self.ascii else { return false } 37 | return _isBase58Char(a) 38 | } 39 | } 40 | 41 | extension String { 42 | /// Returns true if the string is a valid base58 character, false otherwise. 43 | public var isBase58: Bool { 44 | return self.withCString { 45 | _isBase58String($0) 46 | } 47 | } 48 | } 49 | 50 | /// Encodes the data as a base58 string. 51 | public func toBase58(_ data: Data) -> Base58 { 52 | return data.withUnsafeBytes { (dataBytes: UnsafeRawBufferPointer) -> Base58 in 53 | var bytes: UnsafeMutablePointer! 54 | var count: Int = 0 55 | _encodeBase58(dataBytes®, data.count, &bytes, &count) 56 | return receiveString(bytes: bytes, count: count) |> tagBase58 57 | } 58 | } 59 | 60 | /// Decodes the base58 format string. 61 | /// 62 | /// Throws if the string is not valid base58. 63 | public func toData(_ base58: Base58) throws -> Data { 64 | return try base58®.withCString { (stringBytes) in 65 | var bytes: UnsafeMutablePointer! 66 | var count: Int = 0 67 | if let error = BitcoinError(rawValue: _decodeBase58(stringBytes, &bytes, &count)) { 68 | throw error 69 | } 70 | return receiveData(bytes: bytes, count: count) 71 | } 72 | } 73 | 74 | /// Encodes the data as a base58check string. 75 | public func toBase58Check(version: UInt8) -> (_ data: Data) -> Base58Check { 76 | return { data in 77 | return data.withUnsafeBytes { (dataBytes: UnsafeRawBufferPointer) -> Base58Check in 78 | var bytes: UnsafeMutablePointer! 79 | var count: Int = 0 80 | _encodeBase58Check(dataBytes®, data.count, version, &bytes, &count) 81 | return receiveString(bytes: bytes, count: count) |> tagBase58Check 82 | } 83 | } 84 | } 85 | 86 | public func toBase58Check(_ data: Data) -> Base58Check { 87 | return toBase58Check(version: 0)(data) 88 | } 89 | 90 | public func toData(_ base58Check: Base58Check) throws -> (version: UInt8, payload: Data) { 91 | return try base58Check®.withCString { (stringBytes) in 92 | var bytes: UnsafeMutablePointer! 93 | var count: Int = 0 94 | var version: UInt8 = 0 95 | if let error = BitcoinError(rawValue: _decodeBase58Check(stringBytes, &bytes, &count, &version)) { 96 | throw error 97 | } 98 | let data = receiveData(bytes: bytes, count: count) 99 | return (version, data) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Formats/Base64.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Base64.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/25/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfFoundation 23 | import WolfPipe 24 | 25 | public enum Base64Tag { } 26 | public typealias Base64 = Tagged 27 | public func tagBase64(_ string: String) -> Base64 { return Base64(rawValue: string) } 28 | 29 | /// Encodes the data as a base64 string. 30 | public func toBase64(_ data: Data) -> Base64 { 31 | return data.withUnsafeBytes { dataBytes -> Base64 in 32 | var bytes: UnsafeMutablePointer! 33 | var count: Int = 0 34 | _encodeBase64(dataBytes®, data.count, &bytes, &count) 35 | return receiveString(bytes: bytes, count: count) |> tagBase64 36 | } 37 | } 38 | 39 | /// Decodes the base64 format string. 40 | /// 41 | /// Throws if the string is not valid base64. 42 | public func toData(_ base64: Base64) throws -> Data { 43 | return try base64®.withCString { (stringBytes) in 44 | var bytes: UnsafeMutablePointer! 45 | var count: Int = 0 46 | if let error = BitcoinError(rawValue: _decodeBase64(stringBytes, &bytes, &count)) { 47 | throw error 48 | } 49 | return receiveData(bytes: bytes, count: count) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Formats/Base85.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Base85.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/25/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfFoundation 23 | import WolfPipe 24 | 25 | public enum Base85Tag { } 26 | public typealias Base85 = Tagged 27 | public func tagBase85(_ string: String) -> Base85 { return Base85(rawValue: string) } 28 | 29 | /// Encodes the data as a base85 string. 30 | public func toBase85(_ data: Data) -> Base85 { 31 | return data.withUnsafeBytes { dataBytes -> Base85 in 32 | var bytes: UnsafeMutablePointer! 33 | var count: Int = 0 34 | _encodeBase85(dataBytes®, data.count, &bytes, &count) 35 | return receiveString(bytes: bytes, count: count) |> tagBase85 36 | } 37 | } 38 | 39 | /// Decodes the base85 format string. 40 | /// 41 | /// Throws if the string is not valid base85. 42 | public func toData(_ base85: Base85) throws -> Data { 43 | return try base85®.withCString { (stringBytes) in 44 | var bytes: UnsafeMutablePointer! 45 | var count: Int = 0 46 | if let error = BitcoinError(rawValue: _decodeBase85(stringBytes, &bytes, &count)) { 47 | throw error 48 | } 49 | return receiveData(bytes: bytes, count: count) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Formats/BitcoinHash.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BitcoinHash.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/29/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | import WolfFoundation 24 | 25 | public enum BitcoinHashTag { } 26 | public typealias BitcoinHash = Tagged 27 | public func tagBitcoinHash(_ string: String) -> BitcoinHash { return BitcoinHash(rawValue: string) } 28 | 29 | /// Encodes the bitcoin hash as a string. 30 | /// 31 | /// The bitcoin hash format is like base16, but with the bytes reversed. 32 | /// Throws if the input data is not exactly 32 bytes long. 33 | public func toBitcoinHash(_ data: Data) throws -> BitcoinHash { 34 | guard data.count == 32 else { 35 | throw BitcoinError.invalidFormat 36 | } 37 | return data.withUnsafeBytes { dataBytes -> BitcoinHash in 38 | var bytes: UnsafeMutablePointer! 39 | var count: Int = 0 40 | _encodeBitcoinHash(dataBytes®, &bytes, &count) 41 | return BitcoinHash(rawValue: receiveString(bytes: bytes, count: count)) 42 | } 43 | } 44 | 45 | /// Decodes the base16 (hex) format string. 46 | /// 47 | /// The bitcoin hash format is like base16, but with the bytes reversed. 48 | /// Throws if the string is not valid base16. 49 | public func toData(_ hash: BitcoinHash) throws -> Data { 50 | return try hash®.withCString { stringBytes in 51 | var bytes: UnsafeMutablePointer! 52 | var count: Int = 0 53 | if let error = BitcoinError(rawValue: _decodeBitcoinHash(stringBytes, &bytes, &count)) { 54 | throw error 55 | } 56 | return receiveData(bytes: bytes, count: count) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Formats/SegwitAddress.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SegwitAddress.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/7/19. 6 | // Copyright © 2019 Blockchain Commons. All rights reserved. 7 | // 8 | 9 | // Based on: 10 | // https://github.com/0xDEADP00L/Bech32/blob/master/Sources/SegwitAddrCoder.swift 11 | 12 | // Base32 address format for native v0-16 witness outputs implementation 13 | // https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki 14 | // Inspired by Pieter Wuille C++ implementation 15 | 16 | import Foundation 17 | import WolfPipe 18 | 19 | public struct SegwitAddress { 20 | public let version: Int 21 | public let program: Data 22 | 23 | public init(version: Int, program: Data) { 24 | self.version = version 25 | self.program = program 26 | } 27 | } 28 | 29 | public func toBech32(_ hrp: String) -> (_ address: SegwitAddress) throws -> Bech32 { 30 | return { address in 31 | try SegwitAddrCoder.encode(hrp: hrp, segwitAddress: address) 32 | } 33 | } 34 | 35 | public func toSegwitAddress(_ hrp: String) -> (_ bech32: Bech32) throws -> SegwitAddress { 36 | return { bech32 in 37 | try SegwitAddrCoder.decode(hrp: hrp, bech32: bech32) 38 | } 39 | } 40 | 41 | /// Segregated Witness Address encoder/decoder 42 | fileprivate class SegwitAddrCoder { 43 | /// Convert from one power-of-2 number base to another 44 | private static func convertBits(from: Int, to: Int, pad: Bool, idata: Data) throws -> Data { 45 | var acc: Int = 0 46 | var bits: Int = 0 47 | let maxv: Int = (1 << to) - 1 48 | let maxAcc: Int = (1 << (from + to - 1)) - 1 49 | var odata = Data() 50 | for ibyte in idata { 51 | acc = ((acc << from) | Int(ibyte)) & maxAcc 52 | bits += from 53 | while bits >= to { 54 | bits -= to 55 | odata.append(UInt8((acc >> bits) & maxv)) 56 | } 57 | } 58 | if pad { 59 | if bits != 0 { 60 | odata.append(UInt8((acc << (to - bits)) & maxv)) 61 | } 62 | } else if (bits >= from || ((acc << (to - bits)) & maxv) != 0) { 63 | throw SegwitAddressError.bitsConversionFailed 64 | } 65 | return odata 66 | } 67 | 68 | /// Decode segwit address 69 | fileprivate static func decode(hrp: String, bech32: Bech32) throws -> SegwitAddress { 70 | let dec = try bech32 |> toData 71 | guard dec.hrp == hrp else { 72 | throw SegwitAddressError.hrpMismatch(dec.hrp, hrp) 73 | } 74 | guard dec.data.count >= 1 else { 75 | throw SegwitAddressError.checksumSizeTooLow 76 | } 77 | let conv = try convertBits(from: 5, to: 8, pad: false, idata: dec.data.advanced(by: 1)) 78 | guard conv.count >= 2 && conv.count <= 40 else { 79 | throw SegwitAddressError.dataSizeMismatch(conv.count) 80 | } 81 | guard dec.data[0] <= 16 else { 82 | throw SegwitAddressError.segwitVersionNotSupported(dec.data[0]) 83 | } 84 | if dec.data[0] == 0 && conv.count != 20 && conv.count != 32 { 85 | throw SegwitAddressError.segwitV0ProgramSizeMismatch(conv.count) 86 | } 87 | return SegwitAddress(version: Int(dec.data[0]), program: conv) 88 | } 89 | 90 | /// Encode segwit address 91 | fileprivate static func encode(hrp: String, segwitAddress: SegwitAddress) throws -> Bech32 { 92 | var enc = Data([UInt8(segwitAddress.version)]) 93 | enc.append(try convertBits(from: 8, to: 5, pad: true, idata: segwitAddress.program)) 94 | let result = enc |> toBech32(hrp) 95 | guard let _ = try? decode(hrp: hrp, bech32: result) else { 96 | throw SegwitAddressError.encodingCheckFailed 97 | } 98 | return result 99 | } 100 | } 101 | 102 | public enum SegwitAddressError: LocalizedError { 103 | case bitsConversionFailed 104 | case hrpMismatch(String, String) 105 | case checksumSizeTooLow 106 | 107 | case dataSizeMismatch(Int) 108 | case segwitVersionNotSupported(UInt8) 109 | case segwitV0ProgramSizeMismatch(Int) 110 | 111 | case encodingCheckFailed 112 | 113 | public var errorDescription: String? { 114 | switch self { 115 | case .bitsConversionFailed: 116 | return "Failed to perform bits conversion" 117 | case .checksumSizeTooLow: 118 | return "Checksum size is too low" 119 | case .dataSizeMismatch(let size): 120 | return "Program size \(size) does not meet required range 2...40" 121 | case .encodingCheckFailed: 122 | return "Failed to check result after encoding" 123 | case .hrpMismatch(let got, let expected): 124 | return "Human-readable-part \"\(got)\" does not match requested \"\(expected)\"" 125 | case .segwitV0ProgramSizeMismatch(let size): 126 | return "Segwit program size \(size) does not meet version 0 requirments" 127 | case .segwitVersionNotSupported(let version): 128 | return "Segwit version \(version) is not supported by this decoder" 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Machine/ScriptOperation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScriptOperation.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/14/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing software 16 | // distributed under the License is distributed on an "AS IS" BASIS 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | import WolfFoundation 24 | 25 | public struct ScriptOperation: InstanceContainer { 26 | var wrapped: WrappedInstance 27 | 28 | init(instance: OpaquePointer) { 29 | wrapped = WrappedInstance(instance) 30 | } 31 | 32 | public init() { 33 | self.init(instance: _operationNew()) 34 | } 35 | 36 | public init(_ opcode: ScriptOpcode) { 37 | self.init(instance: _operationFromOpcode(opcode®)) 38 | } 39 | 40 | public init(_ data: Data, isMinimal: Bool = true) throws { 41 | let instance = try data.withUnsafeBytes { dataBytes -> OpaquePointer in 42 | var instance: OpaquePointer! 43 | if let error = BitcoinError(rawValue: _operationFromData(dataBytes®, data.count, isMinimal, &instance)) { 44 | throw error 45 | } 46 | return instance 47 | } 48 | self.init(instance: instance) 49 | } 50 | 51 | public init(_ string: String) throws { 52 | let instance = try string.withCString { (stringBytes: UnsafePointer) -> OpaquePointer in 53 | var instance: OpaquePointer! 54 | if let error = BitcoinError(rawValue: _operationFromString(stringBytes, &instance)) { 55 | throw error 56 | } 57 | return instance 58 | } 59 | self.init(instance: instance) 60 | } 61 | 62 | public static func deserialize(_ data: Data) throws -> ScriptOperation { 63 | let instance = try data.withUnsafeBytes { dataBytes -> OpaquePointer in 64 | var instance: OpaquePointer! 65 | if let error = BitcoinError(rawValue: _operationDeserialize(dataBytes®, data.count, &instance)) { 66 | throw error 67 | } 68 | return instance 69 | } 70 | return ScriptOperation(instance: instance) 71 | } 72 | 73 | public var serialized: Data { 74 | var data: UnsafeMutablePointer! 75 | var dataLength = 0 76 | _operationSerialize(wrapped.instance, &data, &dataLength) 77 | return receiveData(bytes: data, count: dataLength) 78 | } 79 | 80 | public var isValid: Bool { 81 | return _operationIsValid(wrapped.instance) 82 | } 83 | 84 | public func string(with rules: RuleFork) -> String { 85 | var string: UnsafeMutablePointer! 86 | var stringLength = 0 87 | _operationToString(wrapped.instance, rules®, &string, &stringLength) 88 | return receiveString(bytes: string, count: stringLength) 89 | } 90 | 91 | public var opcode: ScriptOpcode { 92 | return ScriptOpcode(rawValue: _operationGetOpcode(wrapped.instance))! 93 | } 94 | 95 | public var data: Data { 96 | var data: UnsafeMutablePointer! 97 | var dataLength = 0 98 | _operationGetData(wrapped.instance, &data, &dataLength) 99 | return receiveData(bytes: data, count: dataLength) 100 | } 101 | } 102 | 103 | extension ScriptOperation: Equatable { 104 | public static func == (lhs: ScriptOperation, rhs: ScriptOperation) -> Bool { 105 | return _operationEqual(lhs.wrapped.instance, rhs.wrapped.instance) 106 | } 107 | } 108 | 109 | extension ScriptOperation: CustomStringConvertible { 110 | public var description: String { 111 | return self |> toString 112 | } 113 | } 114 | 115 | // MARK: - Free Functions 116 | 117 | public func toOperation(_ opcode: ScriptOpcode) -> ScriptOperation { 118 | return ScriptOperation(opcode) 119 | } 120 | 121 | public func toOperation(isMinimal: Bool) -> (_ data: Data) throws -> ScriptOperation { 122 | return { data in 123 | return try ScriptOperation(data, isMinimal: isMinimal) 124 | } 125 | } 126 | 127 | public func toOperation(_ data: Data) throws -> ScriptOperation { 128 | return try data |> toOperation(isMinimal: true) 129 | } 130 | 131 | public func toOperation(_ string: String) throws -> ScriptOperation { 132 | return try ScriptOperation(string) 133 | } 134 | 135 | public func serialize(_ operation: ScriptOperation) -> Data { 136 | return operation.serialized 137 | } 138 | 139 | public func deserializeOperation(_ data: Data) throws -> ScriptOperation { 140 | return try ScriptOperation.deserialize(data) 141 | } 142 | 143 | public func toString(rules: RuleFork) -> (_ operation: ScriptOperation) -> String { 144 | return { operation in 145 | return operation.string(with: rules) 146 | } 147 | } 148 | 149 | public func toString(_ operation: ScriptOperation) -> String { 150 | return operation |> toString(rules: .allRules) 151 | } 152 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Machine/ScriptPattern.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScriptPattern.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/15/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | /// Script patterms. 22 | /// 23 | /// Comments from: bitcoin.org/en/developer-guide#signature-hash-types 24 | public enum ScriptPattern: Int32 25 | { 26 | /// Null Data 27 | /// 28 | /// Pubkey Script: OP_RETURN <0 to 80 bytes of data> (formerly 40 bytes) 29 | /// Null data scripts cannot be spent, so there's no signature script. 30 | case payNullData 31 | 32 | /// Pay to Multisig [BIP11] 33 | /// 34 | /// Pubkey script: [B pubkey][C pubkey...] OP_CHECKMULTISIG 35 | /// Signature script: OP_0 [B sig][C sig...] 36 | case payMultisig 37 | 38 | /// Pay to Public Key (obsolete) 39 | case payPublicKey 40 | 41 | /// Pay to Public Key Hash [P2PKH] 42 | /// 43 | /// Pubkey script: OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG 44 | /// Signature script: 45 | case payKeyHash 46 | 47 | /// Pay to Script Hash [P2SH/BIP16] 48 | /// 49 | /// The redeem script may be any pay type, but only multisig makes sense. 50 | /// Pubkey script: OP_HASH160 OP_EQUAL 51 | /// Signature script: [sig][sig...] 52 | case payScriptHash 53 | 54 | /// Sign Multisig script [BIP11] 55 | case signMultisig 56 | 57 | /// Sign Public Key (obsolete) 58 | case signPublicKey 59 | 60 | /// Sign Public Key Hash [P2PKH] 61 | case signKeyHash 62 | 63 | /// Sign Script Hash [P2SH/BIP16] 64 | case signScriptHash 65 | 66 | /// Witness coinbase reserved value [BIP141]. 67 | case witnessReservation 68 | 69 | /// The script may be valid but does not conform to the common templates. 70 | /// Such scripts are always accepted if they are mined into blocks, but 71 | /// transactions with uncommon scripts may not be forwarded by peers. 72 | case nonStandard 73 | } 74 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Machine/ScriptVersion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScriptVersion.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/15/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | /// Script versions (bip141). 22 | public enum ScriptVersion: Int32 23 | { 24 | /// Defined by bip141. 25 | case zero 26 | 27 | /// All reserved script versions (1..16). 28 | case reserved 29 | 30 | /// All unversioned scripts. 31 | case unversioned 32 | }; 33 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Machine/SigHashAlgorithm.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SigHashAlgorithm.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/19/18. 6 | // 7 | // Copyright © 2019 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | /// Signature hash types. 22 | /// 23 | /// Comments from: bitcoin.org/en/developer-guide#standard-transactions 24 | public struct SigHashAlgorithm: OptionSet { 25 | public let rawValue: UInt8 26 | 27 | public init(rawValue: UInt8) { 28 | self.rawValue = rawValue 29 | } 30 | 31 | /// The default, signs all the inputs and outputs, protecting everything 32 | /// except the signature scripts against modification. 33 | public static let all = SigHashAlgorithm(rawValue: 0x01) 34 | 35 | /// Signs all of the inputs but none of the outputs, allowing anyone to 36 | /// change where the satoshis are going unless other signatures using 37 | /// other signature hash flags protect the outputs. 38 | public static let none = SigHashAlgorithm(rawValue: 0x02) 39 | 40 | /// The only output signed is the one corresponding to this input (the 41 | /// output with the same output index number as this input), ensuring 42 | /// nobody can change your part of the transaction but allowing other 43 | /// signers to change their part of the transaction. The corresponding 44 | /// output must exist or the value '1' will be signed, breaking the 45 | /// security scheme. This input, as well as other inputs, are included 46 | /// in the signature. The sequence numbers of other inputs are not 47 | /// included in the signature, and can be updated. 48 | public static let single = SigHashAlgorithm(rawValue: 0x03) 49 | 50 | /// The above types can be modified with this flag, creating three new 51 | /// combined types. 52 | public static let anyoneCanPay = SigHashAlgorithm(rawValue: 0x80) 53 | 54 | /// Signs all of the outputs but only this one input, and it also allows 55 | /// anyone to add or remove other inputs, so anyone can contribute 56 | /// additional satoshis but they cannot change how many satoshis are 57 | /// sent nor where they go. 58 | public static let allAnyoneCanPay = SigHashAlgorithm([.all, .anyoneCanPay]) 59 | 60 | /// Signs only this one input and allows anyone to add or remove other 61 | /// inputs or outputs, so anyone who gets a copy of this input can spend 62 | /// it however they'd like. 63 | public static let noneAnyoneCanPay = SigHashAlgorithm([.none, .anyoneCanPay]) 64 | 65 | /// Signs this one input and its corresponding output. Allows anyone to 66 | /// add or remove other inputs. 67 | public static let singleAnyoneCanPay = SigHashAlgorithm([.single, .anyoneCanPay]) 68 | } 69 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Math/DERSignature.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DERSignature.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/19/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfFoundation 23 | import WolfPipe 24 | 25 | public enum DERSignatureTag { } 26 | /// A DER-encoded signature 27 | public typealias DERSignature = Tagged 28 | 29 | public func tagDERSignature(_ data: Data) throws -> DERSignature { 30 | guard (64 ... 72).contains(data.count) else { 31 | throw BitcoinError.invalidDataSize 32 | } 33 | return DERSignature(rawValue: data) 34 | } 35 | 36 | public func toDERSignature(_ ecSignature: ECSignature) throws -> DERSignature { 37 | return try ecSignature®.withUnsafeBytes { (ecSignatureBytes: UnsafeRawBufferPointer) in 38 | var derSignatureBytes: UnsafeMutablePointer! 39 | var derSignatureLength = 0 40 | if let error = BitcoinError(rawValue: _encodeSignature(ecSignatureBytes®, &derSignatureBytes, &derSignatureLength)) { 41 | throw error 42 | } 43 | return try receiveData(bytes: derSignatureBytes, count: derSignatureLength) |> tagDERSignature 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Math/ECSignature.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ECSignature.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/19/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfFoundation 23 | import WolfPipe 24 | 25 | public enum ECSignatureTag { } 26 | /// Parsed ECDSA signature 27 | public typealias ECSignature = Tagged 28 | 29 | public func tagECSignature(_ data: Data) throws -> ECSignature { 30 | guard data.count == 64 else { 31 | throw BitcoinError.invalidDataSize 32 | } 33 | return ECSignature(rawValue: data) 34 | } 35 | 36 | public func toECSignature(isStrict: Bool) -> (_ derSignature: DERSignature) throws -> ECSignature { 37 | return { derSignature in 38 | try derSignature®.withUnsafeBytes { (derSignatureBytes: UnsafeRawBufferPointer) in 39 | var ecSignatureBytes: UnsafeMutablePointer! 40 | var ecSignatureLength = 0 41 | if let error = BitcoinError(rawValue: _parseSignature(derSignatureBytes®, derSignature®.count, isStrict, &ecSignatureBytes, &ecSignatureLength)) { 42 | throw error 43 | } 44 | return try receiveData(bytes: ecSignatureBytes, count: ecSignatureLength) |> tagECSignature 45 | } 46 | } 47 | } 48 | 49 | public func toECSignature(_ derSignature: DERSignature) throws -> ECSignature { 50 | return try derSignature |> toECSignature(isStrict: true) 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Math/Endorsement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Endorsement.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/20/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import Foundation 22 | import WolfFoundation 23 | 24 | public enum EndorsementTag { } 25 | /// DER encoded signature with sighash byte for contract endorsement: 26 | public typealias Endorsement = Tagged 27 | 28 | public func tagEndorsement(_ data: Data) throws -> Endorsement { 29 | guard (9 ... 73).contains(data.count) else { 30 | throw BitcoinError.invalidDataSize 31 | } 32 | return Endorsement(rawValue: data) 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Math/Hash.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Hash.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/24/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | 24 | public func toRIPEMD160(_ data: Data) -> ShortHash { 25 | return data.withUnsafeBytes { dataBytes in 26 | var bytes: UnsafeMutablePointer! 27 | var count: Int = 0 28 | _ripemd160(dataBytes®, data.count, &bytes, &count) 29 | return try! receiveData(bytes: bytes, count: count) |> tagShortHash 30 | } 31 | } 32 | 33 | public func toSHA160(_ data: Data) -> ShortHash { 34 | return data.withUnsafeBytes { dataBytes in 35 | var bytes: UnsafeMutablePointer! 36 | var count: Int = 0 37 | _sha160(dataBytes®, data.count, &bytes, &count) 38 | return try! receiveData(bytes: bytes, count: count) |> tagShortHash 39 | } 40 | } 41 | 42 | public func toSHA256(_ data: Data) -> HashDigest { 43 | return data.withUnsafeBytes { dataBytes in 44 | var bytes: UnsafeMutablePointer! 45 | var count: Int = 0 46 | _sha256(dataBytes®, data.count, &bytes, &count) 47 | return try! receiveData(bytes: bytes, count: count) |> tagHashDigest 48 | } 49 | } 50 | 51 | public func toSHA512(_ data: Data) -> LongHash { 52 | return data.withUnsafeBytes { dataBytes in 53 | var bytes: UnsafeMutablePointer! 54 | var count: Int = 0 55 | _sha512(dataBytes®, data.count, &bytes, &count) 56 | return try! receiveData(bytes: bytes, count: count) |> tagLongHash 57 | } 58 | } 59 | 60 | public func toBitcoin256(_ data: Data) -> HashDigest { 61 | return data.withUnsafeBytes { dataBytes in 62 | var bytes: UnsafeMutablePointer! 63 | var count: Int = 0 64 | _bitcoin256(dataBytes®, data.count, &bytes, &count) 65 | return try! receiveData(bytes: bytes, count: count) |> tagHashDigest 66 | } 67 | } 68 | 69 | public func toBitcoin160(_ data: Data) -> ShortHash { 70 | return data.withUnsafeBytes { dataBytes in 71 | var bytes: UnsafeMutablePointer! 72 | var count: Int = 0 73 | _bitcoin160(dataBytes®, data.count, &bytes, &count) 74 | return try! receiveData(bytes: bytes, count: count) |> tagShortHash 75 | } 76 | } 77 | 78 | public func toSHA256HMAC(key: Data) -> (_ data: Data) -> HashDigest { 79 | return { data in 80 | data.withUnsafeBytes { dataBytes in 81 | key.withUnsafeBytes { keyBytes in 82 | var bytes: UnsafeMutablePointer! 83 | var count: Int = 0 84 | _sha256HMAC(dataBytes®, data.count, keyBytes®, key.count, &bytes, &count) 85 | return try! receiveData(bytes: bytes, count: count) |> tagHashDigest 86 | } 87 | } 88 | } 89 | } 90 | 91 | public func toSHA512HMAC(key: Data) -> (_ data: Data) -> LongHash { 92 | return { data in 93 | data.withUnsafeBytes { dataBytes in 94 | key.withUnsafeBytes { keyBytes in 95 | var bytes: UnsafeMutablePointer! 96 | var count: Int = 0 97 | _sha512HMAC(dataBytes®, data.count, keyBytes®, key.count, &bytes, &count) 98 | return try! receiveData(bytes: bytes, count: count) |> tagLongHash 99 | } 100 | } 101 | } 102 | } 103 | 104 | public func toPKCS5PBKDF2HMACSHA512(salt: Data, iterations: Int) -> (_ passphrase: Data) -> LongHash { 105 | return { passphrase in 106 | passphrase.withUnsafeBytes { passphraseBytes in 107 | salt.withUnsafeBytes { saltBytes in 108 | var bytes: UnsafeMutablePointer! 109 | var count: Int = 0 110 | _pkcs5PBKDF2HMACSHA512(passphraseBytes®, passphrase.count, saltBytes®, salt.count, iterations, &bytes, &count) 111 | return try! receiveData(bytes: bytes, count: count) |> tagLongHash 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Math/Signing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Signing.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/28/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | import WolfFoundation 24 | 25 | public func signMessage(with privateKey: ECPrivateKey) -> (_ message: Data) -> ECSignature { 26 | return { message in 27 | message |> toSHA256 |> sign(with: privateKey) 28 | } 29 | } 30 | 31 | public func verifySignature(publicKey: ECPublicKey, signature: ECSignature) -> (_ message: Data) -> Bool { 32 | return { message in 33 | message |> toSHA256 |> verifySignature(publicKey: publicKey, signature: signature) 34 | } 35 | } 36 | 37 | public func sign(with privateKey: ECPrivateKey) -> (_ hash: HashDigest) -> ECSignature { 38 | return { hash in 39 | return hash®.withUnsafeBytes { hashBytes -> ECSignature in 40 | privateKey®.withUnsafeBytes { privateKeyBytes -> ECSignature in 41 | var signature: UnsafeMutablePointer! 42 | var signatureLength = 0 43 | _sign(hashBytes®, privateKeyBytes®, &signature, &signatureLength) 44 | return try! receiveData(bytes: signature, count: signatureLength) |> tagECSignature 45 | } 46 | } 47 | } 48 | } 49 | 50 | public func verifySignature(publicKey: ECPublicKey, signature: ECSignature) -> (_ hash: HashDigest) -> Bool { 51 | return { hash in 52 | return hash®.withUnsafeBytes { hashBytes in 53 | publicKey®.withUnsafeBytes { publicKeyBytes in 54 | signature®.withUnsafeBytes { signatureBytes in 55 | _verifySignature(publicKeyBytes®, publicKey®.count, hashBytes®, signatureBytes®) 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | /// Create a message signature. 63 | public func signMessage(with wif: WIF) -> (_ message: Data) -> String { 64 | return { message in 65 | wif®.withCString { (wifString: UnsafePointer) in 66 | message.withUnsafeBytes { messageBytes in 67 | var signatureBytes: UnsafeMutablePointer! 68 | var signatureLength = 0 69 | _messageSign(messageBytes®, message.count, wifString, &signatureBytes, &signatureLength) 70 | return receiveString(bytes: signatureBytes, count: signatureLength) 71 | } 72 | } 73 | } 74 | } 75 | 76 | /// Validate a message signature. 77 | public func validateMessage(paymentAddress: PaymentAddress, signature: String) -> (_ message: Data) -> Bool { 78 | return { message in 79 | paymentAddress®.withCString { (paymentAddressString: UnsafePointer) in 80 | signature.withCString { (signatureString: UnsafePointer) in 81 | message.withUnsafeBytes { messageBytes in 82 | _messageValidate(paymentAddressString, signatureString, messageBytes®, message.count) 83 | } 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Sources/Bitcoin/SSS/Crypto.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Crypto.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/24/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | 23 | public struct Crypto { 24 | public static var keyLength: Int { return _crypto_stream_keybytes() } 25 | public static var nonceLength: Int { return _crypto_stream_noncebytes() } 26 | 27 | public struct Ciphertext: Codable { 28 | public let message: Data 29 | public let nonce: Data 30 | 31 | public init(message: Data, nonce: Data) { 32 | self.message = message 33 | self.nonce = nonce 34 | } 35 | } 36 | 37 | public static func encrypt(plaintext: Data, key: Data) throws -> Ciphertext { 38 | guard key.count == keyLength else { throw BitcoinError.invalidDataSize } 39 | let messageLength = plaintext.count 40 | var output = Data(count: messageLength) 41 | let nonce = seed(count: nonceLength) 42 | let result = output.withUnsafeMutableBytes { outputBytes in 43 | plaintext.withUnsafeBytes { inputBytes in 44 | nonce.withUnsafeBytes { nonceBytes in 45 | key.withUnsafeBytes { keyBytes in 46 | _crypto_stream_xor(outputBytes®, inputBytes®, messageLength, nonceBytes®, keyBytes®) 47 | } 48 | } 49 | } 50 | } 51 | guard result == 0 else { throw BitcoinError.invalidData } 52 | return Ciphertext(message: output, nonce: nonce) 53 | } 54 | 55 | public static func decrypt(ciphertext: Ciphertext, key: Data) throws -> Data { 56 | guard key.count == keyLength else { throw BitcoinError.invalidDataSize } 57 | guard ciphertext.nonce.count == nonceLength else { throw BitcoinError.invalidDataSize } 58 | let messageLength = ciphertext.message.count 59 | var output = Data(count: messageLength) 60 | let result = output.withUnsafeMutableBytes { outputBytes in 61 | ciphertext.message.withUnsafeBytes { inputBytes in 62 | ciphertext.nonce.withUnsafeBytes { nonceBytes in 63 | key.withUnsafeBytes { keyBytes in 64 | _crypto_stream_xor(outputBytes®, inputBytes®, messageLength, nonceBytes®, keyBytes®) 65 | } 66 | } 67 | } 68 | } 69 | guard result == 0 else { throw BitcoinError.invalidData } 70 | return output 71 | } 72 | 73 | public static func generateKey() -> Data { 74 | return seed(count: keyLength) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Sources/Bitcoin/SSS/SSS.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SSS.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/24/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | 23 | public struct SSS { 24 | public static func randomBytes(count: Int) throws -> Data { 25 | var data = Data(count: count) 26 | let result = data.withUnsafeMutableBytes { 27 | _randombytes($0®, count) 28 | } 29 | guard result == 0 else { 30 | throw BitcoinError.noRandomNumberSource 31 | } 32 | return data 33 | } 34 | 35 | public static func createShares(from message: SSSMessage, shareCount: Int, quorum: Int) -> [SSSShare] { 36 | let outBufferSize = SSSShare.length * shareCount + 1000 37 | var outBuffer = Data(count: outBufferSize) 38 | return outBuffer.withUnsafeMutableBytes { outBytes in 39 | message.data.withUnsafeBytes { messageBytes in 40 | _sss_create_shares(outBytes®, messageBytes®, UInt8(shareCount), UInt8(quorum)) 41 | 42 | var shares = [SSSShare]() 43 | let p = outBytes® 44 | for i in 0 ..< shareCount { 45 | let data = Data(bytes: p + i * SSSShare.length, count: SSSShare.length) 46 | try! shares.append(SSSShare(data: data)) 47 | } 48 | return shares 49 | } 50 | } 51 | } 52 | 53 | public static func combineShares(_ shares: [SSSShare]) -> SSSMessage? { 54 | let inBuffer = shares.reduce(into: Data()) { (buf, share) in buf.append(share.data) } 55 | return inBuffer.withUnsafeBytes { sharesBytes -> SSSMessage? in 56 | var data = Data(count: SSSMessage.length) 57 | let result = data.withUnsafeMutableBytes { messageBytes in 58 | return _sss_combine_shares(messageBytes®, sharesBytes®, UInt8(shares.count)) 59 | } 60 | guard result == 0 else { 61 | return nil 62 | } 63 | return try! SSSMessage(data: data) 64 | } 65 | } 66 | 67 | static let maxMessageSize = 4096 68 | static let shareMetadataSize = 49 69 | 70 | public static func createShares(from message: Data, shareCount: Int, quorum: Int) throws -> [SSSKeyShare] { 71 | let messageSize = message.count 72 | guard messageSize <= maxMessageSize else { 73 | throw BitcoinError.invalidDataSize 74 | } 75 | let shareLength = messageSize + shareMetadataSize 76 | let outBufferLen = shareLength * shareCount 77 | var outData = Data(count: outBufferLen) 78 | return try outData.withUnsafeMutableBytes { outBytes -> [SSSKeyShare] in 79 | return try message.withUnsafeBytes { inBytes -> [SSSKeyShare] in 80 | let result = _sss_create_shares_varlen(outBytes®, inBytes®, messageSize, UInt8(shareCount), UInt8(quorum)) 81 | guard result == 0 else { 82 | throw BitcoinError.invalidDataSize 83 | } 84 | 85 | var shares = [SSSKeyShare]() 86 | let p = outBytes® 87 | let id = seed(count: 16) 88 | for i in 0 ..< shareCount { 89 | let message = Data(bytes: p + i * shareLength, count: shareLength) 90 | shares.append(SSSKeyShare(message: message, id: id)) 91 | } 92 | return shares 93 | } 94 | } 95 | } 96 | 97 | public static func combineShares(_ shares: [SSSKeyShare]) throws -> Data? { 98 | guard !shares.isEmpty else { return nil } 99 | 100 | let shareSize = shares.first!.message.count 101 | guard (shareMetadataSize...(maxMessageSize + shareMetadataSize)).contains(shareSize) else { 102 | throw BitcoinError.invalidDataSize 103 | } 104 | var sharesData = Data() 105 | for share in shares { 106 | let shareData = share.message 107 | guard shareData.count == shareSize else { 108 | throw BitcoinError.invalidDataSize 109 | } 110 | sharesData += shareData 111 | } 112 | let messageSize = shareSize - 49 113 | var message = Data(count: messageSize) 114 | let result: Int32 = message.withUnsafeMutableBytes { messageBytes -> Int32 in 115 | sharesData.withUnsafeBytes { sharesBytes -> Int32 in 116 | _sss_combine_shares_varlen(messageBytes®, sharesBytes®, shareSize, UInt8(shares.count)) 117 | } 118 | } 119 | guard result == 0 else { 120 | return nil 121 | } 122 | return message 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Sources/Bitcoin/SSS/SSSKeyShare.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SSSKeyShare.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/24/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import Foundation 22 | 23 | public struct SSSKeyShare: Codable { 24 | public let message: Data 25 | public let id: Data 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Bitcoin/SSS/SSSMessage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SSSMessage.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/24/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | 24 | public struct SSSMessage { 25 | private typealias `Self` = SSSMessage 26 | 27 | public static let length = _sss_message_length() 28 | 29 | public var data: Data 30 | 31 | public init(data: Data) throws { 32 | guard data.count == Self.length else { 33 | throw BitcoinError.invalidDataSize 34 | } 35 | self.data = data 36 | } 37 | } 38 | 39 | extension SSSMessage: Hashable { 40 | public func hash(into hasher: inout Hasher) { 41 | hasher.combine(data) 42 | } 43 | } 44 | 45 | extension SSSMessage: Equatable { 46 | public static func == (lhs: SSSMessage, rhs: SSSMessage) -> Bool { 47 | return lhs.data == rhs.data 48 | } 49 | } 50 | 51 | extension SSSMessage: CustomStringConvertible { 52 | public var description: String { 53 | return data |> toBase16 |> rawValue 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Bitcoin/SSS/SSSShare.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SSSShare.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/24/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | 24 | public struct SSSShare { 25 | private typealias `Self` = SSSShare 26 | 27 | public static let length = _sss_share_length() 28 | 29 | public let data: Data 30 | 31 | public init(data: Data) throws { 32 | guard data.count == Self.length else { 33 | throw BitcoinError.invalidDataSize 34 | } 35 | self.data = data 36 | } 37 | 38 | public init(base64: Base64) throws { 39 | data = try base64 |> toData 40 | guard data.count == Self.length else { 41 | throw BitcoinError.invalidDataSize 42 | } 43 | } 44 | 45 | public var base64: Base64 { 46 | return data |> toBase64 47 | } 48 | } 49 | 50 | extension SSSShare: Hashable { 51 | public func hash(into hasher: inout Hasher) { 52 | hasher.combine(data) 53 | } 54 | } 55 | 56 | extension SSSShare: Equatable { 57 | public static func == (lhs: SSSShare, rhs: SSSShare) -> Bool { 58 | return lhs.data == rhs.data 59 | } 60 | } 61 | 62 | extension SSSShare: CustomStringConvertible { 63 | public var description: String { 64 | return data |> toBase16 |> rawValue 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Seed.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Seed.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/28/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import Security 22 | import Foundation 23 | 24 | private var randomNumberGenerator = SecureRandomNumberGenerator() 25 | 26 | public final class SecureRandomNumberGenerator: RandomNumberGenerator { 27 | public func next() -> UInt64 { 28 | var result: UInt64 = 0 29 | precondition(SecRandomCopyBytes(kSecRandomDefault, MemoryLayout.size, &result) == errSecSuccess) 30 | return result 31 | } 32 | } 33 | 34 | public func seed(count: Int, using generator: inout T) -> Data where T: RandomNumberGenerator { 35 | var data = Data() 36 | for _ in 0 ..< count { 37 | data.append(UInt8.random(in: UInt8.min ... UInt8.max, using: &generator)) 38 | } 39 | return data 40 | } 41 | 42 | public func seed(count: Int) -> Data { 43 | return seed(count: count, using: &randomNumberGenerator) 44 | } 45 | 46 | public func seed(bits: Int, using generator: inout T) -> Data where T: RandomNumberGenerator { 47 | let extraBits = bits % 8 48 | let count = (bits / 8) + (extraBits > 0 ? 1 : 0) 49 | var s = seed(count: count, using: &generator) 50 | if extraBits > 0 { 51 | let mask = UInt8(0xFF) << (8 - extraBits) 52 | s[count - 1] = s.last! & mask 53 | } 54 | return s 55 | } 56 | 57 | public func seed(bits: Int) -> Data { 58 | return seed(bits: bits, using: &randomNumberGenerator) 59 | } 60 | 61 | public func seed() -> Data { 62 | return seed(count: minimumSeedSize) 63 | } 64 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Util.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Util.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/24/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfFoundation 23 | 24 | final class WrappedInstance { 25 | let instance: OpaquePointer 26 | 27 | init(_ instance: OpaquePointer) { 28 | self.instance = instance 29 | } 30 | 31 | deinit { 32 | _deleteInstance(instance) 33 | } 34 | } 35 | 36 | func receiveString(bytes: UnsafeMutablePointer, count: Int) -> String { 37 | defer { _freeData(bytes) } 38 | let stringData = Data(bytes: bytes, count: count) 39 | return String(data: stringData, encoding: .utf8)! 40 | } 41 | 42 | func receiveData(bytes: UnsafeMutablePointer, count: Int) -> Data { 43 | defer { _freeData(bytes) } 44 | return Data(bytes: bytes, count: count) 45 | } 46 | 47 | protocol InstanceContainer { 48 | init(instance: OpaquePointer) 49 | } 50 | 51 | func receiveInstances(instances: UnsafeMutablePointer, count: Int) -> [T] { 52 | defer { _freeData(instances) } 53 | let buffer = UnsafeBufferPointer(start: instances, count: count) 54 | return buffer.map { T(instance: $0) } 55 | } 56 | 57 | func trim(_ string: String) -> String { 58 | return string.trimmingCharacters(in: .whitespacesAndNewlines) 59 | } 60 | 61 | public postfix func ® (lhs: UnsafeRawBufferPointer) -> UnsafePointer { 62 | return lhs.bindMemory(to: UInt8.self).baseAddress! 63 | } 64 | 65 | public postfix func ® (lhs: UnsafeMutableRawBufferPointer) -> UnsafeMutablePointer { 66 | return lhs.bindMemory(to: UInt8.self).baseAddress! 67 | } 68 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/Asset.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Asset.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 2/2/19. 6 | // 7 | 8 | import WolfPipe 9 | 10 | public typealias PaymentAddressValidator = (PaymentAddress, Network) -> String? 11 | 12 | public struct Asset: Codable { 13 | public let name: String 14 | public let symbol: String 15 | public let network: Network 16 | public let decimalPlaces: Int 17 | public let coinType: CoinType 18 | public let paymentAddressValidator: PaymentAddressValidator? 19 | 20 | public init(name: String, symbol: String, network: Network, decimalPlaces: Int, coinType: CoinType, paymentAddressValidator: PaymentAddressValidator? = nil) { 21 | self.name = name 22 | self.symbol = symbol 23 | self.network = network 24 | self.decimalPlaces = decimalPlaces 25 | self.coinType = coinType 26 | self.paymentAddressValidator = paymentAddressValidator 27 | } 28 | 29 | public init(symbol: String) throws { 30 | guard let asset = assetsBySymbol[symbol] else { 31 | throw BitcoinError.unknownAsset 32 | } 33 | self = asset 34 | } 35 | 36 | public init(from decoder: Decoder) throws { 37 | let container = try decoder.singleValueContainer() 38 | let symbol = try container.decode(String.self) 39 | try self.init(symbol: symbol) 40 | } 41 | 42 | public func encode(to encoder: Encoder) throws { 43 | var container = encoder.singleValueContainer() 44 | try container.encode(symbol) 45 | } 46 | 47 | public func validatePaymentAddress(_ address: PaymentAddress) -> String? { 48 | guard let validator = paymentAddressValidator else { 49 | return "I can't parse that kind of address yet." 50 | } 51 | return validator(address, network) 52 | } 53 | 54 | public func coinType(for network: Network) -> CoinType { 55 | switch network { 56 | case .mainnet: 57 | return coinType 58 | case .testnet: 59 | return .testnet 60 | } 61 | } 62 | } 63 | 64 | extension Asset: Equatable { 65 | public static func == (lhs: Asset, rhs: Asset) -> Bool { 66 | return lhs.symbol == rhs.symbol 67 | } 68 | } 69 | 70 | private func btcPaymentAddressValidator(address: PaymentAddress, network: Network) -> String? { 71 | do { 72 | let wrappedData = try address |> addressDecode 73 | guard let version = PaymentAddressVersion(version: wrappedData.prefix) else { 74 | return "This is not a recognized Bitcoin address." 75 | } 76 | let addressNetwork = version.network 77 | guard addressNetwork == network else { 78 | return "This Bitcoin address is for \(addressNetwork). It may not be used on \(network)." 79 | } 80 | } catch { 81 | return "The Bitcoin address is not valid." 82 | } 83 | return nil 84 | } 85 | 86 | public func identifyAsset(from address: PaymentAddress) -> Asset? { 87 | do { 88 | let wrappedData = try address |> addressDecode 89 | guard let version = PaymentAddressVersion(version: wrappedData.prefix) else { 90 | return nil 91 | } 92 | switch version.network { 93 | case .mainnet: 94 | return .btc 95 | case .testnet: 96 | return .btct 97 | } 98 | } catch { 99 | return nil 100 | } 101 | } 102 | 103 | public let ethDecimalPlaces = 18 104 | 105 | public extension Asset { 106 | static let bch = Asset(name: "Bitcoin Cash", symbol: "BCH", network: .mainnet, decimalPlaces: btcDecimalPlaces, coinType: .bch) 107 | static let btc = Asset(name: "Bitcoin", symbol: "BTC", network: .mainnet, decimalPlaces: btcDecimalPlaces, coinType: .btc, paymentAddressValidator: btcPaymentAddressValidator) 108 | static let btct = Asset(name: "Bitcoin Testnet", symbol: "BTCT", network: .testnet, decimalPlaces: btcDecimalPlaces, coinType: .testnet, paymentAddressValidator: btcPaymentAddressValidator) 109 | static let etc = Asset(name: "Ethereum Classic", symbol: "ETC", network: .mainnet, decimalPlaces: ethDecimalPlaces, coinType: .etc) 110 | static let eth = Asset(name: "Ethereum", symbol: "ETH", network: .mainnet, decimalPlaces: ethDecimalPlaces, coinType: .eth) 111 | static let ltc = Asset(name: "Litecoin", symbol: "LTC", network: .mainnet, decimalPlaces: btcDecimalPlaces, coinType: .ltc) 112 | } 113 | 114 | public var assetsBySymbol: [String: Asset] = [ 115 | "BCH": .bch, 116 | "BTC": .btc, 117 | "BTCT": .btct, 118 | "ETC": .etc, 119 | "ETH": .eth, 120 | "LTC": .ltc 121 | ] 122 | 123 | public func toBase10(_ asset: Asset) -> (_ amount: Fragments) -> Base10 { 124 | { amount in 125 | amount |> toBase10(decimalPlaces: asset.decimalPlaces) 126 | } 127 | } 128 | 129 | public func toFragments(_ asset: Asset, strict: Bool = true) -> (_ base10: Base10) throws -> Fragments { 130 | { base10 in 131 | try base10 |> toFragments(decimalPlaces: asset.decimalPlaces, strict: strict) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/BIP32Path.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BIP32Path.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 2/2/19. 6 | // 7 | 8 | public struct BIP32Path { 9 | public let components: [Component] 10 | 11 | public init(_ components: [Component]) { 12 | self.components = components 13 | } 14 | 15 | public init(_ string: String) throws { 16 | let componentStrings = string.split(separator: "/").map { String($0) } 17 | guard !componentStrings.isEmpty else { throw BitcoinError.invalidFormat } 18 | components = try componentStrings.map { component in 19 | guard let c = Component(component) else { 20 | throw BitcoinError.invalidFormat 21 | } 22 | return c 23 | } 24 | } 25 | 26 | public enum Component { 27 | case master 28 | case index(Index) 29 | 30 | init?(_ string: String) { 31 | if string == "m" { 32 | self = .master 33 | } else { 34 | if let indexComponent = try? Index(string) { 35 | self = .index(indexComponent) 36 | } else { 37 | return nil 38 | } 39 | } 40 | } 41 | } 42 | 43 | public struct Index { 44 | public let index: Int 45 | public let isHardened: Bool 46 | 47 | public init(_ index: Int, isHardened: Bool) { 48 | self.index = index 49 | self.isHardened = isHardened 50 | } 51 | 52 | public init(_ string: String) throws { 53 | guard !string.isEmpty else { throw BitcoinError.invalidFormat } 54 | let s: String 55 | if string.last! == "'" { 56 | isHardened = true 57 | s = String(string.dropLast()) 58 | } else { 59 | isHardened = false 60 | s = string 61 | } 62 | guard let i = Int(s) else { throw BitcoinError.invalidFormat } 63 | index = i 64 | } 65 | } 66 | 67 | public func appending(_ path: BIP32Path) -> BIP32Path { 68 | var c = components 69 | c.append(contentsOf: path.components) 70 | return BIP32Path(c) 71 | } 72 | } 73 | 74 | extension BIP32Path: CustomStringConvertible { 75 | public var description: String { 76 | return components.map({ $0.description }).joined(separator: "/") 77 | } 78 | } 79 | 80 | extension BIP32Path: ExpressibleByArrayLiteral { 81 | public init(arrayLiteral: Component...) { 82 | self.init(arrayLiteral) 83 | } 84 | } 85 | 86 | extension BIP32Path.Component: ExpressibleByStringLiteral { 87 | public init(stringLiteral: String) { 88 | self.init(stringLiteral)! 89 | } 90 | } 91 | 92 | extension BIP32Path.Component: CustomStringConvertible { 93 | public var description: String { 94 | switch self { 95 | case .master: 96 | return "m" 97 | case .index(let i): 98 | if i.isHardened { 99 | return "\(i.index)'" 100 | } else { 101 | return "\(i.index)" 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/ChainType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChainType.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 12/28/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | /// The value of the "change" or "chain type" field referred to in: 22 | /// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki 23 | /// 24 | /// The value of `identity` has been suggested by Christopher Allen as a place to sequester value in use by BTCR DID documents. 25 | /// https://w3c-ccg.github.io/didm-btcr/ 26 | 27 | import Foundation 28 | 29 | public enum ChainType: Int, Codable, CaseIterable { 30 | case external = 0 31 | case change = 1 32 | case identity = 7 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/CoinType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoinType.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 12/28/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | /// This is an extensible enumerated type. This means that additional members of `CoinType` 22 | /// can be added at compile-time by clients of this module by simply extending `CoinType` 23 | /// with additional `public` `static` members as seen below. 24 | /// 25 | /// See also: 26 | /// - Multi-Account Hierarchy for Deterministic Wallets 27 | /// - https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki 28 | 29 | import WolfFoundation 30 | 31 | public struct CoinType: RawRepresentable, Hashable, Comparable, CustomStringConvertible { 32 | public let rawValue: Int 33 | 34 | public init(_ rawValue: Int) { 35 | self.rawValue = rawValue 36 | } 37 | 38 | // RawRepresentable 39 | public init?(rawValue: Int) { self.init(rawValue) } 40 | 41 | // Hashable 42 | public var hashValue: Int { return rawValue.hashValue } 43 | 44 | // CustomStringConvertible 45 | public var description: String { 46 | return String(describing: rawValue) 47 | } 48 | 49 | public static func < (left: CoinType, right: CoinType) -> Bool { 50 | return left® < right® 51 | } 52 | 53 | public static func index(for coinType: CoinType, network: Network) -> Int { 54 | switch network { 55 | case .mainnet: 56 | return coinType® 57 | case .testnet: 58 | return CoinType.testnet® 59 | } 60 | } 61 | } 62 | 63 | /// Please see: 64 | /// SLIP-0044 : Registered coin types for BIP-0044 65 | /// https://github.com/satoshilabs/slips/blob/master/slip-0044.md 66 | extension CoinType { 67 | public static let btc = CoinType(0) 68 | public static let testnet = CoinType(1) // all coin types 69 | public static let ltc = CoinType(2) 70 | public static let eth = CoinType(60) 71 | public static let etc = CoinType(61) 72 | public static let bch = CoinType(145) 73 | } 74 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/ECKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ECKey.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/29/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import Foundation 22 | import WolfPipe 23 | import WolfFoundation 24 | 25 | public class ECKey: RawRepresentable, Codable { 26 | public let rawValue: Data 27 | 28 | public required init(rawValue: Data) { 29 | self.rawValue = rawValue 30 | } 31 | 32 | required public init(from decoder: Decoder) throws { 33 | let container = try decoder.singleValueContainer() 34 | rawValue = try container.decode(Data.self) 35 | } 36 | 37 | public func encode(to encoder: Encoder) throws { 38 | var container = encoder.singleValueContainer() 39 | try container.encode(rawValue) 40 | } 41 | } 42 | 43 | extension ECKey: Hashable { 44 | public func hash(into hasher: inout Hasher) { 45 | hasher.combine(rawValue) 46 | } 47 | } 48 | 49 | extension ECKey: Equatable { 50 | public static func == (lhs: ECKey, rhs: ECKey) -> Bool { 51 | return lhs® == rhs® 52 | } 53 | } 54 | 55 | extension ECKey: CustomStringConvertible { 56 | public var description: String { 57 | return rawValue |> toBase16 |> WolfPipe.rawValue 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/ECPrivateKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ECPrivateKey.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/30/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | 24 | public let ecPrivateKeySize: Int = { return _ecPrivateKeySize() }() 25 | 26 | public class ECPrivateKey: ECKey { 27 | public convenience init() { 28 | try! self.init(seed(count: ecPrivateKeySize)) 29 | } 30 | 31 | public init(_ rawValue: Data) throws { 32 | guard rawValue.count == ecPrivateKeySize else { 33 | throw BitcoinError.invalidDataSize 34 | } 35 | super.init(rawValue: rawValue) 36 | } 37 | 38 | public required init(rawValue: Data) { 39 | super.init(rawValue: rawValue) 40 | } 41 | 42 | required public init(from decoder: Decoder) throws { 43 | try super.init(from: decoder) 44 | } 45 | } 46 | 47 | // MARK: - Free functions 48 | 49 | public func toECPrivateKey(_ data: Data) throws -> ECPrivateKey { 50 | return try ECPrivateKey(data) 51 | } 52 | 53 | /// Create a new EC private key from entropy. 54 | public func newECPrivateKey(_ entropy: Data) throws -> ECPrivateKey { 55 | guard entropy.count >= minimumSeedSize else { 56 | throw BitcoinError.seedTooSmall 57 | } 58 | var privateKeyBytes: UnsafeMutablePointer! 59 | var privateKeyLength: Int = 0 60 | try entropy.withUnsafeBytes { seedBytes in 61 | if let error = BitcoinError(rawValue: _ecNewPrivateKey(seedBytes®, entropy.count, &privateKeyBytes, &privateKeyLength)) { 62 | throw error 63 | } 64 | } 65 | return try ECPrivateKey(receiveData(bytes: privateKeyBytes, count: privateKeyLength)) 66 | } 67 | 68 | public func toECPrivateKey(_ hdPrivateKey: HDKey) throws -> ECPrivateKey { 69 | let ecKey = try hdPrivateKey |> toECKey 70 | guard let ecPrivateKey = ecKey as? ECPrivateKey else { 71 | throw BitcoinError.invalidDerivation 72 | } 73 | return ecPrivateKey 74 | } 75 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/HDKeyPurpose.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HDKeyPurpose.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 2/2/19. 6 | // 7 | 8 | public enum HDKeyPurpose: Int { 9 | case defaultAccount = 0 // BIP32 10 | case accountTree = 44 // BIP44 11 | case multisig = 45 // BIP45 12 | case p2wpkhInP2sh = 49 // BIP49 13 | case p2wpkh = 84 // BIP84 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/HDKeyVersion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HDKeyVersion.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 2/2/19. 6 | // 7 | 8 | import WolfFoundation 9 | 10 | public struct HDKeyVersion { 11 | public let network: Network 12 | public let purpose: HDKeyPurpose? 13 | public let coinType: CoinType 14 | public let publicVersion: UInt32 15 | public let publicPrefix: Base58 16 | public let privateVersion: UInt32 17 | public let privatePrefix: Base58 18 | public let encodings: Set 19 | 20 | // 21 | // Note that version bytes are specific to particular lengths of payloads. 22 | // 23 | // Python code to derive version from prefix is here: 24 | // https://bitcoin.stackexchange.com/questions/28380/i-want-to-generate-a-bip32-version-number-for-namecoin-and-other-altcoins/56639#56639 25 | // 26 | 27 | public init(coinType: CoinType, network: Network, purpose: HDKeyPurpose? = nil, publicVersion: UInt32, publicPrefix: Base58, privateVersion: UInt32, privatePrefix: Base58, encodings: Set) { 28 | self.coinType = coinType 29 | self.network = network 30 | self.purpose = purpose 31 | self.publicVersion = publicVersion 32 | self.publicPrefix = publicPrefix 33 | self.privateVersion = privateVersion 34 | self.privatePrefix = privatePrefix 35 | self.encodings = encodings 36 | } 37 | 38 | public var bip32Path: BIP32Path? { 39 | guard let purpose = purpose else { return nil } 40 | return [ 41 | .index(.init(purpose®, isHardened: true)), 42 | .index(.init(coinType®, isHardened: true)) 43 | ] 44 | } 45 | 46 | public static func findVersion(coinType: CoinType, network: Network, encoding: PaymentAddressEncoding) -> HDKeyVersion? { 47 | return hdKeyVersions.first { $0.coinType == coinType && $0.network == network && $0.encodings.contains(encoding) } 48 | } 49 | } 50 | 51 | public func network(_ version: HDKeyVersion) -> Network { 52 | return version.network 53 | } 54 | 55 | /// SLIP-0132 : Registered HD version bytes for BIP-0032 56 | /// https://github.com/satoshilabs/slips/blob/master/slip-0132.md 57 | public var hdKeyVersions: [HDKeyVersion] = [ 58 | .init(coinType: .btc, network: .mainnet, purpose: .accountTree, publicVersion: 0x0488b21e, publicPrefix: "xpub", privateVersion: 0x0488ade4, privatePrefix: "xprv", encodings: [.p2pkh, .p2sh]), 59 | .init(coinType: .btc, network: .mainnet, purpose: .p2wpkhInP2sh, publicVersion: 0x049d7cb2, publicPrefix: "ypub", privateVersion: 0x049d7878, privatePrefix: "yprv", encodings: [.p2wpkhInP2sh]), 60 | .init(coinType: .btc, network: .mainnet, purpose: .p2wpkh, publicVersion: 0x04b24746, publicPrefix: "zpub", privateVersion: 0x04b2430c, privatePrefix: "zprv", encodings: [.p2wpkh]), 61 | .init(coinType: .btc, network: .mainnet, publicVersion: 0x0295b43f, publicPrefix: "Ypub", privateVersion: 0x0295b005, privatePrefix: "Yprv", encodings: [.p2wshInP2sh]), 62 | .init(coinType: .btc, network: .mainnet, publicVersion: 0x02aa7ed3, publicPrefix: "Zpub", privateVersion: 0x02aa7a99, privatePrefix: "Zprv", encodings: [.p2wsh]), 63 | 64 | .init(coinType: .btc, network: .testnet, purpose: .accountTree, publicVersion: 0x043587cf, publicPrefix: "tpub", privateVersion: 0x04358394, privatePrefix: "tprv", encodings: [.p2pkh, .p2sh]), 65 | .init(coinType: .btc, network: .testnet, purpose: .p2wpkhInP2sh, publicVersion: 0x044a5262, publicPrefix: "upub", privateVersion: 0x044a4e28, privatePrefix: "uprv", encodings: [.p2wpkhInP2sh]), 66 | .init(coinType: .btc, network: .testnet, purpose: .p2wpkh, publicVersion: 0x045f1cf6, publicPrefix: "vpub", privateVersion: 0x045f18bc, privatePrefix: "vprv", encodings: [.p2wpkh]), 67 | .init(coinType: .btc, network: .testnet, publicVersion: 0x024289ef, publicPrefix: "Upub", privateVersion: 0x024285b5, privatePrefix: "Uprv", encodings: [.p2wshInP2sh]), 68 | .init(coinType: .btc, network: .testnet, publicVersion: 0x02575483, publicPrefix: "Vpub", privateVersion: 0x02575048, privatePrefix: "Vprv", encodings: [.p2wsh]), 69 | ] 70 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/HashDigest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HashDigest.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/8/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import Foundation 22 | import WolfFoundation 23 | import WolfPipe 24 | 25 | public enum HashDigestTag { } 26 | public typealias HashDigest = Tagged 27 | 28 | public let nullHashDigest = try! Data(repeatElement(0, count: 32)) |> tagHashDigest 29 | 30 | //// MARK: - Free functions 31 | 32 | public func tagHashDigest(_ data: Data) throws -> HashDigest { 33 | guard data.count == 32 else { 34 | throw BitcoinError.invalidDataSize 35 | } 36 | return HashDigest(rawValue: data) 37 | } 38 | 39 | public func hashEncode(_ hash: HashDigest) -> String { 40 | return hash® |> reversed |> toBase16 |> rawValue 41 | } 42 | 43 | public func hashDecode(_ string: String) throws -> HashDigest { 44 | return try string |> dataLiteral |> reversed |> tagHashDigest 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/LongHash.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LongHash.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 12/1/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import Foundation 22 | import WolfFoundation 23 | 24 | public enum LongHashTag { } 25 | public typealias LongHash = Tagged 26 | 27 | public func tagLongHash(_ data: Data) throws -> LongHash { 28 | guard data.count == 64 else { 29 | throw BitcoinError.invalidDataSize 30 | } 31 | return LongHash(rawValue: data) 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/Mnemonic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mnemonic.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 10/29/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | import WolfFoundation 24 | 25 | public enum MnemonicTag { } 26 | public typealias Mnemonic = Tagged 27 | public func tagMnemonic(_ string: String) -> Mnemonic { return Mnemonic(rawValue: string) } 28 | 29 | /// A valid mnemonic word count is evenly divisible by this number. 30 | public let mnemonicWordMultiple: Int = { return _mnemonicWordMultiple() }() 31 | 32 | /// A valid seed byte count is evenly divisible by this number. 33 | public let mnemonicSeedMultiple: Int = { return _mnemonicSeedMultiple() }() 34 | 35 | public enum Language: String, CaseIterable { 36 | case en 37 | case es 38 | case ja 39 | case it 40 | case fr 41 | case cs 42 | case ru 43 | case uk 44 | case zh_Hans 45 | case zh_Hant 46 | } 47 | 48 | public func wordList(for language: Language) -> [String] { 49 | var wordsBytes: UnsafeMutablePointer! 50 | var wordsBytesLength: Int = 0 51 | if let error = BitcoinError(rawValue: _wordlistForLanguage(language®.cString(using: .utf8)!, &wordsBytes, &wordsBytesLength)) { 52 | fatalError(String(describing: error)) 53 | } 54 | let words = receiveString(bytes: wordsBytes, count: wordsBytesLength) 55 | return words.split(separator: " ").map { String($0) } 56 | } 57 | 58 | public func newMnemonic(language: Language) -> (_ entropy: Data) throws -> Mnemonic { 59 | return { entropy in 60 | guard entropy.count % mnemonicSeedMultiple == 0 else { 61 | throw BitcoinError.invalidSeedSize 62 | } 63 | guard let dictionary = _dictionaryForLanguage(language®.cString(using: .utf8)!) else { 64 | throw BitcoinError.unsupportedLanguage 65 | } 66 | var mnemo: UnsafeMutablePointer! 67 | var mnemoLength: Int = 0 68 | try entropy.withUnsafeBytes { seedBytes in 69 | if let error = BitcoinError(rawValue: _mnemonicNew(seedBytes®, entropy.count, dictionary, &mnemo, &mnemoLength)) { 70 | throw error 71 | } 72 | } 73 | return receiveString(bytes: mnemo, count: mnemoLength) |> tagMnemonic 74 | } 75 | } 76 | 77 | public func newMnemonic(_ entropy: Data) throws -> Mnemonic { 78 | return try newMnemonic(language: .en)(entropy) 79 | } 80 | 81 | public func toSeed(language: Language, passphrase: String? = nil) -> (_ mnemonic: Mnemonic) throws -> Data { 82 | return { mnemonic in 83 | let normalizedMnemonic = mnemonic®.precomposedStringWithCanonicalMapping 84 | let normalizedPassphrase = passphrase?.precomposedStringWithCanonicalMapping 85 | guard let dictionary = _dictionaryForLanguage(language®.cString(using: .utf8)!) else { 86 | throw BitcoinError.unsupportedLanguage 87 | } 88 | var seed: UnsafeMutablePointer! 89 | var seedLength: Int = 0 90 | try normalizedMnemonic.withCString { mnemonicCStr in 91 | if let np = normalizedPassphrase { 92 | try np.withCString { passphraseCStr in 93 | if let error = BitcoinError(rawValue: _mnemonicToSeed(mnemonicCStr, dictionary, passphraseCStr, &seed, &seedLength)) { 94 | throw error 95 | } 96 | } 97 | } else { 98 | if let error = BitcoinError(rawValue: _mnemonicToSeed(mnemonicCStr, dictionary, nil, &seed, &seedLength)) { 99 | throw error 100 | } 101 | } 102 | } 103 | return receiveData(bytes: seed, count: seedLength) 104 | } 105 | } 106 | 107 | public func toSeed(passphrase: String?) -> (_ mnemonic: Mnemonic) throws -> Data { 108 | return { mnemonic in 109 | var seed: Data! 110 | _ = Language.allCases.first { language in 111 | do { 112 | seed = try mnemonic |> toSeed(language: language, passphrase: passphrase) 113 | return true 114 | } catch { 115 | return false 116 | } 117 | } 118 | guard seed != nil else { 119 | throw BitcoinError.invalidFormat 120 | } 121 | return seed 122 | } 123 | } 124 | 125 | public func toSeed(_ mnemonic: Mnemonic) throws -> Data { 126 | return try mnemonic |> toSeed(passphrase: nil) 127 | } 128 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/Network.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Network.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 12/10/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | public enum Network: String, Codable { 22 | case mainnet 23 | case testnet 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/ShortHash.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShortHash.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/15/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import Foundation 22 | import WolfFoundation 23 | 24 | public enum ShortHashTag { } 25 | public typealias ShortHash = Tagged 26 | 27 | public func tagShortHash(_ data: Data) throws -> ShortHash { 28 | guard data.count == 20 else { 29 | throw BitcoinError.invalidDataSize 30 | } 31 | return ShortHash(rawValue: data) 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/WIF.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WIF.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/29/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import Foundation 23 | import WolfFoundation 24 | import WolfPipe 25 | 26 | public enum WIFTag { } 27 | public typealias WIF = Tagged 28 | 29 | public func tagWIF(_ string: String) -> WIF { return WIF(rawValue: string) } 30 | 31 | extension Network { 32 | public var wifVersion: UInt8 { 33 | switch self { 34 | case .mainnet: 35 | return 0x80 36 | case .testnet: 37 | return 0xef 38 | } 39 | } 40 | } 41 | 42 | /// Convert an EC private key to a WIF private key. 43 | public func toWIF(network: Network, isCompressed: Bool = true) -> (_ privateKey: ECPrivateKey) throws -> WIF { 44 | return { privateKey in 45 | var wifBytes: UnsafeMutablePointer! 46 | var wifLength: Int = 0 47 | try privateKey®.withUnsafeBytes { privateKeyBytes in 48 | if let error = BitcoinError(rawValue: _ecPrivateKeyToWIF(privateKeyBytes®, privateKey®.count, network.wifVersion, isCompressed, &wifBytes, &wifLength)) { 49 | throw error 50 | } 51 | } 52 | return receiveString(bytes: wifBytes, count: wifLength) |> tagWIF 53 | } 54 | } 55 | 56 | /// Convert a WIF private key to an EC private key. 57 | public func toECPrivateKey(_ wif: WIF) throws -> ECPrivateKey { 58 | var privateKeyBytes: UnsafeMutablePointer! 59 | var privateKeyLength: Int = 0 60 | try wif®.withCString { wifString in 61 | if let error = BitcoinError(rawValue: _wifToECPrivateKey(wifString, &privateKeyBytes, &privateKeyLength)) { 62 | throw error 63 | } 64 | } 65 | return try ECPrivateKey(receiveData(bytes: privateKeyBytes, count: privateKeyLength)) 66 | } 67 | -------------------------------------------------------------------------------- /Sources/Bitcoin/Wallet/WrappedData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WrappedData.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 11/6/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import CBitcoin 22 | import WolfPipe 23 | 24 | public struct WrappedData: Encodable { 25 | public let prefix: UInt8 26 | public let payload: Data 27 | public let checksum: UInt32 28 | 29 | public enum CodingKeys: String, CodingKey { 30 | case prefix 31 | case payload 32 | case checksum 33 | } 34 | 35 | public func encode(to encoder: Encoder) throws { 36 | var container = encoder.container(keyedBy: CodingKeys.self) 37 | try container.encode(prefix, forKey: .prefix) 38 | try container.encode(payload |> toBase16, forKey: .payload) 39 | try container.encode(checksum, forKey: .checksum) 40 | } 41 | } 42 | 43 | /// Add a version byte and checksum to the data. 44 | public func wrapEncode(version: UInt8) -> (_ data: Data) -> Data { 45 | return { data in 46 | data.withUnsafeBytes { dataBytes in 47 | var wrappedDataBytes: UnsafeMutablePointer! 48 | var wrappedDataLength = 0 49 | _wrapEncode(dataBytes®, data.count, version, &wrappedDataBytes, &wrappedDataLength) 50 | return receiveData(bytes: wrappedDataBytes, count: wrappedDataLength) 51 | } 52 | } 53 | } 54 | 55 | /// Add a version byte (0) and checksum to the data. 56 | public func wrapEncode(_ data: Data) -> Data { 57 | return data |> wrapEncode(version: 0) 58 | } 59 | 60 | /// Validate the checksum of checked data and recover its version and payload. 61 | public func wrapDecode(_ wrappedData: Data) throws -> WrappedData { 62 | return try wrappedData.withUnsafeBytes { wrappedDataBytes in 63 | var prefix: UInt8 = 0 64 | var payloadBytes: UnsafeMutablePointer! 65 | var payloadLength = 0 66 | var checksum: UInt32 = 0 67 | if let error = BitcoinError(rawValue: _wrapDecode(wrappedDataBytes®, wrappedData.count, &prefix, &payloadBytes, &payloadLength, &checksum)) { 68 | throw error 69 | } 70 | let payload = receiveData(bytes: payloadBytes, count: payloadLength) 71 | return WrappedData(prefix: prefix, payload: payload, checksum: checksum) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Sources/BitcoinDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 09/15/2018. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import UIKit 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | @UIApplicationMain 27 | class AppDelegate: UIResponder, UIApplicationDelegate { 28 | var window: UIWindow? 29 | 30 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 31 | testBitcoin() 32 | return true 33 | } 34 | 35 | private func testBitcoin() { 36 | // Should print "true" twice. 37 | print("Nakomoto" |> toUTF8 |> signMessage(with: "KwE19y2Ud8EUEBjeUG4Uc4qWUJUUoZJxHR3xUfTpCSsJEDv2o8fu" |> tagWIF) == "HxQp3cXgOIhBEGXks27sfeSQHVgNUeYgl5i5wG/dOUYaSIRnnzXR6NcyH+AfNAHtkWcyOD9rX4pojqmuQyH79K4=") 38 | print("Nakomoto" |> toUTF8 |> validateMessage(paymentAddress: "1PeChFbhxDD9NLbU21DfD55aQBC4ZTR3tE", signature: "HxQp3cXgOIhBEGXks27sfeSQHVgNUeYgl5i5wG/dOUYaSIRnnzXR6NcyH+AfNAHtkWcyOD9rX4pojqmuQyH79K4=")) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/BitcoinDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Sources/BitcoinDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Sources/BitcoinDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/BitcoinDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | $(PRODUCT_NAME) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Sources/BitcoinDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Bitcoin 4 | // 5 | // Created by Wolf McNally on 09/15/2018. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import UIKit 22 | 23 | class ViewController: UIViewController { 24 | } 25 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestBech32Bis.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestBech32Bis.swift 3 | // BitcoinDemo 4 | // 5 | // Created by Wolf McNally on 1/24/20. 6 | // Copyright © 2020 Blockchain Commons. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Foundation 11 | import Bitcoin 12 | import WolfPipe 13 | import WolfFoundation 14 | 15 | class TestBech32Bis: XCTestCase { 16 | func test1() throws { 17 | let string = "tx1:rqqq-qqqq-qygr-lgl" 18 | let encodedTxRef = string |> tagEncodedTxRef 19 | let txRef = try encodedTxRef |> toDecoded(version: .bech32bis) 20 | XCTAssertEqual(txRef.network, .mainnet) 21 | XCTAssertEqual(txRef.blockHeight, 0) 22 | XCTAssertEqual(txRef.txIndex, 0) 23 | XCTAssertEqual(txRef.outIndex, 0) 24 | XCTAssertEqual(txRef |> toEncoded(version: .bech32bis) |> rawValue, string) 25 | } 26 | 27 | func test2() throws { 28 | let string = "txtest1:xjk0-uqay-zz5s-jae" 29 | let encodedTxRef = string |> tagEncodedTxRef 30 | let txRef = try encodedTxRef |> toDecoded(version: .bech32bis) 31 | XCTAssertEqual(txRef.network, .testnet) 32 | XCTAssertEqual(txRef.blockHeight, 466793) 33 | XCTAssertEqual(txRef.txIndex, 2205) 34 | XCTAssertEqual(txRef.outIndex, 0) 35 | XCTAssertEqual(txRef |> toEncoded(version: .bech32bis) |> rawValue, string) 36 | } 37 | 38 | func test3() throws { 39 | let encodedTxRef = "tx1:yqqq-qqqq-qqqq-f0ng-4y" |> tagEncodedTxRef 40 | // output index encoded as zero not allowed 41 | XCTAssertThrowsError( try encodedTxRef |> toDecoded(version: .bech32bis) ) 42 | } 43 | 44 | func test4() throws { 45 | let string = "txtest1:8jk0-uqay-zu4x-z32g-ap" 46 | let encodedTxRef = string |> tagEncodedTxRef 47 | let txRef = try encodedTxRef |> toDecoded(version: .bech32bis) 48 | XCTAssertEqual(txRef.network, .testnet) 49 | XCTAssertEqual(txRef.blockHeight, 466793) 50 | XCTAssertEqual(txRef.txIndex, 2205) 51 | XCTAssertEqual(txRef.outIndex, 0x1ABC) 52 | XCTAssertEqual(txRef |> toEncoded(version: .bech32bis) |> rawValue, string) 53 | } 54 | } 55 | 56 | // txref = "tx1-rqqq-qqqq-qygr-lgl"; // used to be "tx1-rqqq-qqqq-qmhu-qhp" 57 | // loc = txref::decode(txref); 58 | // EXPECT_EQ(loc.hrp, "tx"); 59 | // EXPECT_EQ(loc.magicCode, txref::MAGIC_BTC_MAIN); // 3 60 | // EXPECT_EQ(loc.blockHeight, 0); 61 | // EXPECT_EQ(loc.transactionPosition, 0); 62 | // EXPECT_EQ(loc.txoIndex, 0); 63 | // 64 | // txref = "txtest1-xjk0-uqay-zz5s-jae"; // used to be "txtest1-xjk0-uqay-zat0-dz8" 65 | // loc = txref::decode(txref); 66 | // EXPECT_EQ(loc.hrp, "txtest"); 67 | // EXPECT_EQ(loc.magicCode, txref::MAGIC_BTC_TEST); // 6 68 | // EXPECT_EQ(loc.blockHeight, 466793); 69 | // EXPECT_EQ(loc.transactionPosition, 2205); 70 | // EXPECT_EQ(loc.txoIndex, 0); 71 | // 72 | // txref = "tx1:yqqq-qqqq-qqqq-f0ng-4y"; // used to be "tx1:yqqq-qqqq-qqqq-ksvh-26" 73 | // loc = txref::decode(txref); 74 | // EXPECT_EQ(loc.hrp, "tx"); 75 | // EXPECT_EQ(loc.magicCode, txref::MAGIC_BTC_MAIN_EXTENDED); // 4 76 | // EXPECT_EQ(loc.blockHeight, 0); 77 | // EXPECT_EQ(loc.transactionPosition, 0); 78 | // EXPECT_EQ(loc.txoIndex, 0); 79 | // 80 | // txref = "txtest1:8jk0-uqay-zu4x-z32g-ap"; // used to be "txtest1:8jk0-uqay-zu4x-aw4h-zl" 81 | // loc = txref::decode(txref); 82 | // EXPECT_EQ(loc.hrp, "txtest"); 83 | // EXPECT_EQ(loc.magicCode, txref::MAGIC_BTC_TEST_EXTENDED); // 7 84 | // EXPECT_EQ(loc.blockHeight, 466793); 85 | // EXPECT_EQ(loc.transactionPosition, 2205); 86 | // EXPECT_EQ(loc.txoIndex, 0x1ABC); 87 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestEC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestEC.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 10/30/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestEC: XCTestCase { 27 | func testECPrivateKeyNew() { 28 | let f = tagBase16 >>> toData >>> newECPrivateKey >>> rawValue >>> toBase16 29 | XCTAssert(try! "baadf00dbaadf00dbaadf00dbaadf00d" |> f == "8ed1d17dabce1fccbbe5e9bf008b318334e5bcc78eb9e7c1ea850b7eb0ddb9c8") 30 | XCTAssertThrowsError(try "baadf00dbaadf00d" |> f == "won't happen") // seed too short 31 | } 32 | 33 | func testToECPublicKey() { 34 | let f = { tagBase16 >>> toData >>> toECPrivateKey >>> toECPublicKey(isCompressed: $0) >>> rawValue >>> toBase16 } 35 | XCTAssert(try! "4c721ccd679b817ea5e86e34f9d46abb1660a63955dde908702214eaab038475" |> f(true) == "03ac9e60013853128b42a1324609bac2ccff6a0b4844b6301f1f552e15ee14c7a5") 36 | XCTAssert(try! "8ed1d17dabce1fccbbe5e9bf008b318334e5bcc78eb9e7c1ea850b7eb0ddb9c8" |> f(true) == "0247140d2811498679fe9a0467a75ac7aa581476c102d27377bc0232635af8ad36") 37 | XCTAssert(try! "8ed1d17dabce1fccbbe5e9bf008b318334e5bcc78eb9e7c1ea850b7eb0ddb9c8" |> f(false) == "0447140d2811498679fe9a0467a75ac7aa581476c102d27377bc0232635af8ad36e87bb04f401be3b770a0f3e2267a6c3b14a3074f6b5ce4419f1fcdc1ca4b1cb6") 38 | } 39 | 40 | func testToECPaymentAddress() { 41 | let f = { (network: Network, type: ECPaymentAddressType) in tagBase16 >>> toData >>> toECPublicKey >>> toECPaymentAddress(network: network, type: type) } 42 | XCTAssert(try! "0247140d2811498679fe9a0467a75ac7aa581476c102d27377bc0232635af8ad36" |> f(.mainnet, .p2kh) == "1EKJFK8kBmasFRYY3Ay9QjpJLm4vemJtC1") 43 | XCTAssert(try! "0247140d2811498679fe9a0467a75ac7aa581476c102d27377bc0232635af8ad36" |> f(.testnet, .p2kh) == "mtqFYNDizo282Y29kjwXEf2dCkfdZZydbf") 44 | XCTAssert(try! "0447140d2811498679fe9a0467a75ac7aa581476c102d27377bc0232635af8ad36e87bb04f401be3b770a0f3e2267a6c3b14a3074f6b5ce4419f1fcdc1ca4b1cb6" |> f(.mainnet, .p2kh) == "197FLrycah42jKDgfmTaok7b8kNHA7R2ih") 45 | XCTAssert(try! "0447140d2811498679fe9a0467a75ac7aa581476c102d27377bc0232635af8ad36e87bb04f401be3b770a0f3e2267a6c3b14a3074f6b5ce4419f1fcdc1ca4b1cb6" |> f(.testnet, .p2kh) == "modCdv4bPiVHWRhJPLRxdfKuzjxz275cah") 46 | } 47 | 48 | func testToWIF() { 49 | let f = tagBase16 >>> toData >>> toECPrivateKey >>> toWIF(network: .mainnet) 50 | let g = { tagBase16 >>> toData >>> toECPrivateKey >>> toWIF(network: .mainnet, isCompressed: $0) } 51 | XCTAssert(try! "8ed1d17dabce1fccbbe5e9bf008b318334e5bcc78eb9e7c1ea850b7eb0ddb9c8" |> f == "L21LJEeJwK35wby1BeTjwWssrhrgQE2MZrpTm2zbMC677czAHHu3") 52 | XCTAssert(try! "8ed1d17dabce1fccbbe5e9bf008b318334e5bcc78eb9e7c1ea850b7eb0ddb9c8" |> g(false) == "5JuBiWpsjfXNxsWuc39KntBAiAiAP2bHtrMGaYGKCppq4MuVcQL") 53 | XCTAssert(try! "0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D" |> g(false) == "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ") 54 | } 55 | 56 | func testWIFToECPrivateKey() { 57 | let f = tagWIF >>> toECPrivateKey >>> rawValue >>> toBase16 58 | XCTAssert(try! "L21LJEeJwK35wby1BeTjwWssrhrgQE2MZrpTm2zbMC677czAHHu3" |> f == "8ed1d17dabce1fccbbe5e9bf008b318334e5bcc78eb9e7c1ea850b7eb0ddb9c8") 59 | XCTAssert(try! "5JuBiWpsjfXNxsWuc39KntBAiAiAP2bHtrMGaYGKCppq4MuVcQL" |> f == "8ed1d17dabce1fccbbe5e9bf008b318334e5bcc78eb9e7c1ea850b7eb0ddb9c8") 60 | XCTAssert(try! "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ" |> f == "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d") 61 | } 62 | 63 | func testWIFToECPublicKey() { 64 | let f = tagWIF >>> toECPrivateKey >>> toECPublicKey >>> rawValue >>> toBase16 65 | let g = { tagWIF >>> toECPrivateKey >>> toECPublicKey(isCompressed: $0) >>> rawValue >>> toBase16 } 66 | XCTAssert(try! "L21LJEeJwK35wby1BeTjwWssrhrgQE2MZrpTm2zbMC677czAHHu3" |> f == "0247140d2811498679fe9a0467a75ac7aa581476c102d27377bc0232635af8ad36") 67 | XCTAssert(try! "5JuBiWpsjfXNxsWuc39KntBAiAiAP2bHtrMGaYGKCppq4MuVcQL" |> g(false) == "0447140d2811498679fe9a0467a75ac7aa581476c102d27377bc0232635af8ad36e87bb04f401be3b770a0f3e2267a6c3b14a3074f6b5ce4419f1fcdc1ca4b1cb6") 68 | XCTAssert(try! "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ" |> g(false) == "04d0de0aaeaefad02b8bdc8a01a1b8b11c696bd3d66a2c5f10780d95b7df42645cd85228a6fb29940e858e7e55842ae2bd115d1ed7cc0e82d934e929c97648cb0a") 69 | } 70 | 71 | func testMessageSign() { 72 | XCTAssert("Nakomoto" |> toUTF8 |> signMessage(with: "KwE19y2Ud8EUEBjeUG4Uc4qWUJUUoZJxHR3xUfTpCSsJEDv2o8fu" |> tagWIF) == "HxQp3cXgOIhBEGXks27sfeSQHVgNUeYgl5i5wG/dOUYaSIRnnzXR6NcyH+AfNAHtkWcyOD9rX4pojqmuQyH79K4=") 73 | XCTAssert("Nakomoto" |> toUTF8 |> signMessage(with: "5HpMRgt5u8yyU1AfPwcgLGphD5Qu4ka4v7McE4jKrGNpQPyRqXC" |> tagWIF) == "GxQp3cXgOIhBEGXks27sfeSQHVgNUeYgl5i5wG/dOUYaSIRnnzXR6NcyH+AfNAHtkWcyOD9rX4pojqmuQyH79K4=") 74 | } 75 | 76 | func testMessageValidate() { 77 | // Compressed 78 | XCTAssert("Nakomoto" |> toUTF8 |> validateMessage(paymentAddress: "1PeChFbhxDD9NLbU21DfD55aQBC4ZTR3tE", signature: "HxQp3cXgOIhBEGXks27sfeSQHVgNUeYgl5i5wG/dOUYaSIRnnzXR6NcyH+AfNAHtkWcyOD9rX4pojqmuQyH79K4=")) 79 | // Uncompressed 80 | XCTAssert("Nakomoto" |> toUTF8 |> validateMessage(paymentAddress: "1Em1SX7qQq1pTmByqLRafhL1ypx2V786tP", signature: "GxQp3cXgOIhBEGXks27sfeSQHVgNUeYgl5i5wG/dOUYaSIRnnzXR6NcyH+AfNAHtkWcyOD9rX4pojqmuQyH79K4=")) 81 | // Compressed-invalid 82 | XCTAssertFalse("Satoshi" |> toUTF8 |> validateMessage(paymentAddress: "1PeChFbhxDD9NLbU21DfD55aQBC4ZTR3tE", signature: "HxQp3cXgOIhBEGXks27sfeSQHVgNUeYgl5i5wG/dOUYaSIRnnzXR6NcyH+AfNAHtkWcyOD9rX4pojqmuQyH79K4=")) 83 | } 84 | 85 | func testECKeyHashable() { 86 | let key1 = ECPrivateKey() 87 | let key2 = ECPrivateKey() 88 | let s: Set = [key1, key2] 89 | XCTAssert(s.contains(key1)) 90 | XCTAssert(s.contains(key2)) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestEllipticCurve.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestEllipticCurve.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 11/28/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestEllipticCurve: XCTestCase { 27 | // Scenario 1 28 | let SECRET1 = try! "8010b1bb119ad37d4b65a1022a314897b1b3614b345974332cb1b9582cf03536" |> dataLiteral |> toECPrivateKey 29 | let COMPRESSED1 = try! "0309ba8621aefd3b6ba4ca6d11a4746e8df8d35d9b51b383338f627ba7fc732731" |> dataLiteral |> toECPublicKey 30 | let UNCOMPRESSED1 = try! "0409ba8621aefd3b6ba4ca6d11a4746e8df8d35d9b51b383338f627ba7fc7327318c3a6ec6acd33c36328b8fb4349b31671bcd3a192316ea4f6236ee1ae4a7d8c9" |> dataLiteral |> toECPublicKey 31 | 32 | // Scenario 2 33 | let COMPRESSED2 = try! "03bc88a1bd6ebac38e9a9ed58eda735352ad10650e235499b7318315cc26c9b55b" |> dataLiteral |> toECPublicKey 34 | let SIGHASH2 = "ed8f9b40c2d349c8a7e58cebe79faa25c21b6bb85b874901f72a1b3f1ad0a67f" |> dataLiteral 35 | let SIGNATURE2 = "3045022100bc494fbd09a8e77d8266e2abdea9aef08b9e71b451c7d8de9f63cda33a62437802206b93edd6af7c659db42c579eb34a3a4cb60c28b5a6bc86fd5266d42f6b8bb67d" |> dataLiteral 36 | 37 | // Scenario 3 38 | let SECRET3 = try! "ce8f4b713ffdd2658900845251890f30371856be201cd1f5b3d970f793634333" |> dataLiteral |> reversed |> toECPrivateKey 39 | let SIGHASH3 = try! "f89572635651b2e4f89778350616989183c98d1a721c911324bf9f17a0cf5bf0" |> dataLiteral |> reversed |> tagHashDigest 40 | let EC_SIGNATURE3 = try! "4832febef8b31c7c922a15cb4063a43ab69b099bba765e24facef50dfbb4d057928ed5c6b6886562c2fe6972fd7c7f462e557129067542cce6b37d72e5ea5037" |> dataLiteral |> tagECSignature 41 | let DER_SIGNATURE3 = try! "3044022057d0b4fb0df5cefa245e76ba9b099bb63aa46340cb152a927c1cb3f8befe324802203750eae5727db3e6cc4275062971552e467f7cfd7269fec2626588b6c6d58e92" |> dataLiteral |> tagDERSignature 42 | 43 | // EC_SUM 44 | let GENERATOR_POINT_MULT_4 = "02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13" |> dataLiteral 45 | 46 | func test_secret_to_public__positive() { 47 | XCTAssertEqual(try! SECRET1 |> toECPublicKey, COMPRESSED1) 48 | } 49 | 50 | func test_decompress__positive() { 51 | let uncompressed = try! COMPRESSED1 |> decompress 52 | XCTAssertEqual(uncompressed, UNCOMPRESSED1) 53 | let compressed = try! uncompressed |> compress 54 | XCTAssertEqual(compressed, COMPRESSED1) 55 | 56 | // Redundant compression/decompression 57 | XCTAssertEqual(try! COMPRESSED1 |> compress, compressed) 58 | XCTAssertEqual(try! UNCOMPRESSED1 |> decompress, uncompressed) 59 | } 60 | 61 | func test_sign__positive() { 62 | XCTAssertEqual(SIGHASH3 |> sign(with: SECRET3), EC_SIGNATURE3) 63 | } 64 | 65 | func test_encode_signature__positive() { 66 | XCTAssertEqual(try! EC_SIGNATURE3 |> toDERSignature, DER_SIGNATURE3) 67 | } 68 | 69 | func testSignMessage() { 70 | let message = "The quick brown 🦊 jumps over the lazy 🐶." |> toUTF8 71 | let privateKey = ECPrivateKey() 72 | let signature = message |> signMessage(with: privateKey) 73 | let publicKey = try! privateKey |> toECPublicKey 74 | XCTAssertTrue(message |> verifySignature(publicKey: publicKey, signature: signature)) 75 | } 76 | 77 | func testInvalidKeyLengths() { 78 | let data = try! "01020304" |> tagHex |> toData 79 | XCTAssertThrowsError(try data |> toECPrivateKey) 80 | XCTAssertThrowsError(try data |> toECPublicKey) 81 | } 82 | } 83 | 84 | // BOOST_AUTO_TEST_CASE(elliptic_curve__encode_signature__positive__test) 85 | // { 86 | // der_signature out; 87 | // const ec_signature signature = base16_literal(EC_SIGNATURE3); 88 | // BOOST_REQUIRE(encode_signature(out, signature)); 89 | // 90 | // const auto result = encode_base16(out); 91 | // BOOST_REQUIRE_EQUAL(result, DER_SIGNATURE3); 92 | // } 93 | 94 | // BOOST_AUTO_TEST_CASE(elliptic_curve__sign__positive__test) 95 | // { 96 | // ec_signature signature; 97 | // const ec_secret secret = hash_literal(SECRET3); 98 | // const hash_digest sighash = hash_literal(SIGHASH3); 99 | // BOOST_REQUIRE(sign(signature, secret, sighash)); 100 | // 101 | // const auto result = encode_base16(signature); 102 | // BOOST_REQUIRE_EQUAL(result, EC_SIGNATURE3); 103 | // } 104 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestHash.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestHash.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 10/25/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestHash: XCTestCase { 27 | func testHashSizes() { 28 | XCTAssertNoThrow(try "8099ed424ffa0d81a42f75b61aff1f22ef7bfd11" |> tagBase16 |> toData |> tagShortHash) 29 | XCTAssertThrowsError(try "00112233" |> tagBase16 |> toData |> tagShortHash) 30 | 31 | XCTAssertNoThrow(try "b34ae13c097ec7a206b515b0cc3ff4b2c2f4e0fce30298604be140cdc7a76b74" |> tagBase16 |> toData |> tagHashDigest) 32 | XCTAssertThrowsError(try "00112233" |> tagBase16 |> toData |> tagHashDigest) 33 | 34 | XCTAssertNoThrow(try "a26171d120b6e921e28c98c94a8a9ef540bb642b1da24b7aed830401d180ee62f2297a0f91f6b218539f04ae8db70779e7241e66e9d92f97bf75d20d6b23720a" |> tagBase16 |> toData |> tagLongHash) 35 | XCTAssertThrowsError(try "00112233" |> tagBase16 |> toData |> tagLongHash) 36 | } 37 | 38 | func testHash() { 39 | let message = "The quick brown 🦊 jumps over the lazy 🐶." |> toUTF8 40 | XCTAssert(message |> toRIPEMD160 |> rawValue |> toBase16 == "8099ed424ffa0d81a42f75b61aff1f22ef7bfd11") 41 | XCTAssert(message |> toSHA160 |> rawValue |> toBase16 == "0c248790270625fabd126d61bffb78625bf191c7") 42 | XCTAssert(message |> toSHA256 |> rawValue |> toBase16 == "b34ae13c097ec7a206b515b0cc3ff4b2c2f4e0fce30298604be140cdc7a76b74") 43 | XCTAssert(message |> toSHA512 |> rawValue |> toBase16 == "a26171d120b6e921e28c98c94a8a9ef540bb642b1da24b7aed830401d180ee62f2297a0f91f6b218539f04ae8db70779e7241e66e9d92f97bf75d20d6b23720a") 44 | XCTAssert(message |> toBitcoin256 |> rawValue |> toBase16 == "032ba0f612f1564b19df42bbb611f6ca0bbc2a5a7553e576babeb5f88429cb2a") 45 | XCTAssert(message |> toBitcoin160 |> rawValue |> toBase16 == "87c1d0bb4d6148c837463b3bedc72c260d2dcff5") 46 | 47 | let key = "secret" |> toUTF8 48 | XCTAssert(message |> toSHA256HMAC(key: key) |> rawValue |> toBase16 == "16a1b60714655152de8b7360422b4843aaa6d603882295a87ef316ef1c297be9") 49 | XCTAssert(message |> toSHA512HMAC(key: key) |> rawValue |> toBase16 == "1bd5a8a845dae3b4b5cd125d77336d3d0b899b78e7446788942849d61e9b221caf4d79d39ac1b3fbc9edee3743cbfcda693b1d76015ee771d99f1284b22cdfe1") 50 | 51 | let passphrase = "password" |> toUTF8 52 | let salt = "salt" |> toUTF8 53 | let data: Base16 = "fef7276b107040a0a713bcbec9fd3e191cc6153249e245a3e1a22087dbe616060bbfc8411c6363f3c10ab5d02a56c38e2066a4e205b0ca8f959fd731e5fa584b" 54 | XCTAssert(passphrase |> toPKCS5PBKDF2HMACSHA512(salt: salt, iterations: 100) |> rawValue |> toBase16 == data) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestInput.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 11/12/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestInput: XCTestCase { 27 | 28 | let validRawInput = ("54b755c39207d443fd96a8d12c94446a1c6f66e39c95e894c23418d7501f681b01000" + 29 | "0006b48304502203267910f55f2297360198fff57a3631be850965344370f732950b4" + 30 | "7795737875022100f7da90b82d24e6e957264b17d3e5042bab8946ee5fc676d15d915" + 31 | "da450151d36012103893d5a06201d5cf61400e96fa4a7514fc12ab45166ace618d68b" + 32 | "8066c9c585f9ffffffff") |> dataLiteral 33 | 34 | func test1() { 35 | let input = Input() 36 | XCTAssertFalse(input.isValid) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestMnemonic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestMnemonic.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 10/29/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestMnemonic: XCTestCase { 27 | func testNewMnemonic() { 28 | func test(_ seed: String, _ language: Language, _ mnemonic: String) -> Bool { 29 | let a = seed 30 | return try! a |> dataLiteral |> newMnemonic(language: language) |> rawValue == mnemonic 31 | } 32 | 33 | XCTAssert(test("baadf00dbaadf00d", .en, "rival hurdle address inspire tenant alone")) 34 | XCTAssert(test("baadf00dbaadf00dbaadf00dbaadf00d", .en, "rival hurdle address inspire tenant almost turkey safe asset step lab boy")) 35 | XCTAssertThrowsError(try "baadf00dbaadf00dbaadf00dbaadf00dff" |> dataLiteral |> newMnemonic(language: .en) == "won't happen") // Invalid seed size 36 | XCTAssert(test("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", .en, "legal winner thank year wave sausage worth useful legal winner thank yellow")) 37 | XCTAssert(test("baadf00dbaadf00dbaadf00dbaadf00d", .es, "previo humilde actuar jarabe tabique ahorro tope pulpo anís señal lavar bahía")) 38 | 39 | XCTAssert(test("baadf00dbaadf00dbaadf00dbaadf00d", .fr, "placard garantir acerbe gratuit soluble affaire théorie ponctuel anguleux salon horrible bateau")) 40 | XCTAssert(test("baadf00dbaadf00dbaadf00dbaadf00d", .it, "rizoma lastra affabile lucidato sultano algebra tramonto rupe annuncio sonda mega bavosa")) 41 | XCTAssert(test("baadf00dbaadf00dbaadf00dbaadf00d", .ja, "ねんかん すずしい あひる せたけ ほとんど あんまり めいあん のべる いなか ふとる ぜんりゃく えいせい")) 42 | XCTAssert(test("baadf00dbaadf00dbaadf00dbaadf00d", .cs, "semeno mudrc babka nasekat uvolnit bazuka vydra skanzen broskev trefit nuget datel")) 43 | XCTAssert(test("baadf00dbaadf00dbaadf00dbaadf00d", .ru, "ремарка кривой айсберг лауреат тротуар амнезия фонтан рояль бакалея сухой магазин бунт")) 44 | XCTAssert(test("baadf00dbaadf00dbaadf00dbaadf00d", .uk, "сержант ледачий актив люкс фах арена цемент слон бесіда тротуар мандри верба")) 45 | XCTAssert(test("baadf00dbaadf00dbaadf00dbaadf00d", .zh_Hans, "博 肉 地 危 惜 多 陪 荒 因 患 伊 基")) 46 | XCTAssert(test("baadf00dbaadf00dbaadf00dbaadf00d", .zh_Hant, "博 肉 地 危 惜 多 陪 荒 因 患 伊 基")) 47 | } 48 | 49 | func testMnemonicToSeed() { 50 | func test(_ m: String, seed: String) -> Bool { 51 | return try! m |> tagMnemonic |> toSeed |> toBase16 |> rawValue == seed 52 | } 53 | 54 | XCTAssert(test("rival hurdle address inspire tenant alone", seed: "33498afc5ef71e87afd7cad1e50a9d9adb9e30d3ca4b1da5dc370d266aa7796cbc1854eebce5ab3fd3b02b6625e2a82868dbb693e988e47d74106f04c76a6263")) 55 | } 56 | 57 | func testMnemonicToSeedWithPassphrase() { 58 | func test(_ m: String, passphrase: String, seed: String) -> Bool { 59 | return try! m |> tagMnemonic |> toSeed(passphrase: passphrase) |> toBase16 |> rawValue == seed 60 | } 61 | 62 | XCTAssert(test("legal winner thank year wave sausage worth useful legal winner thank yellow", passphrase: "TREZOR", seed: "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607")) 63 | XCTAssert(test("legal winner thank year wave sausage worth useful legal winner thank yellow", passphrase: "博 肉 地 危 惜 多 陪 荒 因 患 伊 基", seed: "3e52585ea1275472a82fa0dcd84121e742140f64a302eca7c390832ba428c707a7ebf449267ae592c51f1740259226e31520de39fd8f33e08788fd21221c6f4e")) 64 | XCTAssert(test("previo humilde actuar jarabe tabique ahorro tope pulpo anís señal lavar bahía", passphrase: "博 肉 地 危 惜 多 陪 荒 因 患 伊 基", seed: "e72505021b97e15171fe09e996898888579c4196c445d7629762c5b09586e3fb3d68380120b8d8a6ed6f9a73306dab7bf54127f3a610ede2a2d5b4e59916ac73")) 65 | } 66 | 67 | func testWordList() throws { 68 | let words = wordList(for: .en) 69 | XCTAssertEqual(words.count, 2048) 70 | 71 | func wordsWithPrefix(_ prefix: String) -> [String] { 72 | return words.filter { $0.hasPrefix(prefix) } 73 | } 74 | 75 | XCTAssertEqual(wordsWithPrefix("af"), ["affair", "afford", "afraid"]) 76 | XCTAssertEqual(wordsWithPrefix("zo"), ["zone", "zoo"]) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestOpcode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestOpcode.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 11/12/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestOpcode: XCTestCase { 27 | func testToString1() { 28 | func f(_ op: ScriptOpcode, _ expected: String) -> Bool { 29 | return op |> toString(rules: .allRules) == expected 30 | && op |> toString(rules: .noRules) == expected 31 | } 32 | 33 | XCTAssert(f(.pushSize0, "zero")) 34 | XCTAssert(f(.pushSize42, "push_42")) 35 | XCTAssert(f(.pushOneSize, "pushdata1")) 36 | XCTAssert(f(.pushTwoSize, "pushdata2")) 37 | XCTAssert(f(.pushFourSize, "pushdata4")) 38 | XCTAssert(f(.reserved80, "reserved")) 39 | XCTAssert(f(.pushPositive7, "7")) 40 | XCTAssert(f(.reserved98, "ver")) 41 | XCTAssert(f(.reserved186, "0xba")) 42 | XCTAssert(f(.reserved255, "0xff")) 43 | } 44 | 45 | func testToString2() { 46 | XCTAssert(ScriptOpcode.checklocktimeverify |> toString(rules: .noRules) == "nop2") 47 | XCTAssert(ScriptOpcode.checklocktimeverify |> toString(rules: .bip65Rule) == "checklocktimeverify") 48 | 49 | XCTAssert(ScriptOpcode.checksequenceverify |> toString(rules: .noRules) == "nop3") 50 | XCTAssert(ScriptOpcode.checksequenceverify |> toString(rules: .bip112Rule) == "checksequenceverify") 51 | } 52 | 53 | func testToOpcode() { 54 | func f(_ s: String, _ op: ScriptOpcode) -> Bool { 55 | return try! s |> toOpcode == op 56 | } 57 | 58 | // zero 59 | XCTAssert(f("zero", .pushSize0)) 60 | XCTAssert(f("push_0", .pushSize0)) 61 | XCTAssert(f("0", .pushSize0)) 62 | 63 | // push n (special) 64 | XCTAssert(f("push_1", .pushSize1)) 65 | XCTAssert(f("push_75", .pushSize75)) 66 | 67 | // push n byte size (pushdata) 68 | XCTAssert(f("push_one", .pushOneSize)) 69 | XCTAssert(f("pushdata1", .pushOneSize)) 70 | 71 | XCTAssert(f("push_two", .pushTwoSize)) 72 | XCTAssert(f("pushdata2", .pushTwoSize)) 73 | 74 | XCTAssert(f("push_four", .pushFourSize)) 75 | XCTAssert(f("pushdata4", .pushFourSize)) 76 | } 77 | 78 | func testToHexadecimal() { 79 | XCTAssert(ScriptOpcode.pushSize0® == 0x00) 80 | XCTAssert(ScriptOpcode.pushSize42® == 0x2a) 81 | XCTAssert(ScriptOpcode.reserved255® == 0xff) 82 | } 83 | 84 | func testHexadecimalToOpcode() { 85 | XCTAssertThrowsError(try "0001" |> tagBase16 |> toData |> toOpcode) 86 | XCTAssert(try! "ff" |> tagBase16 |> toData |> toOpcode == .reserved255) 87 | XCTAssert(try! "FE" |> tagBase16 |> toData |> toOpcode == .reserved254) 88 | XCTAssert(try! "Fe" |> tagBase16 |> toData |> toOpcode == .reserved254) 89 | XCTAssert(try! "42" |> tagBase16 |> toData |> toOpcode == .pushSize66) 90 | XCTAssert(try! "4f" |> tagBase16 |> toData |> toOpcode == .pushNegative1) 91 | } 92 | 93 | func testInvalidOpcodeMnemonic() { 94 | XCTAssertThrowsError(try "FOO" |> toOpcode) 95 | } 96 | 97 | func testOpcodeDescription() { 98 | let opcode = try! "nop" |> toOpcode 99 | XCTAssertEqual(opcode.description, "nop") 100 | XCTAssertEqual(opcode |> toString, "nop") 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestOperation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestOperation.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 11/14/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestOperation: XCTestCase { 27 | func test1() { 28 | let operation = ScriptOperation() 29 | XCTAssertFalse(operation.isValid) 30 | XCTAssert(operation.data.isEmpty) 31 | XCTAssert(operation.opcode == .disabledXor) 32 | } 33 | 34 | func test2() { 35 | let data = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" |> dataLiteral 36 | let operation = try! ScriptOperation(data) 37 | XCTAssert(operation.isValid) 38 | XCTAssert(operation.opcode == .pushSize32) 39 | XCTAssert(operation.data == data) 40 | } 41 | 42 | func test3() { 43 | let data = "23156214" |> dataLiteral 44 | let operation = try! ScriptOperation(data) 45 | XCTAssert(operation.isValid) 46 | let operation2 = operation 47 | XCTAssert(operation == operation2) 48 | } 49 | 50 | func test4() { 51 | let data = Data() 52 | XCTAssertThrowsError(try ScriptOperation.deserialize(data)) 53 | } 54 | 55 | func test5() { 56 | let rawOperation = "00" |> dataLiteral 57 | let operation = try! ScriptOperation.deserialize(rawOperation) 58 | XCTAssert(operation.isValid) 59 | XCTAssert(operation.serialized == rawOperation) 60 | 61 | let duplicate = try! ScriptOperation.deserialize(operation.serialized) 62 | XCTAssert(operation == duplicate) 63 | 64 | XCTAssert(operation.opcode == .pushSize0) 65 | XCTAssert(operation.data.isEmpty) 66 | } 67 | 68 | func test6() { 69 | let data75 = Data(repeating: ".".asciiByte, count: 75) 70 | let rawOperation = ("4b" |> dataLiteral) + data75 71 | let operation = try! ScriptOperation.deserialize(rawOperation) 72 | XCTAssert(operation.isValid) 73 | XCTAssert(operation.serialized == rawOperation) 74 | 75 | let duplicate = try! ScriptOperation.deserialize(operation.serialized) 76 | XCTAssert(operation == duplicate) 77 | 78 | XCTAssert(operation.opcode == .pushSize75) 79 | XCTAssert(operation.data == data75) 80 | } 81 | 82 | func test7() { 83 | func f(_ opcode: ScriptOpcode, _ s: String) -> Bool { 84 | return opcode |> toOperation |> toString == s 85 | } 86 | XCTAssert(f(.pushSize0, "zero")) 87 | XCTAssert(f(.pushSize75, "push_75")) 88 | XCTAssert(f(.pushPositive7, "7")) 89 | } 90 | 91 | func test8() { 92 | XCTAssert(try! "07" |> dataLiteral |> toOperation(isMinimal: true) |> toString == "7") 93 | XCTAssert(try! "07" |> dataLiteral |> toOperation(isMinimal: false) |> toString == "[07]") 94 | XCTAssert(try! "42" |> dataLiteral |> toOperation |> toString == "[42]") 95 | XCTAssert(try! "112233" |> dataLiteral |> toOperation |> toString == "[112233]") 96 | } 97 | 98 | func test9() { 99 | XCTAssert(try! "03112233" |> dataLiteral |> deserializeOperation |> toString == "[112233]") 100 | XCTAssert(try! "4c03112233" |> dataLiteral |> deserializeOperation |> toString == "[1.112233]") 101 | XCTAssert(try! "4d0300112233" |> dataLiteral |> deserializeOperation |> toString == "[2.112233]") 102 | XCTAssert(try! "4e03000000112233" |> dataLiteral |> deserializeOperation |> toString == "[4.112233]") 103 | } 104 | 105 | func test10() { 106 | XCTAssert(ScriptOpcode.nop2 |> toString(rules: .noRules) == "nop2") 107 | XCTAssert(ScriptOpcode.nop2 |> toString(rules: .bip65Rule) == "checklocktimeverify") 108 | XCTAssert(ScriptOpcode.nop3 |> toString(rules: .noRules) == "nop3") 109 | XCTAssert(ScriptOpcode.nop3 |> toString(rules: .bip112Rule) == "checksequenceverify") 110 | } 111 | 112 | func test11() { 113 | func f(_ operationString: String, _ opcode: ScriptOpcode, _ dataString: String = "") -> Bool { 114 | let op = try! operationString |> toOperation 115 | let data = try! Base16(rawValue: dataString) |> toData 116 | guard op.opcode == opcode else { return false } 117 | guard op.data == data else { return false } 118 | return true 119 | } 120 | 121 | XCTAssert(f("-1", .pushNegative1)) 122 | XCTAssert(f("0", .pushSize0)) 123 | XCTAssert(f("1", .pushPositive1)) 124 | XCTAssert(f("16", .pushPositive16)) 125 | 126 | XCTAssert(f("17", .pushSize1, "11")) 127 | XCTAssert(f("-2", .pushSize1, "82")) 128 | 129 | XCTAssert(f("9223372036854775807", .pushSize8, "ffffffffffffff7f")) 130 | XCTAssert(f("-9223372036854775807", .pushSize8, "ffffffffffffffff")) 131 | 132 | XCTAssert(f("''", .pushSize0)) 133 | XCTAssert(f("'a'", .pushSize1, "61")) 134 | XCTAssert(f("'abc'", .pushSize3, "616263")) 135 | XCTAssert(f("'O'", .pushSize1, "4f")) 136 | 137 | XCTAssert(f("push_0", .pushSize0)) 138 | } 139 | 140 | func test12() { 141 | XCTAssertThrowsError(try "push_1" |> toOperation) 142 | XCTAssertThrowsError(try "push_75" |> toOperation) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestOutputPoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestOutputPoint.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 11/12/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestOutputPoint: XCTestCase { 27 | func test1() { 28 | let outputPoint = OutputPoint() 29 | XCTAssertFalse(outputPoint.isValid) 30 | } 31 | 32 | func test2() { 33 | let hash = try! "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" |> dataLiteral |> tagHashDigest 34 | let index = 1234 35 | let outputPoint = OutputPoint(hash: hash, index: index) 36 | XCTAssert(outputPoint.isValid) 37 | 38 | let outputPoint2 = outputPoint 39 | XCTAssert(outputPoint == outputPoint2) 40 | XCTAssert(outputPoint2.hash == hash) 41 | XCTAssert(outputPoint2.index == index) 42 | } 43 | 44 | func test3() { 45 | let data = "10" |> dataLiteral 46 | XCTAssertThrowsError(try data |> deserializeOutputPoint) 47 | } 48 | 49 | func test4() { 50 | let index = 53213 51 | let hash = try! "11121314151617180101ab1111cd1111011011ab1111cd1101111111ab1111cd" |> dataLiteral |> tagHashDigest 52 | let initial = OutputPoint(hash: hash, index: index) 53 | XCTAssert(initial.isValid) 54 | XCTAssertEqual(initial.hash, hash) 55 | XCTAssertEqual(initial.index, index) 56 | 57 | 58 | XCTAssertEqual(initial.description, """ 59 | {"hash":"11121314151617180101ab1111cd1111011011ab1111cd1101111111ab1111cd","index":53213} 60 | """) 61 | 62 | var point = OutputPoint() 63 | XCTAssertFalse(initial == point) 64 | 65 | point = try! initial |> serialize |> deserializeOutputPoint 66 | XCTAssert(point.isValid) 67 | XCTAssert(initial == point) 68 | } 69 | 70 | func test5() { 71 | let data = "46682488f0a721124a3905a1bb72445bf13493e2cd46c5c0c8db1c15afa0d58e00000000" |> dataLiteral 72 | let point = try! data |> deserializeOutputPoint 73 | XCTAssert(point.isValid) 74 | XCTAssertEqual(point.hash |> hashEncode, "8ed5a0af151cdbc8c0c546cde29334f15b4472bba105394a1221a7f088246846") 75 | XCTAssertEqual(point.index, 0) 76 | XCTAssertEqual(point |> serialize, data) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestPaymentAddress.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestPaymentAddress.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 11/5/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestPaymentAddress: XCTestCase { 27 | func testAddressEncode() { 28 | let hash = try! "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb" |> dataLiteral |> tagShortHash 29 | XCTAssert(hash |> addressEncode == "1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E") 30 | XCTAssert(hash |> addressEncode(network: .testnet) == "mwy5FX7MVgDutKYbXBxQG5q7EL6pmhHT58") 31 | } 32 | 33 | func testAddressDecode() { 34 | let mainnetAddress: PaymentAddress = "1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E" 35 | let testnetAddress: PaymentAddress = "mwy5FX7MVgDutKYbXBxQG5q7EL6pmhHT58" 36 | 37 | let f = addressDecode >>> toJSONString(outputFormatting: .sortedKeys) 38 | XCTAssert(try! mainnetAddress |> f == """ 39 | {"checksum":2743498322,"payload":"b472a266d0bd89c13706a4132ccfb16f7c3b9fcb","prefix":0} 40 | """) 41 | XCTAssert(try! testnetAddress |> f == """ 42 | {"checksum":1475514977,"payload":"b472a266d0bd89c13706a4132ccfb16f7c3b9fcb","prefix":111} 43 | """) 44 | } 45 | 46 | func testAddressEmbed() { 47 | XCTAssert("Satoshi Nakamoto" |> toUTF8 |> addressEmbed(network: .mainnet, type: .p2pkh) == "168LnUjqoJLie1PW5dTaF2vKUU9Jf6Fe4a") 48 | XCTAssert("Satoshi Nakamoto" |> toUTF8 |> addressEmbed(network: .testnet, type: .p2pkh) == "mkeJ5XppcKmyR7s7oCRx4x8eLTk1Xrso8t") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestRandom.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestRandom.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 10/28/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | import WolfNumerics 26 | 27 | class TestRandom: XCTestCase { 28 | func testSeed() { 29 | var rng = SeededRandomNumberGenerator(seed: 1) 30 | let data = seed(count: 10, using: &rng) 31 | XCTAssert(data |> toBase16 == "610d61f11a62b17cd484") 32 | let bip39seed = seed(bits: 512, using: &rng) 33 | XCTAssert(bip39seed |> toBase16 == "ff2a8b2622253ea52073d2798bf1880ef7f97047cbc41e00f9c9a5b3fa76f263a913c5a78ed3ed208a1a3356294cc535e35e526c2866451d61f2995df29d6137") 34 | } 35 | 36 | func testSeed2() { 37 | let data = seed() 38 | XCTAssertEqual(data.count, minimumSeedSize) 39 | } 40 | 41 | func testSeed3() { 42 | let data = seed(bits: 9) 43 | XCTAssertEqual(data.count, 2) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestSSS.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestSSS.swift 3 | // Bitcoin_Example 4 | // 5 | // Created by Wolf McNally on 10/24/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | @testable import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestSSS: XCTestCase { 27 | func testRandomBytes() throws { 28 | let bytes1 = try SSS.randomBytes(count: 64) 29 | XCTAssertEqual(bytes1.count, 64) 30 | let bytes2 = try SSS.randomBytes(count: 64) 31 | XCTAssertNotEqual(bytes1, bytes2) 32 | } 33 | 34 | func testSSS() throws { 35 | let message = try SSSMessage(data: SSS.randomBytes(count: SSSMessage.length)) 36 | 37 | func test(shareCount: Int, quorum: Int, retrievedCount: Int) throws -> Bool { 38 | let shares = SSS.createShares(from: message, shareCount: shareCount, quorum: quorum) 39 | let encodedShares = shares.map { $0.base64 } 40 | let retrievedShares = Array(encodedShares.shuffled()[0.. toUTF8 62 | let key = Crypto.generateKey() 63 | let ciphertext = try Crypto.encrypt(plaintext: plaintext, key: key) 64 | let encodedCiphertext = try JSONEncoder().encode(ciphertext) 65 | //print(try encodedCiphertext |> fromUTF8) 66 | 67 | // === 68 | 69 | let decodedCiphertext = try JSONDecoder().decode(Crypto.Ciphertext.self, from: encodedCiphertext) 70 | let recoveredPlaintext = try Crypto.decrypt(ciphertext: decodedCiphertext, key: key) 71 | XCTAssertEqual(plaintext, recoveredPlaintext) 72 | } 73 | 74 | func testSSSWithCrypto() throws { 75 | let plaintext = """ 76 | Inner North London, top floor flat 77 | All white walls, white carpet, white cat, 78 | Rice Paper partitions, modern art and ambition 79 | The host's a physician, 80 | Bright bloke, has his own practice 81 | His girlfriend's an actress, an old mate of ours from home 82 | And they're always great fun, so to dinner we've come. 83 | """ |> toUTF8 84 | 85 | let keyShares = try SSS.createShares(from: plaintext, shareCount: 3, quorum: 2) 86 | let encodedShares = try JSONEncoder().encode(keyShares) 87 | //print(try encodedShares |> fromUTF8) 88 | 89 | // === 90 | 91 | let decodedShares = try JSONDecoder().decode([SSSKeyShare].self, from: encodedShares) 92 | let truncatedShares = Array(decodedShares.shuffled().dropLast()) 93 | let recoveredPlaintext = try SSS.combineShares(truncatedShares) 94 | 95 | XCTAssertEqual(plaintext, recoveredPlaintext) 96 | } 97 | 98 | func testChecksum() { 99 | let message = [1, 2, 3, 4] 100 | let expectedChecksum = [120, 542, 324] 101 | let checksum = SplitRecoveryPhrase.createChecksum(message) 102 | XCTAssertEqual(checksum, expectedChecksum) 103 | XCTAssertTrue(SplitRecoveryPhrase.verifyChecksum(message + checksum)) 104 | } 105 | 106 | func testRecoveryPhrase() throws { 107 | let wordCount = 13 108 | let entropy = SplitRecoveryPhrase.generateEntropy(wordCount: wordCount) 109 | let recoveryPhrase = SplitRecoveryPhrase.makeRecoveryPhrase(from: entropy, wordCount: wordCount) 110 | //print(recoveryPhrase) 111 | let data = try SplitRecoveryPhrase.makeData(from: recoveryPhrase) 112 | XCTAssertEqual(entropy, data) 113 | } 114 | 115 | func testSplitRecoveryPhrase() throws { 116 | let wordCount = 13 117 | let entropy = SplitRecoveryPhrase.generateEntropy(wordCount: wordCount) 118 | let recoveryPhrase = SplitRecoveryPhrase.makeRecoveryPhrase(from: entropy, wordCount: wordCount) 119 | print(recoveryPhrase) 120 | let keyShares = try SSS.createShares(from: entropy, shareCount: 3, quorum: 2) 121 | print(keyShares) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestTxRef.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestTxRef.swift 3 | // BitcoinTests 4 | // 5 | // Created by Wolf McNally on 11/8/19. 6 | // Copyright © 2019 Blockchain Commons. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Foundation 11 | import Bitcoin 12 | import WolfFoundation 13 | import WolfPipe 14 | 15 | /// 16 | /// Tests from `Bech32 Encoded Tx Position References` 17 | /// https://github.com/bitcoin/bips/blob/master/bip-0136.mediawiki 18 | /// https://github.com/WebOfTrustInfo/btcr-hackathon-2019/issues/5 19 | /// 20 | 21 | class TestTxRef: XCTestCase { 22 | func testValid() throws { 23 | let tests: [(EncodedTxRef, TxRef)] = [ 24 | // Mainnet, no outIndex 25 | ("tx1:rqqq-qqqq-qmhu-qhp", TxRef(network: .mainnet, blockHeight: 0x0, txIndex: 0x0)), 26 | ("tx1:rqqq-qqll-l8xh-jkg", TxRef(network: .mainnet, blockHeight: 0x0, txIndex: 0x7FFF)), 27 | ("tx1:r7ll-llqq-qghq-qr8", TxRef(network: .mainnet, blockHeight: 0xFFFFFF, txIndex: 0x0)), 28 | ("tx1:r7ll-llll-l5xt-jzw", TxRef(network: .mainnet, blockHeight: 0xFFFFFF, txIndex: 0x7FFF)), 29 | 30 | // Mainnet, outIndex zero 31 | ("tx1:rqqq-qqqq-qmhu-qhp", TxRef(network: .mainnet, blockHeight: 0x0, txIndex: 0x0, outIndex: 0x0)), 32 | ("tx1:rqqq-qqll-l8xh-jkg", TxRef(network: .mainnet, blockHeight: 0x0, txIndex: 0x7FFF, outIndex: 0x0)), 33 | ("tx1:r7ll-llqq-qghq-qr8", TxRef(network: .mainnet, blockHeight: 0xFFFFFF, txIndex: 0x0, outIndex: 0x0)), 34 | ("tx1:r7ll-llll-l5xt-jzw", TxRef(network: .mainnet, blockHeight: 0xFFFFFF, txIndex: 0x7FFF, outIndex: 0x0)), 35 | 36 | // Mainnet, output non-zero 37 | ("tx1:yqqq-qqqq-qpqq-5j9q-nz", TxRef(network: .mainnet, blockHeight: 0x0, txIndex: 0x0, outIndex: 0x1)), 38 | ("tx1:yqqq-qqll-lpqq-wd7a-nw", TxRef(network: .mainnet, blockHeight: 0x0, txIndex: 0x7FFF, outIndex: 0x1)), 39 | ("tx1:y7ll-llqq-qpqq-lktn-jq", TxRef(network: .mainnet, blockHeight: 0xFFFFFF, txIndex: 0x0, outIndex: 0x1)), 40 | ("tx1:y7ll-llll-lpqq-9fsw-jv", TxRef(network: .mainnet, blockHeight: 0xFFFFFF, txIndex: 0x7FFF, outIndex: 0x1)), 41 | ("tx1:yjk0-uqay-zrfq-g2cg-t8", TxRef(network: .mainnet, blockHeight: 0x71F69, txIndex: 0x89D, outIndex: 0x123)), 42 | ("tx1:yjk0-uqay-zu4x-nk6u-pc", TxRef(network: .mainnet, blockHeight: 0x71F69, txIndex: 0x89D, outIndex: 0x1ABC)), 43 | 44 | 45 | // Testnet, no outIndex 46 | ("txtest1:xqqq-qqqq-qkla-64l", TxRef(network: .testnet, blockHeight: 0x0, txIndex: 0x0)), 47 | ("txtest1:xqqq-qqll-l2wk-g5k", TxRef(network: .testnet, blockHeight: 0x0, txIndex: 0x7FFF)), 48 | ("txtest1:x7ll-llqq-q9lp-6pe", TxRef(network: .testnet, blockHeight: 0xFFFFFF, txIndex: 0x0)), 49 | ("txtest1:x7ll-llll-lew2-gqs", TxRef(network: .testnet, blockHeight: 0xFFFFFF, txIndex: 0x7FFF)), 50 | 51 | // Testnet, outIndex zero 52 | ("txtest1:xqqq-qqqq-qkla-64l", TxRef(network: .testnet, blockHeight: 0x0, txIndex: 0x0, outIndex: 0x0)), 53 | ("txtest1:xqqq-qqll-l2wk-g5k", TxRef(network: .testnet, blockHeight: 0x0, txIndex: 0x7FFF, outIndex: 0x0)), 54 | ("txtest1:x7ll-llqq-q9lp-6pe", TxRef(network: .testnet, blockHeight: 0xFFFFFF, txIndex: 0x0, outIndex: 0x0)), 55 | ("txtest1:x7ll-llll-lew2-gqs", TxRef(network: .testnet, blockHeight: 0xFFFFFF, txIndex: 0x7FFF, outIndex: 0x0)), 56 | 57 | // Testnet, outIndex non-zero 58 | ("txtest1:8qqq-qqqq-qpqq-622t-s9", TxRef(network: .testnet, blockHeight: 0x0, txIndex: 0x0, outIndex: 0x1)), 59 | ("txtest1:8qqq-qqll-lpqq-q43k-sf", TxRef(network: .testnet, blockHeight: 0x0, txIndex: 0x7FFF, outIndex: 0x1)), 60 | ("txtest1:87ll-llqq-qpqq-3wyc-38", TxRef(network: .testnet, blockHeight: 0xFFFFFF, txIndex: 0x0, outIndex: 0x1)), 61 | ("txtest1:87ll-llll-lpqq-t3l9-3t", TxRef(network: .testnet, blockHeight: 0xFFFFFF, txIndex: 0x7FFF, outIndex: 0x1)), 62 | ("txtest1:8jk0-uqay-zrfq-xjhr-gq", TxRef(network: .testnet, blockHeight: 0x71F69, txIndex: 0x89D, outIndex: 0x123)), 63 | ("txtest1:8jk0-uqay-zu4x-aw4h-zl", TxRef(network: .testnet, blockHeight: 0x71F69, txIndex: 0x89D, outIndex: 0x1ABC)) 64 | ] 65 | 66 | try tests.forEach { 67 | let (encodedTxRef, txRef) = $0 68 | let decoded = try encodedTxRef |> toDecoded 69 | XCTAssertEqual(decoded, txRef) 70 | let encoded = decoded |> toEncoded 71 | XCTAssertEqual(encoded, encodedTxRef) 72 | } 73 | } 74 | 75 | func testValidNoncanonical() throws { 76 | let tests: [(EncodedTxRef, TxRef)] = [ 77 | ("tx1:rjk0-uqay-zsrw-hqe", TxRef(network: .mainnet, blockHeight: 0x71F69, txIndex: 0x89D)), 78 | ("TX1RJK0UQAYZSRWHQE", TxRef(network: .mainnet, blockHeight: 0x71F69, txIndex: 0x89D)), 79 | ("TX1RJK0--UQaYZSRw----HQE", TxRef(network: .mainnet, blockHeight: 0x71F69, txIndex: 0x89D)), 80 | ("tx1 rjk0 uqay zsrw hqe", TxRef(network: .mainnet, blockHeight: 0x71F69, txIndex: 0x89D)), 81 | ("tx1!rjk0\\uqay*zsrw^^hqe", TxRef(network: .mainnet, blockHeight: 0x71F69, txIndex: 0x89D)) 82 | ] 83 | 84 | try tests.forEach { 85 | let (encodedTxRef, txRef) = $0 86 | let decoded = try encodedTxRef |> toDecoded 87 | XCTAssertEqual(decoded, txRef) 88 | } 89 | } 90 | 91 | func testInvalid() throws { 92 | let tests: [EncodedTxRef] = [ 93 | "tx1:t7ll-llll-ldup-3hh", 94 | "tx1:rlll-llll-lfet-r2y", 95 | "tx1:rjk0-u5ng-gghq-fkg7", 96 | "tx1:rjk0-u5qd-s43z", 97 | "tx1:yqqq-qqqq-qqqq-ksvh-26" // zero out index 98 | ] 99 | 100 | try tests.forEach { encodedTxRef in 101 | XCTAssertThrowsError( try encodedTxRef |> toDecoded ) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Tests/BitcoinDemo/TestWrapped.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestWrapped.swift 3 | // Bitcoin_Tests 4 | // 5 | // Created by Wolf McNally on 11/6/18. 6 | // 7 | // Copyright © 2018 Blockchain Commons. 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | import XCTest 22 | import Bitcoin 23 | import WolfPipe 24 | import WolfFoundation 25 | 26 | class TestWrapped: XCTestCase { 27 | func testWrapEncode() { 28 | let f = { tagBase16 >>> toData >>> wrapEncode(version: $0) >>> toBase16 } 29 | XCTAssert(try! "031bab84e687e36514eeaf5a017c30d32c1f59dd4ea6629da7970ca374513dd006" |> f(0) == "00031bab84e687e36514eeaf5a017c30d32c1f59dd4ea6629da7970ca374513dd0065b09d03c") 30 | XCTAssert(try! "031bab84e687e36514eeaf5a017c30d32c1f59dd4ea6629da7970ca374513dd006" |> f(42) == "2a031bab84e687e36514eeaf5a017c30d32c1f59dd4ea6629da7970ca374513dd006298eebe4") 31 | } 32 | 33 | func testWrapDecode() { 34 | let f = tagBase16 >>> toData >>> wrapDecode >>> toJSONString(outputFormatting: .sortedKeys) 35 | XCTAssert(try! "00031bab84e687e36514eeaf5a017c30d32c1f59dd4ea6629da7970ca374513dd0065b09d03c" |> f == """ 36 | {"checksum":1020266843,"payload":"031bab84e687e36514eeaf5a017c30d32c1f59dd4ea6629da7970ca374513dd006","prefix":0} 37 | """) 38 | XCTAssert(try! "2a031bab84e687e36514eeaf5a017c30d32c1f59dd4ea6629da7970ca374513dd006298eebe4" |> f == """ 39 | {"checksum":3840642601,"payload":"031bab84e687e36514eeaf5a017c30d32c1f59dd4ea6629da7970ca374513dd006","prefix":42} 40 | """) 41 | } 42 | } 43 | --------------------------------------------------------------------------------