// jsonRPCRead is the RPC wrapper around the jsonRead function to handle reading // and responding to RPC messages. func jsonRPCRead(w http.ResponseWriter, r *http.Request, s *rpcServer) { if atomic.LoadInt32(&s.shutdown) != 0 { return } body, err := btcjson.GetRaw(r.Body) if err != nil { rpcsLog.Errorf("Error getting json message: %v", err) return } var reply btcjson.Reply cmd, jsonErr := parseCmd(body) if cmd != nil { // Unmarshaling at least a valid JSON-RPC message succeeded. // Use the provided id for errors. id := cmd.Id() reply.Id = &id } if jsonErr != nil { reply.Error = jsonErr } else { reply = standardCmdReply(cmd, s) } rpcsLog.Tracef("reply: %v", reply) msg, err := btcjson.MarshallAndSend(reply, w) if err != nil { rpcsLog.Errorf(msg) return } rpcsLog.Debugf(msg) }
// jsonRPCRead is the RPC wrapper around the jsonRead function to handles // reading and responding to RPC messages. func jsonRPCRead(w http.ResponseWriter, r *http.Request, s *rpcServer) { r.Close = true if atomic.LoadInt32(&s.shutdown) != 0 { return } body, err := btcjson.GetRaw(r.Body) if err != nil { log.Errorf("RPCS: Error getting json message: %v", err) return } // Error is intentionally ignored here. It's used in in the // websocket handler to tell when a method is not supported by // the standard RPC API, and is not needed here. Error logging // is done inside jsonRead, so no need to log the error here. reply, _ := jsonRead(body, s, nil) log.Tracef("[RPCS] reply: %v", reply) msg, err := btcjson.MarshallAndSend(reply, w) if err != nil { log.Errorf(msg) return } log.Debugf(msg) }
// TestMarshallAndSend tests the MarshallAndSend function to make sure it can // create a json message to write to the io.Writerr and to make sure // it fails properly in cases where it cannot generate json. func TestMarshallAndSend(t *testing.T) { jsonError := btcjson.Error{ Code: -32700, Message: "Parse error", } // json.Marshal cannot handle channels so this is a good way to get a // marshal failure. badRes := make(chan interface{}) rawReply := btcjson.Reply{ Result: badRes, Error: &jsonError, Id: nil, } var w bytes.Buffer msg, err := btcjson.MarshallAndSend(rawReply, &w) if fmt.Sprintf("%s", err) != "json: unsupported type: chan interface {}" { t.Error("Should not be able to unmarshall channel") } // Use something simple so we can compare the reply. rawReply = btcjson.Reply{ Result: nil, Error: nil, Id: nil, } msg, err = btcjson.MarshallAndSend(rawReply, &w) if msg != "[RPCS] reply: {<nil> <nil> <nil>}" { t.Error("Incorrect reply:", msg) } expBuf := "{\"result\":null,\"error\":null,\"id\":null}\n" if w.String() != expBuf { t.Error("Incorrect data in buffer:", w.String()) } return }
// 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 }