2013-08-06 23:55:22 +02:00
|
|
|
// Copyright (c) 2013 Conformal Systems LLC.
|
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2013-08-14 22:55:31 +02:00
|
|
|
"bytes"
|
2013-12-05 19:21:51 +01:00
|
|
|
"crypto/subtle"
|
2013-11-07 17:25:11 +01:00
|
|
|
"crypto/tls"
|
2013-10-01 22:43:45 +02:00
|
|
|
"encoding/base64"
|
2014-03-20 08:06:10 +01:00
|
|
|
"encoding/binary"
|
2013-08-14 22:55:31 +02:00
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
2013-10-01 22:43:45 +02:00
|
|
|
"fmt"
|
2014-06-27 18:13:04 +02:00
|
|
|
"io"
|
2014-01-10 20:45:04 +01:00
|
|
|
"io/ioutil"
|
2013-08-06 23:55:22 +02:00
|
|
|
"math/big"
|
2014-03-20 08:06:10 +01:00
|
|
|
"math/rand"
|
2013-08-06 23:55:22 +02:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2013-11-07 17:25:11 +01:00
|
|
|
"os"
|
2013-08-06 23:55:22 +02:00
|
|
|
"strconv"
|
2014-06-27 21:12:22 +02:00
|
|
|
"strings"
|
2013-08-06 23:55:22 +02:00
|
|
|
"sync"
|
2013-10-03 01:33:42 +02:00
|
|
|
"sync/atomic"
|
2013-11-07 17:25:11 +01:00
|
|
|
"time"
|
2014-07-02 15:50:08 +02:00
|
|
|
|
|
|
|
"github.com/conformal/btcchain"
|
|
|
|
"github.com/conformal/btcdb"
|
|
|
|
"github.com/conformal/btcjson"
|
|
|
|
"github.com/conformal/btcnet"
|
|
|
|
"github.com/conformal/btcscript"
|
|
|
|
"github.com/conformal/btcutil"
|
|
|
|
"github.com/conformal/btcwire"
|
|
|
|
"github.com/conformal/btcws"
|
|
|
|
"github.com/conformal/fastsha256"
|
|
|
|
"github.com/conformal/websocket"
|
2013-08-06 23:55:22 +02:00
|
|
|
)
|
|
|
|
|
2014-03-20 08:06:10 +01: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 + ((btcwire.MaxBlockHeaderPayload + 8) /
|
|
|
|
fastsha256.BlockSize)) * fastsha256.BlockSize
|
|
|
|
|
|
|
|
// hash1Len is the length of the hash1 field of the getwork RPC. It
|
|
|
|
// consists of a zero hash plus the internal sha256 padding. See
|
|
|
|
// the getworkDataLen comment for details about the internal sha256
|
|
|
|
// padding format.
|
|
|
|
hash1Len = (1 + ((btcwire.HashSize + 8) / fastsha256.BlockSize)) *
|
|
|
|
fastsha256.BlockSize
|
2014-06-27 21:12:22 +02:00
|
|
|
|
Implement some BIP0023 getblocktemplate mutations.
This commit implements a portion of the mutations section of BIP0023.
In particular, it adds the mutable, mintime, maxtime, and noncerange keys
to the returned block template along with indicating support for the time,
transactions/add, prevblock, and coinbase/append mutations. Also, the
addition of the mintime and maxtime fields imply support for the
time/decrement and time/increment mutations. Further, if the caller
indicates the coinbasevalue capability, the coinbasetxn field will be
omitted thereby implying support for the coinbase and generation
mutations.
Closes #124.
2014-06-30 03:00:51 +02:00
|
|
|
// gbtNonceRange is two 32-bit big-endian hexadecimal integers which
|
|
|
|
// represent the valid ranges of nonces returned by the getblocktemplate
|
|
|
|
// RPC.
|
|
|
|
gbtNonceRange = "00000000ffffffff"
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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 (
|
Implement some BIP0023 getblocktemplate mutations.
This commit implements a portion of the mutations section of BIP0023.
In particular, it adds the mutable, mintime, maxtime, and noncerange keys
to the returned block template along with indicating support for the time,
transactions/add, prevblock, and coinbase/append mutations. Also, the
addition of the mintime and maxtime fields imply support for the
time/decrement and time/increment mutations. Further, if the caller
indicates the coinbasevalue capability, the coinbasetxn field will be
omitted thereby implying support for the coinbase and generation
mutations.
Closes #124.
2014-06-30 03:00:51 +02:00
|
|
|
// 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",
|
|
|
|
}
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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(btcscript.NewScriptBuilder().
|
|
|
|
AddData([]byte(coinbaseFlags)).Script()),
|
|
|
|
}
|
2014-06-27 21:12:22 +02:00
|
|
|
|
|
|
|
// 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"}
|
2014-03-20 08:06:10 +01:00
|
|
|
)
|
2014-01-18 07:03:31 +01:00
|
|
|
|
2013-08-14 22:55:31 +02:00
|
|
|
// Errors
|
|
|
|
var (
|
Clean up notification contexts and goroutines after ws disconnect.
This refactors the wallet notification code to reverse the order of
how notification contexts are stored. Before, watched addresses and
outpoints were used as keys, with a special reply channel as the
value. This channel was read from and replies were marshalled and
sent to the main wallet notification chan, but the goroutine handling
this marshalling never exited because the reply channel was never
closed (and couldn't have been, because there was no way to tell it
was handling notifications for any particular wallet).
Notification contexts are now primarily mapped by wallet notification
channels, and code to send the notifications send directly to the
wallet channel, with the previous goroutine reading the reply chan
properly closing.
The RPC code is also refactored with this change as well, to separate
it more from websocket code. Websocket JSON extensions are no longer
available to RPC clients.
While here, unbreak RPC. Previously, replies were never sent back.
This broke when I merged in my websocket code, as sends for the reply
channel in jsonRead blocked before a reader for the channel was
opened. A 3 liner could have fixed this, but doing a proper fix
(changing jsonRead so it did not use the reply channel as it is
unneeded for the standard RPC API) is preferred.
2013-10-16 20:12:00 +02:00
|
|
|
// ErrBadParamsField describes an error where the parameters JSON
|
|
|
|
// field cannot be properly parsed.
|
2013-08-14 22:55:31 +02:00
|
|
|
ErrBadParamsField = errors.New("bad params field")
|
|
|
|
)
|
|
|
|
|
2014-06-27 18:13:04 +02:00
|
|
|
type commandHandler func(*rpcServer, btcjson.Cmd, <-chan struct{}) (interface{}, error)
|
2013-12-31 21:48:50 +01:00
|
|
|
|
|
|
|
// handlers maps RPC command strings to appropriate handler functions.
|
2014-01-16 15:45:17 +01:00
|
|
|
// this is copied by init because help references rpcHandlers and thus causes
|
|
|
|
// a dependancy loop.
|
|
|
|
var rpcHandlers map[string]commandHandler
|
|
|
|
var rpcHandlersBeforeInit = map[string]commandHandler{
|
2014-01-21 16:03:15 +01:00
|
|
|
"addnode": handleAddNode,
|
|
|
|
"createrawtransaction": handleCreateRawTransaction,
|
|
|
|
"debuglevel": handleDebugLevel,
|
|
|
|
"decoderawtransaction": handleDecodeRawTransaction,
|
|
|
|
"decodescript": handleDecodeScript,
|
2014-06-11 16:46:19 +02:00
|
|
|
"estimatefee": handleUnimplemented,
|
|
|
|
"estimatepriority": handleUnimplemented,
|
2014-01-24 21:10:02 +01:00
|
|
|
"getaddednodeinfo": handleGetAddedNodeInfo,
|
2014-05-11 09:21:27 +02:00
|
|
|
"getbestblock": handleGetBestBlock,
|
2014-01-21 16:03:15 +01:00
|
|
|
"getbestblockhash": handleGetBestBlockHash,
|
|
|
|
"getblock": handleGetBlock,
|
2014-06-11 16:46:19 +02:00
|
|
|
"getblockchaininfo": handleUnimplemented,
|
2014-01-21 16:03:15 +01:00
|
|
|
"getblockcount": handleGetBlockCount,
|
|
|
|
"getblockhash": handleGetBlockHash,
|
2014-06-27 21:12:22 +02:00
|
|
|
"getblocktemplate": handleGetBlockTemplate,
|
2014-08-12 17:33:07 +02:00
|
|
|
"getchaintips": handleUnimplemented,
|
2014-01-21 16:03:15 +01:00
|
|
|
"getconnectioncount": handleGetConnectionCount,
|
2014-05-11 09:21:27 +02:00
|
|
|
"getcurrentnet": handleGetCurrentNet,
|
2014-01-21 16:03:15 +01:00
|
|
|
"getdifficulty": handleGetDifficulty,
|
|
|
|
"getgenerate": handleGetGenerate,
|
|
|
|
"gethashespersec": handleGetHashesPerSec,
|
2014-01-29 02:24:30 +01:00
|
|
|
"getinfo": handleGetInfo,
|
2014-05-04 06:51:54 +02:00
|
|
|
"getmininginfo": handleGetMiningInfo,
|
2014-02-04 08:26:12 +01:00
|
|
|
"getnettotals": handleGetNetTotals,
|
2014-02-07 23:31:14 +01:00
|
|
|
"getnetworkhashps": handleGetNetworkHashPS,
|
2014-06-11 16:46:19 +02:00
|
|
|
"getnetworkinfo": handleUnimplemented,
|
2014-01-21 16:03:15 +01:00
|
|
|
"getpeerinfo": handleGetPeerInfo,
|
|
|
|
"getrawmempool": handleGetRawMempool,
|
|
|
|
"getrawtransaction": handleGetRawTransaction,
|
2014-07-05 02:42:45 +02:00
|
|
|
"gettxout": handleGetTxOut,
|
2014-03-20 08:06:10 +01:00
|
|
|
"getwork": handleGetWork,
|
2014-01-21 16:03:15 +01:00
|
|
|
"help": handleHelp,
|
|
|
|
"ping": handlePing,
|
|
|
|
"sendrawtransaction": handleSendRawTransaction,
|
|
|
|
"setgenerate": handleSetGenerate,
|
|
|
|
"stop": handleStop,
|
|
|
|
"submitblock": handleSubmitBlock,
|
2014-07-03 05:52:34 +02:00
|
|
|
"validateaddress": handleValidateAddress,
|
2014-01-21 16:03:15 +01:00
|
|
|
"verifychain": handleVerifyChain,
|
2014-01-16 15:45:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2014-07-02 16:45:17 +02:00
|
|
|
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{}{},
|
|
|
|
"verifymessage": struct{}{},
|
|
|
|
"walletlock": struct{}{},
|
|
|
|
"walletpassphrase": struct{}{},
|
|
|
|
"walletpassphrasechange": struct{}{},
|
2014-01-16 15:45:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Commands that are temporarily unimplemented.
|
2014-07-02 16:45:17 +02:00
|
|
|
var rpcUnimplemented = map[string]struct{}{}
|
2013-12-31 21:48:50 +01:00
|
|
|
|
2014-03-20 08:06:10 +01:00
|
|
|
// workStateBlockInfo houses information about how to reconstruct a block given
|
|
|
|
// its template and signature script.
|
|
|
|
type workStateBlockInfo struct {
|
|
|
|
msgBlock *btcwire.MsgBlock
|
|
|
|
signatureScript []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// workState houses state that is used in between multiple RPC invocations to
|
|
|
|
// getwork.
|
|
|
|
type workState struct {
|
|
|
|
sync.Mutex
|
|
|
|
lastTxUpdate time.Time
|
|
|
|
lastGenerated time.Time
|
|
|
|
prevHash *btcwire.ShaHash
|
|
|
|
msgBlock *btcwire.MsgBlock
|
|
|
|
extraNonce uint64
|
|
|
|
blockInfo map[btcwire.ShaHash]*workStateBlockInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
// newWorkState returns a new instance of a workState with all internal fields
|
|
|
|
// initialized and ready to use.
|
|
|
|
func newWorkState() *workState {
|
|
|
|
return &workState{
|
|
|
|
blockInfo: make(map[btcwire.ShaHash]*workStateBlockInfo),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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 *btcwire.ShaHash
|
|
|
|
minTimestamp time.Time
|
|
|
|
template *BlockTemplate
|
2014-06-27 21:12:22 +02:00
|
|
|
notifyMap map[btcwire.ShaHash]map[int64]chan struct{}
|
2014-06-27 21:12:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// newGbtWorkState returns a new instance of a gbtWorkState with all internal
|
|
|
|
// fields initialized and ready to use.
|
|
|
|
func newGbtWorkState() *gbtWorkState {
|
2014-06-27 21:12:22 +02:00
|
|
|
return &gbtWorkState{
|
|
|
|
notifyMap: make(map[btcwire.ShaHash]map[int64]chan struct{}),
|
|
|
|
}
|
2014-06-27 21:12:22 +02:00
|
|
|
}
|
|
|
|
|
2013-08-06 23:55:22 +02:00
|
|
|
// rpcServer holds the items the rpc server may need to access (config,
|
|
|
|
// shutdown, main server, etc.)
|
|
|
|
type rpcServer struct {
|
2014-06-27 21:12:22 +02:00
|
|
|
started int32
|
|
|
|
shutdown int32
|
|
|
|
server *server
|
|
|
|
authsha [fastsha256.Size]byte
|
|
|
|
ntfnMgr *wsNotificationManager
|
|
|
|
numClients int32
|
|
|
|
statusLines map[int]string
|
|
|
|
statusLock sync.RWMutex
|
|
|
|
wg sync.WaitGroup
|
|
|
|
listeners []net.Listener
|
|
|
|
workState *workState
|
|
|
|
gbtWorkState *gbtWorkState
|
|
|
|
quit chan int
|
2013-08-14 22:55:31 +02:00
|
|
|
}
|
|
|
|
|
2013-08-06 23:55:22 +02:00
|
|
|
// Start is used by server.go to start the rpc listener.
|
|
|
|
func (s *rpcServer) Start() {
|
2013-10-03 01:33:42 +02:00
|
|
|
if atomic.AddInt32(&s.started, 1) != 1 {
|
2013-08-06 23:55:22 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Trace("Starting RPC server")
|
2013-10-23 17:45:43 +02:00
|
|
|
rpcServeMux := http.NewServeMux()
|
2014-01-18 07:03:31 +01:00
|
|
|
httpServer := &http.Server{
|
|
|
|
Handler: rpcServeMux,
|
|
|
|
|
|
|
|
// Timeout connections which don't complete the initial
|
|
|
|
// handshake within the allowed timeframe.
|
|
|
|
ReadTimeout: time.Second * rpcAuthTimeoutSeconds,
|
|
|
|
}
|
2013-10-23 17:45:43 +02:00
|
|
|
rpcServeMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
2014-02-08 02:59:20 +01:00
|
|
|
w.Header().Set("Connection", "close")
|
2014-04-25 06:19:07 +02:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2014-02-19 03:44:37 +01:00
|
|
|
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()
|
2014-01-22 21:10:04 +01:00
|
|
|
if _, err := s.checkAuth(r, true); err != nil {
|
2013-10-01 22:43:45 +02:00
|
|
|
jsonAuthFail(w, r, s)
|
2013-12-05 19:21:51 +01:00
|
|
|
return
|
2013-10-01 22:43:45 +02:00
|
|
|
}
|
2013-12-05 19:21:51 +01:00
|
|
|
jsonRPCRead(w, r, s)
|
2013-08-06 23:55:22 +02:00
|
|
|
})
|
2014-01-15 04:53:07 +01:00
|
|
|
|
2014-02-19 00:23:33 +01:00
|
|
|
// Websocket endpoint.
|
2014-02-03 17:46:03 +01:00
|
|
|
rpcServeMux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
2014-01-22 21:10:04 +01:00
|
|
|
authenticated, err := s.checkAuth(r, false)
|
|
|
|
if err != nil {
|
2013-12-05 19:21:51 +01:00
|
|
|
http.Error(w, "401 Unauthorized.", http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
2014-06-07 07:35:34 +02:00
|
|
|
|
|
|
|
// Attempt to upgrade the connection to a websocket connection
|
|
|
|
// using the default size for read/write buffers.
|
|
|
|
ws, err := websocket.Upgrade(w, r, nil, 0, 0)
|
|
|
|
if err != nil {
|
|
|
|
if _, ok := err.(websocket.HandshakeError); !ok {
|
|
|
|
rpcsLog.Errorf("Unexpected websocket error: %v",
|
|
|
|
err)
|
|
|
|
}
|
|
|
|
return
|
2014-01-17 22:03:43 +01:00
|
|
|
}
|
2014-06-07 07:35:34 +02:00
|
|
|
s.WebsocketHandler(ws, r.RemoteAddr, authenticated)
|
2013-12-05 19:21:51 +01:00
|
|
|
})
|
|
|
|
|
2013-08-07 17:38:39 +02:00
|
|
|
for _, listener := range s.listeners {
|
2013-09-13 00:24:37 +02:00
|
|
|
s.wg.Add(1)
|
2013-08-07 17:38:39 +02:00
|
|
|
go func(listener net.Listener) {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Infof("RPC server listening on %s", listener.Addr())
|
2013-08-07 17:38:39 +02:00
|
|
|
httpServer.Serve(listener)
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Tracef("RPC listener done for %s", listener.Addr())
|
2013-08-07 17:38:39 +02:00
|
|
|
s.wg.Done()
|
|
|
|
}(listener)
|
|
|
|
}
|
2014-03-04 17:15:25 +01:00
|
|
|
|
|
|
|
s.ntfnMgr.Start()
|
2013-08-06 23:55:22 +02:00
|
|
|
}
|
|
|
|
|
2014-06-27 18:13:04 +02:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2014-02-19 03:44:37 +01:00
|
|
|
// 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 {
|
2014-07-03 03:55:22 +02:00
|
|
|
if int(atomic.LoadInt32(&s.numClients)+1) > cfg.RPCMaxClients {
|
2014-02-19 03:44:37 +01:00
|
|
|
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() {
|
2014-07-03 03:55:22 +02:00
|
|
|
atomic.AddInt32(&s.numClients, 1)
|
2014-02-19 03:44:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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() {
|
2014-07-03 03:55:22 +02:00
|
|
|
atomic.AddInt32(&s.numClients, -1)
|
2014-02-19 03:44:37 +01:00
|
|
|
}
|
|
|
|
|
2013-12-05 19:21:51 +01:00
|
|
|
// 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.
|
2014-01-22 21:10:04 +01:00
|
|
|
func (s *rpcServer) checkAuth(r *http.Request, require bool) (bool, error) {
|
2013-12-05 19:21:51 +01:00
|
|
|
authhdr := r.Header["Authorization"]
|
|
|
|
if len(authhdr) <= 0 {
|
2014-01-22 21:10:04 +01:00
|
|
|
if require {
|
2014-02-19 07:30:18 +01:00
|
|
|
rpcsLog.Warnf("RPC authentication failure from %s",
|
|
|
|
r.RemoteAddr)
|
2014-01-22 21:10:04 +01:00
|
|
|
return false, errors.New("auth failure")
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
2013-12-05 19:21:51 +01:00
|
|
|
}
|
|
|
|
|
2014-03-23 06:49:20 +01:00
|
|
|
authsha := fastsha256.Sum256([]byte(authhdr[0]))
|
2013-12-05 19:21:51 +01:00
|
|
|
cmp := subtle.ConstantTimeCompare(authsha[:], s.authsha[:])
|
|
|
|
if cmp != 1 {
|
2014-02-19 07:30:18 +01:00
|
|
|
rpcsLog.Warnf("RPC authentication failure from %s", r.RemoteAddr)
|
2014-01-22 21:10:04 +01:00
|
|
|
return false, errors.New("auth failure")
|
2013-12-05 19:21:51 +01:00
|
|
|
}
|
2014-01-22 21:10:04 +01:00
|
|
|
return true, nil
|
2013-12-05 19:21:51 +01:00
|
|
|
}
|
|
|
|
|
2013-08-06 23:55:22 +02:00
|
|
|
// Stop is used by server.go to stop the rpc listener.
|
|
|
|
func (s *rpcServer) Stop() error {
|
2013-10-03 01:33:42 +02:00
|
|
|
if atomic.AddInt32(&s.shutdown, 1) != 1 {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Infof("RPC server is already in the process of shutting down")
|
2013-08-06 23:55:22 +02:00
|
|
|
return nil
|
|
|
|
}
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Warnf("RPC server shutting down")
|
2013-08-07 17:38:39 +02:00
|
|
|
for _, listener := range s.listeners {
|
|
|
|
err := listener.Close()
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Problem shutting down rpc: %v", err)
|
2013-08-07 17:38:39 +02:00
|
|
|
return err
|
|
|
|
}
|
2013-08-06 23:55:22 +02:00
|
|
|
}
|
2014-02-19 00:23:33 +01:00
|
|
|
s.ntfnMgr.Shutdown()
|
2014-03-04 17:15:25 +01:00
|
|
|
s.ntfnMgr.WaitForShutdown()
|
2013-08-14 22:55:31 +02:00
|
|
|
close(s.quit)
|
2014-01-15 04:53:07 +01:00
|
|
|
s.wg.Wait()
|
|
|
|
rpcsLog.Infof("RPC server shutdown complete")
|
2013-08-06 23:55:22 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-01-10 20:45:04 +01:00
|
|
|
// genCertPair generates a key/cert pair to the paths provided.
|
|
|
|
func genCertPair(certFile, keyFile string) error {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Infof("Generating TLS certificates...")
|
2013-11-07 17:25:11 +01:00
|
|
|
|
2014-01-10 20:45:04 +01:00
|
|
|
org := "btcd autogenerated cert"
|
|
|
|
validUntil := time.Now().Add(10 * 365 * 24 * time.Hour)
|
|
|
|
cert, key, err := btcutil.NewTLSCertPair(org, validUntil, nil)
|
2013-11-07 17:25:11 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-01-10 20:45:04 +01:00
|
|
|
// Write cert and key files.
|
|
|
|
if err = ioutil.WriteFile(certFile, cert, 0666); err != nil {
|
2013-11-07 17:25:11 +01:00
|
|
|
return err
|
|
|
|
}
|
2014-01-10 20:45:04 +01:00
|
|
|
if err = ioutil.WriteFile(keyFile, key, 0600); err != nil {
|
|
|
|
os.Remove(certFile)
|
2013-11-07 17:25:11 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Infof("Done generating TLS certificates")
|
2013-11-07 17:25:11 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2013-09-18 07:36:40 +02:00
|
|
|
// newRPCServer returns a new instance of the rpcServer struct.
|
2013-11-14 02:51:37 +01:00
|
|
|
func newRPCServer(listenAddrs []string, s *server) (*rpcServer, error) {
|
2013-12-05 19:21:51 +01:00
|
|
|
login := cfg.RPCUser + ":" + cfg.RPCPass
|
|
|
|
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login))
|
2013-08-06 23:55:22 +02:00
|
|
|
rpc := rpcServer{
|
2014-06-27 21:12:22 +02:00
|
|
|
authsha: fastsha256.Sum256([]byte(auth)),
|
|
|
|
server: s,
|
|
|
|
statusLines: make(map[int]string),
|
|
|
|
workState: newWorkState(),
|
|
|
|
gbtWorkState: newGbtWorkState(),
|
|
|
|
quit: make(chan int),
|
2013-08-06 23:55:22 +02:00
|
|
|
}
|
2014-02-19 00:23:33 +01:00
|
|
|
rpc.ntfnMgr = newWsNotificationManager(&rpc)
|
2013-08-06 23:55:22 +02:00
|
|
|
|
2013-11-20 22:55:36 +01:00
|
|
|
// check for existence of cert file and key file
|
2013-11-07 17:25:11 +01:00
|
|
|
if !fileExists(cfg.RPCKey) && !fileExists(cfg.RPCCert) {
|
|
|
|
// if both files do not exist, we generate them.
|
2014-01-10 20:45:04 +01:00
|
|
|
err := genCertPair(cfg.RPCCert, cfg.RPCKey)
|
2013-11-07 17:25:11 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
keypair, err := tls.LoadX509KeyPair(cfg.RPCCert, cfg.RPCKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
tlsConfig := tls.Config{
|
|
|
|
Certificates: []tls.Certificate{keypair},
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(oga) this code is similar to that in server, should be
|
2013-11-14 02:51:37 +01:00
|
|
|
// factored into something shared.
|
2013-11-26 01:40:16 +01:00
|
|
|
ipv4ListenAddrs, ipv6ListenAddrs, _, err := parseListeners(listenAddrs)
|
2013-11-07 17:25:11 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2013-11-14 02:51:37 +01:00
|
|
|
listeners := make([]net.Listener, 0,
|
|
|
|
len(ipv6ListenAddrs)+len(ipv4ListenAddrs))
|
|
|
|
for _, addr := range ipv4ListenAddrs {
|
2014-03-21 03:54:50 +01:00
|
|
|
listener, err := tls.Listen("tcp4", addr, &tlsConfig)
|
2013-11-14 02:51:37 +01:00
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Warnf("Can't listen on %s: %v", addr,
|
2013-11-14 02:51:37 +01:00
|
|
|
err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
listeners = append(listeners, listener)
|
2013-08-07 17:38:39 +02:00
|
|
|
}
|
|
|
|
|
2013-11-14 02:51:37 +01:00
|
|
|
for _, addr := range ipv6ListenAddrs {
|
2014-03-21 03:54:50 +01:00
|
|
|
listener, err := tls.Listen("tcp6", addr, &tlsConfig)
|
2013-11-14 02:51:37 +01:00
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Warnf("Can't listen on %s: %v", addr,
|
2013-11-14 02:51:37 +01:00
|
|
|
err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
listeners = append(listeners, listener)
|
|
|
|
}
|
|
|
|
if len(listeners) == 0 {
|
|
|
|
return nil, errors.New("RPCS: No valid listen address")
|
2013-08-06 23:55:22 +02:00
|
|
|
}
|
2013-08-07 17:38:39 +02:00
|
|
|
|
|
|
|
rpc.listeners = listeners
|
2013-08-14 22:55:31 +02:00
|
|
|
|
2014-03-21 03:54:50 +01:00
|
|
|
return &rpc, nil
|
2013-08-06 23:55:22 +02:00
|
|
|
}
|
|
|
|
|
2013-10-01 22:43:45 +02:00
|
|
|
// jsonAuthFail sends a message back to the client if the http auth is rejected.
|
|
|
|
func jsonAuthFail(w http.ResponseWriter, r *http.Request, s *rpcServer) {
|
2014-01-23 18:25:09 +01:00
|
|
|
w.Header().Add("WWW-Authenticate", `Basic realm="btcd RPC"`)
|
|
|
|
http.Error(w, "401 Unauthorized.", http.StatusUnauthorized)
|
2013-10-01 22:43:45 +02:00
|
|
|
}
|
|
|
|
|
2013-12-31 22:53:19 +01:00
|
|
|
// jsonRPCRead is the RPC wrapper around the jsonRead function to handle reading
|
|
|
|
// and responding to RPC messages.
|
2013-09-18 07:36:40 +02:00
|
|
|
func jsonRPCRead(w http.ResponseWriter, r *http.Request, s *rpcServer) {
|
2013-10-03 01:33:42 +02:00
|
|
|
if atomic.LoadInt32(&s.shutdown) != 0 {
|
2013-08-06 23:55:22 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
body, err := btcjson.GetRaw(r.Body)
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Error getting json message: %v", err)
|
2013-08-06 23:55:22 +02:00
|
|
|
return
|
|
|
|
}
|
2013-08-14 22:55:31 +02:00
|
|
|
|
2014-06-27 18:13:04 +02: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()
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Warnf("Failed to hijack HTTP connection: %v", err)
|
|
|
|
errCode := http.StatusInternalServerError
|
|
|
|
http.Error(w, strconv.FormatInt(int64(errCode), 10)+" "+
|
|
|
|
err.Error(), errCode)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
defer buf.Flush()
|
|
|
|
conn.SetReadDeadline(timeZeroVal)
|
|
|
|
|
2013-11-07 18:47:54 +01:00
|
|
|
var reply btcjson.Reply
|
|
|
|
cmd, jsonErr := parseCmd(body)
|
|
|
|
if cmd != nil {
|
|
|
|
// Unmarshaling at least a valid JSON-RPC message succeeded.
|
|
|
|
// Use the provided id for errors.
|
|
|
|
id := cmd.Id()
|
|
|
|
reply.Id = &id
|
|
|
|
}
|
|
|
|
if jsonErr != nil {
|
|
|
|
reply.Error = jsonErr
|
|
|
|
} else {
|
2014-06-27 18:13:04 +02:00
|
|
|
// Setup a close notifier. Since the connection is hijacked,
|
|
|
|
// the CloseNotifer on the ResponseWriter is not available.
|
|
|
|
closeChan := make(chan struct{}, 1)
|
|
|
|
go func() {
|
|
|
|
_, err := conn.Read(make([]byte, 1))
|
|
|
|
if err != nil {
|
|
|
|
close(closeChan)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
reply = standardCmdReply(cmd, s, closeChan)
|
2013-11-07 18:47:54 +01:00
|
|
|
}
|
|
|
|
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Tracef("reply: %v", reply)
|
2013-08-14 22:55:31 +02:00
|
|
|
|
2014-06-27 18:13:04 +02:00
|
|
|
err = s.writeHTTPResponseHeaders(r, w.Header(), http.StatusOK, buf)
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
msg, err := btcjson.MarshallAndSend(reply, buf)
|
Clean up notification contexts and goroutines after ws disconnect.
This refactors the wallet notification code to reverse the order of
how notification contexts are stored. Before, watched addresses and
outpoints were used as keys, with a special reply channel as the
value. This channel was read from and replies were marshalled and
sent to the main wallet notification chan, but the goroutine handling
this marshalling never exited because the reply channel was never
closed (and couldn't have been, because there was no way to tell it
was handling notifications for any particular wallet).
Notification contexts are now primarily mapped by wallet notification
channels, and code to send the notifications send directly to the
wallet channel, with the previous goroutine reading the reply chan
properly closing.
The RPC code is also refactored with this change as well, to separate
it more from websocket code. Websocket JSON extensions are no longer
available to RPC clients.
While here, unbreak RPC. Previously, replies were never sent back.
This broke when I merged in my websocket code, as sends for the reply
channel in jsonRead blocked before a reader for the channel was
opened. A 3 liner could have fixed this, but doing a proper fix
(changing jsonRead so it did not use the reply channel as it is
unneeded for the standard RPC API) is preferred.
2013-10-16 20:12:00 +02:00
|
|
|
if err != nil {
|
2014-06-27 18:13:04 +02:00
|
|
|
rpcsLog.Error(err)
|
Clean up notification contexts and goroutines after ws disconnect.
This refactors the wallet notification code to reverse the order of
how notification contexts are stored. Before, watched addresses and
outpoints were used as keys, with a special reply channel as the
value. This channel was read from and replies were marshalled and
sent to the main wallet notification chan, but the goroutine handling
this marshalling never exited because the reply channel was never
closed (and couldn't have been, because there was no way to tell it
was handling notifications for any particular wallet).
Notification contexts are now primarily mapped by wallet notification
channels, and code to send the notifications send directly to the
wallet channel, with the previous goroutine reading the reply chan
properly closing.
The RPC code is also refactored with this change as well, to separate
it more from websocket code. Websocket JSON extensions are no longer
available to RPC clients.
While here, unbreak RPC. Previously, replies were never sent back.
This broke when I merged in my websocket code, as sends for the reply
channel in jsonRead blocked before a reader for the channel was
opened. A 3 liner could have fixed this, but doing a proper fix
(changing jsonRead so it did not use the reply channel as it is
unneeded for the standard RPC API) is preferred.
2013-10-16 20:12:00 +02:00
|
|
|
return
|
2013-08-14 22:55:31 +02:00
|
|
|
}
|
2014-04-13 21:50:28 +02:00
|
|
|
rpcsLog.Tracef(msg)
|
2013-08-14 22:55:31 +02:00
|
|
|
}
|
|
|
|
|
2013-11-04 19:31:56 +01:00
|
|
|
// handleUnimplemented is a temporary handler for commands that we should
|
|
|
|
// support but do not.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleUnimplemented(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-11-04 19:31:56 +01:00
|
|
|
return nil, btcjson.ErrUnimplemented
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleAskWallet is the handler for commands that we do recognise as valid
|
|
|
|
// but that we can not answer correctly since it involves wallet state.
|
|
|
|
// These commands will be implemented in btcwallet.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleAskWallet(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-11-04 19:31:56 +01:00
|
|
|
return nil, btcjson.ErrNoWallet
|
|
|
|
}
|
|
|
|
|
2013-12-31 01:22:39 +01:00
|
|
|
// handleAddNode handles addnode commands.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleAddNode(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-29 18:18:53 +01:00
|
|
|
c := cmd.(*btcjson.AddNodeCmd)
|
|
|
|
|
2014-05-29 19:35:09 +02:00
|
|
|
addr := normalizeAddress(c.Addr, activeNetParams.DefaultPort)
|
2013-10-29 18:18:53 +01:00
|
|
|
var err error
|
|
|
|
switch c.SubCmd {
|
|
|
|
case "add":
|
|
|
|
err = s.server.AddAddr(addr, true)
|
|
|
|
case "remove":
|
|
|
|
err = s.server.RemoveAddr(addr)
|
|
|
|
case "onetry":
|
|
|
|
err = s.server.AddAddr(addr, false)
|
|
|
|
default:
|
2014-07-02 16:25:42 +02:00
|
|
|
err = errors.New("invalid subcommand for addnode")
|
2013-10-29 18:18:53 +01:00
|
|
|
}
|
|
|
|
|
2013-12-31 20:15:44 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-29 18:18:53 +01:00
|
|
|
// no data returned unless an error.
|
2013-12-31 20:15:44 +01:00
|
|
|
return nil, nil
|
2013-10-29 18:18:53 +01:00
|
|
|
}
|
|
|
|
|
2014-01-01 17:17:40 +01:00
|
|
|
// messageToHex serializes a message to the wire protocol encoding using the
|
|
|
|
// latest protocol version and returns a hex-encoded string of the result.
|
|
|
|
func messageToHex(msg btcwire.Message) (string, error) {
|
|
|
|
var buf bytes.Buffer
|
2014-05-07 18:33:32 +02:00
|
|
|
err := msg.BtcEncode(&buf, maxProtocolVersion)
|
2014-01-01 17:17:40 +01:00
|
|
|
if err != nil {
|
|
|
|
return "", btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return hex.EncodeToString(buf.Bytes()), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleCreateRawTransaction handles createrawtransaction commands.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleCreateRawTransaction(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-01-01 17:17:40 +01:00
|
|
|
c := cmd.(*btcjson.CreateRawTransactionCmd)
|
|
|
|
|
|
|
|
// Add all transaction inputs to a new transaction after performing
|
2014-03-13 14:45:41 +01:00
|
|
|
// some validity checks.
|
2014-01-01 17:17:40 +01:00
|
|
|
mtx := btcwire.NewMsgTx()
|
|
|
|
for _, input := range c.Inputs {
|
|
|
|
txHash, err := btcwire.NewShaHashFromStr(input.Txid)
|
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.ErrDecodeHexString
|
|
|
|
}
|
|
|
|
|
|
|
|
if input.Vout < 0 {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInvalidParameter.Code,
|
|
|
|
Message: "Invalid parameter, vout must be positive",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
prevOut := btcwire.NewOutPoint(txHash, uint32(input.Vout))
|
|
|
|
txIn := btcwire.NewTxIn(prevOut, []byte{})
|
|
|
|
mtx.AddTxIn(txIn)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add all transaction outputs to the transaction after performing
|
2014-03-13 14:45:41 +01:00
|
|
|
// some validity checks.
|
2014-01-01 17:17:40 +01:00
|
|
|
for encodedAddr, amount := range c.Amounts {
|
|
|
|
// Ensure amount is in the valid range for monetary amounts.
|
|
|
|
if amount <= 0 || amount > btcutil.MaxSatoshi {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrType.Code,
|
|
|
|
Message: "Invalid amount",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decode the provided address.
|
2014-03-19 02:44:03 +01:00
|
|
|
addr, err := btcutil.DecodeAddress(encodedAddr,
|
2014-05-28 00:44:55 +02:00
|
|
|
activeNetParams.Params)
|
2014-01-01 17:17:40 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInvalidAddressOrKey.Code,
|
|
|
|
Message: btcjson.ErrInvalidAddressOrKey.Message +
|
|
|
|
": " + err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the address is one of the supported types and that
|
|
|
|
// the network encoded with the address matches the network the
|
|
|
|
// server is currently on.
|
2014-02-26 21:06:48 +01:00
|
|
|
switch addr.(type) {
|
2014-01-01 17:17:40 +01:00
|
|
|
case *btcutil.AddressPubKeyHash:
|
|
|
|
case *btcutil.AddressScriptHash:
|
|
|
|
default:
|
|
|
|
return nil, btcjson.ErrInvalidAddressOrKey
|
|
|
|
}
|
2014-05-28 00:44:55 +02:00
|
|
|
if !addr.IsForNet(s.server.netParams) {
|
2014-01-01 17:17:40 +01:00
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInvalidAddressOrKey.Code,
|
|
|
|
Message: fmt.Sprintf("%s: %q",
|
|
|
|
btcjson.ErrInvalidAddressOrKey.Message,
|
|
|
|
encodedAddr),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new script which pays to the provided address.
|
|
|
|
pkScript, err := btcscript.PayToAddrScript(addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
txOut := btcwire.NewTxOut(amount, pkScript)
|
|
|
|
mtx.AddTxOut(txOut)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the serialized and hex-encoded transaction.
|
|
|
|
mtxHex, err := messageToHex(mtx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return mtxHex, nil
|
|
|
|
}
|
|
|
|
|
2013-11-22 17:46:56 +01:00
|
|
|
// handleDebugLevel handles debuglevel commands.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleDebugLevel(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-11-22 17:46:56 +01:00
|
|
|
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 {
|
2013-12-31 20:15:44 +01:00
|
|
|
return nil, btcjson.Error{
|
2013-11-22 17:46:56 +01:00
|
|
|
Code: btcjson.ErrInvalidParams.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "Done.", nil
|
|
|
|
}
|
|
|
|
|
2013-12-31 01:22:39 +01:00
|
|
|
// createVinList returns a slice of JSON objects for the inputs of the passed
|
|
|
|
// transaction.
|
2013-12-31 03:27:36 +01:00
|
|
|
func createVinList(mtx *btcwire.MsgTx) ([]btcjson.Vin, error) {
|
2013-12-31 01:22:39 +01:00
|
|
|
tx := btcutil.NewTx(mtx)
|
|
|
|
vinList := make([]btcjson.Vin, len(mtx.TxIn))
|
|
|
|
for i, v := range mtx.TxIn {
|
|
|
|
if btcchain.IsCoinBase(tx) {
|
|
|
|
vinList[i].Coinbase = hex.EncodeToString(v.SignatureScript)
|
|
|
|
} else {
|
|
|
|
vinList[i].Txid = v.PreviousOutpoint.Hash.String()
|
2014-06-16 21:44:36 +02:00
|
|
|
vinList[i].Vout = v.PreviousOutpoint.Index
|
2013-12-31 01:22:39 +01:00
|
|
|
|
2013-12-31 02:06:13 +01:00
|
|
|
disbuf, err := btcscript.DisasmString(v.SignatureScript)
|
|
|
|
if err != nil {
|
2013-12-31 03:27:36 +01:00
|
|
|
return nil, btcjson.Error{
|
2013-12-31 02:06:13 +01:00
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
2013-12-31 01:22:39 +01:00
|
|
|
vinList[i].ScriptSig = new(btcjson.ScriptSig)
|
|
|
|
vinList[i].ScriptSig.Asm = disbuf
|
|
|
|
vinList[i].ScriptSig.Hex = hex.EncodeToString(v.SignatureScript)
|
|
|
|
}
|
|
|
|
vinList[i].Sequence = v.Sequence
|
|
|
|
}
|
|
|
|
|
2013-12-31 02:06:13 +01:00
|
|
|
return vinList, nil
|
2013-12-31 01:22:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// createVoutList returns a slice of JSON objects for the outputs of the passed
|
|
|
|
// transaction.
|
2014-05-28 00:44:55 +02:00
|
|
|
func createVoutList(mtx *btcwire.MsgTx, net *btcnet.Params) ([]btcjson.Vout, error) {
|
2013-12-31 01:22:39 +01:00
|
|
|
voutList := make([]btcjson.Vout, len(mtx.TxOut))
|
|
|
|
for i, v := range mtx.TxOut {
|
2014-06-16 21:44:36 +02:00
|
|
|
voutList[i].N = uint32(i)
|
2014-07-07 16:05:17 +02:00
|
|
|
voutList[i].Value = float64(v.Value) / btcutil.SatoshiPerBitcoin
|
2013-12-31 01:22:39 +01:00
|
|
|
|
2013-12-31 02:06:13 +01:00
|
|
|
disbuf, err := btcscript.DisasmString(v.PkScript)
|
|
|
|
if err != nil {
|
2013-12-31 03:27:36 +01:00
|
|
|
return nil, btcjson.Error{
|
2013-12-31 02:06:13 +01:00
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
voutList[i].ScriptPubKey.Asm = disbuf
|
|
|
|
voutList[i].ScriptPubKey.Hex = hex.EncodeToString(v.PkScript)
|
|
|
|
|
2014-01-08 03:30:01 +01:00
|
|
|
// Ignore the error here since an error means the script
|
|
|
|
// couldn't parse and there is no additional information about
|
|
|
|
// it anyways.
|
|
|
|
scriptClass, addrs, reqSigs, _ := btcscript.ExtractPkScriptAddrs(v.PkScript, net)
|
|
|
|
voutList[i].ScriptPubKey.Type = scriptClass.String()
|
2014-06-29 23:36:41 +02:00
|
|
|
voutList[i].ScriptPubKey.ReqSigs = int32(reqSigs)
|
2014-01-02 06:30:00 +01:00
|
|
|
|
2014-01-08 03:30:01 +01:00
|
|
|
if addrs == nil {
|
2014-01-02 06:30:00 +01:00
|
|
|
voutList[i].ScriptPubKey.Addresses = nil
|
|
|
|
} else {
|
2014-01-08 03:30:01 +01:00
|
|
|
voutList[i].ScriptPubKey.Addresses = make([]string, len(addrs))
|
|
|
|
for j, addr := range addrs {
|
2014-01-03 19:22:28 +01:00
|
|
|
voutList[i].ScriptPubKey.Addresses[j] = addr.EncodeAddress()
|
2014-01-02 06:30:00 +01:00
|
|
|
}
|
2013-12-31 01:22:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-31 02:06:13 +01:00
|
|
|
return voutList, nil
|
2013-12-31 01:22:39 +01:00
|
|
|
}
|
|
|
|
|
2014-03-20 08:06:10 +01:00
|
|
|
// createTxRawResult converts the passed transaction and associated parameters
|
|
|
|
// to a raw transaction JSON object.
|
2014-05-28 00:44:55 +02:00
|
|
|
func createTxRawResult(net *btcnet.Params, txSha string, mtx *btcwire.MsgTx,
|
|
|
|
blk *btcutil.Block, maxidx int64,
|
|
|
|
blksha *btcwire.ShaHash) (*btcjson.TxRawResult, error) {
|
|
|
|
|
2014-03-20 08:06:10 +01:00
|
|
|
mtxHex, err := messageToHex(mtx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
vin, err := createVinList(mtx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
vout, err := createVoutList(mtx, net)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
txReply := &btcjson.TxRawResult{
|
|
|
|
Hex: mtxHex,
|
|
|
|
Txid: txSha,
|
|
|
|
Vout: vout,
|
|
|
|
Vin: vin,
|
|
|
|
Version: mtx.Version,
|
|
|
|
LockTime: mtx.LockTime,
|
|
|
|
}
|
|
|
|
|
|
|
|
if blk != nil {
|
|
|
|
blockHeader := &blk.MsgBlock().Header
|
|
|
|
idx := blk.Height()
|
|
|
|
|
|
|
|
// This is not a typo, they are identical in bitcoind as well.
|
|
|
|
txReply.Time = blockHeader.Timestamp.Unix()
|
|
|
|
txReply.Blocktime = blockHeader.Timestamp.Unix()
|
|
|
|
txReply.BlockHash = blksha.String()
|
|
|
|
txReply.Confirmations = uint64(1 + maxidx - idx)
|
|
|
|
}
|
|
|
|
|
|
|
|
return txReply, nil
|
|
|
|
}
|
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// handleDecodeRawTransaction handles decoderawtransaction commands.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleDecodeRawTransaction(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-12-31 01:22:39 +01:00
|
|
|
c := cmd.(*btcjson.DecodeRawTransactionCmd)
|
|
|
|
|
|
|
|
// Deserialize the transaction.
|
|
|
|
hexStr := c.HexTx
|
|
|
|
if len(hexStr)%2 != 0 {
|
|
|
|
hexStr = "0" + hexStr
|
|
|
|
}
|
|
|
|
serializedTx, err := hex.DecodeString(hexStr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
2014-07-02 03:09:22 +02:00
|
|
|
Code: btcjson.ErrDecodeHexString.Code,
|
2013-12-31 01:22:39 +01:00
|
|
|
Message: fmt.Sprintf("argument must be hexadecimal "+
|
|
|
|
"string (not %q)", hexStr),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var mtx btcwire.MsgTx
|
2014-06-05 19:58:28 +02:00
|
|
|
err = mtx.Deserialize(bytes.NewReader(serializedTx))
|
2013-12-31 01:22:39 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrDeserialization.Code,
|
|
|
|
Message: "TX decode failed",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
txSha, _ := mtx.TxSha()
|
|
|
|
|
2013-12-31 02:06:13 +01:00
|
|
|
vin, err := createVinList(&mtx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-05-28 00:44:55 +02:00
|
|
|
vout, err := createVoutList(&mtx, s.server.netParams)
|
2013-12-31 02:06:13 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2013-12-31 01:22:39 +01:00
|
|
|
// Create and return the result.
|
|
|
|
txReply := btcjson.TxRawDecodeResult{
|
|
|
|
Txid: txSha.String(),
|
|
|
|
Version: mtx.Version,
|
|
|
|
Locktime: mtx.LockTime,
|
2013-12-31 02:06:13 +01:00
|
|
|
Vin: vin,
|
|
|
|
Vout: vout,
|
2013-12-31 01:22:39 +01:00
|
|
|
}
|
|
|
|
return txReply, nil
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
2013-08-06 23:55:22 +02:00
|
|
|
|
2014-01-04 06:29:24 +01:00
|
|
|
// handleDecodeScript handles decodescript commands.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleDecodeScript(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-01-04 06:29:24 +01:00
|
|
|
c := cmd.(*btcjson.DecodeScriptCmd)
|
|
|
|
|
|
|
|
// Convert the hex script to bytes.
|
2014-07-02 03:09:22 +02:00
|
|
|
hexStr := c.HexScript
|
|
|
|
if len(hexStr)%2 != 0 {
|
|
|
|
hexStr = "0" + hexStr
|
|
|
|
}
|
|
|
|
script, err := hex.DecodeString(hexStr)
|
2014-01-04 06:29:24 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
2014-07-02 03:09:22 +02:00
|
|
|
Code: btcjson.ErrDecodeHexString.Code,
|
2014-01-04 06:29:24 +01:00
|
|
|
Message: fmt.Sprintf("argument must be hexadecimal "+
|
2014-07-02 03:09:22 +02:00
|
|
|
"string (not %q)", hexStr),
|
2014-01-04 06:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The disassembled string will contain [error] inline if the script
|
|
|
|
// doesn't fully parse, so ignore the error here.
|
|
|
|
disbuf, _ := btcscript.DisasmString(script)
|
|
|
|
|
|
|
|
// Get information about the script.
|
2014-01-08 03:30:01 +01:00
|
|
|
// Ignore the error here since an error means the script couldn't parse
|
|
|
|
// and there is no additinal information about it anyways.
|
2014-05-28 00:44:55 +02:00
|
|
|
net := s.server.netParams
|
2014-01-08 03:30:01 +01:00
|
|
|
scriptClass, addrs, reqSigs, _ := btcscript.ExtractPkScriptAddrs(script, net)
|
|
|
|
addresses := make([]string, len(addrs))
|
|
|
|
for i, addr := range addrs {
|
|
|
|
addresses[i] = addr.EncodeAddress()
|
2014-01-04 06:29:24 +01:00
|
|
|
}
|
|
|
|
|
2014-01-04 19:42:50 +01:00
|
|
|
// Convert the script itself to a pay-to-script-hash address.
|
|
|
|
p2sh, err := btcutil.NewAddressScriptHash(script, net)
|
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-04 06:29:24 +01:00
|
|
|
// Generate and return the reply.
|
|
|
|
reply := btcjson.DecodeScriptResult{
|
|
|
|
Asm: disbuf,
|
2014-06-29 23:36:41 +02:00
|
|
|
ReqSigs: int32(reqSigs),
|
2014-01-08 03:30:01 +01:00
|
|
|
Type: scriptClass.String(),
|
2014-01-04 06:29:24 +01:00
|
|
|
Addresses: addresses,
|
2014-01-04 19:42:50 +01:00
|
|
|
P2sh: p2sh.EncodeAddress(),
|
2014-01-04 06:29:24 +01:00
|
|
|
}
|
|
|
|
return reply, nil
|
|
|
|
}
|
|
|
|
|
2014-01-24 21:10:02 +01:00
|
|
|
// handleGetAddedNodeInfo handles getaddednodeinfo commands.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetAddedNodeInfo(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-01-24 21:10:02 +01:00
|
|
|
c := cmd.(*btcjson.GetAddedNodeInfoCmd)
|
|
|
|
|
|
|
|
// Retrieve a list of persistent (added) peers from the bitcoin server
|
|
|
|
// and filter the list of peer per the specified address (if any).
|
|
|
|
peers := s.server.AddedNodeInfo()
|
|
|
|
if c.Node != "" {
|
|
|
|
found := false
|
|
|
|
for i, peer := range peers {
|
|
|
|
if peer.addr == c.Node {
|
|
|
|
peers = peers[i : i+1]
|
|
|
|
found = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: -24,
|
|
|
|
Message: "Node has not been added.",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-04 08:26:12 +01:00
|
|
|
// Without the dns flag, the result is just a slice of the addresses as
|
|
|
|
// strings.
|
2014-01-24 21:10:02 +01:00
|
|
|
if !c.Dns {
|
|
|
|
results := make([]string, 0, len(peers))
|
|
|
|
for _, peer := range peers {
|
|
|
|
results = append(results, peer.addr)
|
|
|
|
}
|
|
|
|
return results, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the dns flag, the result is an array of JSON objects which
|
|
|
|
// include the result of DNS lookups for each peer.
|
|
|
|
results := make([]*btcjson.GetAddedNodeInfoResult, 0, len(peers))
|
|
|
|
for _, peer := range peers {
|
|
|
|
// Set the "address" of the peer which could be an ip address
|
|
|
|
// or a domain name.
|
|
|
|
var result btcjson.GetAddedNodeInfoResult
|
|
|
|
result.AddedNode = peer.addr
|
|
|
|
isConnected := peer.Connected()
|
|
|
|
result.Connected = &isConnected
|
|
|
|
|
|
|
|
// Split the address into host and port portions so we can do
|
|
|
|
// a DNS lookup against the host. When no port is specified in
|
|
|
|
// the address, just use the address as the host.
|
|
|
|
host, _, err := net.SplitHostPort(peer.addr)
|
|
|
|
if err != nil {
|
|
|
|
host = peer.addr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do a DNS lookup for the address. If the lookup fails, just
|
|
|
|
// use the host.
|
|
|
|
var ipList []string
|
|
|
|
ips, err := btcdLookup(host)
|
|
|
|
if err == nil {
|
|
|
|
ipList = make([]string, 0, len(ips))
|
|
|
|
for _, ip := range ips {
|
|
|
|
ipList = append(ipList, ip.String())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ipList = make([]string, 1)
|
|
|
|
ipList[0] = host
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the addresses and connection info to the result.
|
|
|
|
addrs := make([]btcjson.GetAddedNodeInfoResultAddr, 0, len(ipList))
|
|
|
|
for _, ip := range ipList {
|
|
|
|
var addr btcjson.GetAddedNodeInfoResultAddr
|
|
|
|
addr.Address = ip
|
|
|
|
addr.Connected = "false"
|
|
|
|
if ip == host && peer.Connected() {
|
|
|
|
addr.Connected = directionString(peer.inbound)
|
|
|
|
}
|
|
|
|
addrs = append(addrs, addr)
|
|
|
|
}
|
|
|
|
result.Addresses = &addrs
|
|
|
|
results = append(results, &result)
|
|
|
|
}
|
|
|
|
return results, nil
|
|
|
|
}
|
|
|
|
|
2014-05-11 09:21:27 +02:00
|
|
|
// handleGetBestBlock implements the getbestblock command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetBestBlock(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-05-11 09:21:27 +02:00
|
|
|
// All other "get block" commands give either the height, the
|
|
|
|
// hash, or both but require the block SHA. This gets both for
|
|
|
|
// the best block.
|
|
|
|
sha, height, err := s.server.db.NewestSha()
|
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.ErrBestBlockHash
|
|
|
|
}
|
|
|
|
|
|
|
|
result := &btcws.GetBestBlockResult{
|
|
|
|
Hash: sha.String(),
|
|
|
|
Height: int32(height),
|
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// handleGetBestBlockHash implements the getbestblockhash command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetBestBlockHash(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-29 16:42:34 +01:00
|
|
|
sha, _, err := s.server.db.NewestSha()
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Error getting newest sha: %v", err)
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, btcjson.ErrBestBlockHash
|
|
|
|
}
|
2013-08-14 22:55:31 +02:00
|
|
|
|
2013-11-07 19:53:22 +01:00
|
|
|
return sha.String(), nil
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// handleGetBlock implements the getblock command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetBlock(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-29 16:42:34 +01:00
|
|
|
c := cmd.(*btcjson.GetBlockCmd)
|
|
|
|
sha, err := btcwire.NewShaHashFromStr(c.Hash)
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Error generating sha: %v", err)
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, btcjson.ErrBlockNotFound
|
|
|
|
}
|
|
|
|
blk, err := s.server.db.FetchBlockBySha(sha)
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Error fetching sha: %v", err)
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, btcjson.ErrBlockNotFound
|
|
|
|
}
|
2013-12-26 18:11:12 +01:00
|
|
|
|
|
|
|
// When the verbose flag isn't set, simply return the network-serialized
|
|
|
|
// block as a hex-encoded string.
|
|
|
|
if !c.Verbose {
|
2013-12-27 05:33:31 +01:00
|
|
|
blkHex, err := messageToHex(blk.MsgBlock())
|
2013-12-26 18:11:12 +01:00
|
|
|
if err != nil {
|
2013-12-27 05:33:31 +01:00
|
|
|
return nil, err
|
2013-12-26 18:11:12 +01:00
|
|
|
}
|
|
|
|
return blkHex, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// The verbose flag is set, so generate the JSON object and return it.
|
2013-10-29 16:42:34 +01:00
|
|
|
buf, err := blk.Bytes()
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Error fetching block: %v", err)
|
2013-12-26 18:11:12 +01:00
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
2013-08-06 23:55:22 +02:00
|
|
|
}
|
2013-12-26 18:11:12 +01:00
|
|
|
idx := blk.Height()
|
2013-10-29 16:42:34 +01:00
|
|
|
_, maxidx, err := s.server.db.NewestSha()
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Cannot get newest sha: %v", err)
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, btcjson.ErrBlockNotFound
|
|
|
|
}
|
Clean up notification contexts and goroutines after ws disconnect.
This refactors the wallet notification code to reverse the order of
how notification contexts are stored. Before, watched addresses and
outpoints were used as keys, with a special reply channel as the
value. This channel was read from and replies were marshalled and
sent to the main wallet notification chan, but the goroutine handling
this marshalling never exited because the reply channel was never
closed (and couldn't have been, because there was no way to tell it
was handling notifications for any particular wallet).
Notification contexts are now primarily mapped by wallet notification
channels, and code to send the notifications send directly to the
wallet channel, with the previous goroutine reading the reply chan
properly closing.
The RPC code is also refactored with this change as well, to separate
it more from websocket code. Websocket JSON extensions are no longer
available to RPC clients.
While here, unbreak RPC. Previously, replies were never sent back.
This broke when I merged in my websocket code, as sends for the reply
channel in jsonRead blocked before a reader for the channel was
opened. A 3 liner could have fixed this, but doing a proper fix
(changing jsonRead so it did not use the reply channel as it is
unneeded for the standard RPC API) is preferred.
2013-10-16 20:12:00 +02:00
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
blockHeader := &blk.MsgBlock().Header
|
|
|
|
blockReply := btcjson.BlockResult{
|
|
|
|
Hash: c.Hash,
|
|
|
|
Version: blockHeader.Version,
|
|
|
|
MerkleRoot: blockHeader.MerkleRoot.String(),
|
|
|
|
PreviousHash: blockHeader.PrevBlock.String(),
|
|
|
|
Nonce: blockHeader.Nonce,
|
|
|
|
Time: blockHeader.Timestamp.Unix(),
|
|
|
|
Confirmations: uint64(1 + maxidx - idx),
|
|
|
|
Height: idx,
|
2014-06-29 23:36:41 +02:00
|
|
|
Size: int32(len(buf)),
|
2013-10-29 16:42:34 +01:00
|
|
|
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
|
|
|
|
Difficulty: getDifficultyRatio(blockHeader.Bits),
|
|
|
|
}
|
Clean up notification contexts and goroutines after ws disconnect.
This refactors the wallet notification code to reverse the order of
how notification contexts are stored. Before, watched addresses and
outpoints were used as keys, with a special reply channel as the
value. This channel was read from and replies were marshalled and
sent to the main wallet notification chan, but the goroutine handling
this marshalling never exited because the reply channel was never
closed (and couldn't have been, because there was no way to tell it
was handling notifications for any particular wallet).
Notification contexts are now primarily mapped by wallet notification
channels, and code to send the notifications send directly to the
wallet channel, with the previous goroutine reading the reply chan
properly closing.
The RPC code is also refactored with this change as well, to separate
it more from websocket code. Websocket JSON extensions are no longer
available to RPC clients.
While here, unbreak RPC. Previously, replies were never sent back.
This broke when I merged in my websocket code, as sends for the reply
channel in jsonRead blocked before a reader for the channel was
opened. A 3 liner could have fixed this, but doing a proper fix
(changing jsonRead so it did not use the reply channel as it is
unneeded for the standard RPC API) is preferred.
2013-10-16 20:12:00 +02:00
|
|
|
|
2013-12-08 20:57:14 +01:00
|
|
|
if !c.VerboseTx {
|
2014-03-24 19:18:27 +01:00
|
|
|
transactions := blk.Transactions()
|
|
|
|
txNames := make([]string, len(transactions))
|
|
|
|
for i, tx := range transactions {
|
|
|
|
txNames[i] = tx.Sha().String()
|
2013-12-08 20:57:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
blockReply.Tx = txNames
|
|
|
|
} else {
|
|
|
|
txns := blk.Transactions()
|
|
|
|
rawTxns := make([]btcjson.TxRawResult, len(txns))
|
|
|
|
for i, tx := range txns {
|
|
|
|
txSha := tx.Sha().String()
|
|
|
|
mtx := tx.MsgTx()
|
|
|
|
|
2014-05-28 00:44:55 +02:00
|
|
|
rawTxn, err := createTxRawResult(s.server.netParams,
|
|
|
|
txSha, mtx, blk, maxidx, sha)
|
2013-12-08 20:57:14 +01:00
|
|
|
if err != nil {
|
2013-12-27 06:22:41 +01:00
|
|
|
rpcsLog.Errorf("Cannot create TxRawResult for "+
|
|
|
|
"transaction %s: %v", txSha, err)
|
2013-12-08 20:57:14 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rawTxns[i] = *rawTxn
|
|
|
|
}
|
|
|
|
blockReply.RawTx = rawTxns
|
|
|
|
}
|
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// Get next block unless we are already at the top.
|
|
|
|
if idx < maxidx {
|
|
|
|
var shaNext *btcwire.ShaHash
|
|
|
|
shaNext, err = s.server.db.FetchBlockShaByHeight(int64(idx + 1))
|
2013-08-06 23:55:22 +02:00
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("No next block: %v", err)
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, btcjson.ErrBlockNotFound
|
2013-08-06 23:55:22 +02:00
|
|
|
}
|
2013-10-29 16:42:34 +01:00
|
|
|
blockReply.NextHash = shaNext.String()
|
|
|
|
}
|
2013-08-06 23:55:22 +02:00
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
return blockReply, nil
|
|
|
|
}
|
2013-08-06 23:55:22 +02:00
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// handleGetBlockCount implements the getblockcount command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetBlockCount(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-29 16:42:34 +01:00
|
|
|
_, maxidx, err := s.server.db.NewestSha()
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Error getting newest sha: %v", err)
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, btcjson.ErrBlockCount
|
|
|
|
}
|
2013-08-06 23:55:22 +02:00
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
return maxidx, nil
|
|
|
|
}
|
2013-08-06 23:55:22 +02:00
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// handleGetBlockHash implements the getblockhash command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetBlockHash(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-29 16:42:34 +01:00
|
|
|
c := cmd.(*btcjson.GetBlockHashCmd)
|
|
|
|
sha, err := s.server.db.FetchBlockShaByHeight(c.Index)
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Error getting block: %v", err)
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, btcjson.ErrOutOfRange
|
|
|
|
}
|
2013-08-06 23:55:22 +02:00
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
return sha.String(), nil
|
|
|
|
}
|
2013-08-06 23:55:22 +02:00
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// encodeTemplateID encodes the passed details into an ID that can be used to
|
|
|
|
// uniquely identify a block template.
|
|
|
|
func encodeTemplateID(prevHash *btcwire.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) (*btcwire.ShaHash, int64, error) {
|
|
|
|
fields := strings.Split(templateID, "-")
|
|
|
|
if len(fields) != 2 {
|
|
|
|
return nil, 0, errors.New("invalid longpollid format")
|
|
|
|
}
|
|
|
|
|
|
|
|
prevHash, err := btcwire.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)
|
2014-06-27 21:12:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, 0, errors.New("invalid longpollid format")
|
|
|
|
}
|
|
|
|
|
2014-06-30 21:14:31 +02:00
|
|
|
return prevHash, lastGenerated, nil
|
2014-06-27 21:12:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 *btcwire.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 *btcwire.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 *btcwire.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
|
|
|
|
}
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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 *btcwire.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 {
|
|
|
|
errStr := fmt.Sprintf("Failed to create new block "+
|
|
|
|
"template: %v", err)
|
2014-06-30 21:14:31 +02:00
|
|
|
rpcsLog.Error(errStr)
|
2014-06-27 21:12:22 +02:00
|
|
|
return btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: errStr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
template = blkTemplate
|
|
|
|
msgBlock = template.block
|
|
|
|
targetDifficulty = fmt.Sprintf("%064x",
|
|
|
|
btcchain.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 {
|
|
|
|
return btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2014-06-27 21:12:22 +02:00
|
|
|
|
|
|
|
// Notify any clients that are long polling about the new
|
|
|
|
// template.
|
|
|
|
state.notifyLongPollers(latestHash, lastTxUpdate)
|
2014-06-27 21:12:22 +02:00
|
|
|
} 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 := btcscript.PayToAddrScript(payToAddr)
|
|
|
|
if err != nil {
|
|
|
|
return btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
template.block.Transactions[0].TxOut[0].PkScript = pkScript
|
|
|
|
template.validPayAddress = true
|
|
|
|
|
|
|
|
// Update the merkle root.
|
|
|
|
block := btcutil.NewBlock(template.block)
|
|
|
|
merkles := btcchain.BuildMerkleTreeStore(block.Transactions())
|
|
|
|
template.block.Header.MerkleRoot = *merkles[len(merkles)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set locals for convenience.
|
|
|
|
msgBlock = template.block
|
|
|
|
targetDifficulty = fmt.Sprintf("%064x",
|
|
|
|
btcchain.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.
|
2014-06-27 21:12:22 +02:00
|
|
|
func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld *bool) (*btcjson.GetBlockTemplateResult, error) {
|
Implement some BIP0023 getblocktemplate mutations.
This commit implements a portion of the mutations section of BIP0023.
In particular, it adds the mutable, mintime, maxtime, and noncerange keys
to the returned block template along with indicating support for the time,
transactions/add, prevblock, and coinbase/append mutations. Also, the
addition of the mintime and maxtime fields imply support for the
time/decrement and time/increment mutations. Further, if the caller
indicates the coinbasevalue capability, the coinbasetxn field will be
omitted thereby implying support for the coinbase and generation
mutations.
Closes #124.
2014-06-30 03:00:51 +02:00
|
|
|
// 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
|
|
|
|
curTime := time.Now()
|
|
|
|
if curTime.Before(state.minTimestamp) {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrOutOfRange.Code,
|
|
|
|
Message: fmt.Sprintf("The local time is before the "+
|
|
|
|
"minimum allowed time for a block - current "+
|
|
|
|
"time %v, minimum time %v", curTime,
|
|
|
|
state.minTimestamp),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
maxTime := curTime.Add(time.Second * btcchain.MaxTimeOffsetSeconds)
|
|
|
|
if header.Timestamp.After(maxTime) {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrOutOfRange.Code,
|
|
|
|
Message: fmt.Sprintf("The template time is after the "+
|
|
|
|
"maximum allowed time for a block - template "+
|
|
|
|
"time %v, maximum time %v", curTime, maxTime),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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[btcwire.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 {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
Implement some BIP0023 getblocktemplate mutations.
This commit implements a portion of the mutations section of BIP0023.
In particular, it adds the mutable, mintime, maxtime, and noncerange keys
to the returned block template along with indicating support for the time,
transactions/add, prevblock, and coinbase/append mutations. Also, the
addition of the mintime and maxtime fields imply support for the
time/decrement and time/increment mutations. Further, if the caller
indicates the coinbasevalue capability, the coinbasetxn field will be
omitted thereby implying support for the coinbase and generation
mutations.
Closes #124.
2014-06-30 03:00:51 +02:00
|
|
|
// 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
|
2014-06-30 02:45:10 +02:00
|
|
|
targetDifficulty := fmt.Sprintf("%064x", btcchain.CompactToBig(header.Bits))
|
2014-06-27 21:12:22 +02:00
|
|
|
templateID := encodeTemplateID(state.prevHash, state.lastGenerated)
|
2014-06-27 21:12:22 +02:00
|
|
|
reply := btcjson.GetBlockTemplateResult{
|
|
|
|
Bits: strconv.FormatInt(int64(header.Bits), 16),
|
Implement some BIP0023 getblocktemplate mutations.
This commit implements a portion of the mutations section of BIP0023.
In particular, it adds the mutable, mintime, maxtime, and noncerange keys
to the returned block template along with indicating support for the time,
transactions/add, prevblock, and coinbase/append mutations. Also, the
addition of the mintime and maxtime fields imply support for the
time/decrement and time/increment mutations. Further, if the caller
indicates the coinbasevalue capability, the coinbasetxn field will be
omitted thereby implying support for the coinbase and generation
mutations.
Closes #124.
2014-06-30 03:00:51 +02:00
|
|
|
CurTime: curTime.Unix(),
|
2014-06-27 21:12:22 +02:00
|
|
|
Height: template.height,
|
|
|
|
PreviousHash: header.PrevBlock.String(),
|
|
|
|
SigOpLimit: btcchain.MaxSigOpsPerBlock,
|
|
|
|
SizeLimit: btcwire.MaxBlockPayload,
|
|
|
|
Transactions: transactions,
|
|
|
|
Version: header.Version,
|
2014-06-27 21:12:22 +02:00
|
|
|
LongPollID: templateID,
|
|
|
|
SubmitOld: submitOld,
|
2014-06-30 02:45:10 +02:00
|
|
|
Target: targetDifficulty,
|
Implement some BIP0023 getblocktemplate mutations.
This commit implements a portion of the mutations section of BIP0023.
In particular, it adds the mutable, mintime, maxtime, and noncerange keys
to the returned block template along with indicating support for the time,
transactions/add, prevblock, and coinbase/append mutations. Also, the
addition of the mintime and maxtime fields imply support for the
time/decrement and time/increment mutations. Further, if the caller
indicates the coinbasevalue capability, the coinbasetxn field will be
omitted thereby implying support for the coinbase and generation
mutations.
Closes #124.
2014-06-30 03:00:51 +02:00
|
|
|
MinTime: state.minTimestamp.Unix(),
|
|
|
|
MaxTime: maxTime.Unix(),
|
|
|
|
Mutable: gbtMutableFields,
|
|
|
|
NonceRange: gbtNonceRange,
|
2014-06-27 21:12:22 +02:00
|
|
|
Capabilities: gbtCapabilities,
|
2014-06-27 21:12:22 +02:00
|
|
|
}
|
|
|
|
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.Error{
|
|
|
|
Code: btcjson.ErrInternal.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 " +
|
2014-06-27 21:12:22 +02:00
|
|
|
"addresses via --miningaddr",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serialize the transaction for conversion to hex.
|
|
|
|
tx := msgBlock.Transactions[0]
|
|
|
|
txHash, _ := tx.TxSha()
|
|
|
|
txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
|
|
|
|
if err := tx.Serialize(txBuf); err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
resultTx := btcjson.GetBlockTemplateResultTx{
|
|
|
|
Data: hex.EncodeToString(txBuf.Bytes()),
|
|
|
|
Hash: txHash.String(),
|
|
|
|
Depends: []int64{},
|
|
|
|
Fee: template.fees[0],
|
|
|
|
SigOps: template.sigOpCounts[0],
|
|
|
|
}
|
|
|
|
|
|
|
|
reply.CoinbaseTxn = &resultTx
|
|
|
|
}
|
|
|
|
|
|
|
|
return &reply, nil
|
|
|
|
}
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// handleGetBlockTemplateRequest is a helper for handleGetBlockTemplate which
|
|
|
|
// deals with generating and returning block templates to the caller. It
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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) {
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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.Error{
|
|
|
|
Code: btcjson.ErrInternal.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",
|
2014-06-27 21:12:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an error if there are no peers connected since there is no
|
|
|
|
// way to relay a found block or receive transactions to work on.
|
|
|
|
// However, allow this state when running in the regression test or
|
|
|
|
// simulation test mode.
|
|
|
|
if !(cfg.RegressionTest || cfg.SimNet) && s.server.ConnectedCount() == 0 {
|
|
|
|
return nil, btcjson.ErrClientNotConnected
|
|
|
|
}
|
|
|
|
|
|
|
|
// No point in generating or accepting work before the chain is synced.
|
|
|
|
_, currentHeight := s.server.blockManager.chainState.Best()
|
|
|
|
if currentHeight != 0 && !s.server.blockManager.IsCurrent() {
|
|
|
|
return nil, btcjson.ErrClientInInitialDownload
|
|
|
|
}
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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
|
|
|
|
}
|
2014-06-27 21:12:22 +02:00
|
|
|
return state.blockTemplateResult(useCoinbaseValue, nil)
|
2014-06-27 21:12:22 +02:00
|
|
|
}
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// 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.(btcchain.RuleError)
|
|
|
|
if !ok {
|
|
|
|
return "rejected: " + err.Error()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ruleErr.ErrorCode {
|
|
|
|
case btcchain.ErrDuplicateBlock:
|
|
|
|
return "duplicate"
|
|
|
|
case btcchain.ErrBlockTooBig:
|
|
|
|
return "bad-block-size"
|
|
|
|
case btcchain.ErrBlockVersionTooOld:
|
|
|
|
return "bad-version"
|
|
|
|
case btcchain.ErrInvalidTime:
|
|
|
|
return "bad-time"
|
|
|
|
case btcchain.ErrTimeTooOld:
|
|
|
|
return "time-too-old"
|
|
|
|
case btcchain.ErrTimeTooNew:
|
|
|
|
return "time-too-new"
|
|
|
|
case btcchain.ErrDifficultyTooLow:
|
|
|
|
return "bad-diffbits"
|
|
|
|
case btcchain.ErrUnexpectedDifficulty:
|
|
|
|
return "bad-diffbits"
|
|
|
|
case btcchain.ErrHighHash:
|
|
|
|
return "high-hash"
|
|
|
|
case btcchain.ErrBadMerkleRoot:
|
|
|
|
return "bad-txnmrklroot"
|
|
|
|
case btcchain.ErrBadCheckpoint:
|
|
|
|
return "bad-checkpoint"
|
|
|
|
case btcchain.ErrForkTooOld:
|
|
|
|
return "fork-too-old"
|
2014-07-11 16:57:03 +02:00
|
|
|
case btcchain.ErrCheckpointTimeTooOld:
|
|
|
|
return "checkpoint-time-too-old"
|
2014-06-27 21:12:22 +02:00
|
|
|
case btcchain.ErrNoTransactions:
|
|
|
|
return "bad-txns-none"
|
|
|
|
case btcchain.ErrTooManyTransactions:
|
|
|
|
return "bad-txns-toomany"
|
|
|
|
case btcchain.ErrNoTxInputs:
|
|
|
|
return "bad-txns-noinputs"
|
|
|
|
case btcchain.ErrNoTxOutputs:
|
|
|
|
return "bad-txns-nooutputs"
|
|
|
|
case btcchain.ErrTxTooBig:
|
|
|
|
return "bad-txns-size"
|
|
|
|
case btcchain.ErrBadTxOutValue:
|
|
|
|
return "bad-txns-outputvalue"
|
|
|
|
case btcchain.ErrDuplicateTxInputs:
|
|
|
|
return "bad-txns-dupinputs"
|
|
|
|
case btcchain.ErrBadTxInput:
|
|
|
|
return "bad-txns-badinput"
|
|
|
|
case btcchain.ErrMissingTx:
|
|
|
|
return "bad-txns-missinginput"
|
|
|
|
case btcchain.ErrUnfinalizedTx:
|
|
|
|
return "bad-txns-unfinalizedtx"
|
|
|
|
case btcchain.ErrDuplicateTx:
|
|
|
|
return "bad-txns-duplicate"
|
|
|
|
case btcchain.ErrOverwriteTx:
|
|
|
|
return "bad-txns-overwrite"
|
|
|
|
case btcchain.ErrImmatureSpend:
|
|
|
|
return "bad-txns-maturity"
|
|
|
|
case btcchain.ErrDoubleSpend:
|
|
|
|
return "bad-txns-dblspend"
|
|
|
|
case btcchain.ErrSpendTooHigh:
|
|
|
|
return "bad-txns-highspend"
|
|
|
|
case btcchain.ErrBadFees:
|
|
|
|
return "bad-txns-fees"
|
|
|
|
case btcchain.ErrTooManySigOps:
|
|
|
|
return "high-sigops"
|
|
|
|
case btcchain.ErrFirstTxNotCoinbase:
|
|
|
|
return "bad-txns-nocoinbase"
|
|
|
|
case btcchain.ErrMultipleCoinbases:
|
|
|
|
return "bad-txns-multicoinbase"
|
|
|
|
case btcchain.ErrBadCoinbaseScriptLen:
|
|
|
|
return "bad-cb-length"
|
|
|
|
case btcchain.ErrBadCoinbaseValue:
|
|
|
|
return "bad-cb-value"
|
|
|
|
case btcchain.ErrMissingCoinbaseHeight:
|
|
|
|
return "bad-cb-height"
|
|
|
|
case btcchain.ErrBadCoinbaseHeight:
|
|
|
|
return "bad-cb-height"
|
|
|
|
case btcchain.ErrScriptMalformed:
|
|
|
|
return "bad-script-malformed"
|
|
|
|
case btcchain.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.Error{
|
|
|
|
Code: btcjson.ErrType.Code,
|
|
|
|
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.Error{
|
|
|
|
Code: btcjson.ErrDeserialization.Code,
|
|
|
|
Message: fmt.Sprintf("data must be "+
|
|
|
|
"hexadecimal string (not %q)", hexData),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var msgBlock btcwire.MsgBlock
|
|
|
|
if err := msgBlock.Deserialize(bytes.NewReader(dataBytes)); err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrDeserialization.Code,
|
|
|
|
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 := btcchain.BFDryRun | btcchain.BFNoPoWCheck
|
|
|
|
isOrphan, err := s.server.blockManager.ProcessBlock(block, flags)
|
|
|
|
if err != nil {
|
|
|
|
if _, ok := err.(btcchain.RuleError); !ok {
|
|
|
|
rpcsLog.Errorf("Failed to process block proposal: %v",
|
|
|
|
err)
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: -25, // ErrRpcVerify
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Infof("Rejected block proposal: %v", err)
|
|
|
|
return chainErrToGBTErrString(err), nil
|
|
|
|
}
|
|
|
|
if isOrphan {
|
|
|
|
return "orphan", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2014-06-27 21:12:22 +02:00
|
|
|
// handleGetBlockTemplate implements the getblocktemplate command.
|
|
|
|
//
|
2014-06-27 21:12:22 +02:00
|
|
|
// See https://en.bitcoin.it/wiki/BIP_0022 and
|
|
|
|
// https://en.bitcoin.it/wiki/BIP_0023 for more details.
|
2014-06-27 21:12:22 +02:00
|
|
|
func handleGetBlockTemplate(s *rpcServer, cmd btcjson.Cmd, 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":
|
2014-06-27 21:12:22 +02:00
|
|
|
return handleGetBlockTemplateRequest(s, request, closeChan)
|
2014-06-27 21:12:22 +02:00
|
|
|
case "proposal":
|
|
|
|
return handleGetBlockTemplateProposal(s, request)
|
2014-06-27 21:12:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInvalidParameter.Code,
|
|
|
|
Message: "Invalid mode",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// handleGetConnectionCount implements the getconnectioncount command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetConnectionCount(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-21 19:45:30 +02:00
|
|
|
return s.server.ConnectedCount(), nil
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
Clean up notification contexts and goroutines after ws disconnect.
This refactors the wallet notification code to reverse the order of
how notification contexts are stored. Before, watched addresses and
outpoints were used as keys, with a special reply channel as the
value. This channel was read from and replies were marshalled and
sent to the main wallet notification chan, but the goroutine handling
this marshalling never exited because the reply channel was never
closed (and couldn't have been, because there was no way to tell it
was handling notifications for any particular wallet).
Notification contexts are now primarily mapped by wallet notification
channels, and code to send the notifications send directly to the
wallet channel, with the previous goroutine reading the reply chan
properly closing.
The RPC code is also refactored with this change as well, to separate
it more from websocket code. Websocket JSON extensions are no longer
available to RPC clients.
While here, unbreak RPC. Previously, replies were never sent back.
This broke when I merged in my websocket code, as sends for the reply
channel in jsonRead blocked before a reader for the channel was
opened. A 3 liner could have fixed this, but doing a proper fix
(changing jsonRead so it did not use the reply channel as it is
unneeded for the standard RPC API) is preferred.
2013-10-16 20:12:00 +02:00
|
|
|
|
2014-05-11 09:21:27 +02:00
|
|
|
// handleGetCurrentNet implements the getcurrentnet command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetCurrentNet(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-05-28 00:44:55 +02:00
|
|
|
return s.server.netParams.Net, nil
|
2014-05-11 09:21:27 +02:00
|
|
|
}
|
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// handleGetDifficulty implements the getdifficulty command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetDifficulty(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-29 16:42:34 +01:00
|
|
|
sha, _, err := s.server.db.NewestSha()
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Error getting sha: %v", err)
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, btcjson.ErrDifficulty
|
|
|
|
}
|
2014-01-29 02:09:42 +01:00
|
|
|
blockHeader, err := s.server.db.FetchBlockHeaderBySha(sha)
|
2013-10-29 16:42:34 +01:00
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Error getting block: %v", err)
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, btcjson.ErrDifficulty
|
|
|
|
}
|
|
|
|
return getDifficultyRatio(blockHeader.Bits), nil
|
|
|
|
}
|
2013-10-29 01:43:09 +01:00
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// handleGetGenerate implements the getgenerate command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetGenerate(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-06-12 04:51:21 +02:00
|
|
|
return s.server.cpuMiner.IsMining(), nil
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
2013-10-29 01:43:09 +01:00
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// handleGetHashesPerSec implements the gethashespersec command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetHashesPerSec(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-06-12 04:51:21 +02:00
|
|
|
return int64(s.server.cpuMiner.HashesPerSecond()), nil
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
2013-10-29 01:43:09 +01:00
|
|
|
|
2014-01-29 02:24:30 +01:00
|
|
|
// handleGetInfo implements the getinfo command. We only return the fields
|
|
|
|
// that are not related to wallet functionality.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetInfo(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-01-29 02:24:30 +01:00
|
|
|
// We require the current block height and sha.
|
|
|
|
sha, height, err := s.server.db.NewestSha()
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Error getting sha: %v", err)
|
|
|
|
return nil, btcjson.ErrBlockCount
|
|
|
|
}
|
|
|
|
blkHeader, err := s.server.db.FetchBlockHeaderBySha(sha)
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Error getting block: %v", err)
|
|
|
|
return nil, btcjson.ErrDifficulty
|
|
|
|
}
|
|
|
|
|
2014-03-15 20:36:16 +01:00
|
|
|
ret := &btcjson.InfoResult{
|
2014-06-29 23:36:41 +02:00
|
|
|
Version: int32(1000000*appMajor + 10000*appMinor + 100*appPatch),
|
|
|
|
ProtocolVersion: int32(maxProtocolVersion),
|
|
|
|
Blocks: int32(height),
|
2014-03-15 20:36:16 +01:00
|
|
|
TimeOffset: 0,
|
2014-05-03 21:07:08 +02:00
|
|
|
Connections: s.server.ConnectedCount(),
|
2014-03-15 20:36:16 +01:00
|
|
|
Proxy: cfg.Proxy,
|
|
|
|
Difficulty: getDifficultyRatio(blkHeader.Bits),
|
|
|
|
TestNet: cfg.TestNet3,
|
2014-07-07 16:05:17 +02:00
|
|
|
RelayFee: float64(minTxRelayFee) / btcutil.SatoshiPerBitcoin,
|
2014-01-29 02:24:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
2014-05-04 06:51:54 +02:00
|
|
|
// handleGetMiningInfo implements the getmininginfo command. We only return the
|
|
|
|
// fields that are not related to wallet functionality.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetMiningInfo(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-05-04 06:51:54 +02:00
|
|
|
sha, height, err := s.server.db.NewestSha()
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Error getting sha: %v", err)
|
|
|
|
return nil, btcjson.ErrBlockCount
|
|
|
|
}
|
|
|
|
block, err := s.server.db.FetchBlockBySha(sha)
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Error getting block: %v", err)
|
|
|
|
return nil, btcjson.ErrBlockNotFound
|
|
|
|
}
|
|
|
|
blockBytes, err := block.Bytes()
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Error getting block: %v", err)
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a default getnetworkhashps command to use defaults and make
|
|
|
|
// use of the existing getnetworkhashps handler.
|
|
|
|
gnhpsCmd, err := btcjson.NewGetNetworkHashPSCmd(0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
2014-06-27 18:13:04 +02:00
|
|
|
networkHashesPerSecIface, err := handleGetNetworkHashPS(s, gnhpsCmd,
|
|
|
|
closeChan)
|
2014-05-04 06:51:54 +02:00
|
|
|
if err != nil {
|
|
|
|
// This is already a btcjson.Error from the handler.
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
networkHashesPerSec, ok := networkHashesPerSecIface.(int64)
|
|
|
|
if !ok {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: "networkHashesPerSec is not an int64",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-09 08:32:56 +02:00
|
|
|
result := btcjson.GetMiningInfoResult{
|
2014-05-04 06:51:54 +02:00
|
|
|
Blocks: height,
|
|
|
|
CurrentBlockSize: uint64(len(blockBytes)),
|
|
|
|
CurrentBlockTx: uint64(len(block.MsgBlock().Transactions)),
|
|
|
|
Difficulty: getDifficultyRatio(block.MsgBlock().Header.Bits),
|
2014-06-12 04:51:21 +02:00
|
|
|
Generate: s.server.cpuMiner.IsMining(),
|
2014-06-29 23:36:41 +02:00
|
|
|
GenProcLimit: s.server.cpuMiner.NumWorkers(),
|
2014-06-12 04:51:21 +02:00
|
|
|
HashesPerSec: int64(s.server.cpuMiner.HashesPerSecond()),
|
2014-05-04 06:51:54 +02:00
|
|
|
NetworkHashPS: networkHashesPerSec,
|
|
|
|
PooledTx: uint64(s.server.txMemPool.Count()),
|
|
|
|
TestNet: cfg.TestNet3,
|
|
|
|
}
|
|
|
|
return &result, nil
|
|
|
|
}
|
|
|
|
|
2014-02-04 08:26:12 +01:00
|
|
|
// handleGetNetTotals implements the getnettotals command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetNetTotals(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-02-05 18:09:45 +01:00
|
|
|
totalBytesRecv, totalBytesSent := s.server.NetTotals()
|
2014-02-04 08:26:12 +01:00
|
|
|
reply := &btcjson.GetNetTotalsResult{
|
2014-02-05 18:09:45 +01:00
|
|
|
TotalBytesRecv: totalBytesRecv,
|
|
|
|
TotalBytesSent: totalBytesSent,
|
2014-02-05 18:25:40 +01:00
|
|
|
TimeMillis: time.Now().UTC().UnixNano() / int64(time.Millisecond),
|
2014-02-04 08:26:12 +01:00
|
|
|
}
|
|
|
|
return reply, nil
|
|
|
|
}
|
|
|
|
|
2014-02-07 23:31:14 +01:00
|
|
|
// handleGetNetworkHashPS implements the getnetworkhashps command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetNetworkHashPS(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-02-07 23:31:14 +01:00
|
|
|
c := cmd.(*btcjson.GetNetworkHashPSCmd)
|
|
|
|
|
|
|
|
_, newestHeight, err := s.server.db.NewestSha()
|
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// When the passed height is too high or zero, just return 0 now
|
|
|
|
// since we can't reasonably calculate the number of network hashes
|
|
|
|
// per second from invalid values. When it's negative, use the current
|
|
|
|
// best block height.
|
|
|
|
endHeight := int64(c.Height)
|
|
|
|
if endHeight > newestHeight || endHeight == 0 {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
if endHeight < 0 {
|
|
|
|
endHeight = newestHeight
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the starting block height based on the passed number of
|
|
|
|
// blocks. When the passed value is negative, use the last block the
|
|
|
|
// difficulty changed as the starting height. Also make sure the
|
|
|
|
// starting height is not before the beginning of the chain.
|
|
|
|
var startHeight int64
|
|
|
|
if c.Blocks <= 0 {
|
|
|
|
startHeight = endHeight - ((endHeight % btcchain.BlocksPerRetarget) + 1)
|
|
|
|
} else {
|
|
|
|
startHeight = endHeight - int64(c.Blocks)
|
|
|
|
}
|
|
|
|
if startHeight < 0 {
|
|
|
|
startHeight = 0
|
|
|
|
}
|
|
|
|
rpcsLog.Debugf("Calculating network hashes per second from %d to %d",
|
|
|
|
startHeight, endHeight)
|
|
|
|
|
|
|
|
// Find the min and max block timestamps as well as calculate the total
|
|
|
|
// amount of work that happened between the start and end blocks.
|
|
|
|
var minTimestamp, maxTimestamp time.Time
|
|
|
|
totalWork := big.NewInt(0)
|
|
|
|
for curHeight := startHeight; curHeight <= endHeight; curHeight++ {
|
|
|
|
hash, err := s.server.db.FetchBlockShaByHeight(curHeight)
|
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
header, err := s.server.db.FetchBlockHeaderBySha(hash)
|
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if curHeight == startHeight {
|
|
|
|
minTimestamp = header.Timestamp
|
|
|
|
maxTimestamp = minTimestamp
|
|
|
|
} else {
|
|
|
|
totalWork.Add(totalWork, btcchain.CalcWork(header.Bits))
|
|
|
|
|
|
|
|
if minTimestamp.After(header.Timestamp) {
|
|
|
|
minTimestamp = header.Timestamp
|
|
|
|
}
|
|
|
|
if maxTimestamp.Before(header.Timestamp) {
|
|
|
|
maxTimestamp = header.Timestamp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the difference in seconds between the min and max block
|
|
|
|
// timestamps and avoid division by zero in the case where there is no
|
|
|
|
// time difference.
|
|
|
|
timeDiff := int64(maxTimestamp.Sub(minTimestamp) / time.Second)
|
|
|
|
if timeDiff == 0 {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
hashesPerSec := new(big.Int).Div(totalWork, big.NewInt(timeDiff))
|
|
|
|
return hashesPerSec.Int64(), nil
|
|
|
|
}
|
|
|
|
|
2013-10-21 19:45:30 +02:00
|
|
|
// handleGetPeerInfo implements the getpeerinfo command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetPeerInfo(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-29 18:18:53 +01:00
|
|
|
return s.server.PeerInfo(), nil
|
2013-10-21 19:45:30 +02:00
|
|
|
}
|
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// handleGetRawMempool implements the getrawmempool command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetRawMempool(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-12-11 18:32:16 +01:00
|
|
|
c := cmd.(*btcjson.GetRawMempoolCmd)
|
|
|
|
descs := s.server.txMemPool.TxDescs()
|
|
|
|
|
|
|
|
if c.Verbose {
|
2014-02-16 21:00:40 +01:00
|
|
|
result := make(map[string]*btcjson.GetRawMempoolResult, len(descs))
|
2013-12-11 18:32:16 +01:00
|
|
|
for _, desc := range descs {
|
2014-02-16 21:00:40 +01:00
|
|
|
mpd := &btcjson.GetRawMempoolResult{
|
2014-06-29 23:36:41 +02:00
|
|
|
Size: int32(desc.Tx.MsgTx().SerializeSize()),
|
2013-12-25 19:28:54 +01:00
|
|
|
Fee: float64(desc.Fee) /
|
2014-07-07 16:05:17 +02:00
|
|
|
btcutil.SatoshiPerBitcoin,
|
2013-12-17 15:02:35 +01:00
|
|
|
Time: desc.Added.Unix(),
|
|
|
|
Height: desc.Height,
|
2013-12-11 18:32:16 +01:00
|
|
|
StartingPriority: 0, // We don't mine.
|
2013-12-17 15:02:35 +01:00
|
|
|
CurrentPriority: 0, // We don't mine.
|
2014-01-30 04:22:07 +01:00
|
|
|
Depends: make([]string, 0),
|
2013-12-11 18:32:16 +01:00
|
|
|
}
|
|
|
|
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
|
2013-12-11 18:32:16 +01:00
|
|
|
}
|
2013-12-25 19:28:54 +01:00
|
|
|
|
|
|
|
// The response is simply an array of the transaction hashes if the
|
|
|
|
// verbose flag is not set.
|
2013-12-11 18:32:16 +01:00
|
|
|
hashStrings := make([]string, len(descs))
|
2013-12-17 15:02:35 +01:00
|
|
|
for i := range hashStrings {
|
2013-12-11 18:32:16 +01:00
|
|
|
hashStrings[i] = descs[i].Tx.Sha().String()
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
2013-12-11 18:32:16 +01:00
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
return hashStrings, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleGetRawTransaction implements the getrawtransaction command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetRawTransaction(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-29 16:42:34 +01:00
|
|
|
c := cmd.(*btcjson.GetRawTransactionCmd)
|
2013-11-12 20:03:23 +01:00
|
|
|
|
2013-12-27 06:22:41 +01:00
|
|
|
// Convert the provided transaction hash hex to a ShaHash.
|
|
|
|
txSha, err := btcwire.NewShaHashFromStr(c.Txid)
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Error generating sha: %v", err)
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrBlockNotFound.Code,
|
|
|
|
Message: "Parameter 1 must be a hexaecimal string",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to fetch the transaction from the memory pool and if that fails,
|
|
|
|
// try the block database.
|
2013-12-08 20:57:14 +01:00
|
|
|
var mtx *btcwire.MsgTx
|
|
|
|
var blksha *btcwire.ShaHash
|
|
|
|
tx, err := s.server.txMemPool.FetchTransaction(txSha)
|
|
|
|
if err != nil {
|
|
|
|
txList, err := s.server.db.FetchTxBySha(txSha)
|
2014-07-03 17:30:46 +02:00
|
|
|
if err != nil {
|
2013-12-08 20:57:14 +01:00
|
|
|
rpcsLog.Errorf("Error fetching tx: %v", err)
|
|
|
|
return nil, btcjson.ErrNoTxInfo
|
2013-10-29 01:43:09 +01:00
|
|
|
}
|
2014-07-03 17:30:46 +02:00
|
|
|
if len(txList) == 0 {
|
|
|
|
return nil, btcjson.ErrNoTxInfo
|
|
|
|
}
|
2013-10-29 01:43:09 +01:00
|
|
|
|
2013-12-08 20:57:14 +01:00
|
|
|
lastTx := len(txList) - 1
|
|
|
|
mtx = txList[lastTx].Tx
|
|
|
|
|
|
|
|
blksha = txList[lastTx].BlkSha
|
|
|
|
} else {
|
|
|
|
mtx = tx.MsgTx()
|
|
|
|
}
|
2013-10-29 01:43:09 +01:00
|
|
|
|
2013-12-27 06:22:41 +01:00
|
|
|
// When the verbose flag isn't set, simply return the network-serialized
|
|
|
|
// transaction as a hex-encoded string.
|
2014-01-30 03:21:58 +01:00
|
|
|
if c.Verbose == 0 {
|
2013-12-27 06:22:41 +01:00
|
|
|
mtxHex, err := messageToHex(mtx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return mtxHex, nil
|
|
|
|
}
|
|
|
|
|
2013-12-08 20:57:14 +01:00
|
|
|
var blk *btcutil.Block
|
|
|
|
var maxidx int64
|
|
|
|
if blksha != nil {
|
|
|
|
blk, err = s.server.db.FetchBlockBySha(blksha)
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Error fetching sha: %v", err)
|
|
|
|
return nil, btcjson.ErrBlockNotFound
|
2013-08-06 23:55:22 +02:00
|
|
|
}
|
|
|
|
|
2013-12-08 20:57:14 +01:00
|
|
|
_, maxidx, err = s.server.db.NewestSha()
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Cannot get newest sha: %v", err)
|
|
|
|
return nil, btcjson.ErrNoNewestBlockInfo
|
2013-08-06 23:55:22 +02:00
|
|
|
}
|
2013-12-08 20:57:14 +01:00
|
|
|
}
|
|
|
|
|
2014-05-28 00:44:55 +02:00
|
|
|
rawTxn, jsonErr := createTxRawResult(s.server.netParams, c.Txid, mtx,
|
|
|
|
blk, maxidx, blksha)
|
2013-12-08 20:57:14 +01:00
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Cannot create TxRawResult for txSha=%s: %v", txSha, err)
|
2013-12-31 03:27:36 +01:00
|
|
|
return nil, jsonErr
|
2013-12-08 20:57:14 +01:00
|
|
|
}
|
|
|
|
return *rawTxn, nil
|
|
|
|
}
|
|
|
|
|
2014-03-20 08:06:10 +01:00
|
|
|
// 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]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-05 02:42:45 +02:00
|
|
|
// handleGetTxOut handles gettxout commands.
|
|
|
|
func handleGetTxOut(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
|
|
|
c := cmd.(*btcjson.GetTxOutCmd)
|
|
|
|
|
|
|
|
// Convert the provided transaction hash hex to a ShaHash.
|
|
|
|
txSha, err := btcwire.NewShaHashFromStr(c.Txid)
|
|
|
|
if err != nil {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInvalidParameter.Code,
|
|
|
|
Message: fmt.Sprintf("argument must be hexadecimal "+
|
|
|
|
"string (not %q)", 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 *btcwire.MsgTx
|
|
|
|
var bestBlockSha string
|
|
|
|
var confirmations int64
|
2014-07-24 00:08:25 +02:00
|
|
|
var dbSpentInfo []bool
|
2014-07-05 02:42:45 +02:00
|
|
|
if c.IncludeMempool && s.server.txMemPool.HaveTransaction(txSha) {
|
|
|
|
tx, err := s.server.txMemPool.FetchTransaction(txSha)
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Error fetching tx: %v", err)
|
|
|
|
return nil, btcjson.ErrNoTxInfo
|
|
|
|
}
|
|
|
|
mtx = tx.MsgTx()
|
|
|
|
confirmations = 0
|
|
|
|
bestBlockSha = ""
|
|
|
|
} else {
|
|
|
|
txList, err := s.server.db.FetchTxBySha(txSha)
|
|
|
|
if err != nil || len(txList) == 0 {
|
|
|
|
return nil, btcjson.ErrNoTxInfo
|
|
|
|
}
|
|
|
|
|
2014-07-24 00:08:25 +02:00
|
|
|
lastTx := txList[len(txList)-1]
|
|
|
|
mtx = lastTx.Tx
|
|
|
|
blksha := lastTx.BlkSha
|
|
|
|
txHeight := lastTx.Height
|
|
|
|
dbSpentInfo = lastTx.TxSpent
|
2014-07-05 02:42:45 +02:00
|
|
|
|
|
|
|
_, bestHeight, err := s.server.db.NewestSha()
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("Cannot get newest sha: %v", err)
|
|
|
|
return nil, btcjson.ErrBlockNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
confirmations = 1 + bestHeight - txHeight
|
|
|
|
bestBlockSha = blksha.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Output < 0 || c.Output > len(mtx.TxOut)-1 {
|
|
|
|
return nil, btcjson.ErrInvalidTxVout
|
|
|
|
}
|
|
|
|
|
2014-07-24 00:08:25 +02:00
|
|
|
txOut := mtx.TxOut[c.Output]
|
|
|
|
if txOut == nil {
|
2014-07-05 02:42:45 +02:00
|
|
|
rpcsLog.Errorf("Output index: %d, for txid: %s does not exist.", c.Output, c.Txid)
|
|
|
|
return nil, btcjson.ErrInternal
|
|
|
|
}
|
|
|
|
|
2014-07-24 00:08:25 +02:00
|
|
|
// 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.Output] {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2014-07-05 02:42:45 +02:00
|
|
|
// 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.
|
2014-07-24 00:08:25 +02:00
|
|
|
script := txOut.PkScript
|
2014-07-05 02:42:45 +02:00
|
|
|
disbuf, _ := btcscript.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.
|
|
|
|
net := s.server.netParams
|
|
|
|
scriptClass, addrs, reqSigs, _ := btcscript.ExtractPkScriptAddrs(script, net)
|
|
|
|
addresses := make([]string, len(addrs))
|
|
|
|
for i, addr := range addrs {
|
|
|
|
addresses[i] = addr.EncodeAddress()
|
|
|
|
}
|
|
|
|
|
|
|
|
txOutReply := &btcjson.GetTxOutResult{
|
|
|
|
BestBlock: bestBlockSha,
|
|
|
|
Confirmations: confirmations,
|
2014-07-24 00:08:25 +02:00
|
|
|
Value: btcutil.Amount(txOut.Value).ToUnit(btcutil.AmountBTC),
|
2014-07-05 02:42:45 +02:00
|
|
|
Version: mtx.Version,
|
|
|
|
ScriptPubKey: btcjson.ScriptPubKeyResult{
|
|
|
|
Asm: disbuf,
|
|
|
|
Hex: hex.EncodeToString(script),
|
|
|
|
ReqSigs: int32(reqSigs),
|
|
|
|
Type: scriptClass.String(),
|
|
|
|
Addresses: addresses,
|
|
|
|
},
|
|
|
|
Coinbase: btcchain.IsCoinBase(btcutil.NewTx(mtx)),
|
|
|
|
}
|
|
|
|
return txOutReply, nil
|
|
|
|
}
|
|
|
|
|
2014-03-20 08:06:10 +01:00
|
|
|
// handleGetWorkRequest is a helper for handleGetWork which deals with
|
|
|
|
// generating and returning work to the caller.
|
|
|
|
//
|
|
|
|
// This function MUST be called with the RPC workstate locked.
|
|
|
|
func handleGetWorkRequest(s *rpcServer) (interface{}, error) {
|
|
|
|
state := s.workState
|
|
|
|
|
|
|
|
// Generate a new block template when the current best block has
|
|
|
|
// changed or the transactions in the memory pool have been updated
|
|
|
|
// and it has been at least one minute since the last template was
|
|
|
|
// generated.
|
|
|
|
lastTxUpdate := s.server.txMemPool.LastUpdated()
|
|
|
|
latestHash, latestHeight := s.server.blockManager.chainState.Best()
|
|
|
|
msgBlock := state.msgBlock
|
|
|
|
if msgBlock == nil || state.prevHash == nil ||
|
|
|
|
!state.prevHash.IsEqual(latestHash) ||
|
|
|
|
(state.lastTxUpdate != lastTxUpdate &&
|
|
|
|
time.Now().After(state.lastGenerated.Add(time.Minute))) {
|
|
|
|
|
|
|
|
// Reset the extra nonce and clear all cached template
|
|
|
|
// variations if the best block changed.
|
|
|
|
if state.prevHash != nil && !state.prevHash.IsEqual(latestHash) {
|
|
|
|
state.extraNonce = 0
|
|
|
|
state.blockInfo = make(map[btcwire.ShaHash]*workStateBlockInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset the previous best hash the block template was generated
|
|
|
|
// against so any errors below cause the next invocation to try
|
|
|
|
// again.
|
|
|
|
state.prevHash = nil
|
|
|
|
|
|
|
|
// Choose a payment address at random.
|
2014-06-12 03:09:38 +02:00
|
|
|
payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))]
|
2014-03-20 08:06:10 +01:00
|
|
|
|
2014-06-27 07:41:09 +02:00
|
|
|
template, err := NewBlockTemplate(s.server.txMemPool, payToAddr)
|
2014-03-20 08:06:10 +01:00
|
|
|
if err != nil {
|
|
|
|
errStr := fmt.Sprintf("Failed to create new block "+
|
|
|
|
"template: %v", err)
|
2014-06-30 21:14:31 +02:00
|
|
|
rpcsLog.Error(errStr)
|
2014-03-20 08:06:10 +01:00
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: errStr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
msgBlock = template.block
|
|
|
|
|
|
|
|
// Update work state to ensure another block template isn't
|
|
|
|
// generated until needed.
|
|
|
|
state.msgBlock = msgBlock
|
|
|
|
state.lastGenerated = time.Now()
|
|
|
|
state.lastTxUpdate = lastTxUpdate
|
|
|
|
state.prevHash = latestHash
|
|
|
|
|
|
|
|
rpcsLog.Debugf("Generated block template (timestamp %v, extra "+
|
|
|
|
"nonce %d, target %064x, merkle root %s, signature "+
|
|
|
|
"script %x)", msgBlock.Header.Timestamp,
|
|
|
|
state.extraNonce,
|
|
|
|
btcchain.CompactToBig(msgBlock.Header.Bits),
|
|
|
|
msgBlock.Header.MerkleRoot,
|
|
|
|
msgBlock.Transactions[0].TxIn[0].SignatureScript)
|
|
|
|
} else {
|
|
|
|
// At this point, there is a saved block template and a new
|
|
|
|
// request for work was made, but either the available
|
|
|
|
// transactions haven't change or it hasn't been long enough to
|
|
|
|
// trigger a new block template to be generated. So, update the
|
|
|
|
// existing block template and track the variations so each
|
|
|
|
// variation can be regenerated if a caller finds an answer and
|
|
|
|
// makes a submission against it.
|
|
|
|
|
|
|
|
// Update the time of the block template to the current time
|
|
|
|
// while accounting for the median time of the past several
|
|
|
|
// blocks per the chain consensus rules.
|
|
|
|
UpdateBlockTime(msgBlock, s.server.blockManager)
|
|
|
|
|
|
|
|
// Increment the extra nonce and update the block template
|
|
|
|
// with the new value by regenerating the coinbase script and
|
|
|
|
// setting the merkle root to the new value.
|
|
|
|
state.extraNonce++
|
|
|
|
err := UpdateExtraNonce(msgBlock, latestHeight+1, state.extraNonce)
|
|
|
|
if err != nil {
|
|
|
|
errStr := fmt.Sprintf("Failed to update extra nonce: "+
|
|
|
|
"%v", err)
|
|
|
|
rpcsLog.Warnf(errStr)
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: errStr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Debugf("Updated block template (timestamp %v, extra "+
|
|
|
|
"nonce %d, target %064x, merkle root %s, signature "+
|
|
|
|
"script %x)", msgBlock.Header.Timestamp,
|
|
|
|
state.extraNonce,
|
|
|
|
btcchain.CompactToBig(msgBlock.Header.Bits),
|
|
|
|
msgBlock.Header.MerkleRoot,
|
|
|
|
msgBlock.Transactions[0].TxIn[0].SignatureScript)
|
|
|
|
}
|
|
|
|
|
|
|
|
// In order to efficiently store the variations of block templates that
|
|
|
|
// have been provided to callers, save a pointer to the block as well as
|
|
|
|
// the modified signature script keyed by the merkle root. This
|
|
|
|
// information, along with the data that is included in a work
|
|
|
|
// submission, is used to rebuild the block before checking the
|
|
|
|
// submitted solution.
|
|
|
|
coinbaseTx := msgBlock.Transactions[0]
|
|
|
|
state.blockInfo[msgBlock.Header.MerkleRoot] = &workStateBlockInfo{
|
|
|
|
msgBlock: msgBlock,
|
|
|
|
signatureScript: coinbaseTx.TxIn[0].SignatureScript,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serialize the block header into a buffer large enough to hold the
|
|
|
|
// the block header and the internal sha256 padding that is added and
|
|
|
|
// retuned as part of the data below.
|
|
|
|
data := make([]byte, 0, getworkDataLen)
|
|
|
|
buf := bytes.NewBuffer(data)
|
|
|
|
err := msgBlock.Header.Serialize(buf)
|
2013-12-08 20:57:14 +01:00
|
|
|
if err != nil {
|
2014-03-20 08:06:10 +01:00
|
|
|
errStr := fmt.Sprintf("Failed to serialize data: %v", err)
|
|
|
|
rpcsLog.Warnf(errStr)
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: errStr,
|
|
|
|
}
|
2013-12-08 20:57:14 +01:00
|
|
|
}
|
|
|
|
|
2014-03-20 08:06:10 +01:00
|
|
|
// Calculate the midstate for the block header. The midstate here is
|
|
|
|
// the internal state of the sha256 algorithm for the first chunk of the
|
|
|
|
// block header (sha256 operates on 64-byte chunks) which is before the
|
|
|
|
// nonce. This allows sophisticated callers to avoid hashing the first
|
|
|
|
// chunk over and over while iterating the nonce range.
|
|
|
|
data = data[:buf.Len()]
|
|
|
|
midstate := fastsha256.MidState256(data)
|
|
|
|
|
|
|
|
// Expand the data slice to include the full data buffer and apply the
|
|
|
|
// internal sha256 padding which consists of a single 1 bit followed
|
|
|
|
// by enough zeros to pad the message out to 56 bytes followed by the
|
|
|
|
// length of the message in bits encoded as a big-endian uint64
|
|
|
|
// (8 bytes). Thus, the resulting length is a multiple of the sha256
|
|
|
|
// block size (64 bytes). This makes the data ready for sophisticated
|
|
|
|
// caller to make use of only the second chunk along with the midstate
|
|
|
|
// for the first chunk.
|
|
|
|
data = data[:getworkDataLen]
|
|
|
|
data[btcwire.MaxBlockHeaderPayload] = 0x80
|
|
|
|
binary.BigEndian.PutUint64(data[len(data)-8:],
|
|
|
|
btcwire.MaxBlockHeaderPayload*8)
|
|
|
|
|
|
|
|
// Create the hash1 field which is a zero hash along with the internal
|
|
|
|
// sha256 padding as described above. This field is really quite
|
|
|
|
// useless, but it is required for compatibility with the reference
|
|
|
|
// implementation.
|
|
|
|
var hash1 [hash1Len]byte
|
|
|
|
hash1[btcwire.HashSize] = 0x80
|
|
|
|
binary.BigEndian.PutUint64(hash1[len(hash1)-8:], btcwire.HashSize*8)
|
|
|
|
|
|
|
|
// The final result reverses the each of the fields to little endian.
|
|
|
|
// In particular, the data, hash1, and midstate fields are treated as
|
|
|
|
// arrays of uint32s (per the internal sha256 hashing state) which are
|
|
|
|
// in big endian, and thus each 4 bytes is byte swapped. The target is
|
|
|
|
// also in big endian, but it is treated as a uint256 and byte swapped
|
|
|
|
// to little endian accordingly.
|
|
|
|
//
|
|
|
|
// The fact the fields are reversed in this way is rather odd and likey
|
|
|
|
// an artifact of some legacy internal state in the reference
|
|
|
|
// implementation, but it is required for compatibility.
|
|
|
|
reverseUint32Array(data)
|
|
|
|
reverseUint32Array(hash1[:])
|
|
|
|
reverseUint32Array(midstate[:])
|
|
|
|
target := bigToLEUint256(btcchain.CompactToBig(msgBlock.Header.Bits))
|
|
|
|
reply := &btcjson.GetWorkResult{
|
|
|
|
Data: hex.EncodeToString(data),
|
|
|
|
Hash1: hex.EncodeToString(hash1[:]),
|
|
|
|
Midstate: hex.EncodeToString(midstate[:]),
|
|
|
|
Target: hex.EncodeToString(target[:]),
|
|
|
|
}
|
|
|
|
return reply, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleGetWorkSubmission is a helper for handleGetWork which deals with
|
|
|
|
// the calling submitting work to be verified and processed.
|
|
|
|
//
|
|
|
|
// This function MUST be called with the RPC workstate locked.
|
|
|
|
func handleGetWorkSubmission(s *rpcServer, hexData string) (interface{}, error) {
|
|
|
|
// Ensure the provided data is sane.
|
|
|
|
if len(hexData)%2 != 0 {
|
|
|
|
hexData = "0" + hexData
|
|
|
|
}
|
|
|
|
data, err := hex.DecodeString(hexData)
|
2013-12-31 02:06:13 +01:00
|
|
|
if err != nil {
|
2014-03-20 08:06:10 +01:00
|
|
|
return false, btcjson.Error{
|
2014-07-02 03:09:22 +02:00
|
|
|
Code: btcjson.ErrDecodeHexString.Code,
|
2014-03-20 08:06:10 +01:00
|
|
|
Message: fmt.Sprintf("argument must be "+
|
|
|
|
"hexadecimal string (not %q)", hexData),
|
|
|
|
}
|
2013-12-31 02:06:13 +01:00
|
|
|
}
|
2014-03-20 08:06:10 +01:00
|
|
|
if len(data) != getworkDataLen {
|
|
|
|
return false, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInvalidParameter.Code,
|
|
|
|
Message: fmt.Sprintf("argument must be "+
|
|
|
|
"%d bytes (not %d)", getworkDataLen,
|
|
|
|
len(data)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reverse the data as if it were an array of 32-bit unsigned integers.
|
|
|
|
// The fact the getwork request and submission data is reversed in this
|
|
|
|
// way is rather odd and likey an artifact of some legacy internal state
|
|
|
|
// in the reference implementation, but it is required for
|
|
|
|
// compatibility.
|
|
|
|
reverseUint32Array(data)
|
|
|
|
|
|
|
|
// Deserialize the block header from the data.
|
|
|
|
var submittedHeader btcwire.BlockHeader
|
2014-06-05 19:58:28 +02:00
|
|
|
bhBuf := bytes.NewReader(data[0:btcwire.MaxBlockHeaderPayload])
|
2014-03-20 08:06:10 +01:00
|
|
|
err = submittedHeader.Deserialize(bhBuf)
|
2013-12-31 02:06:13 +01:00
|
|
|
if err != nil {
|
2014-03-20 08:06:10 +01:00
|
|
|
return false, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInvalidParameter.Code,
|
|
|
|
Message: fmt.Sprintf("argument does not "+
|
|
|
|
"contain a valid block header: %v", err),
|
|
|
|
}
|
2013-12-31 02:06:13 +01:00
|
|
|
}
|
|
|
|
|
2014-03-20 08:06:10 +01:00
|
|
|
// 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
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
2013-12-08 20:57:14 +01:00
|
|
|
|
2014-03-20 08:06:10 +01:00
|
|
|
// Reconstruct the block using the submitted header stored block info.
|
|
|
|
msgBlock := blockInfo.msgBlock
|
|
|
|
block := btcutil.NewBlock(msgBlock)
|
|
|
|
msgBlock.Header.Timestamp = submittedHeader.Timestamp
|
|
|
|
msgBlock.Header.Nonce = submittedHeader.Nonce
|
|
|
|
msgBlock.Transactions[0].TxIn[0].SignatureScript = blockInfo.signatureScript
|
|
|
|
merkles := btcchain.BuildMerkleTreeStore(block.Transactions())
|
|
|
|
msgBlock.Header.MerkleRoot = *merkles[len(merkles)-1]
|
2013-12-08 20:57:14 +01:00
|
|
|
|
2014-03-20 08:06:10 +01:00
|
|
|
// Ensure the submitted block hash is less than the target difficulty.
|
2014-05-23 06:14:36 +02:00
|
|
|
err = btcchain.CheckProofOfWork(block, activeNetParams.PowLimit)
|
2014-03-20 08:06:10 +01:00
|
|
|
if err != nil {
|
|
|
|
// Anything other than a rule violation is an unexpected error,
|
|
|
|
// so return that error as an internal error.
|
|
|
|
if _, ok := err.(btcchain.RuleError); !ok {
|
|
|
|
return false, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: fmt.Sprintf("Unexpected error while "+
|
|
|
|
"checking proof of work: %v", err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Debugf("Block submitted via getwork does not meet "+
|
|
|
|
"the required proof of work: %v", err)
|
|
|
|
return false, nil
|
2013-12-08 20:57:14 +01:00
|
|
|
}
|
|
|
|
|
2014-03-20 08:06:10 +01:00
|
|
|
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.
|
2014-06-27 00:02:45 +02:00
|
|
|
isOrphan, err := s.server.blockManager.ProcessBlock(block, btcchain.BFNone)
|
2014-03-20 08:06:10 +01:00
|
|
|
if err != nil || isOrphan {
|
|
|
|
// Anything other than a rule violation is an unexpected error,
|
|
|
|
// so return that error as an internal error.
|
|
|
|
if _, ok := err.(btcchain.RuleError); !ok {
|
|
|
|
return false, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
2014-05-23 06:48:02 +02:00
|
|
|
Message: fmt.Sprintf("Unexpected error while "+
|
2014-03-20 08:06:10 +01:00
|
|
|
"processing block: %v", err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Infof("Block submitted via getwork rejected: %v", err)
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// The block was accepted.
|
|
|
|
blockSha, _ := block.Sha()
|
|
|
|
rpcsLog.Infof("Block submitted via getwork accepted: %s", blockSha)
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleGetWork implements the getwork command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleGetWork(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-03-20 08:06:10 +01:00
|
|
|
c := cmd.(*btcjson.GetWorkCmd)
|
|
|
|
|
2014-06-12 03:09:38 +02:00
|
|
|
// Respond with an error if there are no addresses to pay the created
|
2014-03-20 08:06:10 +01:00
|
|
|
// blocks to.
|
2014-06-12 03:09:38 +02:00
|
|
|
if len(cfg.miningAddrs) == 0 {
|
2014-03-20 08:06:10 +01:00
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
2014-06-12 03:09:38 +02:00
|
|
|
Message: "No payment addresses specified via --miningaddr",
|
2014-03-20 08:06:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2014-05-28 22:19:38 +02:00
|
|
|
// However, allow this state when running in the regression test or
|
|
|
|
// simulation test mode.
|
|
|
|
if !(cfg.RegressionTest || cfg.SimNet) && s.server.ConnectedCount() == 0 {
|
2014-03-20 08:06:10 +01:00
|
|
|
return nil, btcjson.ErrClientNotConnected
|
|
|
|
}
|
|
|
|
|
|
|
|
// No point in generating or accepting work before the chain is synced.
|
2014-05-09 02:46:56 +02:00
|
|
|
_, currentHeight := s.server.blockManager.chainState.Best()
|
|
|
|
if currentHeight != 0 && !s.server.blockManager.IsCurrent() {
|
2014-03-20 08:06:10 +01:00
|
|
|
return nil, btcjson.ErrClientInInitialDownload
|
|
|
|
}
|
|
|
|
|
|
|
|
// Protect concurrent access from multiple RPC invocations for work
|
|
|
|
// requests and submission.
|
|
|
|
s.workState.Lock()
|
|
|
|
defer s.workState.Unlock()
|
|
|
|
|
|
|
|
// When the caller provides data, it is a submission of a supposedly
|
|
|
|
// solved block that needs to be checked and submitted to the network
|
|
|
|
// if valid.
|
|
|
|
if c.Data != "" {
|
|
|
|
return handleGetWorkSubmission(s, c.Data)
|
|
|
|
}
|
|
|
|
|
|
|
|
// No data was provided, so the caller is requesting work.
|
|
|
|
return handleGetWorkRequest(s)
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
|
|
|
|
2014-01-16 15:45:17 +01:00
|
|
|
var helpAddenda = map[string]string{
|
|
|
|
"sendrawtransaction": `
|
|
|
|
NOTE: btcd does not currently support the "allowhighfees" parameter.`,
|
|
|
|
}
|
|
|
|
|
|
|
|
// getHelp text retreives help text from btcjson for the command in question.
|
|
|
|
// If there is any extra btcd specific information related to the given command
|
|
|
|
// then this is appended to the string.
|
|
|
|
func getHelpText(cmdName string) (string, error) {
|
|
|
|
help, err := btcjson.GetHelpString(cmdName)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if helpAddendum, ok := helpAddenda[cmdName]; ok {
|
|
|
|
help += helpAddendum
|
|
|
|
}
|
|
|
|
|
|
|
|
return help, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleHelp implements the help command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleHelp(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-01-16 15:45:17 +01:00
|
|
|
help := cmd.(*btcjson.HelpCmd)
|
|
|
|
|
|
|
|
// if no args we give a list of all known commands
|
|
|
|
if help.Command == "" {
|
|
|
|
commands := ""
|
|
|
|
first := true
|
|
|
|
// TODO(oga) this should have one liner usage for each command
|
|
|
|
// really, but for now just a list of commands is sufficient.
|
|
|
|
for k := range rpcHandlers {
|
|
|
|
if !first {
|
|
|
|
commands += "\n"
|
|
|
|
}
|
|
|
|
commands += k
|
|
|
|
first = false
|
|
|
|
}
|
|
|
|
return commands, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that we actually support the command asked for. We only
|
|
|
|
// search the main list of hanlders since we do not wish to provide help
|
|
|
|
// for commands that are unimplemented or relate to wallet
|
|
|
|
// functionality.
|
2014-01-21 16:03:15 +01:00
|
|
|
if _, ok := rpcHandlers[help.Command]; !ok {
|
2014-01-16 15:45:17 +01:00
|
|
|
return "", fmt.Errorf("help: unknown command: %s", help.Command)
|
|
|
|
}
|
|
|
|
|
|
|
|
return getHelpText(help.Command)
|
|
|
|
}
|
|
|
|
|
2014-01-21 16:03:15 +01:00
|
|
|
// handlePing implements the ping command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handlePing(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-01-21 16:03:15 +01:00
|
|
|
// Ask server to ping \o_
|
|
|
|
nonce, err := btcwire.RandomUint64()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Not sending ping - can not generate "+
|
|
|
|
"nonce: %v", err)
|
|
|
|
}
|
|
|
|
s.server.BroadcastMessage(btcwire.NewMsgPing(nonce))
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
// handleSendRawTransaction implements the sendrawtransaction command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-29 16:42:34 +01:00
|
|
|
c := cmd.(*btcjson.SendRawTransactionCmd)
|
|
|
|
// Deserialize and send off to tx relay
|
2014-07-02 03:09:22 +02:00
|
|
|
hexStr := c.HexTx
|
|
|
|
if len(hexStr)%2 != 0 {
|
|
|
|
hexStr = "0" + hexStr
|
|
|
|
}
|
|
|
|
serializedTx, err := hex.DecodeString(hexStr)
|
2013-10-29 16:42:34 +01:00
|
|
|
if err != nil {
|
2014-07-02 03:09:22 +02:00
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrDecodeHexString.Code,
|
|
|
|
Message: fmt.Sprintf("argument must be hexadecimal "+
|
|
|
|
"string (not %q)", hexStr),
|
|
|
|
}
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
|
|
|
msgtx := btcwire.NewMsgTx()
|
2014-06-05 19:58:28 +02:00
|
|
|
err = msgtx.Deserialize(bytes.NewReader(serializedTx))
|
2013-10-29 16:42:34 +01:00
|
|
|
if err != nil {
|
|
|
|
err := btcjson.Error{
|
2013-10-30 01:41:38 +01:00
|
|
|
Code: btcjson.ErrDeserialization.Code,
|
2013-11-07 16:34:55 +01:00
|
|
|
Message: "TX decode failed",
|
2013-08-14 22:55:31 +02:00
|
|
|
}
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
2013-10-31 06:28:37 +01:00
|
|
|
|
2013-10-29 16:42:34 +01:00
|
|
|
tx := btcutil.NewTx(msgtx)
|
2014-04-22 21:52:36 +02:00
|
|
|
err = s.server.txMemPool.ProcessTransaction(tx, false, false)
|
2013-10-29 16:42:34 +01:00
|
|
|
if err != nil {
|
2013-10-30 20:11:11 +01:00
|
|
|
// 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,
|
2014-02-21 15:03:04 +01:00
|
|
|
// 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).
|
2014-07-09 05:01:13 +02:00
|
|
|
if _, ok := err.(RuleError); ok {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Debugf("Rejected transaction %v: %v", tx.Sha(),
|
2013-10-31 00:40:55 +01:00
|
|
|
err)
|
2013-10-30 20:11:11 +01:00
|
|
|
} else {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Failed to process transaction %v: %v",
|
2013-10-31 00:40:55 +01:00
|
|
|
tx.Sha(), err)
|
2013-10-30 20:11:11 +01:00
|
|
|
}
|
2014-02-21 15:03:04 +01:00
|
|
|
err = btcjson.Error{
|
|
|
|
Code: btcjson.ErrDeserialization.Code,
|
2014-03-17 23:33:56 +01:00
|
|
|
Message: fmt.Sprintf("TX rejected: %v", err),
|
2014-02-21 15:03:04 +01:00
|
|
|
}
|
|
|
|
return nil, err
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
2013-08-14 22:55:31 +02:00
|
|
|
|
2014-03-28 20:49:38 +01:00
|
|
|
// We keep track of all the sendrawtransaction request txs so that we
|
2014-03-28 01:02:57 +01:00
|
|
|
// can rebroadcast them if they don't make their way into a block.
|
2014-03-18 20:40:49 +01:00
|
|
|
iv := btcwire.NewInvVect(btcwire.InvTypeTx, tx.Sha())
|
|
|
|
s.server.AddRebroadcastInventory(iv)
|
|
|
|
|
2013-10-31 06:28:37 +01:00
|
|
|
return tx.Sha().String(), nil
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// handleSetGenerate implements the setgenerate command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleSetGenerate(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-06-12 04:51:21 +02:00
|
|
|
c := cmd.(*btcjson.SetGenerateCmd)
|
|
|
|
|
|
|
|
// Disable generation regardless of the provided generate flag if the
|
|
|
|
// maximum number of threads (goroutines for our purposes) is 0.
|
|
|
|
// Otherwise enable or disable it depending on the provided flag.
|
|
|
|
generate := c.Generate
|
|
|
|
if c.GenProcLimit == 0 {
|
|
|
|
generate = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if !generate {
|
|
|
|
s.server.cpuMiner.Stop()
|
|
|
|
} else {
|
|
|
|
// Respond with an error if there are no addresses to pay the
|
|
|
|
// created blocks to.
|
|
|
|
if len(cfg.miningAddrs) == 0 {
|
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrInternal.Code,
|
|
|
|
Message: "No payment addresses specified " +
|
|
|
|
"via --miningaddr",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// It's safe to call start even if it's already started.
|
|
|
|
s.server.cpuMiner.SetNumWorkers(int32(c.GenProcLimit))
|
|
|
|
s.server.cpuMiner.Start()
|
|
|
|
}
|
2013-10-29 16:42:34 +01:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleStop implements the stop command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleStop(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-10-29 16:42:34 +01:00
|
|
|
s.server.Stop()
|
|
|
|
return "btcd stopping.", nil
|
|
|
|
}
|
|
|
|
|
2014-01-15 16:01:12 +01:00
|
|
|
// handleSubmitBlock implements the submitblock command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleSubmitBlock(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2014-01-15 16:01:12 +01:00
|
|
|
c := cmd.(*btcjson.SubmitBlockCmd)
|
2014-07-02 03:09:22 +02:00
|
|
|
|
|
|
|
// Deserialize the submitted block.
|
|
|
|
hexStr := c.HexBlock
|
|
|
|
if len(hexStr)%2 != 0 {
|
|
|
|
hexStr = "0" + c.HexBlock
|
|
|
|
}
|
|
|
|
serializedBlock, err := hex.DecodeString(hexStr)
|
2014-01-15 16:01:12 +01:00
|
|
|
if err != nil {
|
2014-07-02 03:09:22 +02:00
|
|
|
return nil, btcjson.Error{
|
|
|
|
Code: btcjson.ErrDecodeHexString.Code,
|
|
|
|
Message: fmt.Sprintf("argument must be hexadecimal "+
|
|
|
|
"string (not %q)", hexStr),
|
2014-01-15 16:01:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
block, err := btcutil.NewBlockFromBytes(serializedBlock)
|
|
|
|
if err != nil {
|
2014-07-02 03:09:22 +02:00
|
|
|
return nil, btcjson.Error{
|
2014-01-15 16:01:12 +01:00
|
|
|
Code: btcjson.ErrDeserialization.Code,
|
|
|
|
Message: "Block decode failed",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-27 00:02:45 +02:00
|
|
|
_, err = s.server.blockManager.ProcessBlock(block, btcchain.BFNone)
|
2014-01-15 16:01:12 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Sprintf("rejected: %s", err.Error()), nil
|
|
|
|
}
|
|
|
|
|
2014-07-14 17:08:07 +02:00
|
|
|
blockSha, err := block.Sha()
|
|
|
|
if err == nil {
|
|
|
|
rpcsLog.Infof("Accepted block %s via submitblock", blockSha)
|
|
|
|
}
|
2014-01-15 16:01:12 +01:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2013-11-12 17:39:10 +01:00
|
|
|
func verifyChain(db btcdb.Db, level, depth int32) error {
|
2014-04-14 17:29:51 +02:00
|
|
|
_, curHeight64, err := db.NewestSha()
|
2013-11-12 17:39:10 +01:00
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Verify is unable to fetch current block "+
|
2013-11-12 17:39:10 +01:00
|
|
|
"height: %v", err)
|
|
|
|
}
|
2014-04-14 17:29:51 +02:00
|
|
|
curHeight := int32(curHeight64)
|
2013-11-12 17:39:10 +01:00
|
|
|
|
2014-04-14 17:29:51 +02:00
|
|
|
finishHeight := curHeight - depth
|
|
|
|
if finishHeight < 0 {
|
|
|
|
finishHeight = 0
|
2013-11-12 17:39:10 +01:00
|
|
|
}
|
2014-04-14 17:29:51 +02:00
|
|
|
rpcsLog.Infof("Verifying chain for %d blocks at level %d",
|
|
|
|
curHeight-finishHeight, level)
|
2013-11-12 17:39:10 +01:00
|
|
|
|
2014-04-14 17:29:51 +02:00
|
|
|
for height := curHeight; height > finishHeight; height-- {
|
2013-11-12 17:39:10 +01:00
|
|
|
// Level 0 just looks up the block.
|
|
|
|
sha, err := db.FetchBlockShaByHeight(int64(height))
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Verify is unable to fetch block at "+
|
2013-11-12 17:39:10 +01:00
|
|
|
"height %d: %v", height, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
block, err := db.FetchBlockBySha(sha)
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Verify is unable to fetch block at "+
|
2013-11-12 17:39:10 +01:00
|
|
|
"sha %v height %d: %v", sha, height, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Level 1 does basic chain sanity checks.
|
|
|
|
if level > 0 {
|
|
|
|
err := btcchain.CheckBlockSanity(block,
|
2014-05-23 06:14:36 +02:00
|
|
|
activeNetParams.PowLimit)
|
2013-11-12 17:39:10 +01:00
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Verify is unable to "+
|
2013-11-12 17:39:10 +01:00
|
|
|
"validate block at sha %v height "+
|
2014-02-04 19:55:23 +01:00
|
|
|
"%d: %v", sha, height, err)
|
2013-11-12 17:39:10 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Infof("Chain verify completed successfully")
|
2013-11-12 17:39:10 +01:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-03 05:52:34 +02:00
|
|
|
// handleValidateAddress implements the validateaddress command.
|
|
|
|
func handleValidateAddress(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
|
|
|
c := cmd.(*btcjson.ValidateAddressCmd)
|
|
|
|
|
|
|
|
result := btcjson.ValidateAddressResult{}
|
|
|
|
addr, err := btcutil.DecodeAddress(c.Address, activeNetParams.Params)
|
|
|
|
if err != nil {
|
|
|
|
// Return the default value (false) for IsValid.
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
result.Address = addr.EncodeAddress()
|
|
|
|
result.IsValid = true
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleVerifyChain implements the verifychain command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func handleVerifyChain(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
2013-11-12 17:39:10 +01:00
|
|
|
c := cmd.(*btcjson.VerifyChainCmd)
|
|
|
|
|
|
|
|
err := verifyChain(s.server.db, c.CheckLevel, c.CheckDepth)
|
2013-12-30 17:59:14 +01:00
|
|
|
return err == nil, nil
|
2013-11-12 17:39:10 +01:00
|
|
|
}
|
|
|
|
|
2013-11-07 18:47:54 +01:00
|
|
|
// parseCmd parses a marshaled known command, returning any errors as a
|
|
|
|
// btcjson.Error that can be used in replies. The returned cmd may still
|
|
|
|
// be non-nil if b is at least a valid marshaled JSON-RPC message.
|
|
|
|
func parseCmd(b []byte) (btcjson.Cmd, *btcjson.Error) {
|
|
|
|
cmd, err := btcjson.ParseMarshaledCmd(b)
|
2013-10-29 16:42:34 +01:00
|
|
|
if err != nil {
|
2013-11-07 18:47:54 +01:00
|
|
|
jsonErr, ok := err.(btcjson.Error)
|
|
|
|
if !ok {
|
|
|
|
jsonErr = btcjson.Error{
|
|
|
|
Code: btcjson.ErrParse.Code,
|
|
|
|
Message: err.Error(),
|
2013-10-30 04:23:22 +01:00
|
|
|
}
|
2013-10-29 01:43:09 +01:00
|
|
|
}
|
2013-11-07 18:47:54 +01:00
|
|
|
return cmd, &jsonErr
|
2013-10-29 16:42:34 +01:00
|
|
|
}
|
2013-11-07 18:47:54 +01:00
|
|
|
return cmd, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// standardCmdReply checks that a parsed command is a standard
|
|
|
|
// Bitcoin JSON-RPC command and runs the proper handler to reply to the
|
|
|
|
// command.
|
2014-06-27 18:13:04 +02:00
|
|
|
func standardCmdReply(cmd btcjson.Cmd, s *rpcServer, closeChan <-chan struct{}) (reply btcjson.Reply) {
|
2013-10-29 16:42:34 +01:00
|
|
|
id := cmd.Id()
|
2013-11-07 18:47:54 +01:00
|
|
|
reply.Id = &id
|
2013-10-29 01:43:09 +01:00
|
|
|
|
2013-12-31 22:53:19 +01:00
|
|
|
handler, ok := rpcHandlers[cmd.Method()]
|
2014-01-16 15:45:17 +01:00
|
|
|
if ok {
|
|
|
|
goto handled
|
|
|
|
}
|
|
|
|
_, ok = rpcAskWallet[cmd.Method()]
|
|
|
|
if ok {
|
|
|
|
handler = handleAskWallet
|
|
|
|
goto handled
|
|
|
|
}
|
|
|
|
_, ok = rpcUnimplemented[cmd.Method()]
|
|
|
|
if ok {
|
|
|
|
handler = handleUnimplemented
|
|
|
|
goto handled
|
|
|
|
}
|
|
|
|
reply.Error = &btcjson.ErrMethodNotFound
|
|
|
|
return reply
|
|
|
|
handled:
|
2013-10-29 16:42:34 +01:00
|
|
|
|
2014-06-27 18:13:04 +02:00
|
|
|
result, err := handler(s, cmd, closeChan)
|
2013-10-29 16:42:34 +01:00
|
|
|
if err != nil {
|
2013-11-07 18:47:54 +01:00
|
|
|
jsonErr, ok := err.(btcjson.Error)
|
|
|
|
if !ok {
|
2013-10-29 16:42:34 +01:00
|
|
|
// In the case where we did not have a btcjson
|
|
|
|
// error to begin with, make a new one to send,
|
|
|
|
// but this really should not happen.
|
2013-11-07 18:47:54 +01:00
|
|
|
jsonErr = btcjson.Error{
|
2013-10-30 01:41:38 +01:00
|
|
|
Code: btcjson.ErrInternal.Code,
|
2013-10-29 16:42:34 +01:00
|
|
|
Message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
2013-11-07 18:47:54 +01:00
|
|
|
reply.Error = &jsonErr
|
2013-10-29 16:42:34 +01:00
|
|
|
} else {
|
2013-11-07 18:47:54 +01:00
|
|
|
reply.Result = result
|
|
|
|
}
|
|
|
|
return reply
|
|
|
|
}
|
|
|
|
|
2013-08-06 23:55:22 +02:00
|
|
|
// 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.
|
2014-05-23 06:14:36 +02:00
|
|
|
max := btcchain.CompactToBig(activeNetParams.PowLimitBits)
|
2013-08-06 23:55:22 +02:00
|
|
|
target := btcchain.CompactToBig(bits)
|
|
|
|
|
|
|
|
difficulty := new(big.Rat).SetFrac(max, target)
|
|
|
|
outString := difficulty.FloatString(2)
|
|
|
|
diff, err := strconv.ParseFloat(outString, 64)
|
|
|
|
if err != nil {
|
2013-11-21 19:03:56 +01:00
|
|
|
rpcsLog.Errorf("Cannot get difficulty: %v", err)
|
2013-08-06 23:55:22 +02:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return diff
|
|
|
|
}
|
2014-06-30 20:16:05 +02:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
rpcHandlers = rpcHandlersBeforeInit
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
}
|