|
| 1 | +package simpleacc |
| 2 | + |
| 3 | +import ( |
| 4 | + "github.com/filecoin-project/mir/pkg/accountability/simpleacc/common" |
| 5 | + "github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/certificates/fullcertificates" |
| 6 | + incommon "github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/common" |
| 7 | + "github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/poms" |
| 8 | + "github.com/filecoin-project/mir/pkg/accountability/simpleacc/internal/predecisions" |
| 9 | + "github.com/filecoin-project/mir/pkg/dsl" |
| 10 | + "github.com/filecoin-project/mir/pkg/factorymodule" |
| 11 | + "github.com/filecoin-project/mir/pkg/logging" |
| 12 | + "github.com/filecoin-project/mir/pkg/modules" |
| 13 | + accpbtypes "github.com/filecoin-project/mir/pkg/pb/accountabilitypb/types" |
| 14 | + factorypbtypes "github.com/filecoin-project/mir/pkg/pb/factorypb/types" |
| 15 | + t "github.com/filecoin-project/mir/pkg/types" |
| 16 | +) |
| 17 | + |
| 18 | +// ModuleConfig sets the module ids. All replicas are expected to use identical module configurations. |
| 19 | +type ModuleConfig = common.ModuleConfig |
| 20 | + |
| 21 | +// ModuleParams sets the values for the parameters of an instance of the protocol. |
| 22 | +// All replicas are expected to use identical module parameters. |
| 23 | +type ModuleParams = common.ModuleParams |
| 24 | + |
| 25 | +// NewModule creates a new instance of the (optinal) accountability |
| 26 | +// module. |
| 27 | +// This module can receive predecisions from an |
| 28 | +// ordering module (instead of the ordering module delivering them to the |
| 29 | +// application layer directly). It performs two all-to-all broadcasts |
| 30 | +// with signatures to ensure accountability. The first broadcast is a |
| 31 | +// signed predecision per participant. In the second broadcast, each |
| 32 | +// participant broadcasts a certificate containing a strong quorum of |
| 33 | +// signed predecisions that they each delivered from the first |
| 34 | +// broadcast. Termination occurs once a process receives a strong quorum |
| 35 | +// of correctly signed predecisions. |
| 36 | +// *Accountability* states that if an adversary (controlling less than a |
| 37 | +// strong quorum, but perhaps more or as much as a weak quorum) causes |
| 38 | +// a disagreement (two different correct processes delivering different |
| 39 | +// decisions) then there is at least a weak quorum of processes for which |
| 40 | +// all correct processes eventually receive Proofs-of-Misbehavior (PoMs). |
| 41 | +// In the case of this module, a PoM is a pair of signed predecisions for |
| 42 | +// different predecisions from the same node. |
| 43 | +// The module keeps looking for PoMs with newly received messages |
| 44 | +// (signed predecisions or certificates) after termination, until |
| 45 | +// it is garbage collected. |
| 46 | +// |
| 47 | + |
| 48 | +// Intuition of correctness: a process only terminates if it receives a |
| 49 | +// strong quorum of signed predecisions from distinct processes, forming |
| 50 | +// a certificate. Once this process forms a certificate, it shares it |
| 51 | +// with the rest of participants. If all correct processes terminate, |
| 52 | +// then that means all correct processes will (i) deliver a strong quorum |
| 53 | +// of signed predecisions and (ii) broadcast them in a certificate. Thus, |
| 54 | +// if two correct processes p_1, p_2 disagree (decide d_1, d_2, |
| 55 | +// respectively) then that means they must have each delivered a strong |
| 56 | +// quorum of signed predecisions for different predecisions. By quorum |
| 57 | +// intersection, this means that at least a weak quorum of processes have |
| 58 | +// signed respective predecisions for d_1, d_2 and sent each of them to |
| 59 | +// the respective correct process p_1, p_2. Once p_1 receives the |
| 60 | +// certificate that p_2 broadcasted, p_1 will then generate a weak quorum |
| 61 | +// of PoMs (and vice versa) and broadcast it to the rest of processes. |
| 62 | +// |
| 63 | +// This module effectively implements a variant of the accountability |
| 64 | +// module of Civit et al. at https://ieeexplore.ieee.org/document/9820722/ |
| 65 | +// Except that it ReportedPoMs map[t.NodeID]*accpbtypes.PoM |
| 66 | +// does not implement the optimization using threshold |
| 67 | +// signatures (as we have members with associated weight) |
| 68 | +// |
| 69 | + |
| 70 | +// The optimistic variant of this module is a parametrizable optimization |
| 71 | +// in which certificates are optimistically believed to be correct. This way, |
| 72 | +// in the good case a correct process broadcasts a light certificate of O(1) bits |
| 73 | +// (instead of O(n) of a certificate) |
| 74 | +// and only actually sends the full certificate to nodes from which it receives a light certificate |
| 75 | +// for a predecision other than the locally Decided one. The recipient of the certificate can then |
| 76 | +// generate and broadcast the PoMs. |
| 77 | +// |
| 78 | +// ATTENTION: This module is intended to be used once per instance |
| 79 | +// (to avoid replay attacks) and reinstantiated in a factory. |
| 80 | +func NewModule(mc ModuleConfig, params *ModuleParams, logger logging.Logger) (modules.PassiveModule, error) { |
| 81 | + m := dsl.NewModule(mc.Self) |
| 82 | + |
| 83 | + state := &incommon.State{ |
| 84 | + SignedPredecisions: make(map[t.NodeID]*accpbtypes.SignedPredecision), |
| 85 | + PredecisionCount: make(map[string][]t.NodeID), |
| 86 | + SignedPredecision: nil, |
| 87 | + DecidedCertificate: nil, |
| 88 | + Predecided: false, |
| 89 | + UnsentPoMs: make([]*accpbtypes.PoM, 0), |
| 90 | + SentPoMs: make(map[t.NodeID]*accpbtypes.PoM), |
| 91 | + } |
| 92 | + |
| 93 | + predecisions.IncludePredecisions(m, &mc, params, state, logger) |
| 94 | + fullcertificates.IncludeFullCertificate(m, &mc, params, state, logger) |
| 95 | + poms.IncludePoMs(m, &mc, params, state, logger) |
| 96 | + |
| 97 | + return m, nil |
| 98 | +} |
| 99 | + |
| 100 | +func NewReconfigurableModule(mc ModuleConfig, paramsTemplate ModuleParams, logger logging.Logger) modules.PassiveModule { |
| 101 | + if logger == nil { |
| 102 | + logger = logging.ConsoleErrorLogger |
| 103 | + } |
| 104 | + return factorymodule.New( |
| 105 | + mc.Self, |
| 106 | + factorymodule.DefaultParams( |
| 107 | + |
| 108 | + // This function will be called whenever the factory module |
| 109 | + // is asked to create a new instance of the multisig collector. |
| 110 | + func(accID t.ModuleID, params *factorypbtypes.GeneratorParams) (modules.PassiveModule, error) { |
| 111 | + |
| 112 | + // Extract the IDs of the nodes in the membership associated with this instance |
| 113 | + accParams := params.Type.(*factorypbtypes.GeneratorParams_AccModule).AccModule |
| 114 | + |
| 115 | + // Create a copy of basic module config with an adapted ID for the submodule. |
| 116 | + submc := mc |
| 117 | + submc.Self = accID |
| 118 | + |
| 119 | + // Fill in instance-specific parameters. |
| 120 | + moduleParams := paramsTemplate |
| 121 | + moduleParams.Membership = accParams.Membership |
| 122 | + |
| 123 | + // Create a new instance of the multisig collector. |
| 124 | + accountabilityModule, err := NewModule( |
| 125 | + submc, |
| 126 | + &moduleParams, |
| 127 | + logger, |
| 128 | + ) |
| 129 | + if err != nil { |
| 130 | + return nil, err |
| 131 | + } |
| 132 | + return accountabilityModule, nil |
| 133 | + }, |
| 134 | + ), |
| 135 | + logger, |
| 136 | + ) |
| 137 | +} |
0 commit comments