// jsonRPCRead is the main function that handles reading messages, getting // the data the message requests, and writing the reply. func jsonRPCRead(w http.ResponseWriter, r *http.Request, s *rpcServer) { _ = spew.Dump r.Close = true if atomic.LoadInt32(&s.shutdown) != 0 { return } var rawReply btcjson.Reply body, err := btcjson.GetRaw(r.Body) if err != nil { log.Errorf("[RPCS] Error getting json message: %v", err) return } var message btcjson.Message err = json.Unmarshal(body, &message) if err != nil { log.Errorf("[RPCS] Error unmarshalling json message: %v", err) jsonError := btcjson.Error{ Code: -32700, Message: "Parse error", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: nil, } log.Tracef("[RPCS] reply: %v", rawReply) msg, err := btcjson.MarshallAndSend(rawReply, w) if err != nil { log.Errorf(msg) return } log.Debugf(msg) return } log.Tracef("[RPCS] received: %v", message) // Deal with commands switch message.Method { case "stop": rawReply = btcjson.Reply{ Result: "btcd stopping.", Error: nil, Id: &message.Id, } s.server.Stop() case "getblockcount": _, maxidx, _ := s.server.db.NewestSha() rawReply = btcjson.Reply{ Result: maxidx, Error: nil, Id: &message.Id, } // btcd does not do mining so we can hardcode replies here. case "getgenerate": rawReply = btcjson.Reply{ Result: false, Error: nil, Id: &message.Id, } case "setgenerate": rawReply = btcjson.Reply{ Result: nil, Error: nil, Id: &message.Id, } case "gethashespersec": rawReply = btcjson.Reply{ Result: 0, Error: nil, Id: &message.Id, } case "getblockhash": var f interface{} err = json.Unmarshal(body, &f) m := f.(map[string]interface{}) var idx float64 for _, v := range m { switch vv := v.(type) { case []interface{}: for _, u := range vv { idx, _ = u.(float64) } default: } } sha, err := s.server.db.FetchBlockShaByHeight(int64(idx)) if err != nil { log.Errorf("[RCPS] Error getting block: %v", err) jsonError := btcjson.Error{ Code: -1, Message: "Block number out of range.", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } rawReply = btcjson.Reply{ Result: sha.String(), Error: nil, Id: &message.Id, } case "getblock": var f interface{} err = json.Unmarshal(body, &f) m := f.(map[string]interface{}) var hash string for _, v := range m { switch vv := v.(type) { case []interface{}: for _, u := range vv { hash, _ = u.(string) } default: } } sha, err := btcwire.NewShaHashFromStr(hash) if err != nil { log.Errorf("[RPCS] Error generating sha: %v", err) jsonError := btcjson.Error{ Code: -5, Message: "Block not found", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } blk, err := s.server.db.FetchBlockBySha(sha) if err != nil { log.Errorf("[RPCS] Error fetching sha: %v", err) jsonError := btcjson.Error{ Code: -5, Message: "Block not found", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } idx := blk.Height() buf, err := blk.Bytes() if err != nil { log.Errorf("[RPCS] Error fetching block: %v", err) jsonError := btcjson.Error{ Code: -5, Message: "Block not found", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } txList, _ := blk.TxShas() txNames := make([]string, len(txList)) for i, v := range txList { txNames[i] = v.String() } _, maxidx, err := s.server.db.NewestSha() if err != nil { log.Errorf("[RPCS] Cannot get newest sha: %v", err) return } blockHeader := &blk.MsgBlock().Header blockReply := btcjson.BlockResult{ Hash: hash, Version: blockHeader.Version, MerkleRoot: blockHeader.MerkleRoot.String(), PreviousHash: blockHeader.PrevBlock.String(), Nonce: blockHeader.Nonce, Time: blockHeader.Timestamp.Unix(), Confirmations: uint64(1 + maxidx - idx), Height: idx, Tx: txNames, Size: len(buf), Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), Difficulty: getDifficultyRatio(blockHeader.Bits), } // Get next block unless we are already at the top. if idx < maxidx { shaNext, err := s.server.db.FetchBlockShaByHeight(int64(idx + 1)) if err != nil { log.Errorf("[RPCS] No next block: %v", err) } else { blockReply.NextHash = shaNext.String() } } rawReply = btcjson.Reply{ Result: blockReply, Error: nil, Id: &message.Id, } case "getrawtransaction": var f interface{} err = json.Unmarshal(body, &f) m := f.(map[string]interface{}) var tx string var verbose float64 for _, v := range m { switch vv := v.(type) { case []interface{}: for _, u := range vv { switch uu := u.(type) { case string: tx = uu case float64: verbose = uu default: } } default: } } if int(verbose) != 1 { // Don't return details // not used yet } else { txSha, _ := btcwire.NewShaHashFromStr(tx) var txS *btcwire.MsgTx txList, err := s.server.db.FetchTxBySha(txSha) if err != nil { log.Errorf("[RPCS] Error fetching tx: %v", err) jsonError := btcjson.Error{ Code: -5, Message: "No information available about transaction", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } lastTx := len(txList) - 1 txS = txList[lastTx].Tx blksha := txList[lastTx].BlkSha blk, err := s.server.db.FetchBlockBySha(blksha) if err != nil { log.Errorf("[RPCS] Error fetching sha: %v", err) jsonError := btcjson.Error{ Code: -5, Message: "Block not found", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } idx := blk.Height() txOutList := txS.TxOut voutList := make([]btcjson.Vout, len(txOutList)) txInList := txS.TxIn vinList := make([]btcjson.Vin, len(txInList)) for i, v := range txInList { vinList[i].Sequence = float64(v.Sequence) disbuf, _ := btcscript.DisasmString(v.SignatureScript) vinList[i].ScriptSig.Asm = strings.Replace(disbuf, " ", "", -1) vinList[i].Vout = i + 1 log.Debugf(disbuf) } for i, v := range txOutList { voutList[i].N = i voutList[i].Value = float64(v.Value) / 100000000 isbuf, _ := btcscript.DisasmString(v.PkScript) voutList[i].ScriptPubKey.Asm = isbuf voutList[i].ScriptPubKey.ReqSig = strings.Count(isbuf, "OP_CHECKSIG") _, addr, err := btcscript.ScriptToAddress(v.PkScript) if err != nil { log.Errorf("[RPCS] Error getting address for %v: %v", txSha, err) } else { addrList := make([]string, 1) addrList[0] = addr voutList[i].ScriptPubKey.Addresses = addrList } } _, maxidx, err := s.server.db.NewestSha() if err != nil { log.Errorf("[RPCS] Cannot get newest sha: %v", err) return } confirmations := uint64(1 + maxidx - idx) blockHeader := &blk.MsgBlock().Header txReply := btcjson.TxRawResult{ Txid: tx, Vout: voutList, Vin: vinList, Version: txS.Version, LockTime: txS.LockTime, // This is not a typo, they are identical in // bitcoind as well. Time: blockHeader.Timestamp.Unix(), Blocktime: blockHeader.Timestamp.Unix(), BlockHash: blksha.String(), Confirmations: confirmations, } rawReply = btcjson.Reply{ Result: txReply, Error: nil, Id: &message.Id, } } case "decoderawtransaction": var f interface{} err = json.Unmarshal(body, &f) m := f.(map[string]interface{}) var hash string for _, v := range m { switch vv := v.(type) { case []interface{}: for _, u := range vv { hash, _ = u.(string) } default: } } spew.Dump(hash) txReply := btcjson.TxRawDecodeResult{} rawReply = btcjson.Reply{ Result: txReply, Error: nil, Id: &message.Id, } default: jsonError := btcjson.Error{ Code: -32601, Message: "Method not found", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } } msg, err := btcjson.MarshallAndSend(rawReply, w) if err != nil { log.Errorf(msg) return } log.Debugf(msg) return }
// handleGetBlock implements the getblock command. func handleGetBlock(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) { c := cmd.(*btcjson.GetBlockCmd) sha, err := btcwire.NewShaHashFromStr(c.Hash) if err != nil { rpcsLog.Errorf("Error generating sha: %v", err) return nil, btcjson.ErrBlockNotFound } blk, err := s.server.db.FetchBlockBySha(sha) if err != nil { rpcsLog.Errorf("Error fetching sha: %v", err) return nil, btcjson.ErrBlockNotFound } // When the verbose flag isn't set, simply return the network-serialized // block as a hex-encoded string. if !c.Verbose { blkHex, err := messageToHex(blk.MsgBlock()) if err != nil { return nil, err } return blkHex, nil } // The verbose flag is set, so generate the JSON object and return it. buf, err := blk.Bytes() if err != nil { rpcsLog.Errorf("Error fetching block: %v", err) return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } idx := blk.Height() _, maxidx, err := s.server.db.NewestSha() if err != nil { rpcsLog.Errorf("Cannot get newest sha: %v", err) return nil, btcjson.ErrBlockNotFound } blockHeader := &blk.MsgBlock().Header blockReply := btcjson.BlockResult{ Hash: c.Hash, Version: blockHeader.Version, MerkleRoot: blockHeader.MerkleRoot.String(), PreviousHash: blockHeader.PrevBlock.String(), Nonce: blockHeader.Nonce, Time: blockHeader.Timestamp.Unix(), Confirmations: uint64(1 + maxidx - idx), Height: idx, Size: len(buf), Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), Difficulty: getDifficultyRatio(blockHeader.Bits), } if !c.VerboseTx { txList, _ := blk.TxShas() txNames := make([]string, len(txList)) for i, v := range txList { txNames[i] = v.String() } blockReply.Tx = txNames } else { txns := blk.Transactions() rawTxns := make([]btcjson.TxRawResult, len(txns)) for i, tx := range txns { txSha := tx.Sha().String() mtx := tx.MsgTx() rawTxn, err := createTxRawResult(s.server.btcnet, txSha, mtx, blk, maxidx, sha) if err != nil { rpcsLog.Errorf("Cannot create TxRawResult for "+ "transaction %s: %v", txSha, err) return nil, err } rawTxns[i] = *rawTxn } blockReply.RawTx = rawTxns } // Get next block unless we are already at the top. if idx < maxidx { var shaNext *btcwire.ShaHash shaNext, err = s.server.db.FetchBlockShaByHeight(int64(idx + 1)) if err != nil { rpcsLog.Errorf("No next block: %v", err) return nil, btcjson.ErrBlockNotFound } blockReply.NextHash = shaNext.String() } return blockReply, nil }
// handleGetBlock implements the getblock command. func handleGetBlock(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) { c := cmd.(*btcjson.GetBlockCmd) sha, err := btcwire.NewShaHashFromStr(c.Hash) if err != nil { log.Errorf("RPCS: Error generating sha: %v", err) return nil, btcjson.ErrBlockNotFound } blk, err := s.server.db.FetchBlockBySha(sha) if err != nil { log.Errorf("RPCS: Error fetching sha: %v", err) return nil, btcjson.ErrBlockNotFound } idx := blk.Height() buf, err := blk.Bytes() if err != nil { log.Errorf("RPCS: Error fetching block: %v", err) return nil, btcjson.ErrBlockNotFound } txList, _ := blk.TxShas() txNames := make([]string, len(txList)) for i, v := range txList { txNames[i] = v.String() } _, maxidx, err := s.server.db.NewestSha() if err != nil { log.Errorf("RPCS: Cannot get newest sha: %v", err) return nil, btcjson.ErrBlockNotFound } blockHeader := &blk.MsgBlock().Header blockReply := btcjson.BlockResult{ Hash: c.Hash, Version: blockHeader.Version, MerkleRoot: blockHeader.MerkleRoot.String(), PreviousHash: blockHeader.PrevBlock.String(), Nonce: blockHeader.Nonce, Time: blockHeader.Timestamp.Unix(), Confirmations: uint64(1 + maxidx - idx), Height: idx, Tx: txNames, Size: len(buf), Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), Difficulty: getDifficultyRatio(blockHeader.Bits), } // Get next block unless we are already at the top. if idx < maxidx { var shaNext *btcwire.ShaHash shaNext, err = s.server.db.FetchBlockShaByHeight(int64(idx + 1)) if err != nil { log.Errorf("RPCS: No next block: %v", err) return nil, btcjson.ErrBlockNotFound } blockReply.NextHash = shaNext.String() } return blockReply, nil }