Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
09d0d55f
Commit
09d0d55f
authored
Jun 09, 2015
by
Bas van Kervel
Committed by
Bas van Kervel
Jun 11, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added debug API
parent
faab931c
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
980 additions
and
712 deletions
+980
-712
api.go
rpc/api/api.go
+2
-1
debug.go
rpc/api/debug.go
+169
-0
debug_args.go
rpc/api/debug_args.go
+47
-0
debug_js.go
rpc/api/debug_js.go
+48
-0
utils.go
rpc/api/utils.go
+4
-0
ipc_unix.go
rpc/comms/ipc_unix.go
+5
-5
ipc_windows.go
rpc/comms/ipc_windows.go
+696
-699
jeth.go
rpc/jeth.go
+4
-3
types.go
rpc/shared/types.go
+5
-4
No files found.
rpc/api/api.go
View file @
09d0d55f
...
...
@@ -4,9 +4,10 @@ import "github.com/ethereum/go-ethereum/rpc/shared"
const
(
// List with all API's which are offered over the IPC interface by default
DefaultIpcApis
=
"eth,miner,net,web3"
DefaultIpcApis
=
"
debug,
eth,miner,net,web3"
EthApiName
=
"eth"
DebugApiName
=
"debug"
MergedApiName
=
"merged"
MinerApiName
=
"miner"
NetApiName
=
"net"
...
...
rpc/api/debug.go
0 → 100644
View file @
09d0d55f
package
api
import
(
"fmt"
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/xeth"
)
const
(
DebugVersion
=
"1.0.0"
)
var
(
// mapping between methods and handlers
DebugMapping
=
map
[
string
]
debughandler
{
"debug_dumpBlock"
:
(
*
DebugApi
)
.
DumpBlock
,
"debug_getBlockRlp"
:
(
*
DebugApi
)
.
GetBlockRlp
,
"debug_printBlock"
:
(
*
DebugApi
)
.
PrintBlock
,
"debug_processBlock"
:
(
*
DebugApi
)
.
ProcessBlock
,
"debug_seedHash"
:
(
*
DebugApi
)
.
SeedHash
,
"debug_setHead"
:
(
*
DebugApi
)
.
SetHead
,
}
)
// debug callback handler
type
debughandler
func
(
*
DebugApi
,
*
shared
.
Request
)
(
interface
{},
error
)
// admin api provider
type
DebugApi
struct
{
xeth
*
xeth
.
XEth
ethereum
*
eth
.
Ethereum
methods
map
[
string
]
debughandler
codec
codec
.
ApiCoder
}
// create a new debug api instance
func
NewDebugApi
(
xeth
*
xeth
.
XEth
,
ethereum
*
eth
.
Ethereum
,
coder
codec
.
Codec
)
*
DebugApi
{
return
&
DebugApi
{
xeth
:
xeth
,
ethereum
:
ethereum
,
methods
:
DebugMapping
,
codec
:
coder
.
New
(
nil
),
}
}
// collection with supported methods
func
(
self
*
DebugApi
)
Methods
()
[]
string
{
methods
:=
make
([]
string
,
len
(
self
.
methods
))
i
:=
0
for
k
:=
range
self
.
methods
{
methods
[
i
]
=
k
i
++
}
return
methods
}
// Execute given request
func
(
self
*
DebugApi
)
Execute
(
req
*
shared
.
Request
)
(
interface
{},
error
)
{
if
callback
,
ok
:=
self
.
methods
[
req
.
Method
];
ok
{
return
callback
(
self
,
req
)
}
return
nil
,
&
shared
.
NotImplementedError
{
req
.
Method
}
}
func
(
self
*
DebugApi
)
Name
()
string
{
return
DebugApiName
}
func
(
self
*
DebugApi
)
PrintBlock
(
req
*
shared
.
Request
)
(
interface
{},
error
)
{
args
:=
new
(
BlockNumArg
)
if
err
:=
self
.
codec
.
Decode
(
req
.
Params
,
&
args
);
err
!=
nil
{
return
nil
,
shared
.
NewDecodeParamError
(
err
.
Error
())
}
block
:=
self
.
xeth
.
EthBlockByNumber
(
args
.
BlockNumber
)
return
fmt
.
Sprintf
(
"%s"
,
block
),
nil
}
func
(
self
*
DebugApi
)
DumpBlock
(
req
*
shared
.
Request
)
(
interface
{},
error
)
{
args
:=
new
(
BlockNumArg
)
if
err
:=
self
.
codec
.
Decode
(
req
.
Params
,
&
args
);
err
!=
nil
{
return
nil
,
shared
.
NewDecodeParamError
(
err
.
Error
())
}
block
:=
self
.
xeth
.
EthBlockByNumber
(
args
.
BlockNumber
)
if
block
==
nil
{
return
nil
,
fmt
.
Errorf
(
"block #%d not found"
,
args
.
BlockNumber
)
}
stateDb
:=
state
.
New
(
block
.
Root
(),
self
.
ethereum
.
StateDb
())
if
stateDb
==
nil
{
return
nil
,
nil
}
return
stateDb
.
Dump
(),
nil
}
func
(
self
*
DebugApi
)
GetBlockRlp
(
req
*
shared
.
Request
)
(
interface
{},
error
)
{
args
:=
new
(
BlockNumArg
)
if
err
:=
self
.
codec
.
Decode
(
req
.
Params
,
&
args
);
err
!=
nil
{
return
nil
,
shared
.
NewDecodeParamError
(
err
.
Error
())
}
block
:=
self
.
xeth
.
EthBlockByNumber
(
args
.
BlockNumber
)
if
block
==
nil
{
return
nil
,
fmt
.
Errorf
(
"block #%d not found"
,
args
.
BlockNumber
)
}
encoded
,
err
:=
rlp
.
EncodeToBytes
(
block
)
return
fmt
.
Sprintf
(
"%x"
,
encoded
),
err
}
func
(
self
*
DebugApi
)
SetHead
(
req
*
shared
.
Request
)
(
interface
{},
error
)
{
args
:=
new
(
BlockNumArg
)
if
err
:=
self
.
codec
.
Decode
(
req
.
Params
,
&
args
);
err
!=
nil
{
return
nil
,
shared
.
NewDecodeParamError
(
err
.
Error
())
}
block
:=
self
.
xeth
.
EthBlockByNumber
(
args
.
BlockNumber
)
if
block
==
nil
{
return
nil
,
fmt
.
Errorf
(
"block #%d not found"
,
args
.
BlockNumber
)
}
self
.
ethereum
.
ChainManager
()
.
SetHead
(
block
)
return
nil
,
nil
}
func
(
self
*
DebugApi
)
ProcessBlock
(
req
*
shared
.
Request
)
(
interface
{},
error
)
{
args
:=
new
(
BlockNumArg
)
if
err
:=
self
.
codec
.
Decode
(
req
.
Params
,
&
args
);
err
!=
nil
{
return
nil
,
shared
.
NewDecodeParamError
(
err
.
Error
())
}
block
:=
self
.
xeth
.
EthBlockByNumber
(
args
.
BlockNumber
)
if
block
==
nil
{
return
nil
,
fmt
.
Errorf
(
"block #%d not found"
,
args
.
BlockNumber
)
}
old
:=
vm
.
Debug
defer
func
()
{
vm
.
Debug
=
old
}()
vm
.
Debug
=
true
_
,
err
:=
self
.
ethereum
.
BlockProcessor
()
.
RetryProcess
(
block
)
if
err
==
nil
{
return
true
,
nil
}
return
false
,
err
}
func
(
self
*
DebugApi
)
SeedHash
(
req
*
shared
.
Request
)
(
interface
{},
error
)
{
args
:=
new
(
BlockNumArg
)
if
err
:=
self
.
codec
.
Decode
(
req
.
Params
,
&
args
);
err
!=
nil
{
return
nil
,
shared
.
NewDecodeParamError
(
err
.
Error
())
}
if
hash
,
err
:=
ethash
.
GetSeedHash
(
uint64
(
args
.
BlockNumber
));
err
==
nil
{
return
fmt
.
Sprintf
(
"0x%x"
,
hash
),
nil
}
else
{
return
nil
,
err
}
}
rpc/api/debug_args.go
0 → 100644
View file @
09d0d55f
package
api
import
(
"encoding/json"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/rpc/shared"
)
type
WaitForBlockArgs
struct
{
MinHeight
int
Timeout
int
// in seconds
}
func
(
args
*
WaitForBlockArgs
)
UnmarshalJSON
(
b
[]
byte
)
(
err
error
)
{
var
obj
[]
interface
{}
if
err
:=
json
.
Unmarshal
(
b
,
&
obj
);
err
!=
nil
{
return
shared
.
NewDecodeParamError
(
err
.
Error
())
}
if
len
(
obj
)
>
2
{
return
fmt
.
Errorf
(
"waitForArgs needs 0, 1, 2 arguments"
)
}
// default values when not provided
args
.
MinHeight
=
-
1
args
.
Timeout
=
-
1
if
len
(
obj
)
>=
1
{
var
minHeight
*
big
.
Int
if
minHeight
,
err
=
numString
(
obj
[
0
]);
err
!=
nil
{
return
err
}
args
.
MinHeight
=
int
(
minHeight
.
Int64
())
}
if
len
(
obj
)
>=
2
{
timeout
,
err
:=
numString
(
obj
[
1
])
if
err
!=
nil
{
return
err
}
args
.
Timeout
=
int
(
timeout
.
Int64
())
}
return
nil
}
rpc/api/debug_js.go
0 → 100644
View file @
09d0d55f
package
api
const
Debug_JS
=
`
web3.extend({
property: 'debug',
methods:
[
new web3.extend.Method({
name: 'printBlock',
call: 'debug_printBlock',
params: 1,
inputFormatter: [web3.extend.formatters.formatInputInt],
outputFormatter: web3.extend.formatters.formatOutputString
}),
new web3.extend.Method({
name: 'getBlockRlp',
call: 'debug_getBlockRlp',
params: 1,
inputFormatter: [web3.extend.formatters.formatInputInt],
outputFormatter: web3.extend.formatters.formatOutputString
}),
new web3.extend.Method({
name: 'setHead',
call: 'debug_setHead',
params: 1,
inputFormatter: [web3.extend.formatters.formatInputInt],
outputFormatter: web3.extend.formatters.formatOutputBool
}),
new web3.extend.Method({
name: 'processBlock',
call: 'debug_processBlock',
params: 1,
inputFormatter: [web3.extend.formatters.formatInputInt],
outputFormatter: function(obj) { return obj; }
}),
new web3.extend.Method({
name: 'seedHash',
call: 'debug_seedHash',
params: 1,
inputFormatter: [web3.extend.formatters.formatInputInt],
outputFormatter: web3.extend.formatters.formatOutputString
})
],
properties:
[
]
});
`
rpc/api/utils.go
View file @
09d0d55f
...
...
@@ -21,6 +21,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
for
i
,
name
:=
range
names
{
switch
strings
.
ToLower
(
strings
.
TrimSpace
(
name
))
{
case
DebugApiName
:
apis
[
i
]
=
NewDebugApi
(
xeth
,
eth
,
codec
)
case
EthApiName
:
apis
[
i
]
=
NewEthApi
(
xeth
,
codec
)
case
MinerApiName
:
...
...
@@ -39,6 +41,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
func
Javascript
(
name
string
)
string
{
switch
strings
.
ToLower
(
strings
.
TrimSpace
(
name
))
{
case
DebugApiName
:
return
Debug_JS
case
MinerApiName
:
return
Miner_JS
case
NetApiName
:
...
...
rpc/comms/ipc_unix.go
View file @
09d0d55f
...
...
@@ -3,9 +3,9 @@
package
comms
import
(
"io"
"io"
"net"
"os"
"os"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
...
...
@@ -70,8 +70,8 @@ func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error {
os
.
Remove
(
cfg
.
Endpoint
)
}()
glog
.
V
(
logger
.
Info
)
.
Infof
(
"IPC service started (%s)
\n
"
,
cfg
.
Endpoint
)
glog
.
V
(
logger
.
Info
)
.
Infof
(
"IPC service started (%s)
\n
"
,
cfg
.
Endpoint
)
return
nil
}
rpc/comms/ipc_windows.go
View file @
09d0d55f
// +build windows
package
comms
import
(
"fmt"
"io"
"net"
"os"
"sync"
"syscall"
"time"
"unsafe"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
)
var
(
modkernel32
=
syscall
.
NewLazyDLL
(
"kernel32.dll"
)
procCreateNamedPipeW
=
modkernel32
.
NewProc
(
"CreateNamedPipeW"
)
procConnectNamedPipe
=
modkernel32
.
NewProc
(
"ConnectNamedPipe"
)
procDisconnectNamedPipe
=
modkernel32
.
NewProc
(
"DisconnectNamedPipe"
)
procWaitNamedPipeW
=
modkernel32
.
NewProc
(
"WaitNamedPipeW"
)
procCreateEventW
=
modkernel32
.
NewProc
(
"CreateEventW"
)
procGetOverlappedResult
=
modkernel32
.
NewProc
(
"GetOverlappedResult"
)
procCancelIoEx
=
modkernel32
.
NewProc
(
"CancelIoEx"
)
)
func
createNamedPipe
(
name
*
uint16
,
openMode
uint32
,
pipeMode
uint32
,
maxInstances
uint32
,
outBufSize
uint32
,
inBufSize
uint32
,
defaultTimeout
uint32
,
sa
*
syscall
.
SecurityAttributes
)
(
handle
syscall
.
Handle
,
err
error
)
{
r0
,
_
,
e1
:=
syscall
.
Syscall9
(
procCreateNamedPipeW
.
Addr
(),
8
,
uintptr
(
unsafe
.
Pointer
(
name
)),
uintptr
(
openMode
),
uintptr
(
pipeMode
),
uintptr
(
maxInstances
),
uintptr
(
outBufSize
),
uintptr
(
inBufSize
),
uintptr
(
defaultTimeout
),
uintptr
(
unsafe
.
Pointer
(
sa
)),
0
)
handle
=
syscall
.
Handle
(
r0
)
if
handle
==
syscall
.
InvalidHandle
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
cancelIoEx
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procCancelIoEx
.
Addr
(),
2
,
uintptr
(
handle
),
uintptr
(
unsafe
.
Pointer
(
overlapped
)),
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
connectNamedPipe
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procConnectNamedPipe
.
Addr
(),
2
,
uintptr
(
handle
),
uintptr
(
unsafe
.
Pointer
(
overlapped
)),
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
disconnectNamedPipe
(
handle
syscall
.
Handle
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procDisconnectNamedPipe
.
Addr
(),
1
,
uintptr
(
handle
),
0
,
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
waitNamedPipe
(
name
*
uint16
,
timeout
uint32
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procWaitNamedPipeW
.
Addr
(),
2
,
uintptr
(
unsafe
.
Pointer
(
name
)),
uintptr
(
timeout
),
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
createEvent
(
sa
*
syscall
.
SecurityAttributes
,
manualReset
bool
,
initialState
bool
,
name
*
uint16
)
(
handle
syscall
.
Handle
,
err
error
)
{
var
_p0
uint32
if
manualReset
{
_p0
=
1
}
else
{
_p0
=
0
}
var
_p1
uint32
if
initialState
{
_p1
=
1
}
else
{
_p1
=
0
}
r0
,
_
,
e1
:=
syscall
.
Syscall6
(
procCreateEventW
.
Addr
(),
4
,
uintptr
(
unsafe
.
Pointer
(
sa
)),
uintptr
(
_p0
),
uintptr
(
_p1
),
uintptr
(
unsafe
.
Pointer
(
name
)),
0
,
0
)
handle
=
syscall
.
Handle
(
r0
)
if
handle
==
syscall
.
InvalidHandle
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
getOverlappedResult
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
,
transferred
*
uint32
,
wait
bool
)
(
err
error
)
{
var
_p0
uint32
if
wait
{
_p0
=
1
}
else
{
_p0
=
0
}
r1
,
_
,
e1
:=
syscall
.
Syscall6
(
procGetOverlappedResult
.
Addr
(),
4
,
uintptr
(
handle
),
uintptr
(
unsafe
.
Pointer
(
overlapped
)),
uintptr
(
unsafe
.
Pointer
(
transferred
)),
uintptr
(
_p0
),
0
,
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
const
(
// openMode
pipe_access_duplex
=
0x3
pipe_access_inbound
=
0x1
pipe_access_outbound
=
0x2
// openMode write flags
file_flag_first_pipe_instance
=
0x00080000
file_flag_write_through
=
0x80000000
file_flag_overlapped
=
0x40000000
// openMode ACL flags
write_dac
=
0x00040000
write_owner
=
0x00080000
access_system_security
=
0x01000000
// pipeMode
pipe_type_byte
=
0x0
pipe_type_message
=
0x4
// pipeMode read mode flags
pipe_readmode_byte
=
0x0
pipe_readmode_message
=
0x2
// pipeMode wait mode flags
pipe_wait
=
0x0
pipe_nowait
=
0x1
// pipeMode remote-client mode flags
pipe_accept_remote_clients
=
0x0
pipe_reject_remote_clients
=
0x8
pipe_unlimited_instances
=
255
nmpwait_wait_forever
=
0xFFFFFFFF
// the two not-an-errors below occur if a client connects to the pipe between
// the server's CreateNamedPipe and ConnectNamedPipe calls.
error_no_data
syscall
.
Errno
=
0xE8
error_pipe_connected
syscall
.
Errno
=
0x217
error_pipe_busy
syscall
.
Errno
=
0xE7
error_sem_timeout
syscall
.
Errno
=
0x79
error_bad_pathname
syscall
.
Errno
=
0xA1
error_invalid_name
syscall
.
Errno
=
0x7B
error_io_incomplete
syscall
.
Errno
=
0x3e4
)
var
_
net
.
Conn
=
(
*
PipeConn
)(
nil
)
var
_
net
.
Listener
=
(
*
PipeListener
)(
nil
)
// ErrClosed is the error returned by PipeListener.Accept when Close is called
// on the PipeListener.
var
ErrClosed
=
PipeError
{
"Pipe has been closed."
,
false
}
// PipeError is an error related to a call to a pipe
type
PipeError
struct
{
msg
string
timeout
bool
}
// Error implements the error interface
func
(
e
PipeError
)
Error
()
string
{
return
e
.
msg
}
// Timeout implements net.AddrError.Timeout()
func
(
e
PipeError
)
Timeout
()
bool
{
return
e
.
timeout
}
// Temporary implements net.AddrError.Temporary()
func
(
e
PipeError
)
Temporary
()
bool
{
return
false
}
// Dial connects to a named pipe with the given address. If the specified pipe is not available,
// it will wait indefinitely for the pipe to become available.
//
// The address must be of the form \\.\\pipe\<name> for local pipes and \\<computer>\pipe\<name>
// for remote pipes.
//
// Dial will return a PipeError if you pass in a badly formatted pipe name.
//
// Examples:
// // local pipe
// conn, err := Dial(`\\.\pipe\mypipename`)
//
// // remote pipe
// conn, err := Dial(`\\othercomp\pipe\mypipename`)
func
Dial
(
address
string
)
(
*
PipeConn
,
error
)
{
for
{
conn
,
err
:=
dial
(
address
,
nmpwait_wait_forever
)
if
err
==
nil
{
return
conn
,
nil
}
if
isPipeNotReady
(
err
)
{
<-
time
.
After
(
100
*
time
.
Millisecond
)
continue
}
return
nil
,
err
}
}
// DialTimeout acts like Dial, but will time out after the duration of timeout
func
DialTimeout
(
address
string
,
timeout
time
.
Duration
)
(
*
PipeConn
,
error
)
{
deadline
:=
time
.
Now
()
.
Add
(
timeout
)
now
:=
time
.
Now
()
for
now
.
Before
(
deadline
)
{
millis
:=
uint32
(
deadline
.
Sub
(
now
)
/
time
.
Millisecond
)
conn
,
err
:=
dial
(
address
,
millis
)
if
err
==
nil
{
return
conn
,
nil
}
if
err
==
error_sem_timeout
{
// This is WaitNamedPipe's timeout error, so we know we're done
return
nil
,
PipeError
{
fmt
.
Sprintf
(
"Timed out waiting for pipe '%s' to come available"
,
address
),
true
}
}
if
isPipeNotReady
(
err
)
{
left
:=
deadline
.
Sub
(
time
.
Now
())
retry
:=
100
*
time
.
Millisecond
if
left
>
retry
{
<-
time
.
After
(
retry
)
}
else
{
<-
time
.
After
(
left
-
time
.
Millisecond
)
}
now
=
time
.
Now
()
continue
}
return
nil
,
err
}
return
nil
,
PipeError
{
fmt
.
Sprintf
(
"Timed out waiting for pipe '%s' to come available"
,
address
),
true
}
}
// isPipeNotReady checks the error to see if it indicates the pipe is not ready
func
isPipeNotReady
(
err
error
)
bool
{
// Pipe Busy means another client just grabbed the open pipe end,
// and the server hasn't made a new one yet.
// File Not Found means the server hasn't created the pipe yet.
// Neither is a fatal error.
return
err
==
syscall
.
ERROR_FILE_NOT_FOUND
||
err
==
error_pipe_busy
}
// newOverlapped creates a structure used to track asynchronous
// I/O requests that have been issued.
func
newOverlapped
()
(
*
syscall
.
Overlapped
,
error
)
{
event
,
err
:=
createEvent
(
nil
,
true
,
true
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
syscall
.
Overlapped
{
HEvent
:
event
},
nil
}
// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete.
// This function returns the number of bytes transferred by the operation and an error code if
// applicable (nil otherwise).
func
waitForCompletion
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
)
(
uint32
,
error
)
{
_
,
err
:=
syscall
.
WaitForSingleObject
(
overlapped
.
HEvent
,
syscall
.
INFINITE
)
if
err
!=
nil
{
return
0
,
err
}
var
transferred
uint32
err
=
getOverlappedResult
(
handle
,
overlapped
,
&
transferred
,
true
)
return
transferred
,
err
}
// dial is a helper to initiate a connection to a named pipe that has been started by a server.
// The timeout is only enforced if the pipe server has already created the pipe, otherwise
// this function will return immediately.
func
dial
(
address
string
,
timeout
uint32
)
(
*
PipeConn
,
error
)
{
name
,
err
:=
syscall
.
UTF16PtrFromString
(
string
(
address
))
if
err
!=
nil
{
return
nil
,
err
}
// If at least one instance of the pipe has been created, this function
// will wait timeout milliseconds for it to become available.
// It will return immediately regardless of timeout, if no instances
// of the named pipe have been created yet.
// If this returns with no error, there is a pipe available.
if
err
:=
waitNamedPipe
(
name
,
timeout
);
err
!=
nil
{
if
err
==
error_bad_pathname
{
// badly formatted pipe name
return
nil
,
badAddr
(
address
)
}
return
nil
,
err
}
pathp
,
err
:=
syscall
.
UTF16PtrFromString
(
address
)
if
err
!=
nil
{
return
nil
,
err
}
handle
,
err
:=
syscall
.
CreateFile
(
pathp
,
syscall
.
GENERIC_READ
|
syscall
.
GENERIC_WRITE
,
uint32
(
syscall
.
FILE_SHARE_READ
|
syscall
.
FILE_SHARE_WRITE
),
nil
,
syscall
.
OPEN_EXISTING
,
syscall
.
FILE_FLAG_OVERLAPPED
,
0
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
PipeConn
{
handle
:
handle
,
addr
:
PipeAddr
(
address
)},
nil
}
// Listen returns a new PipeListener that will listen on a pipe with the given
// address. The address must be of the form \\.\pipe\<name>
//
// Listen will return a PipeError for an incorrectly formatted pipe name.
func
Listen
(
address
string
)
(
*
PipeListener
,
error
)
{
handle
,
err
:=
createPipe
(
address
,
true
)
if
err
==
error_invalid_name
{
return
nil
,
badAddr
(
address
)
}
if
err
!=
nil
{
return
nil
,
err
}
return
&
PipeListener
{
addr
:
PipeAddr
(
address
),
handle
:
handle
,
},
nil
}
// PipeListener is a named pipe listener. Clients should typically
// use variables of type net.Listener instead of assuming named pipe.
type
PipeListener
struct
{
addr
PipeAddr
handle
syscall
.
Handle
closed
bool
// acceptHandle contains the current handle waiting for
// an incoming connection or nil.
acceptHandle
syscall
.
Handle
// acceptOverlapped is set before waiting on a connection.
// If not waiting, it is nil.
acceptOverlapped
*
syscall
.
Overlapped
// acceptMutex protects the handle and overlapped structure.
acceptMutex
sync
.
Mutex
}
// Accept implements the Accept method in the net.Listener interface; it
// waits for the next call and returns a generic net.Conn.
func
(
l
*
PipeListener
)
Accept
()
(
net
.
Conn
,
error
)
{
c
,
err
:=
l
.
AcceptPipe
()
for
err
==
error_no_data
{
// Ignore clients that connect and immediately disconnect.
c
,
err
=
l
.
AcceptPipe
()
}
if
err
!=
nil
{
return
nil
,
err
}
return
c
,
nil
}
// AcceptPipe accepts the next incoming call and returns the new connection.
// It might return an error if a client connected and immediately cancelled
// the connection.
func
(
l
*
PipeListener
)
AcceptPipe
()
(
*
PipeConn
,
error
)
{
if
l
==
nil
||
l
.
addr
==
""
||
l
.
closed
{
return
nil
,
syscall
.
EINVAL
}
// the first time we call accept, the handle will have been created by the Listen
// call. This is to prevent race conditions where the client thinks the server
// isn't listening because it hasn't actually called create yet. After the first time, we'll
// have to create a new handle each time
handle
:=
l
.
handle
if
handle
==
0
{
var
err
error
handle
,
err
=
createPipe
(
string
(
l
.
addr
),
false
)
if
err
!=
nil
{
return
nil
,
err
}
}
else
{
l
.
handle
=
0
}
overlapped
,
err
:=
newOverlapped
()
if
err
!=
nil
{
return
nil
,
err
}
defer
syscall
.
CloseHandle
(
overlapped
.
HEvent
)
if
err
:=
connectNamedPipe
(
handle
,
overlapped
);
err
!=
nil
&&
err
!=
error_pipe_connected
{
if
err
==
error_io_incomplete
||
err
==
syscall
.
ERROR_IO_PENDING
{
l
.
acceptMutex
.
Lock
()
l
.
acceptOverlapped
=
overlapped
l
.
acceptHandle
=
handle
l
.
acceptMutex
.
Unlock
()
defer
func
()
{
l
.
acceptMutex
.
Lock
()
l
.
acceptOverlapped
=
nil
l
.
acceptHandle
=
0
l
.
acceptMutex
.
Unlock
()
}()
_
,
err
=
waitForCompletion
(
handle
,
overlapped
)
}
if
err
==
syscall
.
ERROR_OPERATION_ABORTED
{
// Return error compatible to net.Listener.Accept() in case the
// listener was closed.
return
nil
,
ErrClosed
}
if
err
!=
nil
{
return
nil
,
err
}
}
return
&
PipeConn
{
handle
:
handle
,
addr
:
l
.
addr
},
nil
}
// Close stops listening on the address.
// Already Accepted connections are not closed.
func
(
l
*
PipeListener
)
Close
()
error
{
if
l
.
closed
{
return
nil
}
l
.
closed
=
true
if
l
.
handle
!=
0
{
err
:=
disconnectNamedPipe
(
l
.
handle
)
if
err
!=
nil
{
return
err
}
err
=
syscall
.
CloseHandle
(
l
.
handle
)
if
err
!=
nil
{
return
err
}
l
.
handle
=
0
}
l
.
acceptMutex
.
Lock
()
defer
l
.
acceptMutex
.
Unlock
()
if
l
.
acceptOverlapped
!=
nil
&&
l
.
acceptHandle
!=
0
{
// Cancel the pending IO. This call does not block, so it is safe
// to hold onto the mutex above.
if
err
:=
cancelIoEx
(
l
.
acceptHandle
,
l
.
acceptOverlapped
);
err
!=
nil
{
return
err
}
err
:=
syscall
.
CloseHandle
(
l
.
acceptOverlapped
.
HEvent
)
if
err
!=
nil
{
return
err
}
l
.
acceptOverlapped
.
HEvent
=
0
err
=
syscall
.
CloseHandle
(
l
.
acceptHandle
)
if
err
!=
nil
{
return
err
}
l
.
acceptHandle
=
0
}
return
nil
}
// Addr returns the listener's network address, a PipeAddr.
func
(
l
*
PipeListener
)
Addr
()
net
.
Addr
{
return
l
.
addr
}
// PipeConn is the implementation of the net.Conn interface for named pipe connections.
type
PipeConn
struct
{
handle
syscall
.
Handle
addr
PipeAddr
// these aren't actually used yet
readDeadline
*
time
.
Time
writeDeadline
*
time
.
Time
}
type
iodata
struct
{
n
uint32
err
error
}
// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to
// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending,
// the content of iodata is returned.
func
(
c
*
PipeConn
)
completeRequest
(
data
iodata
,
deadline
*
time
.
Time
,
overlapped
*
syscall
.
Overlapped
)
(
int
,
error
)
{
if
data
.
err
==
error_io_incomplete
||
data
.
err
==
syscall
.
ERROR_IO_PENDING
{
var
timer
<-
chan
time
.
Time
if
deadline
!=
nil
{
if
timeDiff
:=
deadline
.
Sub
(
time
.
Now
());
timeDiff
>
0
{
timer
=
time
.
After
(
timeDiff
)
}
}
done
:=
make
(
chan
iodata
)
go
func
()
{
n
,
err
:=
waitForCompletion
(
c
.
handle
,
overlapped
)
done
<-
iodata
{
n
,
err
}
}()
select
{
case
data
=
<-
done
:
case
<-
timer
:
syscall
.
CancelIoEx
(
c
.
handle
,
overlapped
)
data
=
iodata
{
0
,
timeout
(
c
.
addr
.
String
())}
}
}
// Windows will produce ERROR_BROKEN_PIPE upon closing
// a handle on the other end of a connection. Go RPC
// expects an io.EOF error in this case.
if
data
.
err
==
syscall
.
ERROR_BROKEN_PIPE
{
data
.
err
=
io
.
EOF
}
return
int
(
data
.
n
),
data
.
err
}
// Read implements the net.Conn Read method.
func
(
c
*
PipeConn
)
Read
(
b
[]
byte
)
(
int
,
error
)
{
// Use ReadFile() rather than Read() because the latter
// contains a workaround that eats ERROR_BROKEN_PIPE.
overlapped
,
err
:=
newOverlapped
()
if
err
!=
nil
{
return
0
,
err
}
defer
syscall
.
CloseHandle
(
overlapped
.
HEvent
)
var
n
uint32
err
=
syscall
.
ReadFile
(
c
.
handle
,
b
,
&
n
,
overlapped
)
return
c
.
completeRequest
(
iodata
{
n
,
err
},
c
.
readDeadline
,
overlapped
)
}
// Write implements the net.Conn Write method.
func
(
c
*
PipeConn
)
Write
(
b
[]
byte
)
(
int
,
error
)
{
overlapped
,
err
:=
newOverlapped
()
if
err
!=
nil
{
return
0
,
err
}
defer
syscall
.
CloseHandle
(
overlapped
.
HEvent
)
var
n
uint32
err
=
syscall
.
WriteFile
(
c
.
handle
,
b
,
&
n
,
overlapped
)
return
c
.
completeRequest
(
iodata
{
n
,
err
},
c
.
writeDeadline
,
overlapped
)
}
// Close closes the connection.
func
(
c
*
PipeConn
)
Close
()
error
{
return
syscall
.
CloseHandle
(
c
.
handle
)
}
// LocalAddr returns the local network address.
func
(
c
*
PipeConn
)
LocalAddr
()
net
.
Addr
{
return
c
.
addr
}
// RemoteAddr returns the remote network address.
func
(
c
*
PipeConn
)
RemoteAddr
()
net
.
Addr
{
// not sure what to do here, we don't have remote addr....
return
c
.
addr
}
// SetDeadline implements the net.Conn SetDeadline method.
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
func
(
c
*
PipeConn
)
SetDeadline
(
t
time
.
Time
)
error
{
c
.
SetReadDeadline
(
t
)
c
.
SetWriteDeadline
(
t
)
return
nil
}
// SetReadDeadline implements the net.Conn SetReadDeadline method.
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
func
(
c
*
PipeConn
)
SetReadDeadline
(
t
time
.
Time
)
error
{
c
.
readDeadline
=
&
t
return
nil
}
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
func
(
c
*
PipeConn
)
SetWriteDeadline
(
t
time
.
Time
)
error
{
c
.
writeDeadline
=
&
t
return
nil
}
// PipeAddr represents the address of a named pipe.
type
PipeAddr
string
// Network returns the address's network name, "pipe".
func
(
a
PipeAddr
)
Network
()
string
{
return
"pipe"
}
// String returns the address of the pipe
func
(
a
PipeAddr
)
String
()
string
{
return
string
(
a
)
}
// createPipe is a helper function to make sure we always create pipes
// with the same arguments, since subsequent calls to create pipe need
// to use the same arguments as the first one. If first is set, fail
// if the pipe already exists.
func
createPipe
(
address
string
,
first
bool
)
(
syscall
.
Handle
,
error
)
{
n
,
err
:=
syscall
.
UTF16PtrFromString
(
address
)
if
err
!=
nil
{
return
0
,
err
}
mode
:=
uint32
(
pipe_access_duplex
|
syscall
.
FILE_FLAG_OVERLAPPED
)
if
first
{
mode
|=
file_flag_first_pipe_instance
}
return
createNamedPipe
(
n
,
mode
,
pipe_type_byte
,
pipe_unlimited_instances
,
512
,
512
,
0
,
nil
)
}
func
badAddr
(
addr
string
)
PipeError
{
return
PipeError
{
fmt
.
Sprintf
(
"Invalid pipe address '%s'."
,
addr
),
false
}
}
func
timeout
(
addr
string
)
PipeError
{
return
PipeError
{
fmt
.
Sprintf
(
"Pipe IO timed out waiting for '%s'"
,
addr
),
true
}
}
func
newIpcClient
(
cfg
IpcConfig
,
codec
codec
.
Codec
)
(
*
ipcClient
,
error
)
{
c
,
err
:=
Dial
(
cfg
.
Endpoint
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
ipcClient
{
codec
.
New
(
c
)},
nil
}
func
startIpc
(
cfg
IpcConfig
,
codec
codec
.
Codec
,
api
api
.
EthereumApi
)
error
{
os
.
Remove
(
cfg
.
Endpoint
)
// in case it still exists from a previous run
l
,
err
:=
Listen
(
cfg
.
Endpoint
)
if
err
!=
nil
{
return
err
}
os
.
Chmod
(
cfg
.
Endpoint
,
0600
)
go
func
()
{
for
{
conn
,
err
:=
l
.
Accept
()
if
err
!=
nil
{
glog
.
V
(
logger
.
Error
)
.
Infof
(
"Error accepting ipc connection - %v
\n
"
,
err
)
continue
}
go
func
(
conn
net
.
Conn
)
{
codec
:=
codec
.
New
(
conn
)
for
{
req
,
err
:=
codec
.
ReadRequest
()
if
err
==
io
.
EOF
{
codec
.
Close
()
return
}
else
if
err
!=
nil
{
glog
.
V
(
logger
.
Error
)
.
Infof
(
"IPC recv err - %v
\n
"
,
err
)
codec
.
Close
()
return
}
var
rpcResponse
interface
{}
res
,
err
:=
api
.
Execute
(
req
)
rpcResponse
=
shared
.
NewRpcResponse
(
req
.
Id
,
req
.
Jsonrpc
,
res
,
err
)
err
=
codec
.
WriteResponse
(
rpcResponse
)
if
err
!=
nil
{
glog
.
V
(
logger
.
Error
)
.
Infof
(
"IPC send err - %v
\n
"
,
err
)
codec
.
Close
()
return
}
}
}(
conn
)
}
}()
glog
.
V
(
logger
.
Info
)
.
Infof
(
"IPC service started (%s)
\n
"
,
cfg
.
Endpoint
)
return
nil
}
// +build windows
package
comms
import
(
"fmt"
"io"
"net"
"os"
"sync"
"syscall"
"time"
"unsafe"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
)
var
(
modkernel32
=
syscall
.
NewLazyDLL
(
"kernel32.dll"
)
procCreateNamedPipeW
=
modkernel32
.
NewProc
(
"CreateNamedPipeW"
)
procConnectNamedPipe
=
modkernel32
.
NewProc
(
"ConnectNamedPipe"
)
procDisconnectNamedPipe
=
modkernel32
.
NewProc
(
"DisconnectNamedPipe"
)
procWaitNamedPipeW
=
modkernel32
.
NewProc
(
"WaitNamedPipeW"
)
procCreateEventW
=
modkernel32
.
NewProc
(
"CreateEventW"
)
procGetOverlappedResult
=
modkernel32
.
NewProc
(
"GetOverlappedResult"
)
procCancelIoEx
=
modkernel32
.
NewProc
(
"CancelIoEx"
)
)
func
createNamedPipe
(
name
*
uint16
,
openMode
uint32
,
pipeMode
uint32
,
maxInstances
uint32
,
outBufSize
uint32
,
inBufSize
uint32
,
defaultTimeout
uint32
,
sa
*
syscall
.
SecurityAttributes
)
(
handle
syscall
.
Handle
,
err
error
)
{
r0
,
_
,
e1
:=
syscall
.
Syscall9
(
procCreateNamedPipeW
.
Addr
(),
8
,
uintptr
(
unsafe
.
Pointer
(
name
)),
uintptr
(
openMode
),
uintptr
(
pipeMode
),
uintptr
(
maxInstances
),
uintptr
(
outBufSize
),
uintptr
(
inBufSize
),
uintptr
(
defaultTimeout
),
uintptr
(
unsafe
.
Pointer
(
sa
)),
0
)
handle
=
syscall
.
Handle
(
r0
)
if
handle
==
syscall
.
InvalidHandle
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
cancelIoEx
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procCancelIoEx
.
Addr
(),
2
,
uintptr
(
handle
),
uintptr
(
unsafe
.
Pointer
(
overlapped
)),
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
connectNamedPipe
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procConnectNamedPipe
.
Addr
(),
2
,
uintptr
(
handle
),
uintptr
(
unsafe
.
Pointer
(
overlapped
)),
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
disconnectNamedPipe
(
handle
syscall
.
Handle
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procDisconnectNamedPipe
.
Addr
(),
1
,
uintptr
(
handle
),
0
,
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
waitNamedPipe
(
name
*
uint16
,
timeout
uint32
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procWaitNamedPipeW
.
Addr
(),
2
,
uintptr
(
unsafe
.
Pointer
(
name
)),
uintptr
(
timeout
),
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
createEvent
(
sa
*
syscall
.
SecurityAttributes
,
manualReset
bool
,
initialState
bool
,
name
*
uint16
)
(
handle
syscall
.
Handle
,
err
error
)
{
var
_p0
uint32
if
manualReset
{
_p0
=
1
}
else
{
_p0
=
0
}
var
_p1
uint32
if
initialState
{
_p1
=
1
}
else
{
_p1
=
0
}
r0
,
_
,
e1
:=
syscall
.
Syscall6
(
procCreateEventW
.
Addr
(),
4
,
uintptr
(
unsafe
.
Pointer
(
sa
)),
uintptr
(
_p0
),
uintptr
(
_p1
),
uintptr
(
unsafe
.
Pointer
(
name
)),
0
,
0
)
handle
=
syscall
.
Handle
(
r0
)
if
handle
==
syscall
.
InvalidHandle
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
getOverlappedResult
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
,
transferred
*
uint32
,
wait
bool
)
(
err
error
)
{
var
_p0
uint32
if
wait
{
_p0
=
1
}
else
{
_p0
=
0
}
r1
,
_
,
e1
:=
syscall
.
Syscall6
(
procGetOverlappedResult
.
Addr
(),
4
,
uintptr
(
handle
),
uintptr
(
unsafe
.
Pointer
(
overlapped
)),
uintptr
(
unsafe
.
Pointer
(
transferred
)),
uintptr
(
_p0
),
0
,
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
const
(
// openMode
pipe_access_duplex
=
0x3
pipe_access_inbound
=
0x1
pipe_access_outbound
=
0x2
// openMode write flags
file_flag_first_pipe_instance
=
0x00080000
file_flag_write_through
=
0x80000000
file_flag_overlapped
=
0x40000000
// openMode ACL flags
write_dac
=
0x00040000
write_owner
=
0x00080000
access_system_security
=
0x01000000
// pipeMode
pipe_type_byte
=
0x0
pipe_type_message
=
0x4
// pipeMode read mode flags
pipe_readmode_byte
=
0x0
pipe_readmode_message
=
0x2
// pipeMode wait mode flags
pipe_wait
=
0x0
pipe_nowait
=
0x1
// pipeMode remote-client mode flags
pipe_accept_remote_clients
=
0x0
pipe_reject_remote_clients
=
0x8
pipe_unlimited_instances
=
255
nmpwait_wait_forever
=
0xFFFFFFFF
// the two not-an-errors below occur if a client connects to the pipe between
// the server's CreateNamedPipe and ConnectNamedPipe calls.
error_no_data
syscall
.
Errno
=
0xE8
error_pipe_connected
syscall
.
Errno
=
0x217
error_pipe_busy
syscall
.
Errno
=
0xE7
error_sem_timeout
syscall
.
Errno
=
0x79
error_bad_pathname
syscall
.
Errno
=
0xA1
error_invalid_name
syscall
.
Errno
=
0x7B
error_io_incomplete
syscall
.
Errno
=
0x3e4
)
var
_
net
.
Conn
=
(
*
PipeConn
)(
nil
)
var
_
net
.
Listener
=
(
*
PipeListener
)(
nil
)
// ErrClosed is the error returned by PipeListener.Accept when Close is called
// on the PipeListener.
var
ErrClosed
=
PipeError
{
"Pipe has been closed."
,
false
}
// PipeError is an error related to a call to a pipe
type
PipeError
struct
{
msg
string
timeout
bool
}
// Error implements the error interface
func
(
e
PipeError
)
Error
()
string
{
return
e
.
msg
}
// Timeout implements net.AddrError.Timeout()
func
(
e
PipeError
)
Timeout
()
bool
{
return
e
.
timeout
}
// Temporary implements net.AddrError.Temporary()
func
(
e
PipeError
)
Temporary
()
bool
{
return
false
}
// Dial connects to a named pipe with the given address. If the specified pipe is not available,
// it will wait indefinitely for the pipe to become available.
//
// The address must be of the form \\.\\pipe\<name> for local pipes and \\<computer>\pipe\<name>
// for remote pipes.
//
// Dial will return a PipeError if you pass in a badly formatted pipe name.
//
// Examples:
// // local pipe
// conn, err := Dial(`\\.\pipe\mypipename`)
//
// // remote pipe
// conn, err := Dial(`\\othercomp\pipe\mypipename`)
func
Dial
(
address
string
)
(
*
PipeConn
,
error
)
{
for
{
conn
,
err
:=
dial
(
address
,
nmpwait_wait_forever
)
if
err
==
nil
{
return
conn
,
nil
}
if
isPipeNotReady
(
err
)
{
<-
time
.
After
(
100
*
time
.
Millisecond
)
continue
}
return
nil
,
err
}
}
// DialTimeout acts like Dial, but will time out after the duration of timeout
func
DialTimeout
(
address
string
,
timeout
time
.
Duration
)
(
*
PipeConn
,
error
)
{
deadline
:=
time
.
Now
()
.
Add
(
timeout
)
now
:=
time
.
Now
()
for
now
.
Before
(
deadline
)
{
millis
:=
uint32
(
deadline
.
Sub
(
now
)
/
time
.
Millisecond
)
conn
,
err
:=
dial
(
address
,
millis
)
if
err
==
nil
{
return
conn
,
nil
}
if
err
==
error_sem_timeout
{
// This is WaitNamedPipe's timeout error, so we know we're done
return
nil
,
PipeError
{
fmt
.
Sprintf
(
"Timed out waiting for pipe '%s' to come available"
,
address
),
true
}
}
if
isPipeNotReady
(
err
)
{
left
:=
deadline
.
Sub
(
time
.
Now
())
retry
:=
100
*
time
.
Millisecond
if
left
>
retry
{
<-
time
.
After
(
retry
)
}
else
{
<-
time
.
After
(
left
-
time
.
Millisecond
)
}
now
=
time
.
Now
()
continue
}
return
nil
,
err
}
return
nil
,
PipeError
{
fmt
.
Sprintf
(
"Timed out waiting for pipe '%s' to come available"
,
address
),
true
}
}
// isPipeNotReady checks the error to see if it indicates the pipe is not ready
func
isPipeNotReady
(
err
error
)
bool
{
// Pipe Busy means another client just grabbed the open pipe end,
// and the server hasn't made a new one yet.
// File Not Found means the server hasn't created the pipe yet.
// Neither is a fatal error.
return
err
==
syscall
.
ERROR_FILE_NOT_FOUND
||
err
==
error_pipe_busy
}
// newOverlapped creates a structure used to track asynchronous
// I/O requests that have been issued.
func
newOverlapped
()
(
*
syscall
.
Overlapped
,
error
)
{
event
,
err
:=
createEvent
(
nil
,
true
,
true
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
syscall
.
Overlapped
{
HEvent
:
event
},
nil
}
// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete.
// This function returns the number of bytes transferred by the operation and an error code if
// applicable (nil otherwise).
func
waitForCompletion
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
)
(
uint32
,
error
)
{
_
,
err
:=
syscall
.
WaitForSingleObject
(
overlapped
.
HEvent
,
syscall
.
INFINITE
)
if
err
!=
nil
{
return
0
,
err
}
var
transferred
uint32
err
=
getOverlappedResult
(
handle
,
overlapped
,
&
transferred
,
true
)
return
transferred
,
err
}
// dial is a helper to initiate a connection to a named pipe that has been started by a server.
// The timeout is only enforced if the pipe server has already created the pipe, otherwise
// this function will return immediately.
func
dial
(
address
string
,
timeout
uint32
)
(
*
PipeConn
,
error
)
{
name
,
err
:=
syscall
.
UTF16PtrFromString
(
string
(
address
))
if
err
!=
nil
{
return
nil
,
err
}
// If at least one instance of the pipe has been created, this function
// will wait timeout milliseconds for it to become available.
// It will return immediately regardless of timeout, if no instances
// of the named pipe have been created yet.
// If this returns with no error, there is a pipe available.
if
err
:=
waitNamedPipe
(
name
,
timeout
);
err
!=
nil
{
if
err
==
error_bad_pathname
{
// badly formatted pipe name
return
nil
,
badAddr
(
address
)
}
return
nil
,
err
}
pathp
,
err
:=
syscall
.
UTF16PtrFromString
(
address
)
if
err
!=
nil
{
return
nil
,
err
}
handle
,
err
:=
syscall
.
CreateFile
(
pathp
,
syscall
.
GENERIC_READ
|
syscall
.
GENERIC_WRITE
,
uint32
(
syscall
.
FILE_SHARE_READ
|
syscall
.
FILE_SHARE_WRITE
),
nil
,
syscall
.
OPEN_EXISTING
,
syscall
.
FILE_FLAG_OVERLAPPED
,
0
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
PipeConn
{
handle
:
handle
,
addr
:
PipeAddr
(
address
)},
nil
}
// Listen returns a new PipeListener that will listen on a pipe with the given
// address. The address must be of the form \\.\pipe\<name>
//
// Listen will return a PipeError for an incorrectly formatted pipe name.
func
Listen
(
address
string
)
(
*
PipeListener
,
error
)
{
handle
,
err
:=
createPipe
(
address
,
true
)
if
err
==
error_invalid_name
{
return
nil
,
badAddr
(
address
)
}
if
err
!=
nil
{
return
nil
,
err
}
return
&
PipeListener
{
addr
:
PipeAddr
(
address
),
handle
:
handle
,
},
nil
}
// PipeListener is a named pipe listener. Clients should typically
// use variables of type net.Listener instead of assuming named pipe.
type
PipeListener
struct
{
addr
PipeAddr
handle
syscall
.
Handle
closed
bool
// acceptHandle contains the current handle waiting for
// an incoming connection or nil.
acceptHandle
syscall
.
Handle
// acceptOverlapped is set before waiting on a connection.
// If not waiting, it is nil.
acceptOverlapped
*
syscall
.
Overlapped
// acceptMutex protects the handle and overlapped structure.
acceptMutex
sync
.
Mutex
}
// Accept implements the Accept method in the net.Listener interface; it
// waits for the next call and returns a generic net.Conn.
func
(
l
*
PipeListener
)
Accept
()
(
net
.
Conn
,
error
)
{
c
,
err
:=
l
.
AcceptPipe
()
for
err
==
error_no_data
{
// Ignore clients that connect and immediately disconnect.
c
,
err
=
l
.
AcceptPipe
()
}
if
err
!=
nil
{
return
nil
,
err
}
return
c
,
nil
}
// AcceptPipe accepts the next incoming call and returns the new connection.
// It might return an error if a client connected and immediately cancelled
// the connection.
func
(
l
*
PipeListener
)
AcceptPipe
()
(
*
PipeConn
,
error
)
{
if
l
==
nil
||
l
.
addr
==
""
||
l
.
closed
{
return
nil
,
syscall
.
EINVAL
}
// the first time we call accept, the handle will have been created by the Listen
// call. This is to prevent race conditions where the client thinks the server
// isn't listening because it hasn't actually called create yet. After the first time, we'll
// have to create a new handle each time
handle
:=
l
.
handle
if
handle
==
0
{
var
err
error
handle
,
err
=
createPipe
(
string
(
l
.
addr
),
false
)
if
err
!=
nil
{
return
nil
,
err
}
}
else
{
l
.
handle
=
0
}
overlapped
,
err
:=
newOverlapped
()
if
err
!=
nil
{
return
nil
,
err
}
defer
syscall
.
CloseHandle
(
overlapped
.
HEvent
)
if
err
:=
connectNamedPipe
(
handle
,
overlapped
);
err
!=
nil
&&
err
!=
error_pipe_connected
{
if
err
==
error_io_incomplete
||
err
==
syscall
.
ERROR_IO_PENDING
{
l
.
acceptMutex
.
Lock
()
l
.
acceptOverlapped
=
overlapped
l
.
acceptHandle
=
handle
l
.
acceptMutex
.
Unlock
()
defer
func
()
{
l
.
acceptMutex
.
Lock
()
l
.
acceptOverlapped
=
nil
l
.
acceptHandle
=
0
l
.
acceptMutex
.
Unlock
()
}()
_
,
err
=
waitForCompletion
(
handle
,
overlapped
)
}
if
err
==
syscall
.
ERROR_OPERATION_ABORTED
{
// Return error compatible to net.Listener.Accept() in case the
// listener was closed.
return
nil
,
ErrClosed
}
if
err
!=
nil
{
return
nil
,
err
}
}
return
&
PipeConn
{
handle
:
handle
,
addr
:
l
.
addr
},
nil
}
// Close stops listening on the address.
// Already Accepted connections are not closed.
func
(
l
*
PipeListener
)
Close
()
error
{
if
l
.
closed
{
return
nil
}
l
.
closed
=
true
if
l
.
handle
!=
0
{
err
:=
disconnectNamedPipe
(
l
.
handle
)
if
err
!=
nil
{
return
err
}
err
=
syscall
.
CloseHandle
(
l
.
handle
)
if
err
!=
nil
{
return
err
}
l
.
handle
=
0
}
l
.
acceptMutex
.
Lock
()
defer
l
.
acceptMutex
.
Unlock
()
if
l
.
acceptOverlapped
!=
nil
&&
l
.
acceptHandle
!=
0
{
// Cancel the pending IO. This call does not block, so it is safe
// to hold onto the mutex above.
if
err
:=
cancelIoEx
(
l
.
acceptHandle
,
l
.
acceptOverlapped
);
err
!=
nil
{
return
err
}
err
:=
syscall
.
CloseHandle
(
l
.
acceptOverlapped
.
HEvent
)
if
err
!=
nil
{
return
err
}
l
.
acceptOverlapped
.
HEvent
=
0
err
=
syscall
.
CloseHandle
(
l
.
acceptHandle
)
if
err
!=
nil
{
return
err
}
l
.
acceptHandle
=
0
}
return
nil
}
// Addr returns the listener's network address, a PipeAddr.
func
(
l
*
PipeListener
)
Addr
()
net
.
Addr
{
return
l
.
addr
}
// PipeConn is the implementation of the net.Conn interface for named pipe connections.
type
PipeConn
struct
{
handle
syscall
.
Handle
addr
PipeAddr
// these aren't actually used yet
readDeadline
*
time
.
Time
writeDeadline
*
time
.
Time
}
type
iodata
struct
{
n
uint32
err
error
}
// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to
// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending,
// the content of iodata is returned.
func
(
c
*
PipeConn
)
completeRequest
(
data
iodata
,
deadline
*
time
.
Time
,
overlapped
*
syscall
.
Overlapped
)
(
int
,
error
)
{
if
data
.
err
==
error_io_incomplete
||
data
.
err
==
syscall
.
ERROR_IO_PENDING
{
var
timer
<-
chan
time
.
Time
if
deadline
!=
nil
{
if
timeDiff
:=
deadline
.
Sub
(
time
.
Now
());
timeDiff
>
0
{
timer
=
time
.
After
(
timeDiff
)
}
}
done
:=
make
(
chan
iodata
)
go
func
()
{
n
,
err
:=
waitForCompletion
(
c
.
handle
,
overlapped
)
done
<-
iodata
{
n
,
err
}
}()
select
{
case
data
=
<-
done
:
case
<-
timer
:
syscall
.
CancelIoEx
(
c
.
handle
,
overlapped
)
data
=
iodata
{
0
,
timeout
(
c
.
addr
.
String
())}
}
}
// Windows will produce ERROR_BROKEN_PIPE upon closing
// a handle on the other end of a connection. Go RPC
// expects an io.EOF error in this case.
if
data
.
err
==
syscall
.
ERROR_BROKEN_PIPE
{
data
.
err
=
io
.
EOF
}
return
int
(
data
.
n
),
data
.
err
}
// Read implements the net.Conn Read method.
func
(
c
*
PipeConn
)
Read
(
b
[]
byte
)
(
int
,
error
)
{
// Use ReadFile() rather than Read() because the latter
// contains a workaround that eats ERROR_BROKEN_PIPE.
overlapped
,
err
:=
newOverlapped
()
if
err
!=
nil
{
return
0
,
err
}
defer
syscall
.
CloseHandle
(
overlapped
.
HEvent
)
var
n
uint32
err
=
syscall
.
ReadFile
(
c
.
handle
,
b
,
&
n
,
overlapped
)
return
c
.
completeRequest
(
iodata
{
n
,
err
},
c
.
readDeadline
,
overlapped
)
}
// Write implements the net.Conn Write method.
func
(
c
*
PipeConn
)
Write
(
b
[]
byte
)
(
int
,
error
)
{
overlapped
,
err
:=
newOverlapped
()
if
err
!=
nil
{
return
0
,
err
}
defer
syscall
.
CloseHandle
(
overlapped
.
HEvent
)
var
n
uint32
err
=
syscall
.
WriteFile
(
c
.
handle
,
b
,
&
n
,
overlapped
)
return
c
.
completeRequest
(
iodata
{
n
,
err
},
c
.
writeDeadline
,
overlapped
)
}
// Close closes the connection.
func
(
c
*
PipeConn
)
Close
()
error
{
return
syscall
.
CloseHandle
(
c
.
handle
)
}
// LocalAddr returns the local network address.
func
(
c
*
PipeConn
)
LocalAddr
()
net
.
Addr
{
return
c
.
addr
}
// RemoteAddr returns the remote network address.
func
(
c
*
PipeConn
)
RemoteAddr
()
net
.
Addr
{
// not sure what to do here, we don't have remote addr....
return
c
.
addr
}
// SetDeadline implements the net.Conn SetDeadline method.
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
func
(
c
*
PipeConn
)
SetDeadline
(
t
time
.
Time
)
error
{
c
.
SetReadDeadline
(
t
)
c
.
SetWriteDeadline
(
t
)
return
nil
}
// SetReadDeadline implements the net.Conn SetReadDeadline method.
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
func
(
c
*
PipeConn
)
SetReadDeadline
(
t
time
.
Time
)
error
{
c
.
readDeadline
=
&
t
return
nil
}
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
func
(
c
*
PipeConn
)
SetWriteDeadline
(
t
time
.
Time
)
error
{
c
.
writeDeadline
=
&
t
return
nil
}
// PipeAddr represents the address of a named pipe.
type
PipeAddr
string
// Network returns the address's network name, "pipe".
func
(
a
PipeAddr
)
Network
()
string
{
return
"pipe"
}
// String returns the address of the pipe
func
(
a
PipeAddr
)
String
()
string
{
return
string
(
a
)
}
// createPipe is a helper function to make sure we always create pipes
// with the same arguments, since subsequent calls to create pipe need
// to use the same arguments as the first one. If first is set, fail
// if the pipe already exists.
func
createPipe
(
address
string
,
first
bool
)
(
syscall
.
Handle
,
error
)
{
n
,
err
:=
syscall
.
UTF16PtrFromString
(
address
)
if
err
!=
nil
{
return
0
,
err
}
mode
:=
uint32
(
pipe_access_duplex
|
syscall
.
FILE_FLAG_OVERLAPPED
)
if
first
{
mode
|=
file_flag_first_pipe_instance
}
return
createNamedPipe
(
n
,
mode
,
pipe_type_byte
,
pipe_unlimited_instances
,
512
,
512
,
0
,
nil
)
}
func
badAddr
(
addr
string
)
PipeError
{
return
PipeError
{
fmt
.
Sprintf
(
"Invalid pipe address '%s'."
,
addr
),
false
}
}
func
timeout
(
addr
string
)
PipeError
{
return
PipeError
{
fmt
.
Sprintf
(
"Pipe IO timed out waiting for '%s'"
,
addr
),
true
}
}
func
newIpcClient
(
cfg
IpcConfig
,
codec
codec
.
Codec
)
(
*
ipcClient
,
error
)
{
c
,
err
:=
Dial
(
cfg
.
Endpoint
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
ipcClient
{
codec
.
New
(
c
)},
nil
}
func
startIpc
(
cfg
IpcConfig
,
codec
codec
.
Codec
,
api
api
.
EthereumApi
)
error
{
os
.
Remove
(
cfg
.
Endpoint
)
// in case it still exists from a previous run
l
,
err
:=
Listen
(
cfg
.
Endpoint
)
if
err
!=
nil
{
return
err
}
os
.
Chmod
(
cfg
.
Endpoint
,
0600
)
go
func
()
{
for
{
conn
,
err
:=
l
.
Accept
()
if
err
!=
nil
{
glog
.
V
(
logger
.
Error
)
.
Infof
(
"Error accepting ipc connection - %v
\n
"
,
err
)
continue
}
go
func
(
conn
net
.
Conn
)
{
codec
:=
codec
.
New
(
conn
)
for
{
req
,
err
:=
codec
.
ReadRequest
()
if
err
==
io
.
EOF
{
codec
.
Close
()
return
}
else
if
err
!=
nil
{
glog
.
V
(
logger
.
Error
)
.
Infof
(
"IPC recv err - %v
\n
"
,
err
)
codec
.
Close
()
return
}
var
rpcResponse
interface
{}
res
,
err
:=
api
.
Execute
(
req
)
rpcResponse
=
shared
.
NewRpcResponse
(
req
.
Id
,
req
.
Jsonrpc
,
res
,
err
)
err
=
codec
.
WriteResponse
(
rpcResponse
)
if
err
!=
nil
{
glog
.
V
(
logger
.
Error
)
.
Infof
(
"IPC send err - %v
\n
"
,
err
)
codec
.
Close
()
return
}
}
}(
conn
)
}
}()
glog
.
V
(
logger
.
Info
)
.
Infof
(
"IPC service started (%s)
\n
"
,
cfg
.
Endpoint
)
return
nil
}
rpc/jeth.go
View file @
09d0d55f
...
...
@@ -4,12 +4,13 @@ import (
"encoding/json"
"fmt"
"reflect"
"github.com/ethereum/go-ethereum/jsre"
"github.com/robertkrimen/otto"
"github.com/ethereum/go-ethereum/rpc/comms"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/comms"
"github.com/ethereum/go-ethereum/rpc/shared"
"
reflect
"
"
github.com/robertkrimen/otto
"
)
type
Jeth
struct
{
...
...
rpc/shared/types.go
View file @
09d0d55f
...
...
@@ -2,6 +2,7 @@ package shared
import
(
"encoding/json"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
)
...
...
@@ -45,15 +46,15 @@ func NewRpcResponse(id interface{}, jsonrpcver string, reply interface{}, err er
var
response
interface
{}
switch
err
.
(
type
)
{
case
nil
:
case
nil
:
response
=
&
SuccessResponse
{
Jsonrpc
:
jsonrpcver
,
Id
:
id
,
Result
:
reply
}
case
*
NotImplementedError
:
case
*
NotImplementedError
:
jsonerr
:=
&
ErrorObject
{
-
32601
,
err
.
Error
()}
response
=
&
ErrorResponse
{
Jsonrpc
:
jsonrpcver
,
Id
:
id
,
Error
:
jsonerr
}
case
*
DecodeParamError
,
*
InsufficientParamsError
,
*
ValidationError
,
*
InvalidTypeError
:
case
*
DecodeParamError
,
*
InsufficientParamsError
,
*
ValidationError
,
*
InvalidTypeError
:
jsonerr
:=
&
ErrorObject
{
-
32602
,
err
.
Error
()}
response
=
&
ErrorResponse
{
Jsonrpc
:
jsonrpcver
,
Id
:
id
,
Error
:
jsonerr
}
default
:
default
:
jsonerr
:=
&
ErrorObject
{
-
32603
,
err
.
Error
()}
response
=
&
ErrorResponse
{
Jsonrpc
:
jsonrpcver
,
Id
:
id
,
Error
:
jsonerr
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment