Soy nuevo en la codificación, y solo sé rápido, obj-C todavía es extraño para mí. Tengo una billetera que funciona, pero por ahora confío en la API BlockCypher para crear una transacción que NO quiero hacer. ¿Puede alguien ayudarme a decirme qué estoy haciendo mal en el siguiente fragmento de código? Estoy creando una transacción sin procesar, sin embargo, recibo una respuesta extraña cuando la decodifico donde las matrices de direcciones están vacías o son nulas. Algo está muy mal, si alguien tiene alguna experiencia se lo agradecería mucho ya que esto me está volviendo loco.
import UIKit
clase 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()
}
}
No puedo comentar debido a la mala reputación. Echa un vistazo a este enlace: https://github.com/oleganza/CoreBitcoin/blob/master/CoreBitcoinTestsOSX/BTCTransactionTests.swift
Básicamente, crean un tx como ese:
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")
}
}
En mi caso, recupero tx que se creó en el backend de JS y solo lo estoy firmando. Publicaré mi ejemplo de código más tarde.
fontaine