Skip to content

Commit b0837d3

Browse files
authored
Merge pull request #1337 from keep-network/unicast-local
Unicast support - move local implementation from keep-tecdsa to keep-core
2 parents 2784a95 + 315f79d commit b0837d3

File tree

11 files changed

+932
-250
lines changed

11 files changed

+932
-250
lines changed

pkg/net/libp2p/channel.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,6 @@ func (c *channel) RegisterUnmarshaler(unmarshaler func() net.TaggedUnmarshaler)
148148
c.unmarshalersMutex.Lock()
149149
defer c.unmarshalersMutex.Unlock()
150150

151-
if _, exists := c.unmarshalersByType[tpe]; exists {
152-
return fmt.Errorf("type %s already has an associated unmarshaler", tpe)
153-
}
154-
155151
c.unmarshalersByType[tpe] = unmarshaler
156152
return nil
157153
}

pkg/net/libp2p/libp2p.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package libp2p
22

33
import (
44
"context"
5+
"crypto/ecdsa"
56
"fmt"
67
"sync"
78
"time"
@@ -133,6 +134,14 @@ func (p *provider) ConnectionManager() net.ConnectionManager {
133134
return p.connectionManager
134135
}
135136

137+
func (p *provider) CreateTransportIdentifier(publicKey ecdsa.PublicKey) (
138+
net.TransportIdentifier,
139+
error,
140+
) {
141+
networkPublicKey := key.NetworkPublic(publicKey)
142+
return peer.IDFromPublicKey(&networkPublicKey)
143+
}
144+
136145
type connectionManager struct {
137146
host.Host
138147
}

pkg/net/local/broadcast_channel.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package local
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"sync"
7+
"sync/atomic"
8+
9+
"github.com/keep-network/keep-core/pkg/net"
10+
"github.com/keep-network/keep-core/pkg/net/internal"
11+
"github.com/keep-network/keep-core/pkg/net/key"
12+
"github.com/keep-network/keep-core/pkg/net/retransmission"
13+
)
14+
15+
const messageHandlerThrottle = 256
16+
17+
type messageHandler struct {
18+
ctx context.Context
19+
channel chan net.Message
20+
}
21+
22+
type localChannel struct {
23+
counter uint64
24+
name string
25+
identifier net.TransportIdentifier
26+
staticKey *key.NetworkPublic
27+
messageHandlersMutex sync.Mutex
28+
messageHandlers []*messageHandler
29+
unmarshalersMutex sync.Mutex
30+
unmarshalersByType map[string]func() net.TaggedUnmarshaler
31+
retransmissionTicker *retransmission.Ticker
32+
}
33+
34+
func (lc *localChannel) nextSeqno() uint64 {
35+
return atomic.AddUint64(&lc.counter, 1)
36+
}
37+
38+
func (lc *localChannel) Name() string {
39+
return lc.name
40+
}
41+
42+
func (lc *localChannel) Send(ctx context.Context, message net.TaggedMarshaler) error {
43+
bytes, err := message.Marshal()
44+
if err != nil {
45+
return err
46+
}
47+
48+
unmarshaler, found := lc.unmarshalersByType[string(message.Type())]
49+
if !found {
50+
return fmt.Errorf("couldn't find unmarshaler for type %s", string(message.Type()))
51+
}
52+
53+
unmarshaled := unmarshaler()
54+
err = unmarshaled.Unmarshal(bytes)
55+
if err != nil {
56+
return err
57+
}
58+
59+
netMessage := internal.BasicMessage(
60+
lc.identifier,
61+
unmarshaled,
62+
"local",
63+
key.Marshal(lc.staticKey),
64+
lc.nextSeqno(),
65+
)
66+
67+
retransmission.ScheduleRetransmissions(
68+
ctx,
69+
lc.retransmissionTicker,
70+
func() error {
71+
return broadcastMessage(lc.name, netMessage)
72+
},
73+
)
74+
75+
return broadcastMessage(lc.name, netMessage)
76+
}
77+
78+
func (lc *localChannel) deliver(message net.Message) {
79+
lc.messageHandlersMutex.Lock()
80+
snapshot := make([]*messageHandler, len(lc.messageHandlers))
81+
copy(snapshot, lc.messageHandlers)
82+
lc.messageHandlersMutex.Unlock()
83+
84+
for _, handler := range snapshot {
85+
select {
86+
case handler.channel <- message:
87+
default:
88+
logger.Warningf("handler too slow, dropping message")
89+
}
90+
}
91+
}
92+
93+
func (lc *localChannel) Recv(ctx context.Context, handler func(m net.Message)) {
94+
messageHandler := &messageHandler{
95+
ctx: ctx,
96+
channel: make(chan net.Message, messageHandlerThrottle),
97+
}
98+
99+
lc.messageHandlersMutex.Lock()
100+
lc.messageHandlers = append(lc.messageHandlers, messageHandler)
101+
lc.messageHandlersMutex.Unlock()
102+
103+
handleWithRetransmissions := retransmission.WithRetransmissionSupport(handler)
104+
105+
go func() {
106+
for {
107+
select {
108+
case <-ctx.Done():
109+
logger.Debug("context is done, removing handler")
110+
lc.removeHandler(messageHandler)
111+
return
112+
113+
case msg := <-messageHandler.channel:
114+
// Go language specification says that if one or more of the
115+
// communications in the select statement can proceed, a single
116+
// one that will proceed is chosen via a uniform pseudo-random
117+
// selection.
118+
// Thus, it can happen this communication is called when ctx is
119+
// already done. Since we guarantee in the network channel API
120+
// that handler is not called after ctx is done (client code
121+
// could e.g. perform come cleanup), we need to double-check
122+
// the context state here.
123+
if messageHandler.ctx.Err() != nil {
124+
continue
125+
}
126+
127+
handleWithRetransmissions(msg)
128+
}
129+
}
130+
}()
131+
}
132+
133+
func (lc *localChannel) removeHandler(handler *messageHandler) {
134+
lc.messageHandlersMutex.Lock()
135+
defer lc.messageHandlersMutex.Unlock()
136+
137+
for i, h := range lc.messageHandlers {
138+
if h.channel == handler.channel {
139+
lc.messageHandlers[i] = lc.messageHandlers[len(lc.messageHandlers)-1]
140+
lc.messageHandlers = lc.messageHandlers[:len(lc.messageHandlers)-1]
141+
break
142+
}
143+
}
144+
}
145+
146+
func (lc *localChannel) RegisterUnmarshaler(
147+
unmarshaler func() net.TaggedUnmarshaler,
148+
) (err error) {
149+
tpe := unmarshaler().Type()
150+
151+
lc.unmarshalersMutex.Lock()
152+
defer lc.unmarshalersMutex.Unlock()
153+
154+
lc.unmarshalersByType[tpe] = unmarshaler
155+
return nil
156+
}
157+
158+
func (lc *localChannel) SetFilter(filter net.BroadcastChannelFilter) error {
159+
return nil // no-op
160+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package local
2+
3+
import (
4+
"context"
5+
"sync"
6+
"time"
7+
8+
"github.com/keep-network/keep-core/pkg/net"
9+
"github.com/keep-network/keep-core/pkg/net/key"
10+
"github.com/keep-network/keep-core/pkg/net/retransmission"
11+
)
12+
13+
var broadcastChannelsMutex sync.Mutex
14+
var broadcastChannels map[string][]*localChannel
15+
16+
// getBroadcastChannel returns a BroadcastChannel designed to mediate between local
17+
// participants. It delivers all messages sent to the channel through its
18+
// receive channels. RecvChan on a LocalChannel creates a new receive channel
19+
// that is returned to the caller, so that all receive channels can receive
20+
// the message.
21+
func getBroadcastChannel(name string, staticKey *key.NetworkPublic) net.BroadcastChannel {
22+
broadcastChannelsMutex.Lock()
23+
defer broadcastChannelsMutex.Unlock()
24+
if broadcastChannels == nil {
25+
broadcastChannels = make(map[string][]*localChannel)
26+
}
27+
28+
localChannels, exists := broadcastChannels[name]
29+
if !exists {
30+
localChannels = make([]*localChannel, 0)
31+
broadcastChannels[name] = localChannels
32+
}
33+
34+
identifier := randomLocalIdentifier()
35+
channel := &localChannel{
36+
name: name,
37+
identifier: &identifier,
38+
staticKey: staticKey,
39+
messageHandlersMutex: sync.Mutex{},
40+
messageHandlers: make([]*messageHandler, 0),
41+
unmarshalersMutex: sync.Mutex{},
42+
unmarshalersByType: make(map[string]func() net.TaggedUnmarshaler, 0),
43+
retransmissionTicker: retransmission.NewTimeTicker(
44+
context.Background(), 50*time.Millisecond,
45+
),
46+
}
47+
broadcastChannels[name] = append(broadcastChannels[name], channel)
48+
49+
return channel
50+
}
51+
52+
func broadcastMessage(name string, message net.Message) error {
53+
broadcastChannelsMutex.Lock()
54+
targetChannels := broadcastChannels[name]
55+
broadcastChannelsMutex.Unlock()
56+
57+
for _, targetChannel := range targetChannels {
58+
targetChannel.deliver(message)
59+
}
60+
61+
return nil
62+
}

pkg/net/local/identity.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package local
2+
3+
import (
4+
"encoding/hex"
5+
"math/rand"
6+
7+
"github.com/keep-network/keep-core/pkg/net/key"
8+
)
9+
10+
var letterRunes = [52]rune{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
11+
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
12+
'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
13+
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}
14+
15+
type localIdentifier string
16+
17+
func (li localIdentifier) String() string {
18+
return string(li)
19+
}
20+
21+
func randomLocalIdentifier() localIdentifier {
22+
runes := make([]rune, 32)
23+
for i := range runes {
24+
runes[i] = letterRunes[rand.Intn(len(letterRunes))]
25+
}
26+
27+
return localIdentifier(runes)
28+
}
29+
30+
func createLocalIdentifier(staticKey *key.NetworkPublic) localIdentifier {
31+
return localIdentifier(hex.EncodeToString(key.Marshal(staticKey)))
32+
}

0 commit comments

Comments
 (0)