diff --git a/sync/bundle/message/hello.go b/sync/bundle/message/hello.go index 204447f85..40079c7d4 100644 --- a/sync/bundle/message/hello.go +++ b/sync/bundle/message/hello.go @@ -2,6 +2,7 @@ package message import ( "fmt" + "time" "github.com/libp2p/go-libp2p/core/peer" "github.com/pactus-project/pactus/crypto/bls" @@ -12,28 +13,30 @@ import ( ) type HelloMessage struct { - PeerID peer.ID `cbor:"1,keyasint"` - Agent string `cbor:"2,keyasint"` - Moniker string `cbor:"3,keyasint"` - PublicKeys []*bls.PublicKey `cbor:"4,keyasint"` - Signature *bls.Signature `cbor:"5,keyasint"` - Height uint32 `cbor:"6,keyasint"` - Services services.Services `cbor:"7,keyasint"` - GenesisHash hash.Hash `cbor:"8,keyasint"` - BlockHash hash.Hash `cbor:"9,keyasint"` + PeerID peer.ID `cbor:"1,keyasint"` + Agent string `cbor:"2,keyasint"` + Moniker string `cbor:"3,keyasint"` + PublicKeys []*bls.PublicKey `cbor:"4,keyasint"` + Signature *bls.Signature `cbor:"5,keyasint"` + Height uint32 `cbor:"6,keyasint"` + Services services.Services `cbor:"7,keyasint"` + GenesisHash hash.Hash `cbor:"8,keyasint"` + BlockHash hash.Hash `cbor:"9,keyasint"` + MyTimeUnixMilli int64 `cbor:"10,keyasint"` } func NewHelloMessage(pid peer.ID, moniker string, height uint32, services services.Services, blockHash, genesisHash hash.Hash, ) *HelloMessage { return &HelloMessage{ - PeerID: pid, - Agent: version.Agent(), - Moniker: moniker, - GenesisHash: genesisHash, - BlockHash: blockHash, - Height: height, - Services: services, + PeerID: pid, + Agent: version.Agent(), + Moniker: moniker, + GenesisHash: genesisHash, + BlockHash: blockHash, + Height: height, + Services: services, + MyTimeUnixMilli: time.Now().UnixMilli(), } } @@ -48,6 +51,10 @@ func (m *HelloMessage) BasicCheck() error { return aggPublicKey.Verify(m.SignBytes(), m.Signature) } +func (m *HelloMessage) MyTime() time.Time { + return time.UnixMilli(m.MyTimeUnixMilli) +} + func (m *HelloMessage) SignBytes() []byte { return []byte(fmt.Sprintf("%s:%s:%s:%s", m.Type(), m.Agent, m.PeerID, m.GenesisHash.String())) } diff --git a/sync/bundle/message/hello_test.go b/sync/bundle/message/hello_test.go index d37dec7fc..f0e149430 100644 --- a/sync/bundle/message/hello_test.go +++ b/sync/bundle/message/hello_test.go @@ -2,6 +2,7 @@ package message import ( "testing" + "time" "github.com/pactus-project/pactus/crypto" "github.com/pactus-project/pactus/crypto/bls" @@ -45,6 +46,16 @@ func TestHelloMessage(t *testing.T) { assert.Equal(t, errors.Code(m.BasicCheck()), errors.ErrInvalidPublicKey) }) + t.Run("MyTimeUnixMilli of time1 is less or equal than hello message time", func(t *testing.T) { + time1 := time.Now() + myTimeUnixMilli := time1.UnixMilli() + + m := NewHelloMessage(ts.RandPeerID(), "Alice", 100, 0, ts.RandHash(), ts.RandHash()) + + assert.LessOrEqual(t, m.MyTimeUnixMilli, time.Now().UnixMilli()) + assert.GreaterOrEqual(t, m.MyTimeUnixMilli, myTimeUnixMilli) + }) + t.Run("Ok", func(t *testing.T) { valKey := ts.RandValKey() m := NewHelloMessage(ts.RandPeerID(), "Alice", 100, 0, ts.RandHash(), ts.RandHash()) diff --git a/sync/handler_hello.go b/sync/handler_hello.go index 50ef4f05e..e9858cf0f 100644 --- a/sync/handler_hello.go +++ b/sync/handler_hello.go @@ -2,6 +2,8 @@ package sync import ( "fmt" + "math" + "time" "github.com/libp2p/go-libp2p/core/peer" "github.com/pactus-project/pactus/sync/bundle" @@ -38,6 +40,13 @@ func (handler *helloHandler) ParseMessage(m message.Message, initiator peer.ID) return handler.acknowledge(response, initiator) } + if math.Abs(time.Since(msg.MyTime()).Seconds()) > 10 { + response := message.NewHelloAckMessage(message.ResponseCodeRejected, + "time discrepancy exceeds 10 seconds") + + return handler.acknowledge(response, initiator) + } + handler.logger.Debug("updating peer info", "pid", initiator.ShortString(), "moniker", msg.Moniker, diff --git a/sync/handler_hello_test.go b/sync/handler_hello_test.go index 76ca91750..5fcc8cba2 100644 --- a/sync/handler_hello_test.go +++ b/sync/handler_hello_test.go @@ -2,6 +2,7 @@ package sync import ( "testing" + "time" "github.com/pactus-project/pactus/crypto/bls" "github.com/pactus-project/pactus/sync/bundle" @@ -29,8 +30,8 @@ func TestParsingHelloMessages(t *testing.T) { assert.NoError(t, td.receivingNewMessage(td.sync, msg, initiator)) assert.Equal(t, td.sync.peerSet.GetPeer(initiator).Status, peerset.StatusCodeBanned) - bundle := td.shouldPublishMessageWithThisType(t, td.network, message.TypeHelloAck) - assert.Equal(t, bundle.Message.(*message.HelloAckMessage).ResponseCode, message.ResponseCodeRejected) + bdl := td.shouldPublishMessageWithThisType(t, td.network, message.TypeHelloAck) + assert.Equal(t, bdl.Message.(*message.HelloAckMessage).ResponseCode, message.ResponseCodeRejected) }) t.Run("Receiving Hello message from a peer. Genesis hash is wrong.", @@ -44,8 +45,40 @@ func TestParsingHelloMessages(t *testing.T) { assert.NoError(t, td.receivingNewMessage(td.sync, msg, pid)) td.checkPeerStatus(t, pid, peerset.StatusCodeBanned) - bundle := td.shouldPublishMessageWithThisType(t, td.network, message.TypeHelloAck) - assert.Equal(t, bundle.Message.(*message.HelloAckMessage).ResponseCode, message.ResponseCodeRejected) + bdl := td.shouldPublishMessageWithThisType(t, td.network, message.TypeHelloAck) + assert.Equal(t, bdl.Message.(*message.HelloAckMessage).ResponseCode, message.ResponseCodeRejected) + }) + + t.Run("Receiving Hello message from a peer. Difference is greater or equal than -10 seconds.", + func(t *testing.T) { + valKey := td.RandValKey() + height := td.RandUint32NonZero(td.state.LastBlockHeight()) + pid := td.RandPeerID() + msg := message.NewHelloMessage(pid, "kitty", height, services.New(services.Network), + td.state.LastBlockHash(), td.state.Genesis().Hash()) + msg.Sign([]*bls.ValidatorKey{valKey}) + + msg.MyTimeUnixMilli = msg.MyTime().Add(-10 * time.Second).UnixMilli() + assert.NoError(t, td.receivingNewMessage(td.sync, msg, pid)) + td.checkPeerStatus(t, pid, peerset.StatusCodeBanned) + bdl := td.shouldPublishMessageWithThisType(t, td.network, message.TypeHelloAck) + assert.Equal(t, bdl.Message.(*message.HelloAckMessage).ResponseCode, message.ResponseCodeRejected) + }) + + t.Run("Receiving Hello message from a peer. Difference is less or equal than 20 seconds.", + func(t *testing.T) { + valKey := td.RandValKey() + height := td.RandUint32NonZero(td.state.LastBlockHeight()) + pid := td.RandPeerID() + msg := message.NewHelloMessage(pid, "kitty", height, services.New(services.Network), + td.state.LastBlockHash(), td.state.Genesis().Hash()) + msg.Sign([]*bls.ValidatorKey{valKey}) + + msg.MyTimeUnixMilli = msg.MyTime().Add(20 * time.Second).UnixMilli() + assert.NoError(t, td.receivingNewMessage(td.sync, msg, pid)) + td.checkPeerStatus(t, pid, peerset.StatusCodeBanned) + bdl := td.shouldPublishMessageWithThisType(t, td.network, message.TypeHelloAck) + assert.Equal(t, bdl.Message.(*message.HelloAckMessage).ResponseCode, message.ResponseCodeRejected) }) t.Run("Receiving Hello message from a peer. It should be acknowledged and updates the peer info", @@ -59,8 +92,8 @@ func TestParsingHelloMessages(t *testing.T) { assert.NoError(t, td.receivingNewMessage(td.sync, msg, pid)) - bundle := td.shouldPublishMessageWithThisType(t, td.network, message.TypeHelloAck) - assert.Equal(t, bundle.Message.(*message.HelloAckMessage).ResponseCode, message.ResponseCodeOK) + bdl := td.shouldPublishMessageWithThisType(t, td.network, message.TypeHelloAck) + assert.Equal(t, bdl.Message.(*message.HelloAckMessage).ResponseCode, message.ResponseCodeOK) // Check if the peer info is updated p := td.sync.peerSet.GetPeer(pid)