diff --git a/BitcoinKit.xcodeproj/project.pbxproj b/BitcoinKit.xcodeproj/project.pbxproj index e7b01b99..4d6965c6 100644 --- a/BitcoinKit.xcodeproj/project.pbxproj +++ b/BitcoinKit.xcodeproj/project.pbxproj @@ -108,7 +108,7 @@ 14839AAD202FE80400A6CB34 /* ServiceFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14839AAC202FE80400A6CB34 /* ServiceFlags.swift */; }; 14A2961D2032316900E19177 /* HDKeychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A2961C2032316900E19177 /* HDKeychain.swift */; }; 14A2961F2032317B00E19177 /* HDWallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A2961E2032317B00E19177 /* HDWallet.swift */; }; - 14CDC38A2021881A00C01556 /* Address.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14CDC3892021881A00C01556 /* Address.swift */; }; + 14CDC38A2021881A00C01556 /* BitcoinAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14CDC3892021881A00C01556 /* BitcoinAddress.swift */; }; 14CDC38C2021EF5000C01556 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14CDC38B2021EF5000C01556 /* Helpers.swift */; }; 14F37A3A20209B5400D34748 /* Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14F37A3920209B5400D34748 /* Transaction.swift */; }; 14F37A3C20209F9200D34748 /* Script.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14F37A3B20209F9200D34748 /* Script.swift */; }; @@ -150,6 +150,8 @@ 29290BA7210C01E800D2BE78 /* OP_CHECKSIGVERIFY.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29290BA6210C01E800D2BE78 /* OP_CHECKSIGVERIFY.swift */; }; 29330145214F749C0028946B /* QRCodeGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29330144214F749C0028946B /* QRCodeGenerator.swift */; }; 29330F6E210597B700106AFA /* UnitsAndLimits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29330F6D210597B700106AFA /* UnitsAndLimits.swift */; }; + 29351DE82334EC76007CF43F /* Address.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29351DE72334EC76007CF43F /* Address.swift */; }; + 29351DF32335C2A8007CF43F /* AddressType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29351DF22335C2A7007CF43F /* AddressType.swift */; }; 29440D11232FB98F00E99571 /* TransactionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29440D10232FB98F00E99571 /* TransactionBuilder.swift */; }; 29440D13232FBE0900E99571 /* TransactionBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29440D12232FBE0900E99571 /* TransactionBuilderTests.swift */; }; 29440D15232FC64500E99571 /* TransactionSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29440D14232FC64500E99571 /* TransactionSigner.swift */; }; @@ -159,7 +161,6 @@ 29440D1E232FF74700E99571 /* BTCSignatureHashHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29440D1D232FF74700E99571 /* BTCSignatureHashHelper.swift */; }; 29440D20232FF75B00E99571 /* SignatureHashHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29440D1F232FF75B00E99571 /* SignatureHashHelper.swift */; }; 29440D22233003C000E99571 /* BTCSignatureHashHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29440D21233003C000E99571 /* BTCSignatureHashHelperTests.swift */; }; - 2949920020F227EB00D078B6 /* VersionByte.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294991FF20F227EB00D078B6 /* VersionByte.swift */; }; 2949920220F228B400D078B6 /* UnspentTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2949920120F228B400D078B6 /* UnspentTransaction.swift */; }; 2949920620F22DCA00D078B6 /* UnsignedTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2949920520F22DCA00D078B6 /* UnsignedTransaction.swift */; }; 294CE2DF232CA88800922458 /* CoinType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294CE2DE232CA88800922458 /* CoinType.swift */; }; @@ -183,12 +184,25 @@ 296ADC242112EC8C007EE5C7 /* PeerGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296ADC222112EC8C007EE5C7 /* PeerGroup.swift */; }; 296ADC252112EC8C007EE5C7 /* Peer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296ADC232112EC8C007EE5C7 /* Peer.swift */; }; 297C408121100810003AF4EF /* MnemonicTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 297C408021100810003AF4EF /* MnemonicTests.swift */; }; - 297DB97320EB12E60077EEEE /* AddressType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 297DB97220EB12E60077EEEE /* AddressType.swift */; }; + 297DB97320EB12E60077EEEE /* BitcoinAddress+HashType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 297DB97220EB12E60077EEEE /* BitcoinAddress+HashType.swift */; }; 297DB97520EB13320077EEEE /* AddressFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 297DB97420EB13320077EEEE /* AddressFactory.swift */; }; 298FA5812126C77A009EFAC4 /* ScriptFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 298FA5802126C77A009EFAC4 /* ScriptFactory.swift */; }; 299CB46F20EE1EA500B1245C /* Transaction+SignatureHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 299CB46E20EE1EA500B1245C /* Transaction+SignatureHash.swift */; }; 299CB47320F0185500B1245C /* TransactionSignatureSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 299CB47220F0185500B1245C /* TransactionSignatureSerializer.swift */; }; 29AC9965214E8A7000AE82FE /* QRCodeConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AC9964214E8A7000AE82FE /* QRCodeConvertible.swift */; }; + 29D8F12523337D8000E72008 /* Bech32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F12423337D8000E72008 /* Bech32.swift */; }; + 29D8F12723337D8800E72008 /* Base58Check.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F12623337D8800E72008 /* Base58Check.swift */; }; + 29D8F12923337DEE00E72008 /* Base58.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F12823337DEE00E72008 /* Base58.swift */; }; + 29D8F12C2333B6BD00E72008 /* LegacyAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F12B2333B6BC00E72008 /* LegacyAddress.swift */; }; + 29D8F12E2333B6C600E72008 /* Cashaddr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F12D2333B6C600E72008 /* Cashaddr.swift */; }; + 29D8F1302333BED500E72008 /* BitcoinAddress+VersionByte.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F12F2333BED500E72008 /* BitcoinAddress+VersionByte.swift */; }; + 29D8F1322333BF3000E72008 /* BitcoinAddress+HashSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F1312333BF3000E72008 /* BitcoinAddress+HashSize.swift */; }; + 29D8F1342333E10B00E72008 /* BitcoinScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F1332333E10B00E72008 /* BitcoinScheme.swift */; }; + 29D8F1382334A18200E72008 /* BitcoinAddress+Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F1372334A18200E72008 /* BitcoinAddress+Legacy.swift */; }; + 29D8F13A2334A1A300E72008 /* BitcoinAddress+Cashaddr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F1392334A1A300E72008 /* BitcoinAddress+Cashaddr.swift */; }; + 29D8F13C2334B36E00E72008 /* AddressError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F13B2334B36E00E72008 /* AddressError.swift */; }; + 29D8F13E2334B4EE00E72008 /* BitcoinAddress+deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F13D2334B4EE00E72008 /* BitcoinAddress+deprecated.swift */; }; + 29D8F1402334B59700E72008 /* PublicKey+Address.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F13F2334B59700E72008 /* PublicKey+Address.swift */; }; 29E1ED6F210EB4F8007F4627 /* OP_EXAMPLE.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E1ED6E210EB4F8007F4627 /* OP_EXAMPLE.swift */; }; 29E1ED71210EC751007F4627 /* OP_1NEGATE.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E1ED70210EC751007F4627 /* OP_1NEGATE.swift */; }; 29E1ED73210ECD35007F4627 /* OP_CHECKMULTISIG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E1ED72210ECD35007F4627 /* OP_CHECKMULTISIG.swift */; }; @@ -319,7 +333,7 @@ 0C2375A32132501700DB2872 /* MessageSerializerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSerializerTests.swift; sourceTree = ""; }; 0C2CB94C211FAD320087A8EB /* OP_BIN2NUM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OP_BIN2NUM.swift; sourceTree = ""; }; 0C4132EE210EFD1700906E4A /* OP_SWAP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_SWAP.swift; sourceTree = ""; }; - 0C61BF50215B988A001415CA /* Encoding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Encoding.swift; path = Keys/Encoding.swift; sourceTree = ""; }; + 0C61BF50215B988A001415CA /* Encoding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Encoding.swift; sourceTree = ""; }; 0C66CD022125425D0049DB89 /* OP_INVERT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_INVERT.swift; sourceTree = ""; }; 0C66CD04212542660049DB89 /* OP_AND.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_AND.swift; sourceTree = ""; }; 0C66CD06212542730049DB89 /* OP_OR.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_OR.swift; sourceTree = ""; }; @@ -374,7 +388,7 @@ 14A2961B203207F400E19177 /* BitcoinKit.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; name = BitcoinKit.modulemap; path = ../../BitcoinKit/BitcoinKit.modulemap; sourceTree = ""; }; 14A2961C2032316900E19177 /* HDKeychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HDKeychain.swift; sourceTree = ""; }; 14A2961E2032317B00E19177 /* HDWallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HDWallet.swift; sourceTree = ""; }; - 14CDC3892021881A00C01556 /* Address.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Address.swift; sourceTree = ""; }; + 14CDC3892021881A00C01556 /* BitcoinAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinAddress.swift; sourceTree = ""; }; 14CDC38B2021EF5000C01556 /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; }; 14F37A3920209B5400D34748 /* Transaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transaction.swift; sourceTree = ""; }; 14F37A3B20209F9200D34748 /* Script.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Script.swift; sourceTree = ""; }; @@ -413,6 +427,8 @@ 29290BA6210C01E800D2BE78 /* OP_CHECKSIGVERIFY.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_CHECKSIGVERIFY.swift; sourceTree = ""; }; 29330144214F749C0028946B /* QRCodeGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeGenerator.swift; sourceTree = ""; }; 29330F6D210597B700106AFA /* UnitsAndLimits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsAndLimits.swift; sourceTree = ""; }; + 29351DE72334EC76007CF43F /* Address.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Address.swift; sourceTree = ""; }; + 29351DF22335C2A7007CF43F /* AddressType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressType.swift; sourceTree = ""; }; 29440D10232FB98F00E99571 /* TransactionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionBuilder.swift; sourceTree = ""; }; 29440D12232FBE0900E99571 /* TransactionBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionBuilderTests.swift; sourceTree = ""; }; 29440D14232FC64500E99571 /* TransactionSigner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionSigner.swift; sourceTree = ""; }; @@ -422,7 +438,6 @@ 29440D1D232FF74700E99571 /* BTCSignatureHashHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTCSignatureHashHelper.swift; sourceTree = ""; }; 29440D1F232FF75B00E99571 /* SignatureHashHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignatureHashHelper.swift; sourceTree = ""; }; 29440D21233003C000E99571 /* BTCSignatureHashHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTCSignatureHashHelperTests.swift; sourceTree = ""; }; - 294991FF20F227EB00D078B6 /* VersionByte.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionByte.swift; sourceTree = ""; }; 2949920120F228B400D078B6 /* UnspentTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnspentTransaction.swift; sourceTree = ""; }; 2949920520F22DCA00D078B6 /* UnsignedTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsignedTransaction.swift; sourceTree = ""; }; 294CE2DE232CA88800922458 /* CoinType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinType.swift; sourceTree = ""; }; @@ -446,13 +461,26 @@ 296ADC222112EC8C007EE5C7 /* PeerGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerGroup.swift; sourceTree = ""; }; 296ADC232112EC8C007EE5C7 /* Peer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Peer.swift; sourceTree = ""; }; 297C408021100810003AF4EF /* MnemonicTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicTests.swift; sourceTree = ""; }; - 297DB97220EB12E60077EEEE /* AddressType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressType.swift; sourceTree = ""; }; + 297DB97220EB12E60077EEEE /* BitcoinAddress+HashType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+HashType.swift"; sourceTree = ""; }; 297DB97420EB13320077EEEE /* AddressFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressFactory.swift; sourceTree = ""; }; 298FA5802126C77A009EFAC4 /* ScriptFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptFactory.swift; sourceTree = ""; }; 298FA582212788A8009EFAC4 /* BitcoinKit.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = BitcoinKit.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 299CB46E20EE1EA500B1245C /* Transaction+SignatureHash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Transaction+SignatureHash.swift"; sourceTree = ""; }; 299CB47220F0185500B1245C /* TransactionSignatureSerializer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionSignatureSerializer.swift; sourceTree = ""; }; 29AC9964214E8A7000AE82FE /* QRCodeConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeConvertible.swift; sourceTree = ""; }; + 29D8F12423337D8000E72008 /* Bech32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32.swift; sourceTree = ""; }; + 29D8F12623337D8800E72008 /* Base58Check.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base58Check.swift; sourceTree = ""; }; + 29D8F12823337DEE00E72008 /* Base58.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base58.swift; sourceTree = ""; }; + 29D8F12B2333B6BC00E72008 /* LegacyAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyAddress.swift; sourceTree = ""; }; + 29D8F12D2333B6C600E72008 /* Cashaddr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cashaddr.swift; sourceTree = ""; }; + 29D8F12F2333BED500E72008 /* BitcoinAddress+VersionByte.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+VersionByte.swift"; sourceTree = ""; }; + 29D8F1312333BF3000E72008 /* BitcoinAddress+HashSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+HashSize.swift"; sourceTree = ""; }; + 29D8F1332333E10B00E72008 /* BitcoinScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinScheme.swift; sourceTree = ""; }; + 29D8F1372334A18200E72008 /* BitcoinAddress+Legacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+Legacy.swift"; sourceTree = ""; }; + 29D8F1392334A1A300E72008 /* BitcoinAddress+Cashaddr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+Cashaddr.swift"; sourceTree = ""; }; + 29D8F13B2334B36E00E72008 /* AddressError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressError.swift; sourceTree = ""; }; + 29D8F13D2334B4EE00E72008 /* BitcoinAddress+deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+deprecated.swift"; sourceTree = ""; }; + 29D8F13F2334B59700E72008 /* PublicKey+Address.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PublicKey+Address.swift"; sourceTree = ""; }; 29E1ED6E210EB4F8007F4627 /* OP_EXAMPLE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_EXAMPLE.swift; sourceTree = ""; }; 29E1ED70210EC751007F4627 /* OP_1NEGATE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_1NEGATE.swift; sourceTree = ""; }; 29E1ED72210ECD35007F4627 /* OP_CHECKMULTISIG.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_CHECKMULTISIG.swift; sourceTree = ""; }; @@ -689,6 +717,8 @@ 481BDCC52332172C00FC79FC /* SecureGenerateBytes */, 481BDC9623320B7000FC79FC /* Mnemonic */, 29AC9962214E898800AE82FE /* Keys */, + 29D8F12223337D3300E72008 /* Address */, + 29D8F12323337D4C00E72008 /* QR */, 147494EB201F9E4F006D1CF8 /* Network.swift */, 294CE2DE232CA88800922458 /* CoinType.swift */, 1463E6B52025E99C0033DAAE /* BlockChain.swift */, @@ -697,7 +727,6 @@ E6A643072244190400CD4BFC /* Scalar32Bytes.swift */, 14839A7B202F79F900A6CB34 /* PaymentURI.swift */, 147494E3201F9B85006D1CF8 /* Crypto.swift */, - 0C61BF50215B988A001415CA /* Encoding.swift */, A29721F7211B2497007228D5 /* Math.swift */, A246ED0F2118621B0000418D /* MerkleTree.swift */, A2B7A34D2125464400764AE9 /* ProofOfWork.swift */, @@ -719,6 +748,10 @@ 29440D1C232FE32C00E99571 /* Deprecated */ = { isa = PBXGroup; children = ( + 29351DF22335C2A7007CF43F /* AddressType.swift */, + 29D8F12B2333B6BC00E72008 /* LegacyAddress.swift */, + 29D8F12D2333B6C600E72008 /* Cashaddr.swift */, + 297DB97420EB13320077EEEE /* AddressFactory.swift */, 2949920520F22DCA00D078B6 /* UnsignedTransaction.swift */, 299CB47220F0185500B1245C /* TransactionSignatureSerializer.swift */, 299CB46E20EE1EA500B1245C /* Transaction+SignatureHash.swift */, @@ -1007,17 +1040,50 @@ children = ( 141FE2E32022589E00A08B04 /* PrivateKey.swift */, 141FE2E12022588C00A08B04 /* PublicKey.swift */, + 29D8F13F2334B59700E72008 /* PublicKey+Address.swift */, 14A2961C2032316900E19177 /* HDKeychain.swift */, 1482B5E7202721FF0098B612 /* HDPrivateKey.swift */, 1482B5E920273B8A0098B612 /* HDPublicKey.swift */, - 14CDC3892021881A00C01556 /* Address.swift */, - 294991FF20F227EB00D078B6 /* VersionByte.swift */, - 297DB97220EB12E60077EEEE /* AddressType.swift */, - 297DB97420EB13320077EEEE /* AddressFactory.swift */, + ); + path = Keys; + sourceTree = ""; + }; + 29D8F12223337D3300E72008 /* Address */ = { + isa = PBXGroup; + children = ( + 29D8F12A23337F4300E72008 /* Encoding */, + 29D8F1332333E10B00E72008 /* BitcoinScheme.swift */, + 29D8F13B2334B36E00E72008 /* AddressError.swift */, + 29351DE72334EC76007CF43F /* Address.swift */, + 14CDC3892021881A00C01556 /* BitcoinAddress.swift */, + 29D8F13D2334B4EE00E72008 /* BitcoinAddress+deprecated.swift */, + 297DB97220EB12E60077EEEE /* BitcoinAddress+HashType.swift */, + 29D8F1312333BF3000E72008 /* BitcoinAddress+HashSize.swift */, + 29D8F12F2333BED500E72008 /* BitcoinAddress+VersionByte.swift */, + 29D8F1372334A18200E72008 /* BitcoinAddress+Legacy.swift */, + 29D8F1392334A1A300E72008 /* BitcoinAddress+Cashaddr.swift */, + ); + path = Address; + sourceTree = ""; + }; + 29D8F12323337D4C00E72008 /* QR */ = { + isa = PBXGroup; + children = ( 29AC9964214E8A7000AE82FE /* QRCodeConvertible.swift */, 29330144214F749C0028946B /* QRCodeGenerator.swift */, ); - path = Keys; + path = QR; + sourceTree = ""; + }; + 29D8F12A23337F4300E72008 /* Encoding */ = { + isa = PBXGroup; + children = ( + 0C61BF50215B988A001415CA /* Encoding.swift */, + 29D8F12423337D8000E72008 /* Bech32.swift */, + 29D8F12823337DEE00E72008 /* Base58.swift */, + 29D8F12623337D8800E72008 /* Base58Check.swift */, + ); + path = Encoding; sourceTree = ""; }; 481BDC9623320B7000FC79FC /* Mnemonic */ = { @@ -1225,6 +1291,7 @@ 2914BE5C211BD0DF00B349CB /* OP_CHECKSEQUENCEVERIFY.swift in Sources */, 29E1ED6F210EB4F8007F4627 /* OP_EXAMPLE.swift in Sources */, 481BDCA823320B7000FC79FC /* WordList+Italian.swift in Sources */, + 29351DF32335C2A8007CF43F /* AddressType.swift in Sources */, 14F37A3A20209B5400D34748 /* Transaction.swift in Sources */, 14839A93202FE6AC00A6CB34 /* FilterLoadMessage.swift in Sources */, 14839A9D202FE72600A6CB34 /* TransactionOutPoint.swift in Sources */, @@ -1240,7 +1307,7 @@ 0C1DE16B211E7C0900FE8E43 /* OP_2DROP.swift in Sources */, 0C4132EF210EFD1700906E4A /* OP_SWAP.swift in Sources */, 14839AAB202FE7F000A6CB34 /* NetworkAddress.swift in Sources */, - 297DB97320EB12E60077EEEE /* AddressType.swift in Sources */, + 297DB97320EB12E60077EEEE /* BitcoinAddress+HashType.swift in Sources */, 294DDE43211B31EA00B7F645 /* OP_VERIF.swift in Sources */, 14839A83202FE5F800A6CB34 /* AddressMessage.swift in Sources */, 481BDCAA23320B7000FC79FC /* WordList+TraditionalChinese.swift in Sources */, @@ -1252,11 +1319,13 @@ 294DDE3D211B31C100B7F645 /* OP_VER.swift in Sources */, 298FA5812126C77A009EFAC4 /* ScriptFactory.swift in Sources */, 481BDCA423320B7000FC79FC /* WordList.swift in Sources */, + 29D8F13E2334B4EE00E72008 /* BitcoinAddress+deprecated.swift in Sources */, 14839AA3202FE78600A6CB34 /* Message.swift in Sources */, 29290B95210AF86C00D2BE78 /* OP_N.swift in Sources */, 0C1DE171211E7DE300FE8E43 /* OP_2OVER.swift in Sources */, 29290BA5210AFD7A00D2BE78 /* OP_PUSHDATA.swift in Sources */, 297DB97520EB13320077EEEE /* AddressFactory.swift in Sources */, + 29D8F1302333BED500E72008 /* BitcoinAddress+VersionByte.swift in Sources */, 0C1DE180211EACE800FE8E43 /* OP_NUM2BIN.swift in Sources */, 0C0900382116A7F50077E9BC /* OP_ADD.swift in Sources */, 0C09002821169C810077E9BC /* OP_1ADD.swift in Sources */, @@ -1272,6 +1341,7 @@ CFA2906C2101CD96001A1BAB /* ScriptChunk.swift in Sources */, 14F37A3C20209F9200D34748 /* Script.swift in Sources */, 0C1DE17C211E8A0100FE8E43 /* OP_SPLIT.swift in Sources */, + 29D8F12E2333B6C600E72008 /* Cashaddr.swift in Sources */, 294DDE3B211B31B100B7F645 /* OP_NOP.swift in Sources */, 0C66CD07212542730049DB89 /* OP_OR.swift in Sources */, 0C1DE15F211E6FA700FE8E43 /* OP_NIP.swift in Sources */, @@ -1306,7 +1376,9 @@ 481BDCA923320B7000FC79FC /* WordList+Korean.swift in Sources */, 0C1DE161211E717500FE8E43 /* OP_OVER.swift in Sources */, 0C1DD42321182126004BA8A8 /* OP_MOD.swift in Sources */, + 29D8F1342333E10B00E72008 /* BitcoinScheme.swift in Sources */, A29721F8211B2497007228D5 /* Math.swift in Sources */, + 29D8F13C2334B36E00E72008 /* AddressError.swift in Sources */, 29290B9B210AF88C00D2BE78 /* OP_HASH160.swift in Sources */, 481BDCAC23320B7000FC79FC /* WordList+SimplifiedChinese.swift in Sources */, 0C1DE165211E75A800FE8E43 /* OP_ROLL.swift in Sources */, @@ -1316,6 +1388,7 @@ 141FE2E42022589E00A08B04 /* PrivateKey.swift in Sources */, 299CB46F20EE1EA500B1245C /* Transaction+SignatureHash.swift in Sources */, 294DDE49211B322E00B7F645 /* OP_ENDIF.swift in Sources */, + 29D8F12923337DEE00E72008 /* Base58.swift in Sources */, 29AC9965214E8A7000AE82FE /* QRCodeConvertible.swift in Sources */, 0C1DD40E21181AAC004BA8A8 /* OP_NUMNOTEQUAL.swift in Sources */, 2914BE5A211BD0CC00B349CB /* OP_CHECKLOCKTIMEVERIFY.swift in Sources */, @@ -1323,9 +1396,9 @@ 29290B93210AF86400D2BE78 /* OP_0.swift in Sources */, 294CE2DF232CA88800922458 /* CoinType.swift in Sources */, 0C1DE163211E724E00FE8E43 /* OP_PICK.swift in Sources */, + 29D8F13A2334A1A300E72008 /* BitcoinAddress+Cashaddr.swift in Sources */, 481BDCA623320B7000FC79FC /* WordList+English.swift in Sources */, 2914BE47211BAB0500B349CB /* OP_HASH256.swift in Sources */, - 2949920020F227EB00D078B6 /* VersionByte.swift in Sources */, 29248EEF2104B64E00CC9051 /* ScriptChunkHelper.swift in Sources */, 14839A85202FE60E00A6CB34 /* InventoryMessage.swift in Sources */, 14839AA7202FE7C700A6CB34 /* VarInt.swift in Sources */, @@ -1341,9 +1414,11 @@ 14839A89202FE63400A6CB34 /* InventoryItem.swift in Sources */, 14CDC38C2021EF5000C01556 /* Helpers.swift in Sources */, 1419E83B202CDBE500FCB0BE /* BitcoinKitPrivate.m in Sources */, + 29351DE82334EC76007CF43F /* Address.swift in Sources */, 481BDCA723320B7000FC79FC /* WordList+French.swift in Sources */, 0C1DD40C21181A40004BA8A8 /* OP_LESSTHAN.swift in Sources */, 147494F0201FAE30006D1CF8 /* Serialization.swift in Sources */, + 29D8F1402334B59700E72008 /* PublicKey+Address.swift in Sources */, 147494E4201F9B85006D1CF8 /* Crypto.swift in Sources */, 0C0900422116B9DE0077E9BC /* OP_LSHIFT.swift in Sources */, A2B7A34E2125464400764AE9 /* ProofOfWork.swift in Sources */, @@ -1354,6 +1429,7 @@ 2914BE4E211BCF7600B349CB /* OP_RESERVED.swift in Sources */, 14839A81202FE5CA00A6CB34 /* VerackMessage.swift in Sources */, 29E1ED71210EC751007F4627 /* OP_1NEGATE.swift in Sources */, + 29D8F1382334A18200E72008 /* BitcoinAddress+Legacy.swift in Sources */, 294CE2EB232CE9AE00922458 /* FeeCalculator.swift in Sources */, 29440D17232FD1C500E99571 /* BCHSignatureHashHelper.swift in Sources */, 29440D1E232FF74700E99571 /* BTCSignatureHashHelper.swift in Sources */, @@ -1379,9 +1455,10 @@ 0C1DD41F211820BB004BA8A8 /* OP_MUL.swift in Sources */, 29290BA7210C01E800D2BE78 /* OP_CHECKSIGVERIFY.swift in Sources */, 0C1DD41A21181DDF004BA8A8 /* OP_NUMEQUALVERIFY.swift in Sources */, + 29D8F12523337D8000E72008 /* Bech32.swift in Sources */, 14A2961D2032316900E19177 /* HDKeychain.swift in Sources */, 296ADC242112EC8C007EE5C7 /* PeerGroup.swift in Sources */, - 14CDC38A2021881A00C01556 /* Address.swift in Sources */, + 14CDC38A2021881A00C01556 /* BitcoinAddress.swift in Sources */, 29330145214F749C0028946B /* QRCodeGenerator.swift in Sources */, 141FE2E22022588C00A08B04 /* PublicKey.swift in Sources */, 0C1DE175211E816E00FE8E43 /* OP_2SWAP.swift in Sources */, @@ -1394,10 +1471,12 @@ 14839A9B202FE71200A6CB34 /* TransactionInput.swift in Sources */, 2914BE43211BAAE500B349CB /* OP_SHA256.swift in Sources */, 0C0900342116A53F0077E9BC /* OP_NOT.swift in Sources */, + 29D8F12C2333B6BD00E72008 /* LegacyAddress.swift in Sources */, CFA290722102B635001A1BAB /* ScriptMachine.swift in Sources */, 294DDE45211B31FA00B7F645 /* OP_VERNOTIF.swift in Sources */, 0C2375A02132431B00DB2872 /* HeadersMessage.swift in Sources */, 147494EE201FADAC006D1CF8 /* MurmurHash.swift in Sources */, + 29D8F12723337D8800E72008 /* Base58Check.swift in Sources */, 481BDCBB233212D700FC79FC /* Mnemonic+Generate.swift in Sources */, 0C09002E2116A32C0077E9BC /* OP_2MUL.swift in Sources */, 29290B9F210AF8FD00D2BE78 /* OP_EQUAL.swift in Sources */, @@ -1418,6 +1497,7 @@ 0C09002C2116A1C30077E9BC /* OP_NEGATE.swift in Sources */, 1463E6B42025E9480033DAAE /* BlockStore.swift in Sources */, 14839A8D202FE66A00A6CB34 /* PingMessage.swift in Sources */, + 29D8F1322333BF3000E72008 /* BitcoinAddress+HashSize.swift in Sources */, 0C0900362116A62E0077E9BC /* OP_0NOTEQUAL.swift in Sources */, 0C1DE15D211E6EB400FE8E43 /* OP_DROP.swift in Sources */, 299CB47320F0185500B1245C /* TransactionSignatureSerializer.swift in Sources */, diff --git a/Sources/BitcoinKit/Core/Address/Address.swift b/Sources/BitcoinKit/Core/Address/Address.swift new file mode 100644 index 00000000..b9d63a2a --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/Address.swift @@ -0,0 +1,50 @@ +// +// Address.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +public protocol Address: CustomStringConvertible { + var network: Network { get } + var hashType: BitcoinAddress.HashType { get } + var data: Data { get } + var legacy: String { get } + var cashaddr: String { get } +} + +extension Address { + @available(*, deprecated, message: "Always returns nil. If you need public key with address, please use PublicKey instead.") + public var publicKey: Data? { + return nil + } + + @available(*, deprecated, renamed: "legacy") + public var base58: String { + return legacy + } + + @available(*, deprecated, renamed: "hashType") + public var type: BitcoinAddress.HashType { + return hashType + } +} diff --git a/Sources/BitcoinKit/Core/Address/AddressError.swift b/Sources/BitcoinKit/Core/Address/AddressError.swift new file mode 100644 index 00000000..8d3f6fd4 --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/AddressError.swift @@ -0,0 +1,32 @@ +// +// AddressError.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +public enum AddressError: Error { + case invalid + case invalidScheme + case invalidVersionByte + case invalidDataSize +} diff --git a/Sources/BitcoinKit/Core/Address/BitcoinAddress+Cashaddr.swift b/Sources/BitcoinKit/Core/Address/BitcoinAddress+Cashaddr.swift new file mode 100644 index 00000000..c793bea8 --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/BitcoinAddress+Cashaddr.swift @@ -0,0 +1,87 @@ +// +// BitcoinAddress+Cashaddr.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension BitcoinAddress { + public var versionByte: VersionByte { + return VersionByte(hashType, hashSize) + } + + /// Bech32 encoded bitcoincash address format + public var cashaddr: String { + let scheme: BitcoinScheme + switch network { + case .mainnetBCH: scheme = .bitcoincash + case .testnetBCH: scheme = .bchtest + case .mainnetBTC: scheme = .bitcoincash + case .testnetBTC: scheme = .bchtest + default: + assertionFailure("cashaddr is only supported for \(network).") + scheme = .bitcoincash + } + return Bech32.encode(payload: [versionByte.rawValue] + data, prefix: scheme.rawValue) + } + + /// Creates a new BitcoinAddress with the bech32 encoded address with scheme. + /// + /// The network will be .mainnetBTC or .testnetBTC. This initializer performs + /// prefix validation, bech32 decode, and hash size validation. + /// + /// ``` + /// let address = try BitcoinAddress(cashaddr: "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") + /// ``` + /// + /// - Parameter bech32: Bech32 encoded String value to use as the source of the new + /// instance. It must come with scheme "bitcioncash:" or "bchtest:". + public init(cashaddr: String) throws { + // prefix validation and decode + guard let decoded = Bech32.decode(cashaddr) else { + throw AddressError.invalid + } + + switch BitcoinScheme(scheme: decoded.prefix) { + case .some(.bitcoincash): + network = .mainnetBCH + case .some(.bchtest): + network = .testnetBCH + default: + throw AddressError.invalidScheme + } + + let payload = decoded.data + guard let versionByte = VersionByte(payload[0]) else { + throw AddressError.invalidVersionByte + } + self.hashType = versionByte.hashType + self.hashSize = versionByte.hashSize + + self.data = payload.dropFirst() + + // validate data size + guard data.count == hashSize.sizeInBytes else { + throw AddressError.invalidVersionByte + } + } +} diff --git a/Sources/BitcoinKit/Core/Address/BitcoinAddress+HashSize.swift b/Sources/BitcoinKit/Core/Address/BitcoinAddress+HashSize.swift new file mode 100644 index 00000000..85b63226 --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/BitcoinAddress+HashSize.swift @@ -0,0 +1,106 @@ +// +// BitcoinAddress+HashSize.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension BitcoinAddress { + /// An object that represents the hash size of a cashaddr. + /// + /// The 3 least significant bits of VersionByte in cashaddr are the size bits. + /// In most cases, the size of the hash is 160 bis, however different sizes + /// are also possible. + /// https://www.bitcoincash.org/spec/cashaddr.html + public struct HashSize { + public let rawValue: UInt8 + /// Creates a new HashSize instance with 3 bits value. + /// + /// Size bits are the least 3 bits of the version byte. So the rawValue + /// should be 0-7. + /// - Parameter rawValue: UInt8 value of the 3 bits. + public init?(rawValue: UInt8) { + guard [0, 1, 2, 3, 4, 5, 6, 7].contains(rawValue) else { + return nil + } + self.rawValue = rawValue + } + + /// Creates a new HashSize instance with the actual size of the hash. + /// + /// The hash size in bits can be 160, 192, 224, 256, 320, 384, 448 or 512. + /// - Parameter sizeInBits: UInt8 value of the size of the hash in bits. + public init?(sizeInBits: Int) { + switch sizeInBits { + case 160: rawValue = 0 + case 192: rawValue = 1 + case 224: rawValue = 2 + case 256: rawValue = 3 + case 320: rawValue = 4 + case 384: rawValue = 5 + case 448: rawValue = 6 + case 512: rawValue = 7 + default: return nil + } + } + } +} + +extension BitcoinAddress.HashSize { + /// Hash size in bits + public var sizeInBits: Int { + switch rawValue { + case 0: return 160 + case 1: return 192 + case 2: return 224 + case 3: return 256 + case 4: return 320 + case 5: return 384 + case 6: return 448 + case 7: return 512 + default: fatalError("Unsupported size bits") + } + } + + /// Hash size in bytes + public var sizeInBytes: Int { + return sizeInBits / 8 + } +} + +extension BitcoinAddress.HashSize: Equatable { + // swiftlint:disable operator_whitespace + public static func ==(lhs: BitcoinAddress.HashSize, rhs: BitcoinAddress.HashSize) -> Bool { + return lhs.rawValue == rhs.rawValue + } +} + +extension BitcoinAddress.HashSize { + public static let bits160: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 160)! + public static let bits192: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 192)! + public static let bits224: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 224)! + public static let bits256: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 256)! + public static let bits320: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 320)! + public static let bits384: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 384)! + public static let bits448: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 448)! + public static let bits512: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 512)! +} diff --git a/Sources/BitcoinKit/Core/Keys/AddressType.swift b/Sources/BitcoinKit/Core/Address/BitcoinAddress+HashType.swift similarity index 53% rename from Sources/BitcoinKit/Core/Keys/AddressType.swift rename to Sources/BitcoinKit/Core/Address/BitcoinAddress+HashType.swift index 02fb3d68..87567727 100644 --- a/Sources/BitcoinKit/Core/Keys/AddressType.swift +++ b/Sources/BitcoinKit/Core/Address/BitcoinAddress+HashType.swift @@ -1,5 +1,5 @@ // -// AddressType.swift +// BitcoinAddress+HashType.swift // // Copyright © 2018 BitcoinKit developers // @@ -24,29 +24,15 @@ import Foundation -public class AddressType { - static let pubkeyHash: AddressType = PubkeyHash() - static let scriptHash: AddressType = ScriptHash() - - var versionByte: UInt8 { return 0 } - var versionByte160: UInt8 { return versionByte + 0 } - var versionByte192: UInt8 { return versionByte + 1 } - var versionByte224: UInt8 { return versionByte + 2 } - var versionByte256: UInt8 { return versionByte + 3 } - var versionByte320: UInt8 { return versionByte + 4 } - var versionByte384: UInt8 { return versionByte + 5 } - var versionByte448: UInt8 { return versionByte + 6 } - var versionByte512: UInt8 { return versionByte + 7 } -} - -extension AddressType: Equatable { - public static func == (lhs: AddressType, rhs: AddressType) -> Bool { - return lhs.versionByte == rhs.versionByte +extension BitcoinAddress { + /// An object that represents the hash type of a cashaddr. + /// + /// The 4 bits, from the second to the fifth [-XXXX---], are the type bits. + /// So the rawValue should be 0, 8, 16, ..., 120. However, only 0 and 8 are + /// supported for now. Further types will be added as new features are added. + /// https://www.bitcoincash.org/spec/cashaddr.html + public enum HashType: UInt8 { + case pubkeyHash = 0 + case scriptHash = 8 } } -public class PubkeyHash: AddressType { - public override var versionByte: UInt8 { return 0 } -} -public class ScriptHash: AddressType { - public override var versionByte: UInt8 { return 8 } -} diff --git a/Sources/BitcoinKit/Core/Address/BitcoinAddress+Legacy.swift b/Sources/BitcoinKit/Core/Address/BitcoinAddress+Legacy.swift new file mode 100644 index 00000000..bf28bd8f --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/BitcoinAddress+Legacy.swift @@ -0,0 +1,86 @@ +// +// BitcoinAddress+Legacy.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension BitcoinAddress { + /// Base58Check encoded bitcoin address format + public var legacy: String { + switch hashType { + case .pubkeyHash: + return Base58Check.encode([network.pubkeyhash] + data) + case .scriptHash: + return Base58Check.encode([network.pubkeyhash] + data) + } + } + + /// Creates a new BitcoinAddress instance with Base58Check encoded address. + /// + /// The network will be .mainnetBTC or .testnetBTC. This initializer performs + /// base58check and hash size validation. + /// ``` + /// let address = try BitcoinAddress(legacy: "1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") + /// ``` + /// + /// - Parameter legacy: Base58Check encoded String value to use as the source of the new + /// instance. It must be without scheme. + public init(legacy: String) throws { + // Hash size is 160 bits + self.hashSize = .bits160 + + // Base58Check decode + guard let pubKeyHash = Base58Check.decode(legacy) else { + throw AddressError.invalid + } + + let networkVersionByte = pubKeyHash[0] + + // Network + switch networkVersionByte { + case Network.mainnetBTC.pubkeyhash, Network.mainnetBTC.scripthash: + network = .mainnetBTC + case Network.testnetBTC.pubkeyhash, Network.testnetBTC.scripthash: + network = .testnetBTC + default: + throw AddressError.invalidVersionByte + } + + // hash type + switch networkVersionByte { + case Network.mainnetBTC.pubkeyhash, Network.testnetBTC.pubkeyhash: + hashType = .pubkeyHash + case Network.mainnetBTC.scripthash, Network.testnetBTC.scripthash: + hashType = .scriptHash + default: + throw AddressError.invalidVersionByte + } + + self.data = pubKeyHash.dropFirst() + + // validate data size + guard data.count == hashSize.sizeInBytes else { + throw AddressError.invalid + } + } +} diff --git a/Sources/BitcoinKit/Core/Address/BitcoinAddress+VersionByte.swift b/Sources/BitcoinKit/Core/Address/BitcoinAddress+VersionByte.swift new file mode 100644 index 00000000..71c44b6e --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/BitcoinAddress+VersionByte.swift @@ -0,0 +1,83 @@ +// +// BitcoinAddress+VersionByte.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +public extension BitcoinAddress { + /// An object that represents the version byte of a cashaddr. + /// + /// The most signficant bit is reserved and must be 0. The 4 next bits indicate the type of address and the 3 least significant bits indicate the size of the hash. + /// https://www.bitcoincash.org/spec/cashaddr.html + struct VersionByte { + /// Version byte raw value + public let rawValue: UInt8 + /// Hash type (P2PKH or P2SH) + public let hashType: HashType + /// Hash Size + public let hashSize: HashSize + + /// Creates a new VersionByte instance from type and size. + /// + /// - Parameters: + /// - hashType: The type of the hash + /// - hashSize: The size of the hash + public init(_ hashType: HashType, _ hashSize: HashSize) { + self.rawValue = hashType.rawValue + hashSize.rawValue + self.hashType = hashType + self.hashSize = hashSize + } + + /// Creates a new VersionByte instance from a raw UInt8 byte value. + /// + /// - Parameters: + /// - rawValue: The actual version byte + public init?(_ rawValue: UInt8) { + // X------- (The first bit) is zero + // -XXXX--- (Next four bits) are type bits + // -----XXX (The least three bits) are size bits + let firstBit: UInt8 = rawValue & 0b10000000 + let typeBits: UInt8 = rawValue & 0b01111000 + let sizeBits: UInt8 = rawValue & 0b00000111 + guard firstBit == 0 else { + return nil + } + guard let hashType = HashType(rawValue: typeBits) else { + return nil + } + guard let hashSize = HashSize(rawValue: sizeBits) else { + return nil + } + self.rawValue = rawValue + self.hashType = hashType + self.hashSize = hashSize + } + } +} + +extension BitcoinAddress.VersionByte: Equatable { + // swiftlint:disable operator_whitespace + public static func ==(lhs: BitcoinAddress.VersionByte, rhs: BitcoinAddress.VersionByte) -> Bool { + return lhs.rawValue == rhs.rawValue + } +} diff --git a/Sources/BitcoinKit/Core/Address/BitcoinAddress+deprecated.swift b/Sources/BitcoinKit/Core/Address/BitcoinAddress+deprecated.swift new file mode 100644 index 00000000..b2c9ae9e --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/BitcoinAddress+deprecated.swift @@ -0,0 +1,35 @@ +// +// BitcoinAddress+deprecated.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension BitcoinAddress: Address { + @available(*, deprecated, renamed: "legacy") + public var base58: String { return legacy } + + @available(*, unavailable, message: "Use init(cashaddr:) or init(legacy:) instead.") + public init(_ string: String) { + fatalError("init(_:) is not supported anymore. Please use init(cashaddr:) or init(legacy:) instead.") + } +} diff --git a/Sources/BitcoinKit/Core/Address/BitcoinAddress.swift b/Sources/BitcoinKit/Core/Address/BitcoinAddress.swift new file mode 100644 index 00000000..0b2a2590 --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/BitcoinAddress.swift @@ -0,0 +1,97 @@ +// +// BitcoinAddress.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// BitcoinAddress will be renamed to `Address` in ver.2.0.0. The model to represent Bitcoin address. +/// +/// ``` +/// // Initialize from legacy address (Base58Check format) string +/// let legacy = try BitcoinAddress(legacy: "1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") +/// +/// // Initialize from cash address (Cashaddr format) string +/// let cashaddr = try BitcoinAddress(cashaddr: "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") +/// +/// // Initialize from data +/// let p2pkhAddress = try BitcoinAddress(data: pubkeyHash, type: .pubkeyHash, network: .mainnetBCH) +/// let p2shAddress = try BitcoinAddress(data: scriptHash, type: .scriptHash, network: .mainnetBCH) +/// ``` +public struct BitcoinAddress { + public let data: Data + public let network: Network + + // Bitcoin Address parameter + public let hashType: HashType + public let hashSize: HashSize + + /// Creates a new BitcoinAddress instance with raw parameters. + /// + /// This initializer performs hash size validation. + /// ``` + /// // Initialize address from raw parameters + /// let address = try BitcoinAddress(data: pubkeyHash, + /// type: .pubkeyHash, + /// network: .mainnetBCH) + /// ``` + /// + /// - Parameters: + /// - data: The hash of public key or script + /// - hashType: .pubkeyHash or .scriptHash + /// - network: BitcoinCash network .mainnetBCH or .testnetBCH is expected. But you can + /// also use other network. + public init(data: Data, hashType: HashType, network: Network) throws { + guard let hashSize = HashSize(sizeInBits: data.count * 8) else { + throw AddressError.invalidDataSize + } + + self.data = data + self.hashType = hashType + self.hashSize = hashSize + + self.network = network + } +} + +extension BitcoinAddress: CustomStringConvertible { + public var description: String { + switch network { + case .mainnetBCH, .testnetBCH: + return cashaddr + default: + return legacy + } + } +} + +extension BitcoinAddress: Equatable { + public static func == (lhs: BitcoinAddress, rhs: BitcoinAddress) -> Bool { + return lhs.data == rhs.data + && lhs.hashType == rhs.hashType + && lhs.hashSize == rhs.hashSize + } +} + +#if os(iOS) || os(tvOS) || os(watchOS) +extension BitcoinAddress: QRCodeConvertible {} +#endif diff --git a/Sources/BitcoinKit/Core/Address/BitcoinScheme.swift b/Sources/BitcoinKit/Core/Address/BitcoinScheme.swift new file mode 100644 index 00000000..ba3be93c --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/BitcoinScheme.swift @@ -0,0 +1,36 @@ +// +// BitcoinScheme.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +public enum BitcoinScheme: String { + case bitcoincash + case bchtest + case bitcoin + case none = "" + + public init?(scheme: String) { + self.init(rawValue: scheme.lowercased()) + } +} diff --git a/Sources/BitcoinKit/Core/Address/Encoding/Base58.swift b/Sources/BitcoinKit/Core/Address/Encoding/Base58.swift new file mode 100644 index 00000000..b5ed3742 --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/Encoding/Base58.swift @@ -0,0 +1,47 @@ +// +// Base58.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +private struct _Base58: Encoding { + static let baseAlphabets = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + static var zeroAlphabet: Character = "1" + static var base: Int = 58 + + static func sizeFromByte(size: Int) -> Int { + return size * 138 / 100 + 1 + } + static func sizeFromBase(size: Int) -> Int { + return size * 733 / 1000 + 1 + } +} + +public struct Base58 { + public static func encode(_ bytes: Data) -> String { + return _Base58.encode(bytes) + } + public static func decode(_ string: String) -> Data? { + return _Base58.decode(string) + } +} diff --git a/Sources/BitcoinKit/Core/Address/Encoding/Base58Check.swift b/Sources/BitcoinKit/Core/Address/Encoding/Base58Check.swift new file mode 100644 index 00000000..f550b533 --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/Encoding/Base58Check.swift @@ -0,0 +1,74 @@ +// +// Base58Check.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// A set of Base58Check coding methods. +/// +/// ``` +/// // Encode bytes to address +/// let address = Base58Check.encode([versionByte] + pubkeyHash) +/// +/// // Decode address to bytes +/// guard let payload = Base58Check.decode(address) else { +/// // Invalid checksum or Base58 coding +/// throw SomeError() +/// } +/// let versionByte = payload[0] +/// let pubkeyHash = payload.dropFirst() +/// ``` +public struct Base58Check { + /// Encodes the data to Base58Check encoded string + /// + /// Puts checksum bytes to the original data and then, encode the combined + /// data to Base58 string. + /// ``` + /// let address = Base58Check.encode([versionByte] + pubkeyHash) + /// ``` + public static func encode(_ payload: Data) -> String { + let checksum: Data = Crypto.sha256sha256(payload).prefix(4) + return Base58.encode(payload + checksum) + } + + /// Decode the Base58 encoded String value to original payload + /// + /// First validate if checksum bytes are the first 4 bytes of the sha256(sha256(payload)). + /// If it's valid, returns the original payload. + /// ``` + /// let payload = Base58Check.decode(base58checkText) + /// ``` + public static func decode(_ string: String) -> Data? { + guard let raw = Base58.decode(string) else { + return nil + } + let checksum = raw.suffix(4) + let payload = raw.dropLast(4) + let checksumConfirm = Crypto.sha256sha256(payload).prefix(4) + guard checksum == checksumConfirm else { + return nil + } + + return payload + } +} diff --git a/Sources/BitcoinKit/Core/Keys/Encoding.swift b/Sources/BitcoinKit/Core/Address/Encoding/Bech32.swift similarity index 50% rename from Sources/BitcoinKit/Core/Keys/Encoding.swift rename to Sources/BitcoinKit/Core/Address/Encoding/Bech32.swift index 69b7ec96..2593b6af 100644 --- a/Sources/BitcoinKit/Core/Keys/Encoding.swift +++ b/Sources/BitcoinKit/Core/Address/Encoding/Bech32.swift @@ -1,191 +1,101 @@ // -// Encoding.swift -// -// Copyright © 2018 Kishikawa Katsumi -// Copyright © 2018 BitcoinKit developers -// +// Bech32.swift +// +// Copyright © 2019 BitcoinKit developers +// // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // import Foundation -private protocol Encoding { - static var baseAlphabets: String { get } - static var zeroAlphabet: Character { get } - static var base: Int { get } - - // log(256) / log(base), rounded up - static func sizeFromByte(size: Int) -> Int - // log(base) / log(256), rounded up - static func sizeFromBase(size: Int) -> Int - - // Public - static func encode(_ bytes: Data) -> String - static func decode(_ string: String) -> Data? -} - -private struct _Base58: Encoding { - static let baseAlphabets = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - static var zeroAlphabet: Character = "1" - static var base: Int = 58 - - static func sizeFromByte(size: Int) -> Int { - return size * 138 / 100 + 1 - } - static func sizeFromBase(size: Int) -> Int { - return size * 733 / 1000 + 1 - } -} - -public struct Base58 { - public static func encode(_ bytes: Data) -> String { - return _Base58.encode(bytes) - } - public static func decode(_ string: String) -> Data? { - return _Base58.decode(string) - } -} - -// The Base encoding used is home made, and has some differences. Especially, -// leading zeros are kept as single zeros when conversion happens. -extension Encoding { - static func convertBytesToBase(_ bytes: Data) -> [UInt8] { - var length = 0 - let size = sizeFromByte(size: bytes.count) - var encodedBytes: [UInt8] = Array(repeating: 0, count: size) - - for b in bytes { - var carry = Int(b) - var i = 0 - for j in (0...encodedBytes.count - 1).reversed() where carry != 0 || i < length { - carry += 256 * Int(encodedBytes[j]) - encodedBytes[j] = UInt8(carry % base) - carry /= base - i += 1 - } - - assert(carry == 0) - - length = i - } - - var zerosToRemove = 0 - for b in encodedBytes { - if b != 0 { break } - zerosToRemove += 1 - } - - encodedBytes.removeFirst(zerosToRemove) - return encodedBytes - } - - static func encode(_ bytes: Data) -> String { - var bytes = bytes - var zerosCount = 0 - - for b in bytes { - if b != 0 { break } - zerosCount += 1 - } - - bytes.removeFirst(zerosCount) - - let encodedBytes = convertBytesToBase(bytes) - - var str = "" - while 0 < zerosCount { - str += String(zeroAlphabet) - zerosCount -= 1 - } - - for b in encodedBytes { - let index = String.Index(utf16Offset: Int(b), in: baseAlphabets) - str += String(baseAlphabets[index]) - } - - return str - } - - static func decode(_ string: String) -> Data? { - guard !string.isEmpty else { return nil } - - var zerosCount = 0 - var length = 0 - for c in string { - if c != zeroAlphabet { break } - zerosCount += 1 - } - let size = sizeFromBase(size: string.lengthOfBytes(using: .utf8) - zerosCount) - var decodedBytes: [UInt8] = Array(repeating: 0, count: size) - for c in string { - guard let baseIndex: Int = baseAlphabets.firstIndex(of: c)?.utf16Offset(in: baseAlphabets) else { return nil } - var carry = baseIndex - var i = 0 - for j in (0...decodedBytes.count - 1).reversed() where carry != 0 || i < length { - carry += base * Int(decodedBytes[j]) - decodedBytes[j] = UInt8(carry % 256) - carry /= 256 - i += 1 - } - - assert(carry == 0) - length = i - } - - // skip leading zeros - var zerosToRemove = 0 - - for b in decodedBytes { - if b != 0 { break } - zerosToRemove += 1 - } - decodedBytes.removeFirst(zerosToRemove) - - return Data(repeating: 0, count: zerosCount) + Data(decodedBytes) - } -} - +/// A set of Bech32 coding methods. +/// +/// ``` +/// // Encode bytes to address +/// let cashaddr: String = Bech32.encode(payload: [versionByte] + pubkeyHash, +/// prefix: "bitcoincash") +/// +/// // Decode address to bytes +/// guard let payload: Data = Bech32.decode(text: address) else { +/// // Invalid checksum or Bech32 coding +/// throw SomeError() +/// } +/// let versionByte = payload[0] +/// let pubkeyHash = payload.dropFirst() +/// ``` public struct Bech32 { - private static let base32Alphabets = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" - - public static func encode(_ bytes: Data, prefix: String, seperator: String = ":") -> String { - let payload = convertTo5bit(data: bytes, pad: true) - let checksum: Data = createChecksum(prefix: prefix, payload: payload) // Data of [UInt5] - let combined: Data = payload + checksum // Data of [UInt5] + internal static let base32Alphabets = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + + /// Encodes the data to Bech32 encoded string + /// + /// Creates checksum bytes from the prefix and the payload, and then puts the + /// checksum bytes to the original data. Then, encode the combined data to + /// Base32 string. At last, returns the combined string of prefix, separator + /// and the encoded base32 text. + /// ``` + /// let address = Base58Check.encode(payload: [versionByte] + pubkeyHash, + /// prefix: "bitcoincash") + /// ``` + /// - Parameters: + /// - payload: The data to encode + /// - prefix: The prefix of the encoded text. It is also used to create checksum. + /// - separator: separator that separates prefix and Base32 encoded text + public static func encode(payload: Data, prefix: String, separator: String = ":") -> String { + let payloadUint5 = convertTo5bit(data: payload, pad: true) + let checksumUint5: Data = createChecksum(prefix: prefix, payload: payloadUint5) // Data of [UInt5] + let combined: Data = payloadUint5 + checksumUint5 // Data of [UInt5] var base32 = "" for b in combined { let index = String.Index(utf16Offset: Int(b), in: base32Alphabets) base32 += String(base32Alphabets[index]) } - return prefix + seperator + base32 + return prefix + separator + base32 } - // string : "bitcoincash:qql8zpwglr3q5le9jnjxkmypefaku39dkygsx29fzk" - public static func decode(_ string: String, seperator: String = ":") -> (prefix: String, data: Data)? { + @available(*, unavailable, renamed: "encode(payload:prefix:separator:)") + public static func encode(_ bytes: Data, prefix: String, seperator: String = ":") -> String { + return encode(payload: bytes, prefix: prefix, separator: seperator) + } + + /// Decodes the Bech32 encoded string to original payload + /// + /// ``` + /// // Decode address to bytes + /// guard let payload: Data = Bech32.decode(text: address) else { + /// // Invalid checksum or Bech32 coding + /// throw SomeError() + /// } + /// let versionByte = payload[0] + /// let pubkeyHash = payload.dropFirst() + /// ``` + /// - Parameters: + /// - string: The data to encode + /// - separator: separator that separates prefix and Base32 encoded text + public static func decode(_ string: String, separator: String = ":") -> (prefix: String, data: Data)? { // We can't have empty string. // Bech32 should be uppercase only / lowercase only. guard !string.isEmpty && [string.lowercased(), string.uppercased()].contains(string) else { return nil } - let components = string.components(separatedBy: seperator) + let components = string.components(separatedBy: separator) // We can only handle string contains both scheme and base32 guard components.count == 2 else { return nil @@ -213,12 +123,16 @@ public struct Bech32 { } return (prefix, Data(bytes)) } + @available(*, unavailable, renamed: "decode(string:separator:)") + public static func decode(_ string: String, seperator: String = ":") -> (prefix: String, data: Data)? { + return decode(string, separator: seperator) + } - private static func verifyChecksum(prefix: String, payload: Data) -> Bool { + internal static func verifyChecksum(prefix: String, payload: Data) -> Bool { return PolyMod(expand(prefix) + payload) == 0 } - private static func expand(_ prefix: String) -> Data { + internal static func expand(_ prefix: String) -> Data { var ret: Data = Data() let buf: [UInt8] = Array(prefix.utf8) for b in buf { @@ -228,7 +142,7 @@ public struct Bech32 { return ret } - private static func createChecksum(prefix: String, payload: Data) -> Data { + internal static func createChecksum(prefix: String, payload: Data) -> Data { let enc: Data = expand(prefix) + payload + Data(repeating: 0, count: 8) let mod: UInt64 = PolyMod(enc) var ret: Data = Data() @@ -238,7 +152,7 @@ public struct Bech32 { return ret } - private static func PolyMod(_ data: Data) -> UInt64 { + internal static func PolyMod(_ data: Data) -> UInt64 { var c: UInt64 = 1 for d in data { let c0: UInt8 = UInt8(c >> 35) @@ -252,7 +166,7 @@ public struct Bech32 { return c ^ 1 } - private static func convertTo5bit(data: Data, pad: Bool) -> Data { + internal static func convertTo5bit(data: Data, pad: Bool) -> Data { var acc = Int() var bits = UInt8() let maxv: Int = 31 // 31 = 0x1f = 00011111 @@ -300,7 +214,7 @@ public struct Bech32 { return Data(converted) } - private enum DecodeError: Error { + internal enum DecodeError: Error { case invalidCharacter case invalidBits } diff --git a/Sources/BitcoinKit/Core/Address/Encoding/Encoding.swift b/Sources/BitcoinKit/Core/Address/Encoding/Encoding.swift new file mode 100644 index 00000000..eaf4f0f0 --- /dev/null +++ b/Sources/BitcoinKit/Core/Address/Encoding/Encoding.swift @@ -0,0 +1,140 @@ +// +// Encoding.swift +// +// Copyright © 2018 Kishikawa Katsumi +// Copyright © 2018 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +internal protocol Encoding { + static var baseAlphabets: String { get } + static var zeroAlphabet: Character { get } + static var base: Int { get } + + // log(256) / log(base), rounded up + static func sizeFromByte(size: Int) -> Int + // log(base) / log(256), rounded up + static func sizeFromBase(size: Int) -> Int + + // Public + static func encode(_ bytes: Data) -> String + static func decode(_ string: String) -> Data? +} + +// The Base encoding used is home made, and has some differences. Especially, +// leading zeros are kept as single zeros when conversion happens. +extension Encoding { + static func convertBytesToBase(_ bytes: Data) -> [UInt8] { + var length = 0 + let size = sizeFromByte(size: bytes.count) + var encodedBytes: [UInt8] = Array(repeating: 0, count: size) + + for b in bytes { + var carry = Int(b) + var i = 0 + for j in (0...encodedBytes.count - 1).reversed() where carry != 0 || i < length { + carry += 256 * Int(encodedBytes[j]) + encodedBytes[j] = UInt8(carry % base) + carry /= base + i += 1 + } + + assert(carry == 0) + + length = i + } + + var zerosToRemove = 0 + for b in encodedBytes { + if b != 0 { break } + zerosToRemove += 1 + } + + encodedBytes.removeFirst(zerosToRemove) + return encodedBytes + } + + static func encode(_ bytes: Data) -> String { + var bytes = bytes + var zerosCount = 0 + + for b in bytes { + if b != 0 { break } + zerosCount += 1 + } + + bytes.removeFirst(zerosCount) + + let encodedBytes = convertBytesToBase(bytes) + + var str = "" + while 0 < zerosCount { + str += String(zeroAlphabet) + zerosCount -= 1 + } + + for b in encodedBytes { + let index = String.Index(utf16Offset: Int(b), in: baseAlphabets) + str += String(baseAlphabets[index]) + } + + return str + } + + static func decode(_ string: String) -> Data? { + guard !string.isEmpty else { return nil } + + var zerosCount = 0 + var length = 0 + for c in string { + if c != zeroAlphabet { break } + zerosCount += 1 + } + let size = sizeFromBase(size: string.lengthOfBytes(using: .utf8) - zerosCount) + var decodedBytes: [UInt8] = Array(repeating: 0, count: size) + for c in string { + guard let baseIndex: Int = baseAlphabets.firstIndex(of: c)?.utf16Offset(in: baseAlphabets) else { return nil } + var carry = baseIndex + var i = 0 + for j in (0...decodedBytes.count - 1).reversed() where carry != 0 || i < length { + carry += base * Int(decodedBytes[j]) + decodedBytes[j] = UInt8(carry % 256) + carry /= 256 + i += 1 + } + + assert(carry == 0) + length = i + } + + // skip leading zeros + var zerosToRemove = 0 + + for b in decodedBytes { + if b != 0 { break } + zerosToRemove += 1 + } + decodedBytes.removeFirst(zerosToRemove) + + return Data(repeating: 0, count: zerosCount) + Data(decodedBytes) + } +} diff --git a/Sources/BitcoinKit/Core/BlockChain.swift b/Sources/BitcoinKit/Core/BlockChain.swift index 9254c7ce..d74c14c9 100644 --- a/Sources/BitcoinKit/Core/BlockChain.swift +++ b/Sources/BitcoinKit/Core/BlockChain.swift @@ -46,7 +46,7 @@ public class BlockChain { try blockStore.addTransaction(transaction, hash: hash) } - public func calculateBalance(address: Address) throws -> Int64 { + public func calculateBalance(address: BitcoinAddress) throws -> Int64 { return try blockStore.calculateBalance(address: address) } diff --git a/Sources/BitcoinKit/Core/BlockStore.swift b/Sources/BitcoinKit/Core/BlockStore.swift index 93228938..54f1e0f1 100644 --- a/Sources/BitcoinKit/Core/BlockStore.swift +++ b/Sources/BitcoinKit/Core/BlockStore.swift @@ -73,7 +73,7 @@ public class SQLiteBlockStore: BlockStore { private var database: OpaquePointer? private var statements = [String: OpaquePointer]() - public init(file: URL, network: Network = .testnet) throws { + public init(file: URL, network: Network = .testnetBCH) throws { self.network = network try execute { sqlite3_open(file.path, &database) } @@ -372,7 +372,7 @@ public class SQLiteBlockStore: BlockStore { try execute { txId.withUnsafeBytes { sqlite3_bind_blob(stmt, 5, $0.baseAddress.unsafelyUnwrapped, Int32(txId.count), SQLITE_TRANSIENT) } } if Script.isPublicKeyHashOut(output.lockingScript) { let pubKeyHash = Script.getPublicKeyHash(from: output.lockingScript) - let address = publicKeyHashToAddress(Data([network.pubkeyhash]) + pubKeyHash) + let address = Base58Check.encode([network.pubkeyhash] + pubKeyHash) try execute { sqlite3_bind_text(stmt, 6, address, -1, nil) } } @@ -396,7 +396,7 @@ public class SQLiteBlockStore: BlockStore { public func calculateBalance(address: Address) throws -> Int64 { let stmt = statements["calculateBalance"] - try execute { sqlite3_bind_text(stmt, 1, address.base58, -1, SQLITE_TRANSIENT) } + try execute { sqlite3_bind_text(stmt, 1, address.legacy, -1, SQLITE_TRANSIENT) } var balance: Int64 = 0 while sqlite3_step(stmt) == SQLITE_ROW { @@ -411,7 +411,7 @@ public class SQLiteBlockStore: BlockStore { public func transactions(address: Address) throws -> [Payment] { let stmt = statements["transactions"] - try execute { sqlite3_bind_text(stmt, 1, address.base58, -1, SQLITE_TRANSIENT) } + try execute { sqlite3_bind_text(stmt, 1, address.legacy, -1, SQLITE_TRANSIENT) } var payments = [Payment]() while sqlite3_step(stmt) == SQLITE_ROW { @@ -419,7 +419,15 @@ public class SQLiteBlockStore: BlockStore { let address = sqlite3_column_text(stmt, 1)! let index = sqlite3_column_int64(stmt, 2) let value = sqlite3_column_int64(stmt, 3) - payments.append(Payment(state: .received, index: index, amount: value, from: try! AddressFactory.create(String(cString: address)), to: try! AddressFactory.create(String(cString: address)), txid: Data(bytes: txid!, count: 32))) + payments.append( + Payment(state: .received, + index: index, + amount: value, + from: try! BitcoinAddress(legacy: String(cString: address)), + to: try! BitcoinAddress(legacy: String(cString: address)), + txid: Data(bytes: txid!, count: 32) + ) + ) } try execute { sqlite3_reset(stmt) } @@ -429,7 +437,7 @@ public class SQLiteBlockStore: BlockStore { public func unspentTransactions(address: Address) throws -> [Payment] { let stmt = statements["unspentTransactions"] - try execute { sqlite3_bind_text(stmt, 1, address.base58, -1, SQLITE_TRANSIENT) } + try execute { sqlite3_bind_text(stmt, 1, address.legacy, -1, SQLITE_TRANSIENT) } var payments = [Payment]() while sqlite3_step(stmt) == SQLITE_ROW { @@ -437,7 +445,16 @@ public class SQLiteBlockStore: BlockStore { let address = sqlite3_column_text(stmt, 1)! let index = sqlite3_column_int64(stmt, 2) let value = sqlite3_column_int64(stmt, 3) - payments.append(Payment(state: .received, index: index, amount: value, from: try! AddressFactory.create(String(cString: address)), to: try! AddressFactory.create(String(cString: address)), txid: Data(bytes: txid!, count: 32))) + payments.append( + Payment( + state: .received, + index: index, + amount: value, + from: try! BitcoinAddress(legacy: String(cString: address)), + to: try! BitcoinAddress(legacy: String(cString: address)), + txid: Data(bytes: txid!, count: 32) + ) + ) } try execute { sqlite3_reset(stmt) } diff --git a/Sources/BitcoinKit/Core/Helpers.swift b/Sources/BitcoinKit/Core/Helpers.swift index a1a98bd1..f88663fe 100644 --- a/Sources/BitcoinKit/Core/Helpers.swift +++ b/Sources/BitcoinKit/Core/Helpers.swift @@ -42,13 +42,3 @@ func pton(_ address: String) -> Data { _ = buffer.withUnsafeMutableBytes { memcpy($0.baseAddress.unsafelyUnwrapped, &addr, 16) } return buffer } - -/// Version = 1 byte of 0 (zero); on the test network, this is 1 byte of 111 -/// Key hash = Version concatenated with RIPEMD-160(SHA-256(public key)) -/// Checksum = 1st 4 bytes of SHA-256(SHA-256(Key hash)) -/// Bitcoin Address = Base58Encode(Key hash concatenated with Checksum) -func publicKeyHashToAddress(_ hash: Data) -> String { - let checksum = Crypto.sha256sha256(hash).prefix(4) - let address = Base58.encode(hash + checksum) - return address -} diff --git a/Sources/BitcoinKit/Core/Keys/Address.swift b/Sources/BitcoinKit/Core/Keys/Address.swift deleted file mode 100644 index e57bfbeb..00000000 --- a/Sources/BitcoinKit/Core/Keys/Address.swift +++ /dev/null @@ -1,215 +0,0 @@ -// -// Address.swift -// -// Copyright © 2018 Kishikawa Katsumi -// Copyright © 2018 BitcoinKit developers -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -import Foundation - -public protocol AddressProtocol { - var network: Network { get } - var type: AddressType { get } - var data: Data { get } - var publicKey: Data? { get } - - var base58: String { get } - var cashaddr: String { get } -} - -#if os(iOS) || os(tvOS) || os(watchOS) -public typealias Address = AddressProtocol & QRCodeConvertible -#else -public typealias Address = AddressProtocol -#endif - -public enum AddressError: Error { - case invalid - case invalidScheme - case invalidVersionByte -} - -public struct LegacyAddress: Address { - public let network: Network - public let type: AddressType - public let data: Data - public let base58: Base58Check - public let cashaddr: String - public let publicKey: Data? - - public typealias Base58Check = String - - public init(data: Data, type: AddressType, network: Network, base58: String, bech32: String, publicKey: Data?) { - self.data = data - self.type = type - self.network = network - self.base58 = base58 - self.cashaddr = bech32 - self.publicKey = publicKey - } - - public init(_ base58: Base58Check) throws { - guard let raw = Base58.decode(base58) else { - throw AddressError.invalid - } - let checksum = raw.suffix(4) - let pubKeyHash = raw.dropLast(4) - let checksumConfirm = Crypto.sha256sha256(pubKeyHash).prefix(4) - guard checksum == checksumConfirm else { - throw AddressError.invalid - } - - let network: Network - let type: AddressType - let addressPrefix = pubKeyHash[0] - switch addressPrefix { - case Network.mainnet.pubkeyhash: - network = .mainnet - type = .pubkeyHash - case Network.testnet.pubkeyhash: - network = .testnet - type = .pubkeyHash - case Network.mainnet.scripthash: - network = .mainnet - type = .scriptHash - case Network.testnet.scripthash: - network = .testnet - type = .scriptHash - default: - throw AddressError.invalidVersionByte - } - - self.network = network - self.type = type - self.publicKey = nil - self.data = pubKeyHash.dropFirst() - self.base58 = base58 - - // cashaddr - switch type { - case .pubkeyHash, .scriptHash: - let payload = Data([type.versionByte160]) + self.data - self.cashaddr = Bech32.encode(payload, prefix: network.scheme) - default: - self.cashaddr = "" - } - } - public init(data: Data, type: AddressType, network: Network) { - let addressData: Data = [type.versionByte] + data - self.data = data - self.type = type - self.network = network - self.publicKey = nil - self.base58 = publicKeyHashToAddress(addressData) - self.cashaddr = Bech32.encode(addressData, prefix: network.scheme) - } -} - -extension LegacyAddress: Equatable { - public static func == (lhs: LegacyAddress, rhs: LegacyAddress) -> Bool { - return lhs.network == rhs.network && lhs.data == rhs.data && lhs.type == rhs.type - } -} - -extension LegacyAddress: CustomStringConvertible { - public var description: String { - return base58 - } -} - -public struct Cashaddr: Address { - public let network: Network - public let type: AddressType - public let data: Data - public let base58: String - public let cashaddr: CashaddrWithScheme - public let publicKey: Data? - - public typealias CashaddrWithScheme = String - - public init(data: Data, type: AddressType, network: Network, base58: String, bech32: CashaddrWithScheme, publicKey: Data?) { - self.data = data - self.type = type - self.network = network - self.base58 = base58 - self.cashaddr = bech32 - self.publicKey = publicKey - } - - public init(_ cashaddr: CashaddrWithScheme) throws { - guard let decoded = Bech32.decode(cashaddr) else { - throw AddressError.invalid - } - let (prefix, raw) = (decoded.prefix, decoded.data) - self.cashaddr = cashaddr - self.publicKey = nil - - switch prefix { - case Network.mainnet.scheme: - network = .mainnet - case Network.testnet.scheme: - network = .testnet - default: - throw AddressError.invalidScheme - } - - let versionByte = raw[0] - let hash = raw.dropFirst() - - guard hash.count == VersionByte.getSize(from: versionByte) else { - throw AddressError.invalidVersionByte - } - self.data = hash - guard let typeBits = VersionByte.TypeBits(rawValue: (versionByte & 0b01111000)) else { - throw AddressError.invalidVersionByte - } - - switch typeBits { - case .pubkeyHash: - type = .pubkeyHash - base58 = publicKeyHashToAddress(Data([network.pubkeyhash]) + data) - case .scriptHash: - type = .scriptHash - base58 = publicKeyHashToAddress(Data([network.scripthash]) + data) - } - } - public init(data: Data, type: AddressType, network: Network) { - let addressData: Data = [type.versionByte] + data - self.data = data - self.type = type - self.network = network - self.publicKey = nil - self.base58 = publicKeyHashToAddress(addressData) - self.cashaddr = Bech32.encode(addressData, prefix: network.scheme) - } -} - -extension Cashaddr: Equatable { - public static func == (lhs: Cashaddr, rhs: Cashaddr) -> Bool { - return lhs.network == rhs.network && lhs.data == rhs.data && lhs.type == rhs.type - } -} - -extension Cashaddr: CustomStringConvertible { - public var description: String { - return cashaddr - } -} diff --git a/Sources/BitcoinKit/Core/Keys/HDPrivateKey.swift b/Sources/BitcoinKit/Core/Keys/HDPrivateKey.swift index 8effc1db..47972ad4 100644 --- a/Sources/BitcoinKit/Core/Keys/HDPrivateKey.swift +++ b/Sources/BitcoinKit/Core/Keys/HDPrivateKey.swift @@ -73,8 +73,7 @@ public class HDPrivateKey { data += chainCode data += UInt8(0) data += raw - let checksum = Crypto.sha256sha256(data).prefix(4) - return Base58.encode(data + checksum) + return Base58Check.encode(data) } public func privateKey() -> PrivateKey { diff --git a/Sources/BitcoinKit/Core/Keys/HDPublicKey.swift b/Sources/BitcoinKit/Core/Keys/HDPublicKey.swift index 5e8a03c6..af5b3be5 100644 --- a/Sources/BitcoinKit/Core/Keys/HDPublicKey.swift +++ b/Sources/BitcoinKit/Core/Keys/HDPublicKey.swift @@ -39,7 +39,7 @@ public class HDPublicKey { public let raw: Data public let chainCode: Data - init(raw: Data, chainCode: Data, network: Network = .testnet, depth: UInt8, fingerprint: UInt32, childIndex: UInt32) { + init(raw: Data, chainCode: Data, network: Network = .testnetBCH, depth: UInt8, fingerprint: UInt32, childIndex: UInt32) { self.network = network self.raw = raw self.chainCode = chainCode @@ -56,8 +56,7 @@ public class HDPublicKey { data += childIndex.littleEndian data += chainCode data += raw - let checksum = Crypto.sha256sha256(data).prefix(4) - return Base58.encode(data + checksum) + return Base58Check.encode(data) } public func publicKey() -> PublicKey { diff --git a/Sources/BitcoinKit/Core/Keys/PrivateKey.swift b/Sources/BitcoinKit/Core/Keys/PrivateKey.swift index 2bd22b5d..6681856a 100644 --- a/Sources/BitcoinKit/Core/Keys/PrivateKey.swift +++ b/Sources/BitcoinKit/Core/Keys/PrivateKey.swift @@ -37,7 +37,7 @@ public struct PrivateKey { public let network: Network public let isPublicKeyCompressed: Bool - public init(network: Network = .testnet, isPublicKeyCompressed: Bool = true) { + public init(network: Network = .testnetBCH, isPublicKeyCompressed: Bool = true) { self.network = network self.isPublicKeyCompressed = isPublicKeyCompressed @@ -79,40 +79,30 @@ public struct PrivateKey { } public init(wif: String) throws { - guard let decoded = Base58.decode(wif) else { - throw PrivateKeyError.invalidFormat - } - let checksumDropped = decoded.prefix(decoded.count - 4) - guard checksumDropped.count == (1 + 32) || checksumDropped.count == (1 + 32 + 1) else { + guard var payload = Base58Check.decode(wif), + (payload.count == (1 + 32) || payload.count == (1 + 32 + 1)) else { throw PrivateKeyError.invalidFormat } - let addressPrefix = checksumDropped[0] + let addressPrefix = payload.popFirst()! switch addressPrefix { - case Network.mainnet.privatekey: - network = .mainnet - case Network.testnet.privatekey: - network = .testnet + case Network.mainnetBCH.privatekey: + network = .mainnetBCH + case Network.testnetBCH.privatekey: + network = .testnetBCH default: throw PrivateKeyError.invalidFormat } - let h = Crypto.sha256sha256(checksumDropped) - let calculatedChecksum = h.prefix(4) - let originalChecksum = decoded.suffix(4) - guard calculatedChecksum == originalChecksum else { - throw PrivateKeyError.invalidFormat - } - - // The life is not always easy. Somehow some people added one extra byte to a private key in Base58 to + // The life is not always easy. Somehow some people added one extra byte to a private key in Base58 to // let us know that the resulting public key must be compressed. - self.isPublicKeyCompressed = (checksumDropped.count == (1 + 32 + 1)) + self.isPublicKeyCompressed = (payload.count == (32 + 1)) // Private key itself is always 32 bytes. - data = checksumDropped.dropFirst().prefix(32) + data = payload.prefix(32) } - public init(data: Data, network: Network = .testnet, isPublicKeyCompressed: Bool = true) { + public init(data: Data, network: Network = .testnetBCH, isPublicKeyCompressed: Bool = true) { self.data = data self.network = network self.isPublicKeyCompressed = isPublicKeyCompressed @@ -144,8 +134,7 @@ public struct PrivateKey { // Add extra byte 0x01 in the end. payload += Int8(1) } - let checksum = Crypto.sha256sha256(payload).prefix(4) - return Base58.encode(payload + checksum) + return Base58Check.encode(payload) } public func sign(_ data: Data) -> Data { diff --git a/Sources/BitcoinKit/Core/Keys/PublicKey+Address.swift b/Sources/BitcoinKit/Core/Keys/PublicKey+Address.swift new file mode 100644 index 00000000..43fb678c --- /dev/null +++ b/Sources/BitcoinKit/Core/Keys/PublicKey+Address.swift @@ -0,0 +1,46 @@ +// +// PublicKey+Address.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension PublicKey { + @available(*, deprecated, message: "toLegacy() will be removed. Use toBitcoinAddress instead.") + public func toLegacy() -> LegacyAddress { + return toBitcoinAddress() + } + + @available(*, deprecated, message: "toCashaddr() will be removed. Use toBitcoinAddress instead.") + public func toCashaddr() -> Cashaddr { + return toBitcoinAddress() + } + + @available(*, deprecated, message: "toAddress() will be removed. Use toBitcoinAddress instead.") + public func toAddress() -> Address { + return toBitcoinAddress() + } + + public func toBitcoinAddress() -> BitcoinAddress { + return try! BitcoinAddress(data: pubkeyHash, hashType: .pubkeyHash, network: network) + } +} diff --git a/Sources/BitcoinKit/Core/Keys/PublicKey.swift b/Sources/BitcoinKit/Core/Keys/PublicKey.swift index 441bb275..f728daed 100644 --- a/Sources/BitcoinKit/Core/Keys/PublicKey.swift +++ b/Sources/BitcoinKit/Core/Keys/PublicKey.swift @@ -46,37 +46,6 @@ public struct PublicKey { let header = data[0] self.isCompressed = (header == 0x02 || header == 0x03) } - - /// Version = 1 byte of 0 (zero); on the test network, this is 1 byte of 111 - /// Key hash = Version concatenated with RIPEMD-160(SHA-256(public key)) - /// Checksum = 1st 4 bytes of SHA-256(SHA-256(Key hash)) - /// Bitcoin Address = Base58Encode(Key hash concatenated with Checksum) - private func base58() -> String { - let versionByte: Data = Data([network.pubkeyhash]) - return publicKeyHashToAddress(versionByte + pubkeyHash) - } - - private func bech32() -> String { - let versionByte: Data = Data([VersionByte.pubkeyHash160]) - return Bech32.encode(versionByte + pubkeyHash, prefix: network.scheme) - } - - public func toLegacy() -> LegacyAddress { - return LegacyAddress(data: pubkeyHash, type: .pubkeyHash, network: network, base58: base58(), bech32: bech32(), publicKey: data) - } - - public func toCashaddr() -> Cashaddr { - return Cashaddr(data: pubkeyHash, type: .pubkeyHash, network: network, base58: base58(), bech32: bech32(), publicKey: data) - } - - public func toAddress() -> Address { - switch network { - case .mainnet, .testnet: - return toCashaddr() - default: - return toLegacy() - } - } } extension PublicKey: Equatable { diff --git a/Sources/BitcoinKit/Core/Keys/VersionByte.swift b/Sources/BitcoinKit/Core/Keys/VersionByte.swift deleted file mode 100644 index b13f4c86..00000000 --- a/Sources/BitcoinKit/Core/Keys/VersionByte.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// VersionByte.swift -// -// Copyright © 2018 BitcoinKit developers -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -import Foundation - -public class VersionByte { - static let pubkeyHash160: UInt8 = PubkeyHash160().bytes - static let scriptHash160: UInt8 = ScriptHash160().bytes - var bytes: UInt8 { - return type.rawValue + size.rawValue - } - - public var type: TypeBits { return .pubkeyHash } - public var size: SizeBits { return .size160 } - - public static func getSize(from versionByte: UInt8) -> Int { - guard let sizeBits = SizeBits(rawValue: versionByte & 0x07) else { - return 0 - } - switch sizeBits { - case .size160: - return 20 - case .size192: - return 24 - case .size224: - return 28 - case .size256: - return 32 - case .size320: - return 40 - case .size384: - return 48 - case .size448: - return 56 - case .size512: - return 64 - } - } - - // First 1 bit is zero - // Next 4bits - public enum TypeBits: UInt8 { - case pubkeyHash = 0 - case scriptHash = 8 - } - - // The least 3bits - public enum SizeBits: UInt8 { - case size160 = 0 - case size192 = 1 - case size224 = 2 - case size256 = 3 - case size320 = 4 - case size384 = 5 - case size448 = 6 - case size512 = 7 - } -} - -public class PubkeyHash160: VersionByte { - public override var size: SizeBits { return .size160 } - public override var type: TypeBits { return .pubkeyHash } -} -public class ScriptHash160: VersionByte { - public override var size: SizeBits { return .size160 } - public override var type: TypeBits { return .scriptHash } -} diff --git a/Sources/BitcoinKit/Core/Network.swift b/Sources/BitcoinKit/Core/Network.swift index 73c4f6dd..109967ec 100644 --- a/Sources/BitcoinKit/Core/Network.swift +++ b/Sources/BitcoinKit/Core/Network.swift @@ -26,8 +26,12 @@ import Foundation open class Network { + @available(*, deprecated, renamed: "mainnetBCH") public static let mainnet: Network = BCHMainnet() + @available(*, deprecated, renamed: "testnetBCH") public static let testnet: Network = BCHTestnet() + public static let mainnetBCH: Network = BCHMainnet() + public static let testnetBCH: Network = BCHTestnet() public static let mainnetBTC: Network = BTCMainnet() public static let testnetBTC: Network = BTCTestnet() @@ -123,9 +127,6 @@ public class BTCTestnet: Testnet { override public var magic: UInt32 { return 0x0b110907 } - public override var coinType: CoinType { - return .testnet - } override public var dnsSeeds: [String] { return [ "testnet-seed.bitcoin.jonasschnelli.ch", // Jonas Schnelli @@ -176,9 +177,6 @@ public class BCHTestnet: Testnet { override public var magic: UInt32 { return 0xf4e5f3f4 } - public override var coinType: CoinType { - return .testnet - } override public var dnsSeeds: [String] { return [ "testnet-seed.bitcoinabc.org", @@ -264,6 +262,9 @@ public class Testnet: Network { override public var alias: String { return "regtest" } + public override var coinType: CoinType { + return .testnet + } override public var pubkeyhash: UInt8 { return 0x6f } diff --git a/Sources/BitcoinKit/Core/PaymentURI.swift b/Sources/BitcoinKit/Core/PaymentURI.swift index 90237df8..7fd4866e 100644 --- a/Sources/BitcoinKit/Core/PaymentURI.swift +++ b/Sources/BitcoinKit/Core/PaymentURI.swift @@ -40,15 +40,22 @@ public struct PaymentURI { case amount } + // swiftlint:disable:next cyclomatic_complexity public init(_ string: String) throws { - guard let components = URLComponents(string: string), let scheme = components.scheme, scheme.lowercased() == "bitcoin" else { + guard let components = URLComponents(string: string), + let scheme = components.scheme, + scheme.lowercased() == "bitcoin", + let url = components.url else { throw PaymentURIError.invalid } - guard let address = try? AddressFactory.create(components.path) else { + self.uri = url + if let cashaddr = try? BitcoinAddress(cashaddr: scheme + components.path) { + self.address = cashaddr + } else if let legacy = try? BitcoinAddress(legacy: components.path) { + self.address = legacy + } else { throw PaymentURIError.malformed(.address) } - self.address = address - self.uri = components.url! guard let queryItems = components.queryItems else { self.label = nil @@ -63,17 +70,16 @@ public struct PaymentURI { var amount: Decimal? var others = [String: String]() for queryItem in queryItems { - switch queryItem.name { - case Keys.label.rawValue: + switch Keys(rawValue: queryItem.name) { + case .some(.label): label = queryItem.value - case Keys.message.rawValue: + case .some(.message): message = queryItem.value - case Keys.amount.rawValue: - if let v = queryItem.value, let value = Decimal(string: v) { - amount = value - } else { + case .some(.amount): + guard let v = queryItem.value, let value = Decimal(string: v) else { throw PaymentURIError.malformed(.amount) } + amount = value default: if let value = queryItem.value { others[queryItem.name] = value diff --git a/Sources/BitcoinKit/Core/Keys/QRCodeConvertible.swift b/Sources/BitcoinKit/Core/QR/QRCodeConvertible.swift similarity index 100% rename from Sources/BitcoinKit/Core/Keys/QRCodeConvertible.swift rename to Sources/BitcoinKit/Core/QR/QRCodeConvertible.swift diff --git a/Sources/BitcoinKit/Core/Keys/QRCodeGenerator.swift b/Sources/BitcoinKit/Core/QR/QRCodeGenerator.swift similarity index 100% rename from Sources/BitcoinKit/Core/Keys/QRCodeGenerator.swift rename to Sources/BitcoinKit/Core/QR/QRCodeGenerator.swift diff --git a/Sources/BitcoinKit/Core/Keys/AddressFactory.swift b/Sources/BitcoinKit/Deprecated/AddressFactory.swift similarity index 80% rename from Sources/BitcoinKit/Core/Keys/AddressFactory.swift rename to Sources/BitcoinKit/Deprecated/AddressFactory.swift index e17a68cb..e5744a6b 100644 --- a/Sources/BitcoinKit/Core/Keys/AddressFactory.swift +++ b/Sources/BitcoinKit/Deprecated/AddressFactory.swift @@ -24,16 +24,15 @@ import Foundation +@available(*, deprecated, message: "AddressFactory will be removed in ver. 2.0.0. Use BitcoinAddress.init(legacy:) or BitcoinAddress.init(cashaddr:) instead.") public struct AddressFactory { public static func create(_ plainAddress: String) throws -> Address { do { - return try Cashaddr(plainAddress) - } catch AddressError.invalidVersionByte { - throw AddressError.invalidVersionByte - } catch AddressError.invalidScheme { - throw AddressError.invalidScheme + return try BitcoinAddress(cashaddr: plainAddress) } catch AddressError.invalid { - return try LegacyAddress(plainAddress) + return try BitcoinAddress(legacy: plainAddress) + } catch let e { + throw e } } } diff --git a/Sources/BitcoinKit/Deprecated/AddressType.swift b/Sources/BitcoinKit/Deprecated/AddressType.swift new file mode 100644 index 00000000..d0a8327e --- /dev/null +++ b/Sources/BitcoinKit/Deprecated/AddressType.swift @@ -0,0 +1,28 @@ +// +// AddressType.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +@available(*, deprecated, renamed: "BitcoinAddress.HashType") +public typealias AddressType = BitcoinAddress.HashType diff --git a/Sources/BitcoinKit/Deprecated/Cashaddr.swift b/Sources/BitcoinKit/Deprecated/Cashaddr.swift new file mode 100644 index 00000000..a49d3a11 --- /dev/null +++ b/Sources/BitcoinKit/Deprecated/Cashaddr.swift @@ -0,0 +1,39 @@ +// +// Cashaddr.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// The model to represent Bitcoin Cashaddr of bech32 format. +/// The network is supposed to be mainnetBCH or testnetBCH. +/// +/// ``` +/// // Initialize from bech32 string +/// let address1 = try Cashaddr("bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") +/// +/// // Initialize from data +/// let address2 = Cashaddr(data: pubkeyHash, type: .pubkeyHash, network: .mainnetBTC) +/// let address3 = Cashaddr(data: scriptHash, type: .scriptHash, network: .mainnetBTC) +/// ``` +@available(*, deprecated, renamed: "BitcoinAddress") +public typealias Cashaddr = BitcoinAddress diff --git a/Sources/BitcoinKit/Deprecated/LegacyAddress.swift b/Sources/BitcoinKit/Deprecated/LegacyAddress.swift new file mode 100644 index 00000000..d0177033 --- /dev/null +++ b/Sources/BitcoinKit/Deprecated/LegacyAddress.swift @@ -0,0 +1,39 @@ +// +// LegacyAddress.swift +// +// Copyright © 2019 BitcoinKit developers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// The model to represent Bitcoin LegacyAddress of base58Check format. +/// The network is supposed to be mainnetBTC or testnetBTC. +/// +/// ``` +/// // Initialize from base58check string +/// let address1 = try LegacyAddress("1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") +/// +/// // Initialize from data +/// let address2 = LegacyAddress(data: pubkeyHash, type: .pubkeyHash, network: .mainnetBTC) +/// let address3 = LegacyAddress(data: scriptHash, type: .scriptHash, network: .mainnetBTC) +/// ``` +@available(*, deprecated, renamed: "BitcoinAddress") +public typealias LegacyAddress = BitcoinAddress diff --git a/Sources/BitcoinKit/Mock/MockHelper.swift b/Sources/BitcoinKit/Mock/MockHelper.swift index 2a35272d..301893b0 100644 --- a/Sources/BitcoinKit/Mock/MockHelper.swift +++ b/Sources/BitcoinKit/Mock/MockHelper.swift @@ -42,8 +42,8 @@ public struct MockHelper { } public static func createTransaction(unspentTransaction: UnspentTransaction) -> Transaction { - let toAddress: Address = try! AddressFactory.create("1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx") - let changeAddress: Address = try! AddressFactory.create("1FQc5LdgGHMHEN9nwkjmz6tWkxhPpxBvBU") + let toAddress: BitcoinAddress = try! BitcoinAddress(legacy: "1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx") + let changeAddress: BitcoinAddress = try! BitcoinAddress(legacy: "1FQc5LdgGHMHEN9nwkjmz6tWkxhPpxBvBU") // 1. inputs let unsignedInputs = [TransactionInput(previousOutput: unspentTransaction.outpoint, signatureScript: Data(), diff --git a/Sources/BitcoinKit/Networking/Peer.swift b/Sources/BitcoinKit/Networking/Peer.swift index 28b64f4b..57753f81 100644 --- a/Sources/BitcoinKit/Networking/Peer.swift +++ b/Sources/BitcoinKit/Networking/Peer.swift @@ -66,11 +66,11 @@ public class Peer: NSObject, StreamDelegate { private var inputStream: InputStream! private var outputStream: OutputStream! - public convenience init(network: Network = .testnet) { + public convenience init(network: Network = .testnetBCH) { self.init(host: network.dnsSeeds[Int(arc4random_uniform(UInt32(network.dnsSeeds.count)))], network: network) } - public init(host: String, network: Network = .testnet) { + public init(host: String, network: Network = .testnetBCH) { self.host = host self.network = network latestBlockHash = network.genesisBlock diff --git a/Sources/BitcoinKit/Scripts/Script.swift b/Sources/BitcoinKit/Scripts/Script.swift index 751fdada..f07f8239 100644 --- a/Sources/BitcoinKit/Scripts/Script.swift +++ b/Sources/BitcoinKit/Scripts/Script.swift @@ -61,9 +61,9 @@ public class Script { .append(.OP_EQUAL) } - public func standardP2SHAddress(network: Network) -> Address { + public func standardP2SHAddress(network: Network) -> BitcoinAddress { let scriptHash: Data = Crypto.sha256ripemd160(data) - return Cashaddr(data: scriptHash, type: .scriptHash, network: network) + return try! BitcoinAddress(data: scriptHash, hashType: .scriptHash, network: network) } // Multisignature script attribute. @@ -100,7 +100,7 @@ public class Script { public convenience init?(address: Address) { self.init() - switch address.type { + switch address.hashType { case .pubkeyHash: // OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG do { @@ -121,8 +121,6 @@ public class Script { } catch { return nil } - default: - return nil } } @@ -304,7 +302,7 @@ public class Script { guard let data = pushedData(at: i) else { return } - let pubkey = PublicKey(bytes: data, network: .mainnet) + let pubkey = PublicKey(bytes: data, network: .mainnetBCH) pubkeys.append(pubkey) } @@ -321,17 +319,17 @@ public class Script { return chunks } - public func standardAddress(network: Network) -> Address? { - if isPayToPublicKeyHashScript { - guard let dataChunk = chunk(at: 2) as? DataChunk else { - return nil - } - return Cashaddr(data: dataChunk.pushedData, type: .pubkeyHash, network: network) - } else if isPayToScriptHashScript { - guard let dataChunk = chunk(at: 1) as? DataChunk else { - return nil - } - return Cashaddr(data: dataChunk.pushedData, type: .scriptHash, network: network) + public func standardAddress(network: Network) -> BitcoinAddress? { + if isPayToPublicKeyHashScript, + let pubkeyHash = pushedData(at: 2) { + return try? BitcoinAddress(data: pubkeyHash, + hashType: .pubkeyHash, + network: network) + } else if isPayToScriptHashScript, + let scriptHash = pushedData(at: 1) { + return try? BitcoinAddress(data: scriptHash, + hashType: .scriptHash, + network: network) } return nil } diff --git a/Sources/BitcoinKit/Wallet/HDWallet.swift b/Sources/BitcoinKit/Wallet/HDWallet.swift index 0270a433..1e46ddf0 100644 --- a/Sources/BitcoinKit/Wallet/HDWallet.swift +++ b/Sources/BitcoinKit/Wallet/HDWallet.swift @@ -80,9 +80,9 @@ open class HDWallet { // MARK: - Addresses /// [Cached] External addresses for receiving payment. - public private(set) var externalAddresses: [Address]! + public private(set) var externalAddresses: [BitcoinAddress]! /// [Cached] Internal addresses for change output. - public private(set) var internalAddresses: [Address]! + public private(set) var internalAddresses: [BitcoinAddress]! /// [Cached] Addresses combined both external and internal. public var addresses: [Address] { return externalAddresses + internalAddresses } @@ -111,8 +111,8 @@ open class HDWallet { self.internalPubKeys = internalPrivKeys.map { $0.publicKey() } // Address cache - self.externalAddresses = externalPubKeys.map { $0.toAddress() } - self.internalAddresses = internalPubKeys.map { $0.toAddress() } + self.externalAddresses = externalPubKeys.map { $0.toBitcoinAddress() } + self.internalAddresses = internalPubKeys.map { $0.toBitcoinAddress() } } public convenience init(seed: Data, @@ -169,7 +169,7 @@ open class HDWallet { /// [Non-Cache] Get address for index public func address(index: UInt32, chain: Chain) -> Address { - return pubKey(index: index, chain: chain).toAddress() + return pubKey(index: index, chain: chain).toBitcoinAddress() } /// Increment external index and update privkey/pubkey/address cache. @@ -177,7 +177,7 @@ open class HDWallet { let newIndex: UInt32 = externalIndex + value let newPrivKeys: [PrivateKey] = (externalIndex + 1...newIndex).map { privKey(index: $0, chain: .external) } let newPubKeys: [PublicKey] = newPrivKeys.map { $0.publicKey() } - let newAddresses: [Address] = newPubKeys.map { $0.toAddress() } + let newAddresses: [BitcoinAddress] = newPubKeys.map { $0.toBitcoinAddress() } externalIndex += value externalPrivKeys += newPrivKeys externalPubKeys += newPubKeys @@ -189,7 +189,7 @@ open class HDWallet { let newIndex: UInt32 = internalIndex + value let newPrivKeys: [PrivateKey] = (internalIndex + 1...newIndex).map { privKey(index: $0, chain: .internal) } let newPubKeys: [PublicKey] = newPrivKeys.map { $0.publicKey() } - let newAddresses: [Address] = newPubKeys.map { $0.toAddress() } + let newAddresses: [BitcoinAddress] = newPubKeys.map { $0.toBitcoinAddress() } internalIndex += value internalPrivKeys += newPrivKeys internalPubKeys += newPubKeys diff --git a/Sources/BitcoinKit/Wallet/TransactionBuilder.swift b/Sources/BitcoinKit/Wallet/TransactionBuilder.swift index 1c199775..69423d6f 100644 --- a/Sources/BitcoinKit/Wallet/TransactionBuilder.swift +++ b/Sources/BitcoinKit/Wallet/TransactionBuilder.swift @@ -37,7 +37,7 @@ public struct TransactionBuilder { /// - toAddress: Address to send the amount /// - changeAddress: Address to receive the change /// - Returns: The transaction whose inputs are not signed. - public static func build(from plan: TransactionPlan, toAddress: Address, changeAddress: Address) -> Transaction { + public static func build(from plan: TransactionPlan, toAddress: BitcoinAddress, changeAddress: BitcoinAddress) -> Transaction { let toLockScript: Data = Script(address: toAddress)!.data var outputs: [TransactionOutput] = [ TransactionOutput(value: plan.amount, lockingScript: toLockScript) diff --git a/Tests/BitcoinKitTests/Core/Address/AddressFactoryTests.swift b/Tests/BitcoinKitTests/Core/Address/AddressFactoryTests.swift index 87295655..3993cb3d 100644 --- a/Tests/BitcoinKitTests/Core/Address/AddressFactoryTests.swift +++ b/Tests/BitcoinKitTests/Core/Address/AddressFactoryTests.swift @@ -28,13 +28,13 @@ import XCTest class AddressFactoryTests: XCTestCase { func testAddressFactory() { // Cashaddr - let cashaddr = try? AddressFactory.create("bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") + let cashaddr = try? BitcoinAddress(cashaddr: "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") XCTAssertNotNil(cashaddr) XCTAssertEqual("\(cashaddr!)", "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") // invalid address do { - _ = try AddressFactory.create("bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978💦😆") + _ = try BitcoinAddress(cashaddr: "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978💦😆") XCTFail("Should throw invalid checksum error.") } catch AddressError.invalid { // Success @@ -44,7 +44,7 @@ class AddressFactoryTests: XCTestCase { // mismatch scheme and address do { - _ = try AddressFactory.create("bchtest:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") + _ = try BitcoinAddress(cashaddr: "bchtest:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") XCTFail("Should throw invalid checksum error.") } catch AddressError.invalid { // Success @@ -54,7 +54,7 @@ class AddressFactoryTests: XCTestCase { // wrong network do { - _ = try AddressFactory.create("pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5") + _ = try BitcoinAddress(cashaddr: "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5") XCTFail("Should throw invalid scheme error.") } catch AddressError.invalidScheme { // Success @@ -63,14 +63,14 @@ class AddressFactoryTests: XCTestCase { } // LegacyAddress - let legacyAddress = try? AddressFactory.create("1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") + let legacyAddress = try? BitcoinAddress(legacy: "1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") XCTAssertNotNil(legacyAddress) XCTAssertEqual("\(legacyAddress!)", "1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") XCTAssertEqual(legacyAddress!.cashaddr, "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") // invalid checksum error do { - _ = try AddressFactory.create("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W") + _ = try BitcoinAddress(legacy: "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W") XCTFail("Should throw invalid checksum error.") } catch AddressError.invalid { // Success diff --git a/Tests/BitcoinKitTests/Core/Address/AddressTests.swift b/Tests/BitcoinKitTests/Core/Address/AddressTests.swift index bc9f0788..6d1c2343 100644 --- a/Tests/BitcoinKitTests/Core/Address/AddressTests.swift +++ b/Tests/BitcoinKitTests/Core/Address/AddressTests.swift @@ -30,11 +30,11 @@ class AddressTests: XCTestCase { func testMainnetLegacyAddress() { let privateKey = try! PrivateKey(wif: "5K6EwEiKWKNnWGYwbNtrXjA8KKNntvxNKvepNqNeeLpfW7FSG1v") let publicKey = privateKey.publicKey() - let addressFromPublicKey = publicKey.toLegacy() - XCTAssertEqual("\(addressFromPublicKey)", "1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") - XCTAssertEqual("\(addressFromPublicKey.cashaddr)", "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") + let addressFromPublicKey = publicKey.toBitcoinAddress() + XCTAssertEqual(addressFromPublicKey.legacy, "1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") + XCTAssertEqual(addressFromPublicKey.cashaddr, "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") - let addressFromFormattedAddress = try? LegacyAddress("1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") + let addressFromFormattedAddress = try? BitcoinAddress(legacy: "1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") XCTAssertNotNil(addressFromFormattedAddress) XCTAssertEqual("\(addressFromFormattedAddress!)", "1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") XCTAssertEqual(addressFromFormattedAddress!.cashaddr, "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") @@ -43,11 +43,11 @@ class AddressTests: XCTestCase { func testTestnetLegacyAddress() { let privateKey = try! PrivateKey(wif: "92pMamV6jNyEq9pDpY4f6nBy9KpV2cfJT4L5zDUYiGqyQHJfF1K") let publicKey = privateKey.publicKey() - let addressFromPublicKey = publicKey.toLegacy() - XCTAssertEqual("\(addressFromPublicKey)", "mjNkq5ycsAfY9Vybo9jG8wbkC5mbpo4xgC") - XCTAssertEqual("\(addressFromPublicKey.cashaddr)", "bchtest:qq498xkl67h0espwqxttfn8hdt4g3g05wqtqeyg993") + let addressFromPublicKey = publicKey.toBitcoinAddress() + XCTAssertEqual(addressFromPublicKey.legacy, "mjNkq5ycsAfY9Vybo9jG8wbkC5mbpo4xgC") + XCTAssertEqual(addressFromPublicKey.cashaddr, "bchtest:qq498xkl67h0espwqxttfn8hdt4g3g05wqtqeyg993") - let addressFromFormattedAddress = try? LegacyAddress("mjNkq5ycsAfY9Vybo9jG8wbkC5mbpo4xgC") + let addressFromFormattedAddress = try? BitcoinAddress(legacy: "mjNkq5ycsAfY9Vybo9jG8wbkC5mbpo4xgC") XCTAssertNotNil(addressFromFormattedAddress) XCTAssertEqual("\(addressFromFormattedAddress!)", "mjNkq5ycsAfY9Vybo9jG8wbkC5mbpo4xgC") XCTAssertEqual(addressFromFormattedAddress!.cashaddr, "bchtest:qq498xkl67h0espwqxttfn8hdt4g3g05wqtqeyg993") @@ -55,7 +55,7 @@ class AddressTests: XCTestCase { func testInvalidChecksumLegacyAddress() { do { - _ = try LegacyAddress("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W") + _ = try BitcoinAddress(legacy: "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W") XCTFail("Should throw invalid checksum error.") } catch AddressError.invalid { // Success @@ -67,10 +67,10 @@ class AddressTests: XCTestCase { func testMainnetCashaddr() { let privateKey = try! PrivateKey(wif: "5K6EwEiKWKNnWGYwbNtrXjA8KKNntvxNKvepNqNeeLpfW7FSG1v") let publicKey = privateKey.publicKey() - let addressFromPublicKey = publicKey.toCashaddr() + let addressFromPublicKey = publicKey.toBitcoinAddress() XCTAssertEqual("\(addressFromPublicKey)", "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") - let addressFromFormattedAddress = try? Cashaddr("bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") + let addressFromFormattedAddress = try? BitcoinAddress(cashaddr: "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") XCTAssertNotNil(addressFromFormattedAddress) XCTAssertEqual("\(addressFromFormattedAddress!)", "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") } @@ -78,10 +78,10 @@ class AddressTests: XCTestCase { func testTestnetCashaddr() { let privateKey = try! PrivateKey(wif: "92pMamV6jNyEq9pDpY4f6nBy9KpV2cfJT4L5zDUYiGqyQHJfF1K") let publicKey = privateKey.publicKey() - let addressFromPublicKey = publicKey.toCashaddr() + let addressFromPublicKey = publicKey.toBitcoinAddress() XCTAssertEqual("\(addressFromPublicKey)", "bchtest:qq498xkl67h0espwqxttfn8hdt4g3g05wqtqeyg993") - let addressFromFormattedAddress = try? Cashaddr("bchtest:qq498xkl67h0espwqxttfn8hdt4g3g05wqtqeyg993") + let addressFromFormattedAddress = try? BitcoinAddress(cashaddr: "bchtest:qq498xkl67h0espwqxttfn8hdt4g3g05wqtqeyg993") XCTAssertNotNil(addressFromFormattedAddress) XCTAssertEqual("\(addressFromFormattedAddress!)", "bchtest:qq498xkl67h0espwqxttfn8hdt4g3g05wqtqeyg993") } @@ -89,7 +89,7 @@ class AddressTests: XCTestCase { func testInvalidChecksumCashaddr() { // invalid address do { - _ = try Cashaddr("bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978💦😆") + _ = try BitcoinAddress(cashaddr: "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978💦😆") XCTFail("Should throw invalid checksum error.") } catch AddressError.invalid { // Success @@ -99,7 +99,7 @@ class AddressTests: XCTestCase { // mismatch scheme and address do { - _ = try Cashaddr("bchtest:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") + _ = try BitcoinAddress(cashaddr: "bchtest:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") XCTFail("Should throw invalid checksum error.") } catch AddressError.invalid { // Success @@ -110,7 +110,7 @@ class AddressTests: XCTestCase { func testWrongNetworkCashaddr() { do { - _ = try Cashaddr("pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5") + _ = try BitcoinAddress(cashaddr: "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5") XCTFail("Should throw invalid scheme error.") } catch AddressError.invalidScheme { // Success diff --git a/Tests/BitcoinKitTests/Core/Address/Bech32Tests.swift b/Tests/BitcoinKitTests/Core/Address/Bech32Tests.swift index 5111958b..e0099da1 100644 --- a/Tests/BitcoinKitTests/Core/Address/Bech32Tests.swift +++ b/Tests/BitcoinKitTests/Core/Address/Bech32Tests.swift @@ -95,7 +95,7 @@ class Bech32Tetst: XCTestCase { func HexEncodesToBech32(hex: String, prefix: String, bech32: String, versionByte: UInt8) { //Encode let data = Data(hex: hex)! - XCTAssertEqual(Bech32.encode(Data([versionByte]) + data, prefix: prefix), bech32) + XCTAssertEqual(Bech32.encode(payload: Data([versionByte]) + data, prefix: prefix), bech32) //Decode let data2 = Bech32.decode(bech32)! XCTAssertEqual(data2.prefix, prefix) diff --git a/Tests/BitcoinKitTests/Core/Address/LegacyAddressTests.swift b/Tests/BitcoinKitTests/Core/Address/LegacyAddressTests.swift index 1b337343..b9415a55 100644 --- a/Tests/BitcoinKitTests/Core/Address/LegacyAddressTests.swift +++ b/Tests/BitcoinKitTests/Core/Address/LegacyAddressTests.swift @@ -33,16 +33,17 @@ class LegacyAddressTests: XCTestCase { let privateKey = try! PrivateKey(wif: "5K6EwEiKWKNnWGYwbNtrXjA8KKNntvxNKvepNqNeeLpfW7FSG1v") let publicKey = privateKey.publicKey() - let address1 = publicKey.toLegacy() - XCTAssertEqual("\(address1)", address1.base58) + let address1 = publicKey.toBitcoinAddress() + XCTAssertEqual("\(address1)", address1.cashaddr) - let address2 = try? LegacyAddress("1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") + let address2 = try? BitcoinAddress(legacy: "1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN") XCTAssertNotNil(address2) XCTAssertEqual(address2?.cashaddr, "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978") - XCTAssertEqual(address1, address2) + XCTAssertEqual(address1.legacy, address2?.legacy) + XCTAssertEqual(address1.cashaddr, address2?.cashaddr) do { - _ = try LegacyAddress("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W") + _ = try BitcoinAddress(legacy: "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W") XCTFail("Should throw invalid checksum error.") } catch AddressError.invalid { // Success @@ -56,13 +57,14 @@ class LegacyAddressTests: XCTestCase { let privateKey = try! PrivateKey(wif: "92pMamV6jNyEq9pDpY4f6nBy9KpV2cfJT4L5zDUYiGqyQHJfF1K") let publicKey = privateKey.publicKey() - let address1 = publicKey.toLegacy() - XCTAssertEqual("\(address1)", address1.base58) + let address1 = publicKey.toBitcoinAddress() + XCTAssertEqual("\(address1)", address1.cashaddr) - let address2 = try? LegacyAddress("mjNkq5ycsAfY9Vybo9jG8wbkC5mbpo4xgC") + let address2 = try? BitcoinAddress(legacy: "mjNkq5ycsAfY9Vybo9jG8wbkC5mbpo4xgC") XCTAssertNotNil(address2) XCTAssertEqual(address2?.cashaddr, "bchtest:qq498xkl67h0espwqxttfn8hdt4g3g05wqtqeyg993") - XCTAssertEqual(address1, address2) + XCTAssertEqual(address1.legacy, address2?.legacy) + XCTAssertEqual(address1.cashaddr, address2?.cashaddr) } } } diff --git a/Tests/BitcoinKitTests/Core/BloomFilterTests.swift b/Tests/BitcoinKitTests/Core/BloomFilterTests.swift index 55882d20..ddc7c6b1 100644 --- a/Tests/BitcoinKitTests/Core/BloomFilterTests.swift +++ b/Tests/BitcoinKitTests/Core/BloomFilterTests.swift @@ -64,11 +64,11 @@ class BloomFilterTests: XCTestCase { do { var filter = BloomFilter(elements: 4, falsePositiveRate: 0.001, randomNonce: 100) - let publicKey1 = PublicKey(bytes: Data(hex: "03cdb817b334c8e3bdc6ce3a1eae9e624cc64426eb00ef9207d2021ce6d9253a2a")!, network: .testnet) + let publicKey1 = PublicKey(bytes: Data(hex: "03cdb817b334c8e3bdc6ce3a1eae9e624cc64426eb00ef9207d2021ce6d9253a2a")!, network: .testnetBCH) filter.insert(publicKey1.data) filter.insert(Crypto.sha256ripemd160(publicKey1.data)) - let publicKey2 = PublicKey(bytes: Data(hex: "02784addc6ceed8bbbee10829194ce17c99a6a7029b3a9e078b6f849aa91c937b5")!, network: .testnet) + let publicKey2 = PublicKey(bytes: Data(hex: "02784addc6ceed8bbbee10829194ce17c99a6a7029b3a9e078b6f849aa91c937b5")!, network: .testnetBCH) filter.insert(publicKey2.data) filter.insert(Crypto.sha256ripemd160(publicKey2.data)) diff --git a/Tests/BitcoinKitTests/Core/Keys/HDKeyChainTests.swift b/Tests/BitcoinKitTests/Core/Keys/HDKeyChainTests.swift index b74b1d5a..5c76bab3 100644 --- a/Tests/BitcoinKitTests/Core/Keys/HDKeyChainTests.swift +++ b/Tests/BitcoinKitTests/Core/Keys/HDKeyChainTests.swift @@ -31,7 +31,7 @@ class HDKeyChainTests: XCTestCase { // Master: 000102030405060708090a0b0c0d0e0f let seed = Data(hex: "000102030405060708090a0b0c0d0e0f")! - let keychain = HDKeychain(seed: seed, network: .mainnet) + let keychain = HDKeychain(seed: seed, network: .mainnetBCH) let privateKey = try! keychain.derivedKey(path: "m") XCTAssertEqual(privateKey.extendedPublicKey().extended(), "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8") diff --git a/Tests/BitcoinKitTests/Core/Keys/HDPrivateKeyTests.swift b/Tests/BitcoinKitTests/Core/Keys/HDPrivateKeyTests.swift index 725c5334..6c484957 100644 --- a/Tests/BitcoinKitTests/Core/Keys/HDPrivateKeyTests.swift +++ b/Tests/BitcoinKitTests/Core/Keys/HDPrivateKeyTests.swift @@ -55,7 +55,7 @@ class HDPrivateKeyTests: XCTestCase { let seed = Data(hex: "000102030405060708090a0b0c0d0e0f")! // m - let privateKey = HDPrivateKey(seed: seed, network: .mainnet) + let privateKey = HDPrivateKey(seed: seed, network: .mainnetBCH) XCTAssertEqual(privateKey.extendedPublicKey().extended(), "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8") XCTAssertEqual(privateKey.extended(), "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi") @@ -113,7 +113,7 @@ class HDPrivateKeyTests: XCTestCase { let seed = Data(hex: "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542")! // m - let privateKey = HDPrivateKey(seed: seed, network: .mainnet) + let privateKey = HDPrivateKey(seed: seed, network: .mainnetBCH) XCTAssertEqual(privateKey.extendedPublicKey().extended(), "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB") XCTAssertEqual(privateKey.extended(), "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U") @@ -151,7 +151,7 @@ class HDPrivateKeyTests: XCTestCase { let seed = Data(hex: "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be")! // m - let privateKey = HDPrivateKey(seed: seed, network: .mainnet) + let privateKey = HDPrivateKey(seed: seed, network: .mainnetBCH) XCTAssertEqual(privateKey.extendedPublicKey().extended(), "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13") XCTAssertEqual(privateKey.extended(), "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6") @@ -166,7 +166,7 @@ class HDPrivateKeyTests: XCTestCase { let seed = Data(hex: "000102030405060708090a0b0c0d0e0f")! // m - let privateKey = HDPrivateKey(seed: seed, network: .mainnet) + let privateKey = HDPrivateKey(seed: seed, network: .mainnetBCH) // m/0' let m0prv = try! privateKey.derived(at: 0, hardened: true) // m/0'/1 @@ -179,7 +179,7 @@ class HDPrivateKeyTests: XCTestCase { func testDeriveProblem() { let seed = Data(hex: "f27fd395d30d00f1c11b7551a93961ca41c0a78bce21e9a618e83a99cf74aec159139ef3ef078bc0038557b7cb689933d0806ce33571df78bc4397e7f9976ff2")! - let key = try! HDPrivateKey(seed: seed, network: .mainnet) + let key = try! HDPrivateKey(seed: seed, network: .mainnetBCH) .derived(at: 44, hardened: true) .derived(at: 0, hardened: true) .derived(at: 0, hardened: true) @@ -191,6 +191,6 @@ class HDPrivateKeyTests: XCTestCase { XCTAssertEqual(privKey.data.hex, "00f2c37dad54d1d2be57b06653ea655c6fd8eb3ca3f0b9671e036d50061d265b") XCTAssertEqual(privKey.description, "KwFZ6jFtuvBu7w4R4x4WpzQgSSYTHLEw8Pr2PUkWjADkHJUPNDVg") XCTAssertEqual(privKey.publicKey().description, "02a712f894d58baef44e4fbbc26ed6ca89487db1f17e944f9b45ca2ae666e99d72") - XCTAssertEqual(privKey.publicKey().toLegacy().description, "1DPUysR46jraybTwP3PfSbcBENeLScLxx") + XCTAssertEqual(privKey.publicKey().toBitcoinAddress().legacy, "1DPUysR46jraybTwP3PfSbcBENeLScLxx") } } diff --git a/Tests/BitcoinKitTests/Core/Keys/PrivateKeyTests.swift b/Tests/BitcoinKitTests/Core/Keys/PrivateKeyTests.swift index a066bca5..fa149442 100644 --- a/Tests/BitcoinKitTests/Core/Keys/PrivateKeyTests.swift +++ b/Tests/BitcoinKitTests/Core/Keys/PrivateKeyTests.swift @@ -28,7 +28,7 @@ import XCTest class PrivateKeyTests: XCTestCase { func testGenerateKeyPair() { - let privateKey = PrivateKey(network: .testnet) + let privateKey = PrivateKey(network: .testnetBCH) let publicKey = privateKey.publicKey() XCTAssertNotNil(privateKey) @@ -48,7 +48,7 @@ class PrivateKeyTests: XCTestCase { XCTAssertNotNil(privateKey) XCTAssertNotNil(publicKey) - XCTAssertEqual(privateKey?.network, Network.mainnet) + XCTAssertEqual(privateKey?.network, Network.mainnetBCH) XCTAssertEqual(privateKey?.data.hex, "a7ec27c206a68e33f53d6a35f284c748e0874ca2f0ea56eca6eb7668db0fe805") XCTAssertEqual(privateKey?.description, "5K6EwEiKWKNnWGYwbNtrXjA8KKNntvxNKvepNqNeeLpfW7FSG1v") @@ -63,7 +63,7 @@ class PrivateKeyTests: XCTestCase { XCTAssertNotNil(privateKey) XCTAssertNotNil(publicKey) - XCTAssertEqual(privateKey?.network, Network.testnet) + XCTAssertEqual(privateKey?.network, Network.testnetBCH) XCTAssertEqual(privateKey?.data.hex, "a2359719d3dc9f1539c593e477dc9d57b9653a18e7c94299d87a95ed13525eae") XCTAssertEqual(privateKey?.description, "92pMamV6jNyEq9pDpY4f6nBy9KpV2cfJT4L5zDUYiGqyQHJfF1K") diff --git a/Tests/BitcoinKitTests/Core/MnemonicTests.swift b/Tests/BitcoinKitTests/Core/MnemonicTests.swift index 8555a6e6..4fcc62fc 100644 --- a/Tests/BitcoinKitTests/Core/MnemonicTests.swift +++ b/Tests/BitcoinKitTests/Core/MnemonicTests.swift @@ -198,7 +198,7 @@ class MnemonicTests: XCTestCase { let seed = try! Mnemonic.seed(mnemonic: mnemonic, passphrase: "TREZOR") XCTAssertEqual(seed.hex, expected.seed) - let privateKey = HDPrivateKey(seed: seed, network: .mainnet) + let privateKey = HDPrivateKey(seed: seed, network: .mainnetBCH) XCTAssertEqual(privateKey.extended(), expected.key) } } @@ -415,7 +415,7 @@ class MnemonicTests: XCTestCase { let seed = try! Mnemonic.seed(mnemonic: mnemonic, passphrase: expected.passphrase) XCTAssertEqual(seed.hex, expected.seed) - let privateKey = HDPrivateKey(seed: seed, network: .mainnet) + let privateKey = HDPrivateKey(seed: seed, network: .mainnetBCH) XCTAssertEqual(privateKey.extended(), expected.bip32_xprv) } } diff --git a/Tests/BitcoinKitTests/Core/PaymentURITests.swift b/Tests/BitcoinKitTests/Core/PaymentURITests.swift index da54b70c..1d6d778a 100644 --- a/Tests/BitcoinKitTests/Core/PaymentURITests.swift +++ b/Tests/BitcoinKitTests/Core/PaymentURITests.swift @@ -28,45 +28,67 @@ import XCTest class PaymentURITests: XCTestCase { func testPaymentURI() { - let justAddress = try? PaymentURI("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu") - XCTAssertNotNil(justAddress) - XCTAssertEqual(justAddress?.address.network, .mainnet) - XCTAssertEqual(justAddress?.address.base58, "12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu") - XCTAssertNil(justAddress?.label) - XCTAssertNil(justAddress?.message) - XCTAssertNil(justAddress?.amount) - XCTAssertTrue(justAddress?.others.isEmpty ?? false) - XCTAssertEqual(justAddress?.uri, URL(string: "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu")) + let justAddress: PaymentURI + do { + justAddress = try PaymentURI("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu") + } catch let e { + XCTFail("PaymentURI error :\(e)") + return + } + XCTAssertEqual(justAddress.address.network, .mainnetBTC) + XCTAssertEqual(justAddress.address.legacy, "12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu") + XCTAssertNil(justAddress.label) + XCTAssertNil(justAddress.message) + XCTAssertNil(justAddress.amount) + XCTAssertTrue(justAddress.others.isEmpty) + XCTAssertEqual(justAddress.uri, URL(string: "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu")) - let addressWithName = try? PaymentURI("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?label=Luke-Jr") - XCTAssertNotNil(addressWithName) - XCTAssertEqual(addressWithName?.address.network, .mainnet) - XCTAssertEqual(addressWithName?.address.base58, "12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu") - XCTAssertEqual(addressWithName?.label, "Luke-Jr") - XCTAssertNil(addressWithName?.message) - XCTAssertNil(addressWithName?.amount) - XCTAssertTrue(addressWithName?.others.isEmpty ?? false) - XCTAssertEqual(addressWithName?.uri, URL(string: "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?label=Luke-Jr")) + let addressWithName: PaymentURI + do { + addressWithName = try PaymentURI("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?label=Luke-Jr") + } catch let e { + XCTFail("PaymentURI error :\(e)") + return + } - let request20_30BTCToLukeJr = try? PaymentURI("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=20.3&label=Luke-Jr") - XCTAssertNotNil(request20_30BTCToLukeJr) - XCTAssertEqual(request20_30BTCToLukeJr?.address.network, .mainnet) - XCTAssertEqual(request20_30BTCToLukeJr?.address.base58, "12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu") - XCTAssertEqual(request20_30BTCToLukeJr?.label, "Luke-Jr") - XCTAssertEqual(request20_30BTCToLukeJr?.amount, Decimal(string: "20.30")) - XCTAssertNil(request20_30BTCToLukeJr?.message) - XCTAssertTrue(request20_30BTCToLukeJr?.others.isEmpty ?? false) - XCTAssertEqual(request20_30BTCToLukeJr?.uri, URL(string: "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=20.3&label=Luke-Jr")) + XCTAssertEqual(addressWithName.address.network, .mainnetBTC) + XCTAssertEqual(addressWithName.address.legacy, "12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu") + XCTAssertEqual(addressWithName.label, "Luke-Jr") + XCTAssertNil(addressWithName.message) + XCTAssertNil(addressWithName.amount) + XCTAssertTrue(addressWithName.others.isEmpty) + XCTAssertEqual(addressWithName.uri, URL(string: "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?label=Luke-Jr")) - let request50BTCWithMessage = try? PaymentURI("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=50&label=Luke-Jr&message=Donation%20for%20project%20xyz") + let request20_30BTCToLukeJr: PaymentURI + do { + request20_30BTCToLukeJr = try PaymentURI("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=20.3&label=Luke-Jr") + } catch let e { + XCTFail("PaymentURI error :\(e)") + return + } + XCTAssertEqual(request20_30BTCToLukeJr.address.network, .mainnetBTC) + XCTAssertEqual(request20_30BTCToLukeJr.address.legacy, "12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu") + XCTAssertEqual(request20_30BTCToLukeJr.label, "Luke-Jr") + XCTAssertEqual(request20_30BTCToLukeJr.amount, Decimal(string: "20.30")) + XCTAssertNil(request20_30BTCToLukeJr.message) + XCTAssertTrue(request20_30BTCToLukeJr.others.isEmpty) + XCTAssertEqual(request20_30BTCToLukeJr.uri, URL(string: "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=20.3&label=Luke-Jr")) + + let request50BTCWithMessage: PaymentURI + do { + request50BTCWithMessage = try PaymentURI("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=50&label=Luke-Jr&message=Donation%20for%20project%20xyz") + } catch let e { + XCTFail("PaymentURI error :\(e)") + return + } XCTAssertNotNil(request50BTCWithMessage) - XCTAssertEqual(request50BTCWithMessage?.address.network, .mainnet) - XCTAssertEqual(request50BTCWithMessage?.address.base58, "12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu") - XCTAssertEqual(request50BTCWithMessage?.label, "Luke-Jr") - XCTAssertEqual(request50BTCWithMessage?.amount, Decimal(string: "50")) - XCTAssertEqual(request50BTCWithMessage?.message, "Donation for project xyz") - XCTAssertTrue(request50BTCWithMessage?.others.isEmpty ?? false) - XCTAssertEqual(request50BTCWithMessage?.uri, URL(string: "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=50&label=Luke-Jr&message=Donation%20for%20project%20xyz")) + XCTAssertEqual(request50BTCWithMessage.address.network, .mainnetBTC) + XCTAssertEqual(request50BTCWithMessage.address.legacy, "12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu") + XCTAssertEqual(request50BTCWithMessage.label, "Luke-Jr") + XCTAssertEqual(request50BTCWithMessage.amount, Decimal(string: "50")) + XCTAssertEqual(request50BTCWithMessage.message, "Donation for project xyz") + XCTAssertTrue(request50BTCWithMessage.others.isEmpty) + XCTAssertEqual(request50BTCWithMessage.uri, URL(string: "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=50&label=Luke-Jr&message=Donation%20for%20project%20xyz")) do { _ = try PaymentURI("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=abc&label=Luke-Jr") diff --git a/Tests/BitcoinKitTests/Script/ScriptMachineTests.swift b/Tests/BitcoinKitTests/Script/ScriptMachineTests.swift index 7d72e272..6b967aa0 100644 --- a/Tests/BitcoinKitTests/Script/ScriptMachineTests.swift +++ b/Tests/BitcoinKitTests/Script/ScriptMachineTests.swift @@ -45,12 +45,12 @@ class ScriptMachineTests: XCTestCase { let fromPublicKey = privateKey.publicKey() let fromPubKeyHash = Crypto.sha256ripemd160(fromPublicKey.data) - let toPubKeyHash = Base58.decode(toAddress)!.dropFirst().dropLast(4) + let toPubKeyHash = Base58Check.decode(toAddress)!.dropFirst() // unsigned tx let lockingScript1 = Script.buildPublicKeyHashOut(pubKeyHash: toPubKeyHash) let lockingScript2 = Script.buildPublicKeyHashOut(pubKeyHash: fromPubKeyHash) - + let sending = TransactionOutput(value: amount, lockingScript: lockingScript1) let payback = TransactionOutput(value: balance - amount - fee, lockingScript: lockingScript2) let subScript = Data(hex: "76a9142a539adfd7aefcc02e0196b4ccf76aea88a1f47088ac")! diff --git a/Tests/BitcoinKitTests/Script/ScriptTests.swift b/Tests/BitcoinKitTests/Script/ScriptTests.swift index 4dbb28e9..ea3538b0 100644 --- a/Tests/BitcoinKitTests/Script/ScriptTests.swift +++ b/Tests/BitcoinKitTests/Script/ScriptTests.swift @@ -32,7 +32,7 @@ class ScriptTests: XCTestCase { let fromPublicKey = privateKey.publicKey() let fromPubKeyHash = Crypto.sha256ripemd160(fromPublicKey.data) - let toPubKeyHash = Base58.decode(toAddress)!.dropFirst().dropLast(4) + let toPubKeyHash = Base58Check.decode(toAddress)!.dropFirst() let lockingScript1 = Script.buildPublicKeyHashOut(pubKeyHash: fromPubKeyHash) let lockingScript2 = Script.buildPublicKeyHashOut(pubKeyHash: toPubKeyHash) @@ -56,7 +56,7 @@ class ScriptTests: XCTestCase { let script = Script(data: Data(hex: "76a9147ab89f9fae3f8043dcee5f7b5467a0f0a6e2f7e188ac")!)! XCTAssertTrue(script.isPayToPublicKeyHashScript, "should be regular hash160 script") - let address = try! AddressFactory.create("1CBtcGivXmHQ8ZqdPgeMfcpQNJrqTrSAcG") + let address = try! BitcoinAddress(legacy: "1CBtcGivXmHQ8ZqdPgeMfcpQNJrqTrSAcG") let script2 = Script(address: address) XCTAssertEqual(script2!.data, script.data, "script created from extracted address should be the same as the original script") XCTAssertEqual(script2!.string, script.string, "script created from extracted address should be the same as the original script") @@ -71,7 +71,7 @@ class ScriptTests: XCTestCase { XCTAssertNotNil(redeemScript) let p2shScript = redeemScript!.toP2SH() XCTAssertEqual(p2shScript.hex, "a914629a500c5eaac9261cac990c72241a959ff2d3d987") - let multisigAddr = redeemScript!.standardP2SHAddress(network: Network.testnet) + let multisigAddr = redeemScript!.standardP2SHAddress(network: Network.testnetBCH) XCTAssertEqual(multisigAddr.cashaddr, "bchtest:pp3f55qvt64vjfsu4jvscu3yr22eluknmyt3nkwcx2", "multisig address should be the same as address created from bitcoin-ruby.") } } diff --git a/Tests/BitcoinKitTests/TransactionTests.swift b/Tests/BitcoinKitTests/TransactionTests.swift index 0ee306db..26634bce 100644 --- a/Tests/BitcoinKitTests/TransactionTests.swift +++ b/Tests/BitcoinKitTests/TransactionTests.swift @@ -39,10 +39,10 @@ class TransactionTests: XCTestCase { let amount: UInt64 = 50000000 let fee: UInt64 = 10000000 let change: UInt64 = balance - amount - fee - let toAddress = try! AddressFactory.create("mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB") // https://testnet.coinfaucet.eu/en/ + let toAddress = try! BitcoinAddress(legacy: "mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB") // https://testnet.coinfaucet.eu/en/ let privateKey = try! PrivateKey(wif: "92pMamV6jNyEq9pDpY4f6nBy9KpV2cfJT4L5zDUYiGqyQHJfF1K") - let changeAddress = privateKey.publicKey().toAddress() + let changeAddress = privateKey.publicKey().toBitcoinAddress() let lockScript = Script(address: changeAddress)!.data let output = TransactionOutput(value: 169012961, lockingScript: lockScript) @@ -64,8 +64,8 @@ class TransactionTests: XCTestCase { // Transaction on Bitcoin Cash Mainnet // TxID : 96ee20002b34e468f9d3c5ee54f6a8ddaa61c118889c4f35395c2cd93ba5bbb4 // https://explorer.bitcoin.com/bch/tx/96ee20002b34e468f9d3c5ee54f6a8ddaa61c118889c4f35395c2cd93ba5bbb4 - let toAddress: Address = try! AddressFactory.create("1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx") - let changeAddress: Address = try! AddressFactory.create("1FQc5LdgGHMHEN9nwkjmz6tWkxhPpxBvBU") + let toAddress: BitcoinAddress = try! BitcoinAddress(legacy: "1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx") + let changeAddress: BitcoinAddress = try! BitcoinAddress(legacy: "1FQc5LdgGHMHEN9nwkjmz6tWkxhPpxBvBU") let unspentOutput = TransactionOutput(value: 5151, lockingScript: Data(hex: "76a914aff1e0789e5fe316b729577665aa0a04d5b0f8c788ac")!) let unspentOutpoint = TransactionOutPoint(hash: Data(hex: "e28c2b955293159898e34c6840d99bf4d390e2ee1c6f606939f18ee1e2000d05")!, index: 2) diff --git a/Tests/BitcoinKitTests/Wallet/BCHSignatureHashHelperTests.swift b/Tests/BitcoinKitTests/Wallet/BCHSignatureHashHelperTests.swift index 8fbbc05b..5ec85b23 100644 --- a/Tests/BitcoinKitTests/Wallet/BCHSignatureHashHelperTests.swift +++ b/Tests/BitcoinKitTests/Wallet/BCHSignatureHashHelperTests.swift @@ -47,8 +47,8 @@ class BCHSignatureHashHelperTests: XCTestCase { unspentTransaction = UnspentTransaction(output: prevTxOutput, outpoint: prevTxOutPoint) let plan = TransactionPlan(unspentTransactions: [unspentTransaction], amount: 600, fee: 226, change: 4325) - let toAddress = try! AddressFactory.create("bitcoincash:qpmfhhledgp0jy66r5vmwjwmdfu0up7ujqcp07ha9v") - let changeAddress = try! AddressFactory.create("bitcoincash:qz0q3xmg38sr94rw8wg45vujah7kzma3cskxymnw06") + let toAddress = try! BitcoinAddress(cashaddr: "bitcoincash:qpmfhhledgp0jy66r5vmwjwmdfu0up7ujqcp07ha9v") + let changeAddress = try! BitcoinAddress(cashaddr: "bitcoincash:qz0q3xmg38sr94rw8wg45vujah7kzma3cskxymnw06") tx = TransactionBuilder.build(from: plan, toAddress: toAddress, changeAddress: changeAddress) } diff --git a/Tests/BitcoinKitTests/Wallet/BTCSignatureHashHelperTests.swift b/Tests/BitcoinKitTests/Wallet/BTCSignatureHashHelperTests.swift index 490f0437..493979ef 100644 --- a/Tests/BitcoinKitTests/Wallet/BTCSignatureHashHelperTests.swift +++ b/Tests/BitcoinKitTests/Wallet/BTCSignatureHashHelperTests.swift @@ -43,10 +43,10 @@ class BTCSignatureHashHelperTests: XCTestCase { let privateKey = try! PrivateKey(wif: "92pMamV6jNyEq9pDpY4f6nBy9KpV2cfJT4L5zDUYiGqyQHJfF1K") - let toAddress = try! AddressFactory.create("mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB") + let toAddress = try! BitcoinAddress(legacy: "mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB") let lockingScript1 = Script(address: toAddress)!.data - let lockingScript2 = Script(address: privateKey.publicKey().toAddress())!.data + let lockingScript2 = Script(address: privateKey.publicKey().toBitcoinAddress())!.data let sending = TransactionOutput(value: amount, lockingScript: lockingScript1) let payback = TransactionOutput(value: balance - amount - fee, lockingScript: lockingScript2) diff --git a/Tests/BitcoinKitTests/Wallet/HDWalletTests.swift b/Tests/BitcoinKitTests/Wallet/HDWalletTests.swift index da3e9873..f7261743 100644 --- a/Tests/BitcoinKitTests/Wallet/HDWalletTests.swift +++ b/Tests/BitcoinKitTests/Wallet/HDWalletTests.swift @@ -33,7 +33,7 @@ class HDWalletTests: XCTestCase { passphrase: "TREZOR", externalIndex: 0, internalIndex: 0, - network: .mainnet) + network: .mainnetBCH) } func testInitFromMnemonic() { @@ -42,7 +42,7 @@ class HDWalletTests: XCTestCase { passphrase: "TREZOR", externalIndex: 0, internalIndex: 0, - network: .mainnet) + network: .mainnetBCH) XCTAssertEqual(walletFromMnemonic.mnemonic, @@ -59,7 +59,7 @@ class HDWalletTests: XCTestCase { let walletFromSeed: HDWallet = HDWallet(seed: seed, externalIndex: 0, internalIndex: 0, - network: .mainnet) + network: .mainnetBCH) XCTAssertNil(walletFromSeed.mnemonic) XCTAssertEqual(walletFromSeed.rootXPrivKey.description, "xprv9s21ZrQH143K3h3fDYiay8mocZ3afhfULfb5GX8kCBdno77K4HiA15Tg23wpbeF1pLfs1c5SPmYHrEpTuuRhxMwvKDwqdKiGJS9XFKzUsAF") XCTAssertEqual(walletFromSeed.rootXPubKey.description, "xpub661MyMwAqRbcGB88KaFbLGiYAat55APKhtWg4uYMkXAmfuSTbq2QYsn9sKJCj1YqZPafsboef4h4YbXXhNhPwMbkHTpkf3zLhx7HvFw1NDy") @@ -67,7 +67,7 @@ class HDWalletTests: XCTestCase { } func testCreateWallet() { - let created: HDWallet = HDWallet.create(passphrase: "BitcoinKit-Wallet", network: .mainnet) + let created: HDWallet = HDWallet.create(passphrase: "BitcoinKit-Wallet", network: .mainnetBCH) XCTAssertEqual(created.mnemonic?.count, 12) XCTAssertEqual(created.externalIndex, 0) XCTAssertEqual(created.internalIndex, 0) diff --git a/Tests/BitcoinKitTests/Wallet/TransactionBuilderTests.swift b/Tests/BitcoinKitTests/Wallet/TransactionBuilderTests.swift index f3a5158a..300db78b 100644 --- a/Tests/BitcoinKitTests/Wallet/TransactionBuilderTests.swift +++ b/Tests/BitcoinKitTests/Wallet/TransactionBuilderTests.swift @@ -43,9 +43,9 @@ class TransactionBuilderTests: XCTestCase { let unspentTransaction = UnspentTransaction(output: prevTxOutput, outpoint: prevTxOutPoint) let plan = TransactionPlan(unspentTransactions: [unspentTransaction], amount: 50_000_000, fee: 10_000_000, change: 109_012_961) - let toAddress = try! AddressFactory.create("mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB") + let toAddress = try! BitcoinAddress(legacy: "mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB") let privKey = try! PrivateKey(wif: "92pMamV6jNyEq9pDpY4f6nBy9KpV2cfJT4L5zDUYiGqyQHJfF1K") - let changeAddress = privKey.publicKey().toLegacy() + let changeAddress = privKey.publicKey().toBitcoinAddress() let tx: Transaction = TransactionBuilder.build(from: plan, toAddress: toAddress, changeAddress: changeAddress) let expectedSerializedTx: Data = Data(hex: "010000000131820866b6f840db0eeec1b5ecc44092869ebc72d4ff5e76b46690eb4eca24150100000000ffffffff0280f0fa02000000001976a9149f9a7abd600c0caa03983a77c8c3df8e062cb2fa88ace1677f06000000001976a9142a539adfd7aefcc02e0196b4ccf76aea88a1f47088ac00000000")! @@ -73,8 +73,8 @@ class TransactionBuilderTests: XCTestCase { let unspentTransaction = UnspentTransaction(output: prevTxOutput, outpoint: prevTxOutPoint) let plan = TransactionPlan(unspentTransactions: [unspentTransaction], amount: 600, fee: 226, change: 4325) - let toAddress = try! AddressFactory.create("bitcoincash:qpmfhhledgp0jy66r5vmwjwmdfu0up7ujqcp07ha9v") - let changeAddress = try! AddressFactory.create("bitcoincash:qz0q3xmg38sr94rw8wg45vujah7kzma3cskxymnw06") + let toAddress = try! BitcoinAddress(cashaddr: "bitcoincash:qpmfhhledgp0jy66r5vmwjwmdfu0up7ujqcp07ha9v") + let changeAddress = try! BitcoinAddress(cashaddr: "bitcoincash:qz0q3xmg38sr94rw8wg45vujah7kzma3cskxymnw06") let tx = TransactionBuilder.build(from: plan, toAddress: toAddress, changeAddress: changeAddress) let expectedSerializedTx: Data = Data(hex: "0100000001e28c2b955293159898e34c6840d99bf4d390e2ee1c6f606939f18ee1e2000d050200000000ffffffff0258020000000000001976a914769bdff96a02f9135a1d19b749db6a78fe07dc9088ace5100000000000001976a9149e089b6889e032d46e3b915a3392edfd616fb1c488ac00000000")! XCTAssertEqual(tx.serialized().hex, expectedSerializedTx.hex) diff --git a/Tests/BitcoinKitTests/Wallet/TransactionSignerTests.swift b/Tests/BitcoinKitTests/Wallet/TransactionSignerTests.swift index e5748bbb..8f66ff63 100644 --- a/Tests/BitcoinKitTests/Wallet/TransactionSignerTests.swift +++ b/Tests/BitcoinKitTests/Wallet/TransactionSignerTests.swift @@ -44,8 +44,8 @@ class TransactionSignerTests: XCTestCase { let unspentTransaction = UnspentTransaction(output: prevTxOutput, outpoint: prevTxOutPoint) let plan = TransactionPlan(unspentTransactions: [unspentTransaction], amount: 600, fee: 226, change: 4325) - let toAddress = try! AddressFactory.create("bitcoincash:qpmfhhledgp0jy66r5vmwjwmdfu0up7ujqcp07ha9v") - let changeAddress = try! AddressFactory.create("bitcoincash:qz0q3xmg38sr94rw8wg45vujah7kzma3cskxymnw06") + let toAddress = try! BitcoinAddress(cashaddr: "bitcoincash:qpmfhhledgp0jy66r5vmwjwmdfu0up7ujqcp07ha9v") + let changeAddress = try! BitcoinAddress(cashaddr: "bitcoincash:qz0q3xmg38sr94rw8wg45vujah7kzma3cskxymnw06") let tx = TransactionBuilder.build(from: plan, toAddress: toAddress, changeAddress: changeAddress) let privKey = try! PrivateKey(wif: "L1WFAgk5LxC5NLfuTeADvJ5nm3ooV3cKei5Yi9LJ8ENDfGMBZjdW") diff --git a/WalletExample/WalletExample/ViewController.swift b/WalletExample/WalletExample/ViewController.swift index 56afb112..1c0ae7b0 100644 --- a/WalletExample/WalletExample/ViewController.swift +++ b/WalletExample/WalletExample/ViewController.swift @@ -41,7 +41,7 @@ class ViewController: UIViewController { func createWalletIfNeeded() { if wallet == nil { - let privateKey = PrivateKey(network: .testnet) + let privateKey = PrivateKey(network: .testnetBCH) wallet = Wallet(privateKey: privateKey) wallet?.save() } @@ -71,7 +71,7 @@ class ViewController: UIViewController { } do { - let address: Address = try AddressFactory.create(addressString) + let address: BitcoinAddress = try AddressFactory.create(addressString) try wallet?.send(to: address, amount: 10000, completion: { [weak self] (response) in print(response ?? "") self?.updateBalance()