From a3a7ad83085b117e008c7e313098f6d0cd625dd9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 7 Mar 2022 16:17:51 -0800 Subject: [PATCH] [BFT Testing] Adds orchestrator interface and attack network (#2109) * adds attack orchestrator interface * adds attack network interface * adds attack orchestrator * adds mocks --- insecure/attackNetwork.go | 21 ++++ insecure/attackOrchestrator.go | 18 ++++ insecure/mock/attack_network.go | 111 ++++++++++++++++++++++ insecure/mock/attack_orchestrator.go | 77 +++++++++++++++ insecure/wintermute/attackOrchestrator.go | 64 +++++++++++++ 5 files changed, 291 insertions(+) create mode 100644 insecure/attackNetwork.go create mode 100644 insecure/attackOrchestrator.go create mode 100644 insecure/mock/attack_network.go create mode 100644 insecure/mock/attack_orchestrator.go create mode 100644 insecure/wintermute/attackOrchestrator.go diff --git a/insecure/attackNetwork.go b/insecure/attackNetwork.go new file mode 100644 index 00000000000..2f3bc35f886 --- /dev/null +++ b/insecure/attackNetwork.go @@ -0,0 +1,21 @@ +package insecure + +import ( + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/network" +) + +// AttackNetwork represents the networking interface that is available to the attacker for sending messages "through" corrupted nodes +// "to" the rest of the network. +type AttackNetwork interface { + component.Component + // RpcUnicastOnChannel enforces unicast-dissemination on the specified channel through a corrupted node. + RpcUnicastOnChannel(flow.Identifier, network.Channel, interface{}, flow.Identifier) error + + // RpcPublishOnChannel enforces a publish-dissemination on the specified channel through a corrupted node. + RpcPublishOnChannel(flow.Identifier, network.Channel, interface{}, ...flow.Identifier) error + + // RpcMulticastOnChannel enforces a multicast-dissemination on the specified channel through a corrupted node. + RpcMulticastOnChannel(flow.Identifier, network.Channel, interface{}, uint32, ...flow.Identifier) error +} diff --git a/insecure/attackOrchestrator.go b/insecure/attackOrchestrator.go new file mode 100644 index 00000000000..edd821cb6c2 --- /dev/null +++ b/insecure/attackOrchestrator.go @@ -0,0 +1,18 @@ +package insecure + +import ( + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/network" +) + +// AttackOrchestrator represents the stateful interface that implements a certain type of attack, e.g., wintermute attack. +type AttackOrchestrator interface { + component.Component + + // HandleEventFromCorruptedNode implements logic of processing the events received from a corrupted node. + // + // In Corruptible Conduit Framework for BFT testing, corrupted nodes relay their outgoing events to + // the attacker instead of dispatching them to the network. + HandleEventFromCorruptedNode(flow.Identifier, network.Channel, interface{}, Protocol, uint32, ...flow.Identifier) error +} diff --git a/insecure/mock/attack_network.go b/insecure/mock/attack_network.go new file mode 100644 index 00000000000..f3078d99d6e --- /dev/null +++ b/insecure/mock/attack_network.go @@ -0,0 +1,111 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +package mockinsecure + +import ( + flow "github.com/onflow/flow-go/model/flow" + + irrecoverable "github.com/onflow/flow-go/module/irrecoverable" + + mock "github.com/stretchr/testify/mock" + + network "github.com/onflow/flow-go/network" +) + +// AttackNetwork is an autogenerated mock type for the AttackNetwork type +type AttackNetwork struct { + mock.Mock +} + +// Done provides a mock function with given fields: +func (_m *AttackNetwork) Done() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// Ready provides a mock function with given fields: +func (_m *AttackNetwork) Ready() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// RpcMulticastOnChannel provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4 +func (_m *AttackNetwork) RpcMulticastOnChannel(_a0 flow.Identifier, _a1 network.Channel, _a2 interface{}, _a3 uint32, _a4 ...flow.Identifier) error { + _va := make([]interface{}, len(_a4)) + for _i := range _a4 { + _va[_i] = _a4[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1, _a2, _a3) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(flow.Identifier, network.Channel, interface{}, uint32, ...flow.Identifier) error); ok { + r0 = rf(_a0, _a1, _a2, _a3, _a4...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RpcPublishOnChannel provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *AttackNetwork) RpcPublishOnChannel(_a0 flow.Identifier, _a1 network.Channel, _a2 interface{}, _a3 ...flow.Identifier) error { + _va := make([]interface{}, len(_a3)) + for _i := range _a3 { + _va[_i] = _a3[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1, _a2) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(flow.Identifier, network.Channel, interface{}, ...flow.Identifier) error); ok { + r0 = rf(_a0, _a1, _a2, _a3...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RpcUnicastOnChannel provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *AttackNetwork) RpcUnicastOnChannel(_a0 flow.Identifier, _a1 network.Channel, _a2 interface{}, _a3 flow.Identifier) error { + ret := _m.Called(_a0, _a1, _a2, _a3) + + var r0 error + if rf, ok := ret.Get(0).(func(flow.Identifier, network.Channel, interface{}, flow.Identifier) error); ok { + r0 = rf(_a0, _a1, _a2, _a3) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Start provides a mock function with given fields: _a0 +func (_m *AttackNetwork) Start(_a0 irrecoverable.SignalerContext) { + _m.Called(_a0) +} diff --git a/insecure/mock/attack_orchestrator.go b/insecure/mock/attack_orchestrator.go new file mode 100644 index 00000000000..c9437282f2e --- /dev/null +++ b/insecure/mock/attack_orchestrator.go @@ -0,0 +1,77 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +package mockinsecure + +import ( + insecure "github.com/onflow/flow-go/insecure" + flow "github.com/onflow/flow-go/model/flow" + + irrecoverable "github.com/onflow/flow-go/module/irrecoverable" + + mock "github.com/stretchr/testify/mock" + + network "github.com/onflow/flow-go/network" +) + +// AttackOrchestrator is an autogenerated mock type for the AttackOrchestrator type +type AttackOrchestrator struct { + mock.Mock +} + +// Done provides a mock function with given fields: +func (_m *AttackOrchestrator) Done() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// HandleEventFromCorruptedNode provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4, _a5 +func (_m *AttackOrchestrator) HandleEventFromCorruptedNode(_a0 flow.Identifier, _a1 network.Channel, _a2 interface{}, _a3 insecure.Protocol, _a4 uint32, _a5 ...flow.Identifier) error { + _va := make([]interface{}, len(_a5)) + for _i := range _a5 { + _va[_i] = _a5[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1, _a2, _a3, _a4) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(flow.Identifier, network.Channel, interface{}, insecure.Protocol, uint32, ...flow.Identifier) error); ok { + r0 = rf(_a0, _a1, _a2, _a3, _a4, _a5...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Ready provides a mock function with given fields: +func (_m *AttackOrchestrator) Ready() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// Start provides a mock function with given fields: _a0 +func (_m *AttackOrchestrator) Start(_a0 irrecoverable.SignalerContext) { + _m.Called(_a0) +} diff --git a/insecure/wintermute/attackOrchestrator.go b/insecure/wintermute/attackOrchestrator.go new file mode 100644 index 00000000000..b853e307c89 --- /dev/null +++ b/insecure/wintermute/attackOrchestrator.go @@ -0,0 +1,64 @@ +package wintermute + +import ( + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/insecure" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/network" +) + +// Orchestrator encapsulates a stateful implementation of wintermute attack orchestrator logic. +type Orchestrator struct { + component.Component + logger zerolog.Logger + network insecure.AttackNetwork + corruptedIds flow.IdentityList + allIds flow.IdentityList // identity of all nodes in the network (including non-corrupted ones) +} + +var _ insecure.AttackOrchestrator = &Orchestrator{} + +func NewOrchestrator(allIds flow.IdentityList, corruptedIds flow.IdentityList, attackNetwork insecure.AttackNetwork, logger zerolog.Logger) *Orchestrator { + o := &Orchestrator{ + logger: logger, + network: attackNetwork, + corruptedIds: corruptedIds, + allIds: allIds, + } + + cm := component.NewComponentManagerBuilder(). + AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + o.start(ctx) + + ready() + + <-ctx.Done() + }).Build() + + o.Component = cm + + return o +} + +// start performs the startup of orchestrator components. +func (o *Orchestrator) start(ctx irrecoverable.SignalerContext) { + o.network.Start(ctx) +} + +// HandleEventFromCorruptedNode implements logic of processing the events received from a corrupted node. +// +// In Corruptible Conduit Framework for BFT testing, corrupted nodes relay their outgoing events to +// the attacker instead of dispatching them to the network. +func (o *Orchestrator) HandleEventFromCorruptedNode(corruptedId flow.Identifier, + channel network.Channel, + event interface{}, + protocol insecure.Protocol, + num uint32, + targetIds ...flow.Identifier) error { + + // TODO: implement wintermute attack logic. + panic("implement me") +}