Implement signmessagewithprivkey JSON-RPC command
Reuse the Bitcoin message signature header const also in verifymessage.
This commit is contained in:
parent
b68c50e33c
commit
d2c0123bef
4 changed files with 180 additions and 93 deletions
|
@ -806,6 +806,24 @@ func NewSetGenerateCmd(generate bool, genProcLimit *int) *SetGenerateCmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignMessageWithPrivKeyCmd defines the signmessagewithprivkey JSON-RPC command.
|
||||||
|
type SignMessageWithPrivKeyCmd struct {
|
||||||
|
PrivKey string // base 58 Wallet Import format private key
|
||||||
|
Message string // Message to sign
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSignMessageWithPrivKey returns a new instance which can be used to issue a
|
||||||
|
// signmessagewithprivkey JSON-RPC command.
|
||||||
|
//
|
||||||
|
// The first parameter is a private key in base 58 Wallet Import format.
|
||||||
|
// The second parameter is the message to sign.
|
||||||
|
func NewSignMessageWithPrivKey(privKey, message string) *SignMessageWithPrivKeyCmd {
|
||||||
|
return &SignMessageWithPrivKeyCmd{
|
||||||
|
PrivKey: privKey,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// StopCmd defines the stop JSON-RPC command.
|
// StopCmd defines the stop JSON-RPC command.
|
||||||
type StopCmd struct{}
|
type StopCmd struct{}
|
||||||
|
|
||||||
|
@ -971,6 +989,7 @@ func init() {
|
||||||
MustRegisterCmd("searchrawtransactions", (*SearchRawTransactionsCmd)(nil), flags)
|
MustRegisterCmd("searchrawtransactions", (*SearchRawTransactionsCmd)(nil), flags)
|
||||||
MustRegisterCmd("sendrawtransaction", (*SendRawTransactionCmd)(nil), flags)
|
MustRegisterCmd("sendrawtransaction", (*SendRawTransactionCmd)(nil), flags)
|
||||||
MustRegisterCmd("setgenerate", (*SetGenerateCmd)(nil), flags)
|
MustRegisterCmd("setgenerate", (*SetGenerateCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("signmessagewithprivkey", (*SignMessageWithPrivKeyCmd)(nil), flags)
|
||||||
MustRegisterCmd("stop", (*StopCmd)(nil), flags)
|
MustRegisterCmd("stop", (*StopCmd)(nil), flags)
|
||||||
MustRegisterCmd("submitblock", (*SubmitBlockCmd)(nil), flags)
|
MustRegisterCmd("submitblock", (*SubmitBlockCmd)(nil), flags)
|
||||||
MustRegisterCmd("uptime", (*UptimeCmd)(nil), flags)
|
MustRegisterCmd("uptime", (*UptimeCmd)(nil), flags)
|
||||||
|
|
|
@ -1186,6 +1186,20 @@ func TestChainSvrCmds(t *testing.T) {
|
||||||
GenProcLimit: btcjson.Int(6),
|
GenProcLimit: btcjson.Int(6),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "signmessagewithprivkey",
|
||||||
|
newCmd: func() (interface{}, error) {
|
||||||
|
return btcjson.NewCmd("signmessagewithprivkey", "5Hue", "Hey")
|
||||||
|
},
|
||||||
|
staticCmd: func() interface{} {
|
||||||
|
return btcjson.NewSignMessageWithPrivKey("5Hue", "Hey")
|
||||||
|
},
|
||||||
|
marshalled: `{"jsonrpc":"1.0","method":"signmessagewithprivkey","params":["5Hue","Hey"],"id":1}`,
|
||||||
|
unmarshalled: &btcjson.SignMessageWithPrivKeyCmd{
|
||||||
|
PrivKey: "5Hue",
|
||||||
|
Message: "Hey",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "stop",
|
name: "stop",
|
||||||
newCmd: func() (interface{}, error) {
|
newCmd: func() (interface{}, error) {
|
||||||
|
|
141
rpcserver.go
141
rpcserver.go
|
@ -127,52 +127,53 @@ type commandHandler func(*rpcServer, interface{}, <-chan struct{}) (interface{},
|
||||||
// a dependency loop.
|
// a dependency loop.
|
||||||
var rpcHandlers map[string]commandHandler
|
var rpcHandlers map[string]commandHandler
|
||||||
var rpcHandlersBeforeInit = map[string]commandHandler{
|
var rpcHandlersBeforeInit = map[string]commandHandler{
|
||||||
"addnode": handleAddNode,
|
"addnode": handleAddNode,
|
||||||
"createrawtransaction": handleCreateRawTransaction,
|
"createrawtransaction": handleCreateRawTransaction,
|
||||||
"debuglevel": handleDebugLevel,
|
"debuglevel": handleDebugLevel,
|
||||||
"decoderawtransaction": handleDecodeRawTransaction,
|
"decoderawtransaction": handleDecodeRawTransaction,
|
||||||
"decodescript": handleDecodeScript,
|
"decodescript": handleDecodeScript,
|
||||||
"estimatefee": handleEstimateFee,
|
"estimatefee": handleEstimateFee,
|
||||||
"generate": handleGenerate,
|
"generate": handleGenerate,
|
||||||
"getaddednodeinfo": handleGetAddedNodeInfo,
|
"getaddednodeinfo": handleGetAddedNodeInfo,
|
||||||
"getbestblock": handleGetBestBlock,
|
"getbestblock": handleGetBestBlock,
|
||||||
"getbestblockhash": handleGetBestBlockHash,
|
"getbestblockhash": handleGetBestBlockHash,
|
||||||
"getblock": handleGetBlock,
|
"getblock": handleGetBlock,
|
||||||
"getblockchaininfo": handleGetBlockChainInfo,
|
"getblockchaininfo": handleGetBlockChainInfo,
|
||||||
"getblockcount": handleGetBlockCount,
|
"getblockcount": handleGetBlockCount,
|
||||||
"getblockhash": handleGetBlockHash,
|
"getblockhash": handleGetBlockHash,
|
||||||
"getblockheader": handleGetBlockHeader,
|
"getblockheader": handleGetBlockHeader,
|
||||||
"getblocktemplate": handleGetBlockTemplate,
|
"getblocktemplate": handleGetBlockTemplate,
|
||||||
"getcfilter": handleGetCFilter,
|
"getcfilter": handleGetCFilter,
|
||||||
"getcfilterheader": handleGetCFilterHeader,
|
"getcfilterheader": handleGetCFilterHeader,
|
||||||
"getconnectioncount": handleGetConnectionCount,
|
"getconnectioncount": handleGetConnectionCount,
|
||||||
"getcurrentnet": handleGetCurrentNet,
|
"getcurrentnet": handleGetCurrentNet,
|
||||||
"getdifficulty": handleGetDifficulty,
|
"getdifficulty": handleGetDifficulty,
|
||||||
"getgenerate": handleGetGenerate,
|
"getgenerate": handleGetGenerate,
|
||||||
"gethashespersec": handleGetHashesPerSec,
|
"gethashespersec": handleGetHashesPerSec,
|
||||||
"getheaders": handleGetHeaders,
|
"getheaders": handleGetHeaders,
|
||||||
"getinfo": handleGetInfo,
|
"getinfo": handleGetInfo,
|
||||||
"getmempoolinfo": handleGetMempoolInfo,
|
"getmempoolinfo": handleGetMempoolInfo,
|
||||||
"getmininginfo": handleGetMiningInfo,
|
"getmininginfo": handleGetMiningInfo,
|
||||||
"getnettotals": handleGetNetTotals,
|
"getnettotals": handleGetNetTotals,
|
||||||
"getnetworkhashps": handleGetNetworkHashPS,
|
"getnetworkhashps": handleGetNetworkHashPS,
|
||||||
"getpeerinfo": handleGetPeerInfo,
|
"getpeerinfo": handleGetPeerInfo,
|
||||||
"getrawmempool": handleGetRawMempool,
|
"getrawmempool": handleGetRawMempool,
|
||||||
"getrawtransaction": handleGetRawTransaction,
|
"getrawtransaction": handleGetRawTransaction,
|
||||||
"gettxout": handleGetTxOut,
|
"gettxout": handleGetTxOut,
|
||||||
"help": handleHelp,
|
"help": handleHelp,
|
||||||
"node": handleNode,
|
"node": handleNode,
|
||||||
"ping": handlePing,
|
"ping": handlePing,
|
||||||
"searchrawtransactions": handleSearchRawTransactions,
|
"searchrawtransactions": handleSearchRawTransactions,
|
||||||
"sendrawtransaction": handleSendRawTransaction,
|
"sendrawtransaction": handleSendRawTransaction,
|
||||||
"setgenerate": handleSetGenerate,
|
"setgenerate": handleSetGenerate,
|
||||||
"stop": handleStop,
|
"signmessagewithprivkey": handleSignMessageWithPrivKey,
|
||||||
"submitblock": handleSubmitBlock,
|
"stop": handleStop,
|
||||||
"uptime": handleUptime,
|
"submitblock": handleSubmitBlock,
|
||||||
"validateaddress": handleValidateAddress,
|
"uptime": handleUptime,
|
||||||
"verifychain": handleVerifyChain,
|
"validateaddress": handleValidateAddress,
|
||||||
"verifymessage": handleVerifyMessage,
|
"verifychain": handleVerifyChain,
|
||||||
"version": handleVersion,
|
"verifymessage": handleVerifyMessage,
|
||||||
|
"version": handleVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
// list of commands that we recognize, but for which btcd has no support because
|
// list of commands that we recognize, but for which btcd has no support because
|
||||||
|
@ -3435,6 +3436,52 @@ func handleSetGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{})
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Text used to signify that a signed message follows and to prevent
|
||||||
|
// inadvertently signing a transaction.
|
||||||
|
const messageSignatureHeader = "Bitcoin Signed Message:\n"
|
||||||
|
|
||||||
|
// handleSignMessageWithPrivKey implements the signmessagewithprivkey command.
|
||||||
|
func handleSignMessageWithPrivKey(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||||
|
c := cmd.(*btcjson.SignMessageWithPrivKeyCmd)
|
||||||
|
|
||||||
|
wif, err := btcutil.DecodeWIF(c.PrivKey)
|
||||||
|
if err != nil {
|
||||||
|
message := "Invalid private key"
|
||||||
|
switch err {
|
||||||
|
case btcutil.ErrMalformedPrivateKey:
|
||||||
|
message = "Malformed private key"
|
||||||
|
case btcutil.ErrChecksumMismatch:
|
||||||
|
message = "Private key checksum mismatch"
|
||||||
|
}
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCInvalidAddressOrKey,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !wif.IsForNet(s.cfg.ChainParams) {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCInvalidAddressOrKey,
|
||||||
|
Message: "Private key for wrong network",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
wire.WriteVarString(&buf, 0, messageSignatureHeader)
|
||||||
|
wire.WriteVarString(&buf, 0, c.Message)
|
||||||
|
messageHash := chainhash.DoubleHashB(buf.Bytes())
|
||||||
|
|
||||||
|
sig, err := btcec.SignCompact(btcec.S256(), wif.PrivKey,
|
||||||
|
messageHash, wif.CompressPubKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCInvalidAddressOrKey,
|
||||||
|
Message: "Sign failed",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(sig), nil
|
||||||
|
}
|
||||||
|
|
||||||
// handleStop implements the stop command.
|
// handleStop implements the stop command.
|
||||||
func handleStop(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
func handleStop(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||||
select {
|
select {
|
||||||
|
@ -3615,7 +3662,7 @@ func handleVerifyMessage(s *rpcServer, cmd interface{}, closeChan <-chan struct{
|
||||||
// Validate the signature - this just shows that it was valid at all.
|
// Validate the signature - this just shows that it was valid at all.
|
||||||
// we will compare it with the key next.
|
// we will compare it with the key next.
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n")
|
wire.WriteVarString(&buf, 0, messageSignatureHeader)
|
||||||
wire.WriteVarString(&buf, 0, c.Message)
|
wire.WriteVarString(&buf, 0, c.Message)
|
||||||
expectedMessageHash := chainhash.DoubleHashB(buf.Bytes())
|
expectedMessageHash := chainhash.DoubleHashB(buf.Bytes())
|
||||||
pk, wasCompressed, err := btcec.RecoverCompact(btcec.S256(), sig,
|
pk, wasCompressed, err := btcec.RecoverCompact(btcec.S256(), sig,
|
||||||
|
|
|
@ -559,6 +559,12 @@ var helpDescsEnUS = map[string]string{
|
||||||
"setgenerate-generate": "Use true to enable generation, false to disable it",
|
"setgenerate-generate": "Use true to enable generation, false to disable it",
|
||||||
"setgenerate-genproclimit": "The number of processors (cores) to limit generation to or -1 for default",
|
"setgenerate-genproclimit": "The number of processors (cores) to limit generation to or -1 for default",
|
||||||
|
|
||||||
|
// SignMessageWithPrivKeyCmd help.
|
||||||
|
"signmessagewithprivkey--synopsis": "Sign a message with the private key of an address",
|
||||||
|
"signmessagewithprivkey-privkey": "The private key to sign the message with",
|
||||||
|
"signmessagewithprivkey-message": "The message to create a signature of",
|
||||||
|
"signmessagewithprivkey--result0": "The signature of the message encoded in base 64",
|
||||||
|
|
||||||
// StopCmd help.
|
// StopCmd help.
|
||||||
"stop--synopsis": "Shutdown btcd.",
|
"stop--synopsis": "Shutdown btcd.",
|
||||||
"stop--result0": "The string 'btcd stopping.'",
|
"stop--result0": "The string 'btcd stopping.'",
|
||||||
|
@ -691,52 +697,53 @@ var helpDescsEnUS = map[string]string{
|
||||||
// This information is used to generate the help. Each result type must be a
|
// This information is used to generate the help. Each result type must be a
|
||||||
// pointer to the type (or nil to indicate no return value).
|
// pointer to the type (or nil to indicate no return value).
|
||||||
var rpcResultTypes = map[string][]interface{}{
|
var rpcResultTypes = map[string][]interface{}{
|
||||||
"addnode": nil,
|
"addnode": nil,
|
||||||
"createrawtransaction": {(*string)(nil)},
|
"createrawtransaction": {(*string)(nil)},
|
||||||
"debuglevel": {(*string)(nil), (*string)(nil)},
|
"debuglevel": {(*string)(nil), (*string)(nil)},
|
||||||
"decoderawtransaction": {(*btcjson.TxRawDecodeResult)(nil)},
|
"decoderawtransaction": {(*btcjson.TxRawDecodeResult)(nil)},
|
||||||
"decodescript": {(*btcjson.DecodeScriptResult)(nil)},
|
"decodescript": {(*btcjson.DecodeScriptResult)(nil)},
|
||||||
"estimatefee": {(*float64)(nil)},
|
"estimatefee": {(*float64)(nil)},
|
||||||
"generate": {(*[]string)(nil)},
|
"generate": {(*[]string)(nil)},
|
||||||
"getaddednodeinfo": {(*[]string)(nil), (*[]btcjson.GetAddedNodeInfoResult)(nil)},
|
"getaddednodeinfo": {(*[]string)(nil), (*[]btcjson.GetAddedNodeInfoResult)(nil)},
|
||||||
"getbestblock": {(*btcjson.GetBestBlockResult)(nil)},
|
"getbestblock": {(*btcjson.GetBestBlockResult)(nil)},
|
||||||
"getbestblockhash": {(*string)(nil)},
|
"getbestblockhash": {(*string)(nil)},
|
||||||
"getblock": {(*string)(nil), (*btcjson.GetBlockVerboseResult)(nil)},
|
"getblock": {(*string)(nil), (*btcjson.GetBlockVerboseResult)(nil)},
|
||||||
"getblockcount": {(*int64)(nil)},
|
"getblockcount": {(*int64)(nil)},
|
||||||
"getblockhash": {(*string)(nil)},
|
"getblockhash": {(*string)(nil)},
|
||||||
"getblockheader": {(*string)(nil), (*btcjson.GetBlockHeaderVerboseResult)(nil)},
|
"getblockheader": {(*string)(nil), (*btcjson.GetBlockHeaderVerboseResult)(nil)},
|
||||||
"getblocktemplate": {(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil},
|
"getblocktemplate": {(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil},
|
||||||
"getblockchaininfo": {(*btcjson.GetBlockChainInfoResult)(nil)},
|
"getblockchaininfo": {(*btcjson.GetBlockChainInfoResult)(nil)},
|
||||||
"getcfilter": {(*string)(nil)},
|
"getcfilter": {(*string)(nil)},
|
||||||
"getcfilterheader": {(*string)(nil)},
|
"getcfilterheader": {(*string)(nil)},
|
||||||
"getconnectioncount": {(*int32)(nil)},
|
"getconnectioncount": {(*int32)(nil)},
|
||||||
"getcurrentnet": {(*uint32)(nil)},
|
"getcurrentnet": {(*uint32)(nil)},
|
||||||
"getdifficulty": {(*float64)(nil)},
|
"getdifficulty": {(*float64)(nil)},
|
||||||
"getgenerate": {(*bool)(nil)},
|
"getgenerate": {(*bool)(nil)},
|
||||||
"gethashespersec": {(*float64)(nil)},
|
"gethashespersec": {(*float64)(nil)},
|
||||||
"getheaders": {(*[]string)(nil)},
|
"getheaders": {(*[]string)(nil)},
|
||||||
"getinfo": {(*btcjson.InfoChainResult)(nil)},
|
"getinfo": {(*btcjson.InfoChainResult)(nil)},
|
||||||
"getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)},
|
"getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)},
|
||||||
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
|
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
|
||||||
"getnettotals": {(*btcjson.GetNetTotalsResult)(nil)},
|
"getnettotals": {(*btcjson.GetNetTotalsResult)(nil)},
|
||||||
"getnetworkhashps": {(*int64)(nil)},
|
"getnetworkhashps": {(*int64)(nil)},
|
||||||
"getpeerinfo": {(*[]btcjson.GetPeerInfoResult)(nil)},
|
"getpeerinfo": {(*[]btcjson.GetPeerInfoResult)(nil)},
|
||||||
"getrawmempool": {(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)},
|
"getrawmempool": {(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)},
|
||||||
"getrawtransaction": {(*string)(nil), (*btcjson.TxRawResult)(nil)},
|
"getrawtransaction": {(*string)(nil), (*btcjson.TxRawResult)(nil)},
|
||||||
"gettxout": {(*btcjson.GetTxOutResult)(nil)},
|
"gettxout": {(*btcjson.GetTxOutResult)(nil)},
|
||||||
"node": nil,
|
"node": nil,
|
||||||
"help": {(*string)(nil), (*string)(nil)},
|
"help": {(*string)(nil), (*string)(nil)},
|
||||||
"ping": nil,
|
"ping": nil,
|
||||||
"searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)},
|
"searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)},
|
||||||
"sendrawtransaction": {(*string)(nil)},
|
"sendrawtransaction": {(*string)(nil)},
|
||||||
"setgenerate": nil,
|
"setgenerate": nil,
|
||||||
"stop": {(*string)(nil)},
|
"signmessagewithprivkey": {(*string)(nil)},
|
||||||
"submitblock": {nil, (*string)(nil)},
|
"stop": {(*string)(nil)},
|
||||||
"uptime": {(*int64)(nil)},
|
"submitblock": {nil, (*string)(nil)},
|
||||||
"validateaddress": {(*btcjson.ValidateAddressChainResult)(nil)},
|
"uptime": {(*int64)(nil)},
|
||||||
"verifychain": {(*bool)(nil)},
|
"validateaddress": {(*btcjson.ValidateAddressChainResult)(nil)},
|
||||||
"verifymessage": {(*bool)(nil)},
|
"verifychain": {(*bool)(nil)},
|
||||||
"version": {(*map[string]btcjson.VersionResult)(nil)},
|
"verifymessage": {(*bool)(nil)},
|
||||||
|
"version": {(*map[string]btcjson.VersionResult)(nil)},
|
||||||
|
|
||||||
// Websocket commands.
|
// Websocket commands.
|
||||||
"loadtxfilter": nil,
|
"loadtxfilter": nil,
|
||||||
|
|
Loading…
Add table
Reference in a new issue