Esempio n. 1
0
func IsMultisig(ad *btc.BtcAddr) (yes bool, rec *MultisigAddr) {
	yes = ad.Version == btc.AddrVerScript(common.Testnet)
	if !yes {
		return
	}

	fn := common.GocoinHomeDir + "wallet" +
		string(os.PathSeparator) + "multisig" +
		string(os.PathSeparator) + ad.String() + ".json"

	d, er := ioutil.ReadFile(fn)
	if er != nil {
		//println("fn", fn, er.Error())
		return
	}

	var msa MultisigAddr
	er = json.Unmarshal(d, &msa)
	if er == nil {
		rec = &msa
	} else {
		println(fn, er.Error())
	}

	return
}
Esempio n. 2
0
func list_unspent(addr string) {
	fmt.Println("Checking unspent coins for addr", addr)

	defer func() { // in case if ad.OutScript() would panic
		if r := recover(); r != nil {
			err := r.(error)
			fmt.Println("main panic recovered:", err.Error())
		}
	}()

	var ad *btc.BtcAddr
	var e error
	ad, e = btc.NewAddrFromString(addr)
	if e != nil {
		println(e.Error())
		return
	}
	sa := ad.StealthAddr
	exp_scr := ad.OutScript()
	var walk chain.FunctionWalkUnspent
	var unsp chain.AllUnspentTx

	if sa == nil {
		walk = func(db *qdb.DB, k qdb.KeyType, rec *chain.OneWalkRecord) uint32 {
			if bytes.Equal(rec.Script(), exp_scr) {
				unsp = append(unsp, rec.ToUnspent(ad))
			}
			return 0
		}
	} else {
		wallet.FetchStealthKeys()
		d := wallet.FindStealthSecret(sa)
		if d == nil {
			fmt.Println("No matching secret found in your wallet/stealth folder")
			return
		}
		walk = func(db *qdb.DB, k qdb.KeyType, rec *chain.OneWalkRecord) uint32 {
			if !rec.IsStealthIdx() {
				return 0
			}
			fl, uo := wallet.CheckStealthRec(db, k, rec, ad, d, true)
			if uo != nil {
				unsp = append(unsp, uo)
			}
			return fl
		}
	}
	common.BlockChain.Unspent.BrowseUTXO(false, walk)

	sort.Sort(unsp)
	var sum uint64
	for i := range unsp {
		if len(unsp) < 200 {
			fmt.Println(unsp[i].String())
		}
		sum += unsp[i].Value
	}
	fmt.Printf("Total %.8f unspent BTC in %d outputs at address %s\n",
		float64(sum)/1e8, len(unsp), ad.String())
}
Esempio n. 3
0
func (r *OneWalkRecord) ToUnspent(ad *btc.BtcAddr) (nr *OneUnspentTx) {
	nr = new(OneUnspentTx)
	copy(nr.TxPrevOut.Hash[:], r.v[0:32])
	nr.TxPrevOut.Vout = binary.LittleEndian.Uint32(r.v[32:36])
	nr.Value = binary.LittleEndian.Uint64(r.v[36:44])
	nr.MinedAt = binary.LittleEndian.Uint32(r.v[44:48])
	nr.BtcAddr = ad
	nr.destString = ad.String()
	return
}
Esempio n. 4
0
func (r *QdbRec) ToUnspent(idx uint32, ad *btc.BtcAddr) (nr *OneUnspentTx) {
	nr = new(OneUnspentTx)
	nr.TxPrevOut.Hash = r.TxID
	nr.TxPrevOut.Vout = idx
	nr.Value = r.Outs[idx].Value
	nr.Coinbase = r.Coinbase
	nr.MinedAt = r.InBlock
	nr.BtcAddr = ad
	nr.destString = ad.String()
	return
}
Esempio n. 5
0
// dump hashes to be signed
func dump_hashes_to_sign(tx *btc.Tx) {
	for in := range tx.TxIn {
		uo := UO(unspentOuts[in])
		if uo == nil {
			println("Unknown content of unspent input number", in)
			os.Exit(1)
		}
		var pubad *btc.BtcAddr
		if litecoin {
			pubad = ltc.NewAddrFromPkScript(uo.Pk_script, testnet)
		} else {
			pubad = btc.NewAddrFromPkScript(uo.Pk_script, testnet)
		}
		if pubad != nil {
			hash := tx.SignatureHash(uo.Pk_script, in, btc.SIGHASH_ALL)
			fmt.Printf("Input #%d:\n\tHash : %s\n\tAddr : %s\n", in, hex.EncodeToString(hash), pubad.String())
		} else {
			println("Cannot decode pkscript of unspent input number", in)
			os.Exit(1)
		}
	}
}
Esempio n. 6
0
// make sure the version byte in the given address is what we expect
func assert_address_version(a *btc.BtcAddr) {
	if a.Version != ver_pubkey() && a.Version != ver_script() && a.Version != ver_stealth() {
		println("Sending address", a.String(), "has an incorrect version", a.Version)
		cleanExit(1)
	}
}
Esempio n. 7
0
func list_unspent(addr string) {
	fmt.Println("Checking unspent coins for addr", addr)

	defer func() { // in case if ad.OutScript() would panic
		if r := recover(); r != nil {
			err := r.(error)
			fmt.Println("main panic recovered:", err.Error())
		}
	}()

	var ad *btc.BtcAddr
	var e error
	ad, e = btc.NewAddrFromString(addr)
	if e != nil {
		println(e.Error())
		return
	}
	sa := ad.StealthAddr
	var walk chain.FunctionWalkUnspent
	var unsp chain.AllUnspentTx

	if sa == nil {
		exp_scr := ad.OutScript()
		walk = func(tx *chain.QdbRec) {
			for idx, rec := range tx.Outs {
				if rec != nil && bytes.Equal(rec.PKScr, exp_scr) {
					unsp = append(unsp, tx.ToUnspent(uint32(idx), ad))
				}
			}
		}
	} else {
		var c, spen_exp []byte
		var rec, out *chain.QdbTxOut
		var h160 [20]byte

		wallet.FetchStealthKeys()
		d := wallet.FindStealthSecret(sa)
		if d == nil {
			fmt.Println("No matching secret found in your wallet/stealth folder")
			return
		}
		walk = func(tx *chain.QdbRec) {
			for i := 0; i < len(tx.Outs)-1; i++ {
				if rec = tx.Outs[i]; rec == nil {
					continue
				}
				if out = tx.Outs[i+1]; out == nil {
					continue
				}
				if !rec.IsStealthIdx() || !out.IsP2KH() || !ad.StealthAddr.CheckNonce(rec.PKScr[3:40]) {
					continue
				}
				c = btc.StealthDH(rec.PKScr[7:40], d)
				spen_exp = btc.DeriveNextPublic(sa.SpendKeys[0][:], c)
				btc.RimpHash(spen_exp, h160[:])
				if bytes.Equal(out.PKScr[3:23], h160[:]) {
					uo := new(chain.OneUnspentTx)
					uo.TxPrevOut.Hash = tx.TxID
					uo.TxPrevOut.Vout = uint32(i + 1)
					uo.Value = out.Value
					uo.MinedAt = tx.InBlock
					uo.BtcAddr = btc.NewAddrFromHash160(h160[:], btc.AddrVerPubkey(common.CFG.Testnet))
					uo.FixDestString()
					uo.BtcAddr.StealthAddr = sa
					uo.BtcAddr.Extra = ad.Extra
					uo.StealthC = c
					unsp = append(unsp, uo)
				}
			}
		}
	}
	common.BlockChain.Unspent.BrowseUTXO(false, walk)

	sort.Sort(unsp)
	var sum uint64
	for i := range unsp {
		if len(unsp) < 200 {
			fmt.Println(unsp[i].String())
		}
		sum += unsp[i].Value
	}
	fmt.Printf("Total %.8f unspent BTC in %d outputs at address %s\n",
		float64(sum)/1e8, len(unsp), ad.String())
}
Esempio n. 8
0
func dl_payment(w http.ResponseWriter, r *http.Request) {
	if !ipchecker(r) {
		return
	}

	var err string

	if len(r.Form["outcnt"]) == 1 {
		var thisbal chain.AllUnspentTx
		var pay_cmd string
		var totalinput, spentsofar uint64
		var change_addr *btc.BtcAddr

		tx := new(btc.Tx)
		tx.Version = 1
		tx.Lock_time = 0

		seq, er := strconv.ParseInt(r.Form["tx_seq"][0], 10, 64)
		if er != nil || seq < -2 || seq > 0xffffffff {
			err = "Incorrect Sequence value: " + r.Form["tx_seq"][0]
			goto error
		}

		outcnt, _ := strconv.ParseUint(r.Form["outcnt"][0], 10, 32)

		lck := new(usif.OneLock)
		lck.In.Add(1)
		lck.Out.Add(1)
		usif.LocksChan <- lck
		lck.In.Wait()
		defer lck.Out.Done()

		for i := 1; i <= int(outcnt); i++ {
			is := fmt.Sprint(i)
			if len(r.Form["txout"+is]) == 1 && r.Form["txout"+is][0] == "on" {
				hash := btc.NewUint256FromString(r.Form["txid"+is][0])
				if hash != nil {
					vout, er := strconv.ParseUint(r.Form["txvout"+is][0], 10, 32)
					if er == nil {
						var po = btc.TxPrevOut{Hash: hash.Hash, Vout: uint32(vout)}
						if res, er := common.BlockChain.Unspent.UnspentGet(&po); er == nil {
							addr := btc.NewAddrFromPkScript(res.Pk_script, common.Testnet)

							unsp := &chain.OneUnspentTx{TxPrevOut: po, Value: res.Value,
								MinedAt: res.BlockHeight, Coinbase: res.WasCoinbase, BtcAddr: addr}

							thisbal = append(thisbal, unsp)

							// Add the input to our tx
							tin := new(btc.TxIn)
							tin.Input = po
							tin.Sequence = uint32(seq)
							tx.TxIn = append(tx.TxIn, tin)

							// Add the value to total input value
							totalinput += res.Value

							// If no change specified, use the first input addr as it
							if change_addr == nil {
								change_addr = addr
							}
						}
					}
				}
			}
		}

		if change_addr == nil {
			// There werte no inputs
			return
		}
		//wallet.BalanceMutex.Lock()
		//wallet.BalanceMutex.Unlock()

		for i := 1; ; i++ {
			adridx := fmt.Sprint("adr", i)
			btcidx := fmt.Sprint("btc", i)

			if len(r.Form[adridx]) != 1 || len(r.Form[btcidx]) != 1 {
				break
			}

			if len(r.Form[adridx][0]) > 1 {
				addr, er := btc.NewAddrFromString(r.Form[adridx][0])
				if er == nil {
					am, er := btc.StringToSatoshis(r.Form[btcidx][0])
					if er == nil && am > 0 {
						if pay_cmd == "" {
							pay_cmd = "wallet -useallinputs -send "
						} else {
							pay_cmd += ","
						}
						pay_cmd += addr.Enc58str + "=" + btc.UintToBtc(am)

						outs, er := btc.NewSpendOutputs(addr, am, common.CFG.Testnet)
						if er != nil {
							err = er.Error()
							goto error
						}
						tx.TxOut = append(tx.TxOut, outs...)

						spentsofar += am
					} else {
						err = "Incorrect amount (" + r.Form[btcidx][0] + ") for Output #" + fmt.Sprint(i)
						goto error
					}
				} else {
					err = "Incorrect address (" + r.Form[adridx][0] + ") for Output #" + fmt.Sprint(i)
					goto error
				}
			}
		}

		if pay_cmd == "" {
			err = "No inputs selected"
			goto error
		}

		pay_cmd += fmt.Sprint(" -seq ", seq)

		am, er := btc.StringToSatoshis(r.Form["txfee"][0])
		if er != nil {
			err = "Incorrect fee value: " + r.Form["txfee"][0]
			goto error
		}

		pay_cmd += " -fee " + r.Form["txfee"][0]
		spentsofar += am

		if len(r.Form["change"][0]) > 1 {
			addr, er := btc.NewAddrFromString(r.Form["change"][0])
			if er != nil {
				err = "Incorrect change address: " + r.Form["change"][0]
				goto error
			}
			change_addr = addr
		}
		pay_cmd += " -change " + change_addr.String()

		if totalinput > spentsofar {
			// Add change output
			outs, er := btc.NewSpendOutputs(change_addr, totalinput-spentsofar, common.CFG.Testnet)
			if er != nil {
				err = er.Error()
				goto error
			}
			tx.TxOut = append(tx.TxOut, outs...)
		}

		buf := new(bytes.Buffer)
		zi := zip.NewWriter(buf)

		was_tx := make(map[[32]byte]bool, len(thisbal))
		for i := range thisbal {
			if was_tx[thisbal[i].TxPrevOut.Hash] {
				continue
			}
			was_tx[thisbal[i].TxPrevOut.Hash] = true
			txid := btc.NewUint256(thisbal[i].TxPrevOut.Hash[:])
			fz, _ := zi.Create("balance/" + txid.String() + ".tx")
			if dat, er := common.BlockChain.GetRawTx(thisbal[i].MinedAt, txid); er == nil {
				fz.Write(dat)
			} else {
				println(er.Error())
			}
		}

		fz, _ := zi.Create("balance/unspent.txt")
		for i := range thisbal {
			fmt.Fprintln(fz, thisbal[i].UnspentTextLine())
		}

		if pay_cmd != "" {
			fz, _ = zi.Create(common.CFG.WebUI.PayCommandName)
			fz.Write([]byte(pay_cmd))
		}

		// Non-multisig transaction ...
		fz, _ = zi.Create("tx2sign.txt")
		fz.Write([]byte(hex.EncodeToString(tx.Serialize())))

		zi.Close()
		w.Header()["Content-Type"] = []string{"application/zip"}
		w.Write(buf.Bytes())
		return
	} else {
		err = "Bad request"
	}
error:
	s := load_template("send_error.html")
	write_html_head(w, r)
	s = strings.Replace(s, "<!--ERROR_MSG-->", err, 1)
	w.Write([]byte(s))
	write_html_tail(w)
}
Esempio n. 9
0
// dump raw transaction
func dump_raw_tx() {
	tx := raw_tx_from_file(*dumptxfn)
	if tx == nil {
		fmt.Println("ERROR: Cannot decode the raw transaction")
		return
	}

	var unsigned int

	fmt.Println("ID:", tx.Hash.String())
	fmt.Println("Tx Version:", tx.Version)
	if tx.IsCoinBase() {
		if len(tx.TxIn[0].ScriptSig) >= 4 && tx.TxIn[0].ScriptSig[0] == 3 {
			fmt.Println("Coinbase TX from block height", uint(tx.TxIn[0].ScriptSig[1])|
				uint(tx.TxIn[0].ScriptSig[2])<<8|uint(tx.TxIn[0].ScriptSig[3])<<16)
		} else {
			fmt.Println("Coinbase TX from an unknown block")
		}
		s := hex.EncodeToString(tx.TxIn[0].ScriptSig)
		for len(s) > 0 {
			i := len(s)
			if i > 64 {
				i = 64
			}
			fmt.Println("  ", s[:i])
			s = s[i:]
		}
		//fmt.Println()
	} else {
		fmt.Println("TX IN cnt:", len(tx.TxIn))
		for i := range tx.TxIn {
			fmt.Printf("%4d) %s sl=%d seq=%08x\n", i, tx.TxIn[i].Input.String(),
				len(tx.TxIn[i].ScriptSig), tx.TxIn[i].Sequence)

			if len(tx.TxIn[i].ScriptSig) > 0 {
				if !dump_sigscript(tx.TxIn[i].ScriptSig) {
					unsigned++
				}
			} else {
				unsigned++
			}
		}
	}
	fmt.Println("TX OUT cnt:", len(tx.TxOut))
	for i := range tx.TxOut {
		fmt.Printf("%4d) %20s BTC ", i, btc.UintToBtc(tx.TxOut[i].Value))
		var addr *btc.BtcAddr
		if litecoin {
			addr = ltc.NewAddrFromPkScript(tx.TxOut[i].Pk_script, testnet)
		} else {
			addr = btc.NewAddrFromPkScript(tx.TxOut[i].Pk_script, testnet)
		}
		if addr != nil {
			if addr.Version == AddrVerScript() {
				fmt.Println("to scriptH", addr.String())
			} else {
				fmt.Println("to address", addr.String())
			}
		} else if len(tx.TxOut[i].Pk_script) == 40 && tx.TxOut[i].Pk_script[0] == 0x6a &&
			tx.TxOut[i].Pk_script[1] == 0x26 && tx.TxOut[i].Pk_script[2] == 0x06 {
			fmt.Println("Stealth", hex.EncodeToString(tx.TxOut[i].Pk_script[3:7]),
				hex.EncodeToString(tx.TxOut[i].Pk_script[7:]))
		} else {
			if tx.TxOut[i].Value > 0 {
				fmt.Println("WARNING!!! These coins go to non-standard Pk_script:")
			} else {
				fmt.Println("NULL output to Pk_script:")
			}
			ss, er := btc.ScriptToText(tx.TxOut[i].Pk_script)
			if er == nil {
				for i := range ss {
					fmt.Println("       ", ss[i])
				}
			} else {
				fmt.Println(hex.EncodeToString(tx.TxOut[i].Pk_script))
				fmt.Println(er.Error())
			}
		}
	}
	fmt.Println("Lock Time:", tx.Lock_time)

	if !tx.IsCoinBase() {
		if unsigned > 0 {
			fmt.Println("WARNING:", unsigned, "out of", len(tx.TxIn), "inputs are not signed or signed only patially")
		} else {
			fmt.Println("All", len(tx.TxIn), "transaction inputs seem to be signed")
		}
	}
}
Esempio n. 10
0
func dl_payment(w http.ResponseWriter, r *http.Request) {
	if !ipchecker(r) {
		return
	}

	var err string

	if len(r.Form["outcnt"]) == 1 {
		var thisbal chain.AllUnspentTx
		var pay_cmd string
		var totalinput, spentsofar uint64
		var change_addr *btc.BtcAddr
		var multisig_input []*wallet.MultisigAddr

		addrs_to_msign := make(map[string]bool)

		tx := new(btc.Tx)
		tx.Version = 1
		tx.Lock_time = 0

		outcnt, _ := strconv.ParseUint(r.Form["outcnt"][0], 10, 32)

		wallet.BalanceMutex.Lock()
		for i := 1; i <= int(outcnt); i++ {
			is := fmt.Sprint(i)
			if len(r.Form["txout"+is]) == 1 && r.Form["txout"+is][0] == "on" {
				hash := btc.NewUint256FromString(r.Form["txid"+is][0])
				if hash != nil {
					vout, er := strconv.ParseUint(r.Form["txvout"+is][0], 10, 32)
					if er == nil {
						var po = btc.TxPrevOut{Hash: hash.Hash, Vout: uint32(vout)}
						for j := range wallet.MyBalance {
							if wallet.MyBalance[j].TxPrevOut == po {
								thisbal = append(thisbal, wallet.MyBalance[j])

								// Add the input to our tx
								tin := new(btc.TxIn)
								tin.Input = wallet.MyBalance[j].TxPrevOut
								tin.Sequence = 0xffffffff
								tx.TxIn = append(tx.TxIn, tin)

								// Add new multisig address description
								_, msi := wallet.IsMultisig(wallet.MyBalance[j].BtcAddr)
								multisig_input = append(multisig_input, msi)
								if msi != nil {
									for ai := range msi.ListOfAddres {
										addrs_to_msign[msi.ListOfAddres[ai]] = true
									}
								}

								// Add the value to total input value
								totalinput += wallet.MyBalance[j].Value

								// If no change specified, use the first input addr as it
								if change_addr == nil {
									change_addr = wallet.MyBalance[j].BtcAddr
								}
							}
						}
					}
				}
			}
		}
		wallet.BalanceMutex.Unlock()

		for i := 1; ; i++ {
			adridx := fmt.Sprint("adr", i)
			btcidx := fmt.Sprint("btc", i)

			if len(r.Form[adridx]) != 1 || len(r.Form[btcidx]) != 1 {
				break
			}

			if len(r.Form[adridx][0]) > 1 {
				addr, er := btc.NewAddrFromString(r.Form[adridx][0])
				if er == nil {
					am, er := btc.StringToSatoshis(r.Form[btcidx][0])
					if er == nil && am > 0 {
						if pay_cmd == "" {
							pay_cmd = "wallet -useallinputs -send "
						} else {
							pay_cmd += ","
						}
						pay_cmd += addr.Enc58str + "=" + btc.UintToBtc(am)

						outs, er := btc.NewSpendOutputs(addr, am, common.CFG.Testnet)
						if er != nil {
							err = er.Error()
							goto error
						}
						tx.TxOut = append(tx.TxOut, outs...)

						spentsofar += am
					} else {
						err = "Incorrect amount (" + r.Form[btcidx][0] + ") for Output #" + fmt.Sprint(i)
						goto error
					}
				} else {
					err = "Incorrect address (" + r.Form[adridx][0] + ") for Output #" + fmt.Sprint(i)
					goto error
				}
			}
		}

		if pay_cmd == "" {
			err = "No inputs selected"
			goto error
		}

		am, er := btc.StringToSatoshis(r.Form["txfee"][0])
		if er != nil {
			err = "Incorrect fee value: " + r.Form["txfee"][0]
			goto error
		}

		pay_cmd += " -fee " + r.Form["txfee"][0]
		spentsofar += am

		if len(r.Form["change"][0]) > 1 {
			addr, er := btc.NewAddrFromString(r.Form["change"][0])
			if er != nil {
				err = "Incorrect change address: " + r.Form["change"][0]
				goto error
			}
			change_addr = addr
		}
		pay_cmd += " -change " + change_addr.String()

		if totalinput > spentsofar {
			// Add change output
			outs, er := btc.NewSpendOutputs(change_addr, totalinput-spentsofar, common.CFG.Testnet)
			if er != nil {
				err = er.Error()
				goto error
			}
			tx.TxOut = append(tx.TxOut, outs...)
		}

		buf := new(bytes.Buffer)
		zi := zip.NewWriter(buf)

		was_tx := make(map[[32]byte]bool, len(thisbal))
		for i := range thisbal {
			if was_tx[thisbal[i].TxPrevOut.Hash] {
				continue
			}
			was_tx[thisbal[i].TxPrevOut.Hash] = true
			txid := btc.NewUint256(thisbal[i].TxPrevOut.Hash[:])
			fz, _ := zi.Create("balance/" + txid.String() + ".tx")
			wallet.GetRawTransaction(thisbal[i].MinedAt, txid, fz)
		}

		fz, _ := zi.Create("balance/unspent.txt")
		for i := range thisbal {
			fmt.Fprintln(fz, thisbal[i].UnspentTextLine())
		}

		if len(addrs_to_msign) > 0 {
			// Multisig (or mixed) transaction ...
			for i := range multisig_input {
				if multisig_input[i] == nil {
					continue
				}
				d, er := hex.DecodeString(multisig_input[i].RedeemScript)
				if er != nil {
					println("ERROR parsing hex RedeemScript:", er.Error())
					continue
				}
				ms, er := btc.NewMultiSigFromP2SH(d)
				if er != nil {
					println("ERROR parsing bin RedeemScript:", er.Error())
					continue
				}
				tx.TxIn[i].ScriptSig = ms.Bytes()
			}
			fz, _ = zi.Create("multi_" + common.CFG.PayCommandName)
			fmt.Fprintln(fz, "wallet -raw tx2sign.txt")
			for k, _ := range addrs_to_msign {
				fmt.Fprintln(fz, "wallet -msign", k, "-raw ...")
			}
		} else {
			if pay_cmd != "" {
				fz, _ = zi.Create(common.CFG.PayCommandName)
				fz.Write([]byte(pay_cmd))
			}
		}

		// Non-multisig transaction ...
		fz, _ = zi.Create("tx2sign.txt")
		fz.Write([]byte(hex.EncodeToString(tx.Serialize())))

		zi.Close()
		w.Header()["Content-Type"] = []string{"application/zip"}
		w.Write(buf.Bytes())
		return
	} else {
		err = "Bad request"
	}
error:
	s := load_template("send_error.html")
	write_html_head(w, r)
	s = strings.Replace(s, "<!--ERROR_MSG-->", err, 1)
	w.Write([]byte(s))
	write_html_tail(w)
}
Esempio n. 11
0
func all_addrs(par string) {
	var ptkh_outs, ptkh_vals, ptsh_outs, ptsh_vals uint64
	var best SortedWalletAddrs
	var cnt int = 15

	if par != "" {
		if c, e := strconv.ParseUint(par, 10, 32); e == nil {
			cnt = int(c)
		}
	}

	wallet.BalanceMutex.Lock()
	defer wallet.BalanceMutex.Unlock()

	for k, rec := range wallet.AllBalancesP2SH {
		ptsh_vals += rec.Value
		ptsh_outs += uint64(len(rec.Unsp))
		if sort_by_cnt && len(rec.Unsp) >= 1000 || !sort_by_cnt && rec.Value >= 1000e8 {
			best = append(best, OneWalletAddrs{P2SH: true, Key: k, rec: rec})
		}
	}

	for k, rec := range wallet.AllBalancesP2KH {
		ptkh_vals += rec.Value
		ptkh_outs += uint64(len(rec.Unsp))
		if sort_by_cnt && len(rec.Unsp) >= 1000 || !sort_by_cnt && rec.Value >= 1000e8 {
			best = append(best, OneWalletAddrs{Key: k, rec: rec})
		}
	}

	fmt.Println(btc.UintToBtc(ptkh_vals), "BTC in", ptkh_outs, "unspent recs from", len(wallet.AllBalancesP2KH), "P2KH addresses")
	fmt.Println(btc.UintToBtc(ptsh_vals), "BTC in", ptsh_outs, "unspent recs from", len(wallet.AllBalancesP2SH), "P2SH addresses")

	if sort_by_cnt {
		fmt.Println("Addrs with at least 1000 inps:", len(best))
	} else {
		fmt.Println("Addrs with at least 1000 BTC:", len(best))
	}

	sort.Sort(best)

	var pkscr_p2sk [23]byte
	var pkscr_p2kh [25]byte
	var ad *btc.BtcAddr

	pkscr_p2sk[0] = 0xa9
	pkscr_p2sk[1] = 20
	pkscr_p2sk[22] = 0x87

	pkscr_p2kh[0] = 0x76
	pkscr_p2kh[1] = 0xa9
	pkscr_p2kh[2] = 20
	pkscr_p2kh[23] = 0x88
	pkscr_p2kh[24] = 0xac

	for i := 0; i < len(best) && i < cnt; i++ {
		if best[i].P2SH {
			copy(pkscr_p2sk[2:22], best[i].Key[:])
			ad = btc.NewAddrFromPkScript(pkscr_p2sk[:], common.CFG.Testnet)
		} else {
			copy(pkscr_p2kh[3:23], best[i].Key[:])
			ad = btc.NewAddrFromPkScript(pkscr_p2kh[:], common.CFG.Testnet)
		}
		fmt.Println(i+1, ad.String(), btc.UintToBtc(best[i].rec.Value), "BTC in", len(best[i].rec.Unsp), "inputs")
	}
}