From 0e1e267ddfca409b40553044bd381d6fb377113c Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:53:25 +0100 Subject: [PATCH 1/4] fix: race condition when adding new channel to NodeInfo --- internal/libs/sync/concurrent_slice.go | 94 +++++++ internal/libs/sync/concurrent_slice_test.go | 70 +++++ internal/p2p/p2p_test.go | 5 +- internal/p2p/router.go | 10 +- internal/p2p/router_test.go | 5 +- internal/p2p/transport_test.go | 4 +- node/setup.go | 42 +-- proto/tendermint/p2p/types.pb.go | 290 ++++++++++++-------- proto/tendermint/p2p/types.proto | 2 +- types/node_info.go | 39 +-- types/node_info_test.go | 32 ++- version/version.go | 2 +- 12 files changed, 414 insertions(+), 181 deletions(-) create mode 100644 internal/libs/sync/concurrent_slice.go create mode 100644 internal/libs/sync/concurrent_slice_test.go diff --git a/internal/libs/sync/concurrent_slice.go b/internal/libs/sync/concurrent_slice.go new file mode 100644 index 0000000000..ba5289b981 --- /dev/null +++ b/internal/libs/sync/concurrent_slice.go @@ -0,0 +1,94 @@ +package sync + +import "sync" + +type concurrentSlice[T any] struct { + mtx sync.RWMutex + slice []T +} + +// Slice is a thread-safe slice interface +type Slice[T any] interface { + Append(val ...T) + Reset() + Get(index int) T + Set(index int, val T) + ToSlice() []T + Len() int + Copy() Slice[T] +} + +// NewConcurrentSlice creates a new thread-safe slice. +// It is safe to use from multiple goroutines without additional locking. +// It can be referenced by value, and will behave similarly to a regular slice (which is a reference type). +func NewConcurrentSlice[T any](initial ...T) Slice[T] { + return &concurrentSlice[T]{ + slice: initial, + } +} + +// Append adds an element to the slice +func (s *concurrentSlice[T]) Append(val ...T) { + s.mtx.Lock() + defer s.mtx.Unlock() + + s.slice = append(s.slice, val...) +} + +// Reset removes all elements from the slice +func (s *concurrentSlice[T]) Reset() { + s.mtx.Lock() + defer s.mtx.Unlock() + + s.slice = []T{} +} + +// Get returns the value at the given index +func (s *concurrentSlice[T]) Get(index int) T { + s.mtx.RLock() + defer s.mtx.RUnlock() + + return s.slice[index] +} + +func (s *concurrentSlice[T]) Set(index int, val T) { + s.mtx.Lock() + defer s.mtx.Unlock() + + if index > len(s.slice) { + panic("index out of range") + } else if index == len(s.slice) { + s.slice = append(s.slice, val) + return + } + + s.slice[index] = val +} + +// ToSlice returns a copy of the underlying slice +func (s *concurrentSlice[T]) ToSlice() []T { + s.mtx.RLock() + defer s.mtx.RUnlock() + + slice := make([]T, len(s.slice)) + copy(slice, s.slice) + return slice +} + +// Len returns the length of the slice +func (s *concurrentSlice[T]) Len() int { + s.mtx.RLock() + defer s.mtx.RUnlock() + + return len(s.slice) +} + +// Copy returns a new deep copy of concurrentSlice with the same elements +func (s *concurrentSlice[T]) Copy() Slice[T] { + s.mtx.RLock() + defer s.mtx.RUnlock() + + return &concurrentSlice[T]{ + slice: s.ToSlice(), + } +} diff --git a/internal/libs/sync/concurrent_slice_test.go b/internal/libs/sync/concurrent_slice_test.go new file mode 100644 index 0000000000..360510724e --- /dev/null +++ b/internal/libs/sync/concurrent_slice_test.go @@ -0,0 +1,70 @@ +package sync + +import ( + "sync" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConcurrentSlice(t *testing.T) { + s := NewConcurrentSlice[int](1, 2, 3) + + // Test Append + s.Append(4) + if s.Len() != 4 { + t.Errorf("Expected length of slice to be 4, got %d", s.Len()) + } + + // Test Get + if s.Get(3) != 4 { + t.Errorf("Expected element at index 3 to be 4, got %d", s.Get(3)) + } + + // Test Set + s.Set(3, 5) + + // Test ToSlice + slice := s.ToSlice() + if len(slice) != 4 || slice[3] != 4 { + t.Errorf("Expected ToSlice to return [1 2 3 4], got %v", slice) + } + + // Test Reset + s.Reset() + if s.Len() != 0 { + t.Errorf("Expected length of slice to be 0 after Reset, got %d", s.Len()) + } + + // Test Copy + s.Append(5) + copy := s.Copy() + if copy.Len() != 1 || copy.Get(0) != 5 { + t.Errorf("Expected Copy to return a new slice with [5], got %v", copy.ToSlice()) + } +} + +func TestConcurrentSlice_Concurrency(t *testing.T) { + s := NewConcurrentSlice[int]() + + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(1) + go func(val int) { + defer wg.Done() + s.Append(val) + }(i) + } + + wg.Wait() + + assert.Equal(t, 100, s.Len()) + + if s.Len() != 100 { + t.Errorf("Expected length of slice to be 100, got %d", s.Len()) + } + + for i := 0; i < 100; i++ { + assert.Contains(t, s.ToSlice(), i) + } +} diff --git a/internal/p2p/p2p_test.go b/internal/p2p/p2p_test.go index 97a008d025..2d2c6c2c48 100644 --- a/internal/p2p/p2p_test.go +++ b/internal/p2p/p2p_test.go @@ -3,6 +3,7 @@ package p2p_test import ( "github.com/dashpay/tenderdash/crypto" "github.com/dashpay/tenderdash/crypto/ed25519" + tmsync "github.com/dashpay/tenderdash/internal/libs/sync" "github.com/dashpay/tenderdash/internal/p2p" "github.com/dashpay/tenderdash/types" ) @@ -25,7 +26,7 @@ var ( ListenAddr: "0.0.0.0:0", Network: "test", Moniker: string(selfID), - Channels: []byte{0x01, 0x02}, + Channels: tmsync.NewConcurrentSlice[uint16](0x01, 0x02), } peerKey crypto.PrivKey = ed25519.GenPrivKeyFromSecret([]byte{0x84, 0xd7, 0x01, 0xbf, 0x83, 0x20, 0x1c, 0xfe}) @@ -35,6 +36,6 @@ var ( ListenAddr: "0.0.0.0:0", Network: "test", Moniker: string(peerID), - Channels: []byte{0x01, 0x02}, + Channels: tmsync.NewConcurrentSlice[uint16](0x01, 0x02), } ) diff --git a/internal/p2p/router.go b/internal/p2p/router.go index 644fcb7592..2dac816c60 100644 --- a/internal/p2p/router.go +++ b/internal/p2p/router.go @@ -501,7 +501,7 @@ func (r *Router) openConnection(ctx context.Context, conn Connection) { return } - r.routePeer(ctx, peerInfo.NodeID, conn, toChannelIDs(peerInfo.Channels)) + r.routePeer(ctx, peerInfo.NodeID, conn, toChannelIDs(peerInfo.Channels.ToSlice())) } // dialPeers maintains outbound connections to peers by dialing them. @@ -589,7 +589,7 @@ func (r *Router) connectPeer(ctx context.Context, address NodeAddress) { } // routePeer (also) calls connection close - go r.routePeer(ctx, address.NodeID, conn, toChannelIDs(peerInfo.Channels)) + go r.routePeer(ctx, address.NodeID, conn, toChannelIDs(peerInfo.Channels.ToSlice())) } func (r *Router) getOrMakeQueue(peerID types.NodeID, channels ChannelIDSet) queue { @@ -943,9 +943,9 @@ func (cs ChannelIDSet) Contains(id ChannelID) bool { return ok } -func toChannelIDs(bytes []byte) ChannelIDSet { - c := make(map[ChannelID]struct{}, len(bytes)) - for _, b := range bytes { +func toChannelIDs(ids []uint16) ChannelIDSet { + c := make(map[ChannelID]struct{}, len(ids)) + for _, b := range ids { c[ChannelID(b)] = struct{}{} } return c diff --git a/internal/p2p/router_test.go b/internal/p2p/router_test.go index 37aa4e2c76..c486dab845 100644 --- a/internal/p2p/router_test.go +++ b/internal/p2p/router_test.go @@ -17,6 +17,7 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/dashpay/tenderdash/crypto" + tmsync "github.com/dashpay/tenderdash/internal/libs/sync" "github.com/dashpay/tenderdash/internal/p2p" "github.com/dashpay/tenderdash/internal/p2p/mocks" "github.com/dashpay/tenderdash/internal/p2p/p2ptest" @@ -766,7 +767,7 @@ func TestRouter_ChannelCompatability(t *testing.T) { ListenAddr: "0.0.0.0:0", Network: "test", Moniker: string(peerID), - Channels: []byte{0x03}, + Channels: tmsync.NewConcurrentSlice[uint16](0x03), } mockConnection := &mocks.Connection{} @@ -817,7 +818,7 @@ func TestRouter_DontSendOnInvalidChannel(t *testing.T) { ListenAddr: "0.0.0.0:0", Network: "test", Moniker: string(peerID), - Channels: []byte{0x02}, + Channels: tmsync.NewConcurrentSlice[uint16](0x02), } mockConnection := &mocks.Connection{} diff --git a/internal/p2p/transport_test.go b/internal/p2p/transport_test.go index d22dbb9e28..8172a2c9aa 100644 --- a/internal/p2p/transport_test.go +++ b/internal/p2p/transport_test.go @@ -12,8 +12,8 @@ import ( "github.com/stretchr/testify/require" "github.com/dashpay/tenderdash/crypto/ed25519" + tmsync "github.com/dashpay/tenderdash/internal/libs/sync" "github.com/dashpay/tenderdash/internal/p2p" - "github.com/dashpay/tenderdash/libs/bytes" "github.com/dashpay/tenderdash/types" ) @@ -283,7 +283,7 @@ func TestConnection_Handshake(t *testing.T) { ListenAddr: "listenaddr", Network: "network", Version: "1.2.3", - Channels: bytes.HexBytes([]byte{0xf0, 0x0f}), + Channels: tmsync.NewConcurrentSlice[uint16](0xf0, 0x0f), Moniker: "moniker", Other: types.NodeInfoOther{ TxIndex: "txindex", diff --git a/node/setup.go b/node/setup.go index f78d8f4f47..0262749f13 100644 --- a/node/setup.go +++ b/node/setup.go @@ -20,6 +20,8 @@ import ( "github.com/dashpay/tenderdash/internal/eventbus" "github.com/dashpay/tenderdash/internal/evidence" tmstrings "github.com/dashpay/tenderdash/internal/libs/strings" + tmsync "github.com/dashpay/tenderdash/internal/libs/sync" + "github.com/dashpay/tenderdash/internal/mempool" "github.com/dashpay/tenderdash/internal/p2p" "github.com/dashpay/tenderdash/internal/p2p/client" @@ -363,20 +365,20 @@ func makeNodeInfo( NodeID: nodeKey.ID, Network: genDoc.ChainID, Version: version.TMCoreSemVer, - Channels: []byte{ - byte(p2p.BlockSyncChannel), - byte(consensus.StateChannel), - byte(consensus.DataChannel), - byte(consensus.VoteChannel), - byte(consensus.VoteSetBitsChannel), - byte(p2p.MempoolChannel), - byte(evidence.EvidenceChannel), - byte(statesync.SnapshotChannel), - byte(statesync.ChunkChannel), - byte(statesync.LightBlockChannel), - byte(statesync.ParamsChannel), - byte(pex.PexChannel), - }, + Channels: tmsync.NewConcurrentSlice[uint16]( + uint16(p2p.BlockSyncChannel), + uint16(consensus.StateChannel), + uint16(consensus.DataChannel), + uint16(consensus.VoteChannel), + uint16(consensus.VoteSetBitsChannel), + uint16(p2p.MempoolChannel), + uint16(evidence.EvidenceChannel), + uint16(statesync.SnapshotChannel), + uint16(statesync.ChunkChannel), + uint16(statesync.LightBlockChannel), + uint16(statesync.ParamsChannel), + uint16(pex.PexChannel), + ), Moniker: cfg.Moniker, Other: types.NodeInfoOther{ TxIndex: txIndexerStatus, @@ -405,13 +407,11 @@ func makeSeedNodeInfo( Block: state.Version.Consensus.Block, App: state.Version.Consensus.App, }, - NodeID: nodeKey.ID, - Network: genDoc.ChainID, - Version: version.TMCoreSemVer, - Channels: []byte{ - pex.PexChannel, - }, - Moniker: cfg.Moniker, + NodeID: nodeKey.ID, + Network: genDoc.ChainID, + Version: version.TMCoreSemVer, + Channels: tmsync.NewConcurrentSlice[uint16](pex.PexChannel), + Moniker: cfg.Moniker, Other: types.NodeInfoOther{ TxIndex: "off", RPCAddress: cfg.RPC.ListenAddress, diff --git a/proto/tendermint/p2p/types.pb.go b/proto/tendermint/p2p/types.pb.go index 14aeefe3cb..0f1e210ac6 100644 --- a/proto/tendermint/p2p/types.pb.go +++ b/proto/tendermint/p2p/types.pb.go @@ -98,7 +98,7 @@ type NodeInfo struct { ListenAddr string `protobuf:"bytes,3,opt,name=listen_addr,json=listenAddr,proto3" json:"listen_addr,omitempty"` Network string `protobuf:"bytes,4,opt,name=network,proto3" json:"network,omitempty"` Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` - Channels []byte `protobuf:"bytes,6,opt,name=channels,proto3" json:"channels,omitempty"` + Channels []uint32 `protobuf:"varint,6,rep,packed,name=channels,proto3" json:"channels,omitempty"` Moniker string `protobuf:"bytes,7,opt,name=moniker,proto3" json:"moniker,omitempty"` Other NodeInfoOther `protobuf:"bytes,8,opt,name=other,proto3" json:"other"` ProTxHash []byte `protobuf:"bytes,9,opt,name=pro_tx_hash,json=proTxHash,proto3" json:"pro_tx_hash,omitempty"` @@ -172,7 +172,7 @@ func (m *NodeInfo) GetVersion() string { return "" } -func (m *NodeInfo) GetChannels() []byte { +func (m *NodeInfo) GetChannels() []uint32 { if m != nil { return m.Channels } @@ -899,7 +899,7 @@ func init() { func init() { proto.RegisterFile("tendermint/p2p/types.proto", fileDescriptor_c8a29e659aeca578) } var fileDescriptor_c8a29e659aeca578 = []byte{ - // 1436 bytes of a gzipped FileDescriptorProto + // 1438 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4b, 0x6f, 0xdb, 0x46, 0x17, 0x95, 0xe4, 0x97, 0x74, 0x65, 0x59, 0xf6, 0xc4, 0x5f, 0xc2, 0x38, 0x89, 0xe4, 0xcf, 0xe9, 0xc3, 0xe8, 0x42, 0x2a, 0x14, 0xa0, 0x48, 0x83, 0x06, 0x48, 0xe4, 0x38, 0x91, 0x0b, 0x27, 0x11, @@ -923,73 +923,73 @@ var fileDescriptor_c8a29e659aeca578 = []byte{ 0x3d, 0xf0, 0x89, 0xb9, 0x2c, 0xb7, 0x0e, 0x5c, 0xd4, 0x84, 0x6a, 0xe0, 0x73, 0x81, 0x89, 0x65, 0xbb, 0x6e, 0xa8, 0xaa, 0xab, 0x98, 0x10, 0xa5, 0x1e, 0xbb, 0x6e, 0x88, 0x0c, 0x58, 0x21, 0x58, 0xbc, 0xa6, 0xe1, 0x99, 0xb1, 0xa8, 0x36, 0xe3, 0x50, 0xee, 0xc4, 0x85, 0x2e, 0x45, 0x3b, 0x3a, - 0x44, 0x5b, 0x50, 0x76, 0x3c, 0x9b, 0x10, 0x1c, 0x70, 0x63, 0x79, 0xbb, 0xb8, 0xbb, 0x6a, 0x26, + 0x44, 0x5b, 0x50, 0x76, 0x3c, 0x9b, 0x10, 0x1c, 0x70, 0x63, 0x79, 0x7b, 0x61, 0xb7, 0x66, 0x26, 0xb1, 0x64, 0x0d, 0x29, 0xf1, 0xcf, 0x70, 0x68, 0xac, 0x44, 0x2c, 0x1d, 0xa2, 0xaf, 0x61, 0x89, 0x0a, 0x0f, 0x87, 0x46, 0x59, 0x1d, 0xfb, 0x4e, 0xfe, 0xd8, 0xb1, 0x55, 0x2f, 0x25, 0x48, 0x1f, - 0x3a, 0x62, 0xa0, 0x06, 0x54, 0x59, 0x48, 0x2d, 0x31, 0xb1, 0x3c, 0x9b, 0x7b, 0x46, 0x45, 0x3d, - 0xb3, 0xc2, 0x42, 0x7a, 0x3c, 0xe9, 0xd9, 0xdc, 0xdb, 0xf9, 0x09, 0x6a, 0x53, 0x6c, 0x74, 0x13, - 0xca, 0x62, 0x62, 0xf9, 0xc4, 0xc5, 0x13, 0xe5, 0x72, 0xc5, 0x5c, 0x11, 0x93, 0x03, 0x19, 0xa2, - 0x36, 0x54, 0x43, 0xe6, 0x28, 0x3b, 0x30, 0xe7, 0xda, 0xba, 0xb5, 0xcb, 0x8b, 0x26, 0x98, 0xfd, - 0xbd, 0xc7, 0x51, 0xd6, 0x84, 0x90, 0x39, 0x7a, 0xbd, 0xf3, 0x77, 0x11, 0xca, 0x7d, 0x8c, 0x43, - 0xd5, 0xc6, 0xeb, 0x50, 0xf2, 0xdd, 0x48, 0xb2, 0xbb, 0x7c, 0x79, 0xd1, 0x2c, 0x1d, 0x3c, 0x31, - 0x4b, 0xbe, 0x8b, 0xba, 0xb0, 0xaa, 0x15, 0x2d, 0x9f, 0x9c, 0x50, 0xa3, 0xb4, 0xbd, 0x70, 0x65, - 0x6b, 0x31, 0x0e, 0xb5, 0xae, 0x94, 0x33, 0xab, 0x76, 0x1a, 0xa0, 0x67, 0xb0, 0x16, 0xd8, 0x5c, - 0x58, 0x0e, 0x25, 0x04, 0x3b, 0x02, 0xbb, 0xaa, 0x5d, 0xd5, 0xce, 0x56, 0x2b, 0x9a, 0xdf, 0x56, - 0x3c, 0xbf, 0xad, 0xe3, 0x78, 0x7e, 0xbb, 0x8b, 0x6f, 0xfe, 0x68, 0x16, 0xcd, 0x9a, 0xe4, 0xed, - 0xc5, 0x34, 0xd9, 0x1f, 0x9f, 0xd8, 0x8e, 0xf0, 0xc7, 0x58, 0x35, 0xb5, 0x6c, 0x26, 0x71, 0xde, - 0xca, 0xa5, 0xbc, 0x95, 0xff, 0x14, 0xa1, 0x9e, 0xab, 0x52, 0xf6, 0x34, 0xb6, 0x4b, 0x9b, 0xa9, - 0x43, 0x74, 0x08, 0x1b, 0xaa, 0x64, 0xd7, 0xb7, 0x03, 0x8b, 0x8f, 0x1c, 0x27, 0xb6, 0xf4, 0x63, - 0xaa, 0xae, 0x4b, 0xea, 0x13, 0xdf, 0x0e, 0x8e, 0x22, 0xe2, 0xb4, 0xda, 0x89, 0xed, 0x07, 0xa3, - 0x10, 0x7f, 0xb4, 0x07, 0x89, 0xda, 0xd3, 0x88, 0x88, 0xee, 0x42, 0x2d, 0x2b, 0xc4, 0x95, 0x15, - 0x35, 0x73, 0xd5, 0x4d, 0x31, 0x7c, 0xe7, 0x36, 0x2c, 0xee, 0x3b, 0x1e, 0x95, 0xbf, 0xe9, 0xb1, - 0x1d, 0x8c, 0xb0, 0x3e, 0x60, 0x14, 0xec, 0xfc, 0xb6, 0x01, 0xe5, 0x7d, 0x32, 0xc6, 0x01, 0x65, - 0x18, 0xf5, 0x00, 0x6c, 0x21, 0x42, 0x7f, 0x30, 0x12, 0x58, 0x1a, 0x21, 0x1b, 0xbc, 0x9b, 0x6f, - 0x70, 0x8c, 0x6e, 0x3d, 0x4e, 0xa0, 0xfb, 0x44, 0x84, 0xe7, 0x66, 0x86, 0x8b, 0xbe, 0x80, 0x45, - 0xec, 0x78, 0x54, 0x1b, 0xb5, 0x39, 0xa3, 0xe1, 0x78, 0xb4, 0x57, 0x30, 0x15, 0x06, 0x3d, 0x84, - 0x2a, 0xc3, 0x13, 0x2b, 0xc4, 0xbf, 0x8e, 0x30, 0x17, 0x89, 0x1b, 0x33, 0x73, 0x35, 0x31, 0x23, - 0x44, 0xaf, 0x60, 0x02, 0x4b, 0x22, 0xf4, 0x08, 0x56, 0x23, 0x3a, 0x67, 0xf2, 0x5e, 0x56, 0x1e, - 0x54, 0x3b, 0xb7, 0xae, 0xe4, 0x47, 0x90, 0x5e, 0xc1, 0xac, 0xb2, 0x34, 0x44, 0xf7, 0xa1, 0x1c, - 0x5f, 0xc4, 0x6a, 0x5a, 0x72, 0x4f, 0x8f, 0x6e, 0xe0, 0x7d, 0x8d, 0xe8, 0x15, 0xcc, 0x04, 0x8d, - 0x1e, 0x40, 0x55, 0xdf, 0xd3, 0x96, 0x98, 0x44, 0x37, 0x45, 0xb5, 0x73, 0x23, 0x4b, 0xd6, 0xdb, - 0xad, 0xe3, 0x09, 0x97, 0x75, 0xeb, 0xf0, 0x78, 0xc2, 0xd1, 0x01, 0xd4, 0xd4, 0xb5, 0x9a, 0x1c, - 0x7c, 0x45, 0xb1, 0x77, 0xb2, 0xec, 0xe4, 0x5d, 0xd4, 0xea, 0xca, 0x55, 0x6a, 0xc0, 0xea, 0x20, - 0x13, 0xa3, 0x23, 0xd8, 0x20, 0xd4, 0x8a, 0xd5, 0xb4, 0x0f, 0xd1, 0x1d, 0xf4, 0xe9, 0xd5, 0x72, - 0x2f, 0xa8, 0x16, 0x4c, 0x1c, 0xa9, 0x93, 0xe9, 0x14, 0x3a, 0x84, 0xb5, 0x9c, 0x62, 0x45, 0x29, - 0xde, 0x9d, 0x5b, 0x60, 0xa2, 0x57, 0x1b, 0xe4, 0xd5, 0xe4, 0x4b, 0x71, 0xc4, 0x93, 0xe3, 0xc2, - 0x3c, 0xb5, 0x23, 0x85, 0x4d, 0xcf, 0x5b, 0xe3, 0xd9, 0x04, 0x7a, 0x09, 0xf5, 0x44, 0x4d, 0x17, - 0x57, 0x55, 0x72, 0x9f, 0xcc, 0x97, 0x4b, 0xaa, 0x5b, 0xe3, 0x53, 0x19, 0xf4, 0x1d, 0x6c, 0x70, - 0x62, 0x33, 0xee, 0x51, 0x91, 0x56, 0xb8, 0xaa, 0x24, 0x3f, 0xcb, 0x4a, 0x26, 0x2f, 0xf6, 0xd6, - 0x51, 0x0c, 0x4f, 0x8b, 0x5c, 0xe7, 0xb9, 0x1c, 0xfa, 0x1e, 0x50, 0x56, 0x56, 0x97, 0x5a, 0x53, - 0xba, 0x9f, 0x7f, 0x50, 0x37, 0xa9, 0x76, 0x83, 0xe7, 0x93, 0x72, 0x7a, 0x1c, 0x6f, 0x44, 0xd2, - 0xe9, 0x59, 0x9b, 0x9d, 0x9e, 0x54, 0x74, 0x4f, 0x42, 0x33, 0xd3, 0xe3, 0x64, 0x62, 0xd9, 0x9a, - 0x58, 0x4a, 0x17, 0x58, 0x9f, 0x6d, 0xcd, 0x8c, 0x56, 0xda, 0x68, 0x27, 0x9b, 0x40, 0x3f, 0xc0, - 0xb5, 0xc0, 0x3f, 0xf5, 0x84, 0x35, 0x3d, 0xdc, 0xeb, 0xf3, 0xce, 0x7c, 0x28, 0x09, 0xb9, 0x09, - 0xdf, 0x08, 0xf2, 0x49, 0xf4, 0x33, 0x6c, 0x4e, 0x4b, 0xeb, 0x72, 0x37, 0x94, 0xf6, 0xee, 0x87, - 0xb5, 0x93, 0x9a, 0x51, 0x30, 0x93, 0x95, 0x36, 0x30, 0x3b, 0xb4, 0x87, 0x69, 0xff, 0xd1, 0x3c, - 0x1b, 0xfa, 0x0a, 0x9b, 0x99, 0x50, 0x96, 0x4d, 0xc8, 0x09, 0x4d, 0xd4, 0x74, 0x99, 0xd7, 0x66, - 0x27, 0x74, 0x56, 0x2e, 0x9d, 0x50, 0x36, 0x95, 0x41, 0xdf, 0xc2, 0x1a, 0xc1, 0xaf, 0xad, 0x90, - 0x8e, 0x88, 0x6b, 0x71, 0x81, 0x99, 0xb1, 0x39, 0xdb, 0xf1, 0xe4, 0xcb, 0xb4, 0xf5, 0x02, 0xbf, - 0x36, 0x25, 0xf4, 0x48, 0x60, 0x26, 0x3b, 0x4e, 0x32, 0x31, 0x7a, 0x0e, 0x75, 0xa9, 0x35, 0xb6, - 0x03, 0xdf, 0x8d, 0xcc, 0x34, 0xfe, 0x37, 0x7b, 0xd6, 0x29, 0xb1, 0x57, 0x12, 0xab, 0x0c, 0x93, - 0x67, 0x25, 0xd9, 0x04, 0xfa, 0x06, 0xca, 0x2c, 0xa4, 0x8c, 0x72, 0x3b, 0x30, 0xae, 0x2b, 0x9d, - 0xc6, 0xd5, 0x3a, 0x7d, 0x8d, 0x92, 0x77, 0x68, 0xcc, 0x40, 0x4f, 0x61, 0x35, 0x5e, 0x5b, 0x8c, - 0x06, 0xc6, 0x0d, 0xa5, 0xf0, 0xff, 0xf9, 0x0a, 0xfd, 0x97, 0x87, 0xea, 0x16, 0x8f, 0x43, 0x1a, - 0xa0, 0x47, 0x00, 0xd1, 0x5c, 0x30, 0x3b, 0x14, 0x86, 0x31, 0xfb, 0xe1, 0x99, 0xaa, 0xa8, 0xb2, - 0xfb, 0x76, 0x28, 0xfb, 0x56, 0x19, 0xc4, 0x01, 0xfa, 0x12, 0x16, 0xc7, 0x54, 0x60, 0xe3, 0xe6, - 0xec, 0x3b, 0x20, 0xe5, 0xbe, 0xa2, 0x42, 0xb6, 0x47, 0x21, 0xd1, 0x03, 0x28, 0x7b, 0x36, 0xb7, - 0x14, 0x6b, 0x6b, 0xf6, 0x9b, 0x2f, 0x65, 0xf5, 0x6c, 0xae, 0x89, 0x2b, 0x5e, 0xb4, 0x94, 0x0d, - 0x95, 0x3c, 0x8b, 0x63, 0x61, 0x0d, 0xed, 0x5f, 0x3a, 0xf7, 0x8c, 0x5b, 0xf3, 0x1a, 0x2a, 0x39, - 0x47, 0x58, 0x3c, 0x97, 0x48, 0xd9, 0xd0, 0x71, 0x26, 0x46, 0xcf, 0xa0, 0x96, 0x68, 0x0d, 0x7c, - 0xc1, 0x8d, 0xdb, 0xf3, 0x4c, 0xd4, 0x52, 0x5d, 0x5f, 0xc8, 0x77, 0x52, 0x75, 0x9c, 0x86, 0xe8, - 0x2b, 0x58, 0x76, 0xe8, 0x70, 0xe8, 0x0b, 0xe3, 0x8e, 0x52, 0xb8, 0x7d, 0xb5, 0xc2, 0x9e, 0xc2, - 0xf4, 0x0a, 0xa6, 0x46, 0x4b, 0xf3, 0xa5, 0x11, 0x9a, 0xdb, 0x98, 0x67, 0x7e, 0xcf, 0xe6, 0x09, - 0xbd, 0xe2, 0xc5, 0xc1, 0xd6, 0x43, 0xa8, 0xe7, 0x3e, 0x28, 0xe4, 0xff, 0x8d, 0x33, 0x7c, 0xae, - 0xbf, 0x57, 0xe4, 0x32, 0xfd, 0x86, 0x29, 0x65, 0xbe, 0x61, 0x1e, 0x94, 0xee, 0x17, 0xbb, 0x4b, - 0xb0, 0xc0, 0x47, 0xc3, 0xee, 0xe1, 0xdb, 0xcb, 0x46, 0xf1, 0xdd, 0x65, 0xa3, 0xf8, 0xe7, 0x65, - 0xa3, 0xf8, 0xe6, 0x7d, 0xa3, 0xf0, 0xee, 0x7d, 0xa3, 0xf0, 0xfb, 0xfb, 0x46, 0xe1, 0xc7, 0xce, - 0xa9, 0x2f, 0xbc, 0xd1, 0xa0, 0xe5, 0xd0, 0x61, 0xdb, 0xb5, 0xb9, 0xc7, 0xec, 0xf3, 0x76, 0x54, - 0x9f, 0x8c, 0xa2, 0x3f, 0x4e, 0xed, 0xe9, 0xff, 0x57, 0x83, 0x65, 0x95, 0xbd, 0xf7, 0x6f, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x5d, 0xf9, 0xc2, 0xa1, 0x61, 0x0e, 0x00, 0x00, + 0x3a, 0x62, 0xa0, 0x06, 0x54, 0x59, 0x48, 0x2d, 0x31, 0xb1, 0x3c, 0x9b, 0x7b, 0x46, 0x65, 0xbb, + 0xb8, 0xbb, 0x6a, 0x56, 0x58, 0x48, 0x8f, 0x27, 0x3d, 0x9b, 0x7b, 0x3b, 0x3f, 0x41, 0x6d, 0x8a, + 0x8d, 0x6e, 0x42, 0x59, 0x4c, 0x2c, 0x9f, 0xb8, 0x78, 0xa2, 0x5c, 0xae, 0x98, 0x2b, 0x62, 0x72, + 0x20, 0x43, 0xd4, 0x86, 0x6a, 0xc8, 0x1c, 0x65, 0x07, 0xe6, 0x5c, 0x5b, 0xb7, 0x76, 0x79, 0xd1, + 0x04, 0xb3, 0xbf, 0xf7, 0x38, 0xca, 0x9a, 0x10, 0x32, 0x47, 0xaf, 0x77, 0xfe, 0x2e, 0x42, 0xb9, + 0x8f, 0x71, 0xa8, 0xda, 0x78, 0x1d, 0x4a, 0xbe, 0x1b, 0x49, 0x76, 0x97, 0x2f, 0x2f, 0x9a, 0xa5, + 0x83, 0x27, 0x66, 0xc9, 0x77, 0x51, 0x17, 0x56, 0xb5, 0xa2, 0xe5, 0x93, 0x13, 0x6a, 0x94, 0xb6, + 0x17, 0xae, 0x6c, 0x2d, 0xc6, 0xa1, 0xd6, 0x95, 0x72, 0x66, 0xd5, 0x4e, 0x03, 0xf4, 0x0c, 0xd6, + 0x02, 0x9b, 0x0b, 0xcb, 0xa1, 0x84, 0x60, 0x47, 0x60, 0x57, 0xb5, 0xab, 0xda, 0xd9, 0x6a, 0x45, + 0xf3, 0xdb, 0x8a, 0xe7, 0xb7, 0x75, 0x1c, 0xcf, 0x6f, 0x77, 0xf1, 0xcd, 0x1f, 0xcd, 0xa2, 0x59, + 0x93, 0xbc, 0xbd, 0x98, 0x26, 0xfb, 0xe3, 0x13, 0xdb, 0x11, 0xfe, 0x18, 0xab, 0xa6, 0x96, 0xcd, + 0x24, 0xce, 0x5b, 0xb9, 0x94, 0xb7, 0xf2, 0x9f, 0x22, 0xd4, 0x73, 0x55, 0xca, 0x9e, 0xc6, 0x76, + 0x69, 0x33, 0x75, 0x88, 0x0e, 0x61, 0x43, 0x95, 0xec, 0xfa, 0x76, 0x60, 0xf1, 0x91, 0xe3, 0xc4, + 0x96, 0x7e, 0x4c, 0xd5, 0x75, 0x49, 0x7d, 0xe2, 0xdb, 0xc1, 0x51, 0x44, 0x9c, 0x56, 0x3b, 0xb1, + 0xfd, 0x60, 0x14, 0xe2, 0x8f, 0xf6, 0x20, 0x51, 0x7b, 0x1a, 0x11, 0xd1, 0x5d, 0xa8, 0x65, 0x85, + 0xb8, 0xb2, 0xa2, 0x66, 0xae, 0xba, 0x29, 0x86, 0xef, 0xdc, 0x86, 0xc5, 0x7d, 0xc7, 0xa3, 0xf2, + 0x37, 0x3d, 0xb6, 0x83, 0x11, 0xd6, 0x07, 0x8c, 0x82, 0x9d, 0xdf, 0x36, 0xa0, 0xbc, 0x4f, 0xc6, + 0x38, 0xa0, 0x0c, 0xa3, 0x1e, 0x80, 0x2d, 0x44, 0xe8, 0x0f, 0x46, 0x02, 0x4b, 0x23, 0x64, 0x83, + 0x77, 0xf3, 0x0d, 0x8e, 0xd1, 0xad, 0xc7, 0x09, 0x74, 0x9f, 0x88, 0xf0, 0xdc, 0xcc, 0x70, 0xd1, + 0x17, 0xb0, 0x88, 0x1d, 0x8f, 0x6a, 0xa3, 0x36, 0x67, 0x34, 0x1c, 0x8f, 0xf6, 0x0a, 0xa6, 0xc2, + 0xa0, 0x87, 0x50, 0x65, 0x78, 0x62, 0x85, 0xf8, 0xd7, 0x11, 0xe6, 0x22, 0x71, 0x63, 0x66, 0xae, + 0x26, 0x66, 0x84, 0xe8, 0x15, 0x4c, 0x60, 0x49, 0x84, 0x1e, 0xc1, 0x6a, 0x44, 0xe7, 0x4c, 0xde, + 0xcb, 0xca, 0x83, 0x6a, 0xe7, 0xd6, 0x95, 0xfc, 0x08, 0xd2, 0x2b, 0x98, 0x55, 0x96, 0x86, 0xe8, + 0x3e, 0x94, 0xe3, 0x8b, 0x58, 0x4d, 0x4b, 0xee, 0xe9, 0xd1, 0x0d, 0xbc, 0xaf, 0x11, 0xbd, 0x82, + 0x99, 0xa0, 0xd1, 0x03, 0xa8, 0xea, 0x7b, 0xda, 0x12, 0x13, 0x79, 0x53, 0x48, 0xf2, 0x8d, 0x2c, + 0x59, 0x6f, 0xb7, 0x8e, 0x27, 0x5c, 0xd6, 0xad, 0xc3, 0xe3, 0x09, 0x47, 0x07, 0x50, 0x53, 0xd7, + 0x6a, 0x72, 0xf0, 0x15, 0xc5, 0xde, 0xc9, 0xb2, 0x93, 0x77, 0x51, 0xab, 0x2b, 0x57, 0xa9, 0x01, + 0xab, 0x83, 0x4c, 0x8c, 0x8e, 0x60, 0x83, 0x50, 0x2b, 0x56, 0xd3, 0x3e, 0x44, 0x77, 0xd0, 0xa7, + 0x57, 0xcb, 0xbd, 0xa0, 0x5a, 0x30, 0x71, 0xa4, 0x4e, 0xa6, 0x53, 0xe8, 0x10, 0xd6, 0x72, 0x8a, + 0x15, 0xa5, 0x78, 0x77, 0x6e, 0x81, 0x89, 0x5e, 0x6d, 0x90, 0x57, 0x93, 0x2f, 0xc5, 0x11, 0x4f, + 0x8e, 0x0b, 0xf3, 0xd4, 0x8e, 0x14, 0x36, 0x3d, 0x6f, 0x8d, 0x67, 0x13, 0xe8, 0x25, 0xd4, 0x13, + 0x35, 0x5d, 0x5c, 0x55, 0xc9, 0x7d, 0x32, 0x5f, 0x2e, 0xa9, 0x6e, 0x8d, 0x4f, 0x65, 0xd0, 0x77, + 0xb0, 0xc1, 0x89, 0xcd, 0xb8, 0x47, 0x45, 0x5a, 0xe1, 0xaa, 0x92, 0xfc, 0x2c, 0x2b, 0x99, 0xbc, + 0xd8, 0x5b, 0x47, 0x31, 0x3c, 0x2d, 0x72, 0x9d, 0xe7, 0x72, 0xe8, 0x7b, 0x40, 0x59, 0x59, 0x5d, + 0x6a, 0x4d, 0xe9, 0x7e, 0xfe, 0x41, 0xdd, 0xa4, 0xda, 0x0d, 0x9e, 0x4f, 0xca, 0xe9, 0x71, 0xbc, + 0x11, 0x49, 0xa7, 0x67, 0x6d, 0x76, 0x7a, 0x52, 0xd1, 0x3d, 0x09, 0xcd, 0x4c, 0x8f, 0x93, 0x89, + 0x65, 0x6b, 0x62, 0x29, 0x5d, 0x60, 0x7d, 0xb6, 0x35, 0x33, 0x5a, 0x69, 0xa3, 0x9d, 0x6c, 0x02, + 0xfd, 0x00, 0xd7, 0x02, 0xff, 0xd4, 0x13, 0xd6, 0xf4, 0x70, 0xaf, 0xcf, 0x3b, 0xf3, 0xa1, 0x24, + 0xe4, 0x26, 0x7c, 0x23, 0xc8, 0x27, 0xd1, 0xcf, 0xb0, 0x39, 0x2d, 0xad, 0xcb, 0xdd, 0x50, 0xda, + 0xbb, 0x1f, 0xd6, 0x4e, 0x6a, 0x46, 0xc1, 0x4c, 0x56, 0xda, 0xc0, 0xec, 0xd0, 0x1e, 0xa6, 0xfd, + 0x47, 0xf3, 0x6c, 0xe8, 0x2b, 0x6c, 0x66, 0x42, 0x59, 0x36, 0x21, 0x27, 0x34, 0x51, 0xd3, 0x65, + 0x5e, 0x9b, 0x9d, 0xd0, 0x59, 0xb9, 0x74, 0x42, 0xd9, 0x54, 0x06, 0x7d, 0x0b, 0x6b, 0x04, 0xbf, + 0xb6, 0x42, 0x3a, 0x22, 0xae, 0xc5, 0x05, 0x66, 0xc6, 0xe6, 0x6c, 0xc7, 0x93, 0x2f, 0xd3, 0xd6, + 0x0b, 0xfc, 0xda, 0x94, 0xd0, 0x23, 0x81, 0x99, 0xec, 0x38, 0xc9, 0xc4, 0xe8, 0x39, 0xd4, 0xa5, + 0xd6, 0xd8, 0x0e, 0x7c, 0x37, 0x32, 0xd3, 0xf8, 0xdf, 0xec, 0x59, 0xa7, 0xc4, 0x5e, 0x49, 0xac, + 0x32, 0x4c, 0x9e, 0x95, 0x64, 0x13, 0xe8, 0x1b, 0x28, 0xb3, 0x90, 0x32, 0xca, 0xed, 0xc0, 0xb8, + 0xae, 0x74, 0x1a, 0x57, 0xeb, 0xf4, 0x35, 0x4a, 0xde, 0xa1, 0x31, 0x03, 0x3d, 0x85, 0xd5, 0x78, + 0x6d, 0x31, 0x1a, 0x18, 0x37, 0x94, 0xc2, 0xff, 0xe7, 0x2b, 0xf4, 0x5f, 0x1e, 0xaa, 0x5b, 0x3c, + 0x0e, 0x69, 0x80, 0x1e, 0x01, 0x44, 0x73, 0xc1, 0xec, 0x50, 0x18, 0xc6, 0xec, 0x87, 0x67, 0xaa, + 0xa2, 0xca, 0xee, 0xdb, 0xa1, 0xec, 0x5b, 0x65, 0x10, 0x07, 0xe8, 0x4b, 0x58, 0x1c, 0x53, 0x81, + 0x8d, 0x9b, 0xb3, 0xef, 0x80, 0x94, 0xfb, 0x8a, 0x0a, 0xd9, 0x1e, 0x85, 0x44, 0x0f, 0xa0, 0xec, + 0xd9, 0xdc, 0x52, 0xac, 0xad, 0xd9, 0x6f, 0xbe, 0x94, 0xd5, 0xb3, 0xb9, 0x26, 0xae, 0x78, 0xd1, + 0x52, 0x36, 0x54, 0xf2, 0x2c, 0x8e, 0x85, 0x35, 0xb4, 0x7f, 0xe9, 0xdc, 0x33, 0x6e, 0xcd, 0x6b, + 0xa8, 0xe4, 0x1c, 0x61, 0xf1, 0x5c, 0x22, 0x65, 0x43, 0xc7, 0x99, 0x18, 0x3d, 0x83, 0x5a, 0xa2, + 0x35, 0xf0, 0x05, 0x37, 0x6e, 0xcf, 0x33, 0x51, 0x4b, 0x75, 0x7d, 0x21, 0xdf, 0x49, 0xd5, 0x71, + 0x1a, 0xa2, 0xaf, 0x60, 0xd9, 0xa1, 0xc3, 0xa1, 0x2f, 0x8c, 0x3b, 0x4a, 0xe1, 0xf6, 0xd5, 0x0a, + 0x7b, 0x0a, 0xd3, 0x2b, 0x98, 0x1a, 0x2d, 0xcd, 0x97, 0x46, 0x68, 0x6e, 0x63, 0x9e, 0xf9, 0x3d, + 0x9b, 0x27, 0xf4, 0x8a, 0x17, 0x07, 0x5b, 0x0f, 0xa1, 0x9e, 0xfb, 0xa0, 0x90, 0xff, 0x37, 0xce, + 0xf0, 0xb9, 0xfe, 0x5e, 0x91, 0xcb, 0xf4, 0x1b, 0xa6, 0x94, 0xf9, 0x86, 0x79, 0x50, 0xba, 0x5f, + 0xec, 0x2e, 0xc1, 0x02, 0x1f, 0x0d, 0xbb, 0x87, 0x6f, 0x2f, 0x1b, 0xc5, 0x77, 0x97, 0x8d, 0xe2, + 0x9f, 0x97, 0x8d, 0xe2, 0x9b, 0xf7, 0x8d, 0xc2, 0xbb, 0xf7, 0x8d, 0xc2, 0xef, 0xef, 0x1b, 0x85, + 0x1f, 0x3b, 0xa7, 0xbe, 0xf0, 0x46, 0x83, 0x96, 0x43, 0x87, 0x6d, 0xd7, 0xe6, 0x1e, 0xb3, 0xcf, + 0xdb, 0x51, 0x7d, 0x32, 0x8a, 0xfe, 0x38, 0xb5, 0xa7, 0xff, 0x5f, 0x0d, 0x96, 0x55, 0xf6, 0xde, + 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x4c, 0xe1, 0x61, 0x1a, 0x61, 0x0e, 0x00, 0x00, } func (m *ProtocolVersion) Marshal() (dAtA []byte, err error) { @@ -1075,9 +1075,20 @@ func (m *NodeInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x3a } if len(m.Channels) > 0 { - i -= len(m.Channels) - copy(dAtA[i:], m.Channels) - i = encodeVarintTypes(dAtA, i, uint64(len(m.Channels))) + dAtA3 := make([]byte, len(m.Channels)*10) + var j2 int + for _, num := range m.Channels { + for num >= 1<<7 { + dAtA3[j2] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j2++ + } + dAtA3[j2] = uint8(num) + j2++ + } + i -= j2 + copy(dAtA[i:], dAtA3[:j2]) + i = encodeVarintTypes(dAtA, i, uint64(j2)) i-- dAtA[i] = 0x32 } @@ -1197,12 +1208,12 @@ func (m *PeerInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x20 } if m.LastConnected != nil { - n3, err3 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastConnected, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastConnected):]) - if err3 != nil { - return 0, err3 + n5, err5 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastConnected, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastConnected):]) + if err5 != nil { + return 0, err5 } - i -= n3 - i = encodeVarintTypes(dAtA, i, uint64(n3)) + i -= n5 + i = encodeVarintTypes(dAtA, i, uint64(n5)) i-- dAtA[i] = 0x1a } @@ -1256,22 +1267,22 @@ func (m *PeerAddressInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x20 } if m.LastDialFailure != nil { - n4, err4 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastDialFailure, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastDialFailure):]) - if err4 != nil { - return 0, err4 + n6, err6 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastDialFailure, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastDialFailure):]) + if err6 != nil { + return 0, err6 } - i -= n4 - i = encodeVarintTypes(dAtA, i, uint64(n4)) + i -= n6 + i = encodeVarintTypes(dAtA, i, uint64(n6)) i-- dAtA[i] = 0x1a } if m.LastDialSuccess != nil { - n5, err5 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastDialSuccess, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastDialSuccess):]) - if err5 != nil { - return 0, err5 + n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastDialSuccess, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastDialSuccess):]) + if err7 != nil { + return 0, err7 } - i -= n5 - i = encodeVarintTypes(dAtA, i, uint64(n5)) + i -= n7 + i = encodeVarintTypes(dAtA, i, uint64(n7)) i-- dAtA[i] = 0x12 } @@ -2058,9 +2069,12 @@ func (m *NodeInfo) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } - l = len(m.Channels) - if l > 0 { - n += 1 + l + sovTypes(uint64(l)) + if len(m.Channels) > 0 { + l = 0 + for _, e := range m.Channels { + l += sovTypes(uint64(e)) + } + n += 1 + sovTypes(uint64(l)) + l } l = len(m.Moniker) if l > 0 { @@ -2832,39 +2846,81 @@ func (m *NodeInfo) Unmarshal(dAtA []byte) error { m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Channels", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes + if wireType == 0 { + var v uint32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } } - if iNdEx >= l { + m.Channels = append(m.Channels, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { return io.ErrUnexpectedEOF } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } } + elementCount = count + if elementCount != 0 && len(m.Channels) == 0 { + m.Channels = make([]uint32, 0, elementCount) + } + for iNdEx < postIndex { + var v uint32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Channels = append(m.Channels, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field Channels", wireType) } - if byteLen < 0 { - return ErrInvalidLengthTypes - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthTypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Channels = append(m.Channels[:0], dAtA[iNdEx:postIndex]...) - if m.Channels == nil { - m.Channels = []byte{} - } - iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Moniker", wireType) diff --git a/proto/tendermint/p2p/types.proto b/proto/tendermint/p2p/types.proto index 9baa663eca..2b37dcdde8 100644 --- a/proto/tendermint/p2p/types.proto +++ b/proto/tendermint/p2p/types.proto @@ -24,7 +24,7 @@ message NodeInfo { string listen_addr = 3; string network = 4; string version = 5; - bytes channels = 6; + repeated uint32 channels = 6; string moniker = 7; NodeInfoOther other = 8 [(gogoproto.nullable) = false]; bytes pro_tx_hash = 9; diff --git a/types/node_info.go b/types/node_info.go index eb3720f890..85020a52fa 100644 --- a/types/node_info.go +++ b/types/node_info.go @@ -9,7 +9,7 @@ import ( "github.com/dashpay/tenderdash/crypto" tmstrings "github.com/dashpay/tenderdash/internal/libs/strings" - tmbytes "github.com/dashpay/tenderdash/libs/bytes" + tmsync "github.com/dashpay/tenderdash/internal/libs/sync" tmp2p "github.com/dashpay/tenderdash/proto/tendermint/p2p" ) @@ -48,8 +48,8 @@ type NodeInfo struct { // Channels are HexBytes so easier to read as JSON Network string `json:"network"` // network/chain ID Version string `json:"version"` // major.minor.revision - // FIXME: This should be changed to uint16 to be consistent with the updated channel type - Channels tmbytes.HexBytes `json:"channels"` // channels this node knows about + // Channels supported by this node. Use GetChannels() as a getter. + Channels tmsync.Slice[uint16] `json:"channels"` // channels this node knows about // ASCIIText fields Moniker string `json:"moniker"` // arbitrary moniker @@ -97,11 +97,11 @@ func (info NodeInfo) Validate() error { } // Validate Channels - ensure max and check for duplicates. - if len(info.Channels) > maxNumChannels { - return fmt.Errorf("info.Channels is too long (%v). Max is %v", len(info.Channels), maxNumChannels) + if info.Channels.Len() > maxNumChannels { + return fmt.Errorf("info.Channels is too long (%v). Max is %v", info.Channels.Len(), maxNumChannels) } - channels := make(map[byte]struct{}) - for _, ch := range info.Channels { + channels := make(map[uint16]struct{}) + for _, ch := range info.Channels.ToSlice() { _, ok := channels[ch] if ok { return fmt.Errorf("info.Channels contains duplicate channel id %v", ch) @@ -147,15 +147,15 @@ func (info NodeInfo) CompatibleWith(other NodeInfo) error { } // if we have no channels, we're just testing - if len(info.Channels) == 0 { + if info.Channels.Len() == 0 { return nil } // for each of our channels, check if they have it found := false OUTER_LOOP: - for _, ch1 := range info.Channels { - for _, ch2 := range other.Channels { + for _, ch1 := range info.Channels.ToSlice() { + for _, ch2 := range other.Channels.ToSlice() { if ch1 == ch2 { found = true break OUTER_LOOP // only need one @@ -171,13 +171,13 @@ OUTER_LOOP: // AddChannel is used by the router when a channel is opened to add it to the node info func (info *NodeInfo) AddChannel(channel uint16) { // check that the channel doesn't already exist - for _, ch := range info.Channels { - if ch == byte(channel) { + for _, ch := range info.Channels.ToSlice() { + if ch == channel { return } } - info.Channels = append(info.Channels, byte(channel)) + info.Channels.Append(channel) } func (info NodeInfo) Copy() NodeInfo { @@ -187,7 +187,7 @@ func (info NodeInfo) Copy() NodeInfo { ListenAddr: info.ListenAddr, Network: info.Network, Version: info.Version, - Channels: info.Channels, + Channels: info.Channels.Copy(), Moniker: info.Moniker, Other: info.Other, ProTxHash: info.ProTxHash.Copy(), @@ -203,11 +203,14 @@ func (info NodeInfo) ToProto() *tmp2p.NodeInfo { App: info.ProtocolVersion.App, } + for _, ch := range info.Channels.ToSlice() { + dni.Channels = append(dni.Channels, uint32(ch)) + } + dni.NodeID = string(info.NodeID) dni.ListenAddr = info.ListenAddr dni.Network = info.Network dni.Version = info.Version - dni.Channels = info.Channels dni.Moniker = info.Moniker dni.ProTxHash = info.ProTxHash.Copy() dni.Other = tmp2p.NodeInfoOther{ @@ -232,7 +235,6 @@ func NodeInfoFromProto(pb *tmp2p.NodeInfo) (NodeInfo, error) { ListenAddr: pb.ListenAddr, Network: pb.Network, Version: pb.Version, - Channels: pb.Channels, Moniker: pb.Moniker, Other: NodeInfoOther{ TxIndex: pb.Other.TxIndex, @@ -240,6 +242,11 @@ func NodeInfoFromProto(pb *tmp2p.NodeInfo) (NodeInfo, error) { }, ProTxHash: pb.ProTxHash, } + + for _, ch := range pb.Channels { + dni.Channels.Append(uint16(ch)) + } + return dni, nil } diff --git a/types/node_info_test.go b/types/node_info_test.go index b600df5820..427c964a53 100644 --- a/types/node_info_test.go +++ b/types/node_info_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/dashpay/tenderdash/crypto/ed25519" + tmsync "github.com/dashpay/tenderdash/internal/libs/sync" tmnet "github.com/dashpay/tenderdash/libs/net" "github.com/dashpay/tenderdash/version" ) @@ -20,13 +21,13 @@ func TestNodeInfoValidate(t *testing.T) { ni := NodeInfo{} assert.Error(t, ni.Validate()) - channels := make([]byte, maxNumChannels) - for i := 0; i < maxNumChannels; i++ { - channels[i] = byte(i) + channels := tmsync.NewConcurrentSlice[uint16]() + for i := uint16(0); i < maxNumChannels; i++ { + channels.Append(i) } - dupChannels := make([]byte, 5) - copy(dupChannels, channels[:5]) - dupChannels = append(dupChannels, testCh) + + dupChannels := tmsync.NewConcurrentSlice[uint16](channels.ToSlice()[:5]...) + dupChannels.Append(testCh) nonASCII := "¢§µ" emptyTab := "\t" @@ -39,11 +40,14 @@ func TestNodeInfoValidate(t *testing.T) { }{ { "Too Many Channels", - func(ni *NodeInfo) { ni.Channels = append(channels, byte(maxNumChannels)) }, + func(ni *NodeInfo) { + ni.Channels = channels.Copy() + ni.Channels.Append(maxNumChannels) + }, true, }, {"Duplicate Channel", func(ni *NodeInfo) { ni.Channels = dupChannels }, true}, - {"Good Channels", func(ni *NodeInfo) { ni.Channels = ni.Channels[:5] }, false}, + {"Good Channels", func(ni *NodeInfo) { ni.Channels = tmsync.NewConcurrentSlice(ni.Channels.ToSlice()[:5]...) }, false}, {"Invalid NetAddress", func(ni *NodeInfo) { ni.ListenAddr = "not-an-address" }, true}, {"Good NetAddress", func(ni *NodeInfo) { ni.ListenAddr = "0.0.0.0:26656" }, false}, @@ -117,7 +121,7 @@ func testNodeInfoWithNetwork(t *testing.T, id NodeID, name, network string) Node ListenAddr: fmt.Sprintf("127.0.0.1:%d", getFreePort(t)), Network: network, Version: "1.2.3-rc0-deadbeef", - Channels: []byte{testCh}, + Channels: tmsync.NewConcurrentSlice[uint16](testCh), Moniker: name, Other: NodeInfoOther{ TxIndex: "on", @@ -146,7 +150,7 @@ func TestNodeInfoCompatible(t *testing.T) { assert.NoError(t, ni1.CompatibleWith(ni2)) // add another channel; still compatible - ni2.Channels = []byte{newTestChannel, testCh} + ni2.Channels = tmsync.NewConcurrentSlice[uint16](testCh) assert.NoError(t, ni1.CompatibleWith(ni2)) testCases := []struct { @@ -155,7 +159,7 @@ func TestNodeInfoCompatible(t *testing.T) { }{ {"Wrong block version", func(ni *NodeInfo) { ni.ProtocolVersion.Block++ }}, {"Wrong network", func(ni *NodeInfo) { ni.Network += "-wrong" }}, - {"No common channels", func(ni *NodeInfo) { ni.Channels = []byte{newTestChannel} }}, + {"No common channels", func(ni *NodeInfo) { ni.Channels = tmsync.NewConcurrentSlice[uint16](uint16(newTestChannel)) }}, } for _, tc := range testCases { @@ -167,15 +171,15 @@ func TestNodeInfoCompatible(t *testing.T) { func TestNodeInfoAddChannel(t *testing.T) { nodeInfo := testNodeInfo(t, testNodeID(), "testing") - nodeInfo.Channels = []byte{} + nodeInfo.Channels = tmsync.NewConcurrentSlice[uint16]() require.Empty(t, nodeInfo.Channels) nodeInfo.AddChannel(2) - require.Contains(t, nodeInfo.Channels, byte(0x02)) + require.Contains(t, nodeInfo.Channels.ToSlice(), uint16(2)) // adding the same channel again shouldn't be a problem nodeInfo.AddChannel(2) - require.Contains(t, nodeInfo.Channels, byte(0x02)) + require.Contains(t, nodeInfo.Channels.ToSlice(), uint16(2)) } func TestParseAddressString(t *testing.T) { diff --git a/version/version.go b/version/version.go index 9b66f99f6c..900ecf4957 100644 --- a/version/version.go +++ b/version/version.go @@ -19,7 +19,7 @@ const ( var ( // P2PProtocol versions all p2p behavior and msgs. // This includes proposer selection. - P2PProtocol uint64 = 8 + P2PProtocol uint64 = 9 // BlockProtocol versions all block data structures and processing. // This includes validity of blocks and state updates. From 64038d38e7f0473e7667385ae63bfdf67ef45f24 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 7 Feb 2024 16:30:20 +0100 Subject: [PATCH 2/4] chore: fix missing nodeInfo.Channels initialization --- internal/libs/sync/concurrent_slice_test.go | 6 +++--- internal/p2p/p2ptest/network.go | 2 ++ internal/p2p/router_test.go | 2 ++ internal/p2p/transport_test.go | 9 ++++++--- types/node_info.go | 5 +++++ 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/internal/libs/sync/concurrent_slice_test.go b/internal/libs/sync/concurrent_slice_test.go index 360510724e..f24afa1fb8 100644 --- a/internal/libs/sync/concurrent_slice_test.go +++ b/internal/libs/sync/concurrent_slice_test.go @@ -22,12 +22,12 @@ func TestConcurrentSlice(t *testing.T) { } // Test Set - s.Set(3, 5) + s.Set(1, 5) // Test ToSlice slice := s.ToSlice() - if len(slice) != 4 || slice[3] != 4 { - t.Errorf("Expected ToSlice to return [1 2 3 4], got %v", slice) + if len(slice) != 4 || slice[3] != 4 || slice[1] != 5 { + t.Errorf("Expected ToSlice to return [1 5 3 4], got %v", slice) } // Test Reset diff --git a/internal/p2p/p2ptest/network.go b/internal/p2p/p2ptest/network.go index faf4ed83c6..49d0d74722 100644 --- a/internal/p2p/p2ptest/network.go +++ b/internal/p2p/p2ptest/network.go @@ -12,6 +12,7 @@ import ( "github.com/dashpay/tenderdash/config" "github.com/dashpay/tenderdash/crypto" "github.com/dashpay/tenderdash/crypto/ed25519" + tmsync "github.com/dashpay/tenderdash/internal/libs/sync" "github.com/dashpay/tenderdash/internal/p2p" p2pclient "github.com/dashpay/tenderdash/internal/p2p/client" "github.com/dashpay/tenderdash/libs/log" @@ -272,6 +273,7 @@ func (n *Network) MakeNode(ctx context.Context, t *testing.T, proTxHash crypto.P ListenAddr: "0.0.0.0:0", // FIXME: We have to fake this for now. Moniker: string(nodeID), ProTxHash: proTxHash.Copy(), + Channels: tmsync.NewConcurrentSlice[uint16](), } transport := n.memoryNetwork.CreateTransport(nodeID) diff --git a/internal/p2p/router_test.go b/internal/p2p/router_test.go index c486dab845..05fdf33aba 100644 --- a/internal/p2p/router_test.go +++ b/internal/p2p/router_test.go @@ -304,6 +304,7 @@ func TestRouter_AcceptPeers(t *testing.T) { ListenAddr: "0.0.0.0:0", Network: "other-network", Moniker: string(peerID), + Channels: tmsync.NewConcurrentSlice[uint16](), }, peerKey.PubKey(), false, @@ -505,6 +506,7 @@ func TestRouter_DialPeers(t *testing.T) { ListenAddr: "0.0.0.0:0", Network: "other-network", Moniker: string(peerID), + Channels: tmsync.NewConcurrentSlice[uint16](), }, peerKey.PubKey(), nil, diff --git a/internal/p2p/transport_test.go b/internal/p2p/transport_test.go index 8172a2c9aa..1d87c5179c 100644 --- a/internal/p2p/transport_test.go +++ b/internal/p2p/transport_test.go @@ -291,7 +291,10 @@ func TestConnection_Handshake(t *testing.T) { }, } bKey := ed25519.GenPrivKey() - bInfo := types.NodeInfo{NodeID: types.NodeIDFromPubKey(bKey.PubKey())} + bInfo := types.NodeInfo{ + NodeID: types.NodeIDFromPubKey(bKey.PubKey()), + Channels: tmsync.NewConcurrentSlice[uint16](), + } errCh := make(chan error, 1) go func() { @@ -641,13 +644,13 @@ func dialAcceptHandshake(ctx context.Context, t *testing.T, a, b p2p.Transport) errCh := make(chan error, 1) go func() { privKey := ed25519.GenPrivKey() - nodeInfo := types.NodeInfo{NodeID: types.NodeIDFromPubKey(privKey.PubKey())} + nodeInfo := types.NodeInfo{NodeID: types.NodeIDFromPubKey(privKey.PubKey()), Channels: tmsync.NewConcurrentSlice[uint16]()} _, _, err := ba.Handshake(ctx, 0, nodeInfo, privKey) errCh <- err }() privKey := ed25519.GenPrivKey() - nodeInfo := types.NodeInfo{NodeID: types.NodeIDFromPubKey(privKey.PubKey())} + nodeInfo := types.NodeInfo{NodeID: types.NodeIDFromPubKey(privKey.PubKey()), Channels: tmsync.NewConcurrentSlice[uint16]()} _, _, err := ab.Handshake(ctx, 0, nodeInfo, privKey) require.NoError(t, err) diff --git a/types/node_info.go b/types/node_info.go index 85020a52fa..d217258f4f 100644 --- a/types/node_info.go +++ b/types/node_info.go @@ -97,6 +97,10 @@ func (info NodeInfo) Validate() error { } // Validate Channels - ensure max and check for duplicates. + if info.Channels == nil { + return fmt.Errorf("info.Channels is nil") + } + if info.Channels.Len() > maxNumChannels { return fmt.Errorf("info.Channels is too long (%v). Max is %v", info.Channels.Len(), maxNumChannels) } @@ -235,6 +239,7 @@ func NodeInfoFromProto(pb *tmp2p.NodeInfo) (NodeInfo, error) { ListenAddr: pb.ListenAddr, Network: pb.Network, Version: pb.Version, + Channels: tmsync.NewConcurrentSlice[uint16](), Moniker: pb.Moniker, Other: NodeInfoOther{ TxIndex: pb.Other.TxIndex, From b7e5ea04074083a9befe9229d9c970b19fe8f9c6 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:31:33 +0100 Subject: [PATCH 3/4] fix(sync): concurrent slice marshal/unmarshal json --- internal/libs/sync/concurrent_slice.go | 28 ++++++++++----------- internal/libs/sync/concurrent_slice_test.go | 17 +++++++++++++ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/internal/libs/sync/concurrent_slice.go b/internal/libs/sync/concurrent_slice.go index ba5289b981..a0f0fb65d7 100644 --- a/internal/libs/sync/concurrent_slice.go +++ b/internal/libs/sync/concurrent_slice.go @@ -3,8 +3,8 @@ package sync import "sync" type concurrentSlice[T any] struct { - mtx sync.RWMutex - slice []T + mtx sync.RWMutex `json:"-"` + Items []T `json:"items"` } // Slice is a thread-safe slice interface @@ -23,7 +23,7 @@ type Slice[T any] interface { // It can be referenced by value, and will behave similarly to a regular slice (which is a reference type). func NewConcurrentSlice[T any](initial ...T) Slice[T] { return &concurrentSlice[T]{ - slice: initial, + Items: initial, } } @@ -32,7 +32,7 @@ func (s *concurrentSlice[T]) Append(val ...T) { s.mtx.Lock() defer s.mtx.Unlock() - s.slice = append(s.slice, val...) + s.Items = append(s.Items, val...) } // Reset removes all elements from the slice @@ -40,7 +40,7 @@ func (s *concurrentSlice[T]) Reset() { s.mtx.Lock() defer s.mtx.Unlock() - s.slice = []T{} + s.Items = []T{} } // Get returns the value at the given index @@ -48,21 +48,21 @@ func (s *concurrentSlice[T]) Get(index int) T { s.mtx.RLock() defer s.mtx.RUnlock() - return s.slice[index] + return s.Items[index] } func (s *concurrentSlice[T]) Set(index int, val T) { s.mtx.Lock() defer s.mtx.Unlock() - if index > len(s.slice) { + if index > len(s.Items) { panic("index out of range") - } else if index == len(s.slice) { - s.slice = append(s.slice, val) + } else if index == len(s.Items) { + s.Items = append(s.Items, val) return } - s.slice[index] = val + s.Items[index] = val } // ToSlice returns a copy of the underlying slice @@ -70,8 +70,8 @@ func (s *concurrentSlice[T]) ToSlice() []T { s.mtx.RLock() defer s.mtx.RUnlock() - slice := make([]T, len(s.slice)) - copy(slice, s.slice) + slice := make([]T, len(s.Items)) + copy(slice, s.Items) return slice } @@ -80,7 +80,7 @@ func (s *concurrentSlice[T]) Len() int { s.mtx.RLock() defer s.mtx.RUnlock() - return len(s.slice) + return len(s.Items) } // Copy returns a new deep copy of concurrentSlice with the same elements @@ -89,6 +89,6 @@ func (s *concurrentSlice[T]) Copy() Slice[T] { defer s.mtx.RUnlock() return &concurrentSlice[T]{ - slice: s.ToSlice(), + Items: s.ToSlice(), } } diff --git a/internal/libs/sync/concurrent_slice_test.go b/internal/libs/sync/concurrent_slice_test.go index f24afa1fb8..9bdcd31f40 100644 --- a/internal/libs/sync/concurrent_slice_test.go +++ b/internal/libs/sync/concurrent_slice_test.go @@ -1,6 +1,7 @@ package sync import ( + "encoding/json" "sync" "testing" @@ -68,3 +69,19 @@ func TestConcurrentSlice_Concurrency(t *testing.T) { assert.Contains(t, s.ToSlice(), i) } } + +func TestConcurrentSlice_MarshalUnmarshalJSON(t *testing.T) { + // Create a concurrentSlice + cs := NewConcurrentSlice[uint16](1, 2, 3) + + // Marshal to JSON + data, err := json.Marshal(cs) + assert.NoError(t, err, "Failed to marshal concurrentSlice") + + // Unmarshal from JSON + var cs2 concurrentSlice[uint16] + err = json.Unmarshal(data, &cs2) + assert.NoError(t, err, "Failed to unmarshal concurrentSlice") + + assert.EqualValues(t, cs.ToSlice(), cs2.ToSlice()) +} From 5b45bf91bdc92606c229983bbe71c67d59fcbea3 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:43:22 +0100 Subject: [PATCH 4/4] fix: json marshal nodeInfo channels fails --- internal/libs/sync/concurrent_slice.go | 100 ++++++++++++-------- internal/libs/sync/concurrent_slice_test.go | 19 +++- types/node_info.go | 5 +- types/node_info_test.go | 6 +- 4 files changed, 83 insertions(+), 47 deletions(-) diff --git a/internal/libs/sync/concurrent_slice.go b/internal/libs/sync/concurrent_slice.go index a0f0fb65d7..48d674eb78 100644 --- a/internal/libs/sync/concurrent_slice.go +++ b/internal/libs/sync/concurrent_slice.go @@ -1,94 +1,116 @@ package sync -import "sync" +import ( + "encoding/json" + "sync" +) -type concurrentSlice[T any] struct { - mtx sync.RWMutex `json:"-"` - Items []T `json:"items"` -} - -// Slice is a thread-safe slice interface -type Slice[T any] interface { - Append(val ...T) - Reset() - Get(index int) T - Set(index int, val T) - ToSlice() []T - Len() int - Copy() Slice[T] +// ConcurrentSlice is a thread-safe slice. +// +// It is safe to use from multiple goroutines without additional locking. +// It should be referenced by pointer. +// +// Initialize using NewConcurrentSlice(). +type ConcurrentSlice[T any] struct { + mtx sync.RWMutex + items []T } // NewConcurrentSlice creates a new thread-safe slice. -// It is safe to use from multiple goroutines without additional locking. -// It can be referenced by value, and will behave similarly to a regular slice (which is a reference type). -func NewConcurrentSlice[T any](initial ...T) Slice[T] { - return &concurrentSlice[T]{ - Items: initial, +func NewConcurrentSlice[T any](initial ...T) *ConcurrentSlice[T] { + return &ConcurrentSlice[T]{ + items: initial, } } // Append adds an element to the slice -func (s *concurrentSlice[T]) Append(val ...T) { +func (s *ConcurrentSlice[T]) Append(val ...T) { s.mtx.Lock() defer s.mtx.Unlock() - s.Items = append(s.Items, val...) + s.items = append(s.items, val...) } // Reset removes all elements from the slice -func (s *concurrentSlice[T]) Reset() { +func (s *ConcurrentSlice[T]) Reset() { s.mtx.Lock() defer s.mtx.Unlock() - s.Items = []T{} + s.items = []T{} } // Get returns the value at the given index -func (s *concurrentSlice[T]) Get(index int) T { +func (s *ConcurrentSlice[T]) Get(index int) T { s.mtx.RLock() defer s.mtx.RUnlock() - return s.Items[index] + return s.items[index] } -func (s *concurrentSlice[T]) Set(index int, val T) { +// Set updates the value at the given index. +// If the index is greater than the length of the slice, it panics. +// If the index is equal to the length of the slice, the value is appended. +// Otherwise, the value at the index is updated. +func (s *ConcurrentSlice[T]) Set(index int, val T) { s.mtx.Lock() defer s.mtx.Unlock() - if index > len(s.Items) { + if index > len(s.items) { panic("index out of range") - } else if index == len(s.Items) { - s.Items = append(s.Items, val) + } else if index == len(s.items) { + s.items = append(s.items, val) return } - s.Items[index] = val + s.items[index] = val } // ToSlice returns a copy of the underlying slice -func (s *concurrentSlice[T]) ToSlice() []T { +func (s *ConcurrentSlice[T]) ToSlice() []T { s.mtx.RLock() defer s.mtx.RUnlock() - slice := make([]T, len(s.Items)) - copy(slice, s.Items) + slice := make([]T, len(s.items)) + copy(slice, s.items) return slice } // Len returns the length of the slice -func (s *concurrentSlice[T]) Len() int { +func (s *ConcurrentSlice[T]) Len() int { s.mtx.RLock() defer s.mtx.RUnlock() - return len(s.Items) + return len(s.items) } // Copy returns a new deep copy of concurrentSlice with the same elements -func (s *concurrentSlice[T]) Copy() Slice[T] { +func (s *ConcurrentSlice[T]) Copy() ConcurrentSlice[T] { s.mtx.RLock() defer s.mtx.RUnlock() - return &concurrentSlice[T]{ - Items: s.ToSlice(), + return ConcurrentSlice[T]{ + items: s.ToSlice(), + } +} + +// MarshalJSON implements the json.Marshaler interface. +func (cs *ConcurrentSlice[T]) MarshalJSON() ([]byte, error) { + cs.mtx.RLock() + defer cs.mtx.RUnlock() + + return json.Marshal(cs.items) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (cs *ConcurrentSlice[T]) UnmarshalJSON(data []byte) error { + var items []T + if err := json.Unmarshal(data, &items); err != nil { + return err } + + cs.mtx.Lock() + defer cs.mtx.Unlock() + + cs.items = items + return nil } diff --git a/internal/libs/sync/concurrent_slice_test.go b/internal/libs/sync/concurrent_slice_test.go index 9bdcd31f40..122f3a1a28 100644 --- a/internal/libs/sync/concurrent_slice_test.go +++ b/internal/libs/sync/concurrent_slice_test.go @@ -71,17 +71,26 @@ func TestConcurrentSlice_Concurrency(t *testing.T) { } func TestConcurrentSlice_MarshalUnmarshalJSON(t *testing.T) { - // Create a concurrentSlice + type node struct { + Channels *ConcurrentSlice[uint16] + } cs := NewConcurrentSlice[uint16](1, 2, 3) + node1 := node{ + Channels: cs, + } + // Marshal to JSON - data, err := json.Marshal(cs) + data, err := json.Marshal(node1) assert.NoError(t, err, "Failed to marshal concurrentSlice") // Unmarshal from JSON - var cs2 concurrentSlice[uint16] - err = json.Unmarshal(data, &cs2) + node2 := node{ + // Channels: NewConcurrentSlice[uint16](), + } + + err = json.Unmarshal(data, &node2) assert.NoError(t, err, "Failed to unmarshal concurrentSlice") - assert.EqualValues(t, cs.ToSlice(), cs2.ToSlice()) + assert.EqualValues(t, node1.Channels.ToSlice(), node2.Channels.ToSlice()) } diff --git a/types/node_info.go b/types/node_info.go index d217258f4f..ffb14e2157 100644 --- a/types/node_info.go +++ b/types/node_info.go @@ -49,7 +49,7 @@ type NodeInfo struct { Network string `json:"network"` // network/chain ID Version string `json:"version"` // major.minor.revision // Channels supported by this node. Use GetChannels() as a getter. - Channels tmsync.Slice[uint16] `json:"channels"` // channels this node knows about + Channels *tmsync.ConcurrentSlice[uint16] `json:"channels"` // channels this node knows about // ASCIIText fields Moniker string `json:"moniker"` // arbitrary moniker @@ -185,13 +185,14 @@ func (info *NodeInfo) AddChannel(channel uint16) { } func (info NodeInfo) Copy() NodeInfo { + chans := info.Channels.Copy() return NodeInfo{ ProtocolVersion: info.ProtocolVersion, NodeID: info.NodeID, ListenAddr: info.ListenAddr, Network: info.Network, Version: info.Version, - Channels: info.Channels.Copy(), + Channels: &chans, Moniker: info.Moniker, Other: info.Other, ProTxHash: info.ProTxHash.Copy(), diff --git a/types/node_info_test.go b/types/node_info_test.go index 427c964a53..d16b1e9850 100644 --- a/types/node_info_test.go +++ b/types/node_info_test.go @@ -41,7 +41,7 @@ func TestNodeInfoValidate(t *testing.T) { { "Too Many Channels", func(ni *NodeInfo) { - ni.Channels = channels.Copy() + ni.Channels = ref(channels.Copy()) ni.Channels.Append(maxNumChannels) }, true, @@ -101,6 +101,10 @@ func TestNodeInfoValidate(t *testing.T) { } +func ref[T any](t T) *T { + return &t +} + func testNodeID() NodeID { return NodeIDFromPubKey(ed25519.GenPrivKey().PubKey()) }