func (getter *HeaderGetter) handleHeaders(m network.Message) bool { _, oldHeight := getter.highestKnownBlock() headers, err := messages.ParseHeaders(m.Data) if err != nil { log.Printf("Failed to parse headers: %v", err) return true } log.Printf("Received %d headers from %s.\n", len(headers), m.Endpoint) var hash []byte var height int var incomplete bool var vector messages.InventoryVector for _, h := range headers { prevHeight, err := getter.getBlockHeight(h.PrevBlock) if err != nil { log.Printf("Received bad header %x from %s.", utils.ReverseBytes(h.Hash()), m.Endpoint) // TODO handle this getter.inv.reportMisbehaviour(m.Endpoint, 20, "Sent bad block header.") incomplete = true break } height = prevHeight + 1 h.Height = height hash = h.Hash() getter.db.Set(hash, h) // Request merkleblocks for headers. vector = append(vector, &messages.Inventory{ Type: messages.TypeMsgFilteredBlock, Hash: hash, }) } if len(hash) != 0 && height > oldHeight { getter.setHighestKnownBlock(hash, height) log.Printf("Last header updated to %x (internal byte order), height is %d.", utils.ReverseBytes(hash), height) if height < oldHeight+len(vector) { // TODO not all blocks are new, consider changing endpoint. } } else { // TODO If we haven't caught up yet, this means the endpoint is // misbehaving. } if len(headers) >= 2000 { incomplete = true } else { // TODO question if endpoint is good. } err = getter.inv.SendGetData(vector, m.Endpoint) if err != nil { return true } return incomplete }
func (getter *HeaderGetter) formGetHeaderMessage() network.Message { hash, height := getter.highestKnownBlock() msg := messages.GetBlocks{ Version: 70001, HashStop: make([]byte, 32), Locators: [][]byte{hash}, } loc := hash // Add 20 recent known headers with larger and larger gaps between them // as they get less recent (up to a step of 40 between the two least // recent ones.) for i := 0; i < 20 && loc != nil; i++ { for j := 0; j < i*2; j++ { header, err := getter.loadHeader(loc) if err != nil { log.Printf("Failed to get recent header, %d steps back: %v", i, err) loc = nil break } loc = header.PrevBlock } if loc != nil { msg.Locators = append(msg.Locators, loc) } } var msgBytes bytes.Buffer msg.Serialize(&msgBytes) log.Printf("Getting more headers, starting from %x at height %d.\n", utils.ReverseBytes(hash), height) return network.Message{ Type: "getheaders", Data: msgBytes.Bytes(), } }
func (inv *Inventory) handleMerkleBlock(m network.Message) { block, err := messages.ParseMerkleBlock(m.Data) if err != nil { log.Printf("Failed to parse merkleblock: %v", err) return } if len(block.Flags) > 1 || block.Flags[0] != 0x00 { log.Printf("Merkleblock %x flags %x", utils.ReverseBytes(block.Hash()), block.Flags) root, err := NewMerkleTree(block.TotalTXs, block.Hashes, block.Flags) if err != nil || !bytes.Equal(root.Hash, block.MerkleRoot) { log.Printf("Merkle hash mismatch!") for _, h := range block.Hashes { log.Printf("Input hashes: %x", h) } log.Printf("Calculated merkle hash %x, "+ "expected %x. %d matched transactions.", root.Hash, block.MerkleRoot, len(root.MatchedTransactions())) // TODO handle this without panicking. panic(fmt.Sprintf("TotalTXs %d, Flags %x", block.TotalTXs, block.Flags)) } height, err := inv.headers.getBlockHeight(block.PrevBlock) if err != nil { // TODO This indicates that we're not caught up yet, // which can be useful information if we're receiving // short header lists from a bad node. // TODO Send a getheaders request. log.Printf("Couldn't figure out height of block: %v", err) } else { // Only store block if we known the height. height += 1 block.Height = height inv.config.Database.Set(block.Hash(), block) log.Printf("Received Merkleheaders for block %d", height) _, highest := inv.headers.highestKnownBlock() if height > highest { inv.headers.setHighestKnownBlock(block.Hash(), height) log.Printf("Updated highest known block.") } } // Store transactions even if we couldn't get correct height. // TODO Update height value once we figure it out. short := Block{ Hash: block.Hash(), Timestamp: time.Unix(int64(block.Timestamp), 0), Height: int32(height), } // Attach block information to transactions for _, tx := range root.MatchedTransactions() { // TODO Check against local filter. inv.addTxBlock(tx, &short) } } }
func (t TxVersion) String() string { str := fmt.Sprintf("Transaction %x", utils.ReverseBytes(t.Hash)) if t.tx != nil { str += fmt.Sprintf(" fingerprint %x", t.Id()) } if t.Block != nil { str += fmt.Sprintf(" in block %x (height %d)", t.Block.Hash, t.Block.Height) } return str }
func (getter *HeaderGetter) getBlockHeight(hash []byte) (int, error) { prev, err := getter.loadHeader(hash) if err != nil { block0, _ := hex.DecodeString( "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f") if bytes.Equal(hash, utils.ReverseBytes(block0)) { return 0, nil } return -1, fmt.Errorf("Couldn't find prev block.") } return int(prev.Height), nil }
func (a *Account) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") a.UpdateBalances() fmt.Fprintf(w, "\n<p>Receive addresses:</p>\n") for i, name := range a.recvAddrList { addr := a.addrMap[name] encoded, _ := base58.BitcoinCheckEncode( base58.BitcoinPublicKeyHashPrefix, []byte(name)) // TODO get account numnber right. fmt.Fprintf(w, "m/44'/0'/0'/0/%d: %s, balance: %d\n<br>", i, encoded, addr.Balance()) for _, s := range addr.spendables { fmt.Fprintf(w, "Spendable output from tx %x (fingerprint):<br>", s.outputs[0].TxFingerprint()) for _, o := range s.outputs { fmt.Fprintf(w, "tx %x (hash, internal byte order) (index %d): "+ "%d satoshi\n<br>", utils.ReverseBytes(o.TxHash()), o.Index(), o.Value) } } } fmt.Fprintf(w, "\n<p>Change addresses:</p>\n") for i, name := range a.changeAddrList { addr := a.addrMap[name] encoded, _ := base58.BitcoinCheckEncode( base58.BitcoinPublicKeyHashPrefix, []byte(name)) // TODO get account numnber right. fmt.Fprintf(w, "m/44'/0'/0'/1/%d: %s, balance: %d\n<br>", i, encoded, addr.Balance()) for _, s := range addr.spendables { fmt.Fprintf(w, "Spendable output from tx %x (fingerprint):<br>", s.outputs[0].TxFingerprint()) for _, o := range s.outputs { fmt.Fprintf(w, "tx %x (hash, internal byte order) (index %d): "+ "%d satoshi\n<br>", utils.ReverseBytes(o.TxHash()), o.Index(), o.Value) } } } }
func (b *Block) String() string { str := fmt.Sprintf("%x, version %d\n", utils.ReverseBytes(b.Hash), b.Version) str += fmt.Sprintf("PrevBlock: %x\n", b.PrevBlock) str += fmt.Sprintf("MerkleRoot: %x\n", b.MerkleRoot) str += fmt.Sprintf("Timestamp: %s (%d)\n", time.Unix(int64(b.Timestamp), 0), b.Timestamp) str += fmt.Sprintf("Bits: 0x%x\n", b.Bits) str += fmt.Sprintf("Transactions (%d):\n", len(b.Transactions)) for i, tx := range b.Transactions { str += fmt.Sprintf("TX %d: %s\n", i, tx) } return str }
func (t *Transaction) String() string { str := fmt.Sprintf("%x, version %d\n\n", utils.ReverseBytes(t.Hash()), t.Version) for _, i := range t.Inputs { str += fmt.Sprintf("%s\n\n", i) } for _, o := range t.Outputs { str += fmt.Sprintf("%s\n", o) } str += fmt.Sprintf("\n") if t.LockTime == 0 { str += fmt.Sprintf("Unlocked.") } else { str += fmt.Sprintf("Locktime %d.", t.LockTime) } return str }
func (t *Transaction) MatchesFilter(filter *FilterLoad) bool { if filter.MayContain(t.Hash()) { return true } if filter.MayContain(utils.ReverseBytes(t.Hash())) { log.Printf("Tx matched reverse hash: %x", t.Hash) return true } for _, o := range t.Outputs { if o.MatchesFilter(filter) { return true } } for _, i := range t.Inputs { if i.MatchesFilter(filter) { return true } } return false }
func (t *Transaction) HashInternal() string { r := utils.ReverseBytes(t.Hash()) return fmt.Sprintf("%x (internal byte order)", r) }
func (p OutPoint) String() string { return fmt.Sprintf("%x (output index %d)", utils.ReverseBytes(p.Hash), p.Index) }
// UpdateTxOutputs updates the list of unspent transaction outputs Inventory is // watching. It also alerts attached wallets about new transaction to aid their // discovery process. func (inv *Inventory) UpdateTxOutputs(version *TxVersion) error { // TODO protect against races. tx := version.tx if tx == nil { return fmt.Errorf("Input has no transaction associated with it.") } id := tx.Fingerprint() // Check if new transaction spends one of our unspent outputs. for _, i := range tx.Inputs { prev := i.PreviousOutput outputId := Fingerprint(prev.Hash, prev.Index) out, found := inv.outputs[string(outputId)] if found { // log.Printf("Output %d of tx %x spent by input %d of tx %x.", // prev.Index, out.TxHash(), index, tx.Hash()) out.spentBy = tx } } var outputs []*TxOutput // Check if new transaction pays any of our addresses. for index, o := range tx.Outputs { addr := o.AddressHash() if addr != nil { if inv.filter.Match(addr) { // This is an exact match, but not just for // public address hashes. Either way, it's // highly likely that this is an output that we // can spend, so add it to the list and let the // wallets filter out the ones they can spend. encoded, _ := base58.BitcoinCheckEncode( base58.BitcoinPublicKeyHashPrefix, addr) output := TxOutput{ index: uint32(index), output: o, tx: version, id: id, } log.Printf("Transaction hash %x pays address %s with output %x", utils.ReverseBytes(tx.Hash()), encoded, output.Fingerprint()) inv.outputs[string(output.Fingerprint())] = &output outputs = append(outputs, &output) if inv.config.Wallet != nil { inv.config.Wallet.MarkAddressAsUsed(addr) } } } } // Check if we already know a transaction that spends the new outputs. for _, o := range outputs { for _, version := range inv.txHashes { t := version.tx if t == nil { continue } for _, i := range t.Inputs { prev := i.PreviousOutput if bytes.Equal(prev.Hash, o.TxHash()) && prev.Index == o.index { // log.Printf("Output %x(%d) already spent by input %x(%d).", // o.TxHash(), prev.Index, t.Hash(), index) o.spentBy = t } } } } return nil }