-
Notifications
You must be signed in to change notification settings - Fork 14
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
Accountability module #505
Open
ranchalp
wants to merge
14
commits into
consensus-shipyard:main
Choose a base branch
from
ranchalp:accountability-module
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 13 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
38d6e12
Implement new protos
ranchalp 4a1ba95
Implement accountability module (as a factory)
ranchalp 5bc0c7d
Implement light certificates optimization
ranchalp a14de10
Correct typo in proto message and regenerate protos
ranchalp f089ac2
Make lint happy
ranchalp dcbbee3
Address minor comments
ranchalp c946913
Simplify Certificate use SBDeliver as entry/exit point
ranchalp c8929d2
Remove all events from accountability module (only messages)
ranchalp eb33268
Cleanup light certificates and use native proto type, mark function u…
ranchalp a4b983a
Apply buffered light certificates
ranchalp eafd4e5
Add pomHandler to ModuleParams (application defines what to do with t…
ranchalp 7c4e481
Re-send messages in critical path to other nodes
ranchalp 2c76ffd
Removed Unnecessary Request/Provide messages
ranchalp 8858990
Reformat a few lines of comments
matejpavlovic 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 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,144 @@ | ||
package simpleacc | ||
|
||
import ( | ||
"github.com/filecoin-project/mir/pkg/accountability/simpleacc/common" | ||
"github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/certificates/fullcertificates" | ||
"github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/certificates/lightcertificates" | ||
incommon "github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/common" | ||
"github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/poms" | ||
"github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/predecisions" | ||
"github.com/filecoin-project/mir/pkg/dsl" | ||
"github.com/filecoin-project/mir/pkg/factorymodule" | ||
"github.com/filecoin-project/mir/pkg/logging" | ||
"github.com/filecoin-project/mir/pkg/modules" | ||
accpbtypes "github.com/filecoin-project/mir/pkg/pb/accountabilitypb/types" | ||
factorypbtypes "github.com/filecoin-project/mir/pkg/pb/factorypb/types" | ||
t "github.com/filecoin-project/mir/pkg/types" | ||
) | ||
|
||
// ModuleConfig sets the module ids. All replicas are expected to use identical module configurations. | ||
type ModuleConfig = common.ModuleConfig | ||
|
||
// ModuleParams sets the values for the parameters of an instance of the protocol. | ||
// All replicas are expected to use identical module parameters. | ||
type ModuleParams = common.ModuleParams | ||
|
||
// NewModule creates a new instance of the (optinal) accountability | ||
// module. | ||
// This module can receive decisions from a module that ensures agreement | ||
// (for example, receive a decision from the ordering module, instead of | ||
// the ordering module delivering them to the application layer directly), | ||
// and treats them as predecisions. It performs two all-to-all broadcasts | ||
// with signatures to ensure accountability. The first broadcast is a | ||
// signed predecision per participant. In the second broadcast, each | ||
// participant broadcasts a certificate containing a strong quorum of | ||
// signed predecisions that they each delivered from the first | ||
// broadcast. Termination occurs once a process receives a strong quorum | ||
// of correctly signed predecisions. | ||
// *Accountability* states that if an adversary (controlling less than a | ||
// strong quorum, but perhaps more or as much as a weak quorum) causes | ||
// a disagreement (two different correct processes delivering different | ||
// decisions) then all correct processes eventually receive Proofs-of-Misbehavior (PoMs) | ||
// for a provably malicious coalition at least the size of a weak quorum. | ||
// In the case of this module, a PoM is a pair of different predecisions signed | ||
// by the same node. | ||
// The module keeps looking for PoMs with newly received messages | ||
// (signed predecisions or certificates) after termination, until | ||
// it is garbage collected. | ||
// | ||
|
||
// Intuition of correctness: a process only terminates if it receives a | ||
// strong quorum of signed predecisions from distinct processes, forming | ||
// a certificate. Once this process forms a certificate, it shares it | ||
// with the rest of participants. If all correct processes terminate, | ||
// then that means all correct processes will (i) deliver a strong quorum | ||
// of signed predecisions and (ii) broadcast them in a certificate. Thus, | ||
// if two correct processes p_1, p_2 disagree (decide d_1, d_2, | ||
// respectively) then that means they must have each delivered a strong | ||
// quorum of signed predecisions for different predecisions. By quorum | ||
// intersection, this means that at least a weak quorum of processes have | ||
// signed respective predecisions for d_1, d_2 and sent each of them to | ||
// the respective correct process p_1, p_2. Once p_1 receives the | ||
// certificate that p_2 broadcasted, p_1 will then generate a weak quorum | ||
// of PoMs (and vice versa) and broadcast it to the rest of processes. | ||
// | ||
// This module effectively implements a variant of the accountability | ||
// module of Civit et al. at https://ieeexplore.ieee.org/document/9820722/ | ||
// Except that it does not implement the optimization using threshold | ||
// signatures (as we have members with associated weight) | ||
// | ||
|
||
// The optimistic variant of this module is a parametrizable optimization | ||
// in which certificates are optimistically believed to be correct. This way, | ||
// in the good case a correct process broadcasts a light certificate of O(1) bits | ||
// (instead of O(n) of a certificate) | ||
// and only actually sends the full certificate to nodes from which it receives a light certificate | ||
// for a predecision other than the locally Decided one. The recipient of the certificate can then | ||
// generate and broadcast the PoMs. | ||
// | ||
// ATTENTION: This module is intended to be used once per instance | ||
// (to avoid replay attacks) and reinstantiated in a factory. | ||
func NewModule( | ||
mc ModuleConfig, | ||
params *ModuleParams, | ||
logger logging.Logger) (modules.PassiveModule, error) { | ||
m := dsl.NewModule(mc.Self) | ||
|
||
state := &incommon.State{ | ||
SignedPredecisions: make(map[t.NodeID]*accpbtypes.SignedPredecision), | ||
PredecisionNodeIDs: make(map[string][]t.NodeID), | ||
LocalPredecision: nil, | ||
DecidedCertificate: nil, | ||
Predecided: false, | ||
UnhandledPoMs: make([]*accpbtypes.PoM, 0), | ||
HandledPoMs: make(map[t.NodeID]*accpbtypes.PoM), | ||
} | ||
|
||
predecisions.IncludePredecisions(m, &mc, params, state, logger) | ||
fullcertificates.IncludeFullCertificate(m, &mc, params, state, logger) | ||
if params.LightCertificates { | ||
lightcertificates.IncludeLightCertificate(m, &mc, params, state, logger) | ||
} | ||
poms.IncludePoMs(m, &mc, params, state, logger) | ||
|
||
return m, nil | ||
} | ||
|
||
func NewReconfigurableModule(mc ModuleConfig, paramsTemplate ModuleParams, logger logging.Logger) modules.PassiveModule { | ||
if logger == nil { | ||
logger = logging.ConsoleErrorLogger | ||
} | ||
return factorymodule.New( | ||
mc.Self, | ||
factorymodule.DefaultParams( | ||
|
||
// This function will be called whenever the factory module | ||
// is asked to create a new instance of the accountabuility module. | ||
func(accID t.ModuleID, params *factorypbtypes.GeneratorParams) (modules.PassiveModule, error) { | ||
|
||
accParams := params.Type.(*factorypbtypes.GeneratorParams_AccModule).AccModule | ||
|
||
// Create a copy of basic module config with an adapted ID for the submodule. | ||
submc := mc | ||
submc.Self = accID | ||
|
||
// Fill in instance-specific parameters. | ||
moduleParams := paramsTemplate | ||
moduleParams.Membership = accParams.Membership | ||
moduleParams.RetentionIndex = accParams.RetentionIndex | ||
|
||
// Create a new instance of the multisig collector. | ||
accountabilityModule, err := NewModule( | ||
submc, | ||
&moduleParams, | ||
logger, | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return accountabilityModule, nil | ||
}, | ||
), | ||
logger, | ||
) | ||
} |
This file contains 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,39 @@ | ||
package common | ||
|
||
import ( | ||
incommon "github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/common" | ||
"github.com/filecoin-project/mir/pkg/dsl" | ||
"github.com/filecoin-project/mir/pkg/logging" | ||
accpbtypes "github.com/filecoin-project/mir/pkg/pb/accountabilitypb/types" | ||
trantorpbtypes "github.com/filecoin-project/mir/pkg/pb/trantorpb/types" | ||
timertypes "github.com/filecoin-project/mir/pkg/timer/types" | ||
tt "github.com/filecoin-project/mir/pkg/trantor/types" | ||
t "github.com/filecoin-project/mir/pkg/types" | ||
) | ||
|
||
// ModuleConfig sets the module ids. All replicas are expected to use identical module configurations. | ||
type ModuleConfig struct { | ||
Self t.ModuleID // id of this module, used to uniquely identify an instance of the accountability module. | ||
// It prevents cross-instance signature replay attack and should be unique across all executions. | ||
|
||
Ordering t.ModuleID // provides Predecisions | ||
App t.ModuleID // receives Decisions and/or PoMs | ||
Crypto t.ModuleID // provides cryptographic primitives | ||
Timer t.ModuleID // provides Timing primitives | ||
Net t.ModuleID // provides network primitives | ||
} | ||
|
||
// ModuleParams sets the values for the parameters of an instance of the protocol. | ||
// All replicas are expected to use identical module parameters. | ||
type ModuleParams struct { | ||
Membership *trantorpbtypes.Membership // The list of participating nodes. | ||
LightCertificates bool | ||
ResendFrequency timertypes.Duration // Frequency with which messages in the critical path are re-sent | ||
RetentionIndex tt.RetentionIndex | ||
PoMsHandler func(m dsl.Module, // Function to be called when PoMs detected. | ||
mc *ModuleConfig, | ||
params *ModuleParams, | ||
state *incommon.State, | ||
poms []*accpbtypes.PoM, | ||
logger logging.Logger) | ||
} |
78 changes: 78 additions & 0 deletions
78
pkg/accountability/simpleacc/internal/certificates/fullcertificates/fullcertificates.go
This file contains 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,78 @@ | ||
package fullcertificates | ||
|
||
import ( | ||
"github.com/filecoin-project/mir/pkg/accountability/simpleacc/common" | ||
incommon "github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/common" | ||
"github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/poms" | ||
"github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/predecisions" | ||
"github.com/filecoin-project/mir/pkg/dsl" | ||
"github.com/filecoin-project/mir/pkg/logging" | ||
accpbdsl "github.com/filecoin-project/mir/pkg/pb/accountabilitypb/dsl" | ||
accpbtypes "github.com/filecoin-project/mir/pkg/pb/accountabilitypb/types" | ||
cryptopbdsl "github.com/filecoin-project/mir/pkg/pb/cryptopb/dsl" | ||
cryptopbtypes "github.com/filecoin-project/mir/pkg/pb/cryptopb/types" | ||
t "github.com/filecoin-project/mir/pkg/types" | ||
"github.com/filecoin-project/mir/pkg/util/maputil" | ||
"github.com/filecoin-project/mir/pkg/util/membutil" | ||
"github.com/filecoin-project/mir/pkg/util/sliceutil" | ||
) | ||
|
||
// IncludeFullCertificate implements the full certificate brodcast and verification | ||
// in order to find PoMs. | ||
func IncludeFullCertificate(m dsl.Module, | ||
mc *common.ModuleConfig, | ||
params *common.ModuleParams, | ||
state *incommon.State, | ||
logger logging.Logger, | ||
) { | ||
|
||
accpbdsl.UponFullCertificateReceived(m, func(from t.NodeID, decision []byte, certificate map[t.NodeID][]byte) error { | ||
if len(certificate) == 0 { | ||
logger.Log(logging.LevelDebug, "Ignoring empty predecision certificate") | ||
return nil | ||
} | ||
|
||
if !membutil.HaveStrongQuorum(params.Membership, maputil.GetKeys(certificate)) { | ||
logger.Log(logging.LevelDebug, "Ignoring predecision certificate without strong quorum") | ||
return nil | ||
} | ||
|
||
// Verify all signatures in certificate. | ||
cryptopbdsl.VerifySigs( | ||
m, | ||
mc.Crypto, | ||
sliceutil.Generate( | ||
len(certificate), | ||
func(i int) *cryptopbtypes.SignedData { | ||
return &cryptopbtypes.SignedData{ | ||
Data: [][]byte{decision}, | ||
} | ||
}), | ||
maputil.GetValues(certificate), | ||
maputil.GetKeys(certificate), | ||
&verifySigs{ | ||
certificate: &accpbtypes.FullCertificate{ | ||
Decision: decision, | ||
Signatures: certificate, | ||
}, | ||
}, | ||
) | ||
return nil | ||
}) | ||
|
||
cryptopbdsl.UponSigsVerified(m, func(nodeIds []t.NodeID, errs []error, allOk bool, vsr *verifySigs) error { | ||
for i, nodeID := range nodeIds { | ||
sp := &accpbtypes.SignedPredecision{ | ||
Predecision: vsr.certificate.Decision, | ||
Signature: vsr.certificate.Signatures[nodeID], | ||
} | ||
predecisions.ApplySigVerified(m, mc, params, state, nodeID, errs[i], sp, false, logger) | ||
} | ||
poms.HandlePoMs(m, mc, params, state, logger) | ||
return nil | ||
}) | ||
} | ||
|
||
type verifySigs struct { | ||
certificate *accpbtypes.FullCertificate | ||
} |
77 changes: 77 additions & 0 deletions
77
pkg/accountability/simpleacc/internal/certificates/lightcertificates/lightcertificates.go
This file contains 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,77 @@ | ||
package lightcertificates | ||
|
||
import ( | ||
"reflect" | ||
|
||
"github.com/filecoin-project/mir/pkg/accountability/simpleacc/common" | ||
incommon "github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/common" | ||
"github.com/filecoin-project/mir/pkg/dsl" | ||
"github.com/filecoin-project/mir/pkg/logging" | ||
accpbdsl "github.com/filecoin-project/mir/pkg/pb/accountabilitypb/dsl" | ||
accpbmsgs "github.com/filecoin-project/mir/pkg/pb/accountabilitypb/msgs" | ||
transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" | ||
t "github.com/filecoin-project/mir/pkg/types" | ||
) | ||
|
||
// IncludeLightCertificate implements the (optional) light certificate optimization | ||
// that optimistically sends only the predecision during the light certificate | ||
// so that in the good case where there are no disagreements and all processes | ||
// are correct there is no need to broadcast a full certificate containing O(n) signatures. | ||
func IncludeLightCertificate(m dsl.Module, | ||
mc *common.ModuleConfig, | ||
params *common.ModuleParams, | ||
state *incommon.State, | ||
logger logging.Logger, | ||
) { | ||
|
||
accpbdsl.UponLightCertificateReceived(m, func(from t.NodeID, data []byte) error { | ||
|
||
if !params.LightCertificates { | ||
return nil | ||
} | ||
|
||
if state.DecidedCertificate == nil { | ||
logger.Log(logging.LevelDebug, "Received light certificate before decided certificate, buffering it") | ||
state.LightCertificates[from] = data | ||
return nil | ||
} | ||
|
||
applyLightCertificateReceived(m, mc, state, from, data, logger) | ||
return nil | ||
}) | ||
} | ||
|
||
func applyLightCertificateReceived( | ||
m dsl.Module, | ||
mc *common.ModuleConfig, | ||
state *incommon.State, | ||
from t.NodeID, | ||
data []byte, | ||
logger logging.Logger) { | ||
|
||
decision := state.DecidedCertificate.Decision | ||
|
||
if !reflect.DeepEqual(decision, data) { | ||
logger.Log(logging.LevelWarn, "Received light certificate with different predecision than local decision! sending full certificate to node %v", from) | ||
transportpbdsl.SendMessage( | ||
m, | ||
mc.Net, | ||
accpbmsgs.FullCertificate(mc.Self, | ||
state.DecidedCertificate.Decision, | ||
state.DecidedCertificate.Signatures), | ||
[]t.NodeID{from}) | ||
} | ||
|
||
} | ||
|
||
func ApplyLightCertificatesBuffered( | ||
m dsl.Module, | ||
mc *common.ModuleConfig, | ||
state *incommon.State, | ||
logger logging.Logger) { | ||
|
||
for from, data := range state.LightCertificates { | ||
applyLightCertificateReceived(m, mc, state, from, data, logger) | ||
} | ||
|
||
} |
This file contains 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,24 @@ | ||
package common | ||
|
||
import ( | ||
accpbtypes "github.com/filecoin-project/mir/pkg/pb/accountabilitypb/types" | ||
isspbtypes "github.com/filecoin-project/mir/pkg/pb/isspb/types" | ||
t "github.com/filecoin-project/mir/pkg/types" | ||
) | ||
|
||
type State struct { | ||
SignedPredecisions map[t.NodeID]*accpbtypes.SignedPredecision // Map of received signed predicisions (including own's) with their signer as key. | ||
PredecisionNodeIDs map[string][]t.NodeID // Map of predecisions and the nodes that have signed them with the predecision as key, | ||
// used for fast verification of whether a predecision is predecided by a strong quorum. | ||
LocalPredecision *LocalPredecision // Decision locally decided | ||
DecidedCertificate *accpbtypes.FullCertificate // Locally decided certificate (predecision and list of signatures with signers as key) | ||
Predecided bool // Whether this process has received a predecided value from calling module. | ||
UnhandledPoMs []*accpbtypes.PoM // List of PoMs not yet sent to the application. | ||
HandledPoMs map[t.NodeID]*accpbtypes.PoM // List of PoMs already sent to the application with the signer as key. | ||
LightCertificates map[t.NodeID][]byte // Map of light certificates with the signer as key, buffered if no local decision made yet. | ||
} | ||
|
||
type LocalPredecision struct { | ||
SBDeliver *isspbtypes.SBDeliver // Actual payload of the local predecision. | ||
SignedPredecision *accpbtypes.SignedPredecision // Own signed predecision. | ||
} |
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.
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.
Copy-paste artifact?