func (inv *Inventory) Run() { go func() { for { select { case _ = <-inv.closeCh: inv.headers.Close() inv.config.Database.Close() close(inv.doneCh) return case m, ok := <-inv.input: if !ok { log.Fatal("Inventory input channel closed.") } switch m.Type { case "version": inv.handleVersionMessage(m) case "inv": inv.handleInvMessage(m) case "tx": tx, err := messages.ParseTransaction(m.Data) if err != nil { log.Printf("Failed to parse transaction: %v", err) } else { inv.handleTransaction(tx) } case "merkleblock": inv.handleMerkleBlock(m) default: log.Fatalf("Inventory input channel sent message of type %q.", m.Type) } } } }() }
func NewMockInventory() *MockInventory { tx1, _ := hex.DecodeString("0100000001ca9b66c45ab5d802eff79224806a2bdcd690309a4dfcd8650963d3cd606ceadf000000006b4830450220324de09f8f7c8908d4a4b99c13aa28d1a5b3b680a196e6e6d39255e2b4d126c802210089fed6635be470c33f717db1a44f89150ebad18ab511fcf176c959cef7a9c9420121032d9850b19296fe1077f28a3c64d957f5242367dc6d42595b7ca7ca1aaed3dd1dffffffff0250c30000000000001976a914fe2927160e030613c119fbc68bc9ab0576f5911888ace0220200000000001976a9145ee70c467c5b2c35d2d08ef0d6d202b9c917b26188ac00000000") tx2, _ := hex.DecodeString("010000000194db6f49d474a9522dca43d0d83fc243049ed15600b07be4a568a377a590358b000000006a473044022023b2d60ad177429271d5df08a228e43ae3d60b63a236ed271b539dd7a391a6f102204e49ff80f5d6e06274fcac83e99371a7e324ecb08efd631ffb6f6f8c5889189a0121026db91d30b5e4081e02ff952f8d6b4d5e4528bd6617ae02a9abd7a437af0b6c2cffffffff02400d0300000000001976a91466fb2649c17e60c4cd16a05c903a161a24aa11a688acf3005f02000000001976a9143f896aad85d7fd306348c5f650662ed9346b2d6788ac00000000") parsed1, err := messages.ParseTransaction(tx1) if err != nil { panic(fmt.Sprintf("Failed to parse tx: %v", err)) } parsed2, err := messages.ParseTransaction(tx2) if err != nil { panic(fmt.Sprintf("Failed to parse tx: %v", err)) } inv := &MockInventory{ inv: make(map[string]*messages.Transaction), } inv.inv[string(parsed1.Hash())] = parsed1 inv.inv[string(parsed2.Hash())] = parsed2 return inv }
func (t *TxVersion) DeserializeTransaction() error { if len(t.Data) > 0 && t.tx == nil { var err error t.tx, err = messages.ParseTransaction(t.Data) if err != nil { return fmt.Errorf("Failed to parse transaction: %v", err) } } return nil }
func main() { flag.Parse() sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile) var i *inventory.Inventory var n *network.Network var d *network.Dispatcher output := make(chan network.Message) go func() { sig := <-sigs fmt.Println() fmt.Println(sig) t := time.NewTimer(2 * time.Second) go func() { _ = <-t.C log.Println("shut down timed out; forcing exit") os.Exit(2) }() i.Unsubscribe(d) i.Close() n.Close() d.Close() close(output) os.Exit(1) }() n = network.New(network.Config{ DesiredConnections: *connections, PeerStorageFile: *peerFile, SeedHostnames: []string{ "bitseed.xf2.org", "dnsseed.bluematt.me", "seed.bitcoin.sipa.be", "dnsseed.bitcoin.dashjr.org", "seed.bitcoinstats.com", }, OutputChannel: output, }) config := inventory.Config{ // TODO Set database path from parameter. Database: database.Open(), Network: n, } if *walletSeed != "" { seed := mnemonic.SeedFromWordsPassword( strings.Split(*walletSeed, " "), "") key := hdkeys.NewMasterKey(seed) config.Wallet = wallet.New(key) } i = inventory.New(&config) if config.Wallet != nil { config.Wallet.SetTxInventory(i) } d = network.NewDispatcher(output) i.Subscribe(d) d.Run() i.Run() go func() { <-i.Connected() i.GetRecentMerkleBlocks(2000) }() sendPayment := func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") r.ParseForm() if config.Wallet != nil { var transactions []*messages.Transaction txHexes := r.Form["tx"] if len(txHexes) > 0 { for _, txHex := range txHexes { txSer, err := hex.DecodeString(txHex) if err != nil { fmt.Fprintf(w, "Error parsing transaction hex %q: %v", txHex, err) return } tx, err := messages.ParseTransaction(txSer) if err != nil { fmt.Fprintf(w, "Error parsing transaction %x: %v", txSer, err) return } transactions = append(transactions, tx) } } else { addr, prefix, err := base58.BitcoinCheckDecode(r.FormValue("addr")) if err != nil || prefix != base58.BitcoinPublicKeyHashPrefix { fmt.Fprintf(w, "Error parsing addr %q: %v", r.FormValue("addr"), err) return } value, err := strconv.ParseUint(r.FormValue("value"), 10, 64) if err != nil { fmt.Fprintf(w, "Error parsing value %q: %v", r.FormValue("value"), err) return } payment := config.Wallet.NewPayment() err = payment.AddOutput(addr, value) if err != nil { fmt.Fprintf(w, "Error adding output: %v", err) return } err = payment.AddInputsAndFee(10000) if err != nil { fmt.Fprintf(w, "Error adding inputs: %v", err) return } err = payment.Sign() if err != nil { fmt.Fprintf(w, "Error signing transaction: %v", err) return } transactions = payment.Transactions() } if len(transactions) > 1 { fmt.Fprintf(w, "Using duplicate transactions to cope "+ "with duplicate inputs.<br><br>") } for _, tx := range transactions { fmt.Fprintf(w, "<font face=\"courier\">Transaction: %s</font><br>", strings.Replace(tx.String(), "\n", "<br/>", -1)) verified, err := i.VerifyTransaction(tx) if err != nil || !verified { fmt.Fprintf(w, "Error verifying transaction: %v", err) return } fmt.Fprintf(w, "<br><b>Signatures verified!</b><br><br>") } if r.FormValue("send") == "true" { for _, tx := range transactions { n.SendChannel() <- network.Message{ Type: "tx", Data: tx.Data(), } } n.SendChannel() <- network.Message{ Type: "mempool", } } else { str := "<br><br><a href=\"?" for _, tx := range transactions { str += fmt.Sprintf("tx=%x&", tx.Data()) } str += "send=true\">Broadcast transaction(s)</a>" fmt.Fprintf(w, str) } } else { fmt.Fprintf(w, "No wallet configured.") } } http.Handle("/peers", n) http.Handle("/inventory/", i) http.HandleFunc("/send/payment", sendPayment) log.Fatal(http.ListenAndServe(":8080", nil)) }