// Download raw transaction from a web server (try one after another) func GetTxFromWeb(txid *btc.Uint256) (raw []byte) { raw = GetTxFromExplorer(txid) if raw != nil && txid.Equal(btc.NewSha2Hash(raw)) { println("GetTxFromExplorer - OK") return } raw = GetTxFromWebBTC(txid) if raw != nil && txid.Equal(btc.NewSha2Hash(raw)) { println("GetTxFromWebBTC - OK") return } raw = GetTxFromBlockrIo(txid) if raw != nil && txid.Equal(btc.NewSha2Hash(raw)) { println("GetTxFromBlockrIo - OK") return } raw = GetTxFromBlockchainInfo(txid) if raw != nil && txid.Equal(btc.NewSha2Hash(raw)) { println("GetTxFromBlockchainInfo - OK") return } return }
// Handle incoming "tx" msg func (c *OneConnection) ParseTxNet(pl []byte) { tid := btc.NewSha2Hash(pl) NeedThisTx(tid, func() { // This body is called with a locked TxMutex if uint32(len(pl)) > atomic.LoadUint32(&common.CFG.TXPool.MaxTxSize) { common.CountSafe("TxTooBig") RejectTx(tid, len(pl), TX_REJECTED_TOO_BIG) return } tx, le := btc.NewTx(pl) if tx == nil { RejectTx(tid, len(pl), TX_REJECTED_FORMAT) c.DoS("TxBroken") return } if le != len(pl) { RejectTx(tid, len(pl), TX_REJECTED_LEN_MISMATCH) c.DoS("TxLenMismatch") return } if len(tx.TxIn) < 1 { RejectTx(tid, len(pl), TX_REJECTED_EMPTY_INPUT) c.DoS("TxNoInputs") return } tx.Hash = tid select { case NetTxs <- &TxRcvd{conn: c, tx: tx, raw: pl}: TransactionsPending[tid.BIdx()] = true default: common.CountSafe("NetTxsFULL") } }) }
func print_record(sl []byte) { bh := btc.NewSha2Hash(sl[56:136]) fmt.Print("Block ", bh.String(), " Height ", binary.LittleEndian.Uint32(sl[36:40])) fmt.Println() fmt.Println(" ...", binary.LittleEndian.Uint32(sl[48:52]), " Bytes @ ", binary.LittleEndian.Uint64(sl[40:48]), " in dat file") hdr := sl[56:136] fmt.Println(" ->", btc.NewUint256(hdr[4:36]).String()) }
func write_tx_file(tx *btc.Tx) { signedrawtx := tx.Serialize() tx.Hash = btc.NewSha2Hash(signedrawtx) hs := tx.Hash.String() fmt.Println("TxID", hs) f, _ := os.Create(hs[:8] + ".txt") if f != nil { f.Write([]byte(hex.EncodeToString(signedrawtx))) f.Close() fmt.Println("Transaction data stored in", hs[:8]+".txt") } }
func raw_tx_from_file(fn string) *btc.Tx { dat := sys.GetRawData(fn) if dat == nil { fmt.Println("Cannot fetch raw transaction data") return nil } tx, txle := btc.NewTx(dat) if tx != nil { tx.Hash = btc.NewSha2Hash(dat) if txle != len(dat) { fmt.Println("WARNING: Raw transaction length mismatch", txle, len(dat)) } } return tx }
func (c *one_net_conn) block_pong(d []byte) { if len(d) > 80 { c.ping.Lock() defer c.ping.Unlock() if c.ping.lastBlock != nil { c.ping.bytes += uint(len(d)) h := btc.NewSha2Hash(d[:80]) if h.Equal(c.ping.lastBlock) { //fmt.Println(c.peerip, "bl_pong", c.ping.seq, c.ping.bytes, time.Now().Sub(c.ping.timeSent)) c.ping.lastBlock = nil c.ping.bytes = 0 c.store_ping_result() } } } }
func (c *one_net_conn) block(d []byte) { BlocksMutex.Lock() defer BlocksMutex.Unlock() h := btc.NewSha2Hash(d[:80]) c.Lock() c.last_blk_rcvd = time.Now() c.Unlock() bip := BlocksInProgress[h.Hash] if bip == nil || !bip.Conns[c.id] { COUNTER("UNEX") //fmt.Println(h.String(), "- already received", bip) return } delete(bip.Conns, c.id) c.Lock() c.inprogress-- c.Unlock() atomic.AddUint64(&DlBytesDownloaded, uint64(len(d))) blocksize_update(len(d)) bl, er := btc.NewBlock(d) if er != nil { fmt.Println(c.peerip, "-", er.Error()) c.setbroken(true) return } bl.BuildTxList() if !bytes.Equal(btc.GetMerkel(bl.Txs), bl.MerkleRoot()) { fmt.Println(c.peerip, " - MerkleRoot mismatch at block", bip.Height) c.setbroken(true) return } BlocksCachedSize += uint(len(d)) BlocksCached[bip.Height] = bl delete(BlocksToGet, bip.Height) delete(BlocksInProgress, h.Hash) //fmt.Println(" got block", height) }
func mk_out_tx(sig_scr, pk_scr []byte) (output_tx *btc.Tx) { // We build input_tx only to calculate it's hash for output_tx input_tx := new(btc.Tx) input_tx.Version = 1 input_tx.TxIn = []*btc.TxIn{&btc.TxIn{Input: btc.TxPrevOut{Vout: 0xffffffff}, ScriptSig: []byte{0, 0}, Sequence: 0xffffffff}} input_tx.TxOut = []*btc.TxOut{&btc.TxOut{Pk_script: pk_scr}} // Lock_time = 0 output_tx = new(btc.Tx) output_tx.Version = 1 output_tx.TxIn = []*btc.TxIn{&btc.TxIn{Input: btc.TxPrevOut{Hash: btc.Sha2Sum(input_tx.Serialize()), Vout: 0}, ScriptSig: sig_scr, Sequence: 0xffffffff}} output_tx.TxOut = []*btc.TxOut{&btc.TxOut{}} // Lock_time = 0 output_tx.Hash = btc.NewSha2Hash(output_tx.Serialize()) return }
func LoadRawTx(buf []byte) (s string) { txd, er := hex.DecodeString(string(buf)) if er != nil { txd = buf } // At this place we should have raw transaction in txd tx, le := btc.NewTx(txd) if tx == nil || le != len(txd) { s += fmt.Sprintln("Could not decode transaction file or it has some extra data") return } tx.Hash = btc.NewSha2Hash(txd) network.TxMutex.Lock() defer network.TxMutex.Unlock() if _, ok := network.TransactionsToSend[tx.Hash.BIdx()]; ok { s += fmt.Sprintln("TxID", tx.Hash.String(), "was already in the pool") return } var missinginp bool var totinp, totout uint64 s, missinginp, totinp, totout, er = DecodeTx(tx) if er != nil { return } if missinginp { network.TransactionsToSend[tx.Hash.BIdx()] = &network.OneTxToSend{Tx: tx, Data: txd, Own: 2, Firstseen: time.Now(), Volume: totout} } else { network.TransactionsToSend[tx.Hash.BIdx()] = &network.OneTxToSend{Tx: tx, Data: txd, Own: 1, Firstseen: time.Now(), Volume: totinp, Fee: totinp - totout} } s += fmt.Sprintln("Transaction added to the memory pool. Please double check its details above.") s += fmt.Sprintln("If it does what you intended, you can send it the network.\nUse TxID:", tx.Hash.String()) return }
func push_tx(rawtx string) { dat := sys.GetRawData(rawtx) if dat == nil { println("Cannot fetch the raw transaction data (specify hexdump or filename)") return } val := make(url.Values) val["rawtx"] = []string{hex.EncodeToString(dat)} r, er := http.PostForm(HOST+"txs", val) if er != nil { println(er.Error()) os.Exit(1) } if r.StatusCode == 200 { defer r.Body.Close() res, _ := ioutil.ReadAll(r.Body) if len(res) > 100 { txid := btc.NewSha2Hash(dat) fmt.Println("TxID", txid.String(), "loaded") http_get(HOST + "cfg") // get SID //fmt.Println("sid", SID) u, _ := url.Parse(HOST + "txs2s.xml") ps := url.Values{} ps.Add("sid", SID) ps.Add("send", txid.String()) u.RawQuery = ps.Encode() http_get(u.String()) } } else { println("http.Post returned code", r.StatusCode) os.Exit(1) } }
func main() { if len(os.Args) < 2 { fmt.Println("Specify a path to folder containig blockchain.dat and blockchain.new") fmt.Println("Output bootstrap.dat file will be written in the current folder.") return } blks := chain.NewBlockDB(os.Args[1]) if blks == nil { return } fmt.Println("Loading block index...") bidx = make(map[[32]byte]*chain.BlockTreeNode, 300e3) blks.LoadBlockIndex(nil, walk) var tail, nd *chain.BlockTreeNode var genesis_block_hash *btc.Uint256 for _, v := range bidx { if v == tail { // skip root block (should be only one) continue } par_hash := btc.NewUint256(v.BlockHeader[4:36]) par, ok := bidx[par_hash.Hash] if !ok { genesis_block_hash = par_hash } else { v.Parent = par if tail == nil || v.Height > tail.Height { tail = v } } } if genesis_block_hash == nil { println("genesis_block_hash not found") return } var magic []byte gen_bin, _ := hex.DecodeString(GenesisBitcoin) tmp := btc.NewSha2Hash(gen_bin[:80]) if genesis_block_hash.Equal(tmp) { println("Bitcoin genesis block") magic = []byte{0xF9, 0xBE, 0xB4, 0xD9} } if magic == nil { gen_bin, _ := hex.DecodeString(GenesisTestnet) tmp = btc.NewSha2Hash(gen_bin[:80]) if genesis_block_hash.Equal(tmp) { println("Testnet3 genesis block") magic = []byte{0x0B, 0x11, 0x09, 0x07} } } if magic == nil { println("Unknow genesis block", genesis_block_hash.String()) println("Aborting since cannot figure out the magic bytes") return } var total_data, curr_data int64 for nd = tail; nd.Parent != nil; { nd.Parent.Childs = []*chain.BlockTreeNode{nd} total_data += int64(nd.BlockSize) nd = nd.Parent } fmt.Println("Writting bootstrap.dat, height", tail.Height, " magic", hex.EncodeToString(magic)) f, _ := os.Create("bootstrap.dat") f.Write(magic) binary.Write(f, binary.LittleEndian, uint32(len(gen_bin))) f.Write(gen_bin) for { bl, _, _ := blks.BlockGet(nd.BlockHash) f.Write(magic) binary.Write(f, binary.LittleEndian, uint32(len(bl))) f.Write(bl) curr_data += int64(nd.BlockSize) if (nd.Height & 0xfff) == 0 { fmt.Printf("\r%.1f%%...", 100*float64(curr_data)/float64(total_data)) } if len(nd.Childs) == 0 { break } nd = nd.Childs[0] } fmt.Println("\rDone ") }
func (c *one_net_conn) block(d []byte) { atomic.AddUint64(&DlBytesDownloaded, uint64(len(d))) BlocksMutex.Lock() defer BlocksMutex.Unlock() h := btc.NewSha2Hash(d[:80]) c.Lock() c.last_blk_rcvd = time.Now() c.Unlock() bip := BlocksInProgress[h.Hash] if bip == nil || !bip.Conns[c.id] { COUNTER("BNOT") //fmt.Println(h.String(), "- already received", bip) return } //fmt.Println(h.String(), "- new", bip.Height) COUNTER("BYES") delete(bip.Conns, c.id) c.Lock() c.inprogress-- c.Unlock() blocksize_update(len(d)) bl, er := btc.NewBlock(d) if er != nil { fmt.Println(c.Ip(), "-", er.Error()) c.setbroken(true) return } bl.BuildTxList() if !bytes.Equal(btc.GetMerkel(bl.Txs), bl.MerkleRoot()) { fmt.Println(c.Ip(), " - MerkleRoot mismatch at block", bip.Height) c.setbroken(true) return } delete(BlocksToGet, bip.Height) delete(BlocksInProgress, h.Hash) if len(BlocksInProgress) == 0 { EmptyInProgressCnt++ } //println("got-", bip.Height, BlocksComplete+1) if BlocksComplete+1 == bip.Height { BlocksComplete++ BlockQueue <- bl for { bl = BlocksCached[BlocksComplete+1] if bl == nil { break } BlocksComplete++ delete(BlocksCached, BlocksComplete) BlocksCachedSize -= uint64(len(bl.Raw)) BlockQueue <- bl } } else { BlocksCached[bip.Height] = bl BlocksCachedSize += uint64(len(d)) } }
func main() { flag.BoolVar(&fl_help, "h", false, "Show help") flag.UintVar(&fl_block, "block", 0, "Print details of the given block number (or start -verify from it)") flag.BoolVar(&fl_scan, "scan", false, "Scan database for first extra blocks") flag.BoolVar(&fl_defrag, "defrag", false, "Purge all the orphaned blocks") flag.UintVar(&fl_stop, "stop", 0, "Stop after so many scan errors") flag.StringVar(&fl_dir, "dir", "", "Use blockdb from this directory") flag.StringVar(&fl_split, "split", "", "Split blockdb at this block's hash") flag.UintVar(&fl_skip, "skip", 0, "Skip this many blocks when splitting") flag.StringVar(&fl_append, "append", "", "Append blocks from this folder to the database") flag.BoolVar(&fl_trunc, "trunc", false, "Truncate insted of splitting") flag.BoolVar(&fl_commit, "commit", false, "Optimize the size of the data file") flag.BoolVar(&fl_verify, "verify", false, "Verify each block inside the database") flag.StringVar(&fl_savebl, "savebl", "", "Save block with the given hash to disk") flag.BoolVar(&fl_resetflags, "resetflags", false, "Reset all Invalid and Trusted flags when defragmenting") flag.Parse() if fl_help { flag.PrintDefaults() return } if fl_dir != "" && fl_dir[len(fl_dir)-1] != os.PathSeparator { fl_dir += string(os.PathSeparator) } if fl_append != "" { if fl_append[len(fl_append)-1] != os.PathSeparator { fl_append += string(os.PathSeparator) } fmt.Println("Loading", fl_append+"blockchain.new") dat, er := ioutil.ReadFile(fl_append + "blockchain.new") if er != nil { fmt.Println(er.Error()) return } f, er := os.Open(fl_append + "blockchain.dat") if er != nil { fmt.Println(er.Error()) return } fo, er := os.OpenFile(fl_dir+"blockchain.dat", os.O_WRONLY, 0600) if er != nil { f.Close() fmt.Println(er.Error()) return } datfilelen, _ := fo.Seek(0, os.SEEK_END) fmt.Println("Appending blocks data to blockchain.dat") for { var buf [1024 * 1024]byte n, _ := f.Read(buf[:]) if n > 0 { fo.Write(buf[:n]) } if n != len(buf) { break } } fo.Close() f.Close() fmt.Println("Now appending", len(dat)/136, "records to blockchain.new") fo, er = os.OpenFile(fl_dir+"blockchain.new", os.O_WRONLY, 0600) if er != nil { f.Close() fmt.Println(er.Error()) return } fo.Seek(0, os.SEEK_END) for off := 0; off < len(dat); off += 136 { sl := dat[off : off+136] newoffs := binary.LittleEndian.Uint64(sl[40:48]) + uint64(datfilelen) binary.LittleEndian.PutUint64(sl[40:48], newoffs) fo.Write(sl) } fo.Close() return } fmt.Println("Loading", fl_dir+"blockchain.new") dat, er := ioutil.ReadFile(fl_dir + "blockchain.new") if er != nil { fmt.Println(er.Error()) return } fmt.Println(len(dat)/136, "records") if fl_scan { var scan_errs uint last_bl_height := binary.LittleEndian.Uint32(dat[36:40]) exp_offset := uint64(binary.LittleEndian.Uint32(dat[48:52])) fmt.Println("Scanning database for first extra block(s)...") fmt.Println("First block in the file has height", last_bl_height) for off := 136; off < len(dat); off += 136 { sl := dat[off : off+136] height := binary.LittleEndian.Uint32(sl[36:40]) off_in_bl := binary.LittleEndian.Uint64(sl[40:48]) if height != last_bl_height+1 { fmt.Println("Out of sequence block number", height, last_bl_height+1, "found at offset", off) print_record(dat[off-136 : off]) print_record(dat[off : off+136]) fmt.Println() scan_errs++ } if off_in_bl != exp_offset { fmt.Println("Spare data found just before block number", height, off_in_bl, exp_offset) print_record(dat[off-136 : off]) print_record(dat[off : off+136]) scan_errs++ } if fl_stop != 0 && scan_errs >= fl_stop { break } last_bl_height = height exp_offset += uint64(binary.LittleEndian.Uint32(sl[48:52])) } return } if fl_defrag { blks := make(map[[32]byte]*one_tree_node, len(dat)/136) for off := 0; off < len(dat); off += 136 { sl := new_sl(dat[off : off+136]) blks[sl.HIdx()] = &one_tree_node{off: off, one_idx_rec: sl} } var maxbl uint32 var maxblptr *one_tree_node for _, v := range blks { v.parent = blks[v.PIdx()] h := v.Height() if h > maxbl { maxbl = h maxblptr = v } else if h == maxbl { maxblptr = nil } } fmt.Println("Max block height =", maxbl) if maxblptr == nil { fmt.Println("More than one block at maximum height - cannot continue") return } used := make(map[[32]byte]bool) var first_block *one_tree_node var total_data_size uint64 for n := maxblptr; n != nil; n = n.parent { if n.parent != nil { n.parent.next = n } used[n.PIdx()] = true if first_block == nil || first_block.Height() > n.Height() { first_block = n } total_data_size += uint64(n.DLen()) } if len(used) < len(blks) || fl_resetflags { fmt.Println("Purge", len(blks)-len(used), "blocks from the index file...") f, e := os.Create(fl_dir + "blockchain.tmp") if e != nil { println(e.Error()) return } var off int for n := first_block; n != nil; n = n.next { n.off = off n.sl[0] = n.sl[0] & 0xfc f.Write(n.sl) off += len(n.sl) } f.Close() os.Rename(fl_dir+"blockchain.tmp", fl_dir+"blockchain.new") } else { fmt.Println("The index file looks perfect") } for n := first_block; n != nil && n.next != nil; n = n.next { if n.next.DPos() < n.DPos() { fmt.Println("There is a problem... swapped order in the data file!", n.off) return } } fdat, er := os.OpenFile(fl_dir+"blockchain.dat", os.O_RDWR, 0600) if er != nil { println(er.Error()) return } if fl, _ := fdat.Seek(0, os.SEEK_END); uint64(fl) == total_data_size { fdat.Close() fmt.Println("All good - blockchain.dat has an optimal length") return } if !fl_commit { fdat.Close() fmt.Println("Warning: blockchain.dat shall be defragmented. Use \"-defrag -commit\"") return } fidx, er := os.OpenFile(fl_dir+"blockchain.new", os.O_RDWR, 0600) if er != nil { println(er.Error()) return } // Capture Ctrl+C killchan := make(chan os.Signal, 1) signal.Notify(killchan, os.Interrupt, os.Kill) var doff uint64 var prv_perc uint64 = 101 buf := make([]byte, 0x100000) for n := first_block; n != nil; n = n.next { perc := 1000 * doff / total_data_size dp := n.DPos() dl := n.DLen() if perc != prv_perc { fmt.Printf("\rDefragmenting data file - %.1f%% (%d bytes saved so far)...", float64(perc)/10.0, dp-doff) prv_perc = perc } if dp > doff { if len(buf) < int(dl) { println("WARNIGN: grow block buffer to ") buf = make([]byte, dl) } fdat.Seek(int64(dp), os.SEEK_SET) fdat.Read(buf[:int(dl)]) n.SetDPos(doff) fdat.Seek(int64(doff), os.SEEK_SET) fdat.Write(buf[:int(dl)]) fidx.Seek(int64(n.off), os.SEEK_SET) fidx.Write(n.sl) } doff += uint64(dl) select { case <-killchan: fmt.Println("interrupted") fidx.Close() fdat.Close() fmt.Println("Database closed - should be still usable, but no space saved") return default: } } fidx.Close() fdat.Close() fmt.Println() fmt.Println("Truncating blockchain.dat at position", doff) os.Truncate(fl_dir+"blockchain.dat", int64(doff)) return } if fl_verify { fdat, er := os.OpenFile(fl_dir+"blockchain.dat", os.O_RDWR, 0600) if er != nil { println(er.Error()) return } dat_file_size, _ := fdat.Seek(0, os.SEEK_END) buf := make([]byte, 0x100000) var prv_perc int64 = -1 var totlen uint64 var done sync.WaitGroup for off := 0; off < len(dat); off += 136 { sl := new_sl(dat[off : off+136]) dp := int64(sl.DPos()) le := int(sl.DLen()) hei := uint(sl.Height()) perc := 1000 * dp / dat_file_size if perc != prv_perc { fmt.Printf("\rVerifying blocks data - %.1f%% @ %d / %dMB processed...", float64(perc)/10.0, hei, totlen>>20) prv_perc = perc } if fl_block != 0 && hei < fl_block { continue } fdat.Seek(dp, os.SEEK_SET) fdat.Read(buf[:le]) blk := decomp_block(sl.Flags(), buf[:le]) done.Add(1) go func(blk []byte, sl one_idx_rec, off int) { verify_block(blk, sl, off) done.Done() }(blk, sl, off) totlen += uint64(len(blk)) } done.Wait() // wait for all the goroutines to complete return } if fl_block != 0 { for off := 0; off < len(dat); off += 136 { sl := dat[off : off+136] height := binary.LittleEndian.Uint32(sl[36:40]) if uint(height) == fl_block { print_record(dat[off : off+136]) } } return } if fl_split != "" { th := btc.NewUint256FromString(fl_split) if th == nil { println("incorrect block hash") return } for off := 0; off < len(dat); off += 136 { sl := dat[off : off+136] height := binary.LittleEndian.Uint32(sl[36:40]) bh := btc.NewSha2Hash(sl[56:136]) if bh.Hash == th.Hash { trunc_idx_offs := int64(off) trunc_dat_offs := int64(binary.LittleEndian.Uint64(sl[40:48])) fmt.Println("Truncate blockchain.new at offset", trunc_idx_offs) fmt.Println("Truncate blockchain.dat at offset", trunc_dat_offs) if !fl_trunc { new_dir := fl_dir + fmt.Sprint(height) + string(os.PathSeparator) os.Mkdir(new_dir, os.ModePerm) f, e := os.Open(fl_dir + "blockchain.dat") if e != nil { fmt.Println(e.Error()) return } df, e := os.Create(new_dir + "blockchain.dat") if e != nil { f.Close() fmt.Println(e.Error()) return } f.Seek(trunc_dat_offs, os.SEEK_SET) fmt.Println("But fist save the rest in", new_dir, "...") if fl_skip != 0 { fmt.Println("Skip", fl_skip, "blocks in the output file") for fl_skip > 0 { skipbytes := binary.LittleEndian.Uint32(sl[48:52]) fmt.Println(" -", skipbytes, "bytes of block", binary.LittleEndian.Uint32(sl[36:40])) off += 136 if off < len(dat) { sl = dat[off : off+136] fl_skip-- } else { break } } } for { var buf [1024 * 1024]byte n, _ := f.Read(buf[:]) if n > 0 { df.Write(buf[:n]) } if n != len(buf) { break } } df.Close() f.Close() df, e = os.Create(new_dir + "blockchain.new") if e != nil { f.Close() fmt.Println(e.Error()) return } var off2 int for off2 = off; off2 < len(dat); off2 += 136 { sl := dat[off2 : off2+136] newoffs := binary.LittleEndian.Uint64(sl[40:48]) - uint64(trunc_dat_offs) binary.LittleEndian.PutUint64(sl[40:48], newoffs) df.Write(sl) } df.Close() } os.Truncate(fl_dir+"blockchain.new", trunc_idx_offs) os.Truncate(fl_dir+"blockchain.dat", trunc_dat_offs) return } } fmt.Println("Block not found - nothing truncated") } if fl_savebl != "" { bh := btc.NewUint256FromString(fl_savebl) if bh == nil { println("Incortrect block hash:", fl_savebl) return } for off := 0; off < len(dat); off += 136 { sl := new_sl(dat[off : off+136]) if bytes.Equal(sl.Hash(), bh.Hash[:]) { f, er := os.Open(fl_dir + "blockchain.dat") if er != nil { println(er.Error()) return } buf := make([]byte, int(sl.DLen())) f.Seek(int64(sl.DPos()), os.SEEK_SET) f.Read(buf) f.Close() ioutil.WriteFile(bh.String()+".bin", decomp_block(sl.Flags(), buf), 0600) fmt.Println(bh.String()+".bin written to disk. It has height", sl.Height()) return } } fmt.Println("Block", bh.String(), "not found in the database") return } var minbh, maxbh uint32 minbh = binary.LittleEndian.Uint32(dat[36:40]) maxbh = minbh for off := 136; off < len(dat); off += 136 { sl := new_sl(dat[off : off+136]) bh := sl.Height() if bh > maxbh { maxbh = bh } else if bh < minbh { minbh = bh } } fmt.Println("Block heights from", minbh, "to", maxbh) }
// This function is called from a net conn thread func netBlockReceived(conn *OneConnection, b []byte) { if len(b) < 100 { conn.DoS("ShortBlock") return } hash := btc.NewSha2Hash(b[:80]) idx := hash.BIdx() //println("got block data", hash.String()) MutexRcv.Lock() // the blocks seems to be fine if rb, got := ReceivedBlocks[idx]; got { rb.Cnt++ common.CountSafe("BlockSameRcvd") conn.Mutex.Lock() delete(conn.GetBlockInProgress, idx) conn.Mutex.Unlock() MutexRcv.Unlock() return } // remove from BlocksToGet: b2g := BlocksToGet[idx] if b2g == nil { //println("Block", hash.String(), " from", conn.PeerAddr.Ip(), conn.Node.Agent, " was not expected") var hdr [81]byte var sta int copy(hdr[:80], b[:80]) sta, b2g = conn.ProcessNewHeader(hdr[:]) if b2g == nil { if sta == PH_STATUS_FATAL { println("Unrequested Block: FAIL - Ban", conn.PeerAddr.Ip(), conn.Node.Agent) conn.DoS("BadUnreqBlock") } else { common.CountSafe("ErrUnreqBlock") } //conn.Disconnect() MutexRcv.Unlock() return } //println(c.ConnID, " - taking this new block") common.CountSafe("UnxpectedBlockNEW") } //println("block", b2g.BlockTreeNode.Height," len", len(b), " got from", conn.PeerAddr.Ip(), b2g.InProgress) b2g.Block.Raw = b er := common.BlockChain.PostCheckBlock(b2g.Block) if er != nil { b2g.InProgress-- println("Corrupt block received from", conn.PeerAddr.Ip(), er.Error()) //ioutil.WriteFile(hash.String() + ".bin", b, 0700) conn.DoS("BadBlock") MutexRcv.Unlock() return } orb := &OneReceivedBlock{TmStart: b2g.Started, TmPreproc: b2g.TmPreproc, TmDownload: conn.LastMsgTime, FromConID: conn.ConnID} conn.Mutex.Lock() bip := conn.GetBlockInProgress[idx] if bip == nil { //println(conn.ConnID, "received unrequested block", hash.String()) common.CountSafe("UnreqBlockRcvd") conn.counters["NewBlock!"]++ orb.TxMissing = -2 } else { delete(conn.GetBlockInProgress, idx) conn.counters["NewBlock"]++ orb.TxMissing = -1 } conn.blocksreceived = append(conn.blocksreceived, time.Now()) conn.Mutex.Unlock() ReceivedBlocks[idx] = orb DelB2G(idx) //remove it from BlocksToGet if no more pending downloads MutexRcv.Unlock() NetBlocks <- &BlockRcvd{Conn: conn, Block: b2g.Block, BlockTreeNode: b2g.BlockTreeNode, OneReceivedBlock: orb} }
func (c *OneConnection) ProcessBlockTxn(pl []byte) { if len(pl) < 33 { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "blocktxn error A", hex.EncodeToString(pl)) c.DoS("BlkTxnErrLen") return } hash := btc.NewUint256(pl[:32]) le, n := btc.VLen(pl[32:]) if le < 0 || n > 3 { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "blocktxn error B", hex.EncodeToString(pl)) c.DoS("BlkTxnErrCnt") return } MutexRcv.Lock() defer MutexRcv.Unlock() idx := hash.BIdx() c.Mutex.Lock() bip := c.GetBlockInProgress[idx] if bip == nil { c.Mutex.Unlock() println(c.ConnID, "Unexpected BlkTxn1 received from", c.PeerAddr.Ip()) common.CountSafe("BlkTxnUnexp1") c.DoS("BlkTxnErrBip") return } col := bip.col if col == nil { c.Mutex.Unlock() println("Unexpected BlockTxn2 not expected from this peer", c.PeerAddr.Ip()) common.CountSafe("UnxpectedBlockTxn") c.DoS("BlkTxnErrCol") return } delete(c.GetBlockInProgress, idx) c.Mutex.Unlock() // the blocks seems to be fine if rb, got := ReceivedBlocks[idx]; got { rb.Cnt++ common.CountSafe("BlkTxnSameRcvd") //fmt.Println(c.ConnID, "BlkTxn size", len(pl), "for", hash.String()[48:],"- already have") return } b2g := BlocksToGet[idx] if b2g == nil { panic("BlockTxn: Block missing from BlocksToGet") return } //b2g.InProgress-- //fmt.Println(c.ConnID, "BlockTxn size", len(pl), "-", le, "new txs for block #", b2g.Block.Height) offs := 32 + n for offs < len(pl) { n = btc.TxSize(pl[offs:]) if n == 0 { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "blocktxn corrupt TX") c.DoS("BlkTxnErrTx") return } raw_tx := pl[offs : offs+n] tx_hash := btc.NewSha2Hash(raw_tx) offs += n sid := siphash.Hash(col.K0, col.K1, tx_hash.Hash[:]) & 0xffffffffffff if idx, ok := col.Sid2idx[sid]; ok { col.Txs[idx] = raw_tx } else { common.CountSafe("ShortIDUnknown") println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "blocktxn TX (short) ID unknown") return } } //println(c.ConnID, "Received the rest of compact block version", c.Node.SendCmpctVer) //sta := time.Now() b2g.Block.UpdateContent(col.Assemble()) //sto := time.Now() er := common.BlockChain.PostCheckBlock(b2g.Block) if er != nil { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "Corrupt CmpctBlkB") //c.DoS("BadCmpctBlockB") ioutil.WriteFile(b2g.Hash.String()+".bin", b2g.Block.Raw, 0700) return } DelB2G(idx) //fmt.Println(c.ConnID, "PostCheckBlock OK #", b2g.Block.Height, sto.Sub(sta), time.Now().Sub(sta)) c.Mutex.Lock() c.counters["NewTBlock"]++ c.blocksreceived = append(c.blocksreceived, time.Now()) c.Mutex.Unlock() orb := &OneReceivedBlock{TmStart: b2g.Started, TmPreproc: b2g.TmPreproc, TmDownload: c.LastMsgTime, TxMissing: col.Missing, FromConID: c.ConnID} ReceivedBlocks[idx] = orb NetBlocks <- &BlockRcvd{Conn: c, Block: b2g.Block, BlockTreeNode: b2g.BlockTreeNode, OneReceivedBlock: orb} }
// Download (and re-assemble) raw transaction from blockexplorer.com func GetTxFromExplorer(txid *btc.Uint256) ([]byte, []byte) { url := "http://blockexplorer.com/rawtx/" + txid.String() r, er := http.Get(url) if er == nil && r.StatusCode == 200 { defer r.Body.Close() c, _ := ioutil.ReadAll(r.Body) var txx onetx er = json.Unmarshal(c[:], &txx) if er == nil { // This part looks weird, but this is how I solved seq=FFFFFFFF, if the field not present: for i := range txx.In { txx.In[i].Sequence = 0xffffffff } json.Unmarshal(c[:], &txx) // ... end of the weird solution tx := new(btc.Tx) tx.Version = txx.Ver tx.TxIn = make([]*btc.TxIn, len(txx.In)) for i := range txx.In { tx.TxIn[i] = new(btc.TxIn) tx.TxIn[i].Input.Hash = btc.NewUint256FromString(txx.In[i].Prev_out.Hash).Hash tx.TxIn[i].Input.Vout = txx.In[i].Prev_out.N if txx.In[i].Prev_out.N == 0xffffffff && txx.In[i].Prev_out.Hash == "0000000000000000000000000000000000000000000000000000000000000000" { tx.TxIn[i].ScriptSig, _ = hex.DecodeString(txx.In[i].Coinbase) } else { tx.TxIn[i].ScriptSig, _ = btc.DecodeScript(txx.In[i].ScriptSig) } tx.TxIn[i].Sequence = txx.In[i].Sequence } tx.TxOut = make([]*btc.TxOut, len(txx.Out)) for i := range txx.Out { am, er := btc.StringToSatoshis(txx.Out[i].Value) if er != nil { fmt.Println("Incorrect BTC amount", txx.Out[i].Value, er.Error()) return nil, nil } tx.TxOut[i] = new(btc.TxOut) tx.TxOut[i].Value = am tx.TxOut[i].Pk_script, _ = btc.DecodeScript(txx.Out[i].ScriptPubKey) } tx.Lock_time = txx.Lock_time rawtx := tx.Serialize() if txx.Size != uint(len(rawtx)) { fmt.Printf("Transaction size mismatch: %d expexted, %d decoded\n", txx.Size, len(rawtx)) return nil, rawtx } curid := btc.NewSha2Hash(rawtx) if !curid.Equal(txid) { fmt.Println("The downloaded transaction does not match its ID.", txid.String()) return nil, rawtx } return rawtx, rawtx } else { fmt.Println("json.Unmarshal:", er.Error()) } } else { if er != nil { fmt.Println("http.Get:", er.Error()) } else { fmt.Println("StatusCode=", r.StatusCode) } } return nil, nil }