0

I have a

  • bitcoinWalletAddress in bitcoin testnet (starting with tb1)
  • publicKey already in hex format
  • privateKey in P2WPKH format looking like cTS...

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.

  1. IDK, how to correctly get the hash from private key using Swift.
  2. By reading different sources I don't have an understanding, how to create CORRECTLY the script for signature for P2WPKH particularly.
  3. 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()
    }

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.