2019-06-05 01:59:35 +00:00
|
|
|
var
|
|
|
|
gProtocols: seq[ProtocolInfo]
|
|
|
|
|
|
|
|
# The variables above are immutable RTTI information. We need to tell
|
|
|
|
# Nim to not consider them GcSafe violations:
|
|
|
|
template allProtocols*: auto = {.gcsafe.}: gProtocols
|
|
|
|
|
2019-05-19 18:05:02 +00:00
|
|
|
proc getState*(peer: Peer, proto: ProtocolInfo): RootRef =
|
2019-03-11 09:22:06 +00:00
|
|
|
peer.protocolStates[proto.index]
|
|
|
|
|
|
|
|
template state*(peer: Peer, Protocol: type): untyped =
|
|
|
|
## Returns the state object of a particular protocol for a
|
|
|
|
## particular connection.
|
|
|
|
mixin State
|
|
|
|
bind getState
|
|
|
|
cast[Protocol.State](getState(peer, Protocol.protocolInfo))
|
|
|
|
|
2019-05-19 18:05:02 +00:00
|
|
|
proc getNetworkState*(node: EthereumNode, proto: ProtocolInfo): RootRef =
|
2019-03-11 09:22:06 +00:00
|
|
|
node.protocolStates[proto.index]
|
|
|
|
|
|
|
|
template protocolState*(node: EthereumNode, Protocol: type): untyped =
|
|
|
|
mixin NetworkState
|
|
|
|
bind getNetworkState
|
|
|
|
cast[Protocol.NetworkState](getNetworkState(node, Protocol.protocolInfo))
|
|
|
|
|
|
|
|
template networkState*(connection: Peer, Protocol: type): untyped =
|
|
|
|
## Returns the network state object of a particular protocol for a
|
|
|
|
## particular connection.
|
|
|
|
protocolState(connection.network, Protocol)
|
|
|
|
|
|
|
|
proc initProtocolState*[T](state: T, x: Peer|EthereumNode) {.gcsafe.} = discard
|
|
|
|
|
2019-06-17 11:19:13 +00:00
|
|
|
proc initProtocolStates(peer: Peer, protocols: openarray[ProtocolInfo]) =
|
|
|
|
# Initialize all the active protocol states
|
|
|
|
newSeq(peer.protocolStates, allProtocols.len)
|
|
|
|
for protocol in protocols:
|
|
|
|
let peerStateInit = protocol.peerStateInitializer
|
|
|
|
if peerStateInit != nil:
|
|
|
|
peer.protocolStates[protocol.index] = peerStateInit(peer)
|
|
|
|
|
2019-06-03 17:05:45 +00:00
|
|
|
proc resolveFuture[MsgType](msg: pointer, future: FutureBase) {.gcsafe.} =
|
|
|
|
var f = Future[MsgType](future)
|
|
|
|
doAssert(not f.finished())
|
|
|
|
f.complete(cast[ptr MsgType](msg)[])
|
|
|
|
|
2019-05-29 08:16:59 +00:00
|
|
|
proc requestResolver[MsgType](msg: pointer, future: FutureBase) {.gcsafe.} =
|
|
|
|
var f = Future[Option[MsgType]](future)
|
|
|
|
if not f.finished:
|
|
|
|
if msg != nil:
|
|
|
|
f.complete some(cast[ptr MsgType](msg)[])
|
|
|
|
else:
|
|
|
|
f.complete none(MsgType)
|
|
|
|
else:
|
|
|
|
# This future was already resolved, but let's do some sanity checks
|
|
|
|
# here. The only reasonable explanation is that the request should
|
|
|
|
# have timed out.
|
|
|
|
if msg != nil:
|
|
|
|
if f.read.isSome:
|
|
|
|
doAssert false, "trying to resolve a request twice"
|
|
|
|
else:
|
|
|
|
doAssert false, "trying to resolve a timed out request with a value"
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
if not f.read.isSome:
|
|
|
|
doAssert false, "a request timed out twice"
|
|
|
|
# This can except when the future still completes with an error.
|
|
|
|
# E.g. the `sendMsg` fails because of an already closed transport or a
|
|
|
|
# broken pipe
|
2019-12-02 15:31:10 +00:00
|
|
|
except TransportOsError as e:
|
2019-05-29 08:16:59 +00:00
|
|
|
# E.g. broken pipe
|
2019-12-02 15:31:10 +00:00
|
|
|
trace "TransportOsError during request", err = e.msg
|
2019-05-29 08:16:59 +00:00
|
|
|
except TransportError:
|
|
|
|
trace "Transport got closed during request"
|
2019-12-02 15:31:10 +00:00
|
|
|
except Exception as e:
|
|
|
|
debug "Exception in requestResolver()", exc = e.name, err = e.msg
|
2019-12-04 11:34:37 +00:00
|
|
|
raise e
|
2019-05-29 08:16:59 +00:00
|
|
|
|
|
|
|
proc linkSendFailureToReqFuture[S, R](sendFut: Future[S], resFut: Future[R]) =
|
|
|
|
sendFut.addCallback() do (arg: pointer):
|
|
|
|
if not sendFut.error.isNil:
|
|
|
|
resFut.fail(sendFut.error)
|
|
|
|
|
|
|
|
proc messagePrinter[MsgType](msg: pointer): string {.gcsafe.} =
|
|
|
|
result = ""
|
|
|
|
# TODO: uncommenting the line below increases the compile-time
|
|
|
|
# tremendously (for reasons not yet known)
|
|
|
|
# result = $(cast[ptr MsgType](msg)[])
|
2019-03-11 09:22:06 +00:00
|
|
|
|
2019-06-24 02:09:00 +00:00
|
|
|
proc disconnectAndRaise(peer: Peer,
|
|
|
|
reason: DisconnectionReason,
|
|
|
|
msg: string) {.async.}
|
|
|
|
|
2019-05-29 15:52:28 +00:00
|
|
|
proc handshakeImpl[T](peer: Peer,
|
|
|
|
sendFut: Future[void],
|
|
|
|
responseFut: Future[T],
|
|
|
|
timeout: Duration): Future[T] {.async.} =
|
|
|
|
sendFut.addCallback do (arg: pointer) {.gcsafe.}:
|
|
|
|
if sendFut.failed:
|
|
|
|
debug "Handshake message not delivered", peer
|
|
|
|
|
|
|
|
doAssert timeout.milliseconds > 0
|
|
|
|
yield responseFut or sleepAsync(timeout)
|
|
|
|
if not responseFut.finished:
|
2019-06-17 11:19:13 +00:00
|
|
|
await disconnectAndRaise(peer, HandshakeTimeout,
|
|
|
|
"Protocol handshake was not received in time.")
|
2019-05-29 15:52:28 +00:00
|
|
|
elif responseFut.failed:
|
|
|
|
raise responseFut.error
|
|
|
|
else:
|
|
|
|
return responseFut.read
|
|
|
|
|