// TODO: Condense TestUnspents into ONE call for MULTIPLE addresses (separated by | for Unspents according to API docs) // TODO: Enable backend to call a running bitcoin node for all this instead of callign third party services public static bool TestUnspents(BitcoinChain chain, string address) { // This function queries the Blockchain API for the unspent coin. bool result = false; HotBitcoinAddress hotAddress = null; JObject addressInfoResult; JObject unspentJsonResult; switch (chain) { case BitcoinChain.Core: addressInfoResult = JObject.Parse( new WebClient().DownloadString( "https://blockchain.info/address/" + address + "?format=json&api_key=" + SystemSettings.BlockchainSwarmopsApiKey)); if ((int)addressInfoResult["final_balance"] == 0) { return(false); // no funds on address at all at this time } try { unspentJsonResult = JObject.Parse( new WebClient().DownloadString("https://blockchain.info/unspent?active=" + address + "&api_key=" + SystemSettings.BlockchainSwarmopsApiKey)); } catch (WebException webException) { // A 500 on the above _may_ mean that there's no unspent outpoints. It can also mean a data // retrieval or network error, in which case the exception must absolutely not be interpreted // as valid data of zero unspent outpoints. try { if (webException.Response == null) { throw; // if there's no response at all, we can't do shit } string errorResponseContent = new StreamReader(webException.Response.GetResponseStream()).ReadToEnd(); if (errorResponseContent.Trim().StartsWith("No free outputs to spend")) { // all is okay network-wise, there just aren't any UTXOs so we're getting an error code for that return(false); // no further processing and there are no fresh transactions } throw; // otherwise throw upward } catch (WebException) { // Ok, we tried, but there's apparently a network error so we need to abort this whole thing throw; } } foreach (var unspentJson in unspentJsonResult["unspent_outputs"]) { BitcoinUnspentTransactionOutput txUnspent = new BitcoinUnspentTransactionOutput() { BitcoinAddress = address, ConfirmationCount = (UInt32)unspentJson["confirmations"], Satoshis = (UInt64)unspentJson["value"], TransactionHash = (string)unspentJson["tx_hash_big_endian"], TransactionOutputIndex = (UInt32)unspentJson["tx_output_n"] }; if (txUnspent.ConfirmationCount < 2) { // Fresh transactions, return true result = true; } // Add unspent to database if (hotAddress == null) { hotAddress = HotBitcoinAddress.FromAddress(chain, address); } SwarmDb.GetDatabaseForWriting() .CreateHotBitcoinAddressUnspentConditional(hotAddress.Identity, txUnspent.TransactionHash, (int)txUnspent.TransactionOutputIndex, (Int64)txUnspent.Satoshis, (int)txUnspent.ConfirmationCount); } // Update hotaddress totals HotBitcoinAddresses.UpdateAllUnspentTotals(); return(result); case BitcoinChain.Cash: // TODO: SELECTION OF BLOCK EXPLORER, ADDRESS STRING FORMAT TO GO WITH IT addressInfoResult = JObject.Parse( new WebClient().DownloadString( "https://bitcoincash.blockexplorer.com/api/addr/" + address)); JArray unspentArray; if ((int)addressInfoResult["balanceSat"] == 0 && (int)addressInfoResult["unconfirmedBalanceSat"] == 0) { return(false); // no funds on address at all at this time } try { unspentArray = JArray.Parse( new WebClient().DownloadString("https://bitcoincash.blockexplorer.com/api/addr/" + address + "/utxo")); } catch (WebException webException) { // A 500 on the above _may_ mean that there's no unspent outpoints. It can also mean a data // retrieval or network error, in which case the exception must absolutely not be interpreted // as valid data of zero unspent outpoints. try { if (webException.Response == null) { throw; // if there's no response at all, we can't do shit } string errorResponseContent = new StreamReader(webException.Response.GetResponseStream()).ReadToEnd(); if (errorResponseContent.Trim().StartsWith("No free outputs to spend")) { // all is okay network-wise, there just aren't any UTXOs so we're getting an error code for that return(false); // no further processing and there are no fresh transactions } throw; // otherwise throw upward } catch (WebException) { // Ok, we tried, but there's apparently a network error so we need to abort this whole thing throw; } } foreach (JObject unspentJson in unspentArray.Children()) { BitcoinUnspentTransactionOutput txUnspent = new BitcoinUnspentTransactionOutput() { BitcoinAddress = address, ConfirmationCount = (UInt32)unspentJson["confirmations"], Satoshis = (UInt64)unspentJson["satoshis"], TransactionHash = (string)unspentJson["txid"], TransactionOutputIndex = (UInt32)unspentJson["vout"] }; if (txUnspent.ConfirmationCount < 2) { // Fresh transactions, return true result = true; } // Add unspent to database if (hotAddress == null) { hotAddress = HotBitcoinAddress.GetAddressOrForkCore(chain, address); } SwarmDb.GetDatabaseForWriting() .CreateHotBitcoinAddressUnspentConditional(hotAddress.Identity, txUnspent.TransactionHash, (int)txUnspent.TransactionOutputIndex, (Int64)txUnspent.Satoshis, (int)txUnspent.ConfirmationCount); } // Update hotaddress totals HotBitcoinAddresses.UpdateAllUnspentTotals(); return(result); default: throw new NotImplementedException("Unimplemented bitcoin chain: " + chain); } }
public static Coin[] GetSpendableCoin(BitcoinChain chain, BitcoinSecret secretKey) // we're using BitcoinSecret as arg just to reinforce caller must have privkey to spend funds { List <Coin> coinList = new List <Coin>(); switch (chain) { case BitcoinChain.Core: // For the Core net, we query the Blockchain API for the unspent coin. string addressString = secretKey.PubKey.GetAddress(Network.Main).ToString(); var unspentJsonResult = JObject.Parse( new WebClient().DownloadString("https://blockchain.info/unspent?active=" + addressString + "&api_key=" + SystemSettings.BlockchainSwarmopsApiKey)); foreach (var unspentJson in unspentJsonResult["unspent_outputs"]) { BitcoinUnspentTransactionOutput txUnspent = new BitcoinUnspentTransactionOutput() { BitcoinAddress = addressString, ConfirmationCount = (UInt32)unspentJson["confirmations"], Satoshis = (UInt64)unspentJson["value"], TransactionHash = (string)unspentJson["tx_hash_big_endian"], TransactionOutputIndex = (UInt32)unspentJson["tx_output_n"] }; coinList.Add(txUnspent); // invokes implicit conversion to NBitcoin.Coin } break; case BitcoinChain.Cash: JArray unspentArray; // TODO: SELECT BLOCK EXPLORER AND ITS ACCOMPANYING ADDRESS FORMAT unspentArray = JArray.Parse( new WebClient().DownloadString("https://bitcoincash.blockexplorer.com/api/addr/" + secretKey.PubKey.GetAddress(Network.Main) + "/utxo")); foreach (JObject unspentJson in unspentArray.Children()) { BitcoinUnspentTransactionOutput txUnspent = new BitcoinUnspentTransactionOutput() { BitcoinAddress = secretKey.PubKey.GetAddress(Network.Main).ToString(), ConfirmationCount = (UInt32)unspentJson["confirmations"], Satoshis = (UInt64)unspentJson["satoshis"], TransactionHash = (string)unspentJson["txid"], TransactionOutputIndex = (UInt32)unspentJson["vout"] }; coinList.Add(txUnspent); // invokes implicit conversion to NBitcoin.Coin } break; default: throw new NotImplementedException("Unimplemented chain identity: " + chain); } return(coinList.ToArray()); }