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

Make ECChain opaque [][]byte in GBPFT #149

Merged
merged 4 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion cmd/f3sim/f3sim.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func main() {
sm := sim.NewSimulation(simConfig, graniteConfig, *traceLevel)

// Same chain for everyone.
candidate := sm.Base(0).Extend(sm.CIDGen.Sample())
candidate := sm.Base(0).Extend(sm.TipGen.Sample())
sm.SetChains(sim.ChainCount{Count: *participantCount, Chain: candidate})

err := sm.Run(1, *maxRounds)
Expand Down
1 change: 0 additions & 1 deletion gen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ func main() {
gpbft.GMessage{},
gpbft.Payload{},
gpbft.Justification{},
gpbft.TipSet{},
)
if err != nil {
fmt.Println(err)
Expand Down
113 changes: 37 additions & 76 deletions gpbft/chain.go
Original file line number Diff line number Diff line change
@@ -1,61 +1,31 @@
package gpbft

import (
"encoding/binary"
"bytes"
"encoding/hex"
"errors"
"io"
"strconv"
"strings"
)

// Information about a tipset that is relevant to the F3 protocol.
// This is a lightweight value type comprising 3 machine words.
// Fields are exported for CBOR generation, but are opqaue and should not be accessed
// within the protocol implementation.
type TipSet struct {
// The epoch of the blocks in the tipset.
Epoch int64
// The CID of the tipset.
CID TipSetID
}

// Creates a new tipset.
func NewTipSet(epoch int64, cid TipSetID) TipSet {
return TipSet{
Epoch: epoch,
CID: cid,
}
}

// Returns a zero value tipset.
// The zero value is not a meaningful tipset and may be used to represent bottom.
func ZeroTipSet() TipSet {
return TipSet{}
}

func (t TipSet) IsZero() bool {
return t.Epoch == 0 && t.CID.IsZero()
}

func (t TipSet) String() string {
var b strings.Builder
b.Write(t.CID.Bytes())
b.WriteString("@")
b.WriteString(strconv.FormatInt(t.Epoch, 10))
return b.String()
}

func (t TipSet) MarshalForSigning(w io.Writer) {
_ = binary.Write(w, binary.BigEndian, t.Epoch)
_, _ = w.Write(t.CID.Bytes())
}
// Opaque type representing a tipset.
// This is expected to be:
// - a canonical sequence of CIDs of block headers identifying a tipset,
// - a commitment to the resulting power table,
// - a commitment to additional derived values.
// However, GossipPBFT doesn't need to know anything about that structure.
type TipSet = []byte
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious: why type alias instead of type definition?

Copy link
Member Author

@anorth anorth Apr 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cbor-gen was unable to generate correct (compiling) code for [][]byte. A type definition would have been slightly better, but this isn't a yak I'm prepared to shave.


// A chain of tipsets comprising a base (the last finalised tipset from which the chain extends).
// and (possibly empty) suffix.
// Tipsets are assumed to be built contiguously on each other, though epochs may be missing due to null rounds.
// The zero value is not a valid chain, and represents a "bottom" value when used in a Granite message.
// Tipsets are assumed to be built contiguously on each other,
// though epochs may be missing due to null rounds.
// The zero value is not a valid chain, and represents a "bottom" value
// when used in a Granite message.
type ECChain []TipSet

// A map key for a chain. The zero value means "bottom".
type ChainKey string

// Creates a new chain.
func NewChain(base TipSet, suffix ...TipSet) (ECChain, error) {
var chain ECChain = []TipSet{base}
Expand Down Expand Up @@ -91,27 +61,14 @@ func (c ECChain) Head() TipSet {
return c[len(c)-1]
}

// Returns the CID of the head tipset, or empty string for a zero value
func (c ECChain) HeadOrZero() TipSet {
if c.IsZero() {
return ZeroTipSet()
}
return c.Head()
}

// Returns a new chain with the same base and no suffix.
// Invalid for a zero value.
func (c ECChain) BaseChain() ECChain {
return ECChain{c[0]}
}

// Returns a new chain extending this chain with one tipset.
// The new tipset is given an epoch and weight one greater than the previous head.
func (c ECChain) Extend(cid TipSetID) ECChain {
return append(c, TipSet{
Epoch: c.Head().Epoch + 1,
CID: cid,
})
func (c ECChain) Extend(tip TipSet) ECChain {
return append(c, tip)
}

// Returns a chain with suffix (after the base) truncated to a maximum length.
Expand All @@ -127,7 +84,7 @@ func (c ECChain) Eq(other ECChain) bool {
return false
}
for i := range c {
if c[i] != other[i] {
if !bytes.Equal(c[i], other[i]) {
return false
}
}
Expand All @@ -140,16 +97,16 @@ func (c ECChain) SameBase(other ECChain) bool {
if c.IsZero() || other.IsZero() {
return false
}
return c.Base() == other.Base()
return bytes.Equal(c.Base(), other.Base())
}

// Check whether a chain has a specific base tipset.
// Always false for a zero value.
func (c ECChain) HasBase(t TipSet) bool {
if c.IsZero() || t.IsZero() {
if c.IsZero() || len(t) == 0 {
return false
}
return c[0] == t
return bytes.Equal(c[0], t)
}

// Checks whether a chain has some prefix (including the base).
Expand All @@ -162,7 +119,7 @@ func (c ECChain) HasPrefix(other ECChain) bool {
return false
}
for i := range other {
if c[i] != other[i] {
if !bytes.Equal(c[i], other[i]) {
return false
}
}
Expand All @@ -171,12 +128,12 @@ func (c ECChain) HasPrefix(other ECChain) bool {

// Checks whether a chain has some tipset (including as its base).
func (c ECChain) HasTipset(t TipSet) bool {
if t.IsZero() {
if len(t) == 0 {
// Chain can never contain zero-valued TipSet.
return false
}
for _, t2 := range c {
if t2 == t {
if bytes.Equal(t, t2) {
return true
}
}
Expand All @@ -192,25 +149,29 @@ func (c ECChain) Validate() error {
if c.IsZero() {
return nil
}
var epochSoFar int64
for _, tipSet := range c {
switch {
case tipSet.IsZero():
if len(tipSet) == 0 {
return errors.New("chain cannot contain zero-valued tip sets")
case tipSet.Epoch <= epochSoFar:
return errors.New("chain epoch must be in order and unique")
default:
epochSoFar = tipSet.Epoch
}
}
return nil
}

// Returns an identifier for the chain suitable for use as a map key.
// This must completely determine the sequence of tipsets in the chain.
func (c ECChain) Key() ChainKey {
buf := bytes.Buffer{}
for _, t := range c {
Stebalien marked this conversation as resolved.
Show resolved Hide resolved
buf.Write(t)
anorth marked this conversation as resolved.
Show resolved Hide resolved
}
return ChainKey(buf.String())
}

func (c ECChain) String() string {
var b strings.Builder
b.WriteString("[")
for i, t := range c {
b.WriteString(t.String())
b.WriteString(hex.EncodeToString(t))
if i < len(c)-1 {
b.WriteString(", ")
}
Expand Down
Loading
Loading