github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/tools/balio/balio.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "github.com/piotrnar/gocoin" 9 "github.com/piotrnar/gocoin/lib/btc" 10 "github.com/piotrnar/gocoin/lib/others/ltc" 11 "github.com/piotrnar/gocoin/lib/others/utils" 12 "github.com/piotrnar/gocoin/lib/utxo" 13 "io" 14 "io/ioutil" 15 "net" 16 "net/http" 17 "os" 18 "strconv" 19 "strings" 20 ) 21 22 const MAX_UNSPENT_AT_ONCE = 20 23 24 var ( 25 proxy string 26 ltc_mode bool 27 tbtc bool 28 ) 29 30 func print_help() { 31 fmt.Println() 32 fmt.Println("Specify at lest one parameter on the command line.") 33 fmt.Println(" Name of one text file containing BTC/LTC addresses,") 34 fmt.Println("... or space separteted BTC/LTC addresses themselves.") 35 fmt.Println() 36 fmt.Println("Add -ltc at the command line, to force fetching Litecoin balance.") 37 fmt.Println("Add -t at the command line, to force fetching Testnet balance.") 38 fmt.Println() 39 fmt.Println("To use Tor, setup environment variable TOR=host:port") 40 fmt.Println("The host:port should point to your Tor's SOCKS proxy.") 41 } 42 43 func dials5(tcp, dest string) (conn net.Conn, err error) { 44 //println("Tor'ing to", dest, "via", proxy) 45 var buf [10]byte 46 var host, ps string 47 var port uint64 48 49 conn, err = net.Dial(tcp, proxy) 50 if err != nil { 51 return 52 } 53 54 _, err = conn.Write([]byte{5, 1, 0}) 55 if err != nil { 56 return 57 } 58 59 _, err = io.ReadFull(conn, buf[:2]) 60 if err != nil { 61 return 62 } 63 64 if buf[0] != 5 { 65 err = errors.New("We only support SOCKS5 proxy.") 66 } else if buf[1] != 0 { 67 err = errors.New("SOCKS proxy connection refused.") 68 return 69 } 70 71 host, ps, err = net.SplitHostPort(dest) 72 if err != nil { 73 return 74 } 75 76 port, err = strconv.ParseUint(ps, 10, 16) 77 if err != nil { 78 return 79 } 80 81 req := make([]byte, 5+len(host)+2) 82 copy(req[:4], []byte{5, 1, 0, 3}) 83 req[4] = byte(len(host)) 84 copy(req[5:], []byte(host)) 85 binary.BigEndian.PutUint16(req[len(req)-2:], uint16(port)) 86 _, err = conn.Write(req) 87 if err != nil { 88 return 89 } 90 91 _, err = io.ReadFull(conn, buf[:]) 92 if err != nil { 93 return 94 } 95 96 if buf[1] != 0 { 97 err = errors.New("SOCKS proxy connection terminated.") 98 } 99 100 return 101 } 102 103 func splitHostPort(addr string) (host string, port uint16, err error) { 104 host, portStr, err := net.SplitHostPort(addr) 105 portInt, err := strconv.ParseUint(portStr, 10, 16) 106 port = uint16(portInt) 107 return 108 } 109 110 func curr_unit() string { 111 if ltc_mode { 112 return "LTC" 113 } else { 114 return "BTC" 115 } 116 } 117 118 func load_wallet(fn string) (addrs []*btc.BtcAddr) { 119 f, e := os.Open(fn) 120 if e != nil { 121 println(e.Error()) 122 return 123 } 124 defer f.Close() 125 rd := bufio.NewReader(f) 126 linenr := 0 127 for { 128 var l string 129 l, e = rd.ReadString('\n') 130 l = strings.Trim(l, " \t\r\n") 131 linenr++ 132 if len(l) > 0 { 133 if l[0] == '@' { 134 fmt.Println("netsted wallet in line", linenr, "- ignore it") 135 } else if l[0] != '#' { 136 ls := strings.SplitN(l, " ", 2) 137 if len(ls) > 0 { 138 a, e := btc.NewAddrFromString(ls[0]) 139 if e != nil { 140 println(fmt.Sprint(fn, ":", linenr), e.Error()) 141 } else { 142 addrs = append(addrs, a) 143 } 144 } 145 } 146 } 147 if e != nil { 148 break 149 } 150 } 151 return 152 } 153 154 func main() { 155 fmt.Println("Gocoin BalIO version", gocoin.Version) 156 157 if len(os.Args) < 2 { 158 print_help() 159 return 160 } 161 162 proxy = os.Getenv("TOR") 163 if proxy != "" { 164 fmt.Println("Using Tor at", proxy) 165 http.DefaultClient.Transport = &http.Transport{Dial: dials5} 166 } else { 167 fmt.Println("WARNING: not using Tor (setup TOR variable, if you want)") 168 } 169 170 var addrs []*btc.BtcAddr 171 172 var argz []string 173 for i := 1; i < len(os.Args); i++ { 174 if os.Args[i] == "-ltc" { 175 ltc_mode = true 176 } else if os.Args[i] == "-t" { 177 tbtc = true 178 } else { 179 argz = append(argz, os.Args[i]) 180 } 181 } 182 183 if len(argz) == 1 { 184 fi, er := os.Stat(argz[0]) 185 if er == nil && fi.Size() > 10 && !fi.IsDir() { 186 addrs = load_wallet(argz[0]) 187 if addrs != nil { 188 fmt.Println("Found", len(addrs), "address(es) in", argz[0]) 189 } 190 } 191 } 192 193 if len(addrs) == 0 { 194 for i := range argz { 195 a, e := btc.NewAddrFromString(argz[i]) 196 if e != nil { 197 println(argz[i], ": ", e.Error()) 198 return 199 } else { 200 addrs = append(addrs, a) 201 } 202 } 203 } 204 205 if len(addrs) == 0 { 206 print_help() 207 return 208 } 209 210 for i := range addrs { 211 switch addrs[i].Version { 212 case 48: 213 ltc_mode = true 214 case 111: 215 tbtc = true 216 } 217 } 218 219 if tbtc && ltc_mode { 220 println("Litecoin's testnet is not suppported") 221 return 222 } 223 224 if len(addrs) == 0 { 225 println("No addresses to fetch balance for") 226 return 227 } 228 229 var sum, outcnt uint64 230 231 os.RemoveAll("balance/") 232 os.Mkdir("balance/", 0700) 233 unsp, _ := os.Create("balance/unspent.txt") 234 for off := 0; off < len(addrs); off++ { 235 var res utxo.AllUnspentTx 236 if ltc_mode { 237 res = ltc.GetUnspent(addrs[off]) 238 } else if tbtc { 239 res = utils.GetUnspentTestnet(addrs[off]) 240 } else { 241 res = utils.GetUnspent(addrs[off]) 242 } 243 for _, r := range res { 244 var txraw []byte 245 id := btc.NewUint256(r.TxPrevOut.Hash[:]) 246 if ltc_mode { 247 txraw = ltc.GetTxFromWeb(id) 248 } else if tbtc { 249 txraw = utils.GetTestnetTxFromWeb(id) 250 } else { 251 txraw = utils.GetTxFromWeb(id) 252 } 253 if len(txraw) > 0 { 254 ioutil.WriteFile("balance/"+id.String()+".tx", txraw, 0666) 255 } else { 256 println("ERROR: cannot fetch raw tx data for", id.String()) 257 //os.Exit(1) 258 } 259 260 sum += r.Value 261 outcnt++ 262 263 fmt.Fprintln(unsp, r.UnspentTextLine()) 264 } 265 } 266 unsp.Close() 267 if outcnt > 0 { 268 fmt.Printf("Total %.8f %s in %d unspent outputs.\n", float64(sum)/1e8, curr_unit(), outcnt) 269 fmt.Println("The data has been stored in 'balance' folder.") 270 fmt.Println("Use it with the wallet app to spend any of it.") 271 } else { 272 fmt.Println("No coins found on the given address(es).") 273 } 274 }