Exemplo n.º 1
0
// prepare a signed transaction
func make_signed_tx() {
	// Make an empty transaction
	tx := new(btc.Tx)
	tx.Version = 1
	tx.Lock_time = 0

	// Select as many inputs as we need to pay the full amount (with the fee)
	var btcsofar uint64
	for inpcnt := 0; inpcnt < len(unspentOuts); inpcnt++ {
		uo := UO(unspentOuts[inpcnt])
		// add the input to our transaction:
		tin := new(btc.TxIn)
		tin.Input = *unspentOuts[inpcnt]
		tin.Sequence = 0xffffffff
		tx.TxIn = append(tx.TxIn, tin)

		btcsofar += uo.Value
		if !*useallinputs && (btcsofar >= spendBtc+feeBtc) {
			break
		}
	}
	changeBtc = btcsofar - (spendBtc + feeBtc)
	if *verbose {
		fmt.Printf("Spending %d out of %d outputs...\n", len(tx.TxIn), len(unspentOuts))
	}

	// Build transaction outputs:
	tx.TxOut = make([]*btc.TxOut, len(sendTo))
	for o := range sendTo {
		tx.TxOut[o] = &btc.TxOut{Value: sendTo[o].amount, Pk_script: sendTo[o].addr.OutScript()}
	}

	if changeBtc > 0 {
		// Add one more output (with the change)
		chad := get_change_addr()
		if *verbose {
			fmt.Println("Sending change", changeBtc, "to", chad.String())
		}
		tx.TxOut = append(tx.TxOut, &btc.TxOut{Value: changeBtc, Pk_script: chad.OutScript()})
	}

	if *hashes {
		dump_hashes_to_sign(tx)
	} else {
		sign_tx(tx)
		write_tx_file(tx)

		if *apply2bal {
			apply_to_balance(tx)
		}
	}
}
Exemplo n.º 2
0
func GetTx(txid *btc.Uint256, vout int) bool {
	r, er := http.Get("http://blockexplorer.com/rawtx/" + txid.String())
	if er == nil && r.StatusCode == 200 {
		defer r.Body.Close()
		c, _ := ioutil.ReadAll(r.Body)
		var txx onetx
		er = json.Unmarshal(c[:], &txx)
		if er == nil {
			tx := new(btc.Tx)
			tx.Version = txx.Ver
			tx.TxIn = make([]*btc.TxIn, len(txx.In))
			for i := range txx.In {
				tx.TxIn[i] = new(btc.TxIn)
				tx.TxIn[i].Input.Hash = btc.NewUint256FromString(txx.In[i].Prev_out.Hash).Hash
				tx.TxIn[i].Input.Vout = txx.In[i].Prev_out.N
				tx.TxIn[i].ScriptSig, _ = btc.DecodeScript(txx.In[i].ScriptSig)
				tx.TxIn[i].Sequence = 0xffffffff
			}
			tx.TxOut = make([]*btc.TxOut, len(txx.Out))
			for i := range txx.Out {
				tx.TxOut[i] = new(btc.TxOut)
				tx.TxOut[i].Value = btc.ParseValue(txx.Out[i].Value)
				tx.TxOut[i].Pk_script, _ = btc.DecodeScript(txx.Out[i].ScriptPubKey)
			}
			tx.Lock_time = txx.Lock_time
			rawtx := tx.Serialize()
			curid := btc.NewSha2Hash(rawtx)
			if !curid.Equal(txid) {
				fmt.Println("The downloaded transaction does not match its ID.")
				return false
			}
			ioutil.WriteFile("balance/"+curid.String()+".tx", rawtx, 0666)
			return true
		} else {
			fmt.Println("json.Unmarshal:", er.Error())
		}
	} else {
		if er != nil {
			fmt.Println("http.Get:", er.Error())
		} else {
			fmt.Println("StatusCode=", r.StatusCode)
		}
	}
	return false
}
Exemplo n.º 3
0
// Download (and re-assemble) raw transaction from blockexplorer.com
func GetTxFromExplorer(txid *btc.Uint256) ([]byte, []byte) {
	url := "http://blockexplorer.com/rawtx/" + txid.String()
	r, er := http.Get(url)
	if er == nil && r.StatusCode == 200 {
		defer r.Body.Close()
		c, _ := ioutil.ReadAll(r.Body)
		var txx onetx
		er = json.Unmarshal(c[:], &txx)
		if er == nil {
			// This part looks weird, but this is how I solved seq=FFFFFFFF, if the field not present:
			for i := range txx.In {
				txx.In[i].Sequence = 0xffffffff
			}
			json.Unmarshal(c[:], &txx)
			// ... end of the weird solution

			tx := new(btc.Tx)
			tx.Version = txx.Ver
			tx.TxIn = make([]*btc.TxIn, len(txx.In))
			for i := range txx.In {
				tx.TxIn[i] = new(btc.TxIn)
				tx.TxIn[i].Input.Hash = btc.NewUint256FromString(txx.In[i].Prev_out.Hash).Hash
				tx.TxIn[i].Input.Vout = txx.In[i].Prev_out.N
				if txx.In[i].Prev_out.N == 0xffffffff &&
					txx.In[i].Prev_out.Hash == "0000000000000000000000000000000000000000000000000000000000000000" {
					tx.TxIn[i].ScriptSig, _ = hex.DecodeString(txx.In[i].Coinbase)
				} else {
					tx.TxIn[i].ScriptSig, _ = btc.DecodeScript(txx.In[i].ScriptSig)
				}
				tx.TxIn[i].Sequence = txx.In[i].Sequence
			}
			tx.TxOut = make([]*btc.TxOut, len(txx.Out))
			for i := range txx.Out {
				am, er := btc.StringToSatoshis(txx.Out[i].Value)
				if er != nil {
					fmt.Println("Incorrect BTC amount", txx.Out[i].Value, er.Error())
					return nil, nil
				}
				tx.TxOut[i] = new(btc.TxOut)
				tx.TxOut[i].Value = am
				tx.TxOut[i].Pk_script, _ = btc.DecodeScript(txx.Out[i].ScriptPubKey)
			}
			tx.Lock_time = txx.Lock_time
			rawtx := tx.Serialize()
			if txx.Size != uint(len(rawtx)) {
				fmt.Printf("Transaction size mismatch: %d expexted, %d decoded\n", txx.Size, len(rawtx))
				return nil, rawtx
			}
			curid := btc.NewSha2Hash(rawtx)
			if !curid.Equal(txid) {
				fmt.Println("The downloaded transaction does not match its ID.", txid.String())
				return nil, rawtx
			}
			return rawtx, rawtx
		} else {
			fmt.Println("json.Unmarshal:", er.Error())
		}
	} else {
		if er != nil {
			fmt.Println("http.Get:", er.Error())
		} else {
			fmt.Println("StatusCode=", r.StatusCode)
		}
	}
	return nil, nil
}
Exemplo n.º 4
0
// prepare a signed transaction
func make_signed_tx() {
	// Make an empty transaction
	tx := new(btc.Tx)
	tx.Version = 1
	tx.Lock_time = 0

	// Select as many inputs as we need to pay the full amount (with the fee)
	var btcsofar uint64
	var inpcnt uint
	for inpcnt = 0; inpcnt < uint(len(unspentOuts)); inpcnt++ {
		uo := UO(unspentOuts[inpcnt])
		// add the input to our transaction:
		tin := new(btc.TxIn)
		tin.Input = *unspentOuts[inpcnt]
		tin.Sequence = 0xffffffff
		tx.TxIn = append(tx.TxIn, tin)

		btcsofar += uo.Value
		if btcsofar >= spendBtc+feeBtc {
			break
		}
	}
	changeBtc = btcsofar - (spendBtc + feeBtc)
	fmt.Printf("Spending %d out of %d outputs...\n", inpcnt+1, len(unspentOuts))

	// Build transaction outputs:
	tx.TxOut = make([]*btc.TxOut, len(sendTo))
	for o := range sendTo {
		tx.TxOut[o] = &btc.TxOut{Value: sendTo[o].amount, Pk_script: sendTo[o].addr.OutScript()}
	}

	if changeBtc > 0 {
		// Add one more output (with the change)
		tx.TxOut = append(tx.TxOut, &btc.TxOut{Value: changeBtc, Pk_script: get_change_addr().OutScript()})
	}

	//fmt.Println("Unsigned:", hex.EncodeToString(tx.Serialize()))

	for in := range tx.TxIn {
		uo := UO(unspentOuts[in])
		var found bool
		for j := range publ_addrs {
			if publ_addrs[j].Owns(uo.Pk_script) {
				pub_key, e := btc.NewPublicKey(publ_addrs[j].Pubkey)
				if e != nil {
					println("NewPublicKey:", e.Error(), "\007")
					os.Exit(1)
				}

				// Load the key (private and public)
				var key ecdsa.PrivateKey
				key.D = new(big.Int).SetBytes(priv_keys[j][:])
				key.PublicKey = pub_key.PublicKey

				//Calculate proper transaction hash
				h := tx.SignatureHash(uo.Pk_script, in, btc.SIGHASH_ALL)
				//fmt.Println("SignatureHash:", btc.NewUint256(h).String())

				// Sign
				r, s, err := ecdsa.Sign(rand.Reader, &key, h)
				if err != nil {
					println("Sign:", err.Error(), "\007")
					os.Exit(1)
				}
				rb := r.Bytes()
				sb := s.Bytes()

				if rb[0] >= 0x80 { // I thinnk this is needed, thought I am not quite sure... :P
					rb = append([]byte{0x00}, rb...)
				}

				if sb[0] >= 0x80 { // I thinnk this is needed, thought I am not quite sure... :P
					sb = append([]byte{0x00}, sb...)
				}

				// Output the signing result into a buffer, in format expected by bitcoin protocol
				busig := new(bytes.Buffer)
				busig.WriteByte(0x30)
				busig.WriteByte(byte(4 + len(rb) + len(sb)))
				busig.WriteByte(0x02)
				busig.WriteByte(byte(len(rb)))
				busig.Write(rb)
				busig.WriteByte(0x02)
				busig.WriteByte(byte(len(sb)))
				busig.Write(sb)
				busig.WriteByte(0x01) // hash type

				// Output the signature and the public key into tx.ScriptSig
				buscr := new(bytes.Buffer)
				buscr.WriteByte(byte(busig.Len()))
				buscr.Write(busig.Bytes())

				buscr.WriteByte(byte(len(publ_addrs[j].Pubkey)))
				buscr.Write(publ_addrs[j].Pubkey)

				// assign:
				tx.TxIn[in].ScriptSig = buscr.Bytes()

				found = true
				break
			}
		}
		if !found {
			fmt.Println("You do not have private key for input number", hex.EncodeToString(uo.Pk_script), "\007")
			os.Exit(1)
		}
	}

	rawtx := tx.Serialize()
	tx.Hash = btc.NewSha2Hash(rawtx)

	hs := tx.Hash.String()
	fmt.Println(hs)

	f, _ := os.Create(hs[:8] + ".txt")
	if f != nil {
		f.Write([]byte(hex.EncodeToString(rawtx)))
		f.Close()
		fmt.Println("Transaction data stored in", hs[:8]+".txt")
	}

	f, _ = os.Create("balance/unspent.txt")
	if f != nil {
		for j := uint(0); j < uint(len(unspentOuts)); j++ {
			if j > inpcnt {
				fmt.Fprintln(f, unspentOuts[j], unspentOutsLabel[j])
			}
		}
		fmt.Println(inpcnt, "spent output(s) removed from 'balance/unspent.txt'")

		var addback int
		for out := range tx.TxOut {
			for j := range publ_addrs {
				if publ_addrs[j].Owns(tx.TxOut[out].Pk_script) {
					fmt.Fprintf(f, "%s-%03d # %.8f / %s\n", tx.Hash.String(), out,
						float64(tx.TxOut[out].Value)/1e8, publ_addrs[j].String())
					addback++
				}
			}
		}
		f.Close()
		if addback > 0 {
			f, _ = os.Create("balance/" + hs + ".tx")
			if f != nil {
				f.Write(rawtx)
				f.Close()
			}
			fmt.Println(addback, "new output(s) appended to 'balance/unspent.txt'")
		}
	}
}
Exemplo n.º 5
0
// prepare a signed transaction
func make_signed_tx() {
	// Make an empty transaction
	tx := new(btc.Tx)
	tx.Version = 1
	tx.Lock_time = 0

	// Select as many inputs as we need to pay the full amount (with the fee)
	var btcsofar uint64
	for inpcnt := 0; inpcnt < len(unspentOuts); inpcnt++ {
		uo := UO(unspentOuts[inpcnt])
		// add the input to our transaction:
		tin := new(btc.TxIn)
		tin.Input = *unspentOuts[inpcnt]
		tin.Sequence = 0xffffffff
		tx.TxIn = append(tx.TxIn, tin)

		btcsofar += uo.Value
		if !*useallinputs && (btcsofar >= spendBtc+feeBtc) {
			break
		}
	}
	changeBtc = btcsofar - (spendBtc + feeBtc)
	if *verbose {
		fmt.Printf("Spending %d out of %d outputs...\n", len(tx.TxIn), len(unspentOuts))
	}

	// Build transaction outputs:
	for o := range sendTo {
		if sendTo[o].addr.StealthAddr != nil {
			tx.TxOut = append(tx.TxOut, stealth_txout(sendTo[o].addr.StealthAddr, sendTo[o].amount)...)
		} else {
			tx.TxOut = append(tx.TxOut, &btc.TxOut{Value: sendTo[o].amount,
				Pk_script: sendTo[o].addr.OutScript()})
		}
	}

	if changeBtc > 0 {
		// Add one more output (with the change)
		chad := get_change_addr()
		if *verbose {
			fmt.Println("Sending change", changeBtc, "to", chad.String())
		}
		tx.TxOut = append(tx.TxOut, &btc.TxOut{Value: changeBtc, Pk_script: chad.OutScript()})
	}

	if *message != "" {
		// Add NULL output with an arbitrary message
		scr := new(bytes.Buffer)
		scr.WriteByte(0x6a) // OP_RETURN
		btc.WritePutLen(scr, uint32(len(*message)))
		scr.Write([]byte(*message))
		tx.TxOut = append(tx.TxOut, &btc.TxOut{Value: 0, Pk_script: scr.Bytes()})
	}

	if *hashes {
		dump_hashes_to_sign(tx)
	} else {
		sign_tx(tx)
		write_tx_file(tx)

		if *apply2bal {
			apply_to_balance(tx)
		}
	}
}
Exemplo n.º 6
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)
}
Exemplo n.º 7
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)
}