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 }