lbcd/rpcserver.go

3807 lines
122 KiB
Go
Raw Normal View History

// Copyright (c) 2013-2015 The btcsuite developers
2013-08-06 23:55:22 +02:00
// 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"
2013-10-01 22:43:45 +02:00
"encoding/base64"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
2013-10-01 22:43:45 +02:00
"fmt"
"io"
"io/ioutil"
2013-08-06 23:55:22 +02:00
"math/big"
"math/rand"
2013-08-06 23:55:22 +02:00
"net"
"net/http"
"os"
2013-08-06 23:55:22 +02:00
"strconv"
"strings"
2013-08-06 23:55:22 +02:00
"sync"
"sync/atomic"
"time"
2014-07-02 15:50:08 +02:00
"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"
2013-08-06 23:55:22 +02:00
)
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
)
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,
2015-06-23 21:43:39 +02:00
"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{}{},
// 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
}
2013-08-06 23:55:22 +02:00
// 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.PeerInfo(), 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.PeerInfo(), 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(peerInfos []*btcjson.GetPeerInfoResult, addr string, nodeId int32) bool {
for _, peerInfo := range peerInfos {
if peerInfo.ID == nodeId || peerInfo.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)
// 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{})
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",
}
}
2013-08-06 23:55:22 +02:00
// 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)
}
// 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 {
vinList := make([]btcjson.Vin, len(mtx.TxIn))
for i, v := range mtx.TxIn {
if blockchain.IsCoinBaseTx(mtx) {
vinList[i].Coinbase = hex.EncodeToString(v.SignatureScript)
} else {
vinList[i].Txid = v.PreviousOutPoint.Hash.String()
vinList[i].Vout = v.PreviousOutPoint.Index
// The disassembled string will contain [error] inline
// if the script doesn't fully parse, so ignore the
// error here.
disbuf, _ := txscript.DisasmString(v.SignatureScript)
vinList[i].ScriptSig = new(btcjson.ScriptSig)
vinList[i].ScriptSig.Asm = disbuf
vinList[i].ScriptSig.Hex = hex.EncodeToString(v.SignatureScript)
}
vinList[i].Sequence = v.Sequence
}
return vinList
}
// 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()
}
}
2013-08-06 23:55:22 +02:00
}
return voutList
2013-08-06 23:55:22 +02:00
}
// 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 int64, chainHeight int64) (*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)
2013-08-06 23:55:22 +02:00
}
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(),
}
2013-08-06 23:55:22 +02:00
}
// 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
2013-08-06 23:55:22 +02:00
}
// handleDecodeScript handles decodescript commands.
func handleDecodeScript(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.DecodeScriptCmd)
2013-10-01 22:43:45 +02:00
// Convert the hex script to bytes.
hexStr := c.HexScript
if len(hexStr)%2 != 0 {
hexStr = "0" + hexStr
2013-08-06 23:55:22 +02:00
}
script, err := hex.DecodeString(hexStr)
2013-08-06 23:55:22 +02:00
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: idx,
Size: int32(len(buf)),
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
Difficulty: getDifficultyRatio(blockHeader.Bits),
}
2013-08-06 23:55:22 +02:00
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(int64(idx + 1))
if err != nil {
context := "No next block"
return nil, internalRPCError(err.Error(), context)
}
blockReply.NextHash = shaNext.String()
}
2013-08-06 23:55:22 +02:00
return blockReply, nil
}
2013-08-06 23:55:22 +02:00
// 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(),
}
}
2013-08-06 23:55:22 +02:00
return maxIdx, nil
}
2013-08-06 23:55:22 +02:00
// 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(c.Index)
if err != nil {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCOutOfRange,
Message: "Block number out of range",
}
}
2013-08-06 23:55:22 +02:00
return sha.String(), nil
}
2013-08-06 23:55:22 +02:00
// 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(int64(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")
}
2014-06-30 21:14:31 +02:00
lastGenerated, err := strconv.ParseInt(fields[1], 10, 64)
if err != nil {
return nil, 0, errors.New("invalid longpollid format")
}
2014-06-30 21:14:31 +02:00
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.txMemPool, 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: 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 " +
2014-06-30 21:14:31 +02:00
"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 " +
2014-06-30 21:14:31 +02:00
"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
}
2013-10-29 01:43:09 +01:00
// handleGetGenerate implements the getgenerate command.
func handleGetGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
return s.server.cpuMiner.IsMining(), nil
}
2013-10-29 01:43:09 +01:00
// handleGetHashesPerSec implements the gethashespersec command.
func handleGetHashesPerSec(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
return int64(s.server.cpuMiner.HashesPerSecond()), nil
}
2013-10-29 01:43:09 +01:00
// 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: float64(minTxRelayFee) / btcutil.SatoshiPerBitcoin,
}
return ret, nil
}
2015-06-23 21:43:39 +02:00
// 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: 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 := int64(-1)
if c.Height != nil {
endHeight = int64(*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 := int64(120)
if c.Blocks != nil {
numBlocks = int64(*c.Blocks)
}
var startHeight int64
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) {
return s.server.PeerInfo(), 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)
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(),
2013-12-17 15:02:35 +01:00
Time: desc.Added.Unix(),
Height: 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
}
2013-12-17 15:02:35 +01:00
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))
2013-12-17 15:02:35 +01:00
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 int64
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",
}
}
2013-10-29 01:43:09 +01:00
lastTx := txList[len(txList)-1]
mtx = lastTx.Tx
blkHash = lastTx.BlkSha
blkHeight = lastTx.Height
} else {
mtx = tx.MsgTx()
}
2013-10-29 01:43:09 +01:00
// 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 int64
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(),
}
2013-08-06 23:55:22 +02:00
}
_, chainHeight, err = s.server.db.NewestSha()
if err != nil {
context := "Failed to get newest hash"
return nil, internalRPCError(err.Error(), context)
2013-08-06 23:55:22 +02:00
}
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 int64
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: 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.
Implement a built-in concurrent CPU miner. This commit implements a built-in concurrent CPU miner that can be enabled with the combination of the --generate and --miningaddr options. The --blockminsize, --blockmaxsize, and --blockprioritysize configuration options wich already existed prior to this commit control the block template generation and hence affect blocks mined via the new CPU miner. The following is a quick overview of the changes and design: - Starting btcd with --generate and no addresses specified via --miningaddr will give an error and exit immediately - Makes use of multiple worker goroutines which independently create block templates, solve them, and submit the solved blocks - The default number of worker threads are based on the number of processor cores in the system and can be dynamically changed at run-time - There is a separate speed monitor goroutine used to collate periodic updates from the workers to calculate overall hashing speed - The current mining state, number of workers, and hashes per second can be queried - Updated sample-btcd.conf file has been updated to include the coin generation (mining) settings - Updated doc.go for the new command line options In addition the old --getworkkey option is now deprecated in favor of the new --miningaddr option. This was changed for a few reasons: - There is no reason to have a separate list of keys for getwork and CPU mining - getwork is deprecated and will be going away in the future so that means the --getworkkey flag will also be going away - Having the work 'key' in the option can be confused with wanting a private key while --miningaddr make it a little more clear it is an address that is required Closes #137. Reviewed by @jrick.
2014-06-12 03:09:38 +02:00
payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))]
template, err := NewBlockTemplate(s.server.txMemPool, 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)
Implement a built-in concurrent CPU miner. This commit implements a built-in concurrent CPU miner that can be enabled with the combination of the --generate and --miningaddr options. The --blockminsize, --blockmaxsize, and --blockprioritysize configuration options wich already existed prior to this commit control the block template generation and hence affect blocks mined via the new CPU miner. The following is a quick overview of the changes and design: - Starting btcd with --generate and no addresses specified via --miningaddr will give an error and exit immediately - Makes use of multiple worker goroutines which independently create block templates, solve them, and submit the solved blocks - The default number of worker threads are based on the number of processor cores in the system and can be dynamically changed at run-time - There is a separate speed monitor goroutine used to collate periodic updates from the workers to calculate overall hashing speed - The current mining state, number of workers, and hashes per second can be queried - Updated sample-btcd.conf file has been updated to include the coin generation (mining) settings - Updated doc.go for the new command line options In addition the old --getworkkey option is now deprecated in favor of the new --miningaddr option. This was changed for a few reasons: - There is no reason to have a separate list of keys for getwork and CPU mining - getwork is deprecated and will be going away in the future so that means the --getworkkey flag will also be going away - Having the work 'key' in the option can be confused with wanting a private key while --miningaddr make it a little more clear it is an address that is required Closes #137. Reviewed by @jrick.
2014-06-12 03:09:38 +02:00
// Respond with an error if there are no addresses to pay the created
// blocks to.
Implement a built-in concurrent CPU miner. This commit implements a built-in concurrent CPU miner that can be enabled with the combination of the --generate and --miningaddr options. The --blockminsize, --blockmaxsize, and --blockprioritysize configuration options wich already existed prior to this commit control the block template generation and hence affect blocks mined via the new CPU miner. The following is a quick overview of the changes and design: - Starting btcd with --generate and no addresses specified via --miningaddr will give an error and exit immediately - Makes use of multiple worker goroutines which independently create block templates, solve them, and submit the solved blocks - The default number of worker threads are based on the number of processor cores in the system and can be dynamically changed at run-time - There is a separate speed monitor goroutine used to collate periodic updates from the workers to calculate overall hashing speed - The current mining state, number of workers, and hashes per second can be queried - Updated sample-btcd.conf file has been updated to include the coin generation (mining) settings - Updated doc.go for the new command line options In addition the old --getworkkey option is now deprecated in favor of the new --miningaddr option. This was changed for a few reasons: - There is no reason to have a separate list of keys for getwork and CPU mining - getwork is deprecated and will be going away in the future so that means the --getworkkey flag will also be going away - Having the work 'key' in the option can be confused with wanting a private key while --miningaddr make it a little more clear it is an address that is required Closes #137. Reviewed by @jrick.
2014-06-12 03:09:38 +02:00
if len(cfg.miningAddrs) == 0 {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInternal.Code,
Implement a built-in concurrent CPU miner. This commit implements a built-in concurrent CPU miner that can be enabled with the combination of the --generate and --miningaddr options. The --blockminsize, --blockmaxsize, and --blockprioritysize configuration options wich already existed prior to this commit control the block template generation and hence affect blocks mined via the new CPU miner. The following is a quick overview of the changes and design: - Starting btcd with --generate and no addresses specified via --miningaddr will give an error and exit immediately - Makes use of multiple worker goroutines which independently create block templates, solve them, and submit the solved blocks - The default number of worker threads are based on the number of processor cores in the system and can be dynamically changed at run-time - There is a separate speed monitor goroutine used to collate periodic updates from the workers to calculate overall hashing speed - The current mining state, number of workers, and hashes per second can be queried - Updated sample-btcd.conf file has been updated to include the coin generation (mining) settings - Updated doc.go for the new command line options In addition the old --getworkkey option is now deprecated in favor of the new --miningaddr option. This was changed for a few reasons: - There is no reason to have a separate list of keys for getwork and CPU mining - getwork is deprecated and will be going away in the future so that means the --getworkkey flag will also be going away - Having the work 'key' in the option can be confused with wanting a private key while --miningaddr make it a little more clear it is an address that is required Closes #137. Reviewed by @jrick.
2014-06-12 03:09:38 +02:00
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
}
// 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 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
}
}
// While it's more efficient to check the mempool for relevant transactions
// first, we want to return results in order of occurrence/dependency so
// we'll check the mempool only if there aren't enough results returned
// by the database.
dbTxs, err := s.server.db.FetchTxsForAddr(addr, numToSkip,
numRequested-len(addressTxs))
if err == nil {
for _, txReply := range dbTxs {
addressTxs = append(addressTxs, txReply)
}
}
// 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 len(addressTxs) < numRequested {
memPoolTxs, err := s.server.txMemPool.FilterTransactionsByAddress(addr)
if err == nil {
for _, tx := range memPoolTxs {
txReply := &database.TxListReply{Tx: tx.MsgTx(), Sha: tx.Sha()}
addressTxs = append(addressTxs, txReply)
if len(addressTxs) == numRequested {
break
}
}
}
}
// 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.TxRawResult, 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 int64
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 := createTxRawResult(s.server.chainParams, mtx,
txHash, blkHeader, blkHashStr, blkHeight, maxIdx)
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(int64(height))
if err != nil {
rpcsLog.Errorf("Verify is unable to fetch block at "+
"height %d: %v", height, err)
return err
}
block, err := db.FetchBlockBySha(sha)
if err != nil {
rpcsLog.Errorf("Verify is unable to fetch block at "+
"sha %v height %d: %v", sha, height, err)
return err
}
// Level 1 does basic chain sanity checks.
if level > 0 {
err := 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.
pk, wasCompressed, err := btcec.RecoverCompact(btcec.S256(), sig,
wire.DoubleSha256([]byte("Bitcoin Signed Message:\n"+c.Message)))
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()
2014-01-15 16:01:12 +01:00
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
2014-01-15 16:01:12 +01:00
}
// 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()
2014-01-15 16:01:12 +01:00
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
2014-01-15 16:01:12 +01:00
}
defer conn.Close()
defer buf.Flush()
conn.SetReadDeadline(timeZeroVal)
2014-01-15 16:01:12 +01:00
// 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)
2014-01-15 16:01:12 +01:00
if err != nil {
rpcsLog.Errorf("Failed to marshal reply: %v", err)
return
2014-01-15 16:01:12 +01:00
}
// 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)
}
2014-01-15 16:01:12 +01:00
}
// 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),
2014-08-22 23:22:46 +02:00
}
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)
2014-08-22 23:22:46 +02:00
// 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
2014-08-22 23:22:46 +02:00
}
tlsConfig := tls.Config{
Certificates: []tls.Certificate{keypair},
MinVersion: tls.VersionTLS12,
2014-08-22 23:22:46 +02:00
}
// 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)
}
2014-08-22 23:22:46 +02:00
}
// TODO(oga) this code is similar to that in server, should be
// factored into something shared.
ipv4ListenAddrs, ipv6ListenAddrs, _, err := parseListeners(listenAddrs)
2014-08-22 23:22:46 +02:00
if err != nil {
return nil, err
2014-08-22 23:22:46 +02:00
}
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
2013-10-29 01:43:09 +01:00
}
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
2013-08-06 23:55:22 +02:00
return &rpc, nil
2013-08-06 23:55:22 +02:00
}
func init() {
rpcHandlers = rpcHandlersBeforeInit
rand.Seed(time.Now().UnixNano())
}