Skip to content

Commit

Permalink
hare3 implementation (#4765)
Browse files Browse the repository at this point in the history
implementation is splitted into several files:

#### protocol.go
implements protocol execution logic close to the original paper, baring several differences in syntax and things that we need but are not covered in the paper. interface for interacting with protocol has two methods:

- onMessage
- next(active bool)

only active nodes are publishing messages

#### types.go
types that go over the wire

```go
type Message struct {
	Body
	Sender    types.NodeID
	Signature types.EdSignature
}

type Body struct {
	Layer types.LayerID
	IterRound
	Value       Value
	Eligibility types.HareEligibility
}

type Value struct {
	// Proposals is set in messages for preround and propose rounds.
	Proposals []types.ProposalID `scale:"max=200"`
	// Reference is set in messages for commit and notify rounds.
	Reference *types.Hash32
}
```

signature domain and malfeasance proof remains the same, as we don't intend to run more than one hare in parallel.

#### hare.go
integrates protocol with spacemesh data model, network, synchronization. manages multiple protocol instances (one per layer).

#### legacy_oracle.go
wrapper for oracle that uses active set in consensus, it outputs maximum grades for identities that are in consensus and 0 for everyone else.

#### compat
intergration for weakcoin and hare results to avoid changes across the app


remaining:
- [ ] test commit with different locked value in protocol_test.go (remaining for 100%)
  • Loading branch information
dshulyak committed Aug 30, 2023
1 parent 2ffb4e0 commit 2be4a0f
Show file tree
Hide file tree
Showing 23 changed files with 3,171 additions and 7 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ See [RELEASE](./RELEASE.md) for workflow instructions.

### Features

* [#4765](https://github.com/spacemeshos/go-spacemesh/pull/4765) hare 3 consensus protocol.

Replacement for original version of hare. Won't be enabled on mainnet for now.
Otherwise protocol uses significantly less traffic (atlest x20), and will allow
to set lower expected latency in the network, eventually reducing layer time.

### Improvements

## v1.1.2
Expand Down
4 changes: 4 additions & 0 deletions cmd/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ func EnsureCLIFlags(cmd *cobra.Command, appCFG *config.Config) error {
elem = reflect.ValueOf(&appCFG.HareEligibility).Elem()
assignFields(ff, elem, name)

ff = reflect.TypeOf(appCFG.HARE3)
elem = reflect.ValueOf(&appCFG.HARE3).Elem()
assignFields(ff, elem, name)

ff = reflect.TypeOf(appCFG.Beacon)
elem = reflect.ValueOf(&appCFG.Beacon).Elem()
assignFields(ff, elem, name)
Expand Down
7 changes: 7 additions & 0 deletions codec/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ func putEncoderBuffer(b *bytes.Buffer) {
encoderPool.Put(b)
}

func MustEncodeTo(w io.Writer, value Encodable) {
_, err := EncodeTo(w, value)
if err != nil {
panic(err)
}
}

func MustEncode(value Encodable) []byte {
buf, err := Encode(value)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion common/types/hashes.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (h Hash20) String() string {
// ShortString returns a the first 5 characters of the hash, for logging purposes.
func (h Hash20) ShortString() string {
l := len(h.Hex())
return h.Hex()[util.Min(2, l):util.Min(7, l)]
return Shorten(h.Hex()[util.Min(2, l):], 10)
}

// Format implements fmt.Formatter, forcing the byte slice to be formatted as is,
Expand Down
24 changes: 19 additions & 5 deletions common/types/malfeasance.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ func (hp *HareProof) MarshalLogObject(encoder log.ObjectEncoder) error {
return nil
}

func (hp *HareProof) ToMalfeasenceProof() *MalfeasanceProof {
return &MalfeasanceProof{
Layer: hp.Messages[0].InnerMsg.Layer,
Proof: Proof{
Type: HareEquivocation,
Data: hp,
},
}
}

type AtxProofMsg struct {
InnerMsg ATXMetadata

Expand Down Expand Up @@ -235,6 +245,14 @@ func (hm *HareMetadata) Equivocation(other *HareMetadata) bool {
return hm.Layer == other.Layer && hm.Round == other.Round && hm.MsgHash != other.MsgHash
}

func (hm HareMetadata) ToBytes() []byte {
buf, err := codec.Encode(&hm)
if err != nil {
panic(err.Error())
}
return buf
}

type HareProofMsg struct {
InnerMsg HareMetadata

Expand All @@ -244,11 +262,7 @@ type HareProofMsg struct {

// SignedBytes returns the actual data being signed in a HareProofMsg.
func (m *HareProofMsg) SignedBytes() []byte {
data, err := codec.Encode(&m.InnerMsg)
if err != nil {
log.With().Fatal("failed to serialize MultiBlockProposalsMsg", log.Err(err))
}
return data
return m.InnerMsg.ToBytes()
}

func MalfeasanceInfo(smesher NodeID, mp *MalfeasanceProof) string {
Expand Down
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
vm "github.com/spacemeshos/go-spacemesh/genvm"
hareConfig "github.com/spacemeshos/go-spacemesh/hare/config"
eligConfig "github.com/spacemeshos/go-spacemesh/hare/eligibility/config"
"github.com/spacemeshos/go-spacemesh/hare3"
"github.com/spacemeshos/go-spacemesh/p2p"
"github.com/spacemeshos/go-spacemesh/syncer"
timeConfig "github.com/spacemeshos/go-spacemesh/timesync/config"
Expand Down Expand Up @@ -50,6 +51,7 @@ type Config struct {
P2P p2p.Config `mapstructure:"p2p"`
API grpcserver.Config `mapstructure:"api"`
HARE hareConfig.Config `mapstructure:"hare"`
HARE3 hare3.Config `mapstructure:"hare3"`
HareEligibility eligConfig.Config `mapstructure:"hare-eligibility"`
Beacon beacon.Config `mapstructure:"beacon"`
TIME timeConfig.TimeConfig `mapstructure:"time"`
Expand Down Expand Up @@ -140,6 +142,7 @@ func DefaultConfig() Config {
P2P: p2p.DefaultConfig(),
API: grpcserver.DefaultConfig(),
HARE: hareConfig.DefaultConfig(),
HARE3: hare3.DefaultConfig(),
HareEligibility: eligConfig.DefaultConfig(),
Beacon: beacon.DefaultConfig(),
TIME: timeConfig.DefaultConfig(),
Expand Down
10 changes: 10 additions & 0 deletions config/presets/fastnet.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package presets

import (
"math"
"math/big"
"time"

Expand All @@ -24,13 +25,22 @@ func fastnet() config.Config {

conf.BaseConfig.TestConfig.MinerGoodAtxPct = 50

conf.HARE.Disable = 1 // non-zero low layer will prevent hare1 from running
conf.HARE.N = 800
conf.HARE.ExpectedLeaders = 10
conf.HARE.LimitConcurrent = 5
conf.HARE.LimitIterations = 3
conf.HARE.RoundDuration = 2 * time.Second
conf.HARE.WakeupDelta = 3 * time.Second

conf.HARE3.Enable = true
conf.HARE3.DisableLayer = types.LayerID(math.MaxUint32)
conf.HARE3.Committee = 800
conf.HARE3.Leaders = 10
conf.HARE3.PreroundDelay = 3 * time.Second
conf.HARE3.RoundDuration = 500 * time.Millisecond
conf.HARE3.IterationsLimit = 2

conf.P2P.MinPeers = 10

conf.Genesis = &config.GenesisConfig{
Expand Down
9 changes: 8 additions & 1 deletion hare/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package config

import "time"
import (
"time"

"github.com/spacemeshos/go-spacemesh/common/types"
)

// Config is the configuration of the Hare.
type Config struct {
Expand All @@ -13,6 +17,9 @@ type Config struct {
StopAtxGrading uint32 `mapstructure:"stop-atx-grading"`

Hdist uint32

// If set to non-zero value will disable hare starting at that layer.
Disable types.LayerID `mapstructure:"disable"`
}

// DefaultConfig returns the default configuration for the hare.
Expand Down
4 changes: 4 additions & 0 deletions hare/hare.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,10 @@ func (h *Hare) onTick(ctx context.Context, lid types.LayerID) (bool, error) {
h.With().Debug("hare exiting", log.Context(ctx), lid)
return false, nil
}
if h.config.Disable != 0 && lid >= h.config.Disable {
h.With().Debug("hare is disabled at this layer", log.Context(ctx), lid)
return false, nil
}

h.setLastLayer(lid)

Expand Down
29 changes: 29 additions & 0 deletions hare3/compat/result.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package compat

import (
"context"

"go.uber.org/zap"

"github.com/spacemeshos/go-spacemesh/hare"
"github.com/spacemeshos/go-spacemesh/hare3"
)

func ReportResult(ctx context.Context, logger *zap.Logger, from <-chan hare3.ConsensusOutput, to chan<- hare.LayerOutput) {
for {
select {
case <-ctx.Done():
logger.Info("hare3 results reporter exited")
return
case out := <-from:
select {
case to <- hare.LayerOutput{
Ctx: ctx,
Layer: out.Layer,
Proposals: out.Proposals,
}:
case <-ctx.Done():
}
}
}
}
31 changes: 31 additions & 0 deletions hare3/compat/weakcoin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package compat

import (
"context"

"go.uber.org/zap"

"github.com/spacemeshos/go-spacemesh/common/types"
"github.com/spacemeshos/go-spacemesh/hare3"
)

type weakCoin interface {
Set(types.LayerID, bool) error
}

func ReportWeakcoin(ctx context.Context, logger *zap.Logger, from <-chan hare3.WeakCoinOutput, to weakCoin) {
for {
select {
case <-ctx.Done():
logger.Info("weak coin reporter exited")
return
case out := <-from:
if err := to.Set(out.Layer, out.Coin); err != nil {
logger.Error("failed to update weakcoin",
zap.Uint32("lid", out.Layer.Uint32()),
zap.Error(err),
)
}
}
}
}

0 comments on commit 2be4a0f

Please sign in to comment.