implement #60
This commit is contained in:
parent
7c185b1731
commit
bf1147291b
|
@ -24,7 +24,7 @@ requires "nim > 0.18.0",
|
||||||
"json_serialization"
|
"json_serialization"
|
||||||
|
|
||||||
proc runTest(name: string, defs = "", lang = "c") =
|
proc runTest(name: string, defs = "", lang = "c") =
|
||||||
exec "nim " & lang & " " & defs & " -d:testing --experimental:ForLoopMacros -r tests/" & name
|
exec "nim " & lang & " " & defs & " -d:testing --threads:on --experimental:ForLoopMacros -r tests/" & name
|
||||||
|
|
||||||
task test, "Runs the test suite":
|
task test, "Runs the test suite":
|
||||||
runTest "all_tests"
|
runTest "all_tests"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import
|
||||||
private/types, rlpx, ../eth_p2p
|
private/types, rlpx, ../eth_p2p
|
||||||
|
|
||||||
type
|
type
|
||||||
Action = proc (p: Peer, data: Rlp): Future[void]
|
Action = proc (p: Peer, data: Rlp): Future[void] {.gcsafe.}
|
||||||
|
|
||||||
ProtocolMessagePair = object
|
ProtocolMessagePair = object
|
||||||
protocol: ProtocolInfo
|
protocol: ProtocolInfo
|
||||||
|
@ -87,10 +87,10 @@ proc expectationViolationMsg(mock: MockConf,
|
||||||
result.add "\n"
|
result.add "\n"
|
||||||
|
|
||||||
proc addProtocol(mock: MockConf, p: ProtocolInfo): ProtocolInfo =
|
proc addProtocol(mock: MockConf, p: ProtocolInfo): ProtocolInfo =
|
||||||
new result
|
result = create ProtocolInfoObj
|
||||||
deepCopy(result[], p[])
|
deepCopy(result[], p[])
|
||||||
|
|
||||||
proc incomingMsgHandler(p: Peer, receivedMsgId: int, rlp: Rlp): Future[void] =
|
proc incomingMsgHandler(p: Peer, receivedMsgId: int, rlp: Rlp): Future[void] {.gcsafe.} =
|
||||||
let (receivedMsgProto, receivedMsgInfo) = p.getMsgMetadata(receivedMsgId)
|
let (receivedMsgProto, receivedMsgInfo) = p.getMsgMetadata(receivedMsgId)
|
||||||
let expectedMsgIdx = mock.receivedMsgsCount
|
let expectedMsgIdx = mock.receivedMsgsCount
|
||||||
|
|
||||||
|
|
|
@ -58,8 +58,8 @@ type
|
||||||
observers*: Table[int, PeerObserver]
|
observers*: Table[int, PeerObserver]
|
||||||
|
|
||||||
PeerObserver* = object
|
PeerObserver* = object
|
||||||
onPeerConnected*: proc(p: Peer)
|
onPeerConnected*: proc(p: Peer) {.gcsafe.}
|
||||||
onPeerDisconnected*: proc(p: Peer)
|
onPeerDisconnected*: proc(p: Peer) {.gcsafe.}
|
||||||
|
|
||||||
Capability* = object
|
Capability* = object
|
||||||
name*: string
|
name*: string
|
||||||
|
@ -80,7 +80,7 @@ type
|
||||||
## Quasy-private types. Use at your own risk.
|
## Quasy-private types. Use at your own risk.
|
||||||
##
|
##
|
||||||
|
|
||||||
ProtocolInfo* = ref object
|
ProtocolInfoObj* = object
|
||||||
name*: string
|
name*: string
|
||||||
version*: int
|
version*: int
|
||||||
messages*: seq[MessageInfo]
|
messages*: seq[MessageInfo]
|
||||||
|
@ -93,6 +93,8 @@ type
|
||||||
handshake*: HandshakeStep
|
handshake*: HandshakeStep
|
||||||
disconnectHandler*: DisconnectionHandler
|
disconnectHandler*: DisconnectionHandler
|
||||||
|
|
||||||
|
ProtocolInfo* = ptr ProtocolInfoObj
|
||||||
|
|
||||||
MessageInfo* = object
|
MessageInfo* = object
|
||||||
id*: int
|
id*: int
|
||||||
name*: string
|
name*: string
|
||||||
|
@ -105,8 +107,8 @@ type
|
||||||
|
|
||||||
Dispatcher* = ref object # private
|
Dispatcher* = ref object # private
|
||||||
# The dispatcher stores the mapping of negotiated message IDs between
|
# The dispatcher stores the mapping of negotiated message IDs between
|
||||||
# two connected peers. The dispatcher objects are shared between
|
# two connected peers. The dispatcher may be shared between connections
|
||||||
# connections running with the same set of supported protocols.
|
# running with the same set of supported protocols.
|
||||||
#
|
#
|
||||||
# `protocolOffsets` will hold one slot of each locally supported
|
# `protocolOffsets` will hold one slot of each locally supported
|
||||||
# protocol. If the other peer also supports the protocol, the stored
|
# protocol. If the other peer also supports the protocol, the stored
|
||||||
|
@ -131,13 +133,13 @@ type
|
||||||
|
|
||||||
# Private types:
|
# Private types:
|
||||||
MessageHandlerDecorator* = proc(msgId: int, n: NimNode): NimNode
|
MessageHandlerDecorator* = proc(msgId: int, n: NimNode): NimNode
|
||||||
MessageHandler* = proc(x: Peer, msgId: int, data: Rlp): Future[void]
|
MessageHandler* = proc(x: Peer, msgId: int, data: Rlp): Future[void] {.gcsafe.}
|
||||||
MessageContentPrinter* = proc(msg: pointer): string
|
MessageContentPrinter* = proc(msg: pointer): string {.gcsafe.}
|
||||||
RequestResolver* = proc(msg: pointer, future: FutureBase)
|
RequestResolver* = proc(msg: pointer, future: FutureBase) {.gcsafe.}
|
||||||
NextMsgResolver* = proc(msgData: Rlp, future: FutureBase)
|
NextMsgResolver* = proc(msgData: Rlp, future: FutureBase) {.gcsafe.}
|
||||||
PeerStateInitializer* = proc(peer: Peer): RootRef
|
PeerStateInitializer* = proc(peer: Peer): RootRef {.gcsafe.}
|
||||||
NetworkStateInitializer* = proc(network: EthereumNode): RootRef
|
NetworkStateInitializer* = proc(network: EthereumNode): RootRef {.gcsafe.}
|
||||||
HandshakeStep* = proc(peer: Peer): Future[void]
|
HandshakeStep* = proc(peer: Peer): Future[void] {.gcsafe.}
|
||||||
DisconnectionHandler* = proc(peer: Peer,
|
DisconnectionHandler* = proc(peer: Peer,
|
||||||
reason: DisconnectionReason): Future[void] {.gcsafe.}
|
reason: DisconnectionReason): Future[void] {.gcsafe.}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ when tracingEnabled:
|
||||||
|
|
||||||
var
|
var
|
||||||
gProtocols: seq[ProtocolInfo]
|
gProtocols: seq[ProtocolInfo]
|
||||||
gDispatchers = initSet[Dispatcher]()
|
|
||||||
gDevp2pInfo: ProtocolInfo
|
gDevp2pInfo: ProtocolInfo
|
||||||
|
|
||||||
# The variables above are immutable RTTI information. We need to tell
|
# The variables above are immutable RTTI information. We need to tell
|
||||||
|
@ -43,7 +42,7 @@ proc newFuture[T](location: var Future[T]) =
|
||||||
proc `$`*(p: Peer): string {.inline.} =
|
proc `$`*(p: Peer): string {.inline.} =
|
||||||
$p.remote
|
$p.remote
|
||||||
|
|
||||||
proc disconnect*(peer: Peer, reason: DisconnectionReason, notifyOtherPeer = true) {.async.}
|
proc disconnect*(peer: Peer, reason: DisconnectionReason, notifyOtherPeer = true) {.gcsafe, async.}
|
||||||
|
|
||||||
template raisePeerDisconnected(msg: string, r: DisconnectionReason) =
|
template raisePeerDisconnected(msg: string, r: DisconnectionReason) =
|
||||||
var e = newException(PeerDisconnected, msg)
|
var e = newException(PeerDisconnected, msg)
|
||||||
|
@ -81,7 +80,7 @@ proc getDispatcher(node: EthereumNode,
|
||||||
# https://github.com/nim-lang/Nim/issues/7457
|
# https://github.com/nim-lang/Nim/issues/7457
|
||||||
# We should be able to find an existing dispatcher without allocating a new one
|
# We should be able to find an existing dispatcher without allocating a new one
|
||||||
|
|
||||||
new(result)
|
new result
|
||||||
newSeq(result.protocolOffsets, allProtocols.len)
|
newSeq(result.protocolOffsets, allProtocols.len)
|
||||||
result.protocolOffsets.fill -1
|
result.protocolOffsets.fill -1
|
||||||
|
|
||||||
|
@ -97,9 +96,6 @@ proc getDispatcher(node: EthereumNode,
|
||||||
nextUserMsgId += localProtocol.messages.len
|
nextUserMsgId += localProtocol.messages.len
|
||||||
break findMatchingProtocol
|
break findMatchingProtocol
|
||||||
|
|
||||||
if result in gDispatchers:
|
|
||||||
return gDispatchers[result]
|
|
||||||
else:
|
|
||||||
template copyTo(src, dest; index: int) =
|
template copyTo(src, dest; index: int) =
|
||||||
for i in 0 ..< src.len:
|
for i in 0 ..< src.len:
|
||||||
dest[index + i] = addr src[i]
|
dest[index + i] = addr src[i]
|
||||||
|
@ -114,8 +110,6 @@ proc getDispatcher(node: EthereumNode,
|
||||||
localProtocol.messages.copyTo(result.messages,
|
localProtocol.messages.copyTo(result.messages,
|
||||||
result.protocolOffsets[idx])
|
result.protocolOffsets[idx])
|
||||||
|
|
||||||
gDispatchers.incl result
|
|
||||||
|
|
||||||
proc getMsgName*(peer: Peer, msgId: int): string =
|
proc getMsgName*(peer: Peer, msgId: int): string =
|
||||||
if not peer.dispatcher.isNil and
|
if not peer.dispatcher.isNil and
|
||||||
msgId < peer.dispatcher.messages.len:
|
msgId < peer.dispatcher.messages.len:
|
||||||
|
@ -147,7 +141,7 @@ proc getMsgMetadata*(peer: Peer, msgId: int): (ProtocolInfo, ptr MessageInfo) =
|
||||||
proc newProtocol(name: string, version: int,
|
proc newProtocol(name: string, version: int,
|
||||||
peerInit: PeerStateInitializer,
|
peerInit: PeerStateInitializer,
|
||||||
networkInit: NetworkStateInitializer): ProtocolInfo =
|
networkInit: NetworkStateInitializer): ProtocolInfo =
|
||||||
new result
|
result = create ProtocolInfoObj
|
||||||
result.name = name
|
result.name = name
|
||||||
result.version = version
|
result.version = version
|
||||||
result.messages = @[]
|
result.messages = @[]
|
||||||
|
@ -211,7 +205,7 @@ proc requestResolver[MsgType](msg: pointer, future: FutureBase) =
|
||||||
exc = getCurrentException().name,
|
exc = getCurrentException().name,
|
||||||
err = getCurrentExceptionMsg()
|
err = getCurrentExceptionMsg()
|
||||||
|
|
||||||
proc registerMsg(protocol: var ProtocolInfo,
|
proc registerMsg(protocol: ProtocolInfo,
|
||||||
id: int, name: string,
|
id: int, name: string,
|
||||||
thunk: MessageHandler,
|
thunk: MessageHandler,
|
||||||
printer: MessageContentPrinter,
|
printer: MessageContentPrinter,
|
||||||
|
@ -285,7 +279,7 @@ template compressMsg(peer: Peer, data: Bytes): Bytes =
|
||||||
else:
|
else:
|
||||||
data
|
data
|
||||||
|
|
||||||
proc sendMsg*(peer: Peer, data: Bytes) {.async.} =
|
proc sendMsg*(peer: Peer, data: Bytes) {.gcsafe, async.} =
|
||||||
var cipherText = encryptMsg(peer.compressMsg(data), peer.secretsState)
|
var cipherText = encryptMsg(peer.compressMsg(data), peer.secretsState)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -379,11 +373,12 @@ proc resolveResponseFuture(peer: Peer, msgId: int, msg: pointer, reqId: int) =
|
||||||
template req: auto = outstandingReqs()[idx]
|
template req: auto = outstandingReqs()[idx]
|
||||||
|
|
||||||
if req.future.finished:
|
if req.future.finished:
|
||||||
assert req.timeoutAt < fastEpochTime()
|
assert req.timeoutAt <= fastEpochTime()
|
||||||
# Here we'll remove the expired request by swapping
|
# Here we'll remove the expired request by swapping
|
||||||
# it with the last one in the deque (if necessary):
|
# it with the last one in the deque (if necessary):
|
||||||
if idx != outstandingReqs.len - 1:
|
if idx != outstandingReqs.len - 1:
|
||||||
req = outstandingReqs.popLast
|
req = outstandingReqs.popLast
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
outstandingReqs.shrink(fromLast = 1)
|
outstandingReqs.shrink(fromLast = 1)
|
||||||
# This was the last item, so we don't have any
|
# This was the last item, so we don't have any
|
||||||
|
@ -573,7 +568,7 @@ template networkState*(connection: Peer, Protocol: type): untyped =
|
||||||
## particular connection.
|
## particular connection.
|
||||||
protocolState(connection.network, Protocol)
|
protocolState(connection.network, Protocol)
|
||||||
|
|
||||||
proc initProtocolState*[T](state: T, x: Peer|EthereumNode) = discard
|
proc initProtocolState*[T](state: T, x: Peer|EthereumNode) {.gcsafe.} = discard
|
||||||
|
|
||||||
proc createPeerState[ProtocolState](peer: Peer): RootRef =
|
proc createPeerState[ProtocolState](peer: Peer): RootRef =
|
||||||
var res = new ProtocolState
|
var res = new ProtocolState
|
||||||
|
@ -581,7 +576,7 @@ proc createPeerState[ProtocolState](peer: Peer): RootRef =
|
||||||
initProtocolState(res, peer)
|
initProtocolState(res, peer)
|
||||||
return cast[RootRef](res)
|
return cast[RootRef](res)
|
||||||
|
|
||||||
proc createNetworkState[NetworkState](network: EthereumNode): RootRef =
|
proc createNetworkState[NetworkState](network: EthereumNode): RootRef {.gcsafe.} =
|
||||||
var res = new NetworkState
|
var res = new NetworkState
|
||||||
mixin initProtocolState
|
mixin initProtocolState
|
||||||
initProtocolState(res, network)
|
initProtocolState(res, network)
|
||||||
|
@ -688,6 +683,7 @@ macro p2pProtocolImpl(name: static[string],
|
||||||
of rlpxResponse: userHandlerProc.applyDecorator incomingResponseDecorator
|
of rlpxResponse: userHandlerProc.applyDecorator incomingResponseDecorator
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
|
userHandlerProc.addPragma ident"gcsafe"
|
||||||
userHandlerProc.addPragma ident"async"
|
userHandlerProc.addPragma ident"async"
|
||||||
|
|
||||||
# We allow the user handler to use `openarray` params, but we turn
|
# We allow the user handler to use `openarray` params, but we turn
|
||||||
|
@ -882,7 +878,7 @@ macro p2pProtocolImpl(name: static[string],
|
||||||
|
|
||||||
let thunkName = ident(msgName & "_thunk")
|
let thunkName = ident(msgName & "_thunk")
|
||||||
var thunkProc = quote do:
|
var thunkProc = quote do:
|
||||||
proc `thunkName`(`msgSender`: `Peer`, _: int, data: Rlp) =
|
proc `thunkName`(`msgSender`: `Peer`, _: int, data: Rlp) {.gcsafe.} =
|
||||||
var `receivedRlp` = data
|
var `receivedRlp` = data
|
||||||
var `receivedMsg` {.noinit.}: `msgRecord`
|
var `receivedMsg` {.noinit.}: `msgRecord`
|
||||||
`readParamsPrelude`
|
`readParamsPrelude`
|
||||||
|
@ -917,6 +913,7 @@ macro p2pProtocolImpl(name: static[string],
|
||||||
var msgSendProc = n
|
var msgSendProc = n
|
||||||
# TODO: check that the first param has the correct type
|
# TODO: check that the first param has the correct type
|
||||||
msgSendProc.params[1][0] = msgRecipient
|
msgSendProc.params[1][0] = msgRecipient
|
||||||
|
msgSendProc.addPragma ident"gcsafe"
|
||||||
|
|
||||||
# Add a timeout parameter for all request procs
|
# Add a timeout parameter for all request procs
|
||||||
case msgKind
|
case msgKind
|
||||||
|
@ -1089,7 +1086,6 @@ macro p2pProtocolImpl(name: static[string],
|
||||||
setEventHandlers(`protocol`, `handshake`, `disconnectHandler`)
|
setEventHandlers(`protocol`, `handshake`, `disconnectHandler`)
|
||||||
|
|
||||||
result.add newCall(bindSym("registerProtocol"), protocol)
|
result.add newCall(bindSym("registerProtocol"), protocol)
|
||||||
when isMainModule: echo repr(result)
|
|
||||||
|
|
||||||
when defined(debugRlpxProtocol) or defined(debugMacros):
|
when defined(debugRlpxProtocol) or defined(debugMacros):
|
||||||
echo repr(result)
|
echo repr(result)
|
||||||
|
|
|
@ -100,7 +100,7 @@ type
|
||||||
## XXX: really big messages can cause excessive mem usage when using msg \
|
## XXX: really big messages can cause excessive mem usage when using msg \
|
||||||
## count
|
## count
|
||||||
|
|
||||||
FilterMsgHandler* = proc(msg: ReceivedMessage) {.closure.}
|
FilterMsgHandler* = proc(msg: ReceivedMessage) {.gcsafe, closure.}
|
||||||
|
|
||||||
Filter* = object
|
Filter* = object
|
||||||
src: Option[PublicKey]
|
src: Option[PublicKey]
|
||||||
|
@ -581,7 +581,7 @@ proc subscribeFilter*(filters: var Filters, filter: Filter,
|
||||||
debug "Filter added", filter = id
|
debug "Filter added", filter = id
|
||||||
return id
|
return id
|
||||||
|
|
||||||
proc notify*(filters: var Filters, msg: Message) =
|
proc notify*(filters: var Filters, msg: Message) {.gcsafe.} =
|
||||||
var decoded: Option[DecodedPayload]
|
var decoded: Option[DecodedPayload]
|
||||||
var keyHash: Hash
|
var keyHash: Hash
|
||||||
|
|
||||||
|
@ -667,10 +667,10 @@ type
|
||||||
filters*: Filters
|
filters*: Filters
|
||||||
config*: WhisperConfig
|
config*: WhisperConfig
|
||||||
|
|
||||||
proc run(peer: Peer) {.async.}
|
proc run(peer: Peer) {.gcsafe, async.}
|
||||||
proc run(node: EthereumNode, network: WhisperNetwork) {.async.}
|
proc run(node: EthereumNode, network: WhisperNetwork) {.gcsafe, async.}
|
||||||
|
|
||||||
proc initProtocolState*(network: WhisperNetwork, node: EthereumNode) =
|
proc initProtocolState*(network: WhisperNetwork, node: EthereumNode) {.gcsafe.} =
|
||||||
network.queue = initQueue(defaultQueueCapacity)
|
network.queue = initQueue(defaultQueueCapacity)
|
||||||
network.filters = initTable[string, Filter]()
|
network.filters = initTable[string, Filter]()
|
||||||
network.config.bloom = fullBloom()
|
network.config.bloom = fullBloom()
|
||||||
|
|
Loading…
Reference in New Issue