Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F10907493
bitcoinrpc.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
46 KB
Subscribers
None
bitcoinrpc.cpp
View Options
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include
"init.h"
#include
"util.h"
#include
"sync.h"
#include
"ui_interface.h"
#include
"base58.h"
#include
"bitcoinrpc.h"
#include
"db.h"
#include
<boost/algorithm/string.hpp>
#include
<boost/asio.hpp>
#include
<boost/asio/ip/v6_only.hpp>
#include
<boost/asio/ssl.hpp>
#include
<boost/bind.hpp>
#include
<boost/filesystem.hpp>
#include
<boost/filesystem/fstream.hpp>
#include
<boost/foreach.hpp>
#include
<boost/iostreams/concepts.hpp>
#include
<boost/iostreams/stream.hpp>
#include
<boost/lexical_cast.hpp>
#include
<boost/shared_ptr.hpp>
#include
<list>
using
namespace
std
;
using
namespace
boost
;
using
namespace
boost
::
asio
;
using
namespace
json_spirit
;
static
std
::
string
strRPCUserColonPass
;
// These are created by StartRPCThreads, destroyed in StopRPCThreads
static
asio
::
io_service
*
rpc_io_service
=
NULL
;
static
map
<
string
,
boost
::
shared_ptr
<
deadline_timer
>
>
deadlineTimers
;
static
ssl
::
context
*
rpc_ssl_context
=
NULL
;
static
boost
::
thread_group
*
rpc_worker_group
=
NULL
;
static
inline
unsigned
short
GetDefaultRPCPort
()
{
return
GetBoolArg
(
"-testnet"
,
false
)
?
18332
:
8332
;
}
Object
JSONRPCError
(
int
code
,
const
string
&
message
)
{
Object
error
;
error
.
push_back
(
Pair
(
"code"
,
code
));
error
.
push_back
(
Pair
(
"message"
,
message
));
return
error
;
}
void
RPCTypeCheck
(
const
Array
&
params
,
const
list
<
Value_type
>&
typesExpected
,
bool
fAllowNull
)
{
unsigned
int
i
=
0
;
BOOST_FOREACH
(
Value_type
t
,
typesExpected
)
{
if
(
params
.
size
()
<=
i
)
break
;
const
Value
&
v
=
params
[
i
];
if
(
!
((
v
.
type
()
==
t
)
||
(
fAllowNull
&&
(
v
.
type
()
==
null_type
))))
{
string
err
=
strprintf
(
"Expected type %s, got %s"
,
Value_type_name
[
t
],
Value_type_name
[
v
.
type
()]);
throw
JSONRPCError
(
RPC_TYPE_ERROR
,
err
);
}
i
++
;
}
}
void
RPCTypeCheck
(
const
Object
&
o
,
const
map
<
string
,
Value_type
>&
typesExpected
,
bool
fAllowNull
)
{
BOOST_FOREACH
(
const
PAIRTYPE
(
string
,
Value_type
)
&
t
,
typesExpected
)
{
const
Value
&
v
=
find_value
(
o
,
t
.
first
);
if
(
!
fAllowNull
&&
v
.
type
()
==
null_type
)
throw
JSONRPCError
(
RPC_TYPE_ERROR
,
strprintf
(
"Missing %s"
,
t
.
first
.
c_str
()));
if
(
!
((
v
.
type
()
==
t
.
second
)
||
(
fAllowNull
&&
(
v
.
type
()
==
null_type
))))
{
string
err
=
strprintf
(
"Expected type %s for %s, got %s"
,
Value_type_name
[
t
.
second
],
t
.
first
.
c_str
(),
Value_type_name
[
v
.
type
()]);
throw
JSONRPCError
(
RPC_TYPE_ERROR
,
err
);
}
}
}
int64
AmountFromValue
(
const
Value
&
value
)
{
double
dAmount
=
value
.
get_real
();
if
(
dAmount
<=
0.0
||
dAmount
>
21000000.0
)
throw
JSONRPCError
(
RPC_TYPE_ERROR
,
"Invalid amount"
);
int64
nAmount
=
roundint64
(
dAmount
*
COIN
);
if
(
!
MoneyRange
(
nAmount
))
throw
JSONRPCError
(
RPC_TYPE_ERROR
,
"Invalid amount"
);
return
nAmount
;
}
Value
ValueFromAmount
(
int64
amount
)
{
return
(
double
)
amount
/
(
double
)
COIN
;
}
std
::
string
HexBits
(
unsigned
int
nBits
)
{
union
{
int32_t
nBits
;
char
cBits
[
4
];
}
uBits
;
uBits
.
nBits
=
htonl
((
int32_t
)
nBits
);
return
HexStr
(
BEGIN
(
uBits
.
cBits
),
END
(
uBits
.
cBits
));
}
///
/// Note: This interface may still be subject to change.
///
string
CRPCTable
::
help
(
string
strCommand
)
const
{
string
strRet
;
set
<
rpcfn_type
>
setDone
;
for
(
map
<
string
,
const
CRPCCommand
*>::
const_iterator
mi
=
mapCommands
.
begin
();
mi
!=
mapCommands
.
end
();
++
mi
)
{
const
CRPCCommand
*
pcmd
=
mi
->
second
;
string
strMethod
=
mi
->
first
;
// We already filter duplicates, but these deprecated screw up the sort order
if
(
strMethod
.
find
(
"label"
)
!=
string
::
npos
)
continue
;
if
(
strCommand
!=
""
&&
strMethod
!=
strCommand
)
continue
;
try
{
Array
params
;
rpcfn_type
pfn
=
pcmd
->
actor
;
if
(
setDone
.
insert
(
pfn
).
second
)
(
*
pfn
)(
params
,
true
);
}
catch
(
std
::
exception
&
e
)
{
// Help text is returned in an exception
string
strHelp
=
string
(
e
.
what
());
if
(
strCommand
==
""
)
if
(
strHelp
.
find
(
'\n'
)
!=
string
::
npos
)
strHelp
=
strHelp
.
substr
(
0
,
strHelp
.
find
(
'\n'
));
strRet
+=
strHelp
+
"
\n
"
;
}
}
if
(
strRet
==
""
)
strRet
=
strprintf
(
"help: unknown command: %s
\n
"
,
strCommand
.
c_str
());
strRet
=
strRet
.
substr
(
0
,
strRet
.
size
()
-1
);
return
strRet
;
}
Value
help
(
const
Array
&
params
,
bool
fHelp
)
{
if
(
fHelp
||
params
.
size
()
>
1
)
throw
runtime_error
(
"help [command]
\n
"
"List commands, or get help for a command."
);
string
strCommand
;
if
(
params
.
size
()
>
0
)
strCommand
=
params
[
0
].
get_str
();
return
tableRPC
.
help
(
strCommand
);
}
Value
stop
(
const
Array
&
params
,
bool
fHelp
)
{
// Accept the deprecated and ignored 'detach' boolean argument
if
(
fHelp
||
params
.
size
()
>
1
)
throw
runtime_error
(
"stop
\n
"
"Stop Bitcoin server."
);
// Shutdown will take long enough that the response should get back
StartShutdown
();
return
"Bitcoin server stopping"
;
}
//
// Call Table
//
static
const
CRPCCommand
vRPCCommands
[]
=
{
// name actor (function) okSafeMode threadSafe
// ------------------------ ----------------------- ---------- ----------
{
"help"
,
&
help
,
true
,
true
},
{
"stop"
,
&
stop
,
true
,
true
},
{
"getblockcount"
,
&
getblockcount
,
true
,
false
},
{
"getconnectioncount"
,
&
getconnectioncount
,
true
,
false
},
{
"getpeerinfo"
,
&
getpeerinfo
,
true
,
false
},
{
"addnode"
,
&
addnode
,
true
,
true
},
{
"getaddednodeinfo"
,
&
getaddednodeinfo
,
true
,
true
},
{
"getdifficulty"
,
&
getdifficulty
,
true
,
false
},
{
"getgenerate"
,
&
getgenerate
,
true
,
false
},
{
"setgenerate"
,
&
setgenerate
,
true
,
false
},
{
"gethashespersec"
,
&
gethashespersec
,
true
,
false
},
{
"getinfo"
,
&
getinfo
,
true
,
false
},
{
"getmininginfo"
,
&
getmininginfo
,
true
,
false
},
{
"getnewaddress"
,
&
getnewaddress
,
true
,
false
},
{
"getaccountaddress"
,
&
getaccountaddress
,
true
,
false
},
{
"setaccount"
,
&
setaccount
,
true
,
false
},
{
"getaccount"
,
&
getaccount
,
false
,
false
},
{
"getaddressesbyaccount"
,
&
getaddressesbyaccount
,
true
,
false
},
{
"sendtoaddress"
,
&
sendtoaddress
,
false
,
false
},
{
"getreceivedbyaddress"
,
&
getreceivedbyaddress
,
false
,
false
},
{
"getreceivedbyaccount"
,
&
getreceivedbyaccount
,
false
,
false
},
{
"listreceivedbyaddress"
,
&
listreceivedbyaddress
,
false
,
false
},
{
"listreceivedbyaccount"
,
&
listreceivedbyaccount
,
false
,
false
},
{
"backupwallet"
,
&
backupwallet
,
true
,
false
},
{
"keypoolrefill"
,
&
keypoolrefill
,
true
,
false
},
{
"walletpassphrase"
,
&
walletpassphrase
,
true
,
false
},
{
"walletpassphrasechange"
,
&
walletpassphrasechange
,
false
,
false
},
{
"walletlock"
,
&
walletlock
,
true
,
false
},
{
"encryptwallet"
,
&
encryptwallet
,
false
,
false
},
{
"validateaddress"
,
&
validateaddress
,
true
,
false
},
{
"getbalance"
,
&
getbalance
,
false
,
false
},
{
"move"
,
&
movecmd
,
false
,
false
},
{
"sendfrom"
,
&
sendfrom
,
false
,
false
},
{
"sendmany"
,
&
sendmany
,
false
,
false
},
{
"addmultisigaddress"
,
&
addmultisigaddress
,
false
,
false
},
{
"createmultisig"
,
&
createmultisig
,
true
,
true
},
{
"getrawmempool"
,
&
getrawmempool
,
true
,
false
},
{
"getblock"
,
&
getblock
,
false
,
false
},
{
"getblockhash"
,
&
getblockhash
,
false
,
false
},
{
"gettransaction"
,
&
gettransaction
,
false
,
false
},
{
"listtransactions"
,
&
listtransactions
,
false
,
false
},
{
"listaddressgroupings"
,
&
listaddressgroupings
,
false
,
false
},
{
"signmessage"
,
&
signmessage
,
false
,
false
},
{
"verifymessage"
,
&
verifymessage
,
false
,
false
},
{
"getwork"
,
&
getwork
,
true
,
false
},
{
"listaccounts"
,
&
listaccounts
,
false
,
false
},
{
"settxfee"
,
&
settxfee
,
false
,
false
},
{
"getblocktemplate"
,
&
getblocktemplate
,
true
,
false
},
{
"submitblock"
,
&
submitblock
,
false
,
false
},
{
"listsinceblock"
,
&
listsinceblock
,
false
,
false
},
{
"dumpprivkey"
,
&
dumpprivkey
,
true
,
false
},
{
"importprivkey"
,
&
importprivkey
,
false
,
false
},
{
"listunspent"
,
&
listunspent
,
false
,
false
},
{
"getrawtransaction"
,
&
getrawtransaction
,
false
,
false
},
{
"createrawtransaction"
,
&
createrawtransaction
,
false
,
false
},
{
"decoderawtransaction"
,
&
decoderawtransaction
,
false
,
false
},
{
"signrawtransaction"
,
&
signrawtransaction
,
false
,
false
},
{
"sendrawtransaction"
,
&
sendrawtransaction
,
false
,
false
},
{
"gettxoutsetinfo"
,
&
gettxoutsetinfo
,
true
,
false
},
{
"gettxout"
,
&
gettxout
,
true
,
false
},
{
"lockunspent"
,
&
lockunspent
,
false
,
false
},
{
"listlockunspent"
,
&
listlockunspent
,
false
,
false
},
};
CRPCTable
::
CRPCTable
()
{
unsigned
int
vcidx
;
for
(
vcidx
=
0
;
vcidx
<
(
sizeof
(
vRPCCommands
)
/
sizeof
(
vRPCCommands
[
0
]));
vcidx
++
)
{
const
CRPCCommand
*
pcmd
;
pcmd
=
&
vRPCCommands
[
vcidx
];
mapCommands
[
pcmd
->
name
]
=
pcmd
;
}
}
const
CRPCCommand
*
CRPCTable
::
operator
[](
string
name
)
const
{
map
<
string
,
const
CRPCCommand
*>::
const_iterator
it
=
mapCommands
.
find
(
name
);
if
(
it
==
mapCommands
.
end
())
return
NULL
;
return
(
*
it
).
second
;
}
//
// HTTP protocol
//
// This ain't Apache. We're just using HTTP header for the length field
// and to be compatible with other JSON-RPC implementations.
//
string
HTTPPost
(
const
string
&
strMsg
,
const
map
<
string
,
string
>&
mapRequestHeaders
)
{
ostringstream
s
;
s
<<
"POST / HTTP/1.1
\r\n
"
<<
"User-Agent: bitcoin-json-rpc/"
<<
FormatFullVersion
()
<<
"
\r\n
"
<<
"Host: 127.0.0.1
\r\n
"
<<
"Content-Type: application/json
\r\n
"
<<
"Content-Length: "
<<
strMsg
.
size
()
<<
"
\r\n
"
<<
"Connection: close
\r\n
"
<<
"Accept: application/json
\r\n
"
;
BOOST_FOREACH
(
const
PAIRTYPE
(
string
,
string
)
&
item
,
mapRequestHeaders
)
s
<<
item
.
first
<<
": "
<<
item
.
second
<<
"
\r\n
"
;
s
<<
"
\r\n
"
<<
strMsg
;
return
s
.
str
();
}
string
rfc1123Time
()
{
char
buffer
[
64
];
time_t
now
;
time
(
&
now
);
struct
tm
*
now_gmt
=
gmtime
(
&
now
);
string
locale
(
setlocale
(
LC_TIME
,
NULL
));
setlocale
(
LC_TIME
,
"C"
);
// we want POSIX (aka "C") weekday/month strings
strftime
(
buffer
,
sizeof
(
buffer
),
"%a, %d %b %Y %H:%M:%S +0000"
,
now_gmt
);
setlocale
(
LC_TIME
,
locale
.
c_str
());
return
string
(
buffer
);
}
static
string
HTTPReply
(
int
nStatus
,
const
string
&
strMsg
,
bool
keepalive
)
{
if
(
nStatus
==
HTTP_UNAUTHORIZED
)
return
strprintf
(
"HTTP/1.0 401 Authorization Required
\r\n
"
"Date: %s
\r\n
"
"Server: bitcoin-json-rpc/%s
\r\n
"
"WWW-Authenticate: Basic realm=
\"
jsonrpc
\"\r\n
"
"Content-Type: text/html
\r\n
"
"Content-Length: 296
\r\n
"
"
\r\n
"
"<!DOCTYPE HTML PUBLIC
\"
-//W3C//DTD HTML 4.01 Transitional//EN
\"\r\n
"
"
\"
http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd
\"
>
\r\n
"
"<HTML>
\r\n
"
"<HEAD>
\r\n
"
"<TITLE>Error</TITLE>
\r\n
"
"<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>
\r\n
"
"</HEAD>
\r\n
"
"<BODY><H1>401 Unauthorized.</H1></BODY>
\r\n
"
"</HTML>
\r\n
"
,
rfc1123Time
().
c_str
(),
FormatFullVersion
().
c_str
());
const
char
*
cStatus
;
if
(
nStatus
==
HTTP_OK
)
cStatus
=
"OK"
;
else
if
(
nStatus
==
HTTP_BAD_REQUEST
)
cStatus
=
"Bad Request"
;
else
if
(
nStatus
==
HTTP_FORBIDDEN
)
cStatus
=
"Forbidden"
;
else
if
(
nStatus
==
HTTP_NOT_FOUND
)
cStatus
=
"Not Found"
;
else
if
(
nStatus
==
HTTP_INTERNAL_SERVER_ERROR
)
cStatus
=
"Internal Server Error"
;
else
cStatus
=
""
;
return
strprintf
(
"HTTP/1.1 %d %s
\r\n
"
"Date: %s
\r\n
"
"Connection: %s
\r\n
"
"Content-Length: %"
PRIszu
"
\r\n
"
"Content-Type: application/json
\r\n
"
"Server: bitcoin-json-rpc/%s
\r\n
"
"
\r\n
"
"%s"
,
nStatus
,
cStatus
,
rfc1123Time
().
c_str
(),
keepalive
?
"keep-alive"
:
"close"
,
strMsg
.
size
(),
FormatFullVersion
().
c_str
(),
strMsg
.
c_str
());
}
bool
ReadHTTPRequestLine
(
std
::
basic_istream
<
char
>&
stream
,
int
&
proto
,
string
&
http_method
,
string
&
http_uri
)
{
string
str
;
getline
(
stream
,
str
);
// HTTP request line is space-delimited
vector
<
string
>
vWords
;
boost
::
split
(
vWords
,
str
,
boost
::
is_any_of
(
" "
));
if
(
vWords
.
size
()
<
2
)
return
false
;
// HTTP methods permitted: GET, POST
http_method
=
vWords
[
0
];
if
(
http_method
!=
"GET"
&&
http_method
!=
"POST"
)
return
false
;
// HTTP URI must be an absolute path, relative to current host
http_uri
=
vWords
[
1
];
if
(
http_uri
.
size
()
==
0
||
http_uri
[
0
]
!=
'/'
)
return
false
;
// parse proto, if present
string
strProto
=
""
;
if
(
vWords
.
size
()
>
2
)
strProto
=
vWords
[
2
];
proto
=
0
;
const
char
*
ver
=
strstr
(
strProto
.
c_str
(),
"HTTP/1."
);
if
(
ver
!=
NULL
)
proto
=
atoi
(
ver
+
7
);
return
true
;
}
int
ReadHTTPStatus
(
std
::
basic_istream
<
char
>&
stream
,
int
&
proto
)
{
string
str
;
getline
(
stream
,
str
);
vector
<
string
>
vWords
;
boost
::
split
(
vWords
,
str
,
boost
::
is_any_of
(
" "
));
if
(
vWords
.
size
()
<
2
)
return
HTTP_INTERNAL_SERVER_ERROR
;
proto
=
0
;
const
char
*
ver
=
strstr
(
str
.
c_str
(),
"HTTP/1."
);
if
(
ver
!=
NULL
)
proto
=
atoi
(
ver
+
7
);
return
atoi
(
vWords
[
1
].
c_str
());
}
int
ReadHTTPHeaders
(
std
::
basic_istream
<
char
>&
stream
,
map
<
string
,
string
>&
mapHeadersRet
)
{
int
nLen
=
0
;
loop
{
string
str
;
std
::
getline
(
stream
,
str
);
if
(
str
.
empty
()
||
str
==
"
\r
"
)
break
;
string
::
size_type
nColon
=
str
.
find
(
":"
);
if
(
nColon
!=
string
::
npos
)
{
string
strHeader
=
str
.
substr
(
0
,
nColon
);
boost
::
trim
(
strHeader
);
boost
::
to_lower
(
strHeader
);
string
strValue
=
str
.
substr
(
nColon
+
1
);
boost
::
trim
(
strValue
);
mapHeadersRet
[
strHeader
]
=
strValue
;
if
(
strHeader
==
"content-length"
)
nLen
=
atoi
(
strValue
.
c_str
());
}
}
return
nLen
;
}
int
ReadHTTPMessage
(
std
::
basic_istream
<
char
>&
stream
,
map
<
string
,
string
>&
mapHeadersRet
,
string
&
strMessageRet
,
int
nProto
)
{
mapHeadersRet
.
clear
();
strMessageRet
=
""
;
// Read header
int
nLen
=
ReadHTTPHeaders
(
stream
,
mapHeadersRet
);
if
(
nLen
<
0
||
nLen
>
(
int
)
MAX_SIZE
)
return
HTTP_INTERNAL_SERVER_ERROR
;
// Read message
if
(
nLen
>
0
)
{
vector
<
char
>
vch
(
nLen
);
stream
.
read
(
&
vch
[
0
],
nLen
);
strMessageRet
=
string
(
vch
.
begin
(),
vch
.
end
());
}
string
sConHdr
=
mapHeadersRet
[
"connection"
];
if
((
sConHdr
!=
"close"
)
&&
(
sConHdr
!=
"keep-alive"
))
{
if
(
nProto
>=
1
)
mapHeadersRet
[
"connection"
]
=
"keep-alive"
;
else
mapHeadersRet
[
"connection"
]
=
"close"
;
}
return
HTTP_OK
;
}
bool
HTTPAuthorized
(
map
<
string
,
string
>&
mapHeaders
)
{
string
strAuth
=
mapHeaders
[
"authorization"
];
if
(
strAuth
.
substr
(
0
,
6
)
!=
"Basic "
)
return
false
;
string
strUserPass64
=
strAuth
.
substr
(
6
);
boost
::
trim
(
strUserPass64
);
string
strUserPass
=
DecodeBase64
(
strUserPass64
);
return
strUserPass
==
strRPCUserColonPass
;
}
//
// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
// unspecified (HTTP errors and contents of 'error').
//
// 1.0 spec: http://json-rpc.org/wiki/specification
// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
//
string
JSONRPCRequest
(
const
string
&
strMethod
,
const
Array
&
params
,
const
Value
&
id
)
{
Object
request
;
request
.
push_back
(
Pair
(
"method"
,
strMethod
));
request
.
push_back
(
Pair
(
"params"
,
params
));
request
.
push_back
(
Pair
(
"id"
,
id
));
return
write_string
(
Value
(
request
),
false
)
+
"
\n
"
;
}
Object
JSONRPCReplyObj
(
const
Value
&
result
,
const
Value
&
error
,
const
Value
&
id
)
{
Object
reply
;
if
(
error
.
type
()
!=
null_type
)
reply
.
push_back
(
Pair
(
"result"
,
Value
::
null
));
else
reply
.
push_back
(
Pair
(
"result"
,
result
));
reply
.
push_back
(
Pair
(
"error"
,
error
));
reply
.
push_back
(
Pair
(
"id"
,
id
));
return
reply
;
}
string
JSONRPCReply
(
const
Value
&
result
,
const
Value
&
error
,
const
Value
&
id
)
{
Object
reply
=
JSONRPCReplyObj
(
result
,
error
,
id
);
return
write_string
(
Value
(
reply
),
false
)
+
"
\n
"
;
}
void
ErrorReply
(
std
::
ostream
&
stream
,
const
Object
&
objError
,
const
Value
&
id
)
{
// Send error reply from json-rpc error object
int
nStatus
=
HTTP_INTERNAL_SERVER_ERROR
;
int
code
=
find_value
(
objError
,
"code"
).
get_int
();
if
(
code
==
RPC_INVALID_REQUEST
)
nStatus
=
HTTP_BAD_REQUEST
;
else
if
(
code
==
RPC_METHOD_NOT_FOUND
)
nStatus
=
HTTP_NOT_FOUND
;
string
strReply
=
JSONRPCReply
(
Value
::
null
,
objError
,
id
);
stream
<<
HTTPReply
(
nStatus
,
strReply
,
false
)
<<
std
::
flush
;
}
bool
ClientAllowed
(
const
boost
::
asio
::
ip
::
address
&
address
)
{
// Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses
if
(
address
.
is_v6
()
&&
(
address
.
to_v6
().
is_v4_compatible
()
||
address
.
to_v6
().
is_v4_mapped
()))
return
ClientAllowed
(
address
.
to_v6
().
to_v4
());
if
(
address
==
asio
::
ip
::
address_v4
::
loopback
()
||
address
==
asio
::
ip
::
address_v6
::
loopback
()
||
(
address
.
is_v4
()
// Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet)
&&
(
address
.
to_v4
().
to_ulong
()
&
0xff000000
)
==
0x7f000000
))
return
true
;
const
string
strAddress
=
address
.
to_string
();
const
vector
<
string
>&
vAllow
=
mapMultiArgs
[
"-rpcallowip"
];
BOOST_FOREACH
(
string
strAllow
,
vAllow
)
if
(
WildcardMatch
(
strAddress
,
strAllow
))
return
true
;
return
false
;
}
//
// IOStream device that speaks SSL but can also speak non-SSL
//
template
<
typename
Protocol
>
class
SSLIOStreamDevice
:
public
iostreams
::
device
<
iostreams
::
bidirectional
>
{
public
:
SSLIOStreamDevice
(
asio
::
ssl
::
stream
<
typename
Protocol
::
socket
>
&
streamIn
,
bool
fUseSSLIn
)
:
stream
(
streamIn
)
{
fUseSSL
=
fUseSSLIn
;
fNeedHandshake
=
fUseSSLIn
;
}
void
handshake
(
ssl
::
stream_base
::
handshake_type
role
)
{
if
(
!
fNeedHandshake
)
return
;
fNeedHandshake
=
false
;
stream
.
handshake
(
role
);
}
std
::
streamsize
read
(
char
*
s
,
std
::
streamsize
n
)
{
handshake
(
ssl
::
stream_base
::
server
);
// HTTPS servers read first
if
(
fUseSSL
)
return
stream
.
read_some
(
asio
::
buffer
(
s
,
n
));
return
stream
.
next_layer
().
read_some
(
asio
::
buffer
(
s
,
n
));
}
std
::
streamsize
write
(
const
char
*
s
,
std
::
streamsize
n
)
{
handshake
(
ssl
::
stream_base
::
client
);
// HTTPS clients write first
if
(
fUseSSL
)
return
asio
::
write
(
stream
,
asio
::
buffer
(
s
,
n
));
return
asio
::
write
(
stream
.
next_layer
(),
asio
::
buffer
(
s
,
n
));
}
bool
connect
(
const
std
::
string
&
server
,
const
std
::
string
&
port
)
{
ip
::
tcp
::
resolver
resolver
(
stream
.
get_io_service
());
ip
::
tcp
::
resolver
::
query
query
(
server
.
c_str
(),
port
.
c_str
());
ip
::
tcp
::
resolver
::
iterator
endpoint_iterator
=
resolver
.
resolve
(
query
);
ip
::
tcp
::
resolver
::
iterator
end
;
boost
::
system
::
error_code
error
=
asio
::
error
::
host_not_found
;
while
(
error
&&
endpoint_iterator
!=
end
)
{
stream
.
lowest_layer
().
close
();
stream
.
lowest_layer
().
connect
(
*
endpoint_iterator
++
,
error
);
}
if
(
error
)
return
false
;
return
true
;
}
private
:
bool
fNeedHandshake
;
bool
fUseSSL
;
asio
::
ssl
::
stream
<
typename
Protocol
::
socket
>&
stream
;
};
class
AcceptedConnection
{
public
:
virtual
~
AcceptedConnection
()
{}
virtual
std
::
iostream
&
stream
()
=
0
;
virtual
std
::
string
peer_address_to_string
()
const
=
0
;
virtual
void
close
()
=
0
;
};
template
<
typename
Protocol
>
class
AcceptedConnectionImpl
:
public
AcceptedConnection
{
public
:
AcceptedConnectionImpl
(
asio
::
io_service
&
io_service
,
ssl
::
context
&
context
,
bool
fUseSSL
)
:
sslStream
(
io_service
,
context
),
_d
(
sslStream
,
fUseSSL
),
_stream
(
_d
)
{
}
virtual
std
::
iostream
&
stream
()
{
return
_stream
;
}
virtual
std
::
string
peer_address_to_string
()
const
{
return
peer
.
address
().
to_string
();
}
virtual
void
close
()
{
_stream
.
close
();
}
typename
Protocol
::
endpoint
peer
;
asio
::
ssl
::
stream
<
typename
Protocol
::
socket
>
sslStream
;
private
:
SSLIOStreamDevice
<
Protocol
>
_d
;
iostreams
::
stream
<
SSLIOStreamDevice
<
Protocol
>
>
_stream
;
};
void
ServiceConnection
(
AcceptedConnection
*
conn
);
// Forward declaration required for RPCListen
template
<
typename
Protocol
,
typename
SocketAcceptorService
>
static
void
RPCAcceptHandler
(
boost
::
shared_ptr
<
basic_socket_acceptor
<
Protocol
,
SocketAcceptorService
>
>
acceptor
,
ssl
::
context
&
context
,
bool
fUseSSL
,
AcceptedConnection
*
conn
,
const
boost
::
system
::
error_code
&
error
);
/**
* Sets up I/O resources to accept and handle a new connection.
*/
template
<
typename
Protocol
,
typename
SocketAcceptorService
>
static
void
RPCListen
(
boost
::
shared_ptr
<
basic_socket_acceptor
<
Protocol
,
SocketAcceptorService
>
>
acceptor
,
ssl
::
context
&
context
,
const
bool
fUseSSL
)
{
// Accept connection
AcceptedConnectionImpl
<
Protocol
>*
conn
=
new
AcceptedConnectionImpl
<
Protocol
>
(
acceptor
->
get_io_service
(),
context
,
fUseSSL
);
acceptor
->
async_accept
(
conn
->
sslStream
.
lowest_layer
(),
conn
->
peer
,
boost
::
bind
(
&
RPCAcceptHandler
<
Protocol
,
SocketAcceptorService
>
,
acceptor
,
boost
::
ref
(
context
),
fUseSSL
,
conn
,
boost
::
asio
::
placeholders
::
error
));
}
/**
* Accept and handle incoming connection.
*/
template
<
typename
Protocol
,
typename
SocketAcceptorService
>
static
void
RPCAcceptHandler
(
boost
::
shared_ptr
<
basic_socket_acceptor
<
Protocol
,
SocketAcceptorService
>
>
acceptor
,
ssl
::
context
&
context
,
const
bool
fUseSSL
,
AcceptedConnection
*
conn
,
const
boost
::
system
::
error_code
&
error
)
{
// Immediately start accepting new connections, except when we're cancelled or our socket is closed.
if
(
error
!=
asio
::
error
::
operation_aborted
&&
acceptor
->
is_open
())
RPCListen
(
acceptor
,
context
,
fUseSSL
);
AcceptedConnectionImpl
<
ip
::
tcp
>*
tcp_conn
=
dynamic_cast
<
AcceptedConnectionImpl
<
ip
::
tcp
>*
>
(
conn
);
// TODO: Actually handle errors
if
(
error
)
{
delete
conn
;
}
// Restrict callers by IP. It is important to
// do this before starting client thread, to filter out
// certain DoS and misbehaving clients.
else
if
(
tcp_conn
&&
!
ClientAllowed
(
tcp_conn
->
peer
.
address
()))
{
// Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
if
(
!
fUseSSL
)
conn
->
stream
()
<<
HTTPReply
(
HTTP_FORBIDDEN
,
""
,
false
)
<<
std
::
flush
;
delete
conn
;
}
else
{
ServiceConnection
(
conn
);
conn
->
close
();
delete
conn
;
}
}
void
StartRPCThreads
()
{
strRPCUserColonPass
=
mapArgs
[
"-rpcuser"
]
+
":"
+
mapArgs
[
"-rpcpassword"
];
if
((
mapArgs
[
"-rpcpassword"
]
==
""
)
||
(
mapArgs
[
"-rpcuser"
]
==
mapArgs
[
"-rpcpassword"
]))
{
unsigned
char
rand_pwd
[
32
];
RAND_bytes
(
rand_pwd
,
32
);
string
strWhatAmI
=
"To use bitcoind"
;
if
(
mapArgs
.
count
(
"-server"
))
strWhatAmI
=
strprintf
(
_
(
"To use the %s option"
),
"
\"
-server
\"
"
);
else
if
(
mapArgs
.
count
(
"-daemon"
))
strWhatAmI
=
strprintf
(
_
(
"To use the %s option"
),
"
\"
-daemon
\"
"
);
uiInterface
.
ThreadSafeMessageBox
(
strprintf
(
_
(
"%s, you must set a rpcpassword in the configuration file:
\n
"
"%s
\n
"
"It is recommended you use the following random password:
\n
"
"rpcuser=bitcoinrpc
\n
"
"rpcpassword=%s
\n
"
"(you do not need to remember this password)
\n
"
"The username and password MUST NOT be the same.
\n
"
"If the file does not exist, create it with owner-readable-only file permissions.
\n
"
"It is also recommended to set alertnotify so you are notified of problems;
\n
"
"for example: alertnotify=echo %%s | mail -s
\"
Bitcoin Alert
\"
admin@foo.com
\n
"
),
strWhatAmI
.
c_str
(),
GetConfigFile
().
string
().
c_str
(),
EncodeBase58
(
&
rand_pwd
[
0
],
&
rand_pwd
[
0
]
+
32
).
c_str
()),
""
,
CClientUIInterface
::
MSG_ERROR
);
StartShutdown
();
return
;
}
assert
(
rpc_io_service
==
NULL
);
rpc_io_service
=
new
asio
::
io_service
();
rpc_ssl_context
=
new
ssl
::
context
(
*
rpc_io_service
,
ssl
::
context
::
sslv23
);
const
bool
fUseSSL
=
GetBoolArg
(
"-rpcssl"
);
if
(
fUseSSL
)
{
rpc_ssl_context
->
set_options
(
ssl
::
context
::
no_sslv2
);
filesystem
::
path
pathCertFile
(
GetArg
(
"-rpcsslcertificatechainfile"
,
"server.cert"
));
if
(
!
pathCertFile
.
is_complete
())
pathCertFile
=
filesystem
::
path
(
GetDataDir
())
/
pathCertFile
;
if
(
filesystem
::
exists
(
pathCertFile
))
rpc_ssl_context
->
use_certificate_chain_file
(
pathCertFile
.
string
());
else
printf
(
"ThreadRPCServer ERROR: missing server certificate file %s
\n
"
,
pathCertFile
.
string
().
c_str
());
filesystem
::
path
pathPKFile
(
GetArg
(
"-rpcsslprivatekeyfile"
,
"server.pem"
));
if
(
!
pathPKFile
.
is_complete
())
pathPKFile
=
filesystem
::
path
(
GetDataDir
())
/
pathPKFile
;
if
(
filesystem
::
exists
(
pathPKFile
))
rpc_ssl_context
->
use_private_key_file
(
pathPKFile
.
string
(),
ssl
::
context
::
pem
);
else
printf
(
"ThreadRPCServer ERROR: missing server private key file %s
\n
"
,
pathPKFile
.
string
().
c_str
());
string
strCiphers
=
GetArg
(
"-rpcsslciphers"
,
"TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"
);
SSL_CTX_set_cipher_list
(
rpc_ssl_context
->
impl
(),
strCiphers
.
c_str
());
}
// Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets
const
bool
loopback
=
!
mapArgs
.
count
(
"-rpcallowip"
);
asio
::
ip
::
address
bindAddress
=
loopback
?
asio
::
ip
::
address_v6
::
loopback
()
:
asio
::
ip
::
address_v6
::
any
();
ip
::
tcp
::
endpoint
endpoint
(
bindAddress
,
GetArg
(
"-rpcport"
,
GetDefaultRPCPort
()));
boost
::
system
::
error_code
v6_only_error
;
boost
::
shared_ptr
<
ip
::
tcp
::
acceptor
>
acceptor
(
new
ip
::
tcp
::
acceptor
(
*
rpc_io_service
));
bool
fListening
=
false
;
std
::
string
strerr
;
try
{
acceptor
->
open
(
endpoint
.
protocol
());
acceptor
->
set_option
(
boost
::
asio
::
ip
::
tcp
::
acceptor
::
reuse_address
(
true
));
// Try making the socket dual IPv6/IPv4 (if listening on the "any" address)
acceptor
->
set_option
(
boost
::
asio
::
ip
::
v6_only
(
loopback
),
v6_only_error
);
acceptor
->
bind
(
endpoint
);
acceptor
->
listen
(
socket_base
::
max_connections
);
RPCListen
(
acceptor
,
*
rpc_ssl_context
,
fUseSSL
);
fListening
=
true
;
}
catch
(
boost
::
system
::
system_error
&
e
)
{
strerr
=
strprintf
(
_
(
"An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"
),
endpoint
.
port
(),
e
.
what
());
}
try
{
// If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately
if
(
!
fListening
||
loopback
||
v6_only_error
)
{
bindAddress
=
loopback
?
asio
::
ip
::
address_v4
::
loopback
()
:
asio
::
ip
::
address_v4
::
any
();
endpoint
.
address
(
bindAddress
);
acceptor
.
reset
(
new
ip
::
tcp
::
acceptor
(
*
rpc_io_service
));
acceptor
->
open
(
endpoint
.
protocol
());
acceptor
->
set_option
(
boost
::
asio
::
ip
::
tcp
::
acceptor
::
reuse_address
(
true
));
acceptor
->
bind
(
endpoint
);
acceptor
->
listen
(
socket_base
::
max_connections
);
RPCListen
(
acceptor
,
*
rpc_ssl_context
,
fUseSSL
);
fListening
=
true
;
}
}
catch
(
boost
::
system
::
system_error
&
e
)
{
strerr
=
strprintf
(
_
(
"An error occurred while setting up the RPC port %u for listening on IPv4: %s"
),
endpoint
.
port
(),
e
.
what
());
}
if
(
!
fListening
)
{
uiInterface
.
ThreadSafeMessageBox
(
strerr
,
""
,
CClientUIInterface
::
MSG_ERROR
);
StartShutdown
();
return
;
}
rpc_worker_group
=
new
boost
::
thread_group
();
for
(
int
i
=
0
;
i
<
GetArg
(
"-rpcthreads"
,
4
);
i
++
)
rpc_worker_group
->
create_thread
(
boost
::
bind
(
&
asio
::
io_service
::
run
,
rpc_io_service
));
}
void
StopRPCThreads
()
{
if
(
rpc_io_service
==
NULL
)
return
;
deadlineTimers
.
clear
();
rpc_io_service
->
stop
();
rpc_worker_group
->
join_all
();
delete
rpc_worker_group
;
rpc_worker_group
=
NULL
;
delete
rpc_ssl_context
;
rpc_ssl_context
=
NULL
;
delete
rpc_io_service
;
rpc_io_service
=
NULL
;
}
void
RPCRunHandler
(
const
boost
::
system
::
error_code
&
err
,
boost
::
function
<
void
(
void
)
>
func
)
{
if
(
!
err
)
func
();
}
void
RPCRunLater
(
const
std
::
string
&
name
,
boost
::
function
<
void
(
void
)
>
func
,
int64
nSeconds
)
{
assert
(
rpc_io_service
!=
NULL
);
if
(
deadlineTimers
.
count
(
name
)
==
0
)
{
deadlineTimers
.
insert
(
make_pair
(
name
,
boost
::
shared_ptr
<
deadline_timer
>
(
new
deadline_timer
(
*
rpc_io_service
))));
}
deadlineTimers
[
name
]
->
expires_from_now
(
posix_time
::
seconds
(
nSeconds
));
deadlineTimers
[
name
]
->
async_wait
(
boost
::
bind
(
RPCRunHandler
,
_1
,
func
));
}
class
JSONRequest
{
public
:
Value
id
;
string
strMethod
;
Array
params
;
JSONRequest
()
{
id
=
Value
::
null
;
}
void
parse
(
const
Value
&
valRequest
);
};
void
JSONRequest
::
parse
(
const
Value
&
valRequest
)
{
// Parse request
if
(
valRequest
.
type
()
!=
obj_type
)
throw
JSONRPCError
(
RPC_INVALID_REQUEST
,
"Invalid Request object"
);
const
Object
&
request
=
valRequest
.
get_obj
();
// Parse id now so errors from here on will have the id
id
=
find_value
(
request
,
"id"
);
// Parse method
Value
valMethod
=
find_value
(
request
,
"method"
);
if
(
valMethod
.
type
()
==
null_type
)
throw
JSONRPCError
(
RPC_INVALID_REQUEST
,
"Missing method"
);
if
(
valMethod
.
type
()
!=
str_type
)
throw
JSONRPCError
(
RPC_INVALID_REQUEST
,
"Method must be a string"
);
strMethod
=
valMethod
.
get_str
();
if
(
strMethod
!=
"getwork"
&&
strMethod
!=
"getblocktemplate"
)
printf
(
"ThreadRPCServer method=%s
\n
"
,
strMethod
.
c_str
());
// Parse params
Value
valParams
=
find_value
(
request
,
"params"
);
if
(
valParams
.
type
()
==
array_type
)
params
=
valParams
.
get_array
();
else
if
(
valParams
.
type
()
==
null_type
)
params
=
Array
();
else
throw
JSONRPCError
(
RPC_INVALID_REQUEST
,
"Params must be an array"
);
}
static
Object
JSONRPCExecOne
(
const
Value
&
req
)
{
Object
rpc_result
;
JSONRequest
jreq
;
try
{
jreq
.
parse
(
req
);
Value
result
=
tableRPC
.
execute
(
jreq
.
strMethod
,
jreq
.
params
);
rpc_result
=
JSONRPCReplyObj
(
result
,
Value
::
null
,
jreq
.
id
);
}
catch
(
Object
&
objError
)
{
rpc_result
=
JSONRPCReplyObj
(
Value
::
null
,
objError
,
jreq
.
id
);
}
catch
(
std
::
exception
&
e
)
{
rpc_result
=
JSONRPCReplyObj
(
Value
::
null
,
JSONRPCError
(
RPC_PARSE_ERROR
,
e
.
what
()),
jreq
.
id
);
}
return
rpc_result
;
}
static
string
JSONRPCExecBatch
(
const
Array
&
vReq
)
{
Array
ret
;
for
(
unsigned
int
reqIdx
=
0
;
reqIdx
<
vReq
.
size
();
reqIdx
++
)
ret
.
push_back
(
JSONRPCExecOne
(
vReq
[
reqIdx
]));
return
write_string
(
Value
(
ret
),
false
)
+
"
\n
"
;
}
void
ServiceConnection
(
AcceptedConnection
*
conn
)
{
bool
fRun
=
true
;
while
(
fRun
)
{
int
nProto
=
0
;
map
<
string
,
string
>
mapHeaders
;
string
strRequest
,
strMethod
,
strURI
;
// Read HTTP request line
if
(
!
ReadHTTPRequestLine
(
conn
->
stream
(),
nProto
,
strMethod
,
strURI
))
break
;
// Read HTTP message headers and body
ReadHTTPMessage
(
conn
->
stream
(),
mapHeaders
,
strRequest
,
nProto
);
if
(
strURI
!=
"/"
)
{
conn
->
stream
()
<<
HTTPReply
(
HTTP_NOT_FOUND
,
""
,
false
)
<<
std
::
flush
;
break
;
}
// Check authorization
if
(
mapHeaders
.
count
(
"authorization"
)
==
0
)
{
conn
->
stream
()
<<
HTTPReply
(
HTTP_UNAUTHORIZED
,
""
,
false
)
<<
std
::
flush
;
break
;
}
if
(
!
HTTPAuthorized
(
mapHeaders
))
{
printf
(
"ThreadRPCServer incorrect password attempt from %s
\n
"
,
conn
->
peer_address_to_string
().
c_str
());
/* Deter brute-forcing short passwords.
If this results in a DOS the user really
shouldn't have their RPC port exposed.*/
if
(
mapArgs
[
"-rpcpassword"
].
size
()
<
20
)
MilliSleep
(
250
);
conn
->
stream
()
<<
HTTPReply
(
HTTP_UNAUTHORIZED
,
""
,
false
)
<<
std
::
flush
;
break
;
}
if
(
mapHeaders
[
"connection"
]
==
"close"
)
fRun
=
false
;
JSONRequest
jreq
;
try
{
// Parse request
Value
valRequest
;
if
(
!
read_string
(
strRequest
,
valRequest
))
throw
JSONRPCError
(
RPC_PARSE_ERROR
,
"Parse error"
);
string
strReply
;
// singleton request
if
(
valRequest
.
type
()
==
obj_type
)
{
jreq
.
parse
(
valRequest
);
Value
result
=
tableRPC
.
execute
(
jreq
.
strMethod
,
jreq
.
params
);
// Send reply
strReply
=
JSONRPCReply
(
result
,
Value
::
null
,
jreq
.
id
);
// array of requests
}
else
if
(
valRequest
.
type
()
==
array_type
)
strReply
=
JSONRPCExecBatch
(
valRequest
.
get_array
());
else
throw
JSONRPCError
(
RPC_PARSE_ERROR
,
"Top-level object parse error"
);
conn
->
stream
()
<<
HTTPReply
(
HTTP_OK
,
strReply
,
fRun
)
<<
std
::
flush
;
}
catch
(
Object
&
objError
)
{
ErrorReply
(
conn
->
stream
(),
objError
,
jreq
.
id
);
break
;
}
catch
(
std
::
exception
&
e
)
{
ErrorReply
(
conn
->
stream
(),
JSONRPCError
(
RPC_PARSE_ERROR
,
e
.
what
()),
jreq
.
id
);
break
;
}
}
}
json_spirit
::
Value
CRPCTable
::
execute
(
const
std
::
string
&
strMethod
,
const
json_spirit
::
Array
&
params
)
const
{
// Find method
const
CRPCCommand
*
pcmd
=
tableRPC
[
strMethod
];
if
(
!
pcmd
)
throw
JSONRPCError
(
RPC_METHOD_NOT_FOUND
,
"Method not found"
);
// Observe safe mode
string
strWarning
=
GetWarnings
(
"rpc"
);
if
(
strWarning
!=
""
&&
!
GetBoolArg
(
"-disablesafemode"
)
&&
!
pcmd
->
okSafeMode
)
throw
JSONRPCError
(
RPC_FORBIDDEN_BY_SAFE_MODE
,
string
(
"Safe mode: "
)
+
strWarning
);
try
{
// Execute
Value
result
;
{
if
(
pcmd
->
threadSafe
)
result
=
pcmd
->
actor
(
params
,
false
);
else
{
LOCK2
(
cs_main
,
pwalletMain
->
cs_wallet
);
result
=
pcmd
->
actor
(
params
,
false
);
}
}
return
result
;
}
catch
(
std
::
exception
&
e
)
{
throw
JSONRPCError
(
RPC_MISC_ERROR
,
e
.
what
());
}
}
Object
CallRPC
(
const
string
&
strMethod
,
const
Array
&
params
)
{
if
(
mapArgs
[
"-rpcuser"
]
==
""
&&
mapArgs
[
"-rpcpassword"
]
==
""
)
throw
runtime_error
(
strprintf
(
_
(
"You must set rpcpassword=<password> in the configuration file:
\n
%s
\n
"
"If the file does not exist, create it with owner-readable-only file permissions."
),
GetConfigFile
().
string
().
c_str
()));
// Connect to localhost
bool
fUseSSL
=
GetBoolArg
(
"-rpcssl"
);
asio
::
io_service
io_service
;
ssl
::
context
context
(
io_service
,
ssl
::
context
::
sslv23
);
context
.
set_options
(
ssl
::
context
::
no_sslv2
);
asio
::
ssl
::
stream
<
asio
::
ip
::
tcp
::
socket
>
sslStream
(
io_service
,
context
);
SSLIOStreamDevice
<
asio
::
ip
::
tcp
>
d
(
sslStream
,
fUseSSL
);
iostreams
::
stream
<
SSLIOStreamDevice
<
asio
::
ip
::
tcp
>
>
stream
(
d
);
if
(
!
d
.
connect
(
GetArg
(
"-rpcconnect"
,
"127.0.0.1"
),
GetArg
(
"-rpcport"
,
itostr
(
GetDefaultRPCPort
()))))
throw
runtime_error
(
"couldn't connect to server"
);
// HTTP basic authentication
string
strUserPass64
=
EncodeBase64
(
mapArgs
[
"-rpcuser"
]
+
":"
+
mapArgs
[
"-rpcpassword"
]);
map
<
string
,
string
>
mapRequestHeaders
;
mapRequestHeaders
[
"Authorization"
]
=
string
(
"Basic "
)
+
strUserPass64
;
// Send request
string
strRequest
=
JSONRPCRequest
(
strMethod
,
params
,
1
);
string
strPost
=
HTTPPost
(
strRequest
,
mapRequestHeaders
);
stream
<<
strPost
<<
std
::
flush
;
// Receive HTTP reply status
int
nProto
=
0
;
int
nStatus
=
ReadHTTPStatus
(
stream
,
nProto
);
// Receive HTTP reply message headers and body
map
<
string
,
string
>
mapHeaders
;
string
strReply
;
ReadHTTPMessage
(
stream
,
mapHeaders
,
strReply
,
nProto
);
if
(
nStatus
==
HTTP_UNAUTHORIZED
)
throw
runtime_error
(
"incorrect rpcuser or rpcpassword (authorization failed)"
);
else
if
(
nStatus
>=
400
&&
nStatus
!=
HTTP_BAD_REQUEST
&&
nStatus
!=
HTTP_NOT_FOUND
&&
nStatus
!=
HTTP_INTERNAL_SERVER_ERROR
)
throw
runtime_error
(
strprintf
(
"server returned HTTP error %d"
,
nStatus
));
else
if
(
strReply
.
empty
())
throw
runtime_error
(
"no response from server"
);
// Parse reply
Value
valReply
;
if
(
!
read_string
(
strReply
,
valReply
))
throw
runtime_error
(
"couldn't parse reply from server"
);
const
Object
&
reply
=
valReply
.
get_obj
();
if
(
reply
.
empty
())
throw
runtime_error
(
"expected reply to have result, error and id properties"
);
return
reply
;
}
template
<
typename
T
>
void
ConvertTo
(
Value
&
value
,
bool
fAllowNull
=
false
)
{
if
(
fAllowNull
&&
value
.
type
()
==
null_type
)
return
;
if
(
value
.
type
()
==
str_type
)
{
// reinterpret string as unquoted json value
Value
value2
;
string
strJSON
=
value
.
get_str
();
if
(
!
read_string
(
strJSON
,
value2
))
throw
runtime_error
(
string
(
"Error parsing JSON:"
)
+
strJSON
);
ConvertTo
<
T
>
(
value2
,
fAllowNull
);
value
=
value2
;
}
else
{
value
=
value
.
get_value
<
T
>
();
}
}
// Convert strings to command-specific RPC representation
Array
RPCConvertValues
(
const
std
::
string
&
strMethod
,
const
std
::
vector
<
std
::
string
>
&
strParams
)
{
Array
params
;
BOOST_FOREACH
(
const
std
::
string
&
param
,
strParams
)
params
.
push_back
(
param
);
int
n
=
params
.
size
();
//
// Special case non-string parameter types
//
if
(
strMethod
==
"stop"
&&
n
>
0
)
ConvertTo
<
bool
>
(
params
[
0
]);
if
(
strMethod
==
"getaddednodeinfo"
&&
n
>
0
)
ConvertTo
<
bool
>
(
params
[
0
]);
if
(
strMethod
==
"setgenerate"
&&
n
>
0
)
ConvertTo
<
bool
>
(
params
[
0
]);
if
(
strMethod
==
"setgenerate"
&&
n
>
1
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
1
]);
if
(
strMethod
==
"sendtoaddress"
&&
n
>
1
)
ConvertTo
<
double
>
(
params
[
1
]);
if
(
strMethod
==
"settxfee"
&&
n
>
0
)
ConvertTo
<
double
>
(
params
[
0
]);
if
(
strMethod
==
"getreceivedbyaddress"
&&
n
>
1
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
1
]);
if
(
strMethod
==
"getreceivedbyaccount"
&&
n
>
1
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
1
]);
if
(
strMethod
==
"listreceivedbyaddress"
&&
n
>
0
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
0
]);
if
(
strMethod
==
"listreceivedbyaddress"
&&
n
>
1
)
ConvertTo
<
bool
>
(
params
[
1
]);
if
(
strMethod
==
"listreceivedbyaccount"
&&
n
>
0
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
0
]);
if
(
strMethod
==
"listreceivedbyaccount"
&&
n
>
1
)
ConvertTo
<
bool
>
(
params
[
1
]);
if
(
strMethod
==
"getbalance"
&&
n
>
1
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
1
]);
if
(
strMethod
==
"getblockhash"
&&
n
>
0
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
0
]);
if
(
strMethod
==
"move"
&&
n
>
2
)
ConvertTo
<
double
>
(
params
[
2
]);
if
(
strMethod
==
"move"
&&
n
>
3
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
3
]);
if
(
strMethod
==
"sendfrom"
&&
n
>
2
)
ConvertTo
<
double
>
(
params
[
2
]);
if
(
strMethod
==
"sendfrom"
&&
n
>
3
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
3
]);
if
(
strMethod
==
"listtransactions"
&&
n
>
1
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
1
]);
if
(
strMethod
==
"listtransactions"
&&
n
>
2
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
2
]);
if
(
strMethod
==
"listaccounts"
&&
n
>
0
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
0
]);
if
(
strMethod
==
"walletpassphrase"
&&
n
>
1
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
1
]);
if
(
strMethod
==
"getblocktemplate"
&&
n
>
0
)
ConvertTo
<
Object
>
(
params
[
0
]);
if
(
strMethod
==
"listsinceblock"
&&
n
>
1
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
1
]);
if
(
strMethod
==
"sendmany"
&&
n
>
1
)
ConvertTo
<
Object
>
(
params
[
1
]);
if
(
strMethod
==
"sendmany"
&&
n
>
2
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
2
]);
if
(
strMethod
==
"addmultisigaddress"
&&
n
>
0
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
0
]);
if
(
strMethod
==
"addmultisigaddress"
&&
n
>
1
)
ConvertTo
<
Array
>
(
params
[
1
]);
if
(
strMethod
==
"createmultisig"
&&
n
>
0
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
0
]);
if
(
strMethod
==
"createmultisig"
&&
n
>
1
)
ConvertTo
<
Array
>
(
params
[
1
]);
if
(
strMethod
==
"listunspent"
&&
n
>
0
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
0
]);
if
(
strMethod
==
"listunspent"
&&
n
>
1
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
1
]);
if
(
strMethod
==
"listunspent"
&&
n
>
2
)
ConvertTo
<
Array
>
(
params
[
2
]);
if
(
strMethod
==
"getrawtransaction"
&&
n
>
1
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
1
]);
if
(
strMethod
==
"createrawtransaction"
&&
n
>
0
)
ConvertTo
<
Array
>
(
params
[
0
]);
if
(
strMethod
==
"createrawtransaction"
&&
n
>
1
)
ConvertTo
<
Object
>
(
params
[
1
]);
if
(
strMethod
==
"signrawtransaction"
&&
n
>
1
)
ConvertTo
<
Array
>
(
params
[
1
],
true
);
if
(
strMethod
==
"signrawtransaction"
&&
n
>
2
)
ConvertTo
<
Array
>
(
params
[
2
],
true
);
if
(
strMethod
==
"gettxout"
&&
n
>
1
)
ConvertTo
<
boost
::
int64_t
>
(
params
[
1
]);
if
(
strMethod
==
"gettxout"
&&
n
>
2
)
ConvertTo
<
bool
>
(
params
[
2
]);
if
(
strMethod
==
"lockunspent"
&&
n
>
0
)
ConvertTo
<
bool
>
(
params
[
0
]);
if
(
strMethod
==
"lockunspent"
&&
n
>
1
)
ConvertTo
<
Array
>
(
params
[
1
]);
if
(
strMethod
==
"importprivkey"
&&
n
>
2
)
ConvertTo
<
bool
>
(
params
[
2
]);
return
params
;
}
int
CommandLineRPC
(
int
argc
,
char
*
argv
[])
{
string
strPrint
;
int
nRet
=
0
;
try
{
// Skip switches
while
(
argc
>
1
&&
IsSwitchChar
(
argv
[
1
][
0
]))
{
argc
--
;
argv
++
;
}
// Method
if
(
argc
<
2
)
throw
runtime_error
(
"too few parameters"
);
string
strMethod
=
argv
[
1
];
// Parameters default to strings
std
::
vector
<
std
::
string
>
strParams
(
&
argv
[
2
],
&
argv
[
argc
]);
Array
params
=
RPCConvertValues
(
strMethod
,
strParams
);
// Execute
Object
reply
=
CallRPC
(
strMethod
,
params
);
// Parse reply
const
Value
&
result
=
find_value
(
reply
,
"result"
);
const
Value
&
error
=
find_value
(
reply
,
"error"
);
if
(
error
.
type
()
!=
null_type
)
{
// Error
strPrint
=
"error: "
+
write_string
(
error
,
false
);
int
code
=
find_value
(
error
.
get_obj
(),
"code"
).
get_int
();
nRet
=
abs
(
code
);
}
else
{
// Result
if
(
result
.
type
()
==
null_type
)
strPrint
=
""
;
else
if
(
result
.
type
()
==
str_type
)
strPrint
=
result
.
get_str
();
else
strPrint
=
write_string
(
result
,
true
);
}
}
catch
(
boost
::
thread_interrupted
)
{
throw
;
}
catch
(
std
::
exception
&
e
)
{
strPrint
=
string
(
"error: "
)
+
e
.
what
();
nRet
=
87
;
}
catch
(...)
{
PrintException
(
NULL
,
"CommandLineRPC()"
);
}
if
(
strPrint
!=
""
)
{
fprintf
((
nRet
==
0
?
stdout
:
stderr
),
"%s
\n
"
,
strPrint
.
c_str
());
}
return
nRet
;
}
#ifdef TEST
int
main
(
int
argc
,
char
*
argv
[])
{
#ifdef _MSC_VER
// Turn off Microsoft heap dump noise
_CrtSetReportMode
(
_CRT_WARN
,
_CRTDBG_MODE_FILE
);
_CrtSetReportFile
(
_CRT_WARN
,
CreateFile
(
"NUL"
,
GENERIC_WRITE
,
0
,
NULL
,
OPEN_EXISTING
,
0
,
0
));
#endif
setbuf
(
stdin
,
NULL
);
setbuf
(
stdout
,
NULL
);
setbuf
(
stderr
,
NULL
);
try
{
if
(
argc
>=
2
&&
string
(
argv
[
1
])
==
"-server"
)
{
printf
(
"server ready
\n
"
);
ThreadRPCServer
(
NULL
);
}
else
{
return
CommandLineRPC
(
argc
,
argv
);
}
}
catch
(
boost
::
thread_interrupted
)
{
throw
;
}
catch
(
std
::
exception
&
e
)
{
PrintException
(
&
e
,
"main()"
);
}
catch
(...)
{
PrintException
(
NULL
,
"main()"
);
}
return
0
;
}
#endif
const
CRPCTable
tableRPC
;
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Mon, Nov 25, 07:58 (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4577189
Default Alt Text
bitcoinrpc.cpp (46 KB)
Attached To
rSTAGING Bitcoin ABC staging
Event Timeline
Log In to Comment