func DecodeTx(tx *btc.Tx) (s string, missinginp bool, totinp, totout uint64, e error) { s += fmt.Sprintln("Transaction details (for your information):") s += fmt.Sprintln(len(tx.TxIn), "Input(s):") for i := range tx.TxIn { s += fmt.Sprintf(" %3d %s", i, tx.TxIn[i].Input.String()) var po *btc.TxOut inpid := btc.NewUint256(tx.TxIn[i].Input.Hash[:]) if txinmem, ok := network.TransactionsToSend[inpid.BIdx()]; ok { s += fmt.Sprint(" mempool") if int(tx.TxIn[i].Input.Vout) >= len(txinmem.TxOut) { s += fmt.Sprintf(" - Vout TOO BIG (%d/%d)!", int(tx.TxIn[i].Input.Vout), len(txinmem.TxOut)) } else { po = txinmem.TxOut[tx.TxIn[i].Input.Vout] } } else { po, _ = common.BlockChain.Unspent.UnspentGet(&tx.TxIn[i].Input) if po != nil { s += fmt.Sprintf("%8d", po.BlockHeight) } } if po != nil { ok := btc.VerifyTxScript(tx.TxIn[i].ScriptSig, po.Pk_script, i, tx, true) if !ok { s += fmt.Sprintln("\nERROR: The transacion does not have a valid signature.") e = errors.New("Invalid signature") return } totinp += po.Value s += fmt.Sprintf(" %15.8f BTC @ %s\n", float64(po.Value)/1e8, btc.NewAddrFromPkScript(po.Pk_script, common.Testnet).String()) } else { s += fmt.Sprintln(" - UNKNOWN INPUT") missinginp = true } } s += fmt.Sprintln(len(tx.TxOut), "Output(s):") for i := range tx.TxOut { totout += tx.TxOut[i].Value adr := btc.NewAddrFromPkScript(tx.TxOut[i].Pk_script, common.Testnet) if adr != nil { s += fmt.Sprintf(" %15.8f BTC to adr %s\n", float64(tx.TxOut[i].Value)/1e8, adr.String()) } else { s += fmt.Sprintf(" %15.8f BTC to scr %s\n", float64(tx.TxOut[i].Value)/1e8, hex.EncodeToString(tx.TxOut[i].Pk_script)) } } if missinginp { s += fmt.Sprintln("WARNING: There are missing inputs and we cannot calc input BTC amount.") s += fmt.Sprintln("If there is somethign wrong with this transaction, you can loose money...") } else { s += fmt.Sprintf("All OK: %.8f BTC in -> %.8f BTC out, with %.8f BTC fee\n", float64(totinp)/1e8, float64(totout)/1e8, float64(totinp-totout)/1e8) } return }
func output_tx_xml(w http.ResponseWriter, id string) { txid := btc.NewUint256FromString(id) w.Write([]byte("<tx>")) fmt.Fprint(w, "<id>", id, "</id>") if t2s, ok := network.TransactionsToSend[txid.BIdx()]; ok { w.Write([]byte("<status>OK</status>")) tx := t2s.Tx w.Write([]byte("<inputs>")) for i := range tx.TxIn { w.Write([]byte("<input>")) var po *btc.TxOut inpid := btc.NewUint256(tx.TxIn[i].Input.Hash[:]) if txinmem, ok := network.TransactionsToSend[inpid.BIdx()]; ok { if int(tx.TxIn[i].Input.Vout) < len(txinmem.TxOut) { po = txinmem.TxOut[tx.TxIn[i].Input.Vout] } } else { po, _ = common.BlockChain.Unspent.UnspentGet(&tx.TxIn[i].Input) } if po != nil { ok := btc.VerifyTxScript(tx.TxIn[i].ScriptSig, po.Pk_script, i, tx, true) if !ok { w.Write([]byte("<status>Script FAILED</status>")) } else { w.Write([]byte("<status>OK</status>")) } fmt.Fprint(w, "<value>", po.Value, "</value>") fmt.Fprint(w, "<addr>", btc.NewAddrFromPkScript(po.Pk_script, common.Testnet).String(), "</addr>") fmt.Fprint(w, "<block>", po.BlockHeight, "</block>") } else { w.Write([]byte("<status>UNKNOWN INPUT</status>")) } w.Write([]byte("</input>")) } w.Write([]byte("</inputs>")) w.Write([]byte("<outputs>")) for i := range tx.TxOut { w.Write([]byte("<output>")) fmt.Fprint(w, "<value>", tx.TxOut[i].Value, "</value>") adr := btc.NewAddrFromPkScript(tx.TxOut[i].Pk_script, common.Testnet) if adr != nil { fmt.Fprint(w, "<addr>", adr.String(), "</addr>") } else { fmt.Fprint(w, "<addr>", "scr:"+hex.EncodeToString(tx.TxOut[i].Pk_script), "</addr>") } w.Write([]byte("</output>")) } w.Write([]byte("</outputs>")) } else { w.Write([]byte("<status>Not found</status>")) } w.Write([]byte("</tx>")) }
// Must be called from the chain's thread func HandleNetTx(ntx *TxRcvd, retry bool) (accepted bool) { common.CountSafe("HandleNetTx") tx := ntx.tx var totinp, totout uint64 var frommem bool TxMutex.Lock() if !retry { if _, present := TransactionsPending[tx.Hash.Hash]; !present { // It had to be mined in the meantime, so just drop it now TxMutex.Unlock() common.CountSafe("TxNotPending") return } delete(TransactionsPending, ntx.tx.Hash.Hash) } else { // In case case of retry, it is on the rejected list, // ... so remove it now to free any tied WaitingForInputs deleteRejected(tx.Hash.BIdx()) } pos := make([]*btc.TxOut, len(tx.TxIn)) spent := make([]uint64, len(tx.TxIn)) // Check if all the inputs exist in the chain for i := range tx.TxIn { spent[i] = VoutIdx(&tx.TxIn[i].Input) if _, ok := SpentOutputs[spent[i]]; ok { TransactionsRejected[tx.Hash.BIdx()] = NewRejectedTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_DOUBLE_SPEND) TxMutex.Unlock() common.CountSafe("TxRejectedDoubleSpend") return } if txinmem, ok := TransactionsToSend[tx.TxIn[i].Input.Hash]; common.CFG.TXPool.AllowMemInputs && ok { if int(tx.TxIn[i].Input.Vout) >= len(txinmem.TxOut) { TransactionsRejected[tx.Hash.BIdx()] = NewRejectedTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_BAD_INPUT) TxMutex.Unlock() common.CountSafe("TxRejectedBadInput") return } pos[i] = txinmem.TxOut[tx.TxIn[i].Input.Vout] common.CountSafe("TxInputInMemory") frommem = true } else { pos[i], _ = common.BlockChain.Unspent.UnspentGet(&tx.TxIn[i].Input) if pos[i] == nil { if !common.CFG.TXPool.AllowMemInputs { TransactionsRejected[tx.Hash.BIdx()] = NewRejectedTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_NOT_MINED) TxMutex.Unlock() common.CountSafe("TxRejectedMemInput") return } // In this case, let's "save" it for later... missingid := btc.NewUint256(tx.TxIn[i].Input.Hash[:]) nrtx := NewRejectedTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_NO_TXOU) nrtx.Wait4Input = &Wait4Input{missingTx: missingid, TxRcvd: ntx} TransactionsRejected[tx.Hash.BIdx()] = nrtx // Add to waiting list: var rec *OneWaitingList var newone bool if rec, _ = WaitingForInputs[nrtx.Wait4Input.missingTx.BIdx()]; rec == nil { rec = new(OneWaitingList) rec.TxID = nrtx.Wait4Input.missingTx rec.Ids = make(map[[btc.Uint256IdxLen]byte]time.Time) newone = true } rec.Ids[tx.Hash.BIdx()] = time.Now() WaitingForInputs[nrtx.Wait4Input.missingTx.BIdx()] = rec TxMutex.Unlock() if newone { common.CountSafe("TxRejectedNoInputNew") } else { common.CountSafe("TxRejectedNoInputOld") } return } } totinp += pos[i].Value } // Check if total output value does not exceed total input minout := uint64(btc.MAX_MONEY) for i := range tx.TxOut { if tx.TxOut[i].Value < atomic.LoadUint64(&common.CFG.TXPool.MinVoutValue) { TransactionsRejected[tx.Hash.BIdx()] = NewRejectedTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_DUST) TxMutex.Unlock() common.CountSafe("TxRejectedDust") return } if tx.TxOut[i].Value < minout { minout = tx.TxOut[i].Value } totout += tx.TxOut[i].Value } if totout > totinp { TransactionsRejected[tx.Hash.BIdx()] = NewRejectedTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_OVERSPEND) TxMutex.Unlock() ntx.conn.DoS() common.CountSafe("TxRejectedOverspend") return } // Check for a proper fee fee := totinp - totout if fee < (uint64(len(ntx.raw)) * atomic.LoadUint64(&common.CFG.TXPool.FeePerByte)) { TransactionsRejected[tx.Hash.BIdx()] = NewRejectedTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_LOW_FEE) TxMutex.Unlock() common.CountSafe("TxRejectedLowFee") return } // Verify scripts for i := range tx.TxIn { if !btc.VerifyTxScript(tx.TxIn[i].ScriptSig, pos[i].Pk_script, i, tx, true) { TransactionsRejected[tx.Hash.BIdx()] = NewRejectedTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_SCRIPT_FAIL) TxMutex.Unlock() common.CountSafe("TxRejectedScriptFail") ntx.conn.DoS() return } } rec := &OneTxToSend{Data: ntx.raw, Spent: spent, Volume: totinp, Fee: fee, Firstseen: time.Now(), Tx: tx, Minout: minout} TransactionsToSend[tx.Hash.Hash] = rec for i := range spent { SpentOutputs[spent[i]] = true } wtg := WaitingForInputs[tx.Hash.BIdx()] if wtg != nil { defer RetryWaitingForInput(wtg) // Redo waiting txs when leaving this function } TxMutex.Unlock() common.CountSafe("TxAccepted") if frommem { // Gocoin does not route txs that need unconfirmed inputs rec.Blocked = TX_REJECTED_NOT_MINED common.CountSafe("TxRouteNotMined") } else if isRoutable(rec) { rec.Invsentcnt += NetRouteInv(1, tx.Hash, ntx.conn) common.CountSafe("TxRouteOK") } accepted = true return }