Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
go-libp2p
Commits
bf7f80c3
Commit
bf7f80c3
authored
7 years ago
by
Aviv Eyal
Committed by
Steven Allen
7 years ago
Browse files
Options
Download
Email Patches
Plain Diff
Refactoring
parent
45c3b5d4
master
2018-Q4-OKR
feat/protobuf
fix/473
fix/no-custom-field
fix/reset-ping-stream
fix/revert-correct-external-addr
gx/update-nza0mn
jenkinsfile
multistream-ping
punching
v6.0.23
v6.0.22
v6.0.21
v6.0.20
v6.0.19
v6.0.18
v6.0.17
v6.0.16
v6.0.15
v6.0.14
v6.0.13
v6.0.12
v6.0.11
v6.0.10
v6.0.9
v6.0.8
v6.0.7
v6.0.6
v6.0.5
v6.0.4
v6.0.3
v6.0.2
v6.0.1
v6.0.0
v5.0.21
v5.0.20
v5.0.19
v5.0.18
v5.0.17
v5.0.16
No related merge requests found
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
examples/multipro/echo.go
+5
-6
examples/multipro/echo.go
examples/multipro/main.go
+2
-0
examples/multipro/main.go
examples/multipro/node.go
+38
-27
examples/multipro/node.go
examples/multipro/pb/p2p.pb.go
+27
-18
examples/multipro/pb/p2p.pb.go
examples/multipro/pb/p2p.proto
+6
-5
examples/multipro/pb/p2p.proto
examples/multipro/ping.go
+3
-4
examples/multipro/ping.go
examples/multipro/protocol.go
+13
-6
examples/multipro/protocol.go
with
94 additions
and
66 deletions
+94
-66
examples/multipro/echo.go
View file @
bf7f80c3
...
...
@@ -10,10 +10,9 @@ import (
uuid
"github.com/google/uuid"
"github.com/ipfs/go-ipfs/thirdparty/assert"
p2p
"github.com/
libp2p
/go-libp2p/examples/multipro/pb"
p2p
"github.com/
avive
/go-libp2p/examples/multipro/pb"
protobufCodec
"github.com/multiformats/go-multicodec/protobuf"
"gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host"
"gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
)
// pattern: /protocol-name/request-or-response-message/version
...
...
@@ -52,15 +51,15 @@ func (e EchoProtocol) onEchoRequest(s inet.Stream) {
log
.
Fatal
(
"Failed to authenticate message"
)
return
}
else
{
log
.
Print
(
"Authenticated request content was generated by
claimed
node :-)"
)
log
.
Print
(
"Authenticated request content was generated by
author
node :-)"
)
}
log
.
Printf
(
"%s: Sending echo response to %s. Message id: %s..."
,
s
.
Conn
()
.
LocalPeer
(),
s
.
Conn
()
.
RemotePeer
(),
data
.
MessageData
.
Id
)
// send response to the request using the message string he provided
resp
:=
&
p2p
.
EchoResponse
{
MessageData
:
NewMessageData
(
peer
.
IDB58Encode
(
e
.
node
.
ID
())
,
data
.
MessageData
.
Id
,
false
),
MessageData
:
NewMessageData
(
e
.
node
,
data
.
MessageData
.
Id
,
false
),
Message
:
data
.
Message
}
// sign the data
...
...
@@ -126,7 +125,7 @@ func (e EchoProtocol) Echo(host host.Host) bool {
// create message data
req
:=
&
p2p
.
EchoRequest
{
MessageData
:
NewMessageData
(
peer
.
IDB58Encode
(
e
.
node
.
ID
())
,
uuid
.
New
()
.
String
(),
false
),
MessageData
:
NewMessageData
(
e
.
node
,
uuid
.
New
()
.
String
(),
false
),
Message
:
fmt
.
Sprintf
(
"Echo from %s"
,
e
.
node
.
ID
())}
signature
,
err
:=
e
.
node
.
signProtoMessage
(
req
)
...
...
This diff is collapsed.
Click to expand it.
examples/multipro/main.go
View file @
bf7f80c3
...
...
@@ -27,6 +27,8 @@ func makeRandomNode(port int, done chan bool) *Node {
n
,
_
:=
swarm
.
NewNetwork
(
context
.
Background
(),
[]
ma
.
Multiaddr
{
listen
},
pid
,
peerStore
,
nil
)
host
:=
bhost
.
New
(
n
)
log
.
Printf
(
"New host id (base58 multihash):%s, length:%d"
,
host
.
ID
()
.
Pretty
(),
len
(
host
.
ID
()
.
Pretty
()))
return
NewNode
(
host
,
done
)
}
...
...
This diff is collapsed.
Click to expand it.
examples/multipro/node.go
View file @
bf7f80c3
package
main
import
(
"github.com/gogo/protobuf/proto"
host
"gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host"
peer
"gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
"github.com/gogo/protobuf/proto"
p2p
"github.com/libp2p/go-libp2p/examples/multipro/pb"
p2p
"github.com/avive/go-libp2p/examples/multipro/pb"
"log"
)
// Node type - a p2p host implementing one or more p2p protocols
type
Node
struct
{
host
.
Host
// lib-p2p host
*
PingProtocol
// ping protocol impl
*
EchoProtocol
// echo protocol impl
// add other protocols here...
}
// create a new node with its implemented protocols
func
NewNode
(
host
host
.
Host
,
done
chan
bool
)
*
Node
{
node
:=
&
Node
{
Host
:
host
}
node
.
PingProtocol
=
NewPingProtocol
(
node
,
done
)
node
.
EchoProtocol
=
NewEchoProtocol
(
node
,
done
)
return
node
}
func
(
n
Node
)
authenticateMessage
(
message
proto
.
Message
,
data
*
p2p
.
MessageData
)
bool
{
// store a temp ref to sig and remove it from data
sign
:=
data
.
Sign
data
.
Sign
=
""
//log.Print("Signature: %s", []byte(sign))
// marshall data without the sig to binary format
bin
,
err
:=
proto
.
Marshal
(
message
)
if
err
!=
nil
{
// todo: log
log
.
Fatal
(
err
,
"failed to marshal pb message"
)
return
false
}
...
...
@@ -28,13 +41,12 @@ func (n Node) authenticateMessage(message proto.Message, data *p2p.MessageData)
data
.
Sign
=
sign
peerId
,
err
:=
peer
.
IDB58Decode
(
data
.
NodeId
)
if
err
!=
nil
{
log
.
Fatal
(
err
,
"Failed to decode node id"
)
log
.
Fatal
(
err
,
"Failed to decode node id
from base58
"
)
return
false
}
return
n
.
verifyData
(
bin
,
[]
byte
(
sign
),
peerId
)
return
n
.
verifyData
(
bin
,
[]
byte
(
sign
),
peerId
,
[]
byte
(
data
.
NodePubKey
)
)
}
func
(
n
Node
)
signProtoMessage
(
message
proto
.
Message
)
([]
byte
,
error
)
{
...
...
@@ -52,25 +64,24 @@ func (n Node) signData(data []byte) ([]byte, error) {
}
// precondition: we have info about the signer peer in the local peer store
func
(
n
Node
)
verifyData
(
data
[]
byte
,
signature
[]
byte
,
peerId
peer
.
ID
)
bool
{
func
(
n
Node
)
verifyData
(
data
[]
byte
,
signature
[]
byte
,
peerId
peer
.
ID
,
pubKeyData
[]
byte
)
bool
{
// todo: restore pub key from message and use it
key
:=
n
.
Peerstore
()
.
PubKey
(
peerId
)
//log.Print ("%s %s %s", peerId, key, peerId.String())
res
,
err
:=
key
.
Verify
(
data
,
signature
)
return
res
==
true
&&
err
==
nil
}
// Node type - a host with one or more implemented p2p protocols
type
Node
struct
{
host
.
Host
// lib-p2p host
*
PingProtocol
// ping protocol impl
*
EchoProtocol
// echo protocol impl
// add other protocols here...
}
//todo: fix this
//key, err := key.UnmarshalPublicKey(pubKeyData)
// create a new node with its implemented protocols
func
NewNode
(
host
host
.
Host
,
done
chan
bool
)
*
Node
{
node
:=
&
Node
{
Host
:
host
}
node
.
PingProtocol
=
NewPingProtocol
(
node
,
done
)
node
.
EchoProtocol
=
NewEchoProtocol
(
node
,
done
)
return
node
if
key
==
nil
{
log
.
Fatal
(
"Failed to find public key for %s in local peer store."
,
peerId
.
String
())
return
false
}
res
,
err
:=
key
.
Verify
(
data
,
signature
)
if
err
!=
nil
{
log
.
Fatal
(
"Error authenticating data"
)
return
false
}
return
res
}
This diff is collapsed.
Click to expand it.
examples/multipro/pb/p2p.pb.go
View file @
bf7f80c3
...
...
@@ -39,7 +39,8 @@ type MessageData struct {
Id
string
`protobuf:"bytes,3,opt,name=id" json:"id,omitempty"`
Gossip
bool
`protobuf:"varint,4,opt,name=gossip" json:"gossip,omitempty"`
NodeId
string
`protobuf:"bytes,5,opt,name=nodeId" json:"nodeId,omitempty"`
Sign
string
`protobuf:"bytes,6,opt,name=sign" json:"sign,omitempty"`
NodePubKey
string
`protobuf:"bytes,6,opt,name=nodePubKey" json:"nodePubKey,omitempty"`
Sign
string
`protobuf:"bytes,7,opt,name=sign" json:"sign,omitempty"`
}
func
(
m
*
MessageData
)
Reset
()
{
*
m
=
MessageData
{}
}
...
...
@@ -82,6 +83,13 @@ func (m *MessageData) GetNodeId() string {
return
""
}
func
(
m
*
MessageData
)
GetNodePubKey
()
string
{
if
m
!=
nil
{
return
m
.
NodePubKey
}
return
""
}
func
(
m
*
MessageData
)
GetSign
()
string
{
if
m
!=
nil
{
return
m
.
Sign
...
...
@@ -202,21 +210,22 @@ func init() {
func
init
()
{
proto
.
RegisterFile
(
"p2p.proto"
,
fileDescriptor0
)
}
var
fileDescriptor0
=
[]
byte
{
// 244 bytes of a gzipped FileDescriptorProto
0x1f
,
0x8b
,
0x08
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x02
,
0xff
,
0xbc
,
0x8f
,
0x31
,
0x4f
,
0xc3
,
0x30
,
0x10
,
0x85
,
0xe5
,
0xb4
,
0x04
,
0x72
,
0xa6
,
0x0c
,
0x37
,
0x20
,
0x0b
,
0x31
,
0x44
,
0x11
,
0x43
,
0xa6
,
0x0c
,
0x61
,
0x65
,
0x84
,
0x81
,
0x01
,
0x09
,
0x79
,
0x60
,
0x0f
,
0xc9
,
0x11
,
0x2c
,
0x35
,
0xb6
,
0xe9
,
0x99
,
0x9f
,
0xc4
,
0xff
,
0x44
,
0x5c
,
0x83
,
0xda
,
0xfe
,
0x80
,
0x76
,
0xb2
,
0xdf
,
0xd3
,
0xb3
,
0xdf
,
0xfb
,
0xa0
,
0x88
,
0x6d
,
0x6c
,
0xe2
,
0x26
,
0xa4
,
0x80
,
0x2b
,
0x39
,
0xfa
,
0xb0
,
0xe6
,
0x26
,
0xb6
,
0xb1
,
0xfa
,
0x51
,
0xa0
,
0x5f
,
0x88
,
0xb9
,
0x1b
,
0xe9
,
0xb1
,
0x4b
,
0x1d
,
0xde
,
0xc1
,
0xaa
,
0x5f
,
0x3b
,
0xf2
,
0xe9
,
0x8d
,
0x36
,
0xec
,
0x82
,
0x37
,
0xaa
,
0x54
,
0x75
,
0x61
,
0x0f
,
0x4d
,
0xbc
,
0x85
,
0x22
,
0xb9
,
0x89
,
0x38
,
0x75
,
0x53
,
0x34
,
0x59
,
0xa9
,
0xea
,
0x85
,
0xdd
,
0x19
,
0x78
,
0x05
,
0x99
,
0x1b
,
0xcc
,
0x42
,
0x1e
,
0x66
,
0x6e
,
0xc0
,
0x6b
,
0xc8
,
0xc7
,
0xc0
,
0xec
,
0xa2
,
0x59
,
0x96
,
0xaa
,
0xbe
,
0xb0
,
0xb3
,
0xfa
,
0xf3
,
0x7d
,
0x18
,
0xe8
,
0x79
,
0x30
,
0x67
,
0x92
,
0x9d
,
0x15
,
0x22
,
0x2c
,
0xd9
,
0x8d
,
0xde
,
0xe4
,
0xe2
,
0xca
,
0xbd
,
0x22
,
0xd0
,
0xaf
,
0xce
,
0x8f
,
0x96
,
0xbe
,
0xbe
,
0x89
,
0x13
,
0x3e
,
0x80
,
0x9e
,
0x76
,
0xab
,
0x65
,
0xa4
,
0x6e
,
0x6f
,
0x9a
,
0x03
,
0xb6
,
0x66
,
0x8f
,
0xcb
,
0xee
,
0xc7
,
0xd1
,
0xc0
,
0xf9
,
0x2c
,
0x65
,
0x7c
,
0x61
,
0xff
,
0x65
,
0xf5
,
0x01
,
0x97
,
0xdb
,
0x1a
,
0x8e
,
0xc1
,
0x33
,
0x1d
,
0xad
,
0x87
,
0x40
,
0x3f
,
0xf5
,
0x9f
,
0xe1
,
0x04
,
0x38
,
0xdb
,
0x9a
,
0xe3
,
0xe2
,
0xbc
,
0xe7
,
0xf2
,
0xc3
,
0xfd
,
0x6f
,
0x00
,
0x00
,
0x00
,
0xff
,
0xff
,
0xb9
,
0xf8
,
0x88
,
0xca
,
0x68
,
0x02
,
0x00
,
0x00
,
// 261 bytes of a gzipped FileDescriptorProto
0x1f
,
0x8b
,
0x08
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x02
,
0xff
,
0xbc
,
0x8f
,
0x31
,
0x4f
,
0xfb
,
0x30
,
0x10
,
0xc5
,
0xe5
,
0xb4
,
0xff
,
0xf4
,
0x9f
,
0x33
,
0x65
,
0xb8
,
0x01
,
0x59
,
0x08
,
0xa1
,
0x28
,
0x62
,
0xc8
,
0x94
,
0x21
,
0xac
,
0x8c
,
0x30
,
0x20
,
0x84
,
0x54
,
0x79
,
0x60
,
0x4f
,
0x93
,
0x23
,
0x58
,
0x6a
,
0x6c
,
0xd3
,
0x73
,
0x07
,
0x3e
,
0x20
,
0xdf
,
0x0b
,
0xd5
,
0x0d
,
0x6a
,
0xfa
,
0x01
,
0xca
,
0xe4
,
0x7b
,
0xef
,
0x9e
,
0xef
,
0xe9
,
0x07
,
0x99
,
0xaf
,
0x7d
,
0xe5
,
0xb7
,
0x2e
,
0x38
,
0x5c
,
0xc6
,
0xa7
,
0x75
,
0x1b
,
0xae
,
0x7c
,
0xed
,
0x8b
,
0x6f
,
0x01
,
0xf2
,
0x95
,
0x98
,
0x9b
,
0x9e
,
0x1e
,
0x9b
,
0xd0
,
0xe0
,
0x1d
,
0x2c
,
0xdb
,
0x8d
,
0x21
,
0x1b
,
0xde
,
0x68
,
0xcb
,
0xc6
,
0x59
,
0x25
,
0x72
,
0x51
,
0x66
,
0xfa
,
0xd4
,
0xc4
,
0x1b
,
0xc8
,
0x82
,
0x19
,
0x88
,
0x43
,
0x33
,
0x78
,
0x95
,
0xe4
,
0xa2
,
0x9c
,
0xe9
,
0xa3
,
0x81
,
0x97
,
0x90
,
0x98
,
0x4e
,
0xcd
,
0xe2
,
0xc7
,
0xc4
,
0x74
,
0x78
,
0x05
,
0x69
,
0xef
,
0x98
,
0x8d
,
0x57
,
0xf3
,
0x5c
,
0x94
,
0xff
,
0xf5
,
0xa8
,
0xf6
,
0xbe
,
0x75
,
0x1d
,
0x3d
,
0x77
,
0xea
,
0x5f
,
0xcc
,
0x8e
,
0x0a
,
0x6f
,
0x01
,
0xf6
,
0xd3
,
0x6a
,
0xb7
,
0x7e
,
0xa1
,
0x2f
,
0x95
,
0xc6
,
0xdd
,
0xc4
,
0x41
,
0x84
,
0x39
,
0x9b
,
0xde
,
0xaa
,
0x45
,
0xdc
,
0xc4
,
0xb9
,
0x20
,
0x90
,
0x2b
,
0x63
,
0x7b
,
0x4d
,
0x9f
,
0x3b
,
0xe2
,
0x80
,
0x0f
,
0x20
,
0x87
,
0x23
,
0x55
,
0x84
,
0x90
,
0xf5
,
0x75
,
0x75
,
0xc2
,
0x5e
,
0x4d
,
0xb8
,
0xf5
,
0x34
,
0x8e
,
0x0a
,
0x16
,
0xa3
,
0x8c
,
0x70
,
0x99
,
0xfe
,
0x95
,
0xc5
,
0x3b
,
0x5c
,
0x1c
,
0x6a
,
0xd8
,
0x3b
,
0xcb
,
0x74
,
0xb6
,
0x1e
,
0x02
,
0xf9
,
0xd4
,
0x7e
,
0xb8
,
0x3f
,
0xc0
,
0x39
,
0xd4
,
0x9c
,
0x17
,
0x67
,
0x9d
,
0xc6
,
0x0b
,
0xf7
,
0x3f
,
0x01
,
0x00
,
0x00
,
0xff
,
0xff
,
0x14
,
0x4d
,
0xf8
,
0x0c
,
0x88
,
0x02
,
0x00
,
0x00
,
}
This diff is collapsed.
Click to expand it.
examples/multipro/pb/p2p.proto
View file @
bf7f80c3
...
...
@@ -6,11 +6,12 @@ package protocols.p2p;
message
MessageData
{
// shared between all requests
string
clientVersion
=
1
;
// client version
int64
timestamp
=
2
;
// unix time
string
id
=
3
;
// allows requesters to use request data when processing a response
bool
gossip
=
4
;
// true to have receiver peer gossip the message to neighbors
string
nodeId
=
5
;
// id of node that created the message (not the peer that may have sent it)
bytes
sign
=
6
;
// signature of message data + method specific data by message authoring node
int64
timestamp
=
2
;
// unix time
string
id
=
3
;
// allows requesters to use request data when processing a response
bool
gossip
=
4
;
// true to have receiver peer gossip the message to neighbors
string
nodeId
=
5
;
// id of node that created the message (not the peer that may have sent it). =base58(mh(sha256(nodePubKey)))
string
nodePubKey
=
6
;
// node's Secp256k1 public key bytes (32bytes)
string
sign
=
7
;
// signature of message data + method specific data by message authoring node
}
//// ping protocol
...
...
This diff is collapsed.
Click to expand it.
examples/multipro/ping.go
View file @
bf7f80c3
...
...
@@ -9,10 +9,9 @@ import (
inet
"gx/ipfs/QmbD5yKbXahNvoMqzeuNyKQA9vAs9fUvJg2GXeWU1fVqY5/go-libp2p-net"
uuid
"github.com/google/uuid"
p2p
"github.com/
libp2p
/go-libp2p/examples/multipro/pb"
p2p
"github.com/
avive
/go-libp2p/examples/multipro/pb"
protobufCodec
"github.com/multiformats/go-multicodec/protobuf"
"gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host"
"gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
)
// pattern: /protocol-name/request-or-response-message/version
...
...
@@ -59,7 +58,7 @@ func (p PingProtocol) onPingRequest(s inet.Stream) {
// generate response message
log
.
Printf
(
"%s: Sending ping response to %s. Message id: %s..."
,
s
.
Conn
()
.
LocalPeer
(),
s
.
Conn
()
.
RemotePeer
(),
data
.
MessageData
.
Id
)
resp
:=
&
p2p
.
PingResponse
{
MessageData
:
NewMessageData
(
p
eer
.
IDB58Encode
(
p
.
node
.
ID
())
,
data
.
MessageData
.
Id
,
false
),
resp
:=
&
p2p
.
PingResponse
{
MessageData
:
NewMessageData
(
p
.
node
,
data
.
MessageData
.
Id
,
false
),
Message
:
fmt
.
Sprintf
(
"Ping response from %s"
,
p
.
node
.
ID
())}
// sign the data
...
...
@@ -122,7 +121,7 @@ func (p PingProtocol) Ping(host host.Host) bool {
log
.
Printf
(
"%s: Sending ping to: %s...."
,
p
.
node
.
ID
(),
host
.
ID
())
// create message data
req
:=
&
p2p
.
PingRequest
{
MessageData
:
NewMessageData
(
p
eer
.
IDB58Encode
(
p
.
node
.
ID
())
,
uuid
.
New
()
.
String
(),
false
),
req
:=
&
p2p
.
PingRequest
{
MessageData
:
NewMessageData
(
p
.
node
,
uuid
.
New
()
.
String
(),
false
),
Message
:
fmt
.
Sprintf
(
"Ping from %s"
,
p
.
node
.
ID
())}
// sign the data
...
...
This diff is collapsed.
Click to expand it.
examples/multipro/protocol.go
View file @
bf7f80c3
...
...
@@ -2,14 +2,13 @@ package main
import
(
"bufio"
p2p
"github.com/
libp2p
/go-libp2p/examples/multipro/pb"
p2p
"github.com/
avive
/go-libp2p/examples/multipro/pb"
protobufCodec
"github.com/multiformats/go-multicodec/protobuf"
inet
"gx/ipfs/QmbD5yKbXahNvoMqzeuNyKQA9vAs9fUvJg2GXeWU1fVqY5/go-libp2p-net"
"log"
"time"
//host "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host"
//"bytes"
"github.com/gogo/protobuf/proto"
"gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
)
// node version
...
...
@@ -31,11 +30,19 @@ func sendProtoMessage(data proto.Message, s inet.Stream) bool {
}
// helper method - generate message data shared between all node's p2p protocols
// nodeId - message author id
// messageId - unique for requests, copied from request for responses
func
NewMessageData
(
nodeId
string
,
messageId
string
,
gossip
bool
)
*
p2p
.
MessageData
{
func
NewMessageData
(
node
*
Node
,
messageId
string
,
gossip
bool
)
*
p2p
.
MessageData
{
// this creates a protobuf data for a public key
nodePubKey
,
err
:=
node
.
Peerstore
()
.
PubKey
(
node
.
ID
())
.
Bytes
()
if
err
!=
nil
{
panic
(
"Failed to get public key for sender node from peer store."
)
}
return
&
p2p
.
MessageData
{
ClientVersion
:
clientVersion
,
NodeId
:
nodeId
,
NodeId
:
peer
.
IDB58Encode
(
node
.
ID
()),
NodePubKey
:
string
(
nodePubKey
),
Timestamp
:
time
.
Now
()
.
Unix
(),
Id
:
messageId
,
Gossip
:
gossip
}
...
...
This diff is collapsed.
Click to expand it.
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
Menu
Projects
Groups
Snippets
Help