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()
}

}

  • 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 Aug 1 '18 at 5:40
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.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.