// Copyright (c) 2013-2015 The btcsuite developers // 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" "encoding/json" "errors" "fmt" "io" "io/ioutil" "math/big" "math/rand" "net" "net/http" "os" "strconv" "strings" "sync" "sync/atomic" "time" "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/database" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/btcsuite/fastsha256" "github.com/btcsuite/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 + ((wire.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 + ((wire.HashSize + 8) / fastsha256.BlockSize)) * fastsha256.BlockSize // gbtNonceRange is two 32-bit big-endian hexadecimal integers which // represent the valid ranges of nonces returned by the getblocktemplate // RPC. gbtNonceRange = "00000000ffffffff" // gbtRegenerateSeconds is the number of seconds that must pass before // a new template is generated when the previous block hash has not // changed and there have been changes to the available transactions // in the memory pool. gbtRegenerateSeconds = 60 // maxProtocolVersion is the max protocol version the server supports. maxProtocolVersion = 70002 ) var ( // gbtMutableFields are the manipulations the server allows to be made // to block templates generated by the getblocktemplate RPC. It is // declared here to avoid the overhead of creating the slice on every // invocation for constant data. gbtMutableFields = []string{ "time", "transactions/add", "prevblock", "coinbase/append", } // gbtCoinbaseAux describes additional data that miners should include // in the coinbase signature script. It is declared here to avoid the // overhead of creating a new object on every invocation for constant // data. gbtCoinbaseAux = &btcjson.GetBlockTemplateResultAux{ Flags: hex.EncodeToString(builderScript(txscript. NewScriptBuilder().AddData([]byte(coinbaseFlags)))), } // gbtCapabilities describes additional capabilities returned with a // block template generated by the getblocktemplate RPC. It is // declared here to avoid the overhead of creating the slice on every // invocation for constant data. gbtCapabilities = []string{"proposal"} ) // Errors var ( // ErrRPCUnimplemented is an error returned to RPC clients when the // provided command is recognized, but not implemented. ErrRPCUnimplemented = &btcjson.RPCError{ Code: btcjson.ErrRPCUnimplemented, Message: "Command unimplemented", } // ErrRPCNoWallet is an error returned to RPC clients when the provided // command is recognized as a wallet command. ErrRPCNoWallet = &btcjson.RPCError{ Code: btcjson.ErrRPCNoWallet, Message: "This implementation does not implement wallet commands", } ) type commandHandler func(*rpcServer, interface{}, <-chan struct{}) (interface{}, error) // rpcHandlers maps RPC command strings to appropriate handler functions. // This is set by init because help references rpcHandlers and thus causes // a dependency loop. var rpcHandlers map[string]commandHandler var rpcHandlersBeforeInit = map[string]commandHandler{ "addnode": handleAddNode, "createrawtransaction": handleCreateRawTransaction, "debuglevel": handleDebugLevel, "decoderawtransaction": handleDecodeRawTransaction, "decodescript": handleDecodeScript, "generate": handleGenerate, "getaddednodeinfo": handleGetAddedNodeInfo, "getbestblock": handleGetBestBlock, "getbestblockhash": handleGetBestBlockHash, "getblock": handleGetBlock, "getblockcount": handleGetBlockCount, "getblockhash": handleGetBlockHash, "getblockheader": handleGetBlockHeader, "getblocktemplate": handleGetBlockTemplate, "getconnectioncount": handleGetConnectionCount, "getcurrentnet": handleGetCurrentNet, "getdifficulty": handleGetDifficulty, "getgenerate": handleGetGenerate, "gethashespersec": handleGetHashesPerSec, "getinfo": handleGetInfo, "getmempoolinfo": handleGetMempoolInfo, "getmininginfo": handleGetMiningInfo, "getnettotals": handleGetNetTotals, "getnetworkhashps": handleGetNetworkHashPS, "getpeerinfo": handleGetPeerInfo, "getrawmempool": handleGetRawMempool, "getrawtransaction": handleGetRawTransaction, "gettxout": handleGetTxOut, "getwork": handleGetWork, "help": handleHelp, "node": handleNode, "ping": handlePing, "searchrawtransactions": handleSearchRawTransactions, "sendrawtransaction": handleSendRawTransaction, "setgenerate": handleSetGenerate, "stop": handleStop, "submitblock": handleSubmitBlock, "validateaddress": handleValidateAddress, "verifychain": handleVerifyChain, "verifymessage": handleVerifyMessage, } // 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{}{}, "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{}{}, "walletlock": struct{}{}, "walletpassphrase": struct{}{}, "walletpassphrasechange": struct{}{}, } // Commands that are currently unimplemented, but should ultimately be. var rpcUnimplemented = map[string]struct{}{ "estimatefee": struct{}{}, "estimatepriority": struct{}{}, "getblockchaininfo": struct{}{}, "getchaintips": struct{}{}, "getnetworkinfo": struct{}{}, } // Commands that are available to a limited user var rpcLimited = map[string]struct{}{ // Websockets commands "notifyblocks": struct{}{}, "notifynewtransactions": struct{}{}, "notifyreceived": struct{}{}, "notifyspent": struct{}{}, "rescan": struct{}{}, "session": struct{}{}, // Websockets AND HTTP/S commands "help": struct{}{}, // HTTP/S-only commands "createrawtransaction": struct{}{}, "decoderawtransaction": struct{}{}, "decodescript": struct{}{}, "getbestblock": struct{}{}, "getbestblockhash": struct{}{}, "getblock": struct{}{}, "getblockcount": struct{}{}, "getblockhash": struct{}{}, "getcurrentnet": struct{}{}, "getdifficulty": struct{}{}, "getinfo": struct{}{}, "getnettotals": struct{}{}, "getnetworkhashps": struct{}{}, "getrawmempool": struct{}{}, "getrawtransaction": struct{}{}, "gettxout": struct{}{}, "searchrawtransactions": struct{}{}, "sendrawtransaction": struct{}{}, "submitblock": struct{}{}, "validateaddress": struct{}{}, "verifymessage": struct{}{}, } // builderScript is a convenience function which is used for hard-coded scripts // built with the script builder. Any errors are converted to a panic since it // is only, and must only, be used with hard-coded, and therefore, known good, // scripts. func builderScript(builder *txscript.ScriptBuilder) []byte { script, err := builder.Script() if err != nil { panic(err) } return script } // internalRPCError is a convenience function to convert an internal error to // an RPC error with the appropriate code set. It also logs the error to the // RPC server subsystem since internal errors really should not occur. The // context parameter is only used in the log message and may be empty if it's // not needed. func internalRPCError(errStr, context string) *btcjson.RPCError { logStr := errStr if context != "" { logStr = context + ": " + errStr } rpcsLog.Error(logStr) return btcjson.NewRPCError(btcjson.ErrRPCInternal.Code, errStr) } // rpcDecodeHexError is a convenience function for returning a nicely formatted // RPC error which indicates the provided hex string failed to decode. func rpcDecodeHexError(gotHex string) *btcjson.RPCError { return btcjson.NewRPCError(btcjson.ErrRPCDecodeHexString, fmt.Sprintf("Argument must be hexadecimal string (not %q)", gotHex)) } // workStateBlockInfo houses information about how to reconstruct a block given // its template and signature script. type workStateBlockInfo struct { msgBlock *wire.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 *wire.ShaHash msgBlock *wire.MsgBlock extraNonce uint64 blockInfo map[wire.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[wire.ShaHash]*workStateBlockInfo), } } // gbtWorkState houses state that is used in between multiple RPC invocations to // getblocktemplate. type gbtWorkState struct { sync.Mutex lastTxUpdate time.Time lastGenerated time.Time prevHash *wire.ShaHash minTimestamp time.Time template *BlockTemplate notifyMap map[wire.ShaHash]map[int64]chan struct{} timeSource blockchain.MedianTimeSource } // newGbtWorkState returns a new instance of a gbtWorkState with all internal // fields initialized and ready to use. func newGbtWorkState(timeSource blockchain.MedianTimeSource) *gbtWorkState { return &gbtWorkState{ notifyMap: make(map[wire.ShaHash]map[int64]chan struct{}), timeSource: timeSource, } } // handleUnimplemented is the handler for commands that should ultimately be // supported but are not yet implemented. func handleUnimplemented(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return nil, ErrRPCUnimplemented } // handleAskWallet is the handler for commands that are recognized as valid, but // are unable to answer correctly since it involves wallet state. // These commands will be implemented in btcwallet. func handleAskWallet(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return nil, ErrRPCNoWallet } // handleAddNode handles addnode commands. func handleAddNode(s *rpcServer, cmd interface{}, 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.ConnectNode(addr, true) case "remove": err = s.server.RemoveNodeByAddr(addr) case "onetry": err = s.server.ConnectNode(addr, false) default: return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, Message: "invalid subcommand for addnode", } } if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, Message: err.Error(), } } // no data returned unless an error. return nil, nil } // handleNode handles node commands. func handleNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.NodeCmd) var addr string var nodeID uint64 var errN, err error switch c.SubCmd { case "disconnect": // If we have a valid uint disconnect by node id. Otherwise, // attempt to disconnect by address, returning an error if a // valid IP address is not supplied. if nodeID, errN = strconv.ParseUint(c.Target, 10, 32); errN == nil { err = s.server.DisconnectNodeByID(int32(nodeID)) } else { if _, _, errP := net.SplitHostPort(c.Target); errP == nil || net.ParseIP(c.Target) != nil { addr = normalizeAddress(c.Target, activeNetParams.DefaultPort) err = s.server.DisconnectNodeByAddr(addr) } else { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, Message: "invalid address or node ID", } } } if err != nil && peerExists(s.server.Peers(), addr, int32(nodeID)) { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCMisc, Message: "can't disconnect a permanent peer, use remove", } } case "remove": // If we have a valid uint disconnect by node id. Otherwise, // attempt to disconnect by address, returning an error if a // valid IP address is not supplied. if nodeID, errN = strconv.ParseUint(c.Target, 10, 32); errN == nil { err = s.server.RemoveNodeByID(int32(nodeID)) } else { if _, _, errP := net.SplitHostPort(c.Target); errP == nil || net.ParseIP(c.Target) != nil { addr = normalizeAddress(c.Target, activeNetParams.DefaultPort) err = s.server.RemoveNodeByAddr(addr) } else { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, Message: "invalid address or node ID", } } } if err != nil && peerExists(s.server.Peers(), addr, int32(nodeID)) { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCMisc, Message: "can't remove a temporary peer, use disconnect", } } case "connect": addr = normalizeAddress(c.Target, activeNetParams.DefaultPort) // Default to temporary connections. subCmd := "temp" if c.ConnectSubCmd != nil { subCmd = *c.ConnectSubCmd } switch subCmd { case "perm", "temp": err = s.server.ConnectNode(addr, subCmd == "perm") default: return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, Message: "invalid subcommand for node connect", } } default: return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, Message: "invalid subcommand for node", } } if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, Message: err.Error(), } } // no data returned unless an error. return nil, nil } // peerExists determines if a certain peer is currently connected given // information about all currently connected peers. Peer existence is // determined using either a target address or node id. func peerExists(peers []*serverPeer, addr string, nodeID int32) bool { for _, p := range peers { if p.ID() == nodeID || p.Addr() == addr { return true } } return false } // 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 wire.Message) (string, error) { var buf bytes.Buffer if err := msg.BtcEncode(&buf, maxProtocolVersion); err != nil { context := fmt.Sprintf("Failed to encode msg of type %T", msg) return "", internalRPCError(err.Error(), context) } return hex.EncodeToString(buf.Bytes()), nil } // handleCreateRawTransaction handles createrawtransaction commands. func handleCreateRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.CreateRawTransactionCmd) // Validate the locktime, if given. if c.LockTime != nil && (*c.LockTime < 0 || *c.LockTime > int64(wire.MaxTxInSequenceNum)) { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, Message: "Locktime out of range", } } // Add all transaction inputs to a new transaction after performing // some validity checks. mtx := wire.NewMsgTx() for _, input := range c.Inputs { txHash, err := wire.NewShaHashFromStr(input.Txid) if err != nil { return nil, rpcDecodeHexError(input.Txid) } prevOut := wire.NewOutPoint(txHash, uint32(input.Vout)) txIn := wire.NewTxIn(prevOut, []byte{}) if c.LockTime != nil && *c.LockTime != 0 { txIn.Sequence = wire.MaxTxInSequenceNum - 1 } 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.RPCError{ Code: btcjson.ErrRPCType, Message: "Invalid amount", } } // Decode the provided address. addr, err := btcutil.DecodeAddress(encodedAddr, activeNetParams.Params) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidAddressOrKey, Message: "Invalid address or key: " + 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.RPCError{ Code: btcjson.ErrRPCInvalidAddressOrKey, Message: "Invalid address or key", } } if !addr.IsForNet(s.server.chainParams) { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidAddressOrKey, Message: "Invalid address: " + encodedAddr + " is for the wrong network", } } // Create a new script which pays to the provided address. pkScript, err := txscript.PayToAddrScript(addr) if err != nil { context := "Failed to generate pay-to-address script" return nil, internalRPCError(err.Error(), context) } // Convert the amount to satoshi. satoshi, err := btcutil.NewAmount(amount) if err != nil { context := "Failed to convert amount" return nil, internalRPCError(err.Error(), context) } txOut := wire.NewTxOut(int64(satoshi), pkScript) mtx.AddTxOut(txOut) } // Set the Locktime, if given. if c.LockTime != nil { mtx.LockTime = uint32(*c.LockTime) } // Return the serialized and hex-encoded transaction. Note that this // is intentionally not directly returning because the first return // value is a string and it would result in returning an empty string to // the client instead of nothing (nil) in the case of an error. mtxHex, err := messageToHex(mtx) if err != nil { return nil, err } return mtxHex, nil } // handleDebugLevel handles debuglevel commands. func handleDebugLevel(s *rpcServer, cmd interface{}, 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.RPCError{ Code: btcjson.ErrRPCInvalidParams.Code, Message: err.Error(), } } return "Done.", nil } // createVinList returns a slice of JSON objects for the inputs of the passed // transaction. func createVinList(mtx *wire.MsgTx) []btcjson.Vin { // Coinbase transactions only have a single txin by definition. vinList := make([]btcjson.Vin, len(mtx.TxIn)) if blockchain.IsCoinBaseTx(mtx) { txIn := mtx.TxIn[0] vinList[0].Coinbase = hex.EncodeToString(txIn.SignatureScript) vinList[0].Sequence = txIn.Sequence return vinList } for i, txIn := range mtx.TxIn { // The disassembled string will contain [error] inline // if the script doesn't fully parse, so ignore the // error here. disbuf, _ := txscript.DisasmString(txIn.SignatureScript) vinEntry := &vinList[i] vinEntry.Txid = txIn.PreviousOutPoint.Hash.String() vinEntry.Vout = txIn.PreviousOutPoint.Index vinEntry.Sequence = txIn.Sequence vinEntry.ScriptSig = &btcjson.ScriptSig{ Asm: disbuf, Hex: hex.EncodeToString(txIn.SignatureScript), } } return vinList } // createVinList returns a slice of JSON objects for the inputs of the passed // transaction. func createVinListPrevOut(s *rpcServer, mtx *wire.MsgTx, chainParams *chaincfg.Params, vinExtra int) []btcjson.VinPrevOut { // Coinbase transactions only have a single txin by definition. vinList := make([]btcjson.VinPrevOut, len(mtx.TxIn)) if blockchain.IsCoinBaseTx(mtx) { txIn := mtx.TxIn[0] vinList[0].Coinbase = hex.EncodeToString(txIn.SignatureScript) vinList[0].Sequence = txIn.Sequence return vinList } // Lookup all of the referenced transactions needed to populate the // previous output information if requested. var txStore blockchain.TxStore if vinExtra != 0 { tx := btcutil.NewTx(mtx) txStoreNew, err := s.server.txMemPool.fetchInputTransactions(tx, true) if err == nil { txStore = txStoreNew } } for i, txIn := range mtx.TxIn { // The disassembled string will contain [error] inline // if the script doesn't fully parse, so ignore the // error here. disbuf, _ := txscript.DisasmString(txIn.SignatureScript) vinEntry := &vinList[i] vinEntry.Txid = txIn.PreviousOutPoint.Hash.String() vinEntry.Vout = txIn.PreviousOutPoint.Index vinEntry.Sequence = txIn.Sequence vinEntry.ScriptSig = &btcjson.ScriptSig{ Asm: disbuf, Hex: hex.EncodeToString(txIn.SignatureScript), } // Only populate previous output information if requested and // available. if vinExtra == 0 || len(txStore) == 0 { continue } txData := txStore[txIn.PreviousOutPoint.Hash] if txData == nil { continue } originTxOut := txData.Tx.MsgTx().TxOut[txIn.PreviousOutPoint.Index] // Ignore the error here since an error means the script // couldn't parse and there is no additional information about // it anyways. var strAddrs []string _, addrs, _, _ := txscript.ExtractPkScriptAddrs( originTxOut.PkScript, chainParams) if addrs != nil { strAddrs = make([]string, len(addrs)) for j, addr := range addrs { strAddrs[j] = addr.EncodeAddress() } } vinEntry.PrevOut = &btcjson.PrevOut{ Addresses: strAddrs, Value: btcutil.Amount(originTxOut.Value).ToBTC(), } } return vinList } // createVoutList returns a slice of JSON objects for the outputs of the passed // transaction. func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params) []btcjson.Vout { voutList := make([]btcjson.Vout, len(mtx.TxOut)) for i, v := range mtx.TxOut { voutList[i].N = uint32(i) voutList[i].Value = btcutil.Amount(v.Value).ToBTC() // The disassembled string will contain [error] inline if the // script doesn't fully parse, so ignore the error here. disbuf, _ := txscript.DisasmString(v.PkScript) 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, _ := txscript.ExtractPkScriptAddrs( v.PkScript, chainParams) 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 } // createSearchRawTransactionsResult converts the passed transaction and associated parameters // to a raw transaction JSON object, possibly with vin.PrevOut section. func createSearchRawTransactionsResult(s *rpcServer, chainParams *chaincfg.Params, mtx *wire.MsgTx, txHash string, blkHeader *wire.BlockHeader, blkHash string, blkHeight int32, chainHeight int32, vinExtra int) (*btcjson.SearchRawTransactionsResult, error) { mtxHex, err := messageToHex(mtx) if err != nil { return nil, err } txReply := &btcjson.SearchRawTransactionsResult{ Hex: mtxHex, Txid: txHash, Vout: createVoutList(mtx, chainParams), Vin: createVinListPrevOut(s, mtx, chainParams, vinExtra), Version: mtx.Version, LockTime: mtx.LockTime, } if blkHeader != nil { // This is not a typo, they are identical in bitcoind as well. txReply.Time = blkHeader.Timestamp.Unix() txReply.Blocktime = blkHeader.Timestamp.Unix() txReply.BlockHash = blkHash txReply.Confirmations = uint64(1 + chainHeight - blkHeight) } return txReply, nil } // createTxRawResult converts the passed transaction and associated parameters // to a raw transaction JSON object. func createTxRawResult(chainParams *chaincfg.Params, mtx *wire.MsgTx, txHash string, blkHeader *wire.BlockHeader, blkHash string, blkHeight int32, chainHeight int32) (*btcjson.TxRawResult, error) { mtxHex, err := messageToHex(mtx) if err != nil { return nil, err } txReply := &btcjson.TxRawResult{ Hex: mtxHex, Txid: txHash, Vout: createVoutList(mtx, chainParams), Vin: createVinList(mtx), Version: mtx.Version, LockTime: mtx.LockTime, } if blkHeader != nil { // This is not a typo, they are identical in bitcoind as well. txReply.Time = blkHeader.Timestamp.Unix() txReply.Blocktime = blkHeader.Timestamp.Unix() txReply.BlockHash = blkHash txReply.Confirmations = uint64(1 + chainHeight - blkHeight) } return txReply, nil } // handleDecodeRawTransaction handles decoderawtransaction commands. func handleDecodeRawTransaction(s *rpcServer, cmd interface{}, 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, rpcDecodeHexError(hexStr) } var mtx wire.MsgTx err = mtx.Deserialize(bytes.NewReader(serializedTx)) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCDeserialization, Message: "TX decode failed: " + err.Error(), } } // Create and return the result. txReply := btcjson.TxRawDecodeResult{ Txid: mtx.TxSha().String(), Version: mtx.Version, Locktime: mtx.LockTime, Vin: createVinList(&mtx), Vout: createVoutList(&mtx, s.server.chainParams), } return txReply, nil } // handleDecodeScript handles decodescript commands. func handleDecodeScript(s *rpcServer, cmd interface{}, 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, rpcDecodeHexError(hexStr) } // The disassembled string will contain [error] inline if the script // doesn't fully parse, so ignore the error here. disbuf, _ := txscript.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. scriptClass, addrs, reqSigs, _ := txscript.ExtractPkScriptAddrs(script, s.server.chainParams) 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, s.server.chainParams) if err != nil { context := "Failed to convert script to pay-to-script-hash" return nil, internalRPCError(err.Error(), context) } // Generate and return the reply. reply := btcjson.DecodeScriptResult{ Asm: disbuf, ReqSigs: int32(reqSigs), Type: scriptClass.String(), Addresses: addresses, P2sh: p2sh.EncodeAddress(), } return reply, nil } // handleGenerate handles generate commands. func handleGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { // Respond with an error if there are no addresses to pay the // created blocks to. if len(cfg.miningAddrs) == 0 { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInternal.Code, Message: "No payment addresses specified " + "via --miningaddr", } } c := cmd.(*btcjson.GenerateCmd) // Respond with an error if the client is requesting 0 blocks to be generated. if c.NumBlocks == 0 { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInternal.Code, Message: "Please request a nonzero number of blocks to generate.", } } // Create a reply reply := make([]string, c.NumBlocks) blockHashes, err := s.server.cpuMiner.GenerateNBlocks(c.NumBlocks) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInternal.Code, Message: err.Error(), } } // Mine the correct number of blocks, assigning the hex representation of the // hash of each one to its place in the reply. for i, hash := range blockHashes { reply[i] = hash.String() } return reply, nil } // handleGetAddedNodeInfo handles getaddednodeinfo commands. func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, 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 != nil { node := *c.Node found := false for i, peer := range peers { if peer.Addr() == node { peers = peers[i : i+1] found = true } } if !found { return nil, &btcjson.RPCError{ Code: -24, // TODO: ErrRPCClientNodeNotAdded 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() result.Connected = btcjson.Bool(peer.Connected()) // 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 interface{}, 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.RPCError{ Code: btcjson.ErrRPCBestBlockHash, Message: "Error getting best block hash", } } result := &btcjson.GetBestBlockResult{ Hash: sha.String(), Height: int32(height), } return result, nil } // handleGetBestBlockHash implements the getbestblockhash command. func handleGetBestBlockHash(s *rpcServer, cmd interface{}, 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.RPCError{ Code: btcjson.ErrRPCBestBlockHash, Message: "Error getting best block hash", } } return sha.String(), nil } // 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 := blockchain.CompactToBig(activeNetParams.PowLimitBits) target := blockchain.CompactToBig(bits) difficulty := new(big.Rat).SetFrac(max, target) outString := difficulty.FloatString(8) diff, err := strconv.ParseFloat(outString, 64) if err != nil { rpcsLog.Errorf("Cannot get difficulty: %v", err) return 0 } return diff } // handleGetBlock implements the getblock command. func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetBlockCmd) sha, err := wire.NewShaHashFromStr(c.Hash) if err != nil { return nil, rpcDecodeHexError(c.Hash) } blk, err := s.server.db.FetchBlockBySha(sha) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCBlockNotFound, Message: "Block not found", } } // When the verbose flag isn't set, simply return the network-serialized // block as a hex-encoded string. if c.Verbose != nil && !*c.Verbose { // Note that this is intentionally not directly returning // because the first return value is a string and it would // result in returning an empty string to the client instead of // nothing (nil) in the case of an error. 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 { context := "Failed to get block bytes" return nil, internalRPCError(err.Error(), context) } idx := blk.Height() _, maxIdx, err := s.server.db.NewestSha() if err != nil { context := "Failed to get newest hash" return nil, internalRPCError(err.Error(), context) } blockHeader := &blk.MsgBlock().Header blockReply := btcjson.GetBlockVerboseResult{ 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: int64(idx), Size: int32(len(buf)), Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), Difficulty: getDifficultyRatio(blockHeader.Bits), } if c.VerboseTx == nil || !*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 { rawTxn, err := createTxRawResult(s.server.chainParams, tx.MsgTx(), tx.Sha().String(), blockHeader, sha.String(), idx, maxIdx) if err != nil { return nil, err } rawTxns[i] = *rawTxn } blockReply.RawTx = rawTxns } // Get next block unless we are already at the top. if idx < maxIdx { var shaNext *wire.ShaHash shaNext, err = s.server.db.FetchBlockShaByHeight(idx + 1) if err != nil { context := "No next block" return nil, internalRPCError(err.Error(), context) } blockReply.NextHash = shaNext.String() } return blockReply, nil } // handleGetBlockCount implements the getblockcount command. func handleGetBlockCount(s *rpcServer, cmd interface{}, 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.RPCError{ Code: btcjson.ErrRPCBlockCount, Message: "Error getting block count: " + err.Error(), } } return maxIdx, nil } // handleGetBlockHash implements the getblockhash command. func handleGetBlockHash(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetBlockHashCmd) sha, err := s.server.db.FetchBlockShaByHeight(int32(c.Index)) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCOutOfRange, Message: "Block number out of range", } } return sha.String(), nil } // handleGetBlockHeader implements the getblockheader command. func handleGetBlockHeader(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetBlockHeaderCmd) sha, err := wire.NewShaHashFromStr(c.Hash) if err != nil { return nil, err } if c.Verbose == nil || *c.Verbose { blk, err := s.server.db.FetchBlockBySha(sha) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidAddressOrKey, Message: "Invalid address or key: " + err.Error(), } } _, maxIdx, err := s.server.db.NewestSha() if err != nil { context := "Failed to get newest hash" return nil, internalRPCError(err.Error(), context) } var shaNextStr string shaNext, err := s.server.db.FetchBlockShaByHeight(blk.Height() + 1) if err == nil { shaNextStr = shaNext.String() } msgBlock := blk.MsgBlock() blockHeaderReply := btcjson.GetBlockHeaderVerboseResult{ Hash: c.Hash, Confirmations: uint64(1 + maxIdx - blk.Height()), Height: int32(blk.Height()), Version: msgBlock.Header.Version, MerkleRoot: msgBlock.Header.MerkleRoot.String(), NextHash: shaNextStr, PreviousHash: msgBlock.Header.PrevBlock.String(), Nonce: uint64(msgBlock.Header.Nonce), Time: msgBlock.Header.Timestamp.Unix(), Bits: strconv.FormatInt(int64(msgBlock.Header.Bits), 16), Difficulty: getDifficultyRatio(msgBlock.Header.Bits), } return blockHeaderReply, nil } // Verbose disabled blkHeader, err := s.server.db.FetchBlockHeaderBySha(sha) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidAddressOrKey, Message: "Invalid address or key: " + err.Error(), } } buf := bytes.NewBuffer(make([]byte, 0, wire.MaxBlockHeaderPayload)) if err = blkHeader.BtcEncode(buf, maxProtocolVersion); err != nil { errStr := fmt.Sprintf("Failed to serialize data: %v", err) return nil, internalRPCError(errStr, "") } return hex.EncodeToString(buf.Bytes()), nil } // encodeTemplateID encodes the passed details into an ID that can be used to // uniquely identify a block template. func encodeTemplateID(prevHash *wire.ShaHash, lastGenerated time.Time) string { return fmt.Sprintf("%s-%d", prevHash.String(), lastGenerated.Unix()) } // decodeTemplateID decodes an ID that is used to uniquely identify a block // template. This is mainly used as a mechanism to track when to update clients // that are using long polling for block templates. The ID consists of the // previous block hash for the associated template and the time the associated // template was generated. func decodeTemplateID(templateID string) (*wire.ShaHash, int64, error) { fields := strings.Split(templateID, "-") if len(fields) != 2 { return nil, 0, errors.New("invalid longpollid format") } prevHash, err := wire.NewShaHashFromStr(fields[0]) if err != nil { return nil, 0, errors.New("invalid longpollid format") } lastGenerated, err := strconv.ParseInt(fields[1], 10, 64) if err != nil { return nil, 0, errors.New("invalid longpollid format") } return prevHash, lastGenerated, nil } // notifyLongPollers notifies any channels that have been registered to be // notified when block templates are stale. // // This function MUST be called with the state locked. func (state *gbtWorkState) notifyLongPollers(latestHash *wire.ShaHash, lastGenerated time.Time) { // Notify anything that is waiting for a block template update from a // hash which is not the hash of the tip of the best chain since their // work is now invalid. for hash, channels := range state.notifyMap { if !hash.IsEqual(latestHash) { for _, c := range channels { close(c) } delete(state.notifyMap, hash) } } // Return now if the provided last generated timestamp has not been // initialized. if lastGenerated.IsZero() { return } // Return now if there is nothing registered for updates to the current // best block hash. channels, ok := state.notifyMap[*latestHash] if !ok { return } // Notify anything that is waiting for a block template update from a // block template generated before the most recently generated block // template. lastGeneratedUnix := lastGenerated.Unix() for lastGen, c := range channels { if lastGen < lastGeneratedUnix { close(c) delete(channels, lastGen) } } // Remove the entry altogether if there are no more registered // channels. if len(channels) == 0 { delete(state.notifyMap, *latestHash) } } // NotifyBlockConnected uses the newly-connected block to notify any long poll // clients with a new block template when their existing block template is // stale due to the newly connected block. func (state *gbtWorkState) NotifyBlockConnected(blockSha *wire.ShaHash) { go func() { state.Lock() defer state.Unlock() state.notifyLongPollers(blockSha, state.lastTxUpdate) }() } // NotifyMempoolTx uses the new last updated time for the transaction memory // pool to notify any long poll clients with a new block template when their // existing block template is stale due to enough time passing and the contents // of the memory pool changing. func (state *gbtWorkState) NotifyMempoolTx(lastUpdated time.Time) { go func() { state.Lock() defer state.Unlock() // No need to notify anything if no block templates have been generated // yet. if state.prevHash == nil || state.lastGenerated.IsZero() { return } if time.Now().After(state.lastGenerated.Add(time.Second * gbtRegenerateSeconds)) { state.notifyLongPollers(state.prevHash, lastUpdated) } }() } // templateUpdateChan returns a channel that will be closed once the block // template associated with the passed previous hash and last generated time // is stale. The function will return existing channels for duplicate // parameters which allows multiple clients to wait for the same block template // without requiring a different channel for each client. // // This function MUST be called with the state locked. func (state *gbtWorkState) templateUpdateChan(prevHash *wire.ShaHash, lastGenerated int64) chan struct{} { // Either get the current list of channels waiting for updates about // changes to block template for the previous hash or create a new one. channels, ok := state.notifyMap[*prevHash] if !ok { m := make(map[int64]chan struct{}) state.notifyMap[*prevHash] = m channels = m } // Get the current channel associated with the time the block template // was last generated or create a new one. c, ok := channels[lastGenerated] if !ok { c = make(chan struct{}) channels[lastGenerated] = c } return c } // updateBlockTemplate creates or updates a block template for the work state. // A new block template will be generated when the current best block has // changed or the transactions in the memory pool have been updated and it has // been some time has passed since the last template was generated. Otherwise, // the timestamp for the existing block template is updated (and possibly the // difficulty on testnet per the consesus rules). Finally, if the // useCoinbaseValue flag is flase and the existing block template does not // already contain a valid payment address, the block template will be updated // with a randomly selected payment address from the list of configured // addresses. // // This function MUST be called with the state locked. func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bool) error { lastTxUpdate := s.server.txMemPool.LastUpdated() if lastTxUpdate.IsZero() { lastTxUpdate = time.Now() } // 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 gbtRegenerateSecond since the last template was // generated. var msgBlock *wire.MsgBlock var targetDifficulty string latestHash, _ := s.server.blockManager.chainState.Best() template := state.template if template == nil || state.prevHash == nil || !state.prevHash.IsEqual(latestHash) || (state.lastTxUpdate != lastTxUpdate && time.Now().After(state.lastGenerated.Add(time.Second* gbtRegenerateSeconds))) { // 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 if the caller requests a // full coinbase as opposed to only the pertinent details needed // to create their own coinbase. var payAddr btcutil.Address if !useCoinbaseValue { payAddr = cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] } // Create a new block template that has a coinbase which anyone // can redeem. This is only acceptable because the returned // block template doesn't include the coinbase, so the caller // will ultimately create their own coinbase which pays to the // appropriate address(es). blkTemplate, err := NewBlockTemplate(s.server, payAddr) if err != nil { return internalRPCError("Failed to create new block "+ "template: "+err.Error(), "") } template = blkTemplate msgBlock = template.block targetDifficulty = fmt.Sprintf("%064x", blockchain.CompactToBig(msgBlock.Header.Bits)) // Find the minimum allowed timestamp for the block based on the // median timestamp of the last several blocks per the chain // consensus rules. chainState := &s.server.blockManager.chainState minTimestamp, err := minimumMedianTime(chainState) if err != nil { context := "Failed to get minimum median time" return internalRPCError(err.Error(), context) } // Update work state to ensure another block template isn't // generated until needed. state.template = template state.lastGenerated = time.Now() state.lastTxUpdate = lastTxUpdate state.prevHash = latestHash state.minTimestamp = minTimestamp rpcsLog.Debugf("Generated block template (timestamp %v, "+ "target %s, merkle root %s)", msgBlock.Header.Timestamp, targetDifficulty, msgBlock.Header.MerkleRoot) // Notify any clients that are long polling about the new // template. state.notifyLongPollers(latestHash, lastTxUpdate) } else { // At this point, there is a saved block template and another // request for a template 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. // When the caller requires a full coinbase as opposed to only // the pertinent details needed to create their own coinbase, // add a payment address to the output of the coinbase of the // template if it doesn't already have one. Since this requires // mining addresses to be specified via the config, an error is // returned if none have been specified. if !useCoinbaseValue && !template.validPayAddress { // Choose a payment address at random. payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] // Update the block coinbase output of the template to // pay to the randomly selected payment address. pkScript, err := txscript.PayToAddrScript(payToAddr) if err != nil { context := "Failed to create pay-to-addr script" return internalRPCError(err.Error(), context) } template.block.Transactions[0].TxOut[0].PkScript = pkScript template.validPayAddress = true // Update the merkle root. block := btcutil.NewBlock(template.block) merkles := blockchain.BuildMerkleTreeStore(block.Transactions()) template.block.Header.MerkleRoot = *merkles[len(merkles)-1] } // Set locals for convenience. msgBlock = template.block targetDifficulty = fmt.Sprintf("%064x", blockchain.CompactToBig(msgBlock.Header.Bits)) // 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) msgBlock.Header.Nonce = 0 rpcsLog.Debugf("Updated block template (timestamp %v, "+ "target %s)", msgBlock.Header.Timestamp, targetDifficulty) } return nil } // blockTemplateResult returns the current block template associated with the // state as a btcjson.GetBlockTemplateResult that is ready to be encoded to JSON // and returned to the caller. // // This function MUST be called with the state locked. func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld *bool) (*btcjson.GetBlockTemplateResult, error) { // Ensure the timestamps are still in valid range for the template. // This should really only ever happen if the local clock is changed // after the template is generated, but it's important to avoid serving // invalid block templates. template := state.template msgBlock := template.block header := &msgBlock.Header adjustedTime := state.timeSource.AdjustedTime() maxTime := adjustedTime.Add(time.Second * blockchain.MaxTimeOffsetSeconds) if header.Timestamp.After(maxTime) { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCOutOfRange, Message: fmt.Sprintf("The template time is after the "+ "maximum allowed time for a block - template "+ "time %v, maximum time %v", adjustedTime, maxTime), } } // Convert each transaction in the block template to a template result // transaction. The result does not include the coinbase, so notice // the adjustments to the various lengths and indices. numTx := len(msgBlock.Transactions) transactions := make([]btcjson.GetBlockTemplateResultTx, 0, numTx-1) txIndex := make(map[wire.ShaHash]int64, numTx) for i, tx := range msgBlock.Transactions { txHash := tx.TxSha() txIndex[txHash] = int64(i) // Skip the coinbase transaction. if i == 0 { continue } // Create an array of 1-based indices to transactions that come // before this one in the transactions list which this one // depends on. This is necessary since the created block must // ensure proper ordering of the dependencies. A map is used // before creating the final array to prevent duplicate entries // when mutiple inputs reference the same transaction. dependsMap := make(map[int64]struct{}) for _, txIn := range tx.TxIn { if idx, ok := txIndex[txIn.PreviousOutPoint.Hash]; ok { dependsMap[idx] = struct{}{} } } depends := make([]int64, 0, len(dependsMap)) for idx := range dependsMap { depends = append(depends, idx) } // Serialize the transaction for later conversion to hex. txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) if err := tx.Serialize(txBuf); err != nil { context := "Failed to serialize transaction" return nil, internalRPCError(err.Error(), context) } resultTx := btcjson.GetBlockTemplateResultTx{ Data: hex.EncodeToString(txBuf.Bytes()), Hash: txHash.String(), Depends: depends, Fee: template.fees[i], SigOps: template.sigOpCounts[i], } transactions = append(transactions, resultTx) } // Generate the block template reply. Note that following mutations are // implied by the included or omission of fields: // Including MinTime -> time/decrement // Omitting CoinbaseTxn -> coinbase, generation targetDifficulty := fmt.Sprintf("%064x", blockchain.CompactToBig(header.Bits)) templateID := encodeTemplateID(state.prevHash, state.lastGenerated) reply := btcjson.GetBlockTemplateResult{ Bits: strconv.FormatInt(int64(header.Bits), 16), CurTime: header.Timestamp.Unix(), Height: int64(template.height), PreviousHash: header.PrevBlock.String(), SigOpLimit: blockchain.MaxSigOpsPerBlock, SizeLimit: wire.MaxBlockPayload, Transactions: transactions, Version: header.Version, LongPollID: templateID, SubmitOld: submitOld, Target: targetDifficulty, MinTime: state.minTimestamp.Unix(), MaxTime: maxTime.Unix(), Mutable: gbtMutableFields, NonceRange: gbtNonceRange, Capabilities: gbtCapabilities, } if useCoinbaseValue { reply.CoinbaseAux = gbtCoinbaseAux reply.CoinbaseValue = &msgBlock.Transactions[0].TxOut[0].Value } else { // Ensure the template has a valid payment address associated // with it when a full coinbase is requested. if !template.validPayAddress { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInternal.Code, Message: "A coinbase transaction has been " + "requested, but the server has not " + "been configured with any payment " + "addresses via --miningaddr", } } // Serialize the transaction for conversion to hex. tx := msgBlock.Transactions[0] txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) if err := tx.Serialize(txBuf); err != nil { context := "Failed to serialize transaction" return nil, internalRPCError(err.Error(), context) } resultTx := btcjson.GetBlockTemplateResultTx{ Data: hex.EncodeToString(txBuf.Bytes()), Hash: tx.TxSha().String(), Depends: []int64{}, Fee: template.fees[0], SigOps: template.sigOpCounts[0], } reply.CoinbaseTxn = &resultTx } return &reply, nil } // handleGetBlockTemplateLongPoll a helper for handleGetBlockTemplateRequest // which deals with handling long polling for block templates. When a caller // sends a request with a long poll ID that was previously returned, a response // is not sent until the caller should stop working on the previous block // template in favor of the new one. In particular, this is the case when the // old block template is no longer valid due to a solution already being found // and added to the block chain, or new transactions have shown up and some time // has passed without finding a solution. // // See https://en.bitcoin.it/wiki/BIP_0022 for more details. func handleGetBlockTemplateLongPoll(s *rpcServer, longPollID string, useCoinbaseValue bool, closeChan <-chan struct{}) (interface{}, error) { state := s.gbtWorkState state.Lock() // The state unlock is intentionally not deferred here since it needs to // be manually unlocked before waiting for a notification about block // template changes. if err := state.updateBlockTemplate(s, useCoinbaseValue); err != nil { state.Unlock() return nil, err } // Just return the current block template if the the long poll ID // provided by the caller is invalid. prevHash, lastGenerated, err := decodeTemplateID(longPollID) if err != nil { result, err := state.blockTemplateResult(useCoinbaseValue, nil) if err != nil { state.Unlock() return nil, err } state.Unlock() return result, nil } // Return the block template now if the specific block template // identified by the long poll ID no longer matches the current block // template as this means the provided template is stale. prevTemplateHash := &state.template.block.Header.PrevBlock if !prevHash.IsEqual(prevTemplateHash) || lastGenerated != state.lastGenerated.Unix() { // Include whether or not it is valid to submit work against the // old block template depending on whether or not a solution has // already been found and added to the block chain. submitOld := prevHash.IsEqual(prevTemplateHash) result, err := state.blockTemplateResult(useCoinbaseValue, &submitOld) if err != nil { state.Unlock() return nil, err } state.Unlock() return result, nil } // Register the previous hash and last generated time for notifications // Get a channel that will be notified when the template associated with // the provided ID is is stale and a new block template should be // returned to the caller. longPollChan := state.templateUpdateChan(prevHash, lastGenerated) state.Unlock() select { // When the client closes before it's time to send a reply, just return // now so the goroutine doesn't hang around. case <-closeChan: return nil, ErrClientQuit // Wait until signal received to send the reply. case <-longPollChan: // Fallthrough } // Get the lastest block template state.Lock() defer state.Unlock() if err := state.updateBlockTemplate(s, useCoinbaseValue); err != nil { return nil, err } // Include whether or not it is valid to submit work against the old // block template depending on whether or not a solution has already // been found and added to the block chain. submitOld := prevHash.IsEqual(&state.template.block.Header.PrevBlock) result, err := state.blockTemplateResult(useCoinbaseValue, &submitOld) if err != nil { return nil, err } return result, nil } // handleGetBlockTemplateRequest is a helper for handleGetBlockTemplate which // deals with generating and returning block templates to the caller. It // handles both long poll requests as specified by BIP 0022 as well as regular // requests. In addition, it detects the capabilities reported by the caller // in regards to whether or not it supports creating its own coinbase (the // coinbasetxn and coinbasevalue capabilities) and modifies the returned block // template accordingly. func handleGetBlockTemplateRequest(s *rpcServer, request *btcjson.TemplateRequest, closeChan <-chan struct{}) (interface{}, error) { // Extract the relevant passed capabilities and restrict the result to // either a coinbase value or a coinbase transaction object depending on // the request. Default to only providing a coinbase value. useCoinbaseValue := true if request != nil { var hasCoinbaseValue, hasCoinbaseTxn bool for _, capability := range request.Capabilities { switch capability { case "coinbasetxn": hasCoinbaseTxn = true case "coinbasevalue": hasCoinbaseValue = true } } if hasCoinbaseTxn && !hasCoinbaseValue { useCoinbaseValue = false } } // When a coinbase transaction has been requested, respond with an error // if there are no addresses to pay the created block template to. if !useCoinbaseValue && len(cfg.miningAddrs) == 0 { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInternal.Code, Message: "A coinbase transaction has been requested, " + "but the server has not been configured with " + "any payment addresses 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.RPCError{ Code: btcjson.ErrRPCClientNotConnected, Message: "Bitcoin is not connected", } } // 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.RPCError{ Code: btcjson.ErrRPCClientInInitialDownload, Message: "Bitcoin is downloading blocks...", } } // When a long poll ID was provided, this is a long poll request by the // client to be notified when block template referenced by the ID should // be replaced with a new one. if request != nil && request.LongPollID != "" { return handleGetBlockTemplateLongPoll(s, request.LongPollID, useCoinbaseValue, closeChan) } // Protect concurrent access when updating block templates. state := s.gbtWorkState state.Lock() defer state.Unlock() // Get and return a block template. A new block template will be // generated when the current best block has changed or the transactions // in the memory pool have been updated and it has been at least five // seconds since the last template was generated. Otherwise, the // timestamp for the existing block template is updated (and possibly // the difficulty on testnet per the consesus rules). if err := state.updateBlockTemplate(s, useCoinbaseValue); err != nil { return nil, err } return state.blockTemplateResult(useCoinbaseValue, nil) } // chainErrToGBTErrString converts an error returned from btcchain to a string // which matches the reasons and format described in BIP0022 for rejection // reasons. func chainErrToGBTErrString(err error) string { // When the passed error is not a RuleError, just return a generic // rejected string with the error text. ruleErr, ok := err.(blockchain.RuleError) if !ok { return "rejected: " + err.Error() } switch ruleErr.ErrorCode { case blockchain.ErrDuplicateBlock: return "duplicate" case blockchain.ErrBlockTooBig: return "bad-block-size" case blockchain.ErrBlockVersionTooOld: return "bad-version" case blockchain.ErrInvalidTime: return "bad-time" case blockchain.ErrTimeTooOld: return "time-too-old" case blockchain.ErrTimeTooNew: return "time-too-new" case blockchain.ErrDifficultyTooLow: return "bad-diffbits" case blockchain.ErrUnexpectedDifficulty: return "bad-diffbits" case blockchain.ErrHighHash: return "high-hash" case blockchain.ErrBadMerkleRoot: return "bad-txnmrklroot" case blockchain.ErrBadCheckpoint: return "bad-checkpoint" case blockchain.ErrForkTooOld: return "fork-too-old" case blockchain.ErrCheckpointTimeTooOld: return "checkpoint-time-too-old" case blockchain.ErrNoTransactions: return "bad-txns-none" case blockchain.ErrTooManyTransactions: return "bad-txns-toomany" case blockchain.ErrNoTxInputs: return "bad-txns-noinputs" case blockchain.ErrNoTxOutputs: return "bad-txns-nooutputs" case blockchain.ErrTxTooBig: return "bad-txns-size" case blockchain.ErrBadTxOutValue: return "bad-txns-outputvalue" case blockchain.ErrDuplicateTxInputs: return "bad-txns-dupinputs" case blockchain.ErrBadTxInput: return "bad-txns-badinput" case blockchain.ErrMissingTx: return "bad-txns-missinginput" case blockchain.ErrUnfinalizedTx: return "bad-txns-unfinalizedtx" case blockchain.ErrDuplicateTx: return "bad-txns-duplicate" case blockchain.ErrOverwriteTx: return "bad-txns-overwrite" case blockchain.ErrImmatureSpend: return "bad-txns-maturity" case blockchain.ErrDoubleSpend: return "bad-txns-dblspend" case blockchain.ErrSpendTooHigh: return "bad-txns-highspend" case blockchain.ErrBadFees: return "bad-txns-fees" case blockchain.ErrTooManySigOps: return "high-sigops" case blockchain.ErrFirstTxNotCoinbase: return "bad-txns-nocoinbase" case blockchain.ErrMultipleCoinbases: return "bad-txns-multicoinbase" case blockchain.ErrBadCoinbaseScriptLen: return "bad-cb-length" case blockchain.ErrBadCoinbaseValue: return "bad-cb-value" case blockchain.ErrMissingCoinbaseHeight: return "bad-cb-height" case blockchain.ErrBadCoinbaseHeight: return "bad-cb-height" case blockchain.ErrScriptMalformed: return "bad-script-malformed" case blockchain.ErrScriptValidation: return "bad-script-validate" } return "rejected: " + err.Error() } // handleGetBlockTemplateProposal is a helper for handleGetBlockTemplate which // deals with block proposals. // // See https://en.bitcoin.it/wiki/BIP_0023 for more details. func handleGetBlockTemplateProposal(s *rpcServer, request *btcjson.TemplateRequest) (interface{}, error) { hexData := request.Data if hexData == "" { return false, &btcjson.RPCError{ Code: btcjson.ErrRPCType, Message: fmt.Sprintf("Data must contain the " + "hex-encoded serialized block that is being " + "proposed"), } } // Ensure the provided data is sane and deserialize the proposed block. if len(hexData)%2 != 0 { hexData = "0" + hexData } dataBytes, err := hex.DecodeString(hexData) if err != nil { return false, &btcjson.RPCError{ Code: btcjson.ErrRPCDeserialization, Message: fmt.Sprintf("Data must be "+ "hexadecimal string (not %q)", hexData), } } var msgBlock wire.MsgBlock if err := msgBlock.Deserialize(bytes.NewReader(dataBytes)); err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCDeserialization, Message: "Block decode failed: " + err.Error(), } } block := btcutil.NewBlock(&msgBlock) // Ensure the block is building from the expected previous block. expectedPrevHash, _ := s.server.blockManager.chainState.Best() prevHash := &block.MsgBlock().Header.PrevBlock if expectedPrevHash == nil || !expectedPrevHash.IsEqual(prevHash) { return "bad-prevblk", nil } flags := blockchain.BFDryRun | blockchain.BFNoPoWCheck isOrphan, err := s.server.blockManager.ProcessBlock(block, flags) if err != nil { if _, ok := err.(blockchain.RuleError); !ok { err := rpcsLog.Errorf("Failed to process block "+ "proposal: %v", err) return nil, &btcjson.RPCError{ Code: -25, // TODO: ErrRpcVerify Message: err.Error(), } } rpcsLog.Infof("Rejected block proposal: %v", err) return chainErrToGBTErrString(err), nil } if isOrphan { return "orphan", nil } return nil, nil } // handleGetBlockTemplate implements the getblocktemplate command. // // See https://en.bitcoin.it/wiki/BIP_0022 and // https://en.bitcoin.it/wiki/BIP_0023 for more details. func handleGetBlockTemplate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetBlockTemplateCmd) request := c.Request // Set the default mode and override it if supplied. mode := "template" if request != nil && request.Mode != "" { mode = request.Mode } switch mode { case "template": return handleGetBlockTemplateRequest(s, request, closeChan) case "proposal": return handleGetBlockTemplateProposal(s, request) } return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, Message: "Invalid mode", } } // handleGetConnectionCount implements the getconnectioncount command. func handleGetConnectionCount(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return s.server.ConnectedCount(), nil } // handleGetCurrentNet implements the getcurrentnet command. func handleGetCurrentNet(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return s.server.chainParams.Net, nil } // handleGetDifficulty implements the getdifficulty command. func handleGetDifficulty(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { sha, _, err := s.server.db.NewestSha() if err != nil { rpcsLog.Errorf("Error getting sha: %v", err) return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCDifficulty, Message: "Error getting difficulty: " + err.Error(), } } blockHeader, err := s.server.db.FetchBlockHeaderBySha(sha) if err != nil { rpcsLog.Errorf("Error getting block: %v", err) return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCDifficulty, Message: "Error getting difficulty: " + err.Error(), } } return getDifficultyRatio(blockHeader.Bits), nil } // handleGetGenerate implements the getgenerate command. func handleGetGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return s.server.cpuMiner.IsMining(), nil } // handleGetHashesPerSec implements the gethashespersec command. func handleGetHashesPerSec(s *rpcServer, cmd interface{}, 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 interface{}, closeChan <-chan struct{}) (interface{}, error) { // We require the current block height and sha. sha, height, err := s.server.db.NewestSha() if err != nil { context := "Failed to get newest hash" return nil, internalRPCError(err.Error(), context) } blkHeader, err := s.server.db.FetchBlockHeaderBySha(sha) if err != nil { context := "Failed to get block" return nil, internalRPCError(err.Error(), context) } ret := &btcjson.InfoChainResult{ Version: int32(1000000*appMajor + 10000*appMinor + 100*appPatch), ProtocolVersion: int32(maxProtocolVersion), Blocks: int32(height), TimeOffset: int64(s.server.timeSource.Offset().Seconds()), Connections: s.server.ConnectedCount(), Proxy: cfg.Proxy, Difficulty: getDifficultyRatio(blkHeader.Bits), TestNet: cfg.TestNet3, RelayFee: cfg.minRelayTxFee.ToBTC(), } return ret, nil } // handleGetMempoolInfo implements the getmempoolinfo command. func handleGetMempoolInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { txD := s.server.txMemPool.TxDescs() var numBytes int64 for _, desc := range txD { numBytes += int64(desc.Tx.MsgTx().SerializeSize()) } ret := &btcjson.GetMempoolInfoResult{ Size: int64(len(txD)), Bytes: numBytes, } 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 interface{}, closeChan <-chan struct{}) (interface{}, error) { sha, height, err := s.server.db.NewestSha() if err != nil { context := "Failed to get newest hash" return nil, internalRPCError(err.Error(), context) } block, err := s.server.db.FetchBlockBySha(sha) if err != nil { context := "Failed to get block" return nil, internalRPCError(err.Error(), context) } blockBytes, err := block.Bytes() if err != nil { context := "Failed to get block bytes" return nil, internalRPCError(err.Error(), context) } // Create a default getnetworkhashps command to use defaults and make // use of the existing getnetworkhashps handler. gnhpsCmd := btcjson.NewGetNetworkHashPSCmd(nil, nil) networkHashesPerSecIface, err := handleGetNetworkHashPS(s, gnhpsCmd, closeChan) if err != nil { return nil, err } networkHashesPerSec, ok := networkHashesPerSecIface.(int64) if !ok { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInternal.Code, Message: "networkHashesPerSec is not an int64", } } result := btcjson.GetMiningInfoResult{ Blocks: int64(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 interface{}, 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 interface{}, closeChan <-chan struct{}) (interface{}, error) { // Note: All valid error return paths should return an int64. // Literal zeros are inferred as int, and won't coerce to int64 // because the return value is an interface{}. c := cmd.(*btcjson.GetNetworkHashPSCmd) _, newestHeight, err := s.server.db.NewestSha() if err != nil { context := "Failed to get newest hash" return nil, internalRPCError(err.Error(), context) } // 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 := int32(-1) if c.Height != nil { endHeight = int32(*c.Height) } if endHeight > newestHeight || endHeight == 0 { return int64(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. numBlocks := int32(120) if c.Blocks != nil { numBlocks = int32(*c.Blocks) } var startHeight int32 if numBlocks <= 0 { startHeight = endHeight - ((endHeight % blockchain.BlocksPerRetarget) + 1) } else { startHeight = endHeight - numBlocks } 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 { context := "Failed to fetch block hash" return nil, internalRPCError(err.Error(), context) } header, err := s.server.db.FetchBlockHeaderBySha(hash) if err != nil { context := "Failed to fetch block header" return nil, internalRPCError(err.Error(), context) } if curHeight == startHeight { minTimestamp = header.Timestamp maxTimestamp = minTimestamp } else { totalWork.Add(totalWork, blockchain.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 int64(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 interface{}, closeChan <-chan struct{}) (interface{}, error) { peers := s.server.Peers() syncPeer := s.server.blockManager.SyncPeer() infos := make([]*btcjson.GetPeerInfoResult, 0, len(peers)) for _, p := range peers { statsSnap := p.StatsSnapshot() info := &btcjson.GetPeerInfoResult{ ID: statsSnap.ID, Addr: statsSnap.Addr, Services: fmt.Sprintf("%08d", uint64(statsSnap.Services)), LastSend: statsSnap.LastSend.Unix(), LastRecv: statsSnap.LastRecv.Unix(), BytesSent: statsSnap.BytesSent, BytesRecv: statsSnap.BytesRecv, ConnTime: statsSnap.ConnTime.Unix(), PingTime: float64(statsSnap.LastPingMicros), TimeOffset: statsSnap.TimeOffset, Version: statsSnap.Version, SubVer: statsSnap.UserAgent, Inbound: statsSnap.Inbound, StartingHeight: statsSnap.StartingHeight, CurrentHeight: statsSnap.LastBlock, BanScore: 0, SyncNode: p == syncPeer, } if p.LastPingNonce() != 0 { wait := float64(time.Now().Sub(statsSnap.LastPingTime).Nanoseconds()) // We actually want microseconds. info.PingWait = wait / 1000 } infos = append(infos, info) } return infos, nil } // handleGetRawMempool implements the getrawmempool command. func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetRawMempoolCmd) mp := s.server.txMemPool descs := mp.TxDescs() if c.Verbose != nil && *c.Verbose { result := make(map[string]*btcjson.GetRawMempoolVerboseResult, len(descs)) _, newestHeight, err := s.server.db.NewestSha() if err != nil { context := "Failed to get newest hash" return nil, internalRPCError(err.Error(), context) } mp.RLock() defer mp.RUnlock() for _, desc := range descs { // Calculate the starting and current priority from the // the tx's inputs. Use zeros if one or more of the // input transactions can't be found for some reason. var startingPriority, currentPriority float64 inputTxs, err := mp.fetchInputTransactions(desc.Tx, false) if err == nil { startingPriority = desc.StartingPriority(inputTxs) currentPriority = desc.CurrentPriority(inputTxs, newestHeight+1) } mpd := &btcjson.GetRawMempoolVerboseResult{ Size: int32(desc.Tx.MsgTx().SerializeSize()), Fee: btcutil.Amount(desc.Fee).ToBTC(), Time: desc.Added.Unix(), Height: int64(desc.Height), StartingPriority: startingPriority, CurrentPriority: currentPriority, 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 interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetRawTransactionCmd) // Convert the provided transaction hash hex to a ShaHash. txHash, err := wire.NewShaHashFromStr(c.Txid) if err != nil { return nil, rpcDecodeHexError(c.Txid) } // Try to fetch the transaction from the memory pool and if that fails, // try the block database. var mtx *wire.MsgTx var blkHash *wire.ShaHash var blkHeight int32 tx, err := s.server.txMemPool.FetchTransaction(txHash) if err != nil { txList, err := s.server.db.FetchTxBySha(txHash) if err != nil || len(txList) == 0 { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCNoTxInfo, Message: "No information available about transaction", } } lastTx := txList[len(txList)-1] mtx = lastTx.Tx blkHash = lastTx.BlkSha blkHeight = lastTx.Height } 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 == nil || *c.Verbose == 0 { // Note that this is intentionally not directly returning // because the first return value is a string and it would // result in returning an empty string to the client instead of // nothing (nil) in the case of an error. mtxHex, err := messageToHex(mtx) if err != nil { return nil, err } return mtxHex, nil } var blkHeader *wire.BlockHeader var blkHashStr string var chainHeight int32 if blkHash != nil { blkHeader, err = s.server.db.FetchBlockHeaderBySha(blkHash) if err != nil { rpcsLog.Errorf("Error fetching sha: %v", err) return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCBlockNotFound, Message: "Block not found: " + err.Error(), } } _, chainHeight, err = s.server.db.NewestSha() if err != nil { context := "Failed to get newest hash" return nil, internalRPCError(err.Error(), context) } blkHashStr = blkHash.String() } rawTxn, err := createTxRawResult(s.server.chainParams, mtx, txHash.String(), blkHeader, blkHashStr, blkHeight, chainHeight) if err != nil { return nil, err } 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] } } // handleGetTxOut handles gettxout commands. func handleGetTxOut(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetTxOutCmd) // Convert the provided transaction hash hex to a ShaHash. txHash, err := wire.NewShaHashFromStr(c.Txid) if err != nil { return nil, rpcDecodeHexError(c.Txid) } // If requested and the tx is available in the mempool try to fetch it // from there, otherwise attempt to fetch from the block database. var mtx *wire.MsgTx var bestBlockSha string var confirmations int32 var dbSpentInfo []bool includeMempool := true if c.IncludeMempool != nil { includeMempool = *c.IncludeMempool } // TODO: This is racy. It should attempt to fetch it directly and check // the error. if includeMempool && s.server.txMemPool.HaveTransaction(txHash) { tx, err := s.server.txMemPool.FetchTransaction(txHash) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCNoTxInfo, Message: "No information available about transaction", } } mtx = tx.MsgTx() confirmations = 0 bestBlockSha = "" } else { txList, err := s.server.db.FetchTxBySha(txHash) if err != nil || len(txList) == 0 { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCNoTxInfo, Message: "No information available about transaction", } } lastTx := txList[len(txList)-1] mtx = lastTx.Tx blkHash := lastTx.BlkSha txHeight := lastTx.Height dbSpentInfo = lastTx.TxSpent _, bestHeight, err := s.server.db.NewestSha() if err != nil { context := "Failed to get newest hash" return nil, internalRPCError(err.Error(), context) } confirmations = 1 + bestHeight - txHeight bestBlockSha = blkHash.String() } if c.Vout > uint32(len(mtx.TxOut)-1) { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidTxVout, Message: "Ouput index number (vout) does not exist " + "for transaction.", } } txOut := mtx.TxOut[c.Vout] if txOut == nil { errStr := fmt.Sprintf("Output index: %d for txid: %s does "+ "not exist", c.Vout, c.Txid) return nil, internalRPCError(errStr, "") } // To match the behavior of the reference client, this handler returns // nil (JSON null) if the transaction output is spent by another // transaction already in the database. Unspent transaction outputs // from transactions in mempool, as well as mined transactions that are // spent by a mempool transaction, are not affected by this. if dbSpentInfo != nil && dbSpentInfo[c.Vout] { return nil, nil } // Disassemble script into single line printable format. // The disassembled string will contain [error] inline if the script // doesn't fully parse, so ignore the error here. script := txOut.PkScript disbuf, _ := txscript.DisasmString(script) // Get further info about the script. // 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, _ := txscript.ExtractPkScriptAddrs(script, s.server.chainParams) addresses := make([]string, len(addrs)) for i, addr := range addrs { addresses[i] = addr.EncodeAddress() } txOutReply := &btcjson.GetTxOutResult{ BestBlock: bestBlockSha, Confirmations: int64(confirmations), Value: btcutil.Amount(txOut.Value).ToUnit(btcutil.AmountBTC), Version: mtx.Version, ScriptPubKey: btcjson.ScriptPubKeyResult{ Asm: disbuf, Hex: hex.EncodeToString(script), ReqSigs: int32(reqSigs), Type: scriptClass.String(), Addresses: addresses, }, Coinbase: blockchain.IsCoinBase(btcutil.NewTx(mtx)), } return txOutReply, nil } // 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[wire.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. payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] template, err := NewBlockTemplate(s.server, payToAddr) if err != nil { context := "Failed to create new block template" return nil, internalRPCError(err.Error(), context) } 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, blockchain.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) return nil, internalRPCError(errStr, "") } rpcsLog.Debugf("Updated block template (timestamp %v, extra "+ "nonce %d, target %064x, merkle root %s, signature "+ "script %x)", msgBlock.Header.Timestamp, state.extraNonce, blockchain.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) return nil, internalRPCError(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[wire.MaxBlockHeaderPayload] = 0x80 binary.BigEndian.PutUint64(data[len(data)-8:], wire.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[wire.HashSize] = 0x80 binary.BigEndian.PutUint64(hash1[len(hash1)-8:], wire.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(blockchain.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, rpcDecodeHexError(hexData) } if len(data) != getworkDataLen { return false, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, 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 wire.BlockHeader bhBuf := bytes.NewReader(data[0:wire.MaxBlockHeaderPayload]) err = submittedHeader.Deserialize(bhBuf) if err != nil { return false, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, 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 := blockchain.BuildMerkleTreeStore(block.Transactions()) msgBlock.Header.MerkleRoot = *merkles[len(merkles)-1] // Ensure the submitted block hash is less than the target difficulty. err = blockchain.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.(blockchain.RuleError); !ok { return false, internalRPCError("Unexpected error "+ "while checking proof of work: "+err.Error(), "") } 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, blockchain.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.(blockchain.RuleError); !ok { return false, internalRPCError("Unexpected error "+ "while processing block: "+err.Error(), "") } rpcsLog.Infof("Block submitted via getwork rejected: %v", err) return false, nil } // The block was accepted. rpcsLog.Infof("Block submitted via getwork accepted: %s", block.Sha()) return true, nil } // handleGetWork implements the getwork command. func handleGetWork(s *rpcServer, cmd interface{}, 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.RPCError{ Code: btcjson.ErrRPCInternal.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.RPCError{ Code: btcjson.ErrRPCClientNotConnected, Message: "Bitcoin is not connected", } } // 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.RPCError{ Code: btcjson.ErrRPCClientInInitialDownload, Message: "Bitcoin is downloading blocks...", } } // 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 != nil && *c.Data != "" { return handleGetWorkSubmission(s, *c.Data) } // No data was provided, so the caller is requesting work. return handleGetWorkRequest(s) } // handleHelp implements the help command. func handleHelp(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.HelpCmd) // Provide a usage overview of all commands when no specific command // was specified. var command string if c.Command != nil { command = *c.Command } if command == "" { usage, err := s.helpCacher.rpcUsage(false) if err != nil { context := "Failed to generate RPC usage" return nil, internalRPCError(err.Error(), context) } return usage, nil } // Check that the command asked for is supported and implemented. Only // search the main list of handlers since help should not be provided // for commands that are unimplemented or related to wallet // functionality. if _, ok := rpcHandlers[command]; !ok { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParameter, Message: "Unknown command: " + command, } } // Get the help for the command. help, err := s.helpCacher.rpcMethodHelp(command) if err != nil { context := "Failed to generate help" return nil, internalRPCError(err.Error(), context) } return help, nil } // handlePing implements the ping command. func handlePing(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { // Ask server to ping \o_ nonce, err := wire.RandomUint64() if err != nil { return nil, internalRPCError("Not sending ping - failed to "+ "generate nonce: "+err.Error(), "") } s.server.BroadcastMessage(wire.NewMsgPing(nonce)) return nil, nil } // getMempoolTxsForAddressRange looks up and returns all transactions from the // mempool related to the given address. The, `limit` parameter // should be the max number of transactions to be returned. Additionally, if the // caller wishes to seek forward in the results some amount, the 'skip' parameter // represents how many results to skip. // It will return the array of fetched transactions, along with the amount // of transactions that were actually skipped. func getMempoolTxsForAddressRange(s *rpcServer, addr btcutil.Address, skip int, limit int) ([]*database.TxListReply, int, error) { memPoolTxs, err := s.server.txMemPool.FilterTransactionsByAddress(addr) if err != nil { return nil, 0, err } // If we're asked to skip more transactions than we have, // we skip them all and return an empty slice. if skip >= len(memPoolTxs) { return nil, len(memPoolTxs), nil } var result []*database.TxListReply // Otherwise, calculate the range we have to return and return it. rangeEnd := skip + limit if rangeEnd > len(memPoolTxs) { rangeEnd = len(memPoolTxs) } for _, tx := range memPoolTxs[skip:rangeEnd] { txReply := &database.TxListReply{Tx: tx.MsgTx(), Sha: tx.Sha()} result = append(result, txReply) } return result, skip, nil } // handleSearchRawTransaction implements the searchrawtransactions command. func handleSearchRawTransactions(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { if !cfg.AddrIndex { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCMisc, Message: "Address index must be enabled (--addrindex)", } } if !s.server.addrIndexer.IsCaughtUp() { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCMisc, Message: "Address index has not yet caught up to the " + "current best height", } } c := cmd.(*btcjson.SearchRawTransactionsCmd) // Attempt to decode the supplied address. addr, err := btcutil.DecodeAddress(c.Address, s.server.chainParams) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidAddressOrKey, Message: "Invalid address or key: " + err.Error(), } } var addressTxs []*database.TxListReply var numRequested, numToSkip, skipped int if c.Count != nil { numRequested = *c.Count if numRequested < 0 { numRequested = 1 } } if c.Skip != nil { numToSkip = *c.Skip if numToSkip < 0 { numToSkip = 0 } } var reverse bool if c.Reverse != nil { reverse = *c.Reverse } // Add txs from mempool first if client asked for reverse order, otherwise // add them last. // This code (and txMemPool.FilterTransactionsByAddress()) doesn't sort by // dependency. This might be something we want to do in the future when we // return results for the client's convenience, or leave it to the client. if reverse && len(addressTxs) < numRequested { memPoolTxs, memPoolSkipped, err := getMempoolTxsForAddressRange(s, addr, numToSkip-skipped, numRequested-len(addressTxs)) if err == nil { skipped += memPoolSkipped for _, txReply := range memPoolTxs { addressTxs = append(addressTxs, txReply) } } } // Fetch transactions from the database in the desired order if we need more. if len(addressTxs) < numRequested { dbTxs, dbSkipped, err := s.server.db.FetchTxsForAddr(addr, numToSkip-skipped, numRequested-len(addressTxs), reverse) if err == nil { skipped += dbSkipped for _, txReply := range dbTxs { addressTxs = append(addressTxs, txReply) } } } // Add txs from mempool last if the client didn't ask for reverse order. if !reverse && len(addressTxs) < numRequested { memPoolTxs, memPoolSkipped, err := getMempoolTxsForAddressRange(s, addr, numToSkip-skipped, numRequested-len(addressTxs)) if err == nil { skipped += memPoolSkipped for _, txReply := range memPoolTxs { addressTxs = append(addressTxs, txReply) } } } // If neither source yielded any results, then the address has never // been used. if len(addressTxs) == 0 { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCNoTxInfo, Message: "No information available about transaction", } } // When not in verbose mode, simply return a list of serialized txs. if c.Verbose != nil && *c.Verbose == 0 { serializedTxs := make([]string, len(addressTxs), len(addressTxs)) for i, txReply := range addressTxs { serializedTxs[i], err = messageToHex(txReply.Tx) if err != nil { return nil, err } } return serializedTxs, nil } // Otherwise, we'll need to populate raw tx results. // Grab the current best height for tx confirmation calculation. _, maxIdx, err := s.server.db.NewestSha() if err != nil { context := "Failed to get newest hash" return nil, internalRPCError(err.Error(), context) } rawTxns := make([]btcjson.SearchRawTransactionsResult, len(addressTxs), len(addressTxs)) for i, txReply := range addressTxs { txHash := txReply.Sha.String() mtx := txReply.Tx // Transactions grabbed from the mempool aren't yet // within a block. So we conditionally fetch a txs // embedded block here. This will be reflected in the // final JSON output (mempool won't have confirmations). var blkHeader *wire.BlockHeader var blkHashStr string var blkHeight int32 if txReply.BlkSha != nil { blkHeader, err = s.server.db.FetchBlockHeaderBySha(txReply.BlkSha) if err != nil { rpcsLog.Errorf("Error fetching sha: %v", err) return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCBlockNotFound, Message: "Block not found", } } blkHashStr = txReply.BlkSha.String() blkHeight = txReply.Height } rawTxn, err := createSearchRawTransactionsResult(s, s.server.chainParams, mtx, txHash, blkHeader, blkHashStr, blkHeight, maxIdx, *c.VinExtra) if err != nil { return nil, err } rawTxns[i] = *rawTxn } return rawTxns, nil } // handleSendRawTransaction implements the sendrawtransaction command. func handleSendRawTransaction(s *rpcServer, cmd interface{}, 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, rpcDecodeHexError(hexStr) } msgtx := wire.NewMsgTx() err = msgtx.Deserialize(bytes.NewReader(serializedTx)) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCDeserialization, Message: "TX decode failed: " + err.Error(), } } 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.(RuleError); ok { rpcsLog.Debugf("Rejected transaction %v: %v", tx.Sha(), err) } else { rpcsLog.Errorf("Failed to process transaction %v: %v", tx.Sha(), err) } return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCDeserialization, Message: "TX rejected: " + err.Error(), } } // Keep track of all the sendrawtransaction request txns so that they // can be rebroadcast if they don't make their way into a block. iv := wire.NewInvVect(wire.InvTypeTx, tx.Sha()) s.server.AddRebroadcastInventory(iv, tx) return tx.Sha().String(), nil } // handleSetGenerate implements the setgenerate command. func handleSetGenerate(s *rpcServer, cmd interface{}, 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 genProcLimit := -1 if c.GenProcLimit != nil { genProcLimit = *c.GenProcLimit } if 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.RPCError{ Code: btcjson.ErrRPCInternal.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(genProcLimit)) s.server.cpuMiner.Start() } return nil, nil } // handleStop implements the stop command. func handleStop(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { s.server.Stop() return "btcd stopping.", nil } // handleSubmitBlock implements the submitblock command. func handleSubmitBlock(s *rpcServer, cmd interface{}, 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, rpcDecodeHexError(hexStr) } block, err := btcutil.NewBlockFromBytes(serializedBlock) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCDeserialization, Message: "Block decode failed: " + err.Error(), } } _, err = s.server.blockManager.ProcessBlock(block, blockchain.BFNone) if err != nil { return fmt.Sprintf("rejected: %s", err.Error()), nil } rpcsLog.Infof("Accepted block %s via submitblock", block.Sha()) return nil, nil } // handleValidateAddress implements the validateaddress command. func handleValidateAddress(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.ValidateAddressCmd) result := btcjson.ValidateAddressChainResult{} 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 } func verifyChain(db database.Db, level, depth int32, timeSource blockchain.MedianTimeSource) 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(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 := blockchain.CheckBlockSanity(block, activeNetParams.PowLimit, timeSource) 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 } // handleVerifyChain implements the verifychain command. func handleVerifyChain(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.VerifyChainCmd) var checkLevel, checkDepth int32 if c.CheckLevel != nil { checkLevel = *c.CheckLevel } if c.CheckDepth != nil { checkDepth = *c.CheckDepth } err := verifyChain(s.server.db, checkLevel, checkDepth, s.server.timeSource) return err == nil, nil } // handleVerifyMessage implements the verifymessage command. func handleVerifyMessage(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.VerifyMessageCmd) // Decode the provided address. addr, err := btcutil.DecodeAddress(c.Address, activeNetParams.Params) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidAddressOrKey, Message: "Invalid address or key: " + err.Error(), } } // Only P2PKH addresses are valid for signing. if _, ok := addr.(*btcutil.AddressPubKeyHash); !ok { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCType, Message: "Address is not a pay-to-pubkey-hash address", } } // Decode base64 signature. sig, err := base64.StdEncoding.DecodeString(c.Signature) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCParse.Code, Message: "Malformed base64 encoding: " + err.Error(), } } // Validate the signature - this just shows that it was valid at all. // we will compare it with the key next. var buf bytes.Buffer wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n") wire.WriteVarString(&buf, 0, c.Message) expectedMessageHash := wire.DoubleSha256(buf.Bytes()) pk, wasCompressed, err := btcec.RecoverCompact(btcec.S256(), sig, expectedMessageHash) if err != nil { // Mirror Bitcoin Core behavior, which treats error in // RecoverCompact as invalid signature. return false, nil } // Reconstruct the pubkey hash. btcPK := (*btcec.PublicKey)(pk) var serializedPK []byte if wasCompressed { serializedPK = btcPK.SerializeCompressed() } else { serializedPK = btcPK.SerializeUncompressed() } address, err := btcutil.NewAddressPubKey(serializedPK, activeNetParams.Params) if err != nil { // Again mirror Bitcoin Core behavior, which treats error in public key // reconstruction as invalid signature. return false, nil } // Return boolean if addresses match. return address.EncodeAddress() == c.Address, nil } // 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 limitauthsha [fastsha256.Size]byte ntfnMgr *wsNotificationManager numClients int32 statusLines map[int]string statusLock sync.RWMutex wg sync.WaitGroup listeners []net.Listener workState *workState gbtWorkState *gbtWorkState helpCacher *helpCacher quit chan int } // 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 } // 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 } // 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 { if int(atomic.LoadInt32(&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() { atomic.AddInt32(&s.numClients, 1) } // 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() { atomic.AddInt32(&s.numClients, -1) } // 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. // // The first bool return value signifies auth success (true if successful) and // the second bool return value specifies whether the user can change the state // of the server (true) or whether the user is limited (false). The second is // always false if the first is. func (s *rpcServer) checkAuth(r *http.Request, require bool) (bool, bool, error) { authhdr := r.Header["Authorization"] if len(authhdr) <= 0 { if require { rpcsLog.Warnf("RPC authentication failure from %s", r.RemoteAddr) return false, false, errors.New("auth failure") } return false, false, nil } authsha := fastsha256.Sum256([]byte(authhdr[0])) // Check for limited auth first as in environments with limited users, those // are probably expected to have a higher volume of calls limitcmp := subtle.ConstantTimeCompare(authsha[:], s.limitauthsha[:]) if limitcmp == 1 { return true, false, nil } // Check for admin-level auth cmp := subtle.ConstantTimeCompare(authsha[:], s.authsha[:]) if cmp == 1 { return true, true, nil } // Request's auth doesn't match either user rpcsLog.Warnf("RPC authentication failure from %s", r.RemoteAddr) return false, false, errors.New("auth failure") } // parsedRPCCmd represents a JSON-RPC request object that has been parsed into // a known concrete command along with any error that might have happened while // parsing it. type parsedRPCCmd struct { id interface{} method string cmd interface{} err *btcjson.RPCError } // standardCmdResult checks that a parsed command is a standard Bitcoin JSON-RPC // command and runs the appropriate handler to reply to the command. Any // commands which are not recognized or not implemented will return an error // suitable for use in replies. func (s *rpcServer) standardCmdResult(cmd *parsedRPCCmd, closeChan <-chan struct{}) (interface{}, error) { 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 } return nil, btcjson.ErrRPCMethodNotFound handled: return handler(s, cmd.cmd, closeChan) } // parseCmd parses a JSON-RPC request object into known concrete command. The // err field of the returned parsedRPCCmd struct will contain an RPC error that // is suitable for use in replies if the command is invalid in some way such as // an unregistered command or invalid parameters. func parseCmd(request *btcjson.Request) *parsedRPCCmd { var parsedCmd parsedRPCCmd parsedCmd.id = request.ID parsedCmd.method = request.Method cmd, err := btcjson.UnmarshalCmd(request) if err != nil { // When the error is because the method is not registered, // produce a method not found RPC error. if jerr, ok := err.(btcjson.Error); ok && jerr.ErrorCode == btcjson.ErrUnregisteredMethod { parsedCmd.err = btcjson.ErrRPCMethodNotFound return &parsedCmd } // Otherwise, some type of invalid parameters is the // cause, so produce the equivalent RPC error. parsedCmd.err = btcjson.NewRPCError( btcjson.ErrRPCInvalidParams.Code, err.Error()) return &parsedCmd } parsedCmd.cmd = cmd return &parsedCmd } // createMarshalledReply returns a new marshalled JSON-RPC response given the // passed parameters. It will automatically convert errors that are not of // the type *btcjson.RPCError to the appropriate type as needed. func createMarshalledReply(id, result interface{}, replyErr error) ([]byte, error) { var jsonErr *btcjson.RPCError if replyErr != nil { if jErr, ok := replyErr.(*btcjson.RPCError); ok { jsonErr = jErr } else { jsonErr = internalRPCError(replyErr.Error(), "") } } return btcjson.MarshalResponse(id, result, jsonErr) } // jsonRPCRead handles reading and responding to RPC messages. func (s *rpcServer) jsonRPCRead(w http.ResponseWriter, r *http.Request, isAdmin bool) { if atomic.LoadInt32(&s.shutdown) != 0 { return } // Read and close the JSON-RPC request body from the caller. body, err := ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { errMsg := fmt.Sprintf("error reading JSON message: %v", err) errCode := http.StatusBadRequest http.Error(w, strconv.FormatInt(int64(errCode), 10)+" "+errMsg, errCode) 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) // Attempt to parse the raw body into a JSON-RPC request. var responseID interface{} var jsonErr error var result interface{} var request btcjson.Request if err := json.Unmarshal(body, &request); err != nil { jsonErr = &btcjson.RPCError{ Code: btcjson.ErrRPCParse.Code, Message: "Failed to parse request: " + err.Error(), } } if jsonErr == nil { // Requests with no ID (notifications) must not have a response // per the JSON-RPC spec. if request.ID == nil { return } // The parse was at least successful enough to have an ID so // set it for the response. responseID = request.ID // 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) } }() // Check if the user is limited and set error if method unauthorized if !isAdmin { if _, ok := rpcLimited[request.Method]; !ok { jsonErr = &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParams.Code, Message: "limited user not authorized for this method", } } } if jsonErr == nil { // Attempt to parse the JSON-RPC request into a known concrete // command. parsedCmd := parseCmd(&request) if parsedCmd.err != nil { jsonErr = parsedCmd.err } else { result, jsonErr = s.standardCmdResult(parsedCmd, closeChan) } } } // Marshal the response. msg, err := createMarshalledReply(responseID, result, jsonErr) if err != nil { rpcsLog.Errorf("Failed to marshal reply: %v", err) return } // Write the response. err = s.writeHTTPResponseHeaders(r, w.Header(), http.StatusOK, buf) if err != nil { rpcsLog.Error(err) return } if _, err := buf.Write(msg); err != nil { rpcsLog.Errorf("Failed to write marshalled reply: %v", err) } } // jsonAuthFail sends a message back to the client if the http auth is rejected. func jsonAuthFail(w http.ResponseWriter) { w.Header().Add("WWW-Authenticate", `Basic realm="btcd RPC"`) http.Error(w, "401 Unauthorized.", http.StatusUnauthorized) } // 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() _, isAdmin, err := s.checkAuth(r, true) if err != nil { jsonAuthFail(w) return } // Read and respond to the request. s.jsonRPCRead(w, r, isAdmin) }) // Websocket endpoint. rpcServeMux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { authenticated, isAdmin, err := s.checkAuth(r, false) if err != nil { jsonAuthFail(w) 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) } http.Error(w, "400 Bad Request.", http.StatusBadRequest) return } s.WebsocketHandler(ws, r.RemoteAddr, authenticated, isAdmin) }) 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() } // 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) { rpc := rpcServer{ server: s, statusLines: make(map[int]string), workState: newWorkState(), gbtWorkState: newGbtWorkState(s.timeSource), helpCacher: newHelpCacher(), quit: make(chan int), } if cfg.RPCUser != "" && cfg.RPCPass != "" { login := cfg.RPCUser + ":" + cfg.RPCPass auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) rpc.authsha = fastsha256.Sum256([]byte(auth)) } if cfg.RPCLimitUser != "" && cfg.RPCLimitPass != "" { login := cfg.RPCLimitUser + ":" + cfg.RPCLimitPass auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) rpc.limitauthsha = fastsha256.Sum256([]byte(auth)) } rpc.ntfnMgr = newWsNotificationManager(&rpc) // Setup TLS if not disabled. listenFunc := net.Listen if !cfg.DisableTLS { // Generate the TLS cert and key file if both don't already // exist. if !fileExists(cfg.RPCKey) && !fileExists(cfg.RPCCert) { 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}, MinVersion: tls.VersionTLS12, } // Change the standard net.Listen function to the tls one. listenFunc = func(net string, laddr string) (net.Listener, error) { return tls.Listen(net, laddr, &tlsConfig) } } // 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 := listenFunc("tcp4", addr) if err != nil { rpcsLog.Warnf("Can't listen on %s: %v", addr, err) continue } listeners = append(listeners, listener) } for _, addr := range ipv6ListenAddrs { listener, err := listenFunc("tcp6", addr) 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 } func init() { rpcHandlers = rpcHandlersBeforeInit rand.Seed(time.Now().UnixNano()) }