Пример #1
0
// 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
}
Пример #2
0
func p_miners(w http.ResponseWriter, r *http.Request) {
	if !ipchecker(r) {
		return
	}

	common.ReloadMiners()

	m := make(map[string]omv, 20)
	var om omv
	cnt := 0
	common.Last.Mutex.Lock()
	end := common.Last.Block
	common.Last.Mutex.Unlock()
	var lastts int64
	var diff float64
	var totbts uint64
	var totfees uint64
	current_mid := -1
	now := time.Now().Unix()

	common.LockCfg()
	minerid := common.CFG.Beeps.MinerID
	common.UnlockCfg()

	next_diff_change := 2016 - end.Height%2016

	block_versions := make(map[uint32]uint)

	// bip100
	bip100_voting := make(map[string]uint)
	//var bip100, bip100v uint64
	bip100x := regexp.MustCompile("/BV{0,1}[0-9]+[M]{0,1}/")
	//bip100rx := regexp.MustCompile("/B[0-9]+[M]{0,1}/")

	for ; end != nil; cnt++ {
		if now-int64(end.Timestamp()) > int64(common.CFG.MiningStatHours)*3600 {
			break
		}
		lastts = int64(end.Timestamp())
		bl, _, e := common.BlockChain.Blocks.BlockGet(end.BlockHash)
		if e != nil {
			return
		}

		block, e := btc.NewBlock(bl)
		cbasetx, _ := btc.NewTx(bl[block.TxOffset:])

		block_versions[binary.LittleEndian.Uint32(bl[0:4])]++
		diff += btc.GetDifficulty(end.Bits())
		miner, mid := common.TxMiner(cbasetx)
		if mid == -1 {
			miner = "<i>" + miner + "</i>"
		}
		om = m[miner]
		om.cnt++
		om.bts += uint64(len(bl))
		om.mid = mid

		// Blocks reward
		var rew uint64
		for o := range cbasetx.TxOut {
			rew += cbasetx.TxOut[o].Value
		}
		om.fees += rew - btc.GetBlockReward(end.Height)

		// bip-100
		res := bip100x.Find(cbasetx.TxIn[0].ScriptSig)
		if res != nil {
			bip100_voting[string(res)]++
		}

		m[miner] = om
		if mid != -1 && current_mid == -1 && minerid == string(common.MinerIds[mid].Tag) {
			current_mid = mid
		}
		totbts += uint64(len(bl))
		end = end.Parent
	}

	if cnt == 0 {
		write_html_head(w, r)
		w.Write([]byte(fmt.Sprint("No blocks in last ", common.CFG.MiningStatHours, " hours")))
		write_html_tail(w)
		return
	}

	srt := make(onemiernstat, len(m))
	i := 0
	for k, v := range m {
		srt[i].name = k
		srt[i].omv = v
		i++
	}
	sort.Sort(srt)

	mnrs := load_template("miners.html")
	onerow := load_template("miners_row.html")

	diff /= float64(cnt)
	bph := float64(cnt) / float64(common.CFG.MiningStatHours)
	hrate := bph / 6 * diff * 7158278.826667
	mnrs = strings.Replace(mnrs, "{MINING_HOURS}", fmt.Sprint(common.CFG.MiningStatHours), 1)
	mnrs = strings.Replace(mnrs, "{BLOCKS_COUNT}", fmt.Sprint(cnt), 1)
	mnrs = strings.Replace(mnrs, "{FIRST_BLOCK_TIME}", time.Unix(lastts, 0).Format("2006-01-02 15:04:05"), 1)
	mnrs = strings.Replace(mnrs, "{AVG_BLOCKS_PER_HOUR}", fmt.Sprintf("%.2f", bph), 1)
	mnrs = strings.Replace(mnrs, "{AVG_DIFFICULTY}", common.NumberToString(diff), 1)
	mnrs = strings.Replace(mnrs, "{AVG_HASHDATE}", common.HashrateToString(hrate), 1)
	mnrs = strings.Replace(mnrs, "{AVG_BLOCKSIZE}", fmt.Sprintf("%.1fKB", float64(totbts)/float64(cnt)/1000), 1)
	mnrs = strings.Replace(mnrs, "{DIFF_CHANGE_IN}", fmt.Sprint(next_diff_change), 1)
	mnrs = strings.Replace(mnrs, "{MINER_MON_IDX}", fmt.Sprint(current_mid), 1)

	for i := range srt {
		s := onerow
		s = strings.Replace(s, "{MINER_NAME}", srt[i].name, 1)
		s = strings.Replace(s, "{BLOCK_COUNT}", fmt.Sprint(srt[i].cnt), 1)
		s = strings.Replace(s, "{TOTAL_PERCENT}", fmt.Sprintf("%.0f", 100*float64(srt[i].cnt)/float64(cnt)), 1)
		s = strings.Replace(s, "{MINER_HASHRATE}", common.HashrateToString(hrate*float64(srt[i].cnt)/float64(cnt)), 1)
		s = strings.Replace(s, "{AVG_BLOCK_SIZE}", fmt.Sprintf("%.1fKB", float64(srt[i].bts)/float64(srt[i].cnt)/1000), 1)
		s = strings.Replace(s, "{MINER_ID}", fmt.Sprint(srt[i].mid), -1)
		s = strings.Replace(s, "<!--TOTAL_FEES-->", btc.UintToBtc(srt[i].fees), -1)
		s = strings.Replace(s, "<!--FEE_PER_BYTE-->", fmt.Sprint(srt[i].fees/srt[i].bts), -1)
		mnrs = templ_add(mnrs, "<!--MINER_ROW-->", s)
		totfees += srt[i].fees
	}

	mnrs = strings.Replace(mnrs, "<!--TOTAL_MINING_FEES-->", btc.UintToBtc(totfees), 1)
	mnrs = strings.Replace(mnrs, "<!--AVERAGE_FEE_PER_BYTE-->", fmt.Sprint(totfees/totbts), 1)

	var bv string
	for k, v := range block_versions {
		if bv != "" {
			bv += " + "
		}
		bv += fmt.Sprintf("%dx%d", v, k)
	}
	mnrs = strings.Replace(mnrs, "<!--BLOCK_VERSIONS-->", bv, 1)

	// bip100
	bv = ""
	for k, v := range bip100_voting {
		if bv != "" {
			bv += " + "
		}
		bv += fmt.Sprintf("%sx%d", k, v)
	}

	mnrs = strings.Replace(mnrs, "<!--BLOCKSIZE_VOTES-->", bv, 1)

	write_html_head(w, r)
	w.Write([]byte(mnrs))
	write_html_tail(w)
}