Verwenden von CoreBitcoin in Swift zum Erstellen von Rohtransaktionen

Ich bin neu im Programmieren und kenne nur Swift, obj-C ist mir noch fremd. Ich habe eine funktionierende Brieftasche, aber im Moment verlasse ich mich auf die BlockCypher-API, um eine Transaktion zu erstellen, die ich NICHT machen möchte. Kann mir bitte jemand sagen, was ich im folgenden Code-Snippet falsch mache. Ich erstelle eine Rohtransaktion, aber ich bekomme eine seltsame Antwort, wenn ich sie decodiere, wo die Adressarrays leer oder null sind. Irgendetwas stimmt nicht, wenn jemand irgendwelche Erfahrungen hat, würde ich es sehr schätzen, denn das macht mich verrückt.

import UIKit

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

}

Ich werde jedem, der mir bei der Lösung dieses Problems hilft, ein Kopfgeld von 50 US-Dollar in Bitcoin anbieten, damit es eine ordnungsgemäße Hex-Transaktion zurückgibt.

Antworten (1)

Kann ich wegen schlechter Reputation nicht kommentieren. Sehen Sie sich diesen Link an: https://github.com/oleganza/CoreBitcoin/blob/master/CoreBitcoinTestsOSX/BTCTransactionTests.swift

Im Grunde erstellen sie einen TX wie diesen:

    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 meinem Fall stelle ich tx wieder her, das auf dem JS-Backend erstellt wurde, und signiere es nur. Ich werde mein Codebeispiel später posten.