Using CoreBitcoin in Swift to create raw transaction

0

I am new to coding, and only know swift, obj-C is still foreign to me. I have a functioning wallet but for now am relying on BlockCypher API to build a transaction which I do NOT want to do. Can anyone please help tell me what I am doing wrong in the following code snippet. I am creating a raw transaction however i get a weird response when decoding it where the address arrays are empty or null. Something is very wrong, if anyone has any experience I would so greatly appreciate it as this is driving me crazy.

import UIKit

class BuildTransactionViewController: UIViewController, BTCTransactionBuilderDataSource {

var addressToSpendFrom = "n1QQYAHbw3q6UjWN6Q4d9oqa6u5iUDnPHT"
var privateKeyToSign = "cNeZkP1QPQ37C4rLvoQ8xZ5eujcjsYHZMj8CLfPPohYPvfKhzHWu"
var receiverAddress = "n1v9HH9Abs36fYf8KbwnFUfzR4prLBXhtW"
var inputData = [NSDictionary]()
var scriptArray = [String]()
var transaction = BTCTransaction()

override func viewDidLoad() {
    super.viewDidLoad()

    getUTXOforAddress(address: addressToSpendFrom)
}

func getUTXOforAddress(address: String) {

    var url:NSURL!
    url = NSURL(string: "https://api.blockcypher.com/v1/btc/test3/addrs/\(address)?unspentOnly=true")

    let task = URLSession.shared.dataTask(with: url! as URL) { (data, response, error) -> Void in

        do {

            if error != nil {

                print(error as Any)
                DispatchQueue.main.async {
                    displayAlert(viewController: self, title: "Error", message: "Please check your interent connection.")
                }

            } else {

                if let urlContent = data {

                    do {

                        let jsonUTXOResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableLeaves) as! NSDictionary

                        print("json = \(jsonUTXOResult)")

                        if let utxoCheck = jsonUTXOResult["txrefs"] as? NSArray {

                            self.inputData = utxoCheck as! [NSDictionary]
                            print("utxoCheck = \(utxoCheck)")

                            for item in self.inputData {

                               let transactionHash = (item)["tx_hash"] as! String
                                let value = (item)["value"] as! Int

                                var url:NSURL!
                                url = NSURL(string: "https://api.blockcypher.com/v1/btc/test3/txs/\(transactionHash)")

                                let task = URLSession.shared.dataTask(with: url! as URL) { (data, response, error) -> Void in

                                    do {

                                        if error != nil {

                                            print(error as Any)
                                            DispatchQueue.main.async {
                                                displayAlert(viewController: self, title: "Error", message: "Please check your interent connection.")
                                            }

                                        } else {

                                            if let urlContent = data {

                                                do {

                                                    let txHashResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableLeaves) as! NSDictionary

                                                    print("txHashResult = \(txHashResult)")

                                                    if let outputsCheck = txHashResult["outputs"] as? NSArray {

                                                        print("outputs = \(outputsCheck)")

                                                        for output in outputsCheck {

                                                            if let valueCheck = (output as! NSDictionary)["value"] as? Int {

                                                                if valueCheck == value {

                                                                    let script = (output as! NSDictionary)["script"] as! String
                                                                    self.scriptArray.append(script)
                                                                    print("script = \(script)")
                                                                }

                                                            }

                                                        }

                                                        print("inputData = \(self.inputData)")
                                                        print("scriptArray = \(self.scriptArray)")
                                                        self.callBTCTransaction()

                                                    }

                                                } catch {

                                                    print("JSon processing failed")
                                                    DispatchQueue.main.async {
                                                        displayAlert(viewController: self, title: "Error", message: "Please try again.")
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }

                                task.resume()
                            }
                       }

                    } catch {

                        print("JSon processing failed")
                        DispatchQueue.main.async {
                            displayAlert(viewController: self, title: "Error", message: "Please try again.")
                        }
                    }
                }
            }
        }
    }

    task.resume()

}

func callBTCTransaction() {

    let address = BTCAddress(string: self.receiverAddress)
    let newTransaction = BTCTransactionBuilder()
    newTransaction.dataSource = self
    newTransaction.shouldSign = true
    newTransaction.changeAddress = BTCAddress(string: self.addressToSpendFrom)
    newTransaction.outputs = [BTCTransactionOutput(value: BTCAmount(1000), address: address)]
    newTransaction.feeRate = BTCAmount(2000)
    var result:BTCTransactionBuilderResult? = nil
    do {
        result = try newTransaction.buildTransaction()
        print("transactionRaw = \(String(describing: result?.transaction.hex))")
    } catch {
        print("error = \(error as Any)")
    }
}

func transactionBuilder(_ txbuilder: BTCTransactionBuilder!, keyForUnspentOutput txout: BTCTransactionOutput!) -> BTCKey! {
    print("transactionBuilder")

    let key = BTCKey.init(wif: self.privateKeyToSign)
    key?.isPublicKeyCompressed = true

    return key
}



func unspentOutputs(for txbuilder: BTCTransactionBuilder!) -> NSEnumerator! {

    let outputs = NSMutableArray()

    for (index, item) in inputData.enumerated() {

        let txout = BTCTransactionOutput()
        txout.value = BTCAmount((item).value(forKey: "value") as! Int64)
        txout.script = BTCScript.init(hex: self.scriptArray[index])
        txout.index = UInt32((item).value(forKey: "tx_output_n") as! Int)
        txout.confirmations = UInt((item).value(forKey: "confirmations") as! Int)
        let transactionHash = (item)["tx_hash"] as! String
        txout.transactionHash = transactionHash.data(using: .utf8)
        outputs.add(txout)

    }

    print("outputs = \(outputs)")

    return outputs.objectEnumerator()
}

}

Fontaine

Posted 2018-07-23T00:44:31.490

Reputation: 312

I will offer a bounty of $50 in Bitcoin to anyone who helps me to resolve this so that it returns a proper hex transaction. – Fontaine – 2018-08-01T05:40:35.227

Answers

0

I can't comment due to low reputation. Take a look at this link: https://github.com/oleganza/CoreBitcoin/blob/master/CoreBitcoinTestsOSX/BTCTransactionTests.swift

Basically, they create a tx like that:

    let tx = BTCTransaction()

    var spentCoins = BTCAmount(0)

    // Add all outputs as inputs
    for txout in txouts {
        let txin = BTCTransactionInput()
        txin.previousHash = txout.transactionHash
        txin.previousIndex = txout.index
        tx.addInput(txin)

        print("txhash: http://blockchain.info/rawtx/\(BTCHexFromData(txout.transactionHash))")
        print("txhash: http://blockchain.info/rawtx/\(BTCHexFromData(BTCReversedData(txout.transactionHash))) (reversed)")

        spentCoins += txout.value
    }

    print(String(format: "Total satoshis to spend:       %lld", spentCoins))
    print(String(format: "Total satoshis to destination: %lld", amount))
    print(String(format: "Total satoshis to fee:         %lld", fee))
    print(String(format: "Total satoshis to change:      %lld", spentCoins - (amount + fee)))

    // Add required outputs - payment and change
    let paymentOutput = BTCTransactionOutput(value: amount, address: destinationAddress)
    let changeOutput = BTCTransactionOutput(value: (spentCoins - (amount + fee)), address: changeAddress)

    // Idea: deterministically-randomly choose which output goes first to improve privacy.
    tx.addOutput(paymentOutput)
    tx.addOutput(changeOutput)

    for i in 0 ..< txouts.count {
        // Normally, we have to find proper keys to sign this txin, but in this
        // example we already know that we use a single private key.

        let txout = txouts[i] // output from a previous tx which is referenced by this txin.
        let txin = tx.inputs[i] as! BTCTransactionInput

        let sigScript = BTCScript()

        let d1 = tx.data

        let hashType = BTCSignatureHashType.SIGHASH_ALL


        let getHash: NSData?
        do {
            getHash = try tx.signatureHashForScript(txout.script, inputIndex: UInt32(i), hashType: hashType)
        } catch {
            errorOut = error
            getHash = nil
        }

        let d2 = tx.data

        XCTAssertEqual(d1, d2, "Transaction must not change within signatureHashForScript!")

        // 134675e153a5df1b8e0e0f0c45db0822f8f681a2eb83a0f3492ea8f220d4d3e4
        guard let hash = getHash else { return (nil, errorOut) }
        print(String(format: "Hash for input %d: \(BTCHexFromData(hash))", i))
        let signatureForScript = key.signatureForHash(hash, hashType: hashType)
        sigScript.appendData(signatureForScript)
        sigScript.appendData(key.publicKey)

        let sig = signatureForScript.subdataWithRange(NSRange(location: 0, length: signatureForScript.length - 1))  // trim hashtype byte to check the signature.
        XCTAssertTrue(key.isValidSignature(sig, hash: hash), "Signature must be valid")

        txin.signatureScript = sigScript
    }

    // Validate the signatures before returning for extra measure.

    do {
        let sm = BTCScriptMachine(transaction: tx, inputIndex: 0)

        do {
            try sm.verifyWithOutputScript((txouts.first as BTCTransactionOutput!).script.copy() as! BTCScript)
        } catch {
            print("Error: \(error)")
            XCTFail("should verify first output")
        }


    }

In my case, I recover tx that was built on the JS backend, and I am only signing it. I'll post my code example later.

caffeinum

Posted 2018-07-23T00:44:31.490

Reputation: 51