Skip to content

Commit dec28cd

Browse files
authored
network: Add API for Capabilities (ethersphere#1675)
* network: Add API for Capabilities
1 parent 2d377ce commit dec28cd

File tree

6 files changed

+202
-34
lines changed

6 files changed

+202
-34
lines changed

network/capability/api.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2019 The Swarm authors
2+
// This file is part of the swarm library.
3+
//
4+
// The swarm library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The swarm library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the swarm library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package capability
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/ethersphere/swarm/log"
23+
)
24+
25+
// API abstracts RPC API access to capabilities controls
26+
// will in the future provide notifications of capability changes
27+
type API struct {
28+
*Capabilities
29+
}
30+
31+
// NetAPI creates new API abstraction around provided Capabilities object
32+
func NewAPI(c *Capabilities) *API {
33+
return &API{
34+
Capabilities: c,
35+
}
36+
}
37+
38+
// RegisterCapability adds the given capability object to the Capabilities collection
39+
// If the Capability is already registered an error will be returned
40+
func (a *API) RegisterCapability(cp *Capability) error {
41+
log.Debug("Registering capability", "cp", cp)
42+
return a.Add(cp)
43+
}
44+
45+
// IsRegisteredCapability returns true if a Capability with the given id is registered
46+
func (a *API) IsRegisteredCapability(id CapabilityID) (bool, error) {
47+
return a.Get(id) != nil, nil
48+
}
49+
50+
// MatchCapability returns true if the Capability flag at the given index is set
51+
// Fails with error if the Capability is not registered, or if the index is out of bounds
52+
func (a *API) MatchCapability(id CapabilityID, idx int) (bool, error) {
53+
c := a.Get(id)
54+
if c == nil {
55+
return false, fmt.Errorf("Capability %d not registered", id)
56+
}
57+
if idx > len(c.Cap)-1 {
58+
return false, fmt.Errorf("Capability %d idx %d out of range", id, idx)
59+
}
60+
return c.Cap[idx], nil
61+
}

network/capability/api_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2019 The Swarm authors
2+
// This file is part of the swarm library.
3+
//
4+
// The swarm library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The swarm library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the swarm library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package capability
18+
19+
import (
20+
"testing"
21+
22+
"github.com/ethereum/go-ethereum/rpc"
23+
)
24+
25+
// TestAPI tests that API calls stores and reports correctly
26+
func TestAPI(t *testing.T) {
27+
28+
// Initialize capability
29+
caps := NewCapabilities()
30+
31+
// Set up faux rpc
32+
rpcSrv := rpc.NewServer()
33+
rpcClient := rpc.DialInProc(rpcSrv)
34+
rpcSrv.RegisterName("cap", NewAPI(caps))
35+
36+
// create the capability and register it
37+
c1 := NewCapability(42, 13)
38+
c1.Set(9)
39+
err := rpcClient.Call(nil, "cap_registerCapability", c1)
40+
if err != nil {
41+
t.Fatalf("Register fail: %v", err)
42+
}
43+
44+
// check that the capability is registered
45+
c1.Set(9)
46+
err = rpcClient.Call(nil, "cap_isRegisteredCapability", c1.Id)
47+
if err != nil {
48+
t.Fatalf("Register fail: %v", err)
49+
}
50+
51+
// check that isRegistered doesn't give false positives
52+
c2 := CapabilityID(13)
53+
err = rpcClient.Call(nil, "cap_isRegisteredCapability", c2)
54+
if err != nil {
55+
t.Fatalf("Register fail: %v", err)
56+
}
57+
58+
// check that correct values have been stored
59+
var r bool
60+
err = rpcClient.Call(&r, "cap_matchCapability", c1.Id, 9)
61+
if err != nil {
62+
t.Fatalf("isSet fail: %v", err)
63+
} else if !r {
64+
t.Fatalf("isSet should be false, got %v", r)
65+
}
66+
67+
err = rpcClient.Call(&r, "cap_matchCapability", c1.Id, 1)
68+
if err != nil {
69+
t.Fatalf("isSet fail: %v", err)
70+
} else if r {
71+
t.Fatalf("isSet should be true, got %v", r)
72+
}
73+
}

network/adaptive.go renamed to network/capability/capability.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
1-
package network
1+
// Copyright 2019 The Swarm authors
2+
// This file is part of the swarm library.
3+
//
4+
// The swarm library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The swarm library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the swarm library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package capability
218

319
import (
420
"fmt"
@@ -95,7 +111,7 @@ func NewCapabilities() *Capabilities {
95111
}
96112

97113
// adds a capability to the Capabilities collection
98-
func (c *Capabilities) add(cp *Capability) error {
114+
func (c *Capabilities) Add(cp *Capability) error {
99115
if _, ok := c.idx[cp.Id]; ok {
100116
return fmt.Errorf("Capability id %d already registered", cp.Id)
101117
}
@@ -108,7 +124,7 @@ func (c *Capabilities) add(cp *Capability) error {
108124

109125
// gets the capability with the specified module id
110126
// returns nil if the id doesn't exist
111-
func (c *Capabilities) get(id CapabilityID) *Capability {
127+
func (c *Capabilities) Get(id CapabilityID) *Capability {
112128
idx, ok := c.idx[id]
113129
if !ok {
114130
return nil
@@ -164,7 +180,7 @@ func (c *Capabilities) DecodeRLP(s *rlp.Stream) error {
164180
}
165181

166182
// Add the entry to the Capabilities array
167-
c.add(&cap)
183+
c.Add(&cap)
168184
}
169185

170186
return nil

network/adaptive_test.go renamed to network/capability/capability_test.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
1-
package network
1+
// Copyright 2019 The Swarm authors
2+
// This file is part of the swarm library.
3+
//
4+
// The swarm library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The swarm library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the swarm library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package capability
218

319
import (
420
"bytes"
@@ -63,21 +79,21 @@ func TestCapabilitiesControl(t *testing.T) {
6379

6480
// Register module. Should succeed
6581
c1 := NewCapability(1, 16)
66-
err := caps.add(c1)
82+
err := caps.Add(c1)
6783
if err != nil {
6884
t.Fatalf("RegisterCapabilityModule fail: %v", err)
6985
}
7086

7187
// Fail if capability id already exists
7288
c2 := NewCapability(1, 1)
73-
err = caps.add(c2)
89+
err = caps.Add(c2)
7490
if err == nil {
7591
t.Fatalf("Expected RegisterCapabilityModule call with existing id to fail")
7692
}
7793

7894
// More than one capabilities flag vector should be possible
7995
c3 := NewCapability(2, 1)
80-
err = caps.add(c3)
96+
err = caps.Add(c3)
8197
if err != nil {
8298
t.Fatalf("RegisterCapabilityModule (second) fail: %v", err)
8399
}
@@ -105,8 +121,8 @@ func TestCapabilitiesString(t *testing.T) {
105121
}
106122

107123
caps := NewCapabilities()
108-
caps.add(c1)
109-
caps.add(c2)
124+
caps.Add(c1)
125+
caps.Add(c2)
110126

111127
correctString := "42:001,666:100010101"
112128
if correctString != caps.String() {
@@ -122,12 +138,12 @@ func TestCapabilitiesRLP(t *testing.T) {
122138
Id: 42,
123139
Cap: []bool{true, false, true},
124140
}
125-
c.add(cap1)
141+
c.Add(cap1)
126142
cap2 := &Capability{
127143
Id: 666,
128144
Cap: []bool{true, false, true, false, true, true, false, false, true},
129145
}
130-
c.add(cap2)
146+
c.Add(cap2)
131147
buf := bytes.NewBuffer(nil)
132148
err := rlp.Encode(buf, &c)
133149
if err != nil {
@@ -140,15 +156,15 @@ func TestCapabilitiesRLP(t *testing.T) {
140156
t.Fatal(err)
141157
}
142158

143-
cap1Restored := cRestored.get(cap1.Id)
159+
cap1Restored := cRestored.Get(cap1.Id)
144160
if cap1Restored.Id != cap1.Id {
145161
t.Fatalf("cap 1 id not correct, expected %d, got %d", cap1.Id, cap1Restored.Id)
146162
}
147163
if !cap1.IsSameAs(cap1Restored) {
148164
t.Fatalf("cap 1 caps not correct, expected %v, got %v", cap1.Cap, cap1Restored.Cap)
149165
}
150166

151-
cap2Restored := cRestored.get(cap2.Id)
167+
cap2Restored := cRestored.Get(cap2.Id)
152168
if cap2Restored.Id != cap2.Id {
153169
t.Fatalf("cap 1 id not correct, expected %d, got %d", cap2.Id, cap2Restored.Id)
154170
}

network/protocol.go

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/ethereum/go-ethereum/p2p/enode"
2929
"github.com/ethereum/go-ethereum/rpc"
3030
"github.com/ethersphere/swarm/log"
31+
"github.com/ethersphere/swarm/network/capability"
3132
"github.com/ethersphere/swarm/p2p/protocols"
3233
"github.com/ethersphere/swarm/state"
3334
)
@@ -40,8 +41,8 @@ var (
4041
capabilitiesStorer = 15
4142

4243
// temporary presets to emulate the legacy LightNode/full node regime
43-
fullCapability *Capability
44-
lightCapability *Capability
44+
fullCapability *capability.Capability
45+
lightCapability *capability.Capability
4546
)
4647

4748
const (
@@ -80,19 +81,19 @@ func init() {
8081
}
8182

8283
// temporary convenience functions for legacy "LightNode"
83-
func newLightCapability() *Capability {
84-
c := NewCapability(0, 16)
84+
func newLightCapability() *capability.Capability {
85+
c := capability.NewCapability(0, 16)
8586
c.Set(capabilitiesRetrieve)
8687
c.Set(capabilitiesPush)
8788
return c
8889
}
89-
func isLightCapability(c *Capability) bool {
90+
func isLightCapability(c *capability.Capability) bool {
9091
return lightCapability.IsSameAs(c)
9192
}
9293

9394
// temporary convenience functions for legacy "full node"
94-
func newFullCapability() *Capability {
95-
c := NewCapability(0, 16)
95+
func newFullCapability() *capability.Capability {
96+
c := capability.NewCapability(0, 16)
9697
c.Set(capabilitiesRetrieve)
9798
c.Set(capabilitiesPush)
9899
c.Set(capabilitiesRelayRetrieve)
@@ -101,7 +102,7 @@ func newFullCapability() *Capability {
101102
return c
102103
}
103104

104-
func isFullCapability(c *Capability) bool {
105+
func isFullCapability(c *capability.Capability) bool {
105106
return fullCapability.IsSameAs(c)
106107
}
107108

@@ -124,7 +125,7 @@ type Bzz struct {
124125
handshakes map[enode.ID]*HandshakeMsg
125126
streamerSpec *protocols.Spec
126127
streamerRun func(*BzzPeer) error
127-
capabilities *Capabilities // capabilities control and state
128+
capabilities *capability.Capabilities // capabilities control and state
128129
}
129130

130131
// NewBzz is the swarm protocol constructor
@@ -140,7 +141,7 @@ func NewBzz(config *BzzConfig, kad *Kademlia, store state.Store, streamerSpec *p
140141
handshakes: make(map[enode.ID]*HandshakeMsg),
141142
streamerRun: streamerRun,
142143
streamerSpec: streamerSpec,
143-
capabilities: NewCapabilities(),
144+
capabilities: capability.NewCapabilities(),
144145
}
145146

146147
if config.BootnodeMode {
@@ -150,9 +151,9 @@ func NewBzz(config *BzzConfig, kad *Kademlia, store state.Store, streamerSpec *p
150151

151152
// temporary soon-to-be-legacy light/full, as above
152153
if config.LightNode {
153-
bzz.capabilities.add(newLightCapability())
154+
bzz.capabilities.Add(newLightCapability())
154155
} else {
155-
bzz.capabilities.add(newFullCapability())
156+
bzz.capabilities.Add(newFullCapability())
156157
}
157158

158159
return bzz
@@ -250,7 +251,7 @@ func (b *Bzz) RunProtocol(spec *protocols.Spec, run func(*BzzPeer) error) func(*
250251
Peer: protocols.NewPeer(p, rw, spec),
251252
BzzAddr: handshake.peerAddr,
252253
lastActive: time.Now(),
253-
LightNode: isLightCapability(handshake.Capabilities.get(0)), // this is a temporary member kept until kademlia code accommodates Capabilities instead
254+
LightNode: isLightCapability(handshake.Capabilities.Get(0)), // this is a temporary member kept until kademlia code accommodates Capabilities instead
254255
}
255256

256257
log.Debug("peer created", "addr", handshake.peerAddr.String())
@@ -335,7 +336,7 @@ type HandshakeMsg struct {
335336
Version uint64
336337
NetworkID uint64
337338
Addr *BzzAddr
338-
Capabilities *Capabilities
339+
Capabilities *capability.Capabilities
339340

340341
// peerAddr is the address received in the peer handshake
341342
peerAddr *BzzAddr
@@ -360,7 +361,7 @@ func (b *Bzz) checkHandshake(hs interface{}) error {
360361
return fmt.Errorf("version mismatch %d (!= %d)", rhs.Version, BzzSpec.Version)
361362
}
362363
// temporary check for valid capability settings, legacy full/light
363-
if !isFullCapability(rhs.Capabilities.get(0)) && !isLightCapability(rhs.Capabilities.get(0)) {
364+
if !isFullCapability(rhs.Capabilities.Get(0)) && !isLightCapability(rhs.Capabilities.Get(0)) {
364365
return fmt.Errorf("invalid capabilities setting: %s", rhs.Capabilities)
365366
}
366367
return nil

0 commit comments

Comments
 (0)