Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: check hash value of encrypted topology #123

Merged
merged 6 commits into from
Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func Run() error {
// if topology is not already in file, read from provider
if err != nil {
log.Debug().Msg("Reading topology from provider")
networkTopology, err = topologyProvider.NetworkTopology()
networkTopology, err = topologyProvider.NetworkTopology("")
panicOnError(err)

err = topologyStore.StoreTopology(networkTopology)
Expand Down
16 changes: 6 additions & 10 deletions chains/evm/listener/event-handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ package listener
import (
"context"
"fmt"
"github.com/rs/zerolog"
"math/big"
"strings"

"github.com/rs/zerolog"

"github.com/ChainSafe/chainbridge-core/chains/evm/calls/events"
"github.com/ChainSafe/chainbridge-core/chains/evm/listener"
"github.com/ChainSafe/chainbridge-core/relayer/message"
Expand Down Expand Up @@ -325,11 +326,11 @@ func (eh *RefreshEventHandler) HandleEvent(
return nil
}

topology, err := eh.topologyProvider.NetworkTopology()
if err != nil {
return err
hash := refreshEvents[len(refreshEvents)-1].Hash
if hash == "" {
return fmt.Errorf("hash cannot be empty string")
}
hash, err := topology.Hash()
topology, err := eh.topologyProvider.NetworkTopology(hash)
if err != nil {
return err
}
Expand All @@ -338,11 +339,6 @@ func (eh *RefreshEventHandler) HandleEvent(
return err
}

// if multiple refresh events inside block range use latest
expectedHash := refreshEvents[len(refreshEvents)-1].Hash
if hash != expectedHash {
return fmt.Errorf("aborting refresh because expected hash %s doesn't match %s", expectedHash, hash)
}
eh.connectionGate.SetTopology(topology)
p2p.LoadPeers(eh.host, topology.Peers)

Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ require (
github.com/golang/mock v1.6.0
github.com/imdario/mergo v0.3.12
github.com/libp2p/go-libp2p v0.23.4
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/mitchellh/mapstructure v1.4.2
github.com/multiformats/go-multiaddr v0.7.0
github.com/multiformats/go-multiaddr-dns v0.3.1
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,6 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
Expand Down
36 changes: 0 additions & 36 deletions topology/dev.go

This file was deleted.

8 changes: 4 additions & 4 deletions topology/mock/topology.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 13 additions & 12 deletions topology/topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package topology

import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
Expand All @@ -12,23 +14,13 @@ import (

"github.com/ChainSafe/sygma-relayer/config/relayer"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/mitchellh/hashstructure/v2"
)

type NetworkTopology struct {
Peers []*peer.AddrInfo
Threshold int
}

func (nt NetworkTopology) Hash() (string, error) {
hash, err := hashstructure.Hash(nt, hashstructure.FormatV2, nil)
if err != nil {
return "", err
}

return strconv.FormatUint(hash, 16), nil
}

func (nt NetworkTopology) IsAllowedPeer(peer peer.ID) bool {
for _, p := range nt.Peers {
if p.ID == peer {
Expand Down Expand Up @@ -56,7 +48,9 @@ type Decrypter interface {
}

type NetworkTopologyProvider interface {
NetworkTopology() (NetworkTopology, error)
// NetworkTopology fetches latest topology from network and validates that
// the version matches expected hash.
NetworkTopology(hash string) (NetworkTopology, error)
}

func NewNetworkTopologyProvider(config relayer.TopologyConfiguration, fetcher Fetcher) (NetworkTopologyProvider, error) {
Expand All @@ -78,7 +72,7 @@ type TopologyProvider struct {
fetcher Fetcher
}

func (t *TopologyProvider) NetworkTopology() (NetworkTopology, error) {
func (t *TopologyProvider) NetworkTopology(hash string) (NetworkTopology, error) {
resp, err := t.fetcher.Get(t.url)
if err != nil {
return NetworkTopology{}, err
Expand All @@ -89,6 +83,13 @@ func (t *TopologyProvider) NetworkTopology() (NetworkTopology, error) {
return NetworkTopology{}, err
}

h := sha256.New()
h.Write(body)
eh := hex.EncodeToString(h.Sum(nil))
if hash != "" && eh != hash {
return NetworkTopology{}, fmt.Errorf("topology hash %s not matching expected hash %s", string(eh), hash)
}

unecryptedBody := t.decrypter.Decrypt(string(body))
rawTopology := &RawTopology{}
err = json.Unmarshal(unecryptedBody, rawTopology)
Expand Down
61 changes: 42 additions & 19 deletions topology/topology_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,6 @@ func TestRunNetworkTopologyTestSuite(t *testing.T) {
suite.Run(t, new(NetworkTopologyTestSuite))
}

func (s *NetworkTopologyTestSuite) Test_Hash_ValidHash() {
p1RawAddress := "/ip4/127.0.0.1/tcp/4000/p2p/QmcW3oMdSqoEcjbyd51auqC23vhKX6BqfcZcY2HJ3sKAZR"
p2RawAddress := "/ip4/127.0.0.1/tcp/4002/p2p/QmeWhpY8tknHS29gzf9TAsNEwfejTCNJ7vFpmkV6rNUgyq"
p1, _ := peer.AddrInfoFromString(p1RawAddress)
p2, _ := peer.AddrInfoFromString(p2RawAddress)
topology := topology.NetworkTopology{
Peers: []*peer.AddrInfo{
p1, p2,
},
Threshold: 2,
}

hash, _ := topology.Hash()

s.Equal(hash, "8c6541d0e584f0c0")
}

func (s *NetworkTopologyTestSuite) Test_IsAllowedPeer_ValidPeer() {
p1RawAddress := "/ip4/127.0.0.1/tcp/4000/p2p/QmcW3oMdSqoEcjbyd51auqC23vhKX6BqfcZcY2HJ3sKAZR"
p2RawAddress := "/ip4/127.0.0.1/tcp/4002/p2p/QmeWhpY8tknHS29gzf9TAsNEwfejTCNJ7vFpmkV6rNUgyq"
Expand Down Expand Up @@ -165,7 +148,7 @@ func (s *TopologyProviderTestSuite) Test_FetchingTopologyFails() {
}
topologyProvider, _ := topology.NewNetworkTopologyProvider(topologyConfiguration, s.fetcher)

_, err := topologyProvider.NetworkTopology()
_, err := topologyProvider.NetworkTopology("")

s.NotNil(err)
}
Expand All @@ -180,7 +163,47 @@ func (s *TopologyProviderTestSuite) Test_ValidTopology() {
}
topologyProvider, _ := topology.NewNetworkTopologyProvider(topologyConfiguration, s.fetcher)

tp, err := topologyProvider.NetworkTopology()
tp, err := topologyProvider.NetworkTopology("")

rawTp, _ := topology.ProcessRawTopology(&topology.RawTopology{
Peers: []topology.RawPeer{
{PeerAddress: "/dns4/relayer2/tcp/9001/p2p/QmeTuMtdpPB7zKDgmobEwSvxodrf5aFVSmBXX3SQJVjJaT"},
{PeerAddress: "/dns4/relayer3/tcp/9002/p2p/QmYAYuLUPNwYEBYJaKHcE7NKjUhiUV8txx2xDXHvcYa1xK"},
{PeerAddress: "/dns4/relayer1/tcp/9000/p2p/QmcvEg7jGvuxdsUFRUiE4VdrL2P1Yeju5L83BsJvvXz7zX"},
},
Threshold: "2",
})
s.Nil(err)
s.Equal(rawTp, tp)
}

func (s *TopologyProviderTestSuite) Test_InvalidHash() {
resp := &http.Response{}
resp.Body = io.NopCloser(strings.NewReader("12345678123456786c49ea9bdd0d37f3ad3d266b4ef5d6ef243027b25bd5092091bcd26488e185c4f466c6795535593dc41b03fcd8997985a78bc784c9f561bac89683a0170e5632ec0fc9237a97ebe38f783067d7f0d19dfe708349ca10759e6091228de7899ee10d679c8b444132bfd8106e1d28e944facb21be60182b7c069f264244ab545871ee6d15a1f070cacada34647bc7d2404384b3ee54b9058ec14ae9e017610f392adeb05d33d524e10043908887d932e5a974c8200639c0dc8d77e1cfb65ecbd2f9c731c61212d1a928b5436f3540cfbd981070b5567ced664ef20cc795ebb792231df08f05987a5d9458664d34666995fb15a969440dfd28db35fbd79f9e11cbcfd42409259c4bb1006c0907d2d4b170698e90452ead9ab7f4e41309fe8c586ccee54cc9cfaf3ac22d00b6c6f583a3f7a1fe3ddd470aa12ad9cf63f072798dadb5a21004529fec4a5914d68a18fd0b3fc33079d4ff09af44416b732f024b75b40dd0"))
s.fetcher.EXPECT().Get("test.url").Return(resp, nil)
topologyConfiguration := relayer.TopologyConfiguration{
Url: "test.url",
EncryptionKey: "v8y/B?E(H+MbQeTh",
}
topologyProvider, _ := topology.NewNetworkTopologyProvider(topologyConfiguration, s.fetcher)

_, err := topologyProvider.NetworkTopology("invalid")

s.NotNil(err)
}

func (s *TopologyProviderTestSuite) Test_ValidHash() {
resp := &http.Response{}
resp.Body = io.NopCloser(strings.NewReader("12345678123456786c49ea9bdd0d37f3ad3d266b4ef5d6ef243027b25bd5092091bcd26488e185c4f466c6795535593dc41b03fcd8997985a78bc784c9f561bac89683a0170e5632ec0fc9237a97ebe38f783067d7f0d19dfe708349ca10759e6091228de7899ee10d679c8b444132bfd8106e1d28e944facb21be60182b7c069f264244ab545871ee6d15a1f070cacada34647bc7d2404384b3ee54b9058ec14ae9e017610f392adeb05d33d524e10043908887d932e5a974c8200639c0dc8d77e1cfb65ecbd2f9c731c61212d1a928b5436f3540cfbd981070b5567ced664ef20cc795ebb792231df08f05987a5d9458664d34666995fb15a969440dfd28db35fbd79f9e11cbcfd42409259c4bb1006c0907d2d4b170698e90452ead9ab7f4e41309fe8c586ccee54cc9cfaf3ac22d00b6c6f583a3f7a1fe3ddd470aa12ad9cf63f072798dadb5a21004529fec4a5914d68a18fd0b3fc33079d4ff09af44416b732f024b75b40dd0"))
s.fetcher.EXPECT().Get("test.url").Return(resp, nil)
topologyConfiguration := relayer.TopologyConfiguration{
Url: "test.url",
EncryptionKey: "v8y/B?E(H+MbQeTh",
}
topologyProvider, _ := topology.NewNetworkTopologyProvider(topologyConfiguration, s.fetcher)

expectedHash := "f5909a83374428a4d34ec475ff09f041722145ee7e935847eec9f0b483f9ff06"
tp, err := topologyProvider.NetworkTopology(expectedHash)

rawTp, _ := topology.ProcessRawTopology(&topology.RawTopology{
Peers: []topology.RawPeer{
Expand Down