allow to omit peerId and seqno (#372)

* allow to omit peerId and seqno

* small refactor

* wip

* fix message encoding

* improve rpc signature logic

* remove peerid from verify

* trace fixes

* fix message test

* fix gossip 1.0 tests
This commit is contained in:
Giovanni Petrantoni 2020-09-24 00:56:33 +09:00 committed by GitHub
parent abd234601b
commit ec322124ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 82 additions and 29 deletions

View File

@ -72,10 +72,19 @@ method rpcHandler*(f: FloodSub,
trace "Dropping already-seen message", msgId, peer trace "Dropping already-seen message", msgId, peer
continue continue
if f.verifySignature and not msg.verify(peer.peerId): if (msg.signature.len > 0 or f.verifySignature) and not msg.verify():
# always validate if signature is present or required
debug "Dropping message due to failed signature verification", msgId, peer debug "Dropping message due to failed signature verification", msgId, peer
continue continue
if msg.seqno.len > 0 and msg.seqno.len != 8:
# if we have seqno should be 8 bytes long
debug "Dropping message due to invalid seqno length", msgId, peer
continue
# g.anonymize needs no evaluation when receiving messages
# as we have a "lax" policy and allow signed messages
if not (await f.validate(msg)): if not (await f.validate(msg)):
trace "Dropping message due to failed validation", msgId, peer trace "Dropping message due to failed validation", msgId, peer
continue continue
@ -129,7 +138,11 @@ method publish*(f: FloodSub,
inc f.msgSeqno inc f.msgSeqno
let let
msg = Message.init(f.peerInfo, data, topic, f.msgSeqno, f.sign) msg =
if f.anonymize:
Message.init(none(PeerInfo), data, topic, none(uint64), false)
else:
Message.init(some(f.peerInfo), data, topic, some(f.msgSeqno), f.sign)
msgId = f.msgIdProvider(msg) msgId = f.msgIdProvider(msg)
trace "Created new message", trace "Created new message",

View File

@ -1046,11 +1046,21 @@ method rpcHandler*(g: GossipSub,
g.mcache.put(msgId, msg) g.mcache.put(msgId, msg)
if g.verifySignature and not msg.verify(peer.peerId): if (msg.signature.len > 0 or g.verifySignature) and not msg.verify():
# always validate if signature is present or required
debug "Dropping message due to failed signature verification", msgId, peer debug "Dropping message due to failed signature verification", msgId, peer
g.punishPeer(peer, msg) g.punishPeer(peer, msg)
continue continue
if msg.seqno.len > 0 and msg.seqno.len != 8:
# if we have seqno should be 8 bytes long
debug "Dropping message due to invalid seqno length", msgId, peer
g.punishPeer(peer, msg)
continue
# g.anonymize needs no evaluation when receiving messages
# as we have a "lax" policy and allow signed messages
if not (await g.validate(msg)): if not (await g.validate(msg)):
debug "Dropping message due to failed validation", msgId, peer debug "Dropping message due to failed validation", msgId, peer
g.punishPeer(peer, msg) g.punishPeer(peer, msg)
@ -1197,7 +1207,11 @@ method publish*(g: GossipSub,
inc g.msgSeqno inc g.msgSeqno
let let
msg = Message.init(g.peerInfo, data, topic, g.msgSeqno, g.sign) msg =
if g.anonymize:
Message.init(none(PeerInfo), data, topic, none(uint64), false)
else:
Message.init(some(g.peerInfo), data, topic, some(g.msgSeqno), g.sign)
msgId = g.msgIdProvider(msg) msgId = g.msgIdProvider(msg)
logScope: msgId logScope: msgId

View File

@ -451,10 +451,19 @@ method rpcHandler*(g: GossipSub,
g.mcache.put(msgId, msg) g.mcache.put(msgId, msg)
if g.verifySignature and not msg.verify(peer.peerId): if (msg.signature.len > 0 or g.verifySignature) and not msg.verify():
# always validate if signature is present or required
debug "Dropping message due to failed signature verification", msgId, peer debug "Dropping message due to failed signature verification", msgId, peer
continue continue
if msg.seqno.len > 0 and msg.seqno.len != 8:
# if we have seqno should be 8 bytes long
debug "Dropping message due to invalid seqno length", msgId, peer
continue
# g.anonymize needs no evaluation when receiving messages
# as we have a "lax" policy and allow signed messages
if not (await g.validate(msg)): if not (await g.validate(msg)):
trace "Dropping message due to failed validation", msgId, peer trace "Dropping message due to failed validation", msgId, peer
continue continue
@ -556,7 +565,11 @@ method publish*(g: GossipSub,
inc g.msgSeqno inc g.msgSeqno
let let
msg = Message.init(g.peerInfo, data, topic, g.msgSeqno, g.sign) msg =
if g.anonymize:
Message.init(none(PeerInfo), data, topic, none(uint64), false)
else:
Message.init(some(g.peerInfo), data, topic, some(g.msgSeqno), g.sign)
msgId = g.msgIdProvider(msg) msgId = g.msgIdProvider(msg)
logScope: msgId logScope: msgId

View File

@ -67,6 +67,7 @@ type
observers: ref seq[PubSubObserver] # ref as in smart_ptr observers: ref seq[PubSubObserver] # ref as in smart_ptr
msgIdProvider*: MsgIdProvider # Turn message into message id (not nil) msgIdProvider*: MsgIdProvider # Turn message into message id (not nil)
msgSeqno*: uint64 msgSeqno*: uint64
anonymize*: bool # if we omit fromPeer and seqno from RPC messages we send
method unsubscribePeer*(p: PubSub, peerId: PeerID) {.base.} = method unsubscribePeer*(p: PubSub, peerId: PeerID) {.base.} =
## handle peer disconnects ## handle peer disconnects
@ -329,6 +330,7 @@ proc init*[PubParams: object | bool](
P: typedesc[PubSub], P: typedesc[PubSub],
switch: Switch, switch: Switch,
triggerSelf: bool = false, triggerSelf: bool = false,
anonymize: bool = false,
verifySignature: bool = true, verifySignature: bool = true,
sign: bool = true, sign: bool = true,
msgIdProvider: MsgIdProvider = defaultMsgIdProvider, msgIdProvider: MsgIdProvider = defaultMsgIdProvider,
@ -338,6 +340,7 @@ proc init*[PubParams: object | bool](
P(switch: switch, P(switch: switch,
peerInfo: switch.peerInfo, peerInfo: switch.peerInfo,
triggerSelf: triggerSelf, triggerSelf: triggerSelf,
anonymize: anonymize,
verifySignature: verifySignature, verifySignature: verifySignature,
sign: sign, sign: sign,
peers: initTable[PeerID, PubSubPeer](), peers: initTable[PeerID, PubSubPeer](),
@ -347,6 +350,7 @@ proc init*[PubParams: object | bool](
P(switch: switch, P(switch: switch,
peerInfo: switch.peerInfo, peerInfo: switch.peerInfo,
triggerSelf: triggerSelf, triggerSelf: triggerSelf,
anonymize: anonymize,
verifySignature: verifySignature, verifySignature: verifySignature,
sign: sign, sign: sign,
peers: initTable[PeerID, PubSubPeer](), peers: initTable[PeerID, PubSubPeer](),

View File

@ -33,7 +33,7 @@ func defaultMsgIdProvider*(m: Message): string =
proc sign*(msg: Message, privateKey: PrivateKey): CryptoResult[seq[byte]] = proc sign*(msg: Message, privateKey: PrivateKey): CryptoResult[seq[byte]] =
ok((? privateKey.sign(PubSubPrefix & encodeMessage(msg))).getBytes()) ok((? privateKey.sign(PubSubPrefix & encodeMessage(msg))).getBytes())
proc verify*(m: Message, p: PeerID): bool = proc verify*(m: Message): bool =
if m.signature.len > 0 and m.key.len > 0: if m.signature.len > 0 and m.key.len > 0:
var msg = m var msg = m
msg.signature = @[] msg.signature = @[]
@ -52,20 +52,26 @@ proc verify*(m: Message, p: PeerID): bool =
proc init*( proc init*(
T: type Message, T: type Message,
peer: PeerInfo, peer: Option[PeerInfo],
data: seq[byte], data: seq[byte],
topic: string, topic: string,
seqno: uint64, seqno: Option[uint64],
sign: bool = true): Message {.gcsafe, raises: [CatchableError, Defect].} = sign: bool = true): Message {.gcsafe, raises: [CatchableError, Defect].} =
result = Message( var msg = Message(data: data, topicIDs: @[topic])
fromPeer: peer.peerId,
data: data,
seqno: @(seqno.toBytesBE), # unefficient, fine for now
topicIDs: @[topic])
if sign: # order matters, we want to include seqno in the signature
if peer.keyType != KeyType.HasPrivate: if seqno.isSome:
raise (ref CatchableError)(msg: "Cannot sign message without private key") msg.seqno = @(seqno.get().toBytesBE())
result.signature = sign(result, peer.privateKey).tryGet() if peer.isSome:
result.key = peer.privateKey.getKey().tryGet().getBytes().tryGet() let peer = peer.get()
msg.fromPeer = peer.peerId
if sign:
if peer.keyType != KeyType.HasPrivate:
raise (ref CatchableError)(msg: "Cannot sign message without private key")
msg.signature = sign(msg, peer.privateKey).tryGet()
msg.key = peer.privateKey.getKey().tryGet().getBytes().tryGet()
elif sign:
raise (ref CatchableError)(msg: "Cannot sign message without peer info")
msg

View File

@ -2,6 +2,7 @@ include ../../libp2p/protocols/pubsub/gossipsub
{.used.} {.used.}
import options
import unittest, bearssl import unittest, bearssl
import stew/byteutils import stew/byteutils
import ../../libp2p/standard_setup import ../../libp2p/standard_setup
@ -267,7 +268,7 @@ suite "GossipSub internal":
let peerInfo = randomPeerInfo() let peerInfo = randomPeerInfo()
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
inc seqno inc seqno
let msg = Message.init(peerInfo, ("HELLO" & $i).toBytes(), topic, seqno, false) let msg = Message.init(some(peerInfo), ("HELLO" & $i).toBytes(), topic, some(seqno), false)
gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg) gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg)
check gossipSub.fanout[topic].len == 15 check gossipSub.fanout[topic].len == 15
@ -321,7 +322,7 @@ suite "GossipSub internal":
let peerInfo = randomPeerInfo() let peerInfo = randomPeerInfo()
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
inc seqno inc seqno
let msg = Message.init(peerInfo, ("HELLO" & $i).toBytes(), topic, seqno, false) let msg = Message.init(some(peerInfo), ("HELLO" & $i).toBytes(), topic, some(seqno), false)
gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg) gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg)
let peers = gossipSub.getGossipPeers() let peers = gossipSub.getGossipPeers()
@ -369,7 +370,7 @@ suite "GossipSub internal":
let peerInfo = randomPeerInfo() let peerInfo = randomPeerInfo()
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
inc seqno inc seqno
let msg = Message.init(peerInfo, ("HELLO" & $i).toBytes(), topic, seqno, false) let msg = Message.init(some(peerInfo), ("HELLO" & $i).toBytes(), topic, some(seqno), false)
gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg) gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg)
let peers = gossipSub.getGossipPeers() let peers = gossipSub.getGossipPeers()
@ -417,7 +418,7 @@ suite "GossipSub internal":
let peerInfo = randomPeerInfo() let peerInfo = randomPeerInfo()
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
inc seqno inc seqno
let msg = Message.init(peerInfo, ("bar" & $i).toBytes(), topic, seqno, false) let msg = Message.init(some(peerInfo), ("bar" & $i).toBytes(), topic, some(seqno), false)
gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg) gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg)
let peers = gossipSub.getGossipPeers() let peers = gossipSub.getGossipPeers()

View File

@ -2,6 +2,7 @@ include ../../libp2p/protocols/pubsub/gossipsub10
{.used.} {.used.}
import options
import unittest, bearssl import unittest, bearssl
import stew/byteutils import stew/byteutils
import ../../libp2p/standard_setup import ../../libp2p/standard_setup
@ -244,7 +245,7 @@ suite "GossipSub internal":
let peerInfo = randomPeerInfo() let peerInfo = randomPeerInfo()
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
inc seqno inc seqno
let msg = Message.init(peerInfo, ("HELLO" & $i).toBytes(), topic, seqno, false) let msg = Message.init(some(peerInfo), ("HELLO" & $i).toBytes(), topic, some(seqno), false)
gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg) gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg)
check gossipSub.fanout[topic].len == 15 check gossipSub.fanout[topic].len == 15
@ -296,7 +297,7 @@ suite "GossipSub internal":
let peerInfo = randomPeerInfo() let peerInfo = randomPeerInfo()
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
inc seqno inc seqno
let msg = Message.init(peerInfo, ("HELLO" & $i).toBytes(), topic, seqno, false) let msg = Message.init(some(peerInfo), ("HELLO" & $i).toBytes(), topic, some(seqno), false)
gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg) gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg)
let peers = gossipSub.getGossipPeers() let peers = gossipSub.getGossipPeers()
@ -341,7 +342,7 @@ suite "GossipSub internal":
let peerInfo = randomPeerInfo() let peerInfo = randomPeerInfo()
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
inc seqno inc seqno
let msg = Message.init(peerInfo, ("HELLO" & $i).toBytes(), topic, seqno, false) let msg = Message.init(some(peerInfo), ("HELLO" & $i).toBytes(), topic, some(seqno), false)
gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg) gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg)
let peers = gossipSub.getGossipPeers() let peers = gossipSub.getGossipPeers()
@ -386,7 +387,7 @@ suite "GossipSub internal":
let peerInfo = randomPeerInfo() let peerInfo = randomPeerInfo()
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
inc seqno inc seqno
let msg = Message.init(peerInfo, ("bar" & $i).toBytes(), topic, seqno, false) let msg = Message.init(some(peerInfo), ("bar" & $i).toBytes(), topic, some(seqno), false)
gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg) gossipSub.mcache.put(gossipSub.msgIdProvider(msg), msg)
let peers = gossipSub.getGossipPeers() let peers = gossipSub.getGossipPeers()

View File

@ -2,6 +2,7 @@ import unittest
{.used.} {.used.}
import options
import ../../libp2p/[peerid, peerinfo, import ../../libp2p/[peerid, peerinfo,
crypto/crypto, crypto/crypto,
protocols/pubsub/rpc/message, protocols/pubsub/rpc/message,
@ -14,6 +15,6 @@ suite "Message":
var seqno = 11'u64 var seqno = 11'u64
let let
peer = PeerInfo.init(PrivateKey.random(ECDSA, rng[]).get()) peer = PeerInfo.init(PrivateKey.random(ECDSA, rng[]).get())
msg = Message.init(peer, @[], "topic", seqno, sign = true) msg = Message.init(some(peer), @[], "topic", some(seqno), sign = true)
check verify(msg, peer.peerId) check verify(msg)