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  }