-
Notifications
You must be signed in to change notification settings - Fork 840
Comm Component for Simplex #3998
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
Merged
Merged
Changes from all commits
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
dde076d
add simplex pkg and bls structs
samliok c5be0a4
add simplex to go mod + lint
samliok 5de26b1
added comm, writing tests
samliok 52d343e
add log to createVerifier
samliok 3f74024
add comm tests
samliok d0f0102
Merge branch 'master' into simplex-bls
samliok 458d540
separate into utils file
samliok 49aa0bb
style improvements
samliok 4dfdb34
use GetValidatorSet for consistent validator set
samliok 475a5ef
Merge branch 'master' into simplex-bls
samliok 8c78c03
pass in membership set to config
samliok 14ad42f
remove comment
samliok 2580d63
Merge branch 'master' into simplex-bls
samliok ed4422b
test_util simplified
samliok 14aef3e
add replication outbound msgs
samliok 7d3484d
Merge branch 'simplex-bls' into simplex-comm
samliok b13bc72
lint
samliok 88b06d5
go get
samliok 5f84156
helper function + use testing in utils
samliok 3c362ea
Merge branch 'simplex-bls' into simplex-comm
samliok 03bdc02
Merge branch 'master' into simplex-comm
samliok ebdbfc9
merge conflicts and cleanup
samliok 7a7e5e7
lint
samliok d9b8a2c
logs, and err clarification
samliok c964299
Merge branch 'master' into simplex-comm
samliok 12599d2
add logging to config
samliok a3820e4
Merge branch 'master' into simplex-comm
samliok 5119e93
Merge branch 'master' into simplex-comm
samliok 84106b0
Merge branch 'master' into simplex-comm
samliok 35c0161
rebase
samliok e8a91ec
rebase
samliok ad8ea62
sort nodes in place
samliok 97c816b
update outbound message interface
samliok 91ec652
stray away from mocks + lint
samliok 55ddcdf
update message naming and add send helper
samliok 2bf620b
update simplex
samliok 03758ff
use set instead of array
samliok e1ba039
store both array and set
samliok 3c2274c
Merge branch 'master' into simplex-comm
samliok bd94069
go mod tidy
samliok fb014e1
go mod
samliok 898a29f
small nicts
samliok File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
|
|
||
| package simplex | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
|
|
||
| "github.com/ava-labs/simplex" | ||
| "go.uber.org/zap" | ||
|
|
||
| "github.com/ava-labs/avalanchego/ids" | ||
| "github.com/ava-labs/avalanchego/message" | ||
| "github.com/ava-labs/avalanchego/proto/pb/p2p" | ||
| "github.com/ava-labs/avalanchego/snow/engine/common" | ||
| "github.com/ava-labs/avalanchego/snow/networking/sender" | ||
| "github.com/ava-labs/avalanchego/subnets" | ||
| "github.com/ava-labs/avalanchego/utils/set" | ||
| ) | ||
|
|
||
| var ( | ||
| _ simplex.Communication = (*Comm)(nil) | ||
| errNodeNotFound = errors.New("node not found in the validator list") | ||
| ) | ||
|
|
||
| type Comm struct { | ||
| logger simplex.Logger | ||
| subnetID ids.ID | ||
| chainID ids.ID | ||
| // broadcastNodes are the nodes that should receive broadcast messages | ||
| broadcastNodes set.Set[ids.NodeID] | ||
| // allNodes are the IDs of all the nodes in the subnet | ||
| allNodes []simplex.NodeID | ||
|
|
||
| // sender is used to send messages to other nodes | ||
| sender sender.ExternalSender | ||
| msgBuilder message.OutboundMsgBuilder | ||
| } | ||
|
|
||
| func NewComm(config *Config) (*Comm, error) { | ||
| if _, ok := config.Validators[config.Ctx.NodeID]; !ok { | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
StephenButtolph marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| config.Log.Warn("Node is not a validator for the subnet", | ||
| zap.Stringer("nodeID", config.Ctx.NodeID), | ||
| zap.Stringer("chainID", config.Ctx.ChainID), | ||
| zap.Stringer("subnetID", config.Ctx.SubnetID), | ||
| ) | ||
| return nil, fmt.Errorf("our %w: %s", errNodeNotFound, config.Ctx.NodeID) | ||
| } | ||
|
|
||
| broadcastNodes := set.NewSet[ids.NodeID](len(config.Validators) - 1) | ||
| allNodes := make([]simplex.NodeID, 0, len(config.Validators)) | ||
| // grab all the nodes that are validators for the subnet | ||
| for _, vd := range config.Validators { | ||
| allNodes = append(allNodes, vd.NodeID[:]) | ||
| if vd.NodeID == config.Ctx.NodeID { | ||
| continue // skip our own node ID | ||
| } | ||
|
|
||
| broadcastNodes.Add(vd.NodeID) | ||
| } | ||
|
|
||
| return &Comm{ | ||
| subnetID: config.Ctx.SubnetID, | ||
| broadcastNodes: broadcastNodes, | ||
| allNodes: allNodes, | ||
| logger: config.Log, | ||
| sender: config.Sender, | ||
| msgBuilder: config.OutboundMsgBuilder, | ||
| chainID: config.Ctx.ChainID, | ||
| }, nil | ||
| } | ||
|
|
||
| func (c *Comm) Nodes() []simplex.NodeID { | ||
| return c.allNodes | ||
| } | ||
|
|
||
| func (c *Comm) Send(msg *simplex.Message, destination simplex.NodeID) { | ||
| outboundMsg, err := c.simplexMessageToOutboundMessage(msg) | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if err != nil { | ||
| c.logger.Error("Failed creating message", zap.Error(err)) | ||
| return | ||
| } | ||
|
|
||
| dest, err := ids.ToNodeID(destination) | ||
| if err != nil { | ||
| c.logger.Error("Failed to convert destination NodeID", zap.Error(err)) | ||
| return | ||
| } | ||
|
|
||
| c.sender.Send(outboundMsg, common.SendConfig{NodeIDs: set.Of(dest)}, c.subnetID, subnets.NoOpAllower) | ||
| } | ||
|
|
||
| func (c *Comm) Broadcast(msg *simplex.Message) { | ||
| outboundMsg, err := c.simplexMessageToOutboundMessage(msg) | ||
| if err != nil { | ||
| c.logger.Error("Failed creating message", zap.Error(err)) | ||
| return | ||
| } | ||
|
|
||
| c.sender.Send(outboundMsg, common.SendConfig{NodeIDs: c.broadcastNodes}, c.subnetID, subnets.NoOpAllower) | ||
| } | ||
|
|
||
| func (c *Comm) simplexMessageToOutboundMessage(msg *simplex.Message) (message.OutboundMessage, error) { | ||
| var simplexMsg *p2p.Simplex | ||
| switch { | ||
| case msg.VerifiedBlockMessage != nil: | ||
| bytes, err := msg.VerifiedBlockMessage.VerifiedBlock.Bytes() | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to serialize block: %w", err) | ||
| } | ||
| simplexMsg = newBlockProposal(c.chainID, bytes, msg.VerifiedBlockMessage.Vote) | ||
| case msg.VoteMessage != nil: | ||
| simplexMsg = newVote(c.chainID, msg.VoteMessage) | ||
| case msg.EmptyVoteMessage != nil: | ||
| simplexMsg = newEmptyVote(c.chainID, msg.EmptyVoteMessage) | ||
| case msg.FinalizeVote != nil: | ||
| simplexMsg = newFinalizeVote(c.chainID, msg.FinalizeVote) | ||
| case msg.Notarization != nil: | ||
| simplexMsg = newNotarization(c.chainID, msg.Notarization) | ||
| case msg.EmptyNotarization != nil: | ||
| simplexMsg = newEmptyNotarization(c.chainID, msg.EmptyNotarization) | ||
| case msg.Finalization != nil: | ||
| simplexMsg = newFinalization(c.chainID, msg.Finalization) | ||
| case msg.ReplicationRequest != nil: | ||
| simplexMsg = newReplicationRequest(c.chainID, msg.ReplicationRequest) | ||
| case msg.VerifiedReplicationResponse != nil: | ||
| msg, err := newReplicationResponse(c.chainID, msg.VerifiedReplicationResponse) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to create replication response: %w", err) | ||
| } | ||
| simplexMsg = msg | ||
| default: | ||
| return nil, fmt.Errorf("unknown message type: %+v", msg) | ||
| } | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return c.msgBuilder.SimplexMessage(simplexMsg) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
|
|
||
| package simplex | ||
|
|
||
| import ( | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/ava-labs/simplex" | ||
| "github.com/prometheus/client_golang/prometheus" | ||
| "github.com/stretchr/testify/require" | ||
| "go.uber.org/mock/gomock" | ||
|
|
||
| "github.com/ava-labs/avalanchego/ids" | ||
| "github.com/ava-labs/avalanchego/message" | ||
| "github.com/ava-labs/avalanchego/snow/engine/common" | ||
| "github.com/ava-labs/avalanchego/snow/networking/sender/sendermock" | ||
| "github.com/ava-labs/avalanchego/utils/constants" | ||
| "github.com/ava-labs/avalanchego/utils/set" | ||
| ) | ||
|
|
||
| var testSimplexMessage = simplex.Message{ | ||
| VoteMessage: &simplex.Vote{ | ||
| Vote: simplex.ToBeSignedVote{ | ||
| BlockHeader: simplex.BlockHeader{ | ||
| ProtocolMetadata: simplex.ProtocolMetadata{ | ||
| Version: 1, | ||
| Epoch: 1, | ||
| Round: 1, | ||
| Seq: 1, | ||
| }, | ||
| }, | ||
| }, | ||
| Signature: simplex.Signature{ | ||
| Signer: []byte("dummy_node_id"), | ||
| Value: []byte("dummy_signature"), | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| func TestCommSendMessage(t *testing.T) { | ||
| config := newEngineConfig(t, 1) | ||
|
|
||
| destinationNodeID := ids.GenerateTestNodeID() | ||
| ctrl := gomock.NewController(t) | ||
| sender := sendermock.NewExternalSender(ctrl) | ||
| mc, err := message.NewCreator( | ||
| prometheus.NewRegistry(), | ||
| constants.DefaultNetworkCompressionType, | ||
| 10*time.Second, | ||
| ) | ||
| require.NoError(t, err) | ||
|
|
||
| config.OutboundMsgBuilder = mc | ||
| config.Sender = sender | ||
|
|
||
| comm, err := NewComm(config) | ||
| require.NoError(t, err) | ||
|
|
||
| outboundMsg, err := mc.SimplexMessage(newVote(config.Ctx.ChainID, testSimplexMessage.VoteMessage)) | ||
| require.NoError(t, err) | ||
| expectedSendConfig := common.SendConfig{ | ||
| NodeIDs: set.Of(destinationNodeID), | ||
| } | ||
| sender.EXPECT().Send(outboundMsg, expectedSendConfig, comm.subnetID, gomock.Any()) | ||
|
|
||
| comm.Send(&testSimplexMessage, destinationNodeID[:]) | ||
| } | ||
|
|
||
| // TestCommBroadcast tests the Broadcast method sends to all nodes in the subnet | ||
| // not including the sending node. | ||
| func TestCommBroadcast(t *testing.T) { | ||
| config := newEngineConfig(t, 3) | ||
|
|
||
| ctrl := gomock.NewController(t) | ||
| sender := sendermock.NewExternalSender(ctrl) | ||
| mc, err := message.NewCreator( | ||
| prometheus.NewRegistry(), | ||
| constants.DefaultNetworkCompressionType, | ||
| 10*time.Second, | ||
| ) | ||
| require.NoError(t, err) | ||
|
|
||
| config.OutboundMsgBuilder = mc | ||
| config.Sender = sender | ||
|
|
||
| comm, err := NewComm(config) | ||
| require.NoError(t, err) | ||
| outboundMsg, err := mc.SimplexMessage(newVote(config.Ctx.ChainID, testSimplexMessage.VoteMessage)) | ||
| require.NoError(t, err) | ||
| nodes := make([]ids.NodeID, 0, len(comm.Nodes())) | ||
| for _, node := range comm.Nodes() { | ||
| if node.Equals(config.Ctx.NodeID[:]) { | ||
| continue // skip the sending node | ||
| } | ||
| nodes = append(nodes, ids.NodeID(node)) | ||
| } | ||
|
|
||
| expectedSendConfig := common.SendConfig{ | ||
| NodeIDs: set.Of(nodes...), | ||
| } | ||
|
|
||
| sender.EXPECT().Send(outboundMsg, expectedSendConfig, comm.subnetID, gomock.Any()) | ||
|
|
||
| comm.Broadcast(&testSimplexMessage) | ||
| } | ||
|
|
||
| func TestCommFailsWithoutCurrentNode(t *testing.T) { | ||
| config := newEngineConfig(t, 3) | ||
|
|
||
| ctrl := gomock.NewController(t) | ||
| mc, err := message.NewCreator( | ||
| prometheus.NewRegistry(), | ||
| constants.DefaultNetworkCompressionType, | ||
| 10*time.Second, | ||
| ) | ||
| require.NoError(t, err) | ||
| sender := sendermock.NewExternalSender(ctrl) | ||
|
|
||
| config.OutboundMsgBuilder = mc | ||
| config.Sender = sender | ||
|
|
||
| // set the curNode to a different nodeID than the one in the config | ||
| vdrs := generateTestNodes(t, 3) | ||
| config.Validators = newTestValidatorInfo(vdrs) | ||
|
|
||
| _, err = NewComm(config) | ||
| require.ErrorIs(t, err, errNodeNotFound) | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to this PR, but I have to say I am not sure I understand why this is an un-exported struct. it Makes it impossible to use anywhere in the code (for tests) and we also have only one concrete implementation of it.
@StephenButtolph do you know why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can utilize this in tests by using
message.NewCreator.