func get_blocks() { var bl *btc.Block DlStartTime = time.Now() BlocksMutex.Lock() BlocksComplete = TheBlockChain.BlockTreeEnd.Height CurrentBlockHeight := BlocksComplete + 1 BlocksMutex.Unlock() TheBlockChain.DoNotSync = true tickSec := time.Tick(time.Second) tickDrop := time.Tick(DROP_PEER_EVERY_SEC * time.Second) tickStat := time.Tick(6 * time.Second) for !GlobalExit() && CurrentBlockHeight <= LastBlockHeight { select { case <-tickSec: cc := open_connection_count() if cc > MaxNetworkConns { drop_slowest_peers() } else if cc < MaxNetworkConns { add_new_connections() } case <-tickStat: print_stats() usif_prompt() case <-tickDrop: if open_connection_count() >= MaxNetworkConns { drop_slowest_peers() } case bl = <-BlockQueue: bl.Trusted = CurrentBlockHeight <= TrustUpTo if OnlyStoreBlocks { TheBlockChain.Blocks.BlockAdd(CurrentBlockHeight, bl) } else { er, _, _ := TheBlockChain.CheckBlock(bl) if er != nil { fmt.Println("CheckBlock:", er.Error()) return } else { bl.LastKnownHeight = CurrentBlockHeight + uint32(len(BlockQueue)) TheBlockChain.AcceptBlock(bl) } } atomic.StoreUint32(&LastStoredBlock, CurrentBlockHeight) atomic.AddUint64(&DlBytesProcessed, uint64(len(bl.Raw))) CurrentBlockHeight++ case <-time.After(100 * time.Millisecond): COUNTER("IDLE") TheBlockChain.Unspent.Idle() } } TheBlockChain.Sync() }
// This function either appends a new block at the end of the existing chain // in which case it also applies all the transactions to the unspent database. // If the block does is not the heighest, it is added to the chain, but maked // as an orphan - its transaction will be verified only if the chain would swap // to its branch later on. func (ch *Chain) AcceptBlock(bl *btc.Block) (e error) { prevblk, ok := ch.BlockIndex[btc.NewUint256(bl.ParentHash()).BIdx()] if !ok { panic("This should not happen") } // create new BlockTreeNode cur := new(BlockTreeNode) cur.BlockHash = bl.Hash cur.Parent = prevblk cur.Height = prevblk.Height + 1 cur.BlockSize = uint32(len(bl.Raw)) cur.TxCount = uint32(bl.TxCount) copy(cur.BlockHeader[:], bl.Raw[:80]) // Add this block to the block index ch.BlockIndexAccess.Lock() prevblk.addChild(cur) ch.BlockIndex[cur.BlockHash.BIdx()] = cur ch.BlockIndexAccess.Unlock() if ch.BlockTreeEnd == prevblk { // The head of out chain - apply the transactions var changes *BlockChanges changes, e = ch.ProcessBlockTransactions(bl, cur.Height, bl.LastKnownHeight) if e != nil { // ProcessBlockTransactions failed, so trash the block. println("ProcessBlockTransactions ", cur.BlockHash.String(), cur.Height, e.Error()) ch.BlockIndexAccess.Lock() cur.Parent.delChild(cur) delete(ch.BlockIndex, cur.BlockHash.BIdx()) ch.BlockIndexAccess.Unlock() } else { // ProcessBlockTransactions succeeded, so save the block as "trusted". bl.Trusted = true ch.Blocks.BlockAdd(cur.Height, bl) // Apply the block's trabnsactions to the unspent database: ch.Unspent.CommitBlockTxs(changes, bl.Hash.Hash[:]) if !ch.DoNotSync { ch.Blocks.Sync() } ch.BlockTreeEnd = cur // Advance the head } } else { // The block's parent is not the current head of the chain... // Save the block, though do not makt it as "trusted" just yet ch.Blocks.BlockAdd(cur.Height, bl) // If it has a bigger height than the current head, // ... move the coin state into a new branch. if cur.Height > ch.BlockTreeEnd.Height { ch.MoveToBlock(cur) } } return }
func chkblock(bl *btc.Block) (er error) { // Check timestamp (must not be higher than now +2 hours) if int64(bl.BlockTime()) > time.Now().Unix()+2*60*60 { er = errors.New("CheckBlock() : block timestamp too far in the future") return } MemBlockChainMutex.Lock() if prv, pres := MemBlockChain.BlockIndex[bl.Hash.BIdx()]; pres { MemBlockChainMutex.Unlock() if prv.Parent == nil { // This is genesis block er = errors.New("Genesis") return } else { return } } prevblk, ok := MemBlockChain.BlockIndex[btc.NewUint256(bl.ParentHash()).BIdx()] if !ok { er = errors.New("CheckBlock: " + bl.Hash.String() + " parent not found") return } // Check proof of work gnwr := MemBlockChain.GetNextWorkRequired(prevblk, bl.BlockTime()) if bl.Bits() != gnwr { if !Testnet || ((prevblk.Height+1)%2016) != 0 { MemBlockChainMutex.Unlock() er = errors.New(fmt.Sprint("CheckBlock: Incorrect proof of work at block", prevblk.Height+1)) return } } cur := new(chain.BlockTreeNode) cur.BlockHash = bl.Hash cur.Parent = prevblk cur.Height = prevblk.Height + 1 cur.TxCount = uint32(bl.TxCount) copy(cur.BlockHeader[:], bl.Raw[:80]) prevblk.Childs = append(prevblk.Childs, cur) MemBlockChain.BlockIndex[cur.BlockHash.BIdx()] = cur MemBlockChainMutex.Unlock() LastBlock.Mutex.Lock() if cur.Height > LastBlock.node.Height { LastBlock.node = cur } LastBlock.Mutex.Unlock() return }
func import_blockchain(dir string) { trust := !textui.AskYesNo("Do you want to verify scripts while importing (will be slow)?") BlockDatabase := blockdb.NewBlockDB(dir, common.Magic) chain := chain.NewChain(common.GocoinHomeDir, common.GenesisBlock, false) var bl *btc.Block var er error var dat []byte var totbytes, perbytes uint64 chain.DoNotSync = true fmt.Println("Be patient while importing Satoshi's database... ") start := time.Now().UnixNano() prv := start for { now := time.Now().UnixNano() if now-prv >= 10e9 { stat(now-start, now-prv, totbytes, perbytes, chain.BlockTreeEnd.Height) prv = now // show progress each 10 seconds perbytes = 0 } dat, er = BlockDatabase.FetchNextBlock() if dat == nil || er != nil { println("END of DB file") break } bl, er = btc.NewBlock(dat[:]) if er != nil { println("Block inconsistent:", er.Error()) break } bl.Trusted = trust er, _, _ = chain.CheckBlock(bl) if er != nil { if er.Error() != "Genesis" { println("CheckBlock failed:", er.Error()) //os.Exit(1) // Such a thing should not happen, so let's better abort here. } continue } er = chain.AcceptBlock(bl) if er != nil { println("AcceptBlock failed:", er.Error()) //os.Exit(1) // Such a thing should not happen, so let's better abort here. } totbytes += uint64(len(bl.Raw)) perbytes += uint64(len(bl.Raw)) } stop := time.Now().UnixNano() stat(stop-start, stop-prv, totbytes, perbytes, chain.BlockTreeEnd.Height) fmt.Println("Satoshi's database import finished in", (stop-start)/1e9, "seconds") fmt.Println("Now saving the new database...") chain.Sync() chain.Save() chain.Close() fmt.Println("Database saved. No more imports should be needed.") fmt.Println("It is advised to close and restart the node now, to free some mem.") }
func LocalAcceptBlock(bl *btc.Block, from *network.OneConnection) (e error) { sta := time.Now() e = common.BlockChain.AcceptBlock(bl) if e == nil { network.MutexRcv.Lock() network.ReceivedBlocks[bl.Hash.BIdx()].TmAccept = time.Now().Sub(sta) network.MutexRcv.Unlock() for i := 1; i < len(bl.Txs); i++ { network.TxMined(bl.Txs[i]) /* if msg:=contains_message(bl.Txs[i]); msg!=nil { for xx:=range msg { if msg[xx]<' ' || msg[xx]>127 { msg[xx] = '.' } } fmt.Println("TX", bl.Txs[i].Hash.String(), "says:", "'" + string(msg) + "'") textui.ShowPrompt() } */ } if int64(bl.BlockTime()) > time.Now().Add(-10*time.Minute).Unix() { // Freshly mined block - do the inv and beeps... common.Busy("NetRouteInv") network.NetRouteInv(2, bl.Hash, from) if common.CFG.Beeps.NewBlock { fmt.Println("\007Received block", common.BlockChain.BlockTreeEnd.Height) textui.ShowPrompt() } if common.MinedByUs(bl.Raw) { fmt.Println("\007Mined by '"+common.CFG.Beeps.MinerID+"':", bl.Hash) textui.ShowPrompt() } if common.CFG.Beeps.ActiveFork && common.Last.Block == common.BlockChain.BlockTreeEnd { // Last block has not changed, so it must have been an orphaned block bln := common.BlockChain.BlockIndex[bl.Hash.BIdx()] commonNode := common.Last.Block.FirstCommonParent(bln) forkDepth := bln.Height - commonNode.Height fmt.Println("Orphaned block:", bln.Height, bl.Hash.String(), bln.BlockSize>>10, "KB") if forkDepth > 1 { fmt.Println("\007\007\007WARNING: the fork is", forkDepth, "blocks deep") } textui.ShowPrompt() } if wallet.BalanceChanged && common.CFG.Beeps.NewBalance { fmt.Print("\007") } } common.Last.Mutex.Lock() common.Last.Time = time.Now() common.Last.Block = common.BlockChain.BlockTreeEnd common.Last.Mutex.Unlock() if wallet.BalanceChanged { wallet.BalanceChanged = false fmt.Println("Your balance has just changed") fmt.Print(wallet.DumpBalance(wallet.MyBalance, nil, false, true)) textui.ShowPrompt() } } else { fmt.Println("Warning: AcceptBlock failed. If the block was valid, you may need to rebuild the unspent DB (-r)") } return }
func (ch *Chain) CheckBlock(bl *btc.Block) (er error, dos bool, maybelater bool) { // Size limits if len(bl.Raw) < 81 || len(bl.Raw) > btc.MAX_BLOCK_SIZE { er = errors.New("CheckBlock() : size limits failed") dos = true return } // Check timestamp (must not be higher than now +2 hours) if int64(bl.BlockTime()) > time.Now().Unix()+2*60*60 { er = errors.New("CheckBlock() : block timestamp too far in the future") dos = true return } if prv, pres := ch.BlockIndex[bl.Hash.BIdx()]; pres { if prv.Parent == nil { // This is genesis block er = errors.New("Genesis") return } else { er = errors.New("CheckBlock: " + bl.Hash.String() + " already in") return } } prevblk, ok := ch.BlockIndex[btc.NewUint256(bl.ParentHash()).BIdx()] if !ok { er = errors.New("CheckBlock: " + bl.Hash.String() + " parent not found") maybelater = true return } height := prevblk.Height + 1 // Reject the block if it reaches into the chain deeper than our unwind buffer if prevblk != ch.BlockTreeEnd && int(ch.BlockTreeEnd.Height)-int(height) >= MovingCheckopintDepth { er = errors.New(fmt.Sprint("CheckBlock: btc.Block ", bl.Hash.String(), " hooks too deep into the chain: ", height, "/", ch.BlockTreeEnd.Height, " ", btc.NewUint256(bl.ParentHash()).String())) return } // Check proof of work gnwr := ch.GetNextWorkRequired(prevblk, bl.BlockTime()) if bl.Bits() != gnwr { println("AcceptBlock() : incorrect proof of work ", bl.Bits, " at block", height, " exp:", gnwr) // Here is a "solution" for whatever shit there is in testnet3, that nobody can explain me: if !ch.testnet() || (height%2016) != 0 { er = errors.New("CheckBlock: incorrect proof of work") dos = true return } } if bl.Txs == nil { er = bl.BuildTxList() if er != nil { dos = true return } } if !bl.Trusted { if bl.Version() == 0 || (height >= ForceBlockVer2From && !ch.testnet() && bl.Version() < 2) { er = errors.New("CheckBlock() : Block version too low: " + bl.Hash.String()) dos = true return } if bl.Version() >= 2 { var exp []byte if height >= 0x800000 { if height >= 0x80000000 { exp = []byte{5, byte(height), byte(height >> 8), byte(height >> 16), byte(height >> 24), 0} } else { exp = []byte{4, byte(height), byte(height >> 8), byte(height >> 16), byte(height >> 24)} } } else { exp = []byte{3, byte(height), byte(height >> 8), byte(height >> 16)} } if len(bl.Txs[0].TxIn[0].ScriptSig) < len(exp) || !bytes.Equal(exp, bl.Txs[0].TxIn[0].ScriptSig[:len(exp)]) { er = errors.New("CheckBlock() : Unexpected block number in coinbase: " + bl.Hash.String()) dos = true return } } // This is a stupid check, but well, we need to be satoshi compatible if len(bl.Txs) == 0 || !bl.Txs[0].IsCoinBase() { er = errors.New("CheckBlock() : first tx is not coinbase: " + bl.Hash.String()) dos = true return } // Check Merkle Root - that's importnant if !bytes.Equal(btc.GetMerkel(bl.Txs), bl.MerkleRoot()) { er = errors.New("CheckBlock() : Merkle Root mismatch") dos = true return } // Check transactions - this is the most time consuming task if !CheckTransactions(bl.Txs, height, bl.BlockTime()) { er = errors.New("CheckBlock() : CheckTransactions() failed") dos = true return } } return }
// 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) now := changes.Height == 381 && false //println("pr", changes.Height) 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.BlockTime() >= BIP16SwitchTime) }(bl.Txs[i].TxIn[j].ScriptSig, tout.Pk_script, j, bl.Txs[i]) } // Verify Transaction script: if now { println("+", inp.String(), tout.Value) } 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 }