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>")) w.Write([]byte(fmt.Sprint("<len>", len(t2s.Data), "</len>"))) 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 := script.VerifyTxScript(tx.TxIn[i].ScriptSig, po.Pk_script, i, tx, script.VER_P2SH|script.VER_DERSIG) if !ok { w.Write([]byte("<status>Script FAILED</status>")) } else { w.Write([]byte("<status>OK</status>")) } fmt.Fprint(w, "<value>", po.Value, "</value>") ads := "???" if ad := btc.NewAddrFromPkScript(po.Pk_script, common.Testnet); ad != nil { ads = ad.String() } fmt.Fprint(w, "<addr>", ads, "</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>")) }
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 := script.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 ads := "???" if ad := btc.NewAddrFromPkScript(po.Pk_script, common.Testnet); ad != nil { ads = ad.String() } s += fmt.Sprintf(" %15.8f BTC @ %s\n", float64(po.Value)/1e8, ads) } 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 main() { load_dll() pkscript, _ := hex.DecodeString("76a9147d22f6c9cca35cb4071971fe442da58546aaeb5988ac") d, _ := hex.DecodeString("0100000002232e0afdd9bcad5e3ace8a19ab8ad0ed8cebd6213b098e36cdc8b25af1d5cd30010000006b483045022077768255f192427bd2841555cfc86fdb7332e18c5c530b3b6028cd034a339f9c022100b3876037f63559ca8a2766a86c8dc62d41c869abc539ab983ce8eccf448f117f012102a33ac1e78cd3ff49bde292da2efcf273509d0869fe81571dfb49528c8287a8fcffffffff2fc90cf473e6ce6177818f705f6e96c7ad42f921f23b660ea27f653346e6a8a9010000006a47304402206d5be8061f712fba560b9966e037f7c53cff377b0c15d8c62bd0a2bcb195048602200522601341cdf574e3a39ba0397d8fe5608e37fd46b3fda2684386207ca9bf69012102a33ac1e78cd3ff49bde292da2efcf273509d0869fe81571dfb49528c8287a8fcffffffff0200a86100000000001976a914ff8e92b694527dd77660f873eb3a86eda5ed459f88ac70110100000000001976a9147d22f6c9cca35cb4071971fe442da58546aaeb5988ac00000000") tx, _ := btc.NewTx(d) i := 0 flags := uint32(script.STANDARD_VERIFY_FLAGS) println(flags) res := script.VerifyTxScript(pkscript, i, tx, flags) println("Gocoin:", res) if use_consensus_lib { res = consensus_verify_script(pkscript, i, tx, flags) println("Consen:", res) } }
// This isusually the most time consuming process when applying a new block func (ch *Chain) commitTxs(bl *btc.Block, changes *BlockChanges) (e error) { sumblockin := btc.GetBlockReward(changes.Height) var txoutsum, txinsum, sumblockout uint64 // Add each tx outs from the current block to the temporary pool blUnsp := make(map[[32]byte][]*btc.TxOut, len(bl.Txs)) for i := range bl.Txs { outs := make([]*btc.TxOut, len(bl.Txs[i].TxOut)) for j := range bl.Txs[i].TxOut { bl.Txs[i].TxOut[j].BlockHeight = changes.Height outs[j] = bl.Txs[i].TxOut[j] } blUnsp[bl.Txs[i].Hash.Hash] = outs } // create a channnel to receive results from VerifyScript threads: done := make(chan bool, sys.UseThreads) for i := range bl.Txs { txoutsum, txinsum = 0, 0 // Check each tx for a valid input, except from the first one if i > 0 { tx_trusted := bl.Trusted if !tx_trusted && TrustedTxChecker != nil && TrustedTxChecker(bl.Txs[i].Hash) { tx_trusted = true } scripts_ok := true for j := 0; j < sys.UseThreads; j++ { done <- true } for j := 0; j < len(bl.Txs[i].TxIn); /*&& e==nil*/ j++ { inp := &bl.Txs[i].TxIn[j].Input if _, ok := changes.DeledTxs[*inp]; ok { println("txin", inp.String(), "already spent in this block") e = errors.New("Input spent more then once in same block") break } tout := ch.PickUnspent(inp) if tout == nil { t, ok := blUnsp[inp.Hash] if !ok { e = errors.New("Unknown input TxID: " + btc.NewUint256(inp.Hash[:]).String()) break } if inp.Vout >= uint32(len(t)) { println("Vout too big", len(t), inp.String()) e = errors.New("Vout too big") break } if t[inp.Vout] == nil { println("Vout already spent", inp.String()) e = errors.New("Vout already spent") break } tout = t[inp.Vout] t[inp.Vout] = nil // and now mark it as spent: } if !(<-done) { println("VerifyScript error 1") scripts_ok = false break } if tx_trusted { done <- true } else { go func(sig []byte, prv []byte, i int, tx *btc.Tx) { done <- script.VerifyTxScript(sig, prv, i, tx, bl.BlockTime() >= BIP16SwitchTime) }(bl.Txs[i].TxIn[j].ScriptSig, tout.Pk_script, j, bl.Txs[i]) } // Verify Transaction script: txinsum += tout.Value changes.DeledTxs[*inp] = tout } if scripts_ok { scripts_ok = <-done } for j := 1; j < sys.UseThreads; j++ { if !(<-done) { println("VerifyScript error 2") scripts_ok = false } } if len(done) != 0 { panic("ASSERT: The channel should be empty gere") } if !scripts_ok { return errors.New("VerifyScripts failed") } } else { // For coinbase tx we need to check (like satoshi) whether the script size is between 2 and 100 bytes // (Previously we made sure in CheckBlock() that this was a coinbase type tx) if len(bl.Txs[0].TxIn[0].ScriptSig) < 2 || len(bl.Txs[0].TxIn[0].ScriptSig) > 100 { return errors.New(fmt.Sprint("Coinbase script has a wrong length", len(bl.Txs[0].TxIn[0].ScriptSig))) } } sumblockin += txinsum for j := range bl.Txs[i].TxOut { txoutsum += bl.Txs[i].TxOut[j].Value txa := new(btc.TxPrevOut) copy(txa.Hash[:], bl.Txs[i].Hash.Hash[:]) txa.Vout = uint32(j) _, spent := changes.DeledTxs[*txa] if spent { delete(changes.DeledTxs, *txa) } else { changes.AddedTxs[*txa] = bl.Txs[i].TxOut[j] } } sumblockout += txoutsum if e != nil { return // If any input fails, do not continue } if i > 0 && txoutsum > txinsum { return errors.New(fmt.Sprintf("More spent (%.8f) than at the input (%.8f) in TX %s", float64(txoutsum)/1e8, float64(txinsum)/1e8, bl.Txs[i].Hash.String())) } } if sumblockin < sumblockout { return errors.New(fmt.Sprintf("Out:%d > In:%d", sumblockout, sumblockin)) } return nil }
// This isusually the most time consuming process when applying a new block func (ch *Chain) commitTxs(bl *btc.Block, changes *BlockChanges) (e error) { sumblockin := btc.GetBlockReward(changes.Height) var txoutsum, txinsum, sumblockout uint64 if int(changes.Height)+UnwindBufferMaxHistory >= int(changes.LastKnownHeight) { changes.UndoData = make(map[[32]byte]*QdbRec) } // Add each tx outs from the current block to the temporary pool blUnsp := make(map[[32]byte][]*btc.TxOut, 4*len(bl.Txs)) for i := range bl.Txs { outs := make([]*btc.TxOut, len(bl.Txs[i].TxOut)) copy(outs, bl.Txs[i].TxOut) blUnsp[bl.Txs[i].Hash.Hash] = outs } // create a channnel to receive results from VerifyScript threads: done := make(chan bool, sys.UseThreads) for i := range bl.Txs { txoutsum, txinsum = 0, 0 // Check each tx for a valid input, except from the first one if i > 0 { tx_trusted := bl.Trusted if !tx_trusted && TrustedTxChecker != nil && TrustedTxChecker(bl.Txs[i].Hash) { tx_trusted = true } scripts_ok := true for j := 0; j < sys.UseThreads; j++ { done <- true } for j := 0; j < len(bl.Txs[i].TxIn); /*&& e==nil*/ j++ { inp := &bl.Txs[i].TxIn[j].Input spendrec, waspent := changes.DeledTxs[inp.Hash] if waspent && spendrec[inp.Vout] { println("txin", inp.String(), "already spent in this block") e = errors.New("Input spent more then once in same block") break } tout := ch.PickUnspent(inp) if tout == nil { t, ok := blUnsp[inp.Hash] if !ok { e = errors.New("Unknown input TxID: " + btc.NewUint256(inp.Hash[:]).String()) break } if inp.Vout >= uint32(len(t)) { println("Vout too big", len(t), inp.String()) e = errors.New("Vout too big") break } if t[inp.Vout] == nil { println("Vout already spent", inp.String()) e = errors.New("Vout already spent") break } if t[inp.Vout].WasCoinbase { e = errors.New("Cannot spend block's own coinbase in TxID: " + btc.NewUint256(inp.Hash[:]).String()) break } tout = t[inp.Vout] t[inp.Vout] = nil // and now mark it as spent: } else { if tout.WasCoinbase && changes.Height-tout.BlockHeight < COINBASE_MATURITY { e = errors.New("Trying to spend prematured coinbase: " + btc.NewUint256(inp.Hash[:]).String()) break } // it is confirmed already so delete it later if !waspent { spendrec = make([]bool, tout.VoutCount) changes.DeledTxs[inp.Hash] = spendrec } spendrec[inp.Vout] = true if changes.UndoData != nil { var urec *QdbRec urec = changes.UndoData[inp.Hash] if urec == nil { urec = new(QdbRec) urec.TxID = inp.Hash urec.Coinbase = tout.WasCoinbase urec.InBlock = tout.BlockHeight urec.Outs = make([]*QdbTxOut, tout.VoutCount) changes.UndoData[inp.Hash] = urec } tmp := new(QdbTxOut) tmp.Value = tout.Value tmp.PKScr = make([]byte, len(tout.Pk_script)) copy(tmp.PKScr, tout.Pk_script) urec.Outs[inp.Vout] = tmp } } if !(<-done) { println("VerifyScript error 1") scripts_ok = false break } if tx_trusted { done <- true } else { go func(sig []byte, prv []byte, i int, tx *btc.Tx) { done <- script.VerifyTxScript(sig, prv, i, tx, bl.VerifyFlags) }(bl.Txs[i].TxIn[j].ScriptSig, tout.Pk_script, j, bl.Txs[i]) } txinsum += tout.Value } if scripts_ok { scripts_ok = <-done } for j := 1; j < sys.UseThreads; j++ { if !(<-done) { println("VerifyScript error 2") scripts_ok = false } } if len(done) != 0 { panic("ASSERT: The channel should be empty gere") } if !scripts_ok { return errors.New("VerifyScripts failed") } } else { // For coinbase tx we need to check (like satoshi) whether the script size is between 2 and 100 bytes // (Previously we made sure in CheckBlock() that this was a coinbase type tx) if len(bl.Txs[0].TxIn[0].ScriptSig) < 2 || len(bl.Txs[0].TxIn[0].ScriptSig) > 100 { return errors.New(fmt.Sprint("Coinbase script has a wrong length", len(bl.Txs[0].TxIn[0].ScriptSig))) } } sumblockin += txinsum for j := range bl.Txs[i].TxOut { txoutsum += bl.Txs[i].TxOut[j].Value } sumblockout += txoutsum if e != nil { return // If any input fails, do not continue } if i > 0 && txoutsum > txinsum { return errors.New(fmt.Sprintf("More spent (%.8f) than at the input (%.8f) in TX %s", float64(txoutsum)/1e8, float64(txinsum)/1e8, bl.Txs[i].Hash.String())) } } if sumblockin < sumblockout { return errors.New(fmt.Sprintf("Out:%d > In:%d", sumblockout, sumblockin)) } var rec *QdbRec for k, v := range blUnsp { for i := range v { if v[i] != nil { if rec == nil { rec = new(QdbRec) rec.TxID = k rec.Coinbase = v[i].WasCoinbase rec.InBlock = changes.Height rec.Outs = make([]*QdbTxOut, len(v)) } rec.Outs[i] = &QdbTxOut{Value: v[i].Value, PKScr: v[i].Pk_script} } } if rec != nil { changes.AddList = append(changes.AddList, rec) rec = nil } } return nil }
// This isusually the most time consuming process when applying a new block func (ch *Chain) commitTxs(bl *btc.Block, changes *BlockChanges) (e error) { sumblockin := btc.GetBlockReward(changes.Height) var txoutsum, txinsum, sumblockout uint64 if changes.Height+ch.Unspent.UnwindBufLen >= changes.LastKnownHeight { changes.UndoData = make(map[[32]byte]*QdbRec) } blUnsp := make(map[[32]byte][]*btc.TxOut, 4*len(bl.Txs)) var wg sync.WaitGroup var ver_err_cnt uint32 for i := range bl.Txs { txoutsum, txinsum = 0, 0 bl.SigopsCost += uint32(btc.WITNESS_SCALE_FACTOR * bl.Txs[i].GetLegacySigOpCount()) // Check each tx for a valid input, except from the first one if i > 0 { tx_trusted := bl.Trusted if !tx_trusted && TrustedTxChecker != nil && TrustedTxChecker(bl.Txs[i].Hash) { tx_trusted = true } for j := 0; j < len(bl.Txs[i].TxIn); j++ { inp := &bl.Txs[i].TxIn[j].Input spendrec, waspent := changes.DeledTxs[inp.Hash] if waspent && spendrec[inp.Vout] { println("txin", inp.String(), "already spent in this block") e = errors.New("Input spent more then once in same block") return } tout := ch.PickUnspent(inp) if tout == nil { t, ok := blUnsp[inp.Hash] if !ok { e = errors.New("Unknown input TxID: " + btc.NewUint256(inp.Hash[:]).String()) return } if inp.Vout >= uint32(len(t)) { println("Vout too big", len(t), inp.String()) e = errors.New("Vout too big") return } if t[inp.Vout] == nil { println("Vout already spent", inp.String()) e = errors.New("Vout already spent") return } if t[inp.Vout].WasCoinbase { e = errors.New("Cannot spend block's own coinbase in TxID: " + btc.NewUint256(inp.Hash[:]).String()) return } tout = t[inp.Vout] t[inp.Vout] = nil // and now mark it as spent: } else { if tout.WasCoinbase && changes.Height-tout.BlockHeight < COINBASE_MATURITY { e = errors.New("Trying to spend prematured coinbase: " + btc.NewUint256(inp.Hash[:]).String()) return } // it is confirmed already so delete it later if !waspent { spendrec = make([]bool, tout.VoutCount) changes.DeledTxs[inp.Hash] = spendrec } spendrec[inp.Vout] = true if changes.UndoData != nil { var urec *QdbRec urec = changes.UndoData[inp.Hash] if urec == nil { urec = new(QdbRec) urec.TxID = inp.Hash urec.Coinbase = tout.WasCoinbase urec.InBlock = tout.BlockHeight urec.Outs = make([]*QdbTxOut, tout.VoutCount) changes.UndoData[inp.Hash] = urec } tmp := new(QdbTxOut) tmp.Value = tout.Value tmp.PKScr = make([]byte, len(tout.Pk_script)) copy(tmp.PKScr, tout.Pk_script) urec.Outs[inp.Vout] = tmp } } if !tx_trusted { // run VerifyTxScript() in a parallel task wg.Add(1) go func(prv []byte, amount uint64, i int, tx *btc.Tx) { if !script.VerifyTxScript(prv, amount, i, tx, bl.VerifyFlags) { atomic.AddUint32(&ver_err_cnt, 1) } wg.Done() }(tout.Pk_script, tout.Value, j, bl.Txs[i]) } if btc.IsP2SH(tout.Pk_script) { bl.SigopsCost += uint32(btc.WITNESS_SCALE_FACTOR * btc.GetP2SHSigOpCount(bl.Txs[i].TxIn[j].ScriptSig)) } bl.SigopsCost += uint32(bl.Txs[i].CountWitnessSigOps(j, tout.Pk_script)) txinsum += tout.Value } if !tx_trusted { wg.Wait() if ver_err_cnt > 0 { println("VerifyScript failed", ver_err_cnt, "time(s)") return errors.New(fmt.Sprint("VerifyScripts failed ", ver_err_cnt, "time(s)")) } } } else { // For coinbase tx we need to check (like satoshi) whether the script size is between 2 and 100 bytes // (Previously we made sure in CheckBlock() that this was a coinbase type tx) if len(bl.Txs[0].TxIn[0].ScriptSig) < 2 || len(bl.Txs[0].TxIn[0].ScriptSig) > 100 { return errors.New(fmt.Sprint("Coinbase script has a wrong length ", len(bl.Txs[0].TxIn[0].ScriptSig))) } } sumblockin += txinsum for j := range bl.Txs[i].TxOut { txoutsum += bl.Txs[i].TxOut[j].Value } sumblockout += txoutsum if e != nil { return // If any input fails, do not continue } if i > 0 { bl.Txs[i].Fee = txinsum - txoutsum if txoutsum > txinsum { return errors.New(fmt.Sprintf("More spent (%.8f) than at the input (%.8f) in TX %s", float64(txoutsum)/1e8, float64(txinsum)/1e8, bl.Txs[i].Hash.String())) } } // Add each tx outs from the currently executed TX to the temporary pool outs := make([]*btc.TxOut, len(bl.Txs[i].TxOut)) copy(outs, bl.Txs[i].TxOut) blUnsp[bl.Txs[i].Hash.Hash] = outs } if sumblockin < sumblockout { return errors.New(fmt.Sprintf("Out:%d > In:%d", sumblockout, sumblockin)) } if bl.SigopsCost > btc.MAX_BLOCK_SIGOPS_COST { return errors.New("commitTxs(): too many sigops - RPC_Result:bad-blk-sigops") } var rec *QdbRec for k, v := range blUnsp { for i := range v { if v[i] != nil { if rec == nil { rec = new(QdbRec) rec.TxID = k rec.Coinbase = v[i].WasCoinbase rec.InBlock = changes.Height rec.Outs = make([]*QdbTxOut, len(v)) } rec.Outs[i] = &QdbTxOut{Value: v[i].Value, PKScr: v[i].Pk_script} } } if rec != nil { changes.AddList = append(changes.AddList, rec) rec = nil } } return nil }
// 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.BIdx()]; !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.BIdx()) } 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] = tx.TxIn[i].Input.UIdx() if _, ok := SpentOutputs[spent[i]]; ok { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_DOUBLE_SPEND) TxMutex.Unlock() common.CountSafe("TxRejectedDoubleSpnd") return } inptx := btc.NewUint256(tx.TxIn[i].Input.Hash[:]) if txinmem, ok := TransactionsToSend[inptx.BIdx()]; common.CFG.TXPool.AllowMemInputs && ok { if int(tx.TxIn[i].Input.Vout) >= len(txinmem.TxOut) { RejectTx(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 { var newone bool if !common.CFG.TXPool.AllowMemInputs { RejectTx(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 := RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_NO_TXOU) if nrtx != nil { nrtx.Wait4Input = &Wait4Input{missingTx: missingid, TxRcvd: ntx} // Add to waiting list: var rec *OneWaitingList 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("TxRejectedNoInpNew") } else { common.CountSafe("TxRejectedNoInpOld") } 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) { RejectTx(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 { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_OVERSPEND) TxMutex.Unlock() ntx.conn.DoS("TxOverspend") return } // Check for a proper fee fee := totinp - totout if fee < (uint64(len(ntx.raw)) * atomic.LoadUint64(&common.CFG.TXPool.FeePerByte)) { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_LOW_FEE) TxMutex.Unlock() common.CountSafe("TxRejectedLowFee") return } // Verify scripts for i := range tx.TxIn { if !script.VerifyTxScript(tx.TxIn[i].ScriptSig, pos[i].Pk_script, i, tx, true) { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_SCRIPT_FAIL) TxMutex.Unlock() ntx.conn.DoS("TxScriptFail") return } } rec := &OneTxToSend{Data: ntx.raw, Spent: spent, Volume: totinp, Fee: fee, Firstseen: time.Now(), Tx: tx, Minout: minout} TransactionsToSend[tx.Hash.BIdx()] = rec for i := range spent { SpentOutputs[spent[i]] = tx.Hash.BIdx() } 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 }
func output_tx_xml(w http.ResponseWriter, tx *btc.Tx) { w.Write([]byte("<input_list>")) for i := range tx.TxIn { w.Write([]byte("<input>")) w.Write([]byte("<script_sig>")) w.Write([]byte(hex.EncodeToString(tx.TxIn[i].ScriptSig))) w.Write([]byte("</script_sig>")) 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 := script.VerifyTxScript(po.Pk_script, po.Value, i, tx, script.STANDARD_VERIFY_FLAGS) 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, "<pkscript>", hex.EncodeToString(po.Pk_script), "</pkscript>") if ad := btc.NewAddrFromPkScript(po.Pk_script, common.Testnet); ad != nil { fmt.Fprint(w, "<addr>", ad.String(), "</addr>") } fmt.Fprint(w, "<block>", po.BlockHeight, "</block>") if btc.IsP2SH(po.Pk_script) { fmt.Fprint(w, "<input_sigops>", btc.WITNESS_SCALE_FACTOR*btc.GetP2SHSigOpCount(tx.TxIn[i].ScriptSig), "</input_sigops>") } fmt.Fprint(w, "<witness_sigops>", tx.CountWitnessSigOps(i, po.Pk_script), "</witness_sigops>") } else { w.Write([]byte("<status>UNKNOWN INPUT</status>")) } fmt.Fprint(w, "<sequence>", tx.TxIn[i].Sequence, "</sequence>") if tx.SegWit != nil { w.Write([]byte("<segwit>")) for _, wit := range tx.SegWit[i] { w.Write([]byte("<witness>")) w.Write([]byte(hex.EncodeToString(wit))) w.Write([]byte("</witness>")) } w.Write([]byte("</segwit>")) } w.Write([]byte("</input>")) } w.Write([]byte("</input_list>")) w.Write([]byte("<output_list>")) 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("</output_list>")) }
// Must be called from the chain's thread func HandleNetTx(ntx *TxRcvd, retry bool) (accepted bool) { common.CountSafe("HandleNetTx") tx := ntx.tx start_time := time.Now() var final bool // set to true if any of the inpits has a final sequence var totinp, totout uint64 var frommem bool TxMutex.Lock() if !retry { if _, present := TransactionsPending[tx.Hash.BIdx()]; !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.BIdx()) } 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)) rbf_tx_list := make(map[BIDX]bool) // Check if all the inputs exist in the chain for i := range tx.TxIn { if !final && tx.TxIn[i].Sequence >= 0xfffffffe { final = true } spent[i] = tx.TxIn[i].Input.UIdx() if so, ok := SpentOutputs[spent[i]]; ok { rbf_tx_list[so] = true } inptx := btc.NewUint256(tx.TxIn[i].Input.Hash[:]) if txinmem, ok := TransactionsToSend[inptx.BIdx()]; common.CFG.TXPool.AllowMemInputs && ok { if int(tx.TxIn[i].Input.Vout) >= len(txinmem.TxOut) { RejectTx(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 { var newone bool if !common.CFG.TXPool.AllowMemInputs { RejectTx(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 := RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_NO_TXOU) if nrtx != nil { nrtx.Wait4Input = &Wait4Input{missingTx: missingid, TxRcvd: ntx} // Add to waiting list: var rec *OneWaitingList if rec, _ = WaitingForInputs[nrtx.Wait4Input.missingTx.BIdx()]; rec == nil { rec = new(OneWaitingList) rec.TxID = nrtx.Wait4Input.missingTx rec.Ids = make(map[BIDX]time.Time) newone = true } rec.Ids[tx.Hash.BIdx()] = time.Now() WaitingForInputs[nrtx.Wait4Input.missingTx.BIdx()] = rec } TxMutex.Unlock() if newone { common.CountSafe("TxRejectedNoInpNew") } else { common.CountSafe("TxRejectedNoInpOld") } return } else { if pos[i].WasCoinbase { if common.Last.BlockHeight()+1-pos[i].BlockHeight < chain.COINBASE_MATURITY { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_CB_INMATURE) TxMutex.Unlock() common.CountSafe("TxRejectedCBInmature") fmt.Println(tx.Hash.String(), "trying to spend inmature coinbase block", pos[i].BlockHeight, "at", common.Last.BlockHeight()) return } } } } totinp += pos[i].Value } // Check if total output value does not exceed total input for i := range tx.TxOut { totout += tx.TxOut[i].Value } if totout > totinp { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_OVERSPEND) TxMutex.Unlock() ntx.conn.DoS("TxOverspend") return } // Check for a proper fee fee := totinp - totout if fee < (uint64(tx.VSize()) * atomic.LoadUint64(&common.CFG.TXPool.FeePerByte)) { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_LOW_FEE) TxMutex.Unlock() common.CountSafe("TxRejectedLowFee") return } //var new_spb, old_spb float64 var totlen int var totfees, new_min_fee uint64 if len(rbf_tx_list) > 0 { already_done := make(map[BIDX]bool) for len(already_done) < len(rbf_tx_list) { for k, _ := range rbf_tx_list { if _, yes := already_done[k]; !yes { already_done[k] = true if new_recs := findPendingTxs(TransactionsToSend[k].Tx); len(new_recs) > 0 { if len(rbf_tx_list)+len(new_recs) > 100 { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_RBF_100) TxMutex.Unlock() common.CountSafe("TxRejectedRBF100+") return } for _, id := range new_recs { rbf_tx_list[id] = true } } } } } for k, _ := range rbf_tx_list { ctx := TransactionsToSend[k] if ctx.Final { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_RBF_FINAL) TxMutex.Unlock() common.CountSafe("TxRejectedRBFFinal") return } totlen += len(ctx.Data) totfees += ctx.Fee } new_min_fee = totfees + (uint64(len(ntx.raw)) * atomic.LoadUint64(&common.CFG.TXPool.FeePerByte)) if fee < new_min_fee { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_RBF_LOWFEE) TxMutex.Unlock() common.CountSafe("TxRejectedRBFLowFee") return } } // Verify scripts sigops := btc.WITNESS_SCALE_FACTOR * tx.GetLegacySigOpCount() var wg sync.WaitGroup var ver_err_cnt uint32 prev_dbg_err := script.DBG_ERR script.DBG_ERR = false // keep quiet for incorrect txs for i := range tx.TxIn { wg.Add(1) go func(prv []byte, amount uint64, i int, tx *btc.Tx) { if !script.VerifyTxScript(prv, amount, i, tx, script.STANDARD_VERIFY_FLAGS) { atomic.AddUint32(&ver_err_cnt, 1) } wg.Done() }(pos[i].Pk_script, pos[i].Value, i, tx) } wg.Wait() script.DBG_ERR = prev_dbg_err if ver_err_cnt > 0 { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_SCRIPT_FAIL) TxMutex.Unlock() ntx.conn.DoS("TxScriptFail") if len(rbf_tx_list) > 0 { fmt.Println("RBF try", ver_err_cnt, "script(s) failed!") fmt.Print("> ") } return } for i := range tx.TxIn { if btc.IsP2SH(pos[i].Pk_script) { sigops += btc.WITNESS_SCALE_FACTOR * btc.GetP2SHSigOpCount(tx.TxIn[i].ScriptSig) } sigops += uint(tx.CountWitnessSigOps(i, pos[i].Pk_script)) } if len(rbf_tx_list) > 0 { for k, _ := range rbf_tx_list { ctx := TransactionsToSend[k] DeleteToSend(ctx) common.CountSafe("TxRemovedByRBF") } } rec := &OneTxToSend{Data: ntx.raw, Spent: spent, Volume: totinp, Fee: fee, Firstseen: time.Now(), Tx: tx, MemInputs: frommem, SigopsCost: sigops, Final: final, VerifyTime: time.Now().Sub(start_time)} TransactionsToSend[tx.Hash.BIdx()] = rec TransactionsToSendSize += uint64(len(rec.Data)) for i := range spent { SpentOutputs[spent[i]] = tx.Hash.BIdx() } 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 += NetRouteInvExt(1, tx.Hash, ntx.conn, 1000*fee/uint64(len(ntx.raw))) common.CountSafe("TxRouteOK") } if ntx.conn != nil { ntx.conn.Mutex.Lock() ntx.conn.X.TxsReceived++ ntx.conn.Mutex.Unlock() } accepted = true return }