Exemple #1
0
func xml_balance(w http.ResponseWriter, r *http.Request) {
	if !ipchecker(r) {
		return
	}

	w.Header()["Content-Type"] = []string{"text/xml"}
	w.Write([]byte("<unspent>"))

	wallet.LockBal()
	for i := range wallet.MyBalance {
		w.Write([]byte("<output>"))
		fmt.Fprint(w, "<txid>", btc.NewUint256(wallet.MyBalance[i].TxPrevOut.Hash[:]).String(), "</txid>")
		fmt.Fprint(w, "<vout>", wallet.MyBalance[i].TxPrevOut.Vout, "</vout>")
		fmt.Fprint(w, "<value>", wallet.MyBalance[i].Value, "</value>")
		fmt.Fprint(w, "<inblock>", wallet.MyBalance[i].MinedAt, "</inblock>")
		fmt.Fprint(w, "<blocktime>", get_block_time(wallet.MyBalance[i].MinedAt), "</blocktime>")
		fmt.Fprint(w, "<addr>", wallet.MyBalance[i].BtcAddr.String(), "</addr>")
		fmt.Fprint(w, "<wallet>", html.EscapeString(wallet.MyBalance[i].BtcAddr.Extra.Wallet), "</wallet>")
		fmt.Fprint(w, "<label>", html.EscapeString(wallet.MyBalance[i].BtcAddr.Extra.Label), "</label>")
		fmt.Fprint(w, "<virgin>", fmt.Sprint(wallet.MyBalance[i].BtcAddr.Extra.Virgin), "</virgin>")
		w.Write([]byte("</output>"))
	}
	wallet.UnlockBal()
	w.Write([]byte("</unspent>"))
}
Exemple #2
0
func show_balance_stats(p string) {
	println("CachedAddrs count:", len(wallet.CachedAddrs))
	println("CacheUnspentIdx count:", len(wallet.CacheUnspentIdx))
	println("CacheUnspent count:", len(wallet.CacheUnspent))
	if p != "" {
		wallet.LockBal()
		for i := range wallet.CacheUnspent {
			fmt.Printf("%5d) %35s - %d unspent output(s)\n", i, wallet.CacheUnspent[i].BtcAddr.String(),
				len(wallet.CacheUnspent[i].AllUnspentTx))
			/*for j := range wallet.CacheUnspent[i].AllUnspentTx {
				fmt.Printf(" %5d) %s\n", j, wallet.CacheUnspent[i].AllUnspentTx[j].String())
			}*/
		}
		wallet.UnlockBal()
	}
}
Exemple #3
0
func p_snd(w http.ResponseWriter, r *http.Request) {
	if !ipchecker(r) {
		return
	}

	s := load_template("send.html")

	wallet.LockBal()
	if wallet.MyWallet != nil && len(wallet.MyBalance) > 0 {
		wal := load_template("send_wal.html")
		row_tmp := load_template("send_wal_row.html")
		wal = strings.Replace(wal, "{TOTAL_BTC}", fmt.Sprintf("%.8f", float64(wallet.LastBalance)/1e8), 1)
		wal = strings.Replace(wal, "{UNSPENT_OUTS}", fmt.Sprint(len(wallet.MyBalance)), -1)
		for i := range wallet.MyBalance {
			row := row_tmp
			row = strings.Replace(row, "{ADDR_LABEL}", html.EscapeString(wallet.MyBalance[i].BtcAddr.Label), 1)
			row = strings.Replace(row, "{ROW_NUMBER}", fmt.Sprint(i+1), -1)
			row = strings.Replace(row, "{MINED_IN}", fmt.Sprint(wallet.MyBalance[i].MinedAt), 1)
			row = strings.Replace(row, "{TX_ID}", btc.NewUint256(wallet.MyBalance[i].TxPrevOut.Hash[:]).String(), -1)
			row = strings.Replace(row, "{TX_VOUT}", fmt.Sprint(wallet.MyBalance[i].TxPrevOut.Vout), -1)
			row = strings.Replace(row, "{BTC_AMOUNT}", fmt.Sprintf("%.8f", float64(wallet.MyBalance[i].Value)/1e8), 1)
			row = strings.Replace(row, "{OUT_VALUE}", fmt.Sprint(wallet.MyBalance[i].Value), 1)
			row = strings.Replace(row, "{BTC_ADDR}", wallet.MyBalance[i].BtcAddr.String(), 1)
			wal = templ_add(wal, "<!--UTXOROW-->", row)
		}
		for i := range wallet.MyWallet.Addrs {
			op := "<option value=\"" + wallet.MyWallet.Addrs[i].Enc58str +
				"\">" + wallet.MyWallet.Addrs[i].Enc58str + " (" +
				html.EscapeString(wallet.MyWallet.Addrs[i].Label) + ")</option>"
			//wal = strings.Replace(wal, "<!--ONECHANGEADDR-->", op, 1)
			wal = templ_add(wal, "<!--ONECHANGEADDR-->", op)
		}
		s = strings.Replace(s, "<!--WALLET-->", wal, 1)
	} else {
		if wallet.MyWallet == nil {
			s = strings.Replace(s, "<!--WALLET-->", "You have no wallet", 1)
		} else {
			s = strings.Replace(s, "<!--WALLET-->", "Your current wallet is empty", 1)
		}
	}
	wallet.UnlockBal()

	write_html_head(w, r)
	w.Write([]byte(s))
	write_html_tail(w)
}
Exemple #4
0
func p_home(w http.ResponseWriter, r *http.Request) {
	if !ipchecker(r) {
		return
	}

	s := load_template("home.html")

	wallet.LockBal()
	if len(wallet.MyBalance) > 0 {
		wal := load_template("home_wal.html")
		wal = strings.Replace(wal, "{TOTAL_BTC}", fmt.Sprintf("%.8f", float64(wallet.LastBalance)/1e8), 1)
		wal = strings.Replace(wal, "{UNSPENT_OUTS}", fmt.Sprint(len(wallet.MyBalance)), 1)
		s = strings.Replace(s, "<!--WALLET-->", wal, 1)
	} else {
		if wallet.MyWallet == nil {
			s = strings.Replace(s, "<!--WALLET-->", "You have no wallet", 1)
		} else {
			s = strings.Replace(s, "<!--WALLET-->", "Your balance is <b>zero</b>", 1)
		}
	}
	wallet.UnlockBal()

	common.Last.Mutex.Lock()
	s = strings.Replace(s, "{LAST_BLOCK_HASH}", common.Last.Block.BlockHash.String(), 1)
	s = strings.Replace(s, "{LAST_BLOCK_HEIGHT}", fmt.Sprint(common.Last.Block.Height), 1)
	s = strings.Replace(s, "{LAST_BLOCK_TIME}", time.Unix(int64(common.Last.Block.Timestamp), 0).Format("2006/01/02 15:04:05"), 1)
	s = strings.Replace(s, "{LAST_BLOCK_DIFF}", common.NumberToString(btc.GetDifficulty(common.Last.Block.Bits)), 1)
	s = strings.Replace(s, "{LAST_BLOCK_RCVD}", time.Now().Sub(common.Last.Time).String(), 1)
	common.Last.Mutex.Unlock()

	s = strings.Replace(s, "{BLOCKS_CACHED}", fmt.Sprint(len(network.CachedBlocks)), 1)
	s = strings.Replace(s, "{KNOWN_PEERS}", fmt.Sprint(network.PeerDB.Count()), 1)
	s = strings.Replace(s, "{NODE_UPTIME}", time.Now().Sub(common.StartTime).String(), 1)
	s = strings.Replace(s, "{NET_BLOCK_QSIZE}", fmt.Sprint(len(network.NetBlocks)), 1)
	s = strings.Replace(s, "{NET_TX_QSIZE}", fmt.Sprint(len(network.NetTxs)), 1)

	network.Mutex_net.Lock()
	s = strings.Replace(s, "{OPEN_CONNS_TOTAL}", fmt.Sprint(len(network.OpenCons)), 1)
	s = strings.Replace(s, "{OPEN_CONNS_OUT}", fmt.Sprint(network.OutConsActive), 1)
	s = strings.Replace(s, "{OPEN_CONNS_IN}", fmt.Sprint(network.InConsActive), 1)
	network.Mutex_net.Unlock()

	common.LockBw()
	common.TickRecv()
	common.TickSent()
	s = strings.Replace(s, "{DL_SPEED_NOW}", fmt.Sprint(common.DlBytesPrevSec>>10), 1)
	s = strings.Replace(s, "{DL_SPEED_MAX}", fmt.Sprint(common.DownloadLimit>>10), 1)
	s = strings.Replace(s, "{DL_TOTAL}", common.BytesToString(common.DlBytesTotal), 1)
	s = strings.Replace(s, "{UL_SPEED_NOW}", fmt.Sprint(common.UlBytesPrevSec>>10), 1)
	s = strings.Replace(s, "{UL_SPEED_MAX}", fmt.Sprint(common.UploadLimit>>10), 1)
	s = strings.Replace(s, "{UL_TOTAL}", common.BytesToString(common.UlBytesTotal), 1)
	common.UnlockBw()

	network.ExternalIpMutex.Lock()
	for ip, cnt := range network.ExternalIp4 {
		s = strings.Replace(s, "{ONE_EXTERNAL_IP}",
			fmt.Sprintf("%dx%d.%d.%d.%d&nbsp;&nbsp;{ONE_EXTERNAL_IP}", cnt,
				byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip)), 1)
	}
	network.ExternalIpMutex.Unlock()
	s = strings.Replace(s, "{ONE_EXTERNAL_IP}", "", 1)

	var ms runtime.MemStats
	runtime.ReadMemStats(&ms)
	s = strings.Replace(s, "{HEAP_SIZE_MB}", fmt.Sprint(ms.Alloc>>20), 1)
	s = strings.Replace(s, "{SYSMEM_USED_MB}", fmt.Sprint(ms.Sys>>20), 1)
	s = strings.Replace(s, "{ECDSA_VERIFY_COUNT}", fmt.Sprint(btc.EcdsaVerifyCnt), 1)

	common.LockCfg()
	dat, _ := json.Marshal(&common.CFG)
	common.UnlockCfg()
	s = strings.Replace(s, "{CONFIG_FILE}", strings.Replace(string(dat), ",\"", ", \"", -1), 1)

	write_html_head(w, r)
	w.Write([]byte(s))
	write_html_tail(w)
}
Exemple #5
0
func dl_payment(w http.ResponseWriter, r *http.Request) {
	if !ipchecker(r) {
		return
	}

	var err string

	r.ParseForm()
	if len(r.Form["outcnt"]) == 1 {
		var thisbal btc.AllUnspentTx
		var pay_cmd string

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

		wallet.LockBal()
		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])
							}
						}
					}
				}
			}
		}
		wallet.UnlockBal()

		for i := 1; ; i++ {
			is := fmt.Sprint(i)

			if len(r.Form["adr"+is]) != 1 {
				break
			}

			if len(r.Form["btc"+is]) != 1 {
				break
			}

			if len(r.Form["adr"+is][0]) > 1 {
				addr, er := btc.NewAddrFromString(r.Form["adr"+is][0])
				if er == nil {
					am, er := strconv.ParseFloat(r.Form["btc"+is][0], 64)
					if er == nil && am > 0 {
						if pay_cmd == "" {
							pay_cmd = "wallet -useallinputs -send "
						} else {
							pay_cmd += ","
						}
						pay_cmd += addr.Enc58str + "=" + fmt.Sprintf("%.8f", am)
					} else {
						err = "Incorrect amount (" + r.Form["btc"+is][0] + ") for Output #" + is
						goto error
					}
				} else {
					err = "Incorrect address (" + r.Form["adr"+is][0] + ") for Output #" + is
					goto error
				}
			}
		}

		if pay_cmd != "" && len(r.Form["txfee"]) == 1 {
			pay_cmd += " -fee " + r.Form["txfee"][0]
		}

		if pay_cmd != "" && len(r.Form["change"]) == 1 && len(r.Form["change"][0]) > 1 {
			pay_cmd += " -change " + r.Form["change"][0]
		}

		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.Fprintf(fz, "%s # %.8f BTC @ %s, %d confs\n", thisbal[i].TxPrevOut.String(),
				float64(thisbal[i].Value)/1e8, thisbal[i].BtcAddr.StringLab(),
				1+common.Last.Block.Height-thisbal[i].MinedAt)
		}

		if pay_cmd != "" {
			fz, _ = zi.Create("pay_cmd.txt")
			fz.Write([]byte(pay_cmd))
		}

		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)
}
Exemple #6
0
func p_snd(w http.ResponseWriter, r *http.Request) {
	if !ipchecker(r) {
		return
	}

	s := load_template("send.html")

	wallet.LockBal()
	if wallet.MyWallet != nil && len(wallet.MyBalance) > 0 {
		wal := load_template("send_wal.html")
		row_tmp := load_template("send_wal_row.html")
		wal = strings.Replace(wal, "{TOTAL_BTC}", fmt.Sprintf("%.8f", float64(wallet.LastBalance)/1e8), 1)
		wal = strings.Replace(wal, "{UNSPENT_OUTS}", fmt.Sprint(len(wallet.MyBalance)), -1)
		for i := range wallet.MyBalance {
			row := row_tmp
			row = strings.Replace(row, "{WALLET_FILE}", html.EscapeString(wallet.MyBalance[i].BtcAddr.Extra.Wallet), 1)
			lab := wallet.MyBalance[i].BtcAddr.Extra.Label
			if wallet.MyBalance[i].BtcAddr.Extra.Virgin {
				lab += " ***"
			}
			row = strings.Replace(row, "{ADDR_LABEL}", html.EscapeString(lab), 1)
			row = strings.Replace(row, "{ROW_NUMBER}", fmt.Sprint(i+1), -1)
			row = strings.Replace(row, "{MINED_IN}", fmt.Sprint(wallet.MyBalance[i].MinedAt), 1)
			row = strings.Replace(row, "{TX_ID}", btc.NewUint256(wallet.MyBalance[i].TxPrevOut.Hash[:]).String(), -1)
			row = strings.Replace(row, "{TX_VOUT}", fmt.Sprint(wallet.MyBalance[i].TxPrevOut.Vout), -1)
			row = strings.Replace(row, "{BTC_AMOUNT}", fmt.Sprintf("%.8f", float64(wallet.MyBalance[i].Value)/1e8), 1)
			row = strings.Replace(row, "{OUT_VALUE}", fmt.Sprint(wallet.MyBalance[i].Value), 1)
			row = strings.Replace(row, "{BTC_ADDR}", wallet.MyBalance[i].BtcAddr.String(), 1)
			wal = templ_add(wal, "<!--UTXOROW-->", row)
		}

		// Own wallet
		for i := range wallet.MyWallet.Addrs {
			row := "wallet.push({'addr':'" + wallet.MyWallet.Addrs[i].Enc58str + "', " +
				"'label':'" + wallet.MyWallet.Addrs[i].Extra.Label + "', " +
				"'wallet':'" + wallet.MyWallet.Addrs[i].Extra.Wallet + "', " +
				"'virgin':" + fmt.Sprint(wallet.MyWallet.Addrs[i].Extra.Virgin) + "})\n"
			wal = templ_add(wal, "/*WALLET_ENTRY_JS*/", row)
		}

		// Address Book
		book := wallet.LoadWalfile(common.GocoinHomeDir+"wallet/ADDRESS", 0)
		for i := range book {
			row := "addrbook.push({'addr':'" + book[i].Enc58str + "', " +
				"'label':'" + book[i].Extra.Label + "', " +
				"'wallet':'" + book[i].Extra.Wallet + "'})\n"
			wal = templ_add(wal, "/*WALLET_ENTRY_JS*/", row)
		}
		wal = strings.Replace(wal, "/*WALLET_ENTRY_JS*/", "const ADDR_LIST_SIZE = "+fmt.Sprint(common.CFG.WebUI.AddrListLen), 1)

		s = strings.Replace(s, "<!--WALLET-->", wal, 1)
	} else {
		if wallet.MyWallet == nil {
			s = strings.Replace(s, "<!--WALLET-->", "You have no wallet", 1)
		} else {
			s = strings.Replace(s, "<!--WALLET-->", "Your current wallet is empty", 1)
		}
	}
	wallet.UnlockBal()

	write_html_head(w, r)
	w.Write([]byte(s))
	write_html_tail(w)
}
Exemple #7
0
func dl_payment(w http.ResponseWriter, r *http.Request) {
	if !ipchecker(r) {
		return
	}

	var err string

	r.ParseForm()
	if len(r.Form["outcnt"]) == 1 {
		var thisbal btc.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

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

		wallet.LockBal()
		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)

								totalinput += wallet.MyBalance[j].Value

								if change_addr == nil {
									change_addr = wallet.MyBalance[j].BtcAddr
								}
							}
						}
					}
				}
			}
		}
		wallet.UnlockBal()

		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)

						tout := new(btc.TxOut)
						tout.Value = am
						tout.Pk_script = addr.OutScript()
						tx.TxOut = append(tx.TxOut, tout)

						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
			tout := new(btc.TxOut)
			tout.Value = totalinput - spentsofar
			tout.Pk_script = change_addr.OutScript()
			tx.TxOut = append(tx.TxOut, tout)
		}

		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.Fprintf(fz, "%s # %.8f BTC @ %s, %d confs\n", thisbal[i].TxPrevOut.String(),
				float64(thisbal[i].Value)/1e8, thisbal[i].BtcAddr.StringLab(),
				1+common.Last.Block.Height-thisbal[i].MinedAt)
		}

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

		// Raw 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)
}
Exemple #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 btc.AllUnspentTx
		var pay_cmd string
		var totalinput, spentsofar uint64
		var change_addr *btc.BtcAddr
		var multisig_input []*wallet.MultisigAddr
		var invalid_tx bool

		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.LockBal()
		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.UnlockBal()

		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)

						tout := new(btc.TxOut)
						tout.Value = am
						tout.Pk_script = addr.OutScript()
						if tout.Pk_script != nil {
							tx.TxOut = append(tx.TxOut, tout)
						} else {
							invalid_tx = true
						}

						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
			tout := new(btc.TxOut)
			tout.Value = totalinput - spentsofar
			tout.Pk_script = change_addr.OutScript()
			tx.TxOut = append(tx.TxOut, tout)
		}

		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.Fprintf(fz, "%s # %.8f BTC @ %s, %d confs\n", thisbal[i].TxPrevOut.String(),
				float64(thisbal[i].Value)/1e8, thisbal[i].BtcAddr.StringLab(),
				1+common.Last.Block.Height-thisbal[i].MinedAt)
		}

		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("multi2sign.txt")
			fz.Write([]byte(hex.EncodeToString(tx.Serialize())))

			fz, _ = zi.Create("multi_" + common.CFG.PayCommandName)
			for k, _ := range addrs_to_msign {
				fmt.Fprintln(fz, "wallet -msign", k, "-raw ...")
			}
		} else {
			// Non-multisig transaction ...
			if !invalid_tx {
				fz, _ = zi.Create("tx2sign.txt")
				fz.Write([]byte(hex.EncodeToString(tx.Serialize())))
			}

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

		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)
}