I have a
bitcoinWalletAddress
in bitcoin testnet (starting with tb1)publicKey
already in hex formatprivateKey
in P2WPKH format looking likecTS...
By following this guide and this one I created the raw transaction by hand minus the scriptSig. And I'm a little bit stuck on that.
- IDK, how to correctly get the hash from private key using Swift.
- By reading different sources I don't have an understanding, how to create CORRECTLY the script for signature for P2WPKH particularly.
- Then, when i have p.1 and p.2 how can I sign the script with private key using Swift?
Please help finding the answer! I'm very new to bitcoin "insides")
P.s. what i have for now:
I've imported two packages:
https://github.com/0xDEADP00L/Bech32
https://github.com/keefertaylor/Base58Swift.git
I've wrote extensions:
@testable import Bech32
import CryptoKit
import Base58Swift
extension Array {
func chunks(size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}
extension String {
func chunks(size: Int) -> [String] {
map { $0 }.chunks(size: size).compactMap { String($0) }
}
func leftPaddingZeros(toLength: Int) -> String {
guard toLength > self.count else { return self }
let padding = String(repeating: "0", count: toLength - self.count)
return padding + self
}
func reversedByBytes() -> String {
return self.chunks(size: 2)
.reversed()
.joined(separator: "")
}
/// A data representation of the hexadecimal bytes in this string.
var hexDecodedData: Data {
// Get the UTF8 characters of this string
let chars = Array(utf8)
// Keep the bytes in an UInt8 array and later convert it to Data
var bytes = [UInt8]()
bytes.reserveCapacity(count / 2)
// It is a lot faster to use a lookup map instead of stratal
let map: [UInt8] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // HIJKLMNO
]
// Grab two characters at a time, map them and turn it into a byte
for i in stride(from: 0, to: count, by: 2) {
let index1 = Int(chars[i] & 0x1F ^ 0x10)
let index2 = Int(chars[i + 1] & 0x1F ^ 0x10)
bytes.append(map[index1] << 4 | map[index2])
}
return Data(bytes)
}
}
extension Digest {
var bytes: [UInt8] { Array(makeIterator()) }
var data: Data { Data(bytes) }
var hexStr: String {
bytes.map { String(format: "%02x", $0) }.joined()
}
}
extension Data {
func hexEncodedString() -> String {
return self.map { String(format: "%02x", $0) }.joined()
}
}
And wrote functions to build raw transaction minus ScriptSig
public static func getTransVersion() -> String {
return getFourByteString(number: version)
}
public static func getTransInputs() -> String {
return getHexString(number: inputsNumber)
}
public static func getTransHash() -> String {
return prevTransHash.reversedByBytes()
}
public static func getPrevOutputIndexx() -> String {
return getFourByteString(number: prevOutputIndex)
}
/// Constructing ScriptPubKey from initial key
/// by next algo:
/// OP_0 OP_PUSHBYTES_20 <key> (ASM) ->
/// 0014<key> (HEX)
/// - Returns: scriptPubKey of the output we want to redeem
public static func getScriptPubKey(address: String) throws -> String {
let addrHash = try getAddressHash(address: address)
return "00" + getHexString(number: addrHash.count / 2) + addrHash
}
/// This is always set to 0xffffffff
/// - Returns: four-byte field denoting the sequence
public static func getSequence() -> String {
return String(repeating: "f", count: 8)
}
/// Convert outputsNumber to hex string
/// - Returns: one-byte varint containing the number of outputs in new transaction
public static func getOutputsNumber() -> String {
return getHexString(number: outputsNumber)
}
public static func getOutputAmount(satoshis: Int64) -> String {
return getEightByteString(number: satoshis)
}
public static func getAddressHash(address: String) throws -> String {
let (_, data) = try SegwitAddrCoder().decode(hrp: "tb", addr: address)
return data.hexEncodedString()
}
public static func getLockTime() -> String {
return String(repeating: "0", count: 8)
}
public static func getSigHash() -> String {
return getFourByteString(number: sigHash)
}
public static func getHexString(number: Int) -> String {
return NSString(format:"%02x", number) as String
}
public static func getFullTransaction() throws -> String {
let transHash = try RawTransaction.getScriptPubKey(address: RawTransaction.output2Address)
let transHash2 = try RawTransaction.getScriptPubKey(address: RawTransaction.output1Address)
return RawTransaction.getTransVersion()
+ RawTransaction.getTransInputs()
+ RawTransaction.getTransHash()
+ RawTransaction.getPrevOutputIndexx()
+ RawTransaction.getHexString(number: transHash.count / 2)
+ transHash
+ RawTransaction.getSequence()
+ RawTransaction.getOutputsNumber()
+ RawTransaction.getOutputAmount(satoshis: RawTransaction.output2Amount)
+ RawTransaction.getHexString(number: transHash.count / 2)
+ transHash
+ RawTransaction.getOutputAmount(satoshis: RawTransaction.output1Amount)
+ RawTransaction.getHexString(number: transHash2.count / 2)
+ transHash2
+ RawTransaction.getLockTime()
+ RawTransaction.getSigHash()
}
public static func getDoubleSha(initialString: String) {
let digest = SHA256.hash(data: initialString.hexDecodedData)
let digest2 = SHA256.hash(data: digest.data)
print(digest2.hexStr.reversedByBytes())
print(digest2.hexStr + "\n")
}
public static func getPrivateKeyHex() -> String {
let decode = Base58.base58CheckDecode(RawTransaction.privateKey)!
let str = Data(decode).hexEncodedString()
print(str)
return str
}
private static func getHexString(number: Int64) -> String {
return NSString(format:"%2x", number) as String
}
private static func getFourByteString(number: Int) -> String {
return getHexString(number: number)
.leftPaddingZeros(toLength: 8)
.reversedByBytes()
}
private static func getEightByteString(number: Int64) -> String {
return getHexString(number: number)
.leftPaddingZeros(toLength: 16)
.reversedByBytes()
}