// Copyright (c) 2013 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package main import ( "bytes" "crypto/subtle" "crypto/tls" "encoding/base64" "encoding/binary" "encoding/hex" "errors" "fmt" "io" "io/ioutil" "math/big" "math/rand" "net" "net/http" "os" "strconv" "sync" "sync/atomic" "time" "github.com/conformal/btcchain" "github.com/conformal/btcdb" "github.com/conformal/btcjson" "github.com/conformal/btcnet" "github.com/conformal/btcscript" "github.com/conformal/btcutil" "github.com/conformal/btcwire" "github.com/conformal/btcws" "github.com/conformal/fastsha256" "github.com/conformal/websocket" ) const ( // rpcAuthTimeoutSeconds is the number of seconds a connection to the // RPC server is allowed to stay open without authenticating before it // is closed. rpcAuthTimeoutSeconds = 10 // uint256Size is the number of bytes needed to represent an unsigned // 256-bit integer. uint256Size = 32 // getworkDataLen is the length of the data field of the getwork RPC. // It consists of the serialized block header plus the internal sha256 // padding. The internal sha256 padding consists of a single 1 bit // followed by enough zeros to pad the message out to 56 bytes followed // by length of the message in bits encoded as a big-endian uint64 // (8 bytes). Thus, the resulting length is a multiple of the sha256 // block size (64 bytes). getworkDataLen = (1 + ((btcwire.MaxBlockHeaderPayload + 8) / fastsha256.BlockSize)) * fastsha256.BlockSize // hash1Len is the length of the hash1 field of the getwork RPC. It // consists of a zero hash plus the internal sha256 padding. See // the getworkDataLen comment for details about the internal sha256 // padding format. hash1Len = (1 + ((btcwire.HashSize + 8) / fastsha256.BlockSize)) * fastsha256.BlockSize ) // Errors var ( // ErrBadParamsField describes an error where the parameters JSON // field cannot be properly parsed. ErrBadParamsField = errors.New("bad params field") ) type commandHandler func(*rpcServer, btcjson.Cmd, <-chan struct{}) (interface{}, error) // handlers maps RPC command strings to appropriate handler functions. // this is copied by init because help references rpcHandlers and thus causes // a dependancy loop. var rpcHandlers map[string]commandHandler var rpcHandlersBeforeInit = map[string]commandHandler{ "addnode": handleAddNode, "createrawtransaction": handleCreateRawTransaction, "debuglevel": handleDebugLevel, "decoderawtransaction": handleDecodeRawTransaction, "decodescript": handleDecodeScript, "estimatefee": handleUnimplemented, "estimatepriority": handleUnimplemented, "getaddednodeinfo": handleGetAddedNodeInfo, "getbestblock": handleGetBestBlock, "getbestblockhash": handleGetBestBlockHash, "getblock": handleGetBlock, "getblockchaininfo": handleUnimplemented, "getblockcount": handleGetBlockCount, "getblockhash": handleGetBlockHash, "getblocktemplate": handleUnimplemented, "getconnectioncount": handleGetConnectionCount, "getcurrentnet": handleGetCurrentNet, "getdifficulty": handleGetDifficulty, "getgenerate": handleGetGenerate, "gethashespersec": handleGetHashesPerSec, "getinfo": handleGetInfo, "getmininginfo": handleGetMiningInfo, "getnettotals": handleGetNetTotals, "getnetworkhashps": handleGetNetworkHashPS, "getnetworkinfo": handleUnimplemented, "getpeerinfo": handleGetPeerInfo, "getrawmempool": handleGetRawMempool, "getrawtransaction": handleGetRawTransaction, "getwork": handleGetWork, "help": handleHelp, "ping": handlePing, "sendrawtransaction": handleSendRawTransaction, "setgenerate": handleSetGenerate, "stop": handleStop, "submitblock": handleSubmitBlock, "validateaddress": handleValidateAddress, "verifychain": handleVerifyChain, } func init() { rpcHandlers = rpcHandlersBeforeInit } // list of commands that we recognise, but for which btcd has no support because // it lacks support for wallet functionality. For these commands the user // should ask a connected instance of btcwallet. var rpcAskWallet = map[string]struct{}{ "addmultisigaddress": struct{}{}, "backupwallet": struct{}{}, "createencryptedwallet": struct{}{}, "createmultisig": struct{}{}, "dumpprivkey": struct{}{}, "dumpwallet": struct{}{}, "encryptwallet": struct{}{}, "getaccount": struct{}{}, "getaccountaddress": struct{}{}, "getaddressesbyaccount": struct{}{}, "getbalance": struct{}{}, "getnewaddress": struct{}{}, "getrawchangeaddress": struct{}{}, "getreceivedbyaccount": struct{}{}, "getreceivedbyaddress": struct{}{}, "gettransaction": struct{}{}, "gettxout": struct{}{}, "gettxoutsetinfo": struct{}{}, "getunconfirmedbalance": struct{}{}, "getwalletinfo": struct{}{}, "importprivkey": struct{}{}, "importwallet": struct{}{}, "keypoolrefill": struct{}{}, "listaccounts": struct{}{}, "listaddressgroupings": struct{}{}, "listlockunspent": struct{}{}, "listreceivedbyaccount": struct{}{}, "listreceivedbyaddress": struct{}{}, "listsinceblock": struct{}{}, "listtransactions": struct{}{}, "listunspent": struct{}{}, "lockunspent": struct{}{}, "move": struct{}{}, "sendfrom": struct{}{}, "sendmany": struct{}{}, "sendtoaddress": struct{}{}, "setaccount": struct{}{}, "settxfee": struct{}{}, "signmessage": struct{}{}, "signrawtransaction": struct{}{}, "verifymessage": struct{}{}, "walletlock": struct{}{}, "walletpassphrase": struct{}{}, "walletpassphrasechange": struct{}{}, } // Commands that are temporarily unimplemented. var rpcUnimplemented = map[string]struct{}{} // workStateBlockInfo houses information about how to reconstruct a block given // its template and signature script. type workStateBlockInfo struct { msgBlock *btcwire.MsgBlock signatureScript []byte } // workState houses state that is used in between multiple RPC invocations to // getwork. type workState struct { sync.Mutex lastTxUpdate time.Time lastGenerated time.Time prevHash *btcwire.ShaHash msgBlock *btcwire.MsgBlock extraNonce uint64 blockInfo map[btcwire.ShaHash]*workStateBlockInfo } // newWorkState returns a new instance of a workState with all internal fields // initialized and ready to use. func newWorkState() *workState { return &workState{ blockInfo: make(map[btcwire.ShaHash]*workStateBlockInfo), } } // rpcServer holds the items the rpc server may need to access (config, // shutdown, main server, etc.) type rpcServer struct { started int32 shutdown int32 server *server authsha [fastsha256.Size]byte ntfnMgr *wsNotificationManager numClients int numClientsMutex sync.Mutex statusLines map[int]string statusLock sync.RWMutex wg sync.WaitGroup listeners []net.Listener workState *workState quit chan int } // Start is used by server.go to start the rpc listener. func (s *rpcServer) Start() { if atomic.AddInt32(&s.started, 1) != 1 { return } rpcsLog.Trace("Starting RPC server") rpcServeMux := http.NewServeMux() httpServer := &http.Server{ Handler: rpcServeMux, // Timeout connections which don't complete the initial // handshake within the allowed timeframe. ReadTimeout: time.Second * rpcAuthTimeoutSeconds, } rpcServeMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Connection", "close") w.Header().Set("Content-Type", "application/json") r.Close = true // Limit the number of connections to max allowed. if s.limitConnections(w, r.RemoteAddr) { return } // Keep track of the number of connected clients. s.incrementClients() defer s.decrementClients() if _, err := s.checkAuth(r, true); err != nil { jsonAuthFail(w, r, s) return } jsonRPCRead(w, r, s) }) // Websocket endpoint. rpcServeMux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { authenticated, err := s.checkAuth(r, false) if err != nil { http.Error(w, "401 Unauthorized.", http.StatusUnauthorized) return } // Attempt to upgrade the connection to a websocket connection // using the default size for read/write buffers. ws, err := websocket.Upgrade(w, r, nil, 0, 0) if err != nil { if _, ok := err.(websocket.HandshakeError); !ok { rpcsLog.Errorf("Unexpected websocket error: %v", err) } return } s.WebsocketHandler(ws, r.RemoteAddr, authenticated) }) for _, listener := range s.listeners { s.wg.Add(1) go func(listener net.Listener) { rpcsLog.Infof("RPC server listening on %s", listener.Addr()) httpServer.Serve(listener) rpcsLog.Tracef("RPC listener done for %s", listener.Addr()) s.wg.Done() }(listener) } s.ntfnMgr.Start() } // httpStatusLine returns a response Status-Line (RFC 2616 Section 6.1) // for the given request and response status code. This function was lifted and // adapted from the standard library HTTP server code since it's not exported. func (s *rpcServer) httpStatusLine(req *http.Request, code int) string { // Fast path: key := code proto11 := req.ProtoAtLeast(1, 1) if !proto11 { key = -key } s.statusLock.RLock() line, ok := s.statusLines[key] s.statusLock.RUnlock() if ok { return line } // Slow path: proto := "HTTP/1.0" if proto11 { proto = "HTTP/1.1" } codeStr := strconv.Itoa(code) text := http.StatusText(code) if text != "" { line = proto + " " + codeStr + " " + text + "\r\n" s.statusLock.Lock() s.statusLines[key] = line s.statusLock.Unlock() } else { text = "status code " + codeStr line = proto + " " + codeStr + " " + text + "\r\n" } return line } // writeHTTPResponseHeaders writes the necessary response headers prior to // writing an HTTP body given a request to use for protocol negotiation, headers // to write, a status code, and a writer. func (s *rpcServer) writeHTTPResponseHeaders(req *http.Request, headers http.Header, code int, w io.Writer) error { _, err := io.WriteString(w, s.httpStatusLine(req, code)) if err != nil { return err } err = headers.Write(w) if err != nil { return err } _, err = io.WriteString(w, "\r\n") if err != nil { return err } return nil } // limitConnections responds with a 503 service unavailable and returns true if // adding another client would exceed the maximum allow RPC clients. // // This function is safe for concurrent access. func (s *rpcServer) limitConnections(w http.ResponseWriter, remoteAddr string) bool { s.numClientsMutex.Lock() defer s.numClientsMutex.Unlock() if s.numClients+1 > cfg.RPCMaxClients { rpcsLog.Infof("Max RPC clients exceeded [%d] - "+ "disconnecting client %s", cfg.RPCMaxClients, remoteAddr) http.Error(w, "503 Too busy. Try again later.", http.StatusServiceUnavailable) return true } return false } // incrementClients adds one to the number of connected RPC clients. Note // this only applies to standard clients. Websocket clients have their own // limits and are tracked separately. // // This function is safe for concurrent access. func (s *rpcServer) incrementClients() { s.numClientsMutex.Lock() defer s.numClientsMutex.Unlock() s.numClients++ } // decrementClients subtracts one from the number of connected RPC clients. // Note this only applies to standard clients. Websocket clients have their own // limits and are tracked separately. // // This function is safe for concurrent access. func (s *rpcServer) decrementClients() { s.numClientsMutex.Lock() defer s.numClientsMutex.Unlock() s.numClients-- } // checkAuth checks the HTTP Basic authentication supplied by a wallet // or RPC client in the HTTP request r. If the supplied authentication // does not match the username and password expected, a non-nil error is // returned. // // This check is time-constant. func (s *rpcServer) checkAuth(r *http.Request, require bool) (bool, error) { authhdr := r.Header["Authorization"] if len(authhdr) <= 0 { if require { rpcsLog.Warnf("RPC authentication failure from %s", r.RemoteAddr) return false, errors.New("auth failure") } return false, nil } authsha := fastsha256.Sum256([]byte(authhdr[0])) cmp := subtle.ConstantTimeCompare(authsha[:], s.authsha[:]) if cmp != 1 { rpcsLog.Warnf("RPC authentication failure from %s", r.RemoteAddr) return false, errors.New("auth failure") } return true, nil } // Stop is used by server.go to stop the rpc listener. func (s *rpcServer) Stop() error { if atomic.AddInt32(&s.shutdown, 1) != 1 { rpcsLog.Infof("RPC server is already in the process of shutting down") return nil } rpcsLog.Warnf("RPC server shutting down") for _, listener := range s.listeners { err := listener.Close() if err != nil { rpcsLog.Errorf("Problem shutting down rpc: %v", err) return err } } s.ntfnMgr.Shutdown() s.ntfnMgr.WaitForShutdown() close(s.quit) s.wg.Wait() rpcsLog.Infof("RPC server shutdown complete") return nil } // genCertPair generates a key/cert pair to the paths provided. func genCertPair(certFile, keyFile string) error { rpcsLog.Infof("Generating TLS certificates...") org := "btcd autogenerated cert" validUntil := time.Now().Add(10 * 365 * 24 * time.Hour) cert, key, err := btcutil.NewTLSCertPair(org, validUntil, nil) if err != nil { return err } // Write cert and key files. if err = ioutil.WriteFile(certFile, cert, 0666); err != nil { return err } if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { os.Remove(certFile) return err } rpcsLog.Infof("Done generating TLS certificates") return nil } // newRPCServer returns a new instance of the rpcServer struct. func newRPCServer(listenAddrs []string, s *server) (*rpcServer, error) { login := cfg.RPCUser + ":" + cfg.RPCPass auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) rpc := rpcServer{ authsha: fastsha256.Sum256([]byte(auth)), server: s, statusLines: make(map[int]string), workState: newWorkState(), quit: make(chan int), } rpc.ntfnMgr = newWsNotificationManager(&rpc) // check for existence of cert file and key file if !fileExists(cfg.RPCKey) && !fileExists(cfg.RPCCert) { // if both files do not exist, we generate them. err := genCertPair(cfg.RPCCert, cfg.RPCKey) if err != nil { return nil, err } } keypair, err := tls.LoadX509KeyPair(cfg.RPCCert, cfg.RPCKey) if err != nil { return nil, err } tlsConfig := tls.Config{ Certificates: []tls.Certificate{keypair}, } // TODO(oga) this code is similar to that in server, should be // factored into something shared. ipv4ListenAddrs, ipv6ListenAddrs, _, err := parseListeners(listenAddrs) if err != nil { return nil, err } listeners := make([]net.Listener, 0, len(ipv6ListenAddrs)+len(ipv4ListenAddrs)) for _, addr := range ipv4ListenAddrs { listener, err := tls.Listen("tcp4", addr, &tlsConfig) if err != nil { rpcsLog.Warnf("Can't listen on %s: %v", addr, err) continue } listeners = append(listeners, listener) } for _, addr := range ipv6ListenAddrs { listener, err := tls.Listen("tcp6", addr, &tlsConfig) if err != nil { rpcsLog.Warnf("Can't listen on %s: %v", addr, err) continue } listeners = append(listeners, listener) } if len(listeners) == 0 { return nil, errors.New("RPCS: No valid listen address") } rpc.listeners = listeners return &rpc, nil } // jsonAuthFail sends a message back to the client if the http auth is rejected. func jsonAuthFail(w http.ResponseWriter, r *http.Request, s *rpcServer) { w.Header().Add("WWW-Authenticate", `Basic realm="btcd RPC"`) http.Error(w, "401 Unauthorized.", http.StatusUnauthorized) } // 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 } // Unfortunately, the http server doesn't provide the ability to // change the read deadline for the new connection and having one breaks // long polling. However, not having a read deadline on the initial // connection would mean clients can connect and idle forever. Thus, // hijack the connecton from the HTTP server, clear the read deadline, // and handle writing the response manually. hj, ok := w.(http.Hijacker) if !ok { errMsg := "webserver doesn't support hijacking" rpcsLog.Warnf(errMsg) errCode := http.StatusInternalServerError http.Error(w, strconv.FormatInt(int64(errCode), 10)+" "+errMsg, errCode) return } conn, buf, err := hj.Hijack() if err != nil { rpcsLog.Warnf("Failed to hijack HTTP connection: %v", err) errCode := http.StatusInternalServerError http.Error(w, strconv.FormatInt(int64(errCode), 10)+" "+ err.Error(), errCode) return } defer conn.Close() defer buf.Flush() conn.SetReadDeadline(timeZeroVal) 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 { // Setup a close notifier. Since the connection is hijacked, // the CloseNotifer on the ResponseWriter is not available. closeChan := make(chan struct{}, 1) go func() { _, err := conn.Read(make([]byte, 1)) if err != nil { close(closeChan) } }() reply = standardCmdReply(cmd, s, closeChan) } rpcsLog.Tracef("reply: %v", reply) err = s.writeHTTPResponseHeaders(r, w.Header(), http.StatusOK, buf) if err != nil { rpcsLog.Error(err) return } msg, err := btcjson.MarshallAndSend(reply, buf) if err != nil { rpcsLog.Error(err) return } rpcsLog.Tracef(msg) } // handleUnimplemented is a temporary handler for commands that we should // support but do not. func handleUnimplemented(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { return nil, btcjson.ErrUnimplemented } // handleAskWallet is the handler for commands that we do recognise as valid // but that we can not answer correctly since it involves wallet state. // These commands will be implemented in btcwallet. func handleAskWallet(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { return nil, btcjson.ErrNoWallet } // handleAddNode handles addnode commands. func handleAddNode(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.AddNodeCmd) addr := normalizeAddress(c.Addr, activeNetParams.DefaultPort) var err error switch c.SubCmd { case "add": err = s.server.AddAddr(addr, true) case "remove": err = s.server.RemoveAddr(addr) case "onetry": err = s.server.AddAddr(addr, false) default: err = errors.New("invalid subcommand for addnode") } if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } // no data returned unless an error. return nil, nil } // messageToHex serializes a message to the wire protocol encoding using the // latest protocol version and returns a hex-encoded string of the result. func messageToHex(msg btcwire.Message) (string, error) { var buf bytes.Buffer err := msg.BtcEncode(&buf, maxProtocolVersion) if err != nil { return "", btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } return hex.EncodeToString(buf.Bytes()), nil } // handleCreateRawTransaction handles createrawtransaction commands. func handleCreateRawTransaction(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.CreateRawTransactionCmd) // Add all transaction inputs to a new transaction after performing // some validity checks. mtx := btcwire.NewMsgTx() for _, input := range c.Inputs { txHash, err := btcwire.NewShaHashFromStr(input.Txid) if err != nil { return nil, btcjson.ErrDecodeHexString } if input.Vout < 0 { return nil, btcjson.Error{ Code: btcjson.ErrInvalidParameter.Code, Message: "Invalid parameter, vout must be positive", } } prevOut := btcwire.NewOutPoint(txHash, uint32(input.Vout)) txIn := btcwire.NewTxIn(prevOut, []byte{}) mtx.AddTxIn(txIn) } // Add all transaction outputs to the transaction after performing // some validity checks. for encodedAddr, amount := range c.Amounts { // Ensure amount is in the valid range for monetary amounts. if amount <= 0 || amount > btcutil.MaxSatoshi { return nil, btcjson.Error{ Code: btcjson.ErrType.Code, Message: "Invalid amount", } } // Decode the provided address. addr, err := btcutil.DecodeAddress(encodedAddr, activeNetParams.Params) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInvalidAddressOrKey.Code, Message: btcjson.ErrInvalidAddressOrKey.Message + ": " + err.Error(), } } // Ensure the address is one of the supported types and that // the network encoded with the address matches the network the // server is currently on. switch addr.(type) { case *btcutil.AddressPubKeyHash: case *btcutil.AddressScriptHash: default: return nil, btcjson.ErrInvalidAddressOrKey } if !addr.IsForNet(s.server.netParams) { return nil, btcjson.Error{ Code: btcjson.ErrInvalidAddressOrKey.Code, Message: fmt.Sprintf("%s: %q", btcjson.ErrInvalidAddressOrKey.Message, encodedAddr), } } // Create a new script which pays to the provided address. pkScript, err := btcscript.PayToAddrScript(addr) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } txOut := btcwire.NewTxOut(amount, pkScript) mtx.AddTxOut(txOut) } // Return the serialized and hex-encoded transaction. mtxHex, err := messageToHex(mtx) if err != nil { return nil, err } return mtxHex, nil } // handleDebugLevel handles debuglevel commands. func handleDebugLevel(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.DebugLevelCmd) // Special show command to list supported subsystems. if c.LevelSpec == "show" { return fmt.Sprintf("Supported subsystems %v", supportedSubsystems()), nil } err := parseAndSetDebugLevels(c.LevelSpec) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInvalidParams.Code, Message: err.Error(), } } return "Done.", nil } // createVinList returns a slice of JSON objects for the inputs of the passed // transaction. func createVinList(mtx *btcwire.MsgTx) ([]btcjson.Vin, error) { tx := btcutil.NewTx(mtx) vinList := make([]btcjson.Vin, len(mtx.TxIn)) for i, v := range mtx.TxIn { if btcchain.IsCoinBase(tx) { vinList[i].Coinbase = hex.EncodeToString(v.SignatureScript) } else { vinList[i].Txid = v.PreviousOutpoint.Hash.String() vinList[i].Vout = v.PreviousOutpoint.Index disbuf, err := btcscript.DisasmString(v.SignatureScript) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } vinList[i].ScriptSig = new(btcjson.ScriptSig) vinList[i].ScriptSig.Asm = disbuf vinList[i].ScriptSig.Hex = hex.EncodeToString(v.SignatureScript) } vinList[i].Sequence = v.Sequence } return vinList, nil } // createVoutList returns a slice of JSON objects for the outputs of the passed // transaction. func createVoutList(mtx *btcwire.MsgTx, net *btcnet.Params) ([]btcjson.Vout, error) { voutList := make([]btcjson.Vout, len(mtx.TxOut)) for i, v := range mtx.TxOut { voutList[i].N = uint32(i) voutList[i].Value = float64(v.Value) / float64(btcutil.SatoshiPerBitcoin) disbuf, err := btcscript.DisasmString(v.PkScript) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } voutList[i].ScriptPubKey.Asm = disbuf voutList[i].ScriptPubKey.Hex = hex.EncodeToString(v.PkScript) // Ignore the error here since an error means the script // couldn't parse and there is no additional information about // it anyways. scriptClass, addrs, reqSigs, _ := btcscript.ExtractPkScriptAddrs(v.PkScript, net) voutList[i].ScriptPubKey.Type = scriptClass.String() voutList[i].ScriptPubKey.ReqSigs = int32(reqSigs) if addrs == nil { voutList[i].ScriptPubKey.Addresses = nil } else { voutList[i].ScriptPubKey.Addresses = make([]string, len(addrs)) for j, addr := range addrs { voutList[i].ScriptPubKey.Addresses[j] = addr.EncodeAddress() } } } return voutList, nil } // createTxRawResult converts the passed transaction and associated parameters // to a raw transaction JSON object. func createTxRawResult(net *btcnet.Params, txSha string, mtx *btcwire.MsgTx, blk *btcutil.Block, maxidx int64, blksha *btcwire.ShaHash) (*btcjson.TxRawResult, error) { mtxHex, err := messageToHex(mtx) if err != nil { return nil, err } vin, err := createVinList(mtx) if err != nil { return nil, err } vout, err := createVoutList(mtx, net) if err != nil { return nil, err } txReply := &btcjson.TxRawResult{ Hex: mtxHex, Txid: txSha, Vout: vout, Vin: vin, Version: mtx.Version, LockTime: mtx.LockTime, } if blk != nil { blockHeader := &blk.MsgBlock().Header idx := blk.Height() // This is not a typo, they are identical in bitcoind as well. txReply.Time = blockHeader.Timestamp.Unix() txReply.Blocktime = blockHeader.Timestamp.Unix() txReply.BlockHash = blksha.String() txReply.Confirmations = uint64(1 + maxidx - idx) } return txReply, nil } // handleDecodeRawTransaction handles decoderawtransaction commands. func handleDecodeRawTransaction(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.DecodeRawTransactionCmd) // Deserialize the transaction. hexStr := c.HexTx if len(hexStr)%2 != 0 { hexStr = "0" + hexStr } serializedTx, err := hex.DecodeString(hexStr) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrDecodeHexString.Code, Message: fmt.Sprintf("argument must be hexadecimal "+ "string (not %q)", hexStr), } } var mtx btcwire.MsgTx err = mtx.Deserialize(bytes.NewReader(serializedTx)) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrDeserialization.Code, Message: "TX decode failed", } } txSha, _ := mtx.TxSha() vin, err := createVinList(&mtx) if err != nil { return nil, err } vout, err := createVoutList(&mtx, s.server.netParams) if err != nil { return nil, err } // Create and return the result. txReply := btcjson.TxRawDecodeResult{ Txid: txSha.String(), Version: mtx.Version, Locktime: mtx.LockTime, Vin: vin, Vout: vout, } return txReply, nil } // handleDecodeScript handles decodescript commands. func handleDecodeScript(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.DecodeScriptCmd) // Convert the hex script to bytes. hexStr := c.HexScript if len(hexStr)%2 != 0 { hexStr = "0" + hexStr } script, err := hex.DecodeString(hexStr) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrDecodeHexString.Code, Message: fmt.Sprintf("argument must be hexadecimal "+ "string (not %q)", hexStr), } } // The disassembled string will contain [error] inline if the script // doesn't fully parse, so ignore the error here. disbuf, _ := btcscript.DisasmString(script) // Get information about the script. // Ignore the error here since an error means the script couldn't parse // and there is no additinal information about it anyways. net := s.server.netParams scriptClass, addrs, reqSigs, _ := btcscript.ExtractPkScriptAddrs(script, net) addresses := make([]string, len(addrs)) for i, addr := range addrs { addresses[i] = addr.EncodeAddress() } // Convert the script itself to a pay-to-script-hash address. p2sh, err := btcutil.NewAddressScriptHash(script, net) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } // Generate and return the reply. reply := btcjson.DecodeScriptResult{ Asm: disbuf, ReqSigs: int32(reqSigs), Type: scriptClass.String(), Addresses: addresses, P2sh: p2sh.EncodeAddress(), } return reply, nil } // handleGetAddedNodeInfo handles getaddednodeinfo commands. func handleGetAddedNodeInfo(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetAddedNodeInfoCmd) // Retrieve a list of persistent (added) peers from the bitcoin server // and filter the list of peer per the specified address (if any). peers := s.server.AddedNodeInfo() if c.Node != "" { found := false for i, peer := range peers { if peer.addr == c.Node { peers = peers[i : i+1] found = true } } if !found { return nil, btcjson.Error{ Code: -24, Message: "Node has not been added.", } } } // Without the dns flag, the result is just a slice of the addresses as // strings. if !c.Dns { results := make([]string, 0, len(peers)) for _, peer := range peers { results = append(results, peer.addr) } return results, nil } // With the dns flag, the result is an array of JSON objects which // include the result of DNS lookups for each peer. results := make([]*btcjson.GetAddedNodeInfoResult, 0, len(peers)) for _, peer := range peers { // Set the "address" of the peer which could be an ip address // or a domain name. var result btcjson.GetAddedNodeInfoResult result.AddedNode = peer.addr isConnected := peer.Connected() result.Connected = &isConnected // Split the address into host and port portions so we can do // a DNS lookup against the host. When no port is specified in // the address, just use the address as the host. host, _, err := net.SplitHostPort(peer.addr) if err != nil { host = peer.addr } // Do a DNS lookup for the address. If the lookup fails, just // use the host. var ipList []string ips, err := btcdLookup(host) if err == nil { ipList = make([]string, 0, len(ips)) for _, ip := range ips { ipList = append(ipList, ip.String()) } } else { ipList = make([]string, 1) ipList[0] = host } // Add the addresses and connection info to the result. addrs := make([]btcjson.GetAddedNodeInfoResultAddr, 0, len(ipList)) for _, ip := range ipList { var addr btcjson.GetAddedNodeInfoResultAddr addr.Address = ip addr.Connected = "false" if ip == host && peer.Connected() { addr.Connected = directionString(peer.inbound) } addrs = append(addrs, addr) } result.Addresses = &addrs results = append(results, &result) } return results, nil } // handleGetBestBlock implements the getbestblock command. func handleGetBestBlock(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { // All other "get block" commands give either the height, the // hash, or both but require the block SHA. This gets both for // the best block. sha, height, err := s.server.db.NewestSha() if err != nil { return nil, btcjson.ErrBestBlockHash } result := &btcws.GetBestBlockResult{ Hash: sha.String(), Height: int32(height), } return result, nil } // handleGetBestBlockHash implements the getbestblockhash command. func handleGetBestBlockHash(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { sha, _, err := s.server.db.NewestSha() if err != nil { rpcsLog.Errorf("Error getting newest sha: %v", err) return nil, btcjson.ErrBestBlockHash } return sha.String(), nil } // handleGetBlock implements the getblock command. func handleGetBlock(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (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: int32(len(buf)), Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), Difficulty: getDifficultyRatio(blockHeader.Bits), } if !c.VerboseTx { transactions := blk.Transactions() txNames := make([]string, len(transactions)) for i, tx := range transactions { txNames[i] = tx.Sha().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.netParams, 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 } // handleGetBlockCount implements the getblockcount command. func handleGetBlockCount(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { _, maxidx, err := s.server.db.NewestSha() if err != nil { rpcsLog.Errorf("Error getting newest sha: %v", err) return nil, btcjson.ErrBlockCount } return maxidx, nil } // handleGetBlockHash implements the getblockhash command. func handleGetBlockHash(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetBlockHashCmd) sha, err := s.server.db.FetchBlockShaByHeight(c.Index) if err != nil { rpcsLog.Errorf("Error getting block: %v", err) return nil, btcjson.ErrOutOfRange } return sha.String(), nil } // handleGetConnectionCount implements the getconnectioncount command. func handleGetConnectionCount(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { return s.server.ConnectedCount(), nil } // handleGetCurrentNet implements the getcurrentnet command. func handleGetCurrentNet(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { return s.server.netParams.Net, nil } // handleGetDifficulty implements the getdifficulty command. func handleGetDifficulty(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { sha, _, err := s.server.db.NewestSha() if err != nil { rpcsLog.Errorf("Error getting sha: %v", err) return nil, btcjson.ErrDifficulty } blockHeader, err := s.server.db.FetchBlockHeaderBySha(sha) if err != nil { rpcsLog.Errorf("Error getting block: %v", err) return nil, btcjson.ErrDifficulty } return getDifficultyRatio(blockHeader.Bits), nil } // handleGetGenerate implements the getgenerate command. func handleGetGenerate(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { return s.server.cpuMiner.IsMining(), nil } // handleGetHashesPerSec implements the gethashespersec command. func handleGetHashesPerSec(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { return int64(s.server.cpuMiner.HashesPerSecond()), nil } // handleGetInfo implements the getinfo command. We only return the fields // that are not related to wallet functionality. func handleGetInfo(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { // We require the current block height and sha. sha, height, err := s.server.db.NewestSha() if err != nil { rpcsLog.Errorf("Error getting sha: %v", err) return nil, btcjson.ErrBlockCount } blkHeader, err := s.server.db.FetchBlockHeaderBySha(sha) if err != nil { rpcsLog.Errorf("Error getting block: %v", err) return nil, btcjson.ErrDifficulty } ret := &btcjson.InfoResult{ Version: int32(1000000*appMajor + 10000*appMinor + 100*appPatch), ProtocolVersion: int32(maxProtocolVersion), Blocks: int32(height), TimeOffset: 0, Connections: s.server.ConnectedCount(), Proxy: cfg.Proxy, Difficulty: getDifficultyRatio(blkHeader.Bits), TestNet: cfg.TestNet3, RelayFee: float64(minTxRelayFee) / float64(btcutil.SatoshiPerBitcoin), } return ret, nil } // handleGetMiningInfo implements the getmininginfo command. We only return the // fields that are not related to wallet functionality. func handleGetMiningInfo(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { sha, height, err := s.server.db.NewestSha() if err != nil { rpcsLog.Errorf("Error getting sha: %v", err) return nil, btcjson.ErrBlockCount } block, err := s.server.db.FetchBlockBySha(sha) if err != nil { rpcsLog.Errorf("Error getting block: %v", err) return nil, btcjson.ErrBlockNotFound } blockBytes, err := block.Bytes() if err != nil { rpcsLog.Errorf("Error getting block: %v", err) return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } // Create a default getnetworkhashps command to use defaults and make // use of the existing getnetworkhashps handler. gnhpsCmd, err := btcjson.NewGetNetworkHashPSCmd(0) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } networkHashesPerSecIface, err := handleGetNetworkHashPS(s, gnhpsCmd, closeChan) if err != nil { // This is already a btcjson.Error from the handler. return nil, err } networkHashesPerSec, ok := networkHashesPerSecIface.(int64) if !ok { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: "networkHashesPerSec is not an int64", } } result := &btcjson.GetMiningInfoResult{ Blocks: height, CurrentBlockSize: uint64(len(blockBytes)), CurrentBlockTx: uint64(len(block.MsgBlock().Transactions)), Difficulty: getDifficultyRatio(block.MsgBlock().Header.Bits), Generate: s.server.cpuMiner.IsMining(), GenProcLimit: s.server.cpuMiner.NumWorkers(), HashesPerSec: int64(s.server.cpuMiner.HashesPerSecond()), NetworkHashPS: networkHashesPerSec, PooledTx: uint64(s.server.txMemPool.Count()), TestNet: cfg.TestNet3, } return &result, nil } // handleGetNetTotals implements the getnettotals command. func handleGetNetTotals(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { totalBytesRecv, totalBytesSent := s.server.NetTotals() reply := &btcjson.GetNetTotalsResult{ TotalBytesRecv: totalBytesRecv, TotalBytesSent: totalBytesSent, TimeMillis: time.Now().UTC().UnixNano() / int64(time.Millisecond), } return reply, nil } // handleGetNetworkHashPS implements the getnetworkhashps command. func handleGetNetworkHashPS(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetNetworkHashPSCmd) _, newestHeight, err := s.server.db.NewestSha() if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } // When the passed height is too high or zero, just return 0 now // since we can't reasonably calculate the number of network hashes // per second from invalid values. When it's negative, use the current // best block height. endHeight := int64(c.Height) if endHeight > newestHeight || endHeight == 0 { return 0, nil } if endHeight < 0 { endHeight = newestHeight } // Calculate the starting block height based on the passed number of // blocks. When the passed value is negative, use the last block the // difficulty changed as the starting height. Also make sure the // starting height is not before the beginning of the chain. var startHeight int64 if c.Blocks <= 0 { startHeight = endHeight - ((endHeight % btcchain.BlocksPerRetarget) + 1) } else { startHeight = endHeight - int64(c.Blocks) } if startHeight < 0 { startHeight = 0 } rpcsLog.Debugf("Calculating network hashes per second from %d to %d", startHeight, endHeight) // Find the min and max block timestamps as well as calculate the total // amount of work that happened between the start and end blocks. var minTimestamp, maxTimestamp time.Time totalWork := big.NewInt(0) for curHeight := startHeight; curHeight <= endHeight; curHeight++ { hash, err := s.server.db.FetchBlockShaByHeight(curHeight) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } header, err := s.server.db.FetchBlockHeaderBySha(hash) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } if curHeight == startHeight { minTimestamp = header.Timestamp maxTimestamp = minTimestamp } else { totalWork.Add(totalWork, btcchain.CalcWork(header.Bits)) if minTimestamp.After(header.Timestamp) { minTimestamp = header.Timestamp } if maxTimestamp.Before(header.Timestamp) { maxTimestamp = header.Timestamp } } } // Calculate the difference in seconds between the min and max block // timestamps and avoid division by zero in the case where there is no // time difference. timeDiff := int64(maxTimestamp.Sub(minTimestamp) / time.Second) if timeDiff == 0 { return 0, nil } hashesPerSec := new(big.Int).Div(totalWork, big.NewInt(timeDiff)) return hashesPerSec.Int64(), nil } // handleGetPeerInfo implements the getpeerinfo command. func handleGetPeerInfo(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { return s.server.PeerInfo(), nil } // handleGetRawMempool implements the getrawmempool command. func handleGetRawMempool(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetRawMempoolCmd) descs := s.server.txMemPool.TxDescs() if c.Verbose { result := make(map[string]*btcjson.GetRawMempoolResult, len(descs)) for _, desc := range descs { mpd := &btcjson.GetRawMempoolResult{ Size: int32(desc.Tx.MsgTx().SerializeSize()), Fee: float64(desc.Fee) / float64(btcutil.SatoshiPerBitcoin), Time: desc.Added.Unix(), Height: desc.Height, StartingPriority: 0, // We don't mine. CurrentPriority: 0, // We don't mine. Depends: make([]string, 0), } for _, txIn := range desc.Tx.MsgTx().TxIn { hash := &txIn.PreviousOutpoint.Hash if s.server.txMemPool.HaveTransaction(hash) { mpd.Depends = append(mpd.Depends, hash.String()) } } result[desc.Tx.Sha().String()] = mpd } return result, nil } // The response is simply an array of the transaction hashes if the // verbose flag is not set. hashStrings := make([]string, len(descs)) for i := range hashStrings { hashStrings[i] = descs[i].Tx.Sha().String() } return hashStrings, nil } // handleGetRawTransaction implements the getrawtransaction command. func handleGetRawTransaction(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetRawTransactionCmd) // Convert the provided transaction hash hex to a ShaHash. txSha, err := btcwire.NewShaHashFromStr(c.Txid) if err != nil { rpcsLog.Errorf("Error generating sha: %v", err) return nil, btcjson.Error{ Code: btcjson.ErrBlockNotFound.Code, Message: "Parameter 1 must be a hexaecimal string", } } // Try to fetch the transaction from the memory pool and if that fails, // try the block database. var mtx *btcwire.MsgTx var blksha *btcwire.ShaHash tx, err := s.server.txMemPool.FetchTransaction(txSha) if err != nil { txList, err := s.server.db.FetchTxBySha(txSha) if err != nil { rpcsLog.Errorf("Error fetching tx: %v", err) return nil, btcjson.ErrNoTxInfo } if len(txList) == 0 { return nil, btcjson.ErrNoTxInfo } lastTx := len(txList) - 1 mtx = txList[lastTx].Tx blksha = txList[lastTx].BlkSha } else { mtx = tx.MsgTx() } // When the verbose flag isn't set, simply return the network-serialized // transaction as a hex-encoded string. if c.Verbose == 0 { mtxHex, err := messageToHex(mtx) if err != nil { return nil, err } return mtxHex, nil } var blk *btcutil.Block var maxidx int64 if blksha != nil { blk, err = s.server.db.FetchBlockBySha(blksha) if err != nil { rpcsLog.Errorf("Error fetching sha: %v", err) return nil, btcjson.ErrBlockNotFound } _, maxidx, err = s.server.db.NewestSha() if err != nil { rpcsLog.Errorf("Cannot get newest sha: %v", err) return nil, btcjson.ErrNoNewestBlockInfo } } rawTxn, jsonErr := createTxRawResult(s.server.netParams, c.Txid, mtx, blk, maxidx, blksha) if err != nil { rpcsLog.Errorf("Cannot create TxRawResult for txSha=%s: %v", txSha, err) return nil, jsonErr } return *rawTxn, nil } // bigToLEUint256 returns the passed big integer as an unsigned 256-bit integer // encoded as little-endian bytes. Numbers which are larger than the max // unsigned 256-bit integer are truncated. func bigToLEUint256(n *big.Int) [uint256Size]byte { // Pad or truncate the big-endian big int to correct number of bytes. nBytes := n.Bytes() nlen := len(nBytes) pad := 0 start := 0 if nlen <= uint256Size { pad = uint256Size - nlen } else { start = nlen - uint256Size } var buf [uint256Size]byte copy(buf[pad:], nBytes[start:]) // Reverse the bytes to little endian and return them. for i := 0; i < uint256Size/2; i++ { buf[i], buf[uint256Size-1-i] = buf[uint256Size-1-i], buf[i] } return buf } // reverseUint32Array treats the passed bytes as a series of uint32s and // reverses the byte order of each uint32. The passed byte slice must be a // multiple of 4 for a correct result. The passed bytes slice is modified. func reverseUint32Array(b []byte) { blen := len(b) for i := 0; i < blen; i += 4 { b[i], b[i+3] = b[i+3], b[i] b[i+1], b[i+2] = b[i+2], b[i+1] } } // handleGetWorkRequest is a helper for handleGetWork which deals with // generating and returning work to the caller. // // This function MUST be called with the RPC workstate locked. func handleGetWorkRequest(s *rpcServer) (interface{}, error) { state := s.workState // Generate a new block template when the current best block has // changed or the transactions in the memory pool have been updated // and it has been at least one minute since the last template was // generated. lastTxUpdate := s.server.txMemPool.LastUpdated() latestHash, latestHeight := s.server.blockManager.chainState.Best() msgBlock := state.msgBlock if msgBlock == nil || state.prevHash == nil || !state.prevHash.IsEqual(latestHash) || (state.lastTxUpdate != lastTxUpdate && time.Now().After(state.lastGenerated.Add(time.Minute))) { // Reset the extra nonce and clear all cached template // variations if the best block changed. if state.prevHash != nil && !state.prevHash.IsEqual(latestHash) { state.extraNonce = 0 state.blockInfo = make(map[btcwire.ShaHash]*workStateBlockInfo) } // Reset the previous best hash the block template was generated // against so any errors below cause the next invocation to try // again. state.prevHash = nil // Choose a payment address at random. rand.Seed(time.Now().UnixNano()) payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] template, err := NewBlockTemplate(s.server.txMemPool, payToAddr) if err != nil { errStr := fmt.Sprintf("Failed to create new block "+ "template: %v", err) rpcsLog.Errorf(errStr) return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: errStr, } } msgBlock = template.block // Update work state to ensure another block template isn't // generated until needed. state.msgBlock = msgBlock state.lastGenerated = time.Now() state.lastTxUpdate = lastTxUpdate state.prevHash = latestHash rpcsLog.Debugf("Generated block template (timestamp %v, extra "+ "nonce %d, target %064x, merkle root %s, signature "+ "script %x)", msgBlock.Header.Timestamp, state.extraNonce, btcchain.CompactToBig(msgBlock.Header.Bits), msgBlock.Header.MerkleRoot, msgBlock.Transactions[0].TxIn[0].SignatureScript) } else { // At this point, there is a saved block template and a new // request for work was made, but either the available // transactions haven't change or it hasn't been long enough to // trigger a new block template to be generated. So, update the // existing block template and track the variations so each // variation can be regenerated if a caller finds an answer and // makes a submission against it. // Update the time of the block template to the current time // while accounting for the median time of the past several // blocks per the chain consensus rules. UpdateBlockTime(msgBlock, s.server.blockManager) // Increment the extra nonce and update the block template // with the new value by regenerating the coinbase script and // setting the merkle root to the new value. state.extraNonce++ err := UpdateExtraNonce(msgBlock, latestHeight+1, state.extraNonce) if err != nil { errStr := fmt.Sprintf("Failed to update extra nonce: "+ "%v", err) rpcsLog.Warnf(errStr) return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: errStr, } } rpcsLog.Debugf("Updated block template (timestamp %v, extra "+ "nonce %d, target %064x, merkle root %s, signature "+ "script %x)", msgBlock.Header.Timestamp, state.extraNonce, btcchain.CompactToBig(msgBlock.Header.Bits), msgBlock.Header.MerkleRoot, msgBlock.Transactions[0].TxIn[0].SignatureScript) } // In order to efficiently store the variations of block templates that // have been provided to callers, save a pointer to the block as well as // the modified signature script keyed by the merkle root. This // information, along with the data that is included in a work // submission, is used to rebuild the block before checking the // submitted solution. coinbaseTx := msgBlock.Transactions[0] state.blockInfo[msgBlock.Header.MerkleRoot] = &workStateBlockInfo{ msgBlock: msgBlock, signatureScript: coinbaseTx.TxIn[0].SignatureScript, } // Serialize the block header into a buffer large enough to hold the // the block header and the internal sha256 padding that is added and // retuned as part of the data below. data := make([]byte, 0, getworkDataLen) buf := bytes.NewBuffer(data) err := msgBlock.Header.Serialize(buf) if err != nil { errStr := fmt.Sprintf("Failed to serialize data: %v", err) rpcsLog.Warnf(errStr) return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: errStr, } } // Calculate the midstate for the block header. The midstate here is // the internal state of the sha256 algorithm for the first chunk of the // block header (sha256 operates on 64-byte chunks) which is before the // nonce. This allows sophisticated callers to avoid hashing the first // chunk over and over while iterating the nonce range. data = data[:buf.Len()] midstate := fastsha256.MidState256(data) // Expand the data slice to include the full data buffer and apply the // internal sha256 padding which consists of a single 1 bit followed // by enough zeros to pad the message out to 56 bytes followed by the // length of the message in bits encoded as a big-endian uint64 // (8 bytes). Thus, the resulting length is a multiple of the sha256 // block size (64 bytes). This makes the data ready for sophisticated // caller to make use of only the second chunk along with the midstate // for the first chunk. data = data[:getworkDataLen] data[btcwire.MaxBlockHeaderPayload] = 0x80 binary.BigEndian.PutUint64(data[len(data)-8:], btcwire.MaxBlockHeaderPayload*8) // Create the hash1 field which is a zero hash along with the internal // sha256 padding as described above. This field is really quite // useless, but it is required for compatibility with the reference // implementation. var hash1 [hash1Len]byte hash1[btcwire.HashSize] = 0x80 binary.BigEndian.PutUint64(hash1[len(hash1)-8:], btcwire.HashSize*8) // The final result reverses the each of the fields to little endian. // In particular, the data, hash1, and midstate fields are treated as // arrays of uint32s (per the internal sha256 hashing state) which are // in big endian, and thus each 4 bytes is byte swapped. The target is // also in big endian, but it is treated as a uint256 and byte swapped // to little endian accordingly. // // The fact the fields are reversed in this way is rather odd and likey // an artifact of some legacy internal state in the reference // implementation, but it is required for compatibility. reverseUint32Array(data) reverseUint32Array(hash1[:]) reverseUint32Array(midstate[:]) target := bigToLEUint256(btcchain.CompactToBig(msgBlock.Header.Bits)) reply := &btcjson.GetWorkResult{ Data: hex.EncodeToString(data), Hash1: hex.EncodeToString(hash1[:]), Midstate: hex.EncodeToString(midstate[:]), Target: hex.EncodeToString(target[:]), } return reply, nil } // handleGetWorkSubmission is a helper for handleGetWork which deals with // the calling submitting work to be verified and processed. // // This function MUST be called with the RPC workstate locked. func handleGetWorkSubmission(s *rpcServer, hexData string) (interface{}, error) { // Ensure the provided data is sane. if len(hexData)%2 != 0 { hexData = "0" + hexData } data, err := hex.DecodeString(hexData) if err != nil { return false, btcjson.Error{ Code: btcjson.ErrDecodeHexString.Code, Message: fmt.Sprintf("argument must be "+ "hexadecimal string (not %q)", hexData), } } if len(data) != getworkDataLen { return false, btcjson.Error{ Code: btcjson.ErrInvalidParameter.Code, Message: fmt.Sprintf("argument must be "+ "%d bytes (not %d)", getworkDataLen, len(data)), } } // Reverse the data as if it were an array of 32-bit unsigned integers. // The fact the getwork request and submission data is reversed in this // way is rather odd and likey an artifact of some legacy internal state // in the reference implementation, but it is required for // compatibility. reverseUint32Array(data) // Deserialize the block header from the data. var submittedHeader btcwire.BlockHeader bhBuf := bytes.NewReader(data[0:btcwire.MaxBlockHeaderPayload]) err = submittedHeader.Deserialize(bhBuf) if err != nil { return false, btcjson.Error{ Code: btcjson.ErrInvalidParameter.Code, Message: fmt.Sprintf("argument does not "+ "contain a valid block header: %v", err), } } // Look up the full block for the provided data based on the // merkle root. Return false to indicate the solve failed if // it's not available. state := s.workState blockInfo, ok := state.blockInfo[submittedHeader.MerkleRoot] if !ok { rpcsLog.Debugf("Block submitted via getwork has no matching "+ "template for merkle root %s", submittedHeader.MerkleRoot) return false, nil } // Reconstruct the block using the submitted header stored block info. msgBlock := blockInfo.msgBlock block := btcutil.NewBlock(msgBlock) msgBlock.Header.Timestamp = submittedHeader.Timestamp msgBlock.Header.Nonce = submittedHeader.Nonce msgBlock.Transactions[0].TxIn[0].SignatureScript = blockInfo.signatureScript merkles := btcchain.BuildMerkleTreeStore(block.Transactions()) msgBlock.Header.MerkleRoot = *merkles[len(merkles)-1] // Ensure the submitted block hash is less than the target difficulty. err = btcchain.CheckProofOfWork(block, activeNetParams.PowLimit) if err != nil { // Anything other than a rule violation is an unexpected error, // so return that error as an internal error. if _, ok := err.(btcchain.RuleError); !ok { return false, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: fmt.Sprintf("Unexpected error while "+ "checking proof of work: %v", err), } } rpcsLog.Debugf("Block submitted via getwork does not meet "+ "the required proof of work: %v", err) return false, nil } latestHash, _ := s.server.blockManager.chainState.Best() if !msgBlock.Header.PrevBlock.IsEqual(latestHash) { rpcsLog.Debugf("Block submitted via getwork with previous "+ "block %s is stale", msgBlock.Header.PrevBlock) return false, nil } // Process this block using the same rules as blocks coming from other // nodes. This will in turn relay it to the network like normal. isOrphan, err := s.server.blockManager.ProcessBlock(block, btcchain.BFNone) if err != nil || isOrphan { // Anything other than a rule violation is an unexpected error, // so return that error as an internal error. if _, ok := err.(btcchain.RuleError); !ok { return false, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: fmt.Sprintf("Unexpected error while "+ "processing block: %v", err), } } rpcsLog.Infof("Block submitted via getwork rejected: %v", err) return false, nil } // The block was accepted. blockSha, _ := block.Sha() rpcsLog.Infof("Block submitted via getwork accepted: %s", blockSha) return true, nil } // handleGetWork implements the getwork command. func handleGetWork(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetWorkCmd) // Respond with an error if there are no addresses to pay the created // blocks to. if len(cfg.miningAddrs) == 0 { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: "No payment addresses specified via --miningaddr", } } // Return an error if there are no peers connected since there is no // way to relay a found block or receive transactions to work on. // However, allow this state when running in the regression test or // simulation test mode. if !(cfg.RegressionTest || cfg.SimNet) && s.server.ConnectedCount() == 0 { return nil, btcjson.ErrClientNotConnected } // No point in generating or accepting work before the chain is synced. _, currentHeight := s.server.blockManager.chainState.Best() if currentHeight != 0 && !s.server.blockManager.IsCurrent() { return nil, btcjson.ErrClientInInitialDownload } // Protect concurrent access from multiple RPC invocations for work // requests and submission. s.workState.Lock() defer s.workState.Unlock() // When the caller provides data, it is a submission of a supposedly // solved block that needs to be checked and submitted to the network // if valid. if c.Data != "" { return handleGetWorkSubmission(s, c.Data) } // No data was provided, so the caller is requesting work. return handleGetWorkRequest(s) } var helpAddenda = map[string]string{ "sendrawtransaction": ` NOTE: btcd does not currently support the "allowhighfees" parameter.`, } // getHelp text retreives help text from btcjson for the command in question. // If there is any extra btcd specific information related to the given command // then this is appended to the string. func getHelpText(cmdName string) (string, error) { help, err := btcjson.GetHelpString(cmdName) if err != nil { return "", err } if helpAddendum, ok := helpAddenda[cmdName]; ok { help += helpAddendum } return help, nil } // handleHelp implements the help command. func handleHelp(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { help := cmd.(*btcjson.HelpCmd) // if no args we give a list of all known commands if help.Command == "" { commands := "" first := true // TODO(oga) this should have one liner usage for each command // really, but for now just a list of commands is sufficient. for k := range rpcHandlers { if !first { commands += "\n" } commands += k first = false } return commands, nil } // Check that we actually support the command asked for. We only // search the main list of hanlders since we do not wish to provide help // for commands that are unimplemented or relate to wallet // functionality. if _, ok := rpcHandlers[help.Command]; !ok { return "", fmt.Errorf("help: unknown command: %s", help.Command) } return getHelpText(help.Command) } // handlePing implements the ping command. func handlePing(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { // Ask server to ping \o_ nonce, err := btcwire.RandomUint64() if err != nil { return nil, fmt.Errorf("Not sending ping - can not generate "+ "nonce: %v", err) } s.server.BroadcastMessage(btcwire.NewMsgPing(nonce)) return nil, nil } // handleSendRawTransaction implements the sendrawtransaction command. func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.SendRawTransactionCmd) // Deserialize and send off to tx relay hexStr := c.HexTx if len(hexStr)%2 != 0 { hexStr = "0" + hexStr } serializedTx, err := hex.DecodeString(hexStr) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrDecodeHexString.Code, Message: fmt.Sprintf("argument must be hexadecimal "+ "string (not %q)", hexStr), } } msgtx := btcwire.NewMsgTx() err = msgtx.Deserialize(bytes.NewReader(serializedTx)) if err != nil { err := btcjson.Error{ Code: btcjson.ErrDeserialization.Code, Message: "TX decode failed", } return nil, err } tx := btcutil.NewTx(msgtx) err = s.server.txMemPool.ProcessTransaction(tx, false, false) if err != nil { // When the error is a rule error, it means the transaction was // simply rejected as opposed to something actually going wrong, // so log it as such. Otherwise, something really did go wrong, // so log it as an actual error. In both cases, a JSON-RPC // error is returned to the client with the deserialization // error code (to match bitcoind behavior). if _, ok := err.(TxRuleError); ok { rpcsLog.Debugf("Rejected transaction %v: %v", tx.Sha(), err) } else { rpcsLog.Errorf("Failed to process transaction %v: %v", tx.Sha(), err) } err = btcjson.Error{ Code: btcjson.ErrDeserialization.Code, Message: fmt.Sprintf("TX rejected: %v", err), } return nil, err } // We keep track of all the sendrawtransaction request txs so that we // can rebroadcast them if they don't make their way into a block. iv := btcwire.NewInvVect(btcwire.InvTypeTx, tx.Sha()) s.server.AddRebroadcastInventory(iv) return tx.Sha().String(), nil } // handleSetGenerate implements the setgenerate command. func handleSetGenerate(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.SetGenerateCmd) // Disable generation regardless of the provided generate flag if the // maximum number of threads (goroutines for our purposes) is 0. // Otherwise enable or disable it depending on the provided flag. generate := c.Generate if c.GenProcLimit == 0 { generate = false } if !generate { s.server.cpuMiner.Stop() } else { // Respond with an error if there are no addresses to pay the // created blocks to. if len(cfg.miningAddrs) == 0 { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: "No payment addresses specified " + "via --miningaddr", } } // It's safe to call start even if it's already started. s.server.cpuMiner.SetNumWorkers(int32(c.GenProcLimit)) s.server.cpuMiner.Start() } return nil, nil } // handleStop implements the stop command. func handleStop(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { s.server.Stop() return "btcd stopping.", nil } // handleSubmitBlock implements the submitblock command. func handleSubmitBlock(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.SubmitBlockCmd) // Deserialize the submitted block. hexStr := c.HexBlock if len(hexStr)%2 != 0 { hexStr = "0" + c.HexBlock } serializedBlock, err := hex.DecodeString(hexStr) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrDecodeHexString.Code, Message: fmt.Sprintf("argument must be hexadecimal "+ "string (not %q)", hexStr), } } block, err := btcutil.NewBlockFromBytes(serializedBlock) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrDeserialization.Code, Message: "Block decode failed", } } _, err = s.server.blockManager.ProcessBlock(block, btcchain.BFNone) if err != nil { return fmt.Sprintf("rejected: %s", err.Error()), nil } return nil, nil } func verifyChain(db btcdb.Db, level, depth int32) error { _, curHeight64, err := db.NewestSha() if err != nil { rpcsLog.Errorf("Verify is unable to fetch current block "+ "height: %v", err) } curHeight := int32(curHeight64) finishHeight := curHeight - depth if finishHeight < 0 { finishHeight = 0 } rpcsLog.Infof("Verifying chain for %d blocks at level %d", curHeight-finishHeight, level) for height := curHeight; height > finishHeight; height-- { // Level 0 just looks up the block. sha, err := db.FetchBlockShaByHeight(int64(height)) if err != nil { rpcsLog.Errorf("Verify is unable to fetch block at "+ "height %d: %v", height, err) return err } block, err := db.FetchBlockBySha(sha) if err != nil { rpcsLog.Errorf("Verify is unable to fetch block at "+ "sha %v height %d: %v", sha, height, err) return err } // Level 1 does basic chain sanity checks. if level > 0 { err := btcchain.CheckBlockSanity(block, activeNetParams.PowLimit) if err != nil { rpcsLog.Errorf("Verify is unable to "+ "validate block at sha %v height "+ "%d: %v", sha, height, err) return err } } } rpcsLog.Infof("Chain verify completed successfully") return nil } // handleValidateAddress implements the validateaddress command. func handleValidateAddress(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.ValidateAddressCmd) result := btcjson.ValidateAddressResult{} addr, err := btcutil.DecodeAddress(c.Address, activeNetParams.Params) if err != nil { // Return the default value (false) for IsValid. return result, nil } result.Address = addr.EncodeAddress() result.IsValid = true return result, nil } // handleVerifyChain implements the verifychain command. func handleVerifyChain(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.VerifyChainCmd) err := verifyChain(s.server.db, c.CheckLevel, c.CheckDepth) return err == nil, nil } // parseCmd parses a marshaled known command, returning any errors as a // btcjson.Error that can be used in replies. The returned cmd may still // be non-nil if b is at least a valid marshaled JSON-RPC message. func parseCmd(b []byte) (btcjson.Cmd, *btcjson.Error) { cmd, err := btcjson.ParseMarshaledCmd(b) if err != nil { jsonErr, ok := err.(btcjson.Error) if !ok { jsonErr = btcjson.Error{ Code: btcjson.ErrParse.Code, Message: err.Error(), } } return cmd, &jsonErr } return cmd, nil } // standardCmdReply checks that a parsed command is a standard // Bitcoin JSON-RPC command and runs the proper handler to reply to the // command. func standardCmdReply(cmd btcjson.Cmd, s *rpcServer, closeChan <-chan struct{}) (reply btcjson.Reply) { id := cmd.Id() reply.Id = &id handler, ok := rpcHandlers[cmd.Method()] if ok { goto handled } _, ok = rpcAskWallet[cmd.Method()] if ok { handler = handleAskWallet goto handled } _, ok = rpcUnimplemented[cmd.Method()] if ok { handler = handleUnimplemented goto handled } reply.Error = &btcjson.ErrMethodNotFound return reply handled: result, err := handler(s, cmd, closeChan) if err != nil { jsonErr, ok := err.(btcjson.Error) if !ok { // In the case where we did not have a btcjson // error to begin with, make a new one to send, // but this really should not happen. jsonErr = btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } reply.Error = &jsonErr } else { reply.Result = result } return reply } // getDifficultyRatio returns the proof-of-work difficulty as a multiple of the // minimum difficulty using the passed bits field from the header of a block. func getDifficultyRatio(bits uint32) float64 { // The minimum difficulty is the max possible proof-of-work limit bits // converted back to a number. Note this is not the same as the the // proof of work limit directly because the block difficulty is encoded // in a block with the compact form which loses precision. max := btcchain.CompactToBig(activeNetParams.PowLimitBits) target := btcchain.CompactToBig(bits) difficulty := new(big.Rat).SetFrac(max, target) outString := difficulty.FloatString(2) diff, err := strconv.ParseFloat(outString, 64) if err != nil { rpcsLog.Errorf("Cannot get difficulty: %v", err) return 0 } return diff }