github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/others/utils/fetchtx.go (about)

     1  package utils
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"os"
    10  	"time"
    11  
    12  	"github.com/piotrnar/gocoin/lib/btc"
    13  )
    14  
    15  // GetTxFromExplorer downloads (and re-assembles) raw transaction from blockexplorer.com.
    16  func GetTxFromExplorer(txid *btc.Uint256, testnet bool) (rawtx []byte) {
    17  	var url string
    18  	if testnet {
    19  		url = "http://testnet.blockexplorer.com/api/rawtx/" + txid.String()
    20  	} else {
    21  		url = "http://blockexplorer.com/api/rawtx/" + txid.String()
    22  	}
    23  	r, er := http.Get(url)
    24  	if er == nil {
    25  		if r.StatusCode == 200 {
    26  			defer r.Body.Close()
    27  			c, _ := io.ReadAll(r.Body)
    28  			var txx struct {
    29  				Raw string `json:"rawtx"`
    30  			}
    31  			er = json.Unmarshal(c[:], &txx)
    32  			if er == nil {
    33  				rawtx, er = hex.DecodeString(txx.Raw)
    34  			}
    35  		} else {
    36  			fmt.Println("blockexplorer.com StatusCode=", r.StatusCode)
    37  		}
    38  	}
    39  	if er != nil {
    40  		fmt.Println("blockexplorer.com:", er.Error())
    41  	}
    42  	return
    43  }
    44  
    45  // GetTxFromWebBTC downloads a raw transaction from webbtc.com.
    46  func GetTxFromWebBTC(txid *btc.Uint256) (raw []byte) {
    47  	url := "https://webbtc.com/tx/" + txid.String() + ".bin"
    48  	r, er := http.Get(url)
    49  	if er == nil {
    50  		if r.StatusCode == 200 {
    51  			raw, _ = io.ReadAll(r.Body)
    52  			r.Body.Close()
    53  		} else {
    54  			fmt.Println("webbtc.com StatusCode=", r.StatusCode)
    55  		}
    56  	}
    57  	if er != nil {
    58  		fmt.Println("webbtc.com:", er.Error())
    59  	}
    60  	return
    61  }
    62  
    63  // GetTxFromBlockchainInfo downloads (and re-assembles) raw transaction from blockchain.info.
    64  func GetTxFromBlockchainInfo(txid *btc.Uint256) (rawtx []byte) {
    65  	url := "https://blockchain.info/tx/" + txid.String() + "?format=hex"
    66  	r, er := http.Get(url)
    67  	if er == nil {
    68  		if r.StatusCode == 200 {
    69  			defer r.Body.Close()
    70  			rawhex, _ := io.ReadAll(r.Body)
    71  			rawtx, er = hex.DecodeString(string(rawhex))
    72  		} else {
    73  			fmt.Println("blockchain.info StatusCode=", r.StatusCode)
    74  		}
    75  	}
    76  	if er != nil {
    77  		fmt.Println("blockchain.info:", er.Error())
    78  	}
    79  	return
    80  }
    81  
    82  // GetTxFromBlockcypher downloads (and re-assembles) raw transaction from blockcypher.com.
    83  func GetTxFromBlockcypher(txid *btc.Uint256, currency string) (rawtx []byte) {
    84  	var r *http.Response
    85  	var er error
    86  	var try_cnt int
    87  
    88  	token := os.Getenv("BLOCKCYPHER_TOKEN")
    89  	if token == "" {
    90  		println("WARNING: BLOCKCYPHER_TOKEN envirionment variable not set (get it from blockcypher.com)")
    91  	} else {
    92  		token = "&token=" + token
    93  	}
    94  
    95  	url := "https://api.blockcypher.com/v1/" + currency + "/main/txs/" + txid.String() + "?limit=1000&instart=1000&outstart=1000&includeHex=true" + token
    96  
    97  	for {
    98  		r, er = http.Get(url)
    99  		if er != nil {
   100  			fmt.Println("blockcypher.com:", er.Error())
   101  			return
   102  		}
   103  
   104  		if r.StatusCode == 429 && try_cnt < 5 {
   105  			try_cnt++
   106  			println("Retry blockcypher.com in", try_cnt, "seconds...")
   107  			time.Sleep(time.Duration(try_cnt) * time.Second)
   108  			continue
   109  		}
   110  
   111  		break
   112  	}
   113  
   114  	if er == nil {
   115  		if r.StatusCode == 200 {
   116  			defer r.Body.Close()
   117  			c, _ := io.ReadAll(r.Body)
   118  			var txx struct {
   119  				Raw string `json:"hex"`
   120  			}
   121  			er = json.Unmarshal(c[:], &txx)
   122  			if er == nil {
   123  				rawtx, er = hex.DecodeString(txx.Raw)
   124  			}
   125  		} else {
   126  			fmt.Println("blockcypher.com StatusCode=", r.StatusCode)
   127  		}
   128  	}
   129  	return
   130  }
   131  
   132  func GetTxFromBlockchair(txid *btc.Uint256, currency string) (rawtx []byte) {
   133  	var r *http.Response
   134  	var er error
   135  	var try_cnt int
   136  
   137  	for {
   138  		r, er = http.Get("https://api.blockchair.com/" + currency + "/raw/transaction/" + txid.String())
   139  
   140  		if er != nil {
   141  			return
   142  		}
   143  		if (r.StatusCode == 402 || r.StatusCode == 429) && try_cnt < 5 {
   144  			try_cnt++
   145  			println("Retry blockchair.com in", try_cnt, "seconds...")
   146  			time.Sleep(time.Duration(try_cnt) * time.Second)
   147  			continue
   148  		}
   149  		if r.StatusCode != 200 {
   150  			return
   151  		}
   152  		break
   153  	}
   154  
   155  	c, _ := io.ReadAll(r.Body)
   156  	r.Body.Close()
   157  
   158  	var result struct {
   159  		Data map[string]struct {
   160  			Raw string `json:"raw_transaction"`
   161  		} `json:"data"`
   162  	}
   163  
   164  	er = json.Unmarshal(c, &result)
   165  	if er != nil {
   166  		return
   167  	}
   168  
   169  	if rec, ok := result.Data[txid.String()]; ok {
   170  		rawtx, er = hex.DecodeString(rec.Raw)
   171  	}
   172  
   173  	return
   174  }
   175  
   176  // GetTxFromBlockstream downloads a raw transaction from webbtc.com.
   177  func GetTxFromBlockstream(txid *btc.Uint256, api_url string) (raw []byte) {
   178  	url := api_url + txid.String() + "/raw"
   179  	r, er := http.Get(url)
   180  	if er == nil {
   181  		if r.StatusCode == 200 {
   182  			raw, _ = io.ReadAll(r.Body)
   183  			r.Body.Close()
   184  		} else {
   185  			fmt.Println("blockstream.info get_tx StatusCode=", r.StatusCode)
   186  		}
   187  	}
   188  	if er != nil {
   189  		fmt.Println("blockstream.info get_tx:", er.Error())
   190  	}
   191  	return
   192  }
   193  
   194  func verify_txid(txid *btc.Uint256, rawtx []byte) bool {
   195  	tx, _ := btc.NewTx(rawtx)
   196  	if tx == nil {
   197  		return false
   198  	}
   199  	tx.SetHash(rawtx)
   200  	return txid.Equal(&tx.Hash)
   201  }
   202  
   203  // GetTxFromWeb downloads a raw transaction from a web server (try one after another).
   204  func GetTxFromWeb(txid *btc.Uint256) (raw []byte) {
   205  	//
   206  
   207  	raw = GetTxFromBlockstream(txid, "https://blockstream.info/api/tx/")
   208  	if raw != nil && verify_txid(txid, raw) {
   209  		//println("GetTxFromBlockstream - OK")
   210  		return
   211  	}
   212  
   213  	raw = GetTxFromBlockchair(txid, "bitcoin")
   214  	if raw != nil && verify_txid(txid, raw) {
   215  		//println("GetTxFromBlockchair - OK")
   216  		return
   217  	}
   218  
   219  	raw = GetTxFromExplorer(txid, false)
   220  	if raw != nil && verify_txid(txid, raw) {
   221  		//println("GetTxFromExplorer - OK")
   222  		return
   223  	}
   224  
   225  	raw = GetTxFromBlockchainInfo(txid)
   226  	if raw != nil && verify_txid(txid, raw) {
   227  		//println("GetTxFromBlockchainInfo - OK")
   228  		return
   229  	}
   230  
   231  	raw = GetTxFromBlockcypher(txid, "btc")
   232  	if raw != nil && verify_txid(txid, raw) {
   233  		//println("GetTxFromBlockcypher - OK")
   234  		return
   235  	}
   236  
   237  	raw = GetTxFromWebBTC(txid)
   238  	if raw != nil && verify_txid(txid, raw) {
   239  		//println("GetTxFromWebBTC - OK")
   240  		return
   241  	}
   242  
   243  	return
   244  }
   245  
   246  // GetTestnetTxFromWeb downloads a testnet's raw transaction from a web server.
   247  func GetTestnetTxFromWeb(txid *btc.Uint256) (raw []byte) {
   248  	raw = GetTxFromBlockstream(txid, "https://blockstream.info/testnet/api/tx/")
   249  	if raw != nil && verify_txid(txid, raw) {
   250  		//println("GetTxFromBlockstream - OK")
   251  		return
   252  	}
   253  
   254  	raw = GetTxFromExplorer(txid, true)
   255  	if raw != nil && verify_txid(txid, raw) {
   256  		//println("GetTxFromExplorer - OK")
   257  		return
   258  	}
   259  
   260  	raw = GetTxFromBlockcypher(txid, "btc-testnet")
   261  	if raw != nil && verify_txid(txid, raw) {
   262  		//println("GetTxFromBlockcypher - OK")
   263  		return
   264  	}
   265  
   266  	return
   267  }