func GetNextBlockTemplate(r *GetBlockTemplateResp) { var zer [32]byte common.Last.Mutex.Lock() r.Curtime = uint(time.Now().Unix()) r.Mintime = uint(common.Last.Block.GetMedianTimePast()) + 1 if r.Curtime < r.Mintime { r.Curtime = r.Mintime } height := common.Last.Block.Height + 1 bits := common.BlockChain.GetNextWorkRequired(common.Last.Block, uint32(r.Curtime)) target := btc.SetCompact(bits).Bytes() r.Capabilities = []string{"proposal"} r.Version = 4 r.PreviousBlockHash = common.Last.Block.BlockHash.String() r.Transactions, r.Coinbasevalue = GetTransactions(height, uint32(r.Mintime)) r.Coinbasevalue += btc.GetBlockReward(height) r.Coinbaseaux.Flags = "" r.Longpollid = r.PreviousBlockHash r.Target = hex.EncodeToString(append(zer[:32-len(target)], target...)) r.Mutable = []string{"time", "transactions", "prevblock"} r.Noncerange = "00000000ffffffff" r.Sigoplimit = btc.MAX_BLOCK_SIGOPS_COST / btc.WITNESS_SCALE_FACTOR r.Sizelimit = btc.MAX_BLOCK_SIZE r.Bits = fmt.Sprintf("%08x", bits) r.Height = uint(height) last_given_time = uint32(r.Curtime) last_given_mintime = uint32(r.Mintime) common.Last.Mutex.Unlock() }
func GetAverageFee() float64 { Last.Mutex.Lock() end := Last.Block Last.Mutex.Unlock() LockCfg() blocks := CFG.AverageFeeBlocks UnlockCfg() if blocks <= 0 { blocks = 1 // at leats one block } AverageFeeMutex.Lock() defer AverageFeeMutex.Unlock() if end.Height == averageFeeLastBlock && averageFeeLastCount == blocks { return AverageFee_SPB // we've already calculated for this block } averageFeeLastBlock = end.Height averageFeeLastCount = blocks AverageFeeBytes = 0 AverageFeeTotal = 0 for blocks > 0 { bl, _, e := BlockChain.Blocks.BlockGet(end.BlockHash) if e != nil { return 0 } block, e := btc.NewBlock(bl) if e != nil { return 0 } cbasetx, cbasetxlen := btc.NewTx(bl[block.TxOffset:]) for o := range cbasetx.TxOut { AverageFeeTotal += cbasetx.TxOut[o].Value } AverageFeeTotal -= btc.GetBlockReward(end.Height) AverageFeeBytes += uint64(len(bl) - block.TxOffset - cbasetxlen) /*do not count block header and conibase tx */ blocks-- end = end.Parent } if AverageFeeBytes == 0 { if AverageFeeTotal != 0 { panic("Impossible that miner gest a fee with no transactions in the block") } AverageFee_SPB = 0 } else { AverageFee_SPB = float64(AverageFeeTotal) / float64(AverageFeeBytes) } return AverageFee_SPB }
// 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 }
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("%d (%d%%) x <b>v%d</b>", v, 100*int(v)/cnt, 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) }
func json_blocks(w http.ResponseWriter, r *http.Request) { if !ipchecker(r) { return } type one_block struct { Height uint32 Timestamp uint32 Hash string TxCnt int Size int Version uint32 Reward uint64 Miner string FeeSPB float64 Received uint32 TimePre, TimeDl, TimeVer, TimeQue int WasteCnt uint MissedCnt int FromConID uint32 Sigops int MinFeeKSPB uint64 NonWitnessSize int } var blks []*one_block common.Last.Mutex.Lock() end := common.Last.Block common.Last.Mutex.Unlock() for cnt := uint32(0); end != nil && cnt < atomic.LoadUint32(&common.CFG.WebUI.ShowBlocks); cnt++ { bl, _, e := common.BlockChain.Blocks.BlockGet(end.BlockHash) if e != nil { return } block, e := btc.NewBlock(bl) if e != nil { return } b := new(one_block) b.Height = end.Height b.Timestamp = block.BlockTime() b.Hash = end.BlockHash.String() b.TxCnt = block.TxCount b.Size = len(bl) b.Version = block.Version() cbasetx, cbaselen := btc.NewTx(bl[block.TxOffset:]) for o := range cbasetx.TxOut { b.Reward += cbasetx.TxOut[o].Value } b.Miner, _ = common.TxMiner(cbasetx) if len(bl)-block.TxOffset-cbaselen != 0 { b.FeeSPB = float64(b.Reward-btc.GetBlockReward(end.Height)) / float64(len(bl)-block.TxOffset-cbaselen) } common.BlockChain.BlockIndexAccess.Lock() node := common.BlockChain.BlockIndex[end.BlockHash.BIdx()] common.BlockChain.BlockIndexAccess.Unlock() network.MutexRcv.Lock() rb := network.ReceivedBlocks[end.BlockHash.BIdx()] network.MutexRcv.Unlock() b.Received = uint32(rb.TmStart.Unix()) b.Sigops = int(node.SigopsCost) if rb.TmPreproc.IsZero() { b.TimePre = -1 } else { b.TimePre = int(rb.TmPreproc.Sub(rb.TmStart) / time.Millisecond) } if rb.TmDownload.IsZero() { b.TimeDl = -1 } else { b.TimeDl = int(rb.TmDownload.Sub(rb.TmStart) / time.Millisecond) } if rb.TmQueue.IsZero() { b.TimeQue = -1 } else { b.TimeQue = int(rb.TmQueue.Sub(rb.TmStart) / time.Millisecond) } if rb.TmAccepted.IsZero() { b.TimeVer = -1 } else { b.TimeVer = int(rb.TmAccepted.Sub(rb.TmStart) / time.Millisecond) } b.WasteCnt = rb.Cnt b.MissedCnt = rb.TxMissing b.FromConID = rb.FromConID b.MinFeeKSPB = rb.MinFeeKSPB b.NonWitnessSize = rb.NonWitnessSize blks = append(blks, b) end = end.Parent } bx, er := json.Marshal(blks) if er == nil { w.Header()["Content-Type"] = []string{"application/json"} w.Write(bx) } else { println(er.Error()) } }