Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
whqwjb
go-ethereum
提交
8627680e
G
go-ethereum
项目概览
whqwjb
/
go-ethereum
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
go-ethereum
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
8627680e
编写于
4月 12, 2016
作者:
J
Jeffrey Wilcke
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #2359 from bas-vk/rpc-optional-args
rpc: several fixes and support for optional arguments
上级
934f587b
aa9fff3e
变更
23
展开全部
隐藏空白更改
内联
并排
Showing
23 changed file
with
3431 addition
and
639 deletion
+3431
-639
cmd/geth/js.go
cmd/geth/js.go
+1
-1
cmd/geth/main.go
cmd/geth/main.go
+1
-1
cmd/geth/usage.go
cmd/geth/usage.go
+1
-1
cmd/utils/flags.go
cmd/utils/flags.go
+4
-4
cmd/utils/jeth.go
cmd/utils/jeth.go
+76
-101
eth/api.go
eth/api.go
+35
-25
eth/backend.go
eth/backend.go
+6
-3
jsre/ethereum_js.go
jsre/ethereum_js.go
+2983
-133
node/api.go
node/api.go
+43
-4
node/config.go
node/config.go
+2
-2
node/node.go
node/node.go
+14
-6
node/node_test.go
node/node_test.go
+1
-1
rpc/doc.go
rpc/doc.go
+13
-1
rpc/http.go
rpc/http.go
+6
-14
rpc/ipc_windows.go
rpc/ipc_windows.go
+1
-1
rpc/javascript.go
rpc/javascript.go
+17
-51
rpc/json.go
rpc/json.go
+85
-80
rpc/json_test.go
rpc/json_test.go
+91
-2
rpc/server_test.go
rpc/server_test.go
+39
-159
rpc/types.go
rpc/types.go
+7
-44
rpc/utils.go
rpc/utils.go
+1
-1
rpc/websocket.go
rpc/websocket.go
+2
-2
whisper/api.go
whisper/api.go
+2
-2
未找到文件。
cmd/geth/js.go
浏览文件 @
8627680e
...
...
@@ -238,7 +238,7 @@ func (js *jsre) apiBindings() error {
}
// load only supported API's in javascript runtime
shortcuts
:=
"var eth = web3.eth; "
shortcuts
:=
"var eth = web3.eth;
var personal = web3.personal;
"
for
_
,
apiName
:=
range
apiNames
{
if
apiName
==
"web3"
||
apiName
==
"rpc"
{
continue
// manually mapped or ignore
...
...
cmd/geth/main.go
浏览文件 @
8627680e
...
...
@@ -326,7 +326,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils
.
WSListenAddrFlag
,
utils
.
WSPortFlag
,
utils
.
WSApiFlag
,
utils
.
WSAllowed
Doma
insFlag
,
utils
.
WSAllowed
Orig
insFlag
,
utils
.
IPCDisabledFlag
,
utils
.
IPCApiFlag
,
utils
.
IPCPathFlag
,
...
...
cmd/geth/usage.go
浏览文件 @
8627680e
...
...
@@ -94,7 +94,7 @@ var AppHelpFlagGroups = []flagGroup{
utils
.
WSListenAddrFlag
,
utils
.
WSPortFlag
,
utils
.
WSApiFlag
,
utils
.
WSAllowed
Doma
insFlag
,
utils
.
WSAllowed
Orig
insFlag
,
utils
.
IPCDisabledFlag
,
utils
.
IPCApiFlag
,
utils
.
IPCPathFlag
,
...
...
cmd/utils/flags.go
浏览文件 @
8627680e
...
...
@@ -287,9 +287,9 @@ var (
Usage
:
"API's offered over the WS-RPC interface"
,
Value
:
rpc
.
DefaultHTTPApis
,
}
WSAllowed
Doma
insFlag
=
cli
.
StringFlag
{
Name
:
"ws
doma
ins"
,
Usage
:
"
Domains from which to accept websockets requests (can be spoofed)
"
,
WSAllowed
Orig
insFlag
=
cli
.
StringFlag
{
Name
:
"ws
orig
ins"
,
Usage
:
"
Origins from which to accept websockets requests
"
,
Value
:
""
,
}
ExecFlag
=
cli
.
StringFlag
{
...
...
@@ -659,7 +659,7 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node.
HTTPModules
:
strings
.
Split
(
ctx
.
GlobalString
(
RPCApiFlag
.
Name
),
","
),
WSHost
:
MakeWSRpcHost
(
ctx
),
WSPort
:
ctx
.
GlobalInt
(
WSPortFlag
.
Name
),
WS
Domains
:
ctx
.
GlobalString
(
WSAllowedDoma
insFlag
.
Name
),
WS
Origins
:
ctx
.
GlobalString
(
WSAllowedOrig
insFlag
.
Name
),
WSModules
:
strings
.
Split
(
ctx
.
GlobalString
(
WSApiFlag
.
Name
),
","
),
}
// Configure the Ethereum service
...
...
cmd/utils/jeth.go
浏览文件 @
8627680e
...
...
@@ -37,7 +37,8 @@ func NewJeth(re *jsre.JSRE, client rpc.Client) *Jeth {
return
&
Jeth
{
re
,
client
}
}
func
(
self
*
Jeth
)
err
(
call
otto
.
FunctionCall
,
code
int
,
msg
string
,
id
*
int64
)
(
response
otto
.
Value
)
{
// err returns an error object for the given error code and message.
func
(
self
*
Jeth
)
err
(
call
otto
.
FunctionCall
,
code
int
,
msg
string
,
id
interface
{})
(
response
otto
.
Value
)
{
m
:=
rpc
.
JSONErrResponse
{
Version
:
"2.0"
,
Id
:
id
,
...
...
@@ -56,44 +57,50 @@ func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id *int64) (
return
res
}
// UnlockAccount asks the user for the password and than executes the jeth.UnlockAccount callback in the jsre
// UnlockAccount asks the user for the password and than executes the jeth.UnlockAccount callback in the jsre.
// It will need the public address for the account to unlock as first argument.
// The second argument is an optional string with the password. If not given the user is prompted for the password.
// The third argument is an optional integer which specifies for how long the account will be unlocked (in seconds).
func
(
self
*
Jeth
)
UnlockAccount
(
call
otto
.
FunctionCall
)
(
response
otto
.
Value
)
{
var
account
,
passwd
string
timeout
:=
int64
(
300
)
var
ok
bool
var
account
,
passwd
otto
.
Value
duration
:=
otto
.
NullValue
()
if
len
(
call
.
ArgumentList
)
==
0
{
fmt
.
Println
(
"
expected address of
account to unlock"
)
if
!
call
.
Argument
(
0
)
.
IsString
()
{
fmt
.
Println
(
"
first argument must be the
account to unlock"
)
return
otto
.
FalseValue
()
}
if
len
(
call
.
ArgumentList
)
>=
1
{
if
accountExport
,
err
:=
call
.
Argument
(
0
)
.
Export
();
err
==
nil
{
if
account
,
ok
=
accountExport
.
(
string
);
ok
{
if
len
(
call
.
ArgumentList
)
==
1
{
fmt
.
Printf
(
"Unlock account %s
\n
"
,
account
)
passwd
,
err
=
PromptPassword
(
"Passphrase: "
,
true
)
if
err
!=
nil
{
return
otto
.
FalseValue
()
}
}
}
account
=
call
.
Argument
(
0
)
// if password is not given or as null value -> ask user for password
if
call
.
Argument
(
1
)
.
IsUndefined
()
||
call
.
Argument
(
1
)
.
IsNull
()
{
fmt
.
Printf
(
"Unlock account %s
\n
"
,
account
)
if
password
,
err
:=
PromptPassword
(
"Passphrase: "
,
true
);
err
==
nil
{
passwd
,
_
=
otto
.
ToValue
(
password
)
}
else
{
throwJSExeception
(
err
.
Error
())
}
}
if
len
(
call
.
ArgumentList
)
>=
2
{
if
passwdExport
,
err
:=
call
.
Argument
(
1
)
.
Export
();
err
==
nil
{
passwd
,
_
=
passwdExport
.
(
string
)
}
else
{
if
!
call
.
Argument
(
1
)
.
IsString
()
{
throwJSExeception
(
"password must be a string"
)
}
passwd
=
call
.
Argument
(
1
)
}
if
len
(
call
.
ArgumentList
)
>=
3
{
if
timeoutExport
,
err
:=
call
.
Argument
(
2
)
.
Export
();
err
==
nil
{
timeout
,
_
=
timeoutExport
.
(
int64
)
// third argument is the duration how long the account must be unlocked.
// verify that its a number.
if
call
.
Argument
(
2
)
.
IsDefined
()
&&
!
call
.
Argument
(
2
)
.
IsNull
()
{
if
!
call
.
Argument
(
2
)
.
IsNumber
()
{
throwJSExeception
(
"unlock duration must be a number"
)
}
duration
=
call
.
Argument
(
2
)
}
if
val
,
err
:=
call
.
Otto
.
Call
(
"jeth.unlockAccount"
,
nil
,
account
,
passwd
,
timeout
);
err
==
nil
{
// jeth.unlockAccount will send the request to the backend.
if
val
,
err
:=
call
.
Otto
.
Call
(
"jeth.unlockAccount"
,
nil
,
account
,
passwd
,
duration
);
err
==
nil
{
return
val
}
else
{
throwJSExeception
(
err
.
Error
())
}
return
otto
.
FalseValue
()
...
...
@@ -134,19 +141,31 @@ func (self *Jeth) NewAccount(call otto.FunctionCall) (response otto.Value) {
return
otto
.
FalseValue
()
}
// Send will serialize the first argument, send it to the node and returns the response.
func
(
self
*
Jeth
)
Send
(
call
otto
.
FunctionCall
)
(
response
otto
.
Value
)
{
reqif
,
err
:=
call
.
Argument
(
0
)
.
Export
()
// verify we got a batch request (array) or a single request (object)
ro
:=
call
.
Argument
(
0
)
.
Object
()
if
ro
==
nil
||
(
ro
.
Class
()
!=
"Array"
&&
ro
.
Class
()
!=
"Object"
)
{
throwJSExeception
(
"Internal Error: request must be an object or array"
)
}
// convert otto vm arguments to go values by JSON serialising and parsing.
data
,
err
:=
call
.
Otto
.
Call
(
"JSON.stringify"
,
nil
,
ro
)
if
err
!=
nil
{
return
self
.
err
(
call
,
-
32700
,
err
.
Error
(),
nil
)
throwJSExeception
(
err
.
Error
()
)
}
jsonreq
,
err
:=
json
.
Marshal
(
reqif
)
jsonreq
,
_
:=
data
.
ToString
()
// parse arguments to JSON rpc requests, either to an array (batch) or to a single request.
var
reqs
[]
rpc
.
JSONRequest
batch
:=
true
err
=
json
.
Unmarshal
(
jsonreq
,
&
reqs
)
if
err
!=
nil
{
if
err
=
json
.
Unmarshal
([]
byte
(
jsonreq
),
&
reqs
);
err
!=
nil
{
// single request?
reqs
=
make
([]
rpc
.
JSONRequest
,
1
)
err
=
json
.
Unmarshal
(
jsonreq
,
&
reqs
[
0
])
if
err
=
json
.
Unmarshal
([]
byte
(
jsonreq
),
&
reqs
[
0
]);
err
!=
nil
{
throwJSExeception
(
"invalid request"
)
}
batch
=
false
}
...
...
@@ -154,47 +173,50 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
call
.
Otto
.
Run
(
"var ret_response = new Array(response_len);"
)
for
i
,
req
:=
range
reqs
{
err
:=
self
.
client
.
Send
(
&
req
)
if
err
!=
nil
{
if
err
:=
self
.
client
.
Send
(
&
req
);
err
!=
nil
{
return
self
.
err
(
call
,
-
32603
,
err
.
Error
(),
req
.
Id
)
}
result
:=
make
(
map
[
string
]
interface
{})
err
=
self
.
client
.
Recv
(
&
result
)
if
err
!=
nil
{
if
err
=
self
.
client
.
Recv
(
&
result
);
err
!=
nil
{
return
self
.
err
(
call
,
-
32603
,
err
.
Error
(),
req
.
Id
)
}
_
,
isSuccessResponse
:=
result
[
"result"
]
_
,
isErrorResponse
:=
result
[
"error"
]
if
!
isSuccessResponse
&&
!
isErrorResponse
{
return
self
.
err
(
call
,
-
32603
,
fmt
.
Sprintf
(
"Invalid response"
),
new
(
int64
))
}
id
,
_
:=
result
[
"id"
]
call
.
Otto
.
Set
(
"ret_id"
,
id
)
jsonver
,
_
:=
result
[
"jsonrpc"
]
call
.
Otto
.
Set
(
"ret_id"
,
id
)
call
.
Otto
.
Set
(
"ret_jsonrpc"
,
jsonver
)
call
.
Otto
.
Set
(
"response_idx"
,
i
)
var
payload
[]
byte
if
isSuccessResponse
{
payload
,
_
=
json
.
Marshal
(
result
[
"result"
])
}
else
if
isErrorResponse
{
payload
,
_
=
json
.
Marshal
(
result
[
"error"
])
// call was successful
if
res
,
ok
:=
result
[
"result"
];
ok
{
payload
,
_
:=
json
.
Marshal
(
res
)
call
.
Otto
.
Set
(
"ret_result"
,
string
(
payload
))
response
,
err
=
call
.
Otto
.
Run
(
`
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) };
`
)
continue
}
call
.
Otto
.
Set
(
"ret_result"
,
string
(
payload
))
call
.
Otto
.
Set
(
"response_idx"
,
i
)
response
,
err
=
call
.
Otto
.
Run
(
`
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) };
`
)
// request returned an error
if
res
,
ok
:=
result
[
"error"
];
ok
{
payload
,
_
:=
json
.
Marshal
(
res
)
call
.
Otto
.
Set
(
"ret_result"
,
string
(
payload
))
response
,
err
=
call
.
Otto
.
Run
(
`
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, error: JSON.parse(ret_result) };
`
)
continue
}
return
self
.
err
(
call
,
-
32603
,
fmt
.
Sprintf
(
"Invalid response"
),
new
(
int64
))
}
if
!
batch
{
call
.
Otto
.
Run
(
"ret_response = ret_response[0];"
)
}
// if a callback was given execute it.
if
call
.
Argument
(
1
)
.
IsObject
()
{
call
.
Otto
.
Set
(
"callback"
,
call
.
Argument
(
1
))
call
.
Otto
.
Run
(
`
...
...
@@ -207,53 +229,6 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
return
}
/*
// handleRequest will handle user agent requests by interacting with the user and sending
// the user response back to the geth service
func (self *Jeth) handleRequest(req *shared.Request) bool {
var err error
var args []interface{}
if err = json.Unmarshal(req.Params, &args); err != nil {
glog.V(logger.Info).Infof("Unable to parse agent request - %v\n", err)
return false
}
switch req.Method {
case useragent.AskPasswordMethod:
return self.askPassword(req.Id, req.Jsonrpc, args)
case useragent.ConfirmTransactionMethod:
return self.confirmTransaction(req.Id, req.Jsonrpc, args)
}
return false
}
// askPassword will ask the user to supply the password for a given account
func (self *Jeth) askPassword(id interface{}, jsonrpc string, args []interface{}) bool {
var err error
var passwd string
if len(args) >= 1 {
if account, ok := args[0].(string); ok {
fmt.Printf("Unlock account %s\n", account)
} else {
return false
}
}
passwd, err = PromptPassword("Passphrase: ", true)
if err = self.client.Send(shared.NewRpcResponse(id, jsonrpc, passwd, err)); err != nil {
glog.V(logger.Info).Infof("Unable to send user agent ask password response - %v\n", err)
}
return err == nil
}
func (self *Jeth) confirmTransaction(id interface{}, jsonrpc string, args []interface{}) bool {
// Accept all tx which are send from this console
return self.client.Send(shared.NewRpcResponse(id, jsonrpc, true, nil)) == nil
}
*/
// throwJSExeception panics on an otto value, the Otto VM will then throw msg as a javascript error.
func
throwJSExeception
(
msg
interface
{})
otto
.
Value
{
p
,
_
:=
otto
.
ToValue
(
msg
)
...
...
eth/api.go
浏览文件 @
8627680e
...
...
@@ -25,6 +25,7 @@ import (
"io/ioutil"
"math/big"
"os"
"runtime"
"sync"
"time"
...
...
@@ -96,8 +97,8 @@ type PublicEthereumAPI struct {
}
// NewPublicEthereumAPI creates a new Ethereum protocol API.
func
NewPublicEthereumAPI
(
e
*
Ethereum
)
*
PublicEthereumAPI
{
return
&
PublicEthereumAPI
{
e
,
NewGasPriceOracle
(
e
)
}
func
NewPublicEthereumAPI
(
e
*
Ethereum
,
gpo
*
GasPriceOracle
)
*
PublicEthereumAPI
{
return
&
PublicEthereumAPI
{
e
,
gpo
}
}
// GasPrice returns a suggestion for a gas price.
...
...
@@ -108,11 +109,7 @@ func (s *PublicEthereumAPI) GasPrice() *big.Int {
// GetCompilers returns the collection of available smart contract compilers
func
(
s
*
PublicEthereumAPI
)
GetCompilers
()
([]
string
,
error
)
{
solc
,
err
:=
s
.
e
.
Solc
()
if
err
!=
nil
{
return
nil
,
err
}
if
solc
!=
nil
{
if
err
!=
nil
&&
solc
!=
nil
{
return
[]
string
{
"Solidity"
},
nil
}
...
...
@@ -240,9 +237,15 @@ func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI {
return
&
PrivateMinerAPI
{
e
:
e
}
}
// Start the miner with the given number of threads
func
(
s
*
PrivateMinerAPI
)
Start
(
threads
rpc
.
HexNumber
)
(
bool
,
error
)
{
// Start the miner with the given number of threads. If threads is nil the number of
// workers started is equal to the number of logical CPU's that are usable by this process.
func
(
s
*
PrivateMinerAPI
)
Start
(
threads
*
rpc
.
HexNumber
)
(
bool
,
error
)
{
s
.
e
.
StartAutoDAG
()
if
threads
==
nil
{
threads
=
rpc
.
NewHexNumber
(
runtime
.
NumCPU
())
}
err
:=
s
.
e
.
StartMining
(
threads
.
Int
(),
""
)
if
err
==
nil
{
return
true
,
nil
...
...
@@ -265,7 +268,7 @@ func (s *PrivateMinerAPI) SetExtra(extra string) (bool, error) {
}
// SetGasPrice sets the minimum accepted gas price for the miner.
func
(
s
*
PrivateMinerAPI
)
SetGasPrice
(
gasPrice
rpc
.
Number
)
bool
{
func
(
s
*
PrivateMinerAPI
)
SetGasPrice
(
gasPrice
rpc
.
Hex
Number
)
bool
{
s
.
e
.
Miner
()
.
SetGasPrice
(
gasPrice
.
BigInt
())
return
true
}
...
...
@@ -440,10 +443,15 @@ func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error)
return
common
.
Address
{},
err
}
// UnlockAccount will unlock the account associated with the given address with the given password for duration seconds.
// It returns an indication if the action was successful.
func
(
s
*
PrivateAccountAPI
)
UnlockAccount
(
addr
common
.
Address
,
password
string
,
duration
int
)
bool
{
if
err
:=
s
.
am
.
TimedUnlock
(
addr
,
password
,
time
.
Duration
(
duration
)
*
time
.
Second
);
err
!=
nil
{
// UnlockAccount will unlock the account associated with the given address with
// the given password for duration seconds. If duration is nil it will use a
// default of 300 seconds. It returns an indication if the account was unlocked.
func
(
s
*
PrivateAccountAPI
)
UnlockAccount
(
addr
common
.
Address
,
password
string
,
duration
*
rpc
.
HexNumber
)
bool
{
if
duration
==
nil
{
duration
=
rpc
.
NewHexNumber
(
300
)
}
if
err
:=
s
.
am
.
TimedUnlock
(
addr
,
password
,
time
.
Duration
(
duration
.
Int
())
*
time
.
Second
);
err
!=
nil
{
glog
.
V
(
logger
.
Info
)
.
Infof
(
"%v
\n
"
,
err
)
return
false
}
...
...
@@ -458,7 +466,7 @@ func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
// PublicBlockChainAPI provides an API to access the Ethereum blockchain.
// It offers only methods that operate on public data that is freely available to anyone.
type
PublicBlockChainAPI
struct
{
config
*
core
.
ChainConfig
config
*
core
.
ChainConfig
bc
*
core
.
BlockChain
chainDb
ethdb
.
Database
eventMux
*
event
.
TypeMux
...
...
@@ -466,10 +474,11 @@ type PublicBlockChainAPI struct {
newBlockSubscriptions
map
[
string
]
func
(
core
.
ChainEvent
)
error
// callbacks for new block subscriptions
am
*
accounts
.
Manager
miner
*
miner
.
Miner
gpo
*
GasPriceOracle
}
// NewPublicBlockChainAPI creates a new Etheruem blockchain API.
func
NewPublicBlockChainAPI
(
config
*
core
.
ChainConfig
,
bc
*
core
.
BlockChain
,
m
*
miner
.
Miner
,
chainDb
ethdb
.
Database
,
eventMux
*
event
.
TypeMux
,
am
*
accounts
.
Manager
)
*
PublicBlockChainAPI
{
func
NewPublicBlockChainAPI
(
config
*
core
.
ChainConfig
,
bc
*
core
.
BlockChain
,
m
*
miner
.
Miner
,
chainDb
ethdb
.
Database
,
gpo
*
GasPriceOracle
,
eventMux
*
event
.
TypeMux
,
am
*
accounts
.
Manager
)
*
PublicBlockChainAPI
{
api
:=
&
PublicBlockChainAPI
{
config
:
config
,
bc
:
bc
,
...
...
@@ -478,6 +487,7 @@ func NewPublicBlockChainAPI(config *core.ChainConfig, bc *core.BlockChain, m *mi
eventMux
:
eventMux
,
am
:
am
,
newBlockSubscriptions
:
make
(
map
[
string
]
func
(
core
.
ChainEvent
)
error
),
gpo
:
gpo
,
}
go
api
.
subscriptionLoop
()
...
...
@@ -674,8 +684,8 @@ func (m callmsg) Data() []byte { return m.data }
type
CallArgs
struct
{
From
common
.
Address
`json:"from"`
To
*
common
.
Address
`json:"to"`
Gas
rpc
.
HexNumber
`json:"gas"`
GasPrice
rpc
.
HexNumber
`json:"gasPrice"`
Gas
*
rpc
.
HexNumber
`json:"gas"`
GasPrice
*
rpc
.
HexNumber
`json:"gasPrice"`
Value
rpc
.
HexNumber
`json:"value"`
Data
string
`json:"data"`
}
...
...
@@ -711,11 +721,11 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st
value
:
args
.
Value
.
BigInt
(),
data
:
common
.
FromHex
(
args
.
Data
),
}
if
msg
.
gas
.
Cmp
(
common
.
Big0
)
==
0
{
if
msg
.
gas
==
nil
{
msg
.
gas
=
big
.
NewInt
(
50000000
)
}
if
msg
.
gasPrice
.
Cmp
(
common
.
Big0
)
==
0
{
msg
.
gasPrice
=
new
(
big
.
Int
)
.
Mul
(
big
.
NewInt
(
50
),
common
.
Shannon
)
if
msg
.
gasPrice
==
nil
{
msg
.
gasPrice
=
s
.
gpo
.
SuggestPrice
(
)
}
// Execute the call and return
...
...
@@ -882,10 +892,10 @@ type PublicTransactionPoolAPI struct {
}
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
func
NewPublicTransactionPoolAPI
(
e
*
Ethereum
)
*
PublicTransactionPoolAPI
{
func
NewPublicTransactionPoolAPI
(
e
*
Ethereum
,
gpo
*
GasPriceOracle
)
*
PublicTransactionPoolAPI
{
api
:=
&
PublicTransactionPoolAPI
{
eventMux
:
e
.
EventMux
(),
gpo
:
NewGasPriceOracle
(
e
)
,
gpo
:
gpo
,
chainDb
:
e
.
ChainDb
(),
bc
:
e
.
BlockChain
(),
am
:
e
.
AccountManager
(),
...
...
@@ -1306,7 +1316,7 @@ func newTx(t *types.Transaction) *Tx {
// SignTransaction will sign the given transaction with the from account.
// The node needs to have the private key of the account corresponding with
// the given from address and it needs to be unlocked.
func
(
s
*
PublicTransactionPoolAPI
)
SignTransaction
(
args
*
SignTransactionArgs
)
(
*
SignTransactionResult
,
error
)
{
func
(
s
*
PublicTransactionPoolAPI
)
SignTransaction
(
args
SignTransactionArgs
)
(
*
SignTransactionResult
,
error
)
{
if
args
.
Gas
==
nil
{
args
.
Gas
=
rpc
.
NewHexNumber
(
defaultGas
)
}
...
...
@@ -1397,7 +1407,7 @@ func (s *PublicTransactionPoolAPI) NewPendingTransactions(ctx context.Context) (
// Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the
// pool and reinsert it with the new gas price and limit.
func
(
s
*
PublicTransactionPoolAPI
)
Resend
(
tx
*
Tx
,
gasPrice
,
gasLimit
*
rpc
.
HexNumber
)
(
common
.
Hash
,
error
)
{
func
(
s
*
PublicTransactionPoolAPI
)
Resend
(
tx
Tx
,
gasPrice
,
gasLimit
*
rpc
.
HexNumber
)
(
common
.
Hash
,
error
)
{
pending
:=
s
.
txPool
.
GetTransactions
()
for
_
,
p
:=
range
pending
{
...
...
eth/backend.go
浏览文件 @
8627680e
...
...
@@ -272,11 +272,14 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
// APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func
(
s
*
Ethereum
)
APIs
()
[]
rpc
.
API
{
// share gas price oracle in API's
gpo
:=
NewGasPriceOracle
(
s
)
return
[]
rpc
.
API
{
{
Namespace
:
"eth"
,
Version
:
"1.0"
,
Service
:
NewPublicEthereumAPI
(
s
),
Service
:
NewPublicEthereumAPI
(
s
,
gpo
),
Public
:
true
,
},
{
Namespace
:
"eth"
,
...
...
@@ -291,12 +294,12 @@ func (s *Ethereum) APIs() []rpc.API {
},
{
Namespace
:
"eth"
,
Version
:
"1.0"
,
Service
:
NewPublicBlockChainAPI
(
s
.
chainConfig
,
s
.
BlockChain
(),
s
.
Miner
(),
s
.
ChainDb
(),
s
.
EventMux
(),
s
.
AccountManager
()),
Service
:
NewPublicBlockChainAPI
(
s
.
chainConfig
,
s
.
BlockChain
(),
s
.
Miner
(),
s
.
ChainDb
(),
gpo
,
s
.
EventMux
(),
s
.
AccountManager
()),
Public
:
true
,
},
{
Namespace
:
"eth"
,
Version
:
"1.0"
,
Service
:
NewPublicTransactionPoolAPI
(
s
),
Service
:
NewPublicTransactionPoolAPI
(
s
,
gpo
),
Public
:
true
,
},
{
Namespace
:
"eth"
,
...
...
jsre/ethereum_js.go
浏览文件 @
8627680e
此差异已折叠。
点击以展开。
node/api.go
浏览文件 @
8627680e
...
...
@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/rpc"
"github.com/rcrowley/go-metrics"
)
...
...
@@ -58,14 +59,33 @@ func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) {
}
// StartRPC starts the HTTP RPC API server.
func
(
api
*
PrivateAdminAPI
)
StartRPC
(
host
string
,
port
int
,
cors
string
,
apis
string
)
(
bool
,
error
)
{
func
(
api
*
PrivateAdminAPI
)
StartRPC
(
host
*
string
,
port
*
rpc
.
HexNumber
,
cors
*
string
,
apis
*
string
)
(
bool
,
error
)
{
api
.
node
.
lock
.
Lock
()
defer
api
.
node
.
lock
.
Unlock
()
if
api
.
node
.
httpHandler
!=
nil
{
return
false
,
fmt
.
Errorf
(
"HTTP RPC already running on %s"
,
api
.
node
.
httpEndpoint
)
}
if
err
:=
api
.
node
.
startHTTP
(
fmt
.
Sprintf
(
"%s:%d"
,
host
,
port
),
api
.
node
.
rpcAPIs
,
strings
.
Split
(
apis
,
","
),
cors
);
err
!=
nil
{
if
host
==
nil
{
host
=
&
api
.
node
.
httpHost
}
if
port
==
nil
{
port
=
rpc
.
NewHexNumber
(
api
.
node
.
httpPort
)
}
if
cors
==
nil
{
cors
=
&
api
.
node
.
httpCors
}
modules
:=
api
.
node
.
httpWhitelist
if
apis
!=
nil
{
modules
=
nil
for
_
,
m
:=
range
strings
.
Split
(
*
apis
,
","
)
{
modules
=
append
(
modules
,
strings
.
TrimSpace
(
m
))
}
}
if
err
:=
api
.
node
.
startHTTP
(
fmt
.
Sprintf
(
"%s:%d"
,
*
host
,
port
.
Int
()),
api
.
node
.
rpcAPIs
,
modules
,
*
cors
);
err
!=
nil
{
return
false
,
err
}
return
true
,
nil
...
...
@@ -84,14 +104,33 @@ func (api *PrivateAdminAPI) StopRPC() (bool, error) {
}
// StartWS starts the websocket RPC API server.
func
(
api
*
PrivateAdminAPI
)
StartWS
(
host
string
,
port
int
,
cors
string
,
apis
string
)
(
bool
,
error
)
{
func
(
api
*
PrivateAdminAPI
)
StartWS
(
host
*
string
,
port
*
rpc
.
HexNumber
,
allowedOrigins
*
string
,
apis
*
string
)
(
bool
,
error
)
{
api
.
node
.
lock
.
Lock
()
defer
api
.
node
.
lock
.
Unlock
()
if
api
.
node
.
wsHandler
!=
nil
{
return
false
,
fmt
.
Errorf
(
"WebSocket RPC already running on %s"
,
api
.
node
.
wsEndpoint
)
}
if
err
:=
api
.
node
.
startWS
(
fmt
.
Sprintf
(
"%s:%d"
,
host
,
port
),
api
.
node
.
rpcAPIs
,
strings
.
Split
(
apis
,
","
),
cors
);
err
!=
nil
{
if
host
==
nil
{
host
=
&
api
.
node
.
wsHost
}
if
port
==
nil
{
port
=
rpc
.
NewHexNumber
(
api
.
node
.
wsPort
)
}
if
allowedOrigins
==
nil
{
allowedOrigins
=
&
api
.
node
.
wsOrigins
}
modules
:=
api
.
node
.
wsWhitelist
if
apis
!=
nil
{
modules
=
nil
for
_
,
m
:=
range
strings
.
Split
(
*
apis
,
","
)
{
modules
=
append
(
modules
,
strings
.
TrimSpace
(
m
))
}
}
if
err
:=
api
.
node
.
startWS
(
fmt
.
Sprintf
(
"%s:%d"
,
*
host
,
port
.
Int
()),
api
.
node
.
rpcAPIs
,
modules
,
*
allowedOrigins
);
err
!=
nil
{
return
false
,
err
}
return
true
,
nil
...
...
node/config.go
浏览文件 @
8627680e
...
...
@@ -127,10 +127,10 @@ type Config struct {
// ephemeral nodes).
WSPort
int
// WS
Doma
ins is the list of domain to accept websocket requests from. Please be
// WS
Orig
ins is the list of domain to accept websocket requests from. Please be
// aware that the server can only act upon the HTTP request the client sends and
// cannot verify the validity of the request header.
WS
Doma
ins
string
WS
Orig
ins
string
// WSModules is a list of API modules to expose via the websocket RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
...
...
node/node.go
浏览文件 @
8627680e
...
...
@@ -62,15 +62,19 @@ type Node struct {
ipcListener
net
.
Listener
// IPC RPC listener socket to serve API requests
ipcHandler
*
rpc
.
Server
// IPC RPC request handler to process the API requests
httpHost
string
// HTTP hostname
httpPort
int
// HTTP post
httpEndpoint
string
// HTTP endpoint (interface + port) to listen at (empty = HTTP disabled)
httpWhitelist
[]
string
// HTTP RPC modules to allow through this endpoint
httpCors
string
// HTTP RPC Cross-Origin Resource Sharing header
httpListener
net
.
Listener
// HTTP RPC listener socket to server API requests
httpHandler
*
rpc
.
Server
// HTTP RPC request handler to process the API requests
wsHost
string
// Websocket host
wsPort
int
// Websocket post
wsEndpoint
string
// Websocket endpoint (interface + port) to listen at (empty = websocket disabled)
wsWhitelist
[]
string
// Websocket RPC modules to allow through this endpoint
ws
Doma
ins
string
// Websocket RPC allowed origin domains
ws
Orig
ins
string
// Websocket RPC allowed origin domains
wsListener
net
.
Listener
// Websocket RPC listener socket to server API requests
wsHandler
*
rpc
.
Server
// Websocket RPC request handler to process the API requests
...
...
@@ -110,12 +114,16 @@ func New(conf *Config) (*Node, error) {
},
serviceFuncs
:
[]
ServiceConstructor
{},
ipcEndpoint
:
conf
.
IPCEndpoint
(),
httpHost
:
conf
.
HTTPHost
,
httpPort
:
conf
.
HTTPPort
,
httpEndpoint
:
conf
.
HTTPEndpoint
(),
httpWhitelist
:
conf
.
HTTPModules
,
httpCors
:
conf
.
HTTPCors
,
wsHost
:
conf
.
WSHost
,
wsPort
:
conf
.
WSPort
,
wsEndpoint
:
conf
.
WSEndpoint
(),
wsWhitelist
:
conf
.
WSModules
,
ws
Domains
:
conf
.
WSDoma
ins
,
ws
Origins
:
conf
.
WSOrig
ins
,
eventmux
:
new
(
event
.
TypeMux
),
},
nil
}
...
...
@@ -231,7 +239,7 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error {
n
.
stopInProc
()
return
err
}
if
err
:=
n
.
startWS
(
n
.
wsEndpoint
,
apis
,
n
.
wsWhitelist
,
n
.
ws
Doma
ins
);
err
!=
nil
{
if
err
:=
n
.
startWS
(
n
.
wsEndpoint
,
apis
,
n
.
wsWhitelist
,
n
.
ws
Orig
ins
);
err
!=
nil
{
n
.
stopHTTP
()
n
.
stopIPC
()
n
.
stopInProc
()
...
...
@@ -383,7 +391,7 @@ func (n *Node) stopHTTP() {
}
// startWS initializes and starts the websocket RPC endpoint.
func
(
n
*
Node
)
startWS
(
endpoint
string
,
apis
[]
rpc
.
API
,
modules
[]
string
,
cor
s
string
)
error
{
func
(
n
*
Node
)
startWS
(
endpoint
string
,
apis
[]
rpc
.
API
,
modules
[]
string
,
wsOrigin
s
string
)
error
{
// Short circuit if the WS endpoint isn't being exposed
if
endpoint
==
""
{
return
nil
...
...
@@ -411,14 +419,14 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, cors s
if
listener
,
err
=
net
.
Listen
(
"tcp"
,
endpoint
);
err
!=
nil
{
return
err
}
go
rpc
.
NewWSServer
(
cor
s
,
handler
)
.
Serve
(
listener
)
go
rpc
.
NewWSServer
(
wsOrigin
s
,
handler
)
.
Serve
(
listener
)
glog
.
V
(
logger
.
Info
)
.
Infof
(
"WebSocket endpoint opened: ws://%s"
,
endpoint
)
// All listeners booted successfully
n
.
wsEndpoint
=
endpoint
n
.
wsListener
=
listener
n
.
wsHandler
=
handler
n
.
ws
Domains
=
cor
s
n
.
ws
Origins
=
wsOrigin
s
return
nil
}
...
...
node/node_test.go
浏览文件 @
8627680e
...
...
@@ -554,7 +554,7 @@ func TestAPIGather(t *testing.T) {
{
"multi.v2.nested_theOneMethod"
,
"multi.v2.nested"
},
}
for
i
,
test
:=
range
tests
{
if
err
:=
client
.
Send
(
rpc
.
JSONRequest
{
Id
:
new
(
int64
),
Version
:
"2.0"
,
Method
:
test
.
Method
});
err
!=
nil
{
if
err
:=
client
.
Send
(
rpc
.
JSONRequest
{
Id
:
[]
byte
(
"1"
),
Version
:
"2.0"
,
Method
:
test
.
Method
});
err
!=
nil
{
t
.
Fatalf
(
"test %d: failed to send API request: %v"
,
i
,
err
)
}
reply
:=
new
(
rpc
.
JSONSuccessResponse
)
...
...
rpc/doc.go
浏览文件 @
8627680e
...
...
@@ -29,11 +29,23 @@ Methods that satisfy the following criteria are made available for remote access
- method returned value(s) must be exported or builtin types
An example method:
func (s *CalcService)
Div
(a, b int) (int, error)
func (s *CalcService)
Add
(a, b int) (int, error)
When the returned error isn't nil the returned integer is ignored and the error is
send back to the client. Otherwise the returned integer is send back to the client.
Optional arguments are supported by accepting pointer values as arguments. E.g.
if we want to do the addition in an optional finite field we can accept a mod
argument as pointer value.
func (s *CalService) Add(a, b int, mod *int) (int, error)
This RPC method can be called with 2 integers and a null value as third argument.
In that case the mod argument will be nil. Or it can be called with 3 integers,
in that case mod will be pointing to the given third argument. Since the optional
argument is the last argument the RPC package will also accept 2 integers as
arguments. It will pass the mod argument as nil to the RPC method.
The server offers the ServeCodec method which accepts a ServerCodec instance. It will
read requests from the codec, process the request and sends the response back to the
client using the codec. The server can execute requests concurrently. Responses
...
...
rpc/http.go
浏览文件 @
8627680e
...
...
@@ -20,13 +20,12 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"io"
"github.com/rs/cors"
)
...
...
@@ -36,8 +35,9 @@ const (
// httpClient connects to a geth RPC server over HTTP.
type
httpClient
struct
{
endpoint
*
url
.
URL
// HTTP-RPC server endpoint
lastRes
[]
byte
// HTTP requests are synchronous, store last response
endpoint
*
url
.
URL
// HTTP-RPC server endpoint
httpClient
http
.
Client
// reuse connection
lastRes
[]
byte
// HTTP requests are synchronous, store last response
}
// NewHTTPClient create a new RPC clients that connection to a geth RPC server
...
...
@@ -57,30 +57,22 @@ func (client *httpClient) Send(msg interface{}) error {
var
err
error
client
.
lastRes
=
nil
if
body
,
err
=
json
.
Marshal
(
msg
);
err
!=
nil
{
return
err
}
httpReq
,
err
:=
http
.
NewRequest
(
"POST"
,
client
.
endpoint
.
String
(),
bytes
.
NewBuff
er
(
body
))
resp
,
err
:=
client
.
httpClient
.
Post
(
client
.
endpoint
.
String
(),
"application/json"
,
bytes
.
NewRead
er
(
body
))
if
err
!=
nil
{
return
err
}
httpReq
.
Header
.
Set
(
"Content-Type"
,
"application/json"
)
httpClient
:=
http
.
Client
{}
resp
,
err
:=
httpClient
.
Do
(
httpReq
)
if
err
!=
nil
{
return
err
}
defer
resp
.
Body
.
Close
()
if
resp
.
StatusCode
==
http
.
StatusOK
{
client
.
lastRes
,
err
=
ioutil
.
ReadAll
(
resp
.
Body
)
return
err
}
return
fmt
.
Errorf
(
"
unable to handle request"
)
return
fmt
.
Errorf
(
"
request failed: %s"
,
resp
.
Status
)
}
// Recv will try to deserialize the last received response into the given msg.
...
...
rpc/ipc_windows.go
浏览文件 @
8627680e
...
...
@@ -22,7 +22,7 @@ import (
"net"
"time"
"github.com/microsoft/go-winio"
winio
"github.com/microsoft/go-winio"
)
// ipcListen will create a named pipe on the given endpoint.
...
...
rpc/javascript.go
浏览文件 @
8627680e
...
...
@@ -19,48 +19,15 @@ package rpc
var
(
// Holds geth specific RPC extends which can be used to extend web3
WEB3Extensions
=
map
[
string
]
string
{
"personal"
:
Personal_JS
,
"txpool"
:
TxPool_JS
,
"admin"
:
Admin_JS
,
"eth"
:
Eth_JS
,
"miner"
:
Miner_JS
,
"debug"
:
Debug_JS
,
"net"
:
Net_JS
,
"txpool"
:
TxPool_JS
,
"admin"
:
Admin_JS
,
"eth"
:
Eth_JS
,
"miner"
:
Miner_JS
,
"debug"
:
Debug_JS
,
"net"
:
Net_JS
,
}
)
const
Personal_JS
=
`
web3._extend({
property: 'personal',
methods:
[
new web3._extend.Method({
name: 'newAccount',
call: 'personal_newAccount',
params: 1,
outputFormatter: web3._extend.utils.toAddress
}),
new web3._extend.Method({
name: 'unlockAccount',
call: 'personal_unlockAccount',
params: 3,
}),
new web3._extend.Method({
name: 'lockAccount',
call: 'personal_lockAccount',
params: 1
})
],
properties:
[
new web3._extend.Property({
name: 'listAccounts',
getter: 'personal_listAccounts'
})
]
});
`
const
TxPool_JS
=
`
web3._extend({
property: 'txpool',
...
...
@@ -124,22 +91,22 @@ web3._extend({
new web3._extend.Method({
name: 'startRPC',
call: 'admin_startRPC',
params: 4
params: 4,
inputFormatter: [null, null, null, null]
}),
new web3._extend.Method({
name: 'stopRPC',
call: 'admin_stopRPC',
params: 0
call: 'admin_stopRPC'
}),
new web3._extend.Method({
name: 'startWS',
call: 'admin_startWS',
params: 4
params: 4,
inputFormatter: [null, null, null, null]
}),
new web3._extend.Method({
name: 'stopWS',
call: 'admin_stopWS',
params: 0
call: 'admin_stopWS'
}),
new web3._extend.Method({
name: 'setGlobalRegistrar',
...
...
@@ -219,7 +186,7 @@ web3._extend({
name: 'sign',
call: 'eth_sign',
params: 2,
inputFormatter: [web3._extend.
utils.toAddress
, null]
inputFormatter: [web3._extend.
formatters.inputAddressFormatter
, null]
}),
new web3._extend.Method({
name: 'resend',
...
...
@@ -422,19 +389,18 @@ web3._extend({
new web3._extend.Method({
name: 'start',
call: 'miner_start',
params: 1
params: 1,
inputFormatter: [null]
}),
new web3._extend.Method({
name: 'stop',
call: 'miner_stop',
params: 1
call: 'miner_stop'
}),
new web3._extend.Method({
name: 'setEtherbase',
call: 'miner_setEtherbase',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputBool
inputFormatter: [web3._extend.formatters.inputAddressFormatter]
}),
new web3._extend.Method({
name: 'setExtra',
...
...
rpc/json.go
浏览文件 @
8627680e
...
...
@@ -21,6 +21,7 @@ import (
"fmt"
"io"
"reflect"
"strconv"
"strings"
"sync"
...
...
@@ -40,14 +41,14 @@ const (
type
JSONRequest
struct
{
Method
string
`json:"method"`
Version
string
`json:"jsonrpc"`
Id
*
int64
`json:"id,omitempty"`
Id
json
.
RawMessage
`json:"id,omitempty"`
Payload
json
.
RawMessage
`json:"params,omitempty"`
}
// JSON-RPC response
type
JSONSuccessResponse
struct
{
Version
string
`json:"jsonrpc"`
Id
int
64
`json:"id
"`
Id
int
erface
{}
`json:"id,omitempty
"`
Result
interface
{}
`json:"result"`
}
...
...
@@ -60,9 +61,9 @@ type JSONError struct {
// JSON-RPC error response
type
JSONErrResponse
struct
{
Version
string
`json:"jsonrpc"`
Id
*
int64
`json:"id,omitempty"`
Error
JSONError
`json:"error"`
Version
string
`json:"jsonrpc"`
Id
interface
{}
`json:"id,omitempty"`
Error
JSONError
`json:"error"`
}
// JSON-RPC notification payload
...
...
@@ -78,16 +79,16 @@ type jsonNotification struct {
Params
jsonSubscription
`json:"params"`
}
// jsonCodec reads and writes JSON-RPC messages to the underlying connection. It
also has support for parsing arguments
// and serializing (result) objects.
// jsonCodec reads and writes JSON-RPC messages to the underlying connection. It
// a
lso has support for parsing arguments a
nd serializing (result) objects.
type
jsonCodec
struct
{
close
d
chan
interface
{}
close
r
sync
.
Onc
e
d
*
json
.
Decoder
muEncoder
sync
.
Mutex
e
*
json
.
Encoder
req
JSONRequest
rw
io
.
ReadWriteCloser
close
r
sync
.
Once
// close closed channel once
close
d
chan
interface
{}
// closed on Clos
e
d
ecMu
sync
.
Mutex
// guards d
d
*
json
.
Decoder
// decodes incoming requests
e
ncMu
sync
.
Mutex
// guards e
e
*
json
.
Encoder
// encodes responses
rw
io
.
ReadWriteCloser
// connection
}
// NewJSONCodec creates a new RPC server codec with support for JSON-RPC 2.0
...
...
@@ -109,9 +110,13 @@ func isBatch(msg json.RawMessage) bool {
return
false
}
// ReadRequestHeaders will read new requests without parsing the arguments. It will return a collection of requests, an
// indication if these requests are in batch form or an error when the incoming message could not be read/parsed.
// ReadRequestHeaders will read new requests without parsing the arguments. It will
// return a collection of requests, an indication if these requests are in batch
// form or an error when the incoming message could not be read/parsed.
func
(
c
*
jsonCodec
)
ReadRequestHeaders
()
([]
rpcRequest
,
bool
,
RPCError
)
{
c
.
decMu
.
Lock
()
defer
c
.
decMu
.
Unlock
()
var
incomingMsg
json
.
RawMessage
if
err
:=
c
.
d
.
Decode
(
&
incomingMsg
);
err
!=
nil
{
return
nil
,
false
,
&
invalidRequestError
{
err
.
Error
()}
...
...
@@ -124,21 +129,38 @@ func (c *jsonCodec) ReadRequestHeaders() ([]rpcRequest, bool, RPCError) {
return
parseRequest
(
incomingMsg
)
}
// parseRequest will parse a single request from the given RawMessage. It will return the parsed request, an indication
// if the request was a batch or an error when the request could not be parsed.
// checkReqId returns an error when the given reqId isn't valid for RPC method calls.
// valid id's are strings, numbers or null
func
checkReqId
(
reqId
json
.
RawMessage
)
error
{
if
len
(
reqId
)
==
0
{
return
fmt
.
Errorf
(
"missing request id"
)
}
if
_
,
err
:=
strconv
.
ParseFloat
(
string
(
reqId
),
64
);
err
==
nil
{
return
nil
}
var
str
string
if
err
:=
json
.
Unmarshal
(
reqId
,
&
str
);
err
==
nil
{
return
nil
}
return
fmt
.
Errorf
(
"invalid request id"
)
}
// parseRequest will parse a single request from the given RawMessage. It will return
// the parsed request, an indication if the request was a batch or an error when
// the request could not be parsed.
func
parseRequest
(
incomingMsg
json
.
RawMessage
)
([]
rpcRequest
,
bool
,
RPCError
)
{
var
in
JSONRequest
if
err
:=
json
.
Unmarshal
(
incomingMsg
,
&
in
);
err
!=
nil
{
return
nil
,
false
,
&
invalidMessageError
{
err
.
Error
()}
}
if
in
.
Id
=
=
nil
{
return
nil
,
false
,
&
invalidMessageError
{
"Server cannot handle notifications"
}
if
err
:=
checkReqId
(
in
.
Id
);
err
!
=
nil
{
return
nil
,
false
,
&
invalidMessageError
{
err
.
Error
()
}
}
// subscribe are special, they will always use `subscribeMethod` as
service metho
d
// subscribe are special, they will always use `subscribeMethod` as
first param in the payloa
d
if
in
.
Method
==
subscribeMethod
{
reqs
:=
[]
rpcRequest
{
rpcRequest
{
id
:
*
in
.
Id
,
isPubSub
:
true
}}
reqs
:=
[]
rpcRequest
{
rpcRequest
{
id
:
&
in
.
Id
,
isPubSub
:
true
}}
if
len
(
in
.
Payload
)
>
0
{
// first param must be subscription name
var
subscribeMethod
[
1
]
string
...
...
@@ -156,7 +178,7 @@ func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) {
}
if
in
.
Method
==
unsubscribeMethod
{
return
[]
rpcRequest
{
rpcRequest
{
id
:
*
in
.
Id
,
isPubSub
:
true
,
return
[]
rpcRequest
{
rpcRequest
{
id
:
&
in
.
Id
,
isPubSub
:
true
,
method
:
unsubscribeMethod
,
params
:
in
.
Payload
}},
false
,
nil
}
...
...
@@ -167,10 +189,10 @@ func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) {
}
if
len
(
in
.
Payload
)
==
0
{
return
[]
rpcRequest
{
rpcRequest
{
service
:
elems
[
0
],
method
:
elems
[
1
],
id
:
*
in
.
Id
}},
false
,
nil
return
[]
rpcRequest
{
rpcRequest
{
service
:
elems
[
0
],
method
:
elems
[
1
],
id
:
&
in
.
Id
}},
false
,
nil
}
return
[]
rpcRequest
{
rpcRequest
{
service
:
elems
[
0
],
method
:
elems
[
1
],
id
:
*
in
.
Id
,
params
:
in
.
Payload
}},
false
,
nil
return
[]
rpcRequest
{
rpcRequest
{
service
:
elems
[
0
],
method
:
elems
[
1
],
id
:
&
in
.
Id
,
params
:
in
.
Payload
}},
false
,
nil
}
// parseBatchRequest will parse a batch request into a collection of requests from the given RawMessage, an indication
...
...
@@ -183,14 +205,17 @@ func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCErro
requests
:=
make
([]
rpcRequest
,
len
(
in
))
for
i
,
r
:=
range
in
{
if
r
.
Id
=
=
nil
{
return
nil
,
true
,
&
invalidMessageError
{
"Server cannot handle notifications"
}
if
err
:=
checkReqId
(
r
.
Id
);
err
!
=
nil
{
return
nil
,
false
,
&
invalidMessageError
{
err
.
Error
()
}
}
// (un)subscribe are special, they will always use the same service.method
id
:=
&
in
[
i
]
.
Id
// subscribe are special, they will always use `subscribeMethod` as first param in the payload
if
r
.
Method
==
subscribeMethod
{
requests
[
i
]
=
rpcRequest
{
id
:
*
r
.
I
d
,
isPubSub
:
true
}
requests
[
i
]
=
rpcRequest
{
id
:
i
d
,
isPubSub
:
true
}
if
len
(
r
.
Payload
)
>
0
{
// first param must be subscription name
var
subscribeMethod
[
1
]
string
if
err
:=
json
.
Unmarshal
(
r
.
Payload
,
&
subscribeMethod
);
err
!=
nil
{
glog
.
V
(
logger
.
Debug
)
.
Infof
(
"Unable to parse subscription method: %v
\n
"
,
err
)
...
...
@@ -207,7 +232,7 @@ func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCErro
}
if
r
.
Method
==
unsubscribeMethod
{
requests
[
i
]
=
rpcRequest
{
id
:
*
r
.
I
d
,
isPubSub
:
true
,
method
:
unsubscribeMethod
,
params
:
r
.
Payload
}
requests
[
i
]
=
rpcRequest
{
id
:
i
d
,
isPubSub
:
true
,
method
:
unsubscribeMethod
,
params
:
r
.
Payload
}
continue
}
...
...
@@ -217,9 +242,9 @@ func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCErro
}
if
len
(
r
.
Payload
)
==
0
{
requests
[
i
]
=
rpcRequest
{
service
:
elems
[
0
],
method
:
elems
[
1
],
id
:
*
r
.
I
d
,
params
:
nil
}
requests
[
i
]
=
rpcRequest
{
service
:
elems
[
0
],
method
:
elems
[
1
],
id
:
i
d
,
params
:
nil
}
}
else
{
requests
[
i
]
=
rpcRequest
{
service
:
elems
[
0
],
method
:
elems
[
1
],
id
:
*
r
.
I
d
,
params
:
r
.
Payload
}
requests
[
i
]
=
rpcRequest
{
service
:
elems
[
0
],
method
:
elems
[
1
],
id
:
i
d
,
params
:
r
.
Payload
}
}
}
...
...
@@ -236,58 +261,38 @@ func (c *jsonCodec) ParseRequestArguments(argTypes []reflect.Type, params interf
}
}
func
countArguments
(
args
json
.
RawMessage
)
(
int
,
error
)
{
var
cnt
[]
interface
{}
if
err
:=
json
.
Unmarshal
(
args
,
&
cnt
);
err
!=
nil
{
return
-
1
,
nil
// parsePositionalArguments tries to parse the given args to an array of values with the given types.
// It returns the parsed values or an error when the args could not be parsed. Missing optional arguments
// are returned as reflect.Zero values.
func
parsePositionalArguments
(
args
json
.
RawMessage
,
callbackArgs
[]
reflect
.
Type
)
([]
reflect
.
Value
,
RPCError
)
{
params
:=
make
([]
interface
{},
0
,
len
(
callbackArgs
))
for
_
,
t
:=
range
callbackArgs
{
params
=
append
(
params
,
reflect
.
New
(
t
)
.
Interface
())
}
return
len
(
cnt
),
nil
}
// parsePositionalArguments tries to parse the given args to an array of values with the given types. It returns the
// parsed values or an error when the args could not be parsed.
func
parsePositionalArguments
(
args
json
.
RawMessage
,
argTypes
[]
reflect
.
Type
)
([]
reflect
.
Value
,
RPCError
)
{
argValues
:=
make
([]
reflect
.
Value
,
len
(
argTypes
))
params
:=
make
([]
interface
{},
len
(
argTypes
))
n
,
err
:=
countArguments
(
args
)
if
err
!=
nil
{
if
err
:=
json
.
Unmarshal
(
args
,
&
params
);
err
!=
nil
{
return
nil
,
&
invalidParamsError
{
err
.
Error
()}
}
if
n
!=
len
(
argTypes
)
{
return
nil
,
&
invalidParamsError
{
fmt
.
Sprintf
(
"insufficient params, want %d have %d"
,
len
(
argTypes
),
n
)}
}
for
i
,
t
:=
range
argTypes
{
if
t
.
Kind
()
==
reflect
.
Ptr
{
// values must be pointers for the Unmarshal method, reflect.
// Dereference otherwise reflect.New would create **SomeType
argValues
[
i
]
=
reflect
.
New
(
t
.
Elem
())
params
[
i
]
=
argValues
[
i
]
.
Interface
()
// when not specified blockNumbers are by default latest (-1)
if
blockNumber
,
ok
:=
params
[
i
]
.
(
*
BlockNumber
);
ok
{
*
blockNumber
=
BlockNumber
(
-
1
)
}
}
else
{
argValues
[
i
]
=
reflect
.
New
(
t
)
params
[
i
]
=
argValues
[
i
]
.
Interface
()
// when not specified blockNumbers are by default latest (-1)
if
blockNumber
,
ok
:=
params
[
i
]
.
(
*
BlockNumber
);
ok
{
*
blockNumber
=
BlockNumber
(
-
1
)
}
}
if
len
(
params
)
>
len
(
callbackArgs
)
{
return
nil
,
&
invalidParamsError
{
fmt
.
Sprintf
(
"too many params, want %d got %d"
,
len
(
callbackArgs
),
len
(
params
))}
}
if
err
:=
json
.
Unmarshal
(
args
,
&
params
);
err
!=
nil
{
return
nil
,
&
invalidParamsError
{
err
.
Error
()}
// assume missing params are null values
for
i
:=
len
(
params
);
i
<
len
(
callbackArgs
);
i
++
{
params
=
append
(
params
,
nil
)
}
// Convert pointers back to values where necessary
for
i
,
a
:=
range
argValues
{
if
a
.
Kind
()
!=
argTypes
[
i
]
.
Kind
()
{
argValues
[
i
]
=
reflect
.
Indirect
(
argValues
[
i
])
argValues
:=
make
([]
reflect
.
Value
,
len
(
params
))
for
i
,
p
:=
range
params
{
// verify that JSON null values are only supplied for optional arguments (ptr types)
if
p
==
nil
&&
callbackArgs
[
i
]
.
Kind
()
!=
reflect
.
Ptr
{
return
nil
,
&
invalidParamsError
{
fmt
.
Sprintf
(
"invalid or missing value for params[%d]"
,
i
)}
}
if
p
==
nil
{
argValues
[
i
]
=
reflect
.
Zero
(
callbackArgs
[
i
])
}
else
{
// deref pointers values creates previously with reflect.New
argValues
[
i
]
=
reflect
.
ValueOf
(
p
)
.
Elem
()
}
}
...
...
@@ -295,7 +300,7 @@ func parsePositionalArguments(args json.RawMessage, argTypes []reflect.Type) ([]
}
// CreateResponse will create a JSON-RPC success response with the given id and reply as result.
func
(
c
*
jsonCodec
)
CreateResponse
(
id
int
64
,
reply
interface
{})
interface
{}
{
func
(
c
*
jsonCodec
)
CreateResponse
(
id
int
erface
{}
,
reply
interface
{})
interface
{}
{
if
isHexNum
(
reflect
.
TypeOf
(
reply
))
{
return
&
JSONSuccessResponse
{
Version
:
jsonRPCVersion
,
Id
:
id
,
Result
:
fmt
.
Sprintf
(
`%#x`
,
reply
)}
}
...
...
@@ -303,13 +308,13 @@ func (c *jsonCodec) CreateResponse(id int64, reply interface{}) interface{} {
}
// CreateErrorResponse will create a JSON-RPC error response with the given id and error.
func
(
c
*
jsonCodec
)
CreateErrorResponse
(
id
*
int64
,
err
RPCError
)
interface
{}
{
func
(
c
*
jsonCodec
)
CreateErrorResponse
(
id
interface
{}
,
err
RPCError
)
interface
{}
{
return
&
JSONErrResponse
{
Version
:
jsonRPCVersion
,
Id
:
id
,
Error
:
JSONError
{
Code
:
err
.
Code
(),
Message
:
err
.
Error
()}}
}
// CreateErrorResponseWithInfo will create a JSON-RPC error response with the given id and error.
// info is optional and contains additional information about the error. When an empty string is passed it is ignored.
func
(
c
*
jsonCodec
)
CreateErrorResponseWithInfo
(
id
*
int64
,
err
RPCError
,
info
interface
{})
interface
{}
{
func
(
c
*
jsonCodec
)
CreateErrorResponseWithInfo
(
id
interface
{}
,
err
RPCError
,
info
interface
{})
interface
{}
{
return
&
JSONErrResponse
{
Version
:
jsonRPCVersion
,
Id
:
id
,
Error
:
JSONError
{
Code
:
err
.
Code
(),
Message
:
err
.
Error
(),
Data
:
info
}}
}
...
...
@@ -327,8 +332,8 @@ func (c *jsonCodec) CreateNotification(subid string, event interface{}) interfac
// Write message to client
func
(
c
*
jsonCodec
)
Write
(
res
interface
{})
error
{
c
.
muEncoder
.
Lock
()
defer
c
.
muEncoder
.
Unlock
()
c
.
encMu
.
Lock
()
defer
c
.
encMu
.
Unlock
()
return
c
.
e
.
Encode
(
res
)
}
...
...
rpc/json_test.go
浏览文件 @
8627680e
...
...
@@ -3,7 +3,9 @@ package rpc
import
(
"bufio"
"bytes"
"encoding/json"
"reflect"
"strconv"
"testing"
)
...
...
@@ -51,8 +53,16 @@ func TestJSONRequestParsing(t *testing.T) {
t
.
Fatalf
(
"Expected method 'Add' but got '%s'"
,
requests
[
0
]
.
method
)
}
if
requests
[
0
]
.
id
!=
1234
{
t
.
Fatalf
(
"Expected id 1234 but got %d"
,
requests
[
0
]
.
id
)
if
rawId
,
ok
:=
requests
[
0
]
.
id
.
(
*
json
.
RawMessage
);
ok
{
id
,
e
:=
strconv
.
ParseInt
(
string
(
*
rawId
),
0
,
64
)
if
e
!=
nil
{
t
.
Fatalf
(
"%v"
,
e
)
}
if
id
!=
1234
{
t
.
Fatalf
(
"Expected id 1234 but got %s"
,
id
)
}
}
else
{
t
.
Fatalf
(
"invalid request, expected *json.RawMesage got %T"
,
requests
[
0
]
.
id
)
}
var
arg
int
...
...
@@ -71,3 +81,82 @@ func TestJSONRequestParsing(t *testing.T) {
t
.
Fatalf
(
"expected %d == 11 && %d == 22"
,
v
[
0
]
.
Int
(),
v
[
1
]
.
Int
())
}
}
func
TestJSONRequestParamsParsing
(
t
*
testing
.
T
)
{
var
(
stringT
=
reflect
.
TypeOf
(
""
)
intT
=
reflect
.
TypeOf
(
0
)
intPtrT
=
reflect
.
TypeOf
(
new
(
int
))
stringV
=
reflect
.
ValueOf
(
"abc"
)
i
=
1
intV
=
reflect
.
ValueOf
(
i
)
intPtrV
=
reflect
.
ValueOf
(
&
i
)
)
var
validTests
=
[]
struct
{
input
string
argTypes
[]
reflect
.
Type
expected
[]
reflect
.
Value
}{
{
`[]`
,
[]
reflect
.
Type
{},
[]
reflect
.
Value
{}},
{
`[]`
,
[]
reflect
.
Type
{
intPtrT
},
[]
reflect
.
Value
{
intPtrV
}},
{
`[1]`
,
[]
reflect
.
Type
{
intT
},
[]
reflect
.
Value
{
intV
}},
{
`[1,"abc"]`
,
[]
reflect
.
Type
{
intT
,
stringT
},
[]
reflect
.
Value
{
intV
,
stringV
}},
{
`[null]`
,
[]
reflect
.
Type
{
intPtrT
},
[]
reflect
.
Value
{
intPtrV
}},
{
`[null,"abc"]`
,
[]
reflect
.
Type
{
intPtrT
,
stringT
,
intPtrT
},
[]
reflect
.
Value
{
intPtrV
,
stringV
,
intPtrV
}},
{
`[null,"abc",null]`
,
[]
reflect
.
Type
{
intPtrT
,
stringT
,
intPtrT
},
[]
reflect
.
Value
{
intPtrV
,
stringV
,
intPtrV
}},
}
codec
:=
jsonCodec
{}
for
_
,
test
:=
range
validTests
{
params
:=
(
json
.
RawMessage
)([]
byte
(
test
.
input
))
args
,
err
:=
codec
.
ParseRequestArguments
(
test
.
argTypes
,
params
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
var
match
[]
interface
{}
json
.
Unmarshal
([]
byte
(
test
.
input
),
&
match
)
if
len
(
args
)
!=
len
(
test
.
argTypes
)
{
t
.
Fatalf
(
"expected %d parsed args, got %d"
,
len
(
test
.
argTypes
),
len
(
args
))
}
for
i
,
arg
:=
range
args
{
expected
:=
test
.
expected
[
i
]
if
arg
.
Kind
()
!=
expected
.
Kind
()
{
t
.
Errorf
(
"expected type for param %d in %s"
,
i
,
test
.
input
)
}
if
arg
.
Kind
()
==
reflect
.
Int
&&
arg
.
Int
()
!=
expected
.
Int
()
{
t
.
Errorf
(
"expected int(%d), got int(%d) in %s"
,
expected
.
Int
(),
arg
.
Int
(),
test
.
input
)
}
if
arg
.
Kind
()
==
reflect
.
String
&&
arg
.
String
()
!=
expected
.
String
()
{
t
.
Errorf
(
"expected string(%s), got string(%s) in %s"
,
expected
.
String
(),
arg
.
String
(),
test
.
input
)
}
}
}
var
invalidTests
=
[]
struct
{
input
string
argTypes
[]
reflect
.
Type
}{
{
`[]`
,
[]
reflect
.
Type
{
intT
}},
{
`[null]`
,
[]
reflect
.
Type
{
intT
}},
{
`[1]`
,
[]
reflect
.
Type
{
stringT
}},
{
`[1,2]`
,
[]
reflect
.
Type
{
stringT
}},
{
`["abc", null]`
,
[]
reflect
.
Type
{
stringT
,
intT
}},
}
for
i
,
test
:=
range
invalidTests
{
if
_
,
err
:=
codec
.
ParseRequestArguments
(
test
.
argTypes
,
test
.
input
);
err
==
nil
{
t
.
Errorf
(
"expected test %d - %s to fail"
,
i
,
test
.
input
)
}
}
}
rpc/server_test.go
浏览文件 @
8627680e
此差异已折叠。
点击以展开。
rpc/types.go
浏览文件 @
8627680e
...
...
@@ -56,7 +56,7 @@ type service struct {
// serverRequest is an incoming request
type
serverRequest
struct
{
id
int
64
id
int
erface
{}
svcname
string
rcvr
reflect
.
Value
callb
*
callback
...
...
@@ -85,7 +85,7 @@ type Server struct {
type
rpcRequest
struct
{
service
string
method
string
id
int
64
id
int
erface
{}
isPubSub
bool
params
interface
{}
}
...
...
@@ -106,12 +106,12 @@ type ServerCodec interface {
ReadRequestHeaders
()
([]
rpcRequest
,
bool
,
RPCError
)
// Parse request argument to the given types
ParseRequestArguments
([]
reflect
.
Type
,
interface
{})
([]
reflect
.
Value
,
RPCError
)
// Assemble success response
CreateResponse
(
int
64
,
interface
{})
interface
{}
// Assemble error response
CreateErrorResponse
(
*
int64
,
RPCError
)
interface
{}
// Assemble success response
, expects response id and payload
CreateResponse
(
int
erface
{}
,
interface
{})
interface
{}
// Assemble error response
, expects response id and error
CreateErrorResponse
(
interface
{}
,
RPCError
)
interface
{}
// Assemble error response with extra information about the error through info
CreateErrorResponseWithInfo
(
id
*
int64
,
err
RPCError
,
info
interface
{})
interface
{}
CreateErrorResponseWithInfo
(
id
interface
{}
,
err
RPCError
,
info
interface
{})
interface
{}
// Create notification response
CreateNotification
(
string
,
interface
{})
interface
{}
// Write msg to client.
...
...
@@ -207,43 +207,6 @@ func (h *HexNumber) BigInt() *big.Int {
return
(
*
big
.
Int
)(
h
)
}
type
Number
int64
func
(
n
*
Number
)
UnmarshalJSON
(
data
[]
byte
)
error
{
input
:=
strings
.
TrimSpace
(
string
(
data
))
if
len
(
input
)
>=
2
&&
input
[
0
]
==
'"'
&&
input
[
len
(
input
)
-
1
]
==
'"'
{
input
=
input
[
1
:
len
(
input
)
-
1
]
}
if
len
(
input
)
==
0
{
*
n
=
Number
(
latestBlockNumber
.
Int64
())
return
nil
}
in
:=
new
(
big
.
Int
)
_
,
ok
:=
in
.
SetString
(
input
,
0
)
if
!
ok
{
// test if user supplied string tag
return
fmt
.
Errorf
(
`invalid number %s`
,
data
)
}
if
in
.
Cmp
(
earliestBlockNumber
)
>=
0
&&
in
.
Cmp
(
maxBlockNumber
)
<=
0
{
*
n
=
Number
(
in
.
Int64
())
return
nil
}
return
fmt
.
Errorf
(
"blocknumber not in range [%d, %d]"
,
earliestBlockNumber
,
maxBlockNumber
)
}
func
(
n
*
Number
)
Int64
()
int64
{
return
*
(
*
int64
)(
n
)
}
func
(
n
*
Number
)
BigInt
()
*
big
.
Int
{
return
big
.
NewInt
(
n
.
Int64
())
}
var
(
pendingBlockNumber
=
big
.
NewInt
(
-
2
)
latestBlockNumber
=
big
.
NewInt
(
-
1
)
...
...
rpc/utils.go
浏览文件 @
8627680e
...
...
@@ -232,7 +232,7 @@ func newSubscriptionID() (string, error) {
// on which the given client connects.
func
SupportedModules
(
client
Client
)
(
map
[
string
]
string
,
error
)
{
req
:=
JSONRequest
{
Id
:
new
(
int64
),
Id
:
[]
byte
(
"1"
),
Version
:
"2.0"
,
Method
:
"rpc_modules"
,
}
...
...
rpc/websocket.go
浏览文件 @
8627680e
...
...
@@ -88,10 +88,10 @@ func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http
}
// NewWSServer creates a new websocket RPC server around an API provider.
func
NewWSServer
(
cor
s
string
,
handler
*
Server
)
*
http
.
Server
{
func
NewWSServer
(
allowedOrigin
s
string
,
handler
*
Server
)
*
http
.
Server
{
return
&
http
.
Server
{
Handler
:
websocket
.
Server
{
Handshake
:
wsHandshakeValidator
(
strings
.
Split
(
cor
s
,
","
)),
Handshake
:
wsHandshakeValidator
(
strings
.
Split
(
allowedOrigin
s
,
","
)),
Handler
:
func
(
conn
*
websocket
.
Conn
)
{
handler
.
ServeCodec
(
NewJSONCodec
(
&
wsReaderWriterCloser
{
conn
}),
OptionMethodInvocation
|
OptionSubscriptions
)
...
...
whisper/api.go
浏览文件 @
8627680e
...
...
@@ -84,7 +84,7 @@ type NewFilterArgs struct {
}
// NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages.
func
(
s
*
PublicWhisperAPI
)
NewFilter
(
args
*
NewFilterArgs
)
(
*
rpc
.
HexNumber
,
error
)
{
func
(
s
*
PublicWhisperAPI
)
NewFilter
(
args
NewFilterArgs
)
(
*
rpc
.
HexNumber
,
error
)
{
if
s
.
w
==
nil
{
return
nil
,
whisperOffLineErr
}
...
...
@@ -171,7 +171,7 @@ type PostArgs struct {
}
// Post injects a message into the whisper network for distribution.
func
(
s
*
PublicWhisperAPI
)
Post
(
args
*
PostArgs
)
(
bool
,
error
)
{
func
(
s
*
PublicWhisperAPI
)
Post
(
args
PostArgs
)
(
bool
,
error
)
{
if
s
.
w
==
nil
{
return
false
,
whisperOffLineErr
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录