Skip to content
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

Commit to a next power table and additional commitments #273

Merged
merged 7 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func main() {
err := gen.WriteTupleEncodersToFile("../gpbft/gen.go", "gpbft",
gpbft.TipSet{},
gpbft.GMessage{},
gpbft.SupplementalData{},
gpbft.Payload{},
gpbft.Justification{},
)
Expand Down
9 changes: 6 additions & 3 deletions gpbft/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,13 @@ type Receiver interface {
}

type Chain interface {
// Returns the chain to propose for a new GPBFT instance.
// This should be a suffix of the chain finalised by the immediately prior instance.
// Returns the supplemental data and the chain to propose for a new GPBFT instance.
// The chain must be a suffix of the chain finalised by the immediately prior instance.
// The supplemental data must be derived entirely from prior instances and all participants
// must propose the same supplemental data.
//
// Returns an error if the chain for the instance is not available.
GetChainForInstance(instance uint64) (chain ECChain, err error)
GetProposalForInstance(instance uint64) (data *SupplementalData, chain ECChain, err error)

// Returns the power table and beacon value to be used for a GPBFT instance.
// These values should be derived from a chain previously received as final by the host,
Expand Down
2 changes: 1 addition & 1 deletion gpbft/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func (c ECChain) Prefix(to int) ECChain {
panic("can't get prefix from zero-valued chain")
}
length := min(to+1, len(c))
return c[:length : length]
return c[:length:length]
}

// Compares two ECChains for equality.
Expand Down
130 changes: 128 additions & 2 deletions gpbft/gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 41 additions & 13 deletions gpbft/gpbft.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ type Justification struct {
Signature []byte
}

type SupplementalData struct {
// Merkle-tree of instance-specific commitments. Currently empty but this will eventually
// include things like snark-friendly power-table commitments.
Commitments [32]byte
// The DagCBOR-blake2b256 CID of the power table used to validate the next instance, taking
// lookback into account.
PowerTable CID // []PowerEntry
}

func (d *SupplementalData) Eq(other *SupplementalData) bool {
return d.Commitments == other.Commitments &&
bytes.Equal(d.PowerTable, other.PowerTable)
}

// Fields of the message that make up the signature payload.
type Payload struct {
// GossiPBFT instance (epoch) number.
Expand All @@ -86,15 +100,17 @@ type Payload struct {
Round uint64
// GossiPBFT step name.
Step Phase
// Chain of tipsets proposed/voted for finalisation.
// Always non-empty; the first entry is the base tipset finalised in the previous instance.
// The common data.
SupplementalData SupplementalData
// The value agreed-upon in a single instance.
Value ECChain
}

func (p *Payload) Eq(other *Payload) bool {
return p.Instance == other.Instance &&
p.Round == other.Round &&
p.Step == other.Step &&
p.SupplementalData.Eq(&other.SupplementalData) &&
p.Value.Eq(other.Value)
}

Expand All @@ -114,7 +130,9 @@ func (p *Payload) MarshalForSigning(nn NetworkName) []byte {
_ = binary.Write(&buf, binary.BigEndian, p.Step)
_ = binary.Write(&buf, binary.BigEndian, p.Round)
_ = binary.Write(&buf, binary.BigEndian, p.Instance)
_, _ = buf.Write(p.SupplementalData.Commitments[:])
_, _ = buf.Write(root[:])
_, _ = buf.Write(p.SupplementalData.PowerTable)
return buf.Bytes()
}

Expand All @@ -140,6 +158,9 @@ type instance struct {
// For QUALITY, PREPARE, and COMMIT, this is the latest time (the phase can end sooner).
// For CONVERGE, this is the exact time (the timeout solely defines the phase end).
phaseTimeout time.Time
// Supplemental data that all participants must agree on ahead of time. Messages that
// propose supplemental data that differs with our supplemental data will be discarded.
supplementalData *SupplementalData
// This instance's proposal for the current round. Never bottom.
// This is set after the QUALITY phase, and changes only at the end of a full round.
proposal ECChain
Expand Down Expand Up @@ -171,23 +192,25 @@ func newInstance(
participant *Participant,
instanceID uint64,
input ECChain,
data *SupplementalData,
powerTable PowerTable,
beacon []byte) (*instance, error) {
if input.IsZero() {
return nil, fmt.Errorf("input is empty")
}
return &instance{
participant: participant,
instanceID: instanceID,
input: input,
powerTable: powerTable,
beacon: beacon,
round: 0,
phase: INITIAL_PHASE,
proposal: input,
value: ECChain{},
candidates: []ECChain{input.BaseChain()},
quality: newQuorumState(powerTable),
participant: participant,
instanceID: instanceID,
input: input,
powerTable: powerTable,
beacon: beacon,
round: 0,
phase: INITIAL_PHASE,
supplementalData: data,
proposal: input,
value: ECChain{},
candidates: []ECChain{input.BaseChain()},
quality: newQuorumState(powerTable),
rounds: map[uint64]*roundState{
0: newRoundState(powerTable),
},
Expand Down Expand Up @@ -224,6 +247,11 @@ func (i *instance) Receive(msg *GMessage) error {
return fmt.Errorf("message for instance %d, expected %d: %w",
msg.Vote.Instance, i.instanceID, ErrReceivedWrongInstance)
}
// Ensure all participants are proposing the same supplemental data. This data is based on
// the _base_, so everyone should agree.
if !msg.Vote.SupplementalData.Eq(i.supplementalData) {
return xerrors.Errorf("message for instance %d disagrees on the supplemental data", msg.Vote.Instance)
}
// Perform validation that could not be done until the instance started.
if !(msg.Vote.Value.IsZero() || msg.Vote.Value.HasBase(i.input.Base())) {
return fmt.Errorf("message base %s, expected %s: %w",
Expand Down
Loading
Loading