Skip to content

Commit

Permalink
Merge branch 'main' into uci/copy-templates
Browse files Browse the repository at this point in the history
  • Loading branch information
masih authored Jul 5, 2024
2 parents 4d7b7e9 + 3f20d36 commit 217e335
Show file tree
Hide file tree
Showing 13 changed files with 127 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ jobs:
# Run tests with race detector, excluding fuzz tests.
# Fuzz tests are excluded because they will otherwise
# take too long to complete.
GOTEST_ARGS: -race -run='^[^Fuzz]' -timeout=20m
GOTEST_ARGS: -race -run='^[^Fuzz]' -short -timeout=20m
run: make test

generate:
Expand Down
52 changes: 44 additions & 8 deletions certstore/certstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/Kubuxu/go-broadcast"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
"github.com/ipfs/go-datastore/query"
"golang.org/x/xerrors"
)

Expand Down Expand Up @@ -45,6 +46,10 @@ func open(ctx context.Context, ds datastore.Datastore) (*Store, error) {
ds: namespace.Wrap(ds, datastore.NewKey("/certstore")),
powerTableFrequency: defaultPowerTableFrequency,
}
err := maybeContinueDelete(ctx, ds)
if err != nil {
return nil, xerrors.Errorf("continuing deletion: %w", err)
}

latestInstance, err := cs.readInstanceNumber(ctx, certStoreLatestKey)
if errors.Is(err, datastore.ErrNotFound) {
Expand Down Expand Up @@ -142,7 +147,7 @@ func CreateStore(ctx context.Context, ds datastore.Datastore, firstInstance uint

// OpenStore opens an existing certificate store.
// The passed Datastore has to be thread safe.
// Returns ErrCertstoreNotInitialized if the CertStore does not exist
// Returns ErrNotInitialized if the CertStore does not exist
func OpenStore(ctx context.Context, ds datastore.Datastore) (*Store, error) {
cs, err := open(ctx, ds)
if err != nil {
Expand Down Expand Up @@ -411,17 +416,48 @@ func (cs *Store) SubscribeForNewCerts(ch chan<- *certs.FinalityCertificate) (las
return cs.busCerts.Subscribe(ch)
}

var tombstoneKey = datastore.NewKey("/tombstone")

func maybeContinueDelete(ctx context.Context, ds datastore.Datastore) error {
ok, err := ds.Has(ctx, tombstoneKey)
if err != nil {
return xerrors.Errorf("checking tombstoneKey: %w", err)
}
if !ok {
// no tombstone, exit
return nil
}

qr, err := ds.Query(ctx, query.Query{KeysOnly: true})
if err != nil {
return xerrors.Errorf("starting a query for certs: %w", err)
}
for r := range qr.Next() {
key := datastore.NewKey(r.Key)
if key == tombstoneKey {
continue
}
err := ds.Delete(ctx, key)
if err != nil {
return xerrors.Errorf("error while deleting: %w", err)
}
}
err = ds.Delete(ctx, tombstoneKey)
if err != nil {
return xerrors.Errorf("error while deleting tombstone: %w", err)
}
return nil
}

// DeleteAll is used to remove all certificates from the store and clean it for a new instance
// to be able to use it from scratch.
func (cs *Store) DeleteAll(ctx context.Context) error {
// TODO: Right now we don't clear the content of certs, we just remove the pointers to the
// latest instance and certs. Removing all the certificates requires iterating
// through all the namespace datastore. Leaving it as future work.
// Tracked in: https://github.com/filecoin-project/go-f3/issues/376
if err := cs.ds.Delete(ctx, certStoreFirstKey); err != nil {
return err
err := cs.ds.Put(ctx, tombstoneKey, []byte("tombstone"))
if err != nil {
return xerrors.Errorf("creating a tombstone: %w", err)
}
return cs.ds.Delete(ctx, certStoreLatestKey)

return maybeContinueDelete(ctx, cs.ds)
}

// Delete removes all asset belonging to an instance.
Expand Down
40 changes: 40 additions & 0 deletions certstore/certstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/filecoin-project/go-f3/certs"
"github.com/filecoin-project/go-f3/gpbft"
datastore "github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/query"
ds_sync "github.com/ipfs/go-datastore/sync"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -124,6 +125,45 @@ func TestGetRange(t *testing.T) {
require.Len(t, certs, 5)
}

func TestDeleteAll(t *testing.T) {
ctx := context.Background()
ds := ds_sync.MutexWrap(datastore.NewMapDatastore())

pt, ptCid := testPowerTable(10)
supp := gpbft.SupplementalData{PowerTable: ptCid}
cs, err := CreateStore(ctx, ds, 1, pt)
require.NoError(t, err)

for i := uint64(1); i <= 5; i++ {
cert := &certs.FinalityCertificate{GPBFTInstance: i, SupplementalData: supp}
err := cs.Put(ctx, cert)
require.NoError(t, err)
}

require.NoError(t, cs.DeleteAll(ctx))
_, err = OpenStore(ctx, ds)
require.ErrorIs(t, err, ErrNotInitialized)

verifyEmpty := func() {
res, err := ds.Query(ctx, query.Query{})
require.NoError(t, err)
_, ok := res.NextSync()
require.False(t, ok, "there should be nothing in the datastore")
}
verifyEmpty()

// create it again
_, err = CreateStore(ctx, ds, 1, pt)
require.NoError(t, err)

// put in the tombstone, as if we crashed during DeleteAll
require.NoError(t, ds.Put(ctx, tombstoneKey, []byte("AAAAAAAA")))
// re-opening should delete and fail
_, err = OpenStore(ctx, ds)
require.ErrorIs(t, err, ErrNotInitialized)
verifyEmpty()
}

func TestSubscribeForNewCerts(t *testing.T) {
ctx := context.Background()
ds := ds_sync.MutexWrap(datastore.NewMapDatastore())
Expand Down
6 changes: 5 additions & 1 deletion ec/fake_ec.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ func (ec *FakeEC) genTipset(epoch int64) *Tipset {
rng := h.Sum(nil)
var size uint8
size, rng = rng[0]%8, rng[1:]
_ = rng
if size == 0 {
// if tipset is empty, try again to reduce change for empty tipset
// from 12.5% to 1.5%
size = rng[0] % 8
}
tsk := make([]byte, 0, size*gpbft.CID_MAX_LEN)

if size == 0 {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/filecoin-project/go-f3

go 1.21
go 1.21.0

require (
github.com/Kubuxu/go-broadcast v0.0.0-20240621161059-1a8c90734cd6
Expand Down
7 changes: 6 additions & 1 deletion host.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,15 @@ func (h *gpbftHost) ReceiveDecision(decision *gpbft.Justification) time.Time {
// we decided on something new, the tipset that got finalized can at minimum be 30-60s old.
return ts.Timestamp().Add(ecDelay)
}
if decision.Vote.Instance == manifest.InitialInstance {
// if we are at initial instance, there is no history to look at
return ts.Timestamp().Add(ecDelay)
}
backoffTable := manifest.BaseDecisionBackoffTable

attempts := 0
backoffMultipler := 1.0 // to account for the one ECDelay after which we got the base decistion
backoffMultipler := 1.0 // to account for the one ECDelay after which we got the base decision
// instance - 1 is safe due to check above
for instance := decision.Vote.Instance - 1; instance > manifest.InitialInstance; instance-- {
cert, err := h.client.certStore.Get(h.runningCtx, instance)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions test/drop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
)

func TestDrop_ReachesConsensusDespiteMessageLoss(t *testing.T) {
SkipInRaceMode(t)
t.Parallel()
const (
instanceCount = 5000
Expand Down
4 changes: 2 additions & 2 deletions test/f3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,9 @@ var base manifest.Manifest = manifest.Manifest{
ECFinality: 10,
CommiteeLookback: 5,
// increased delay and period to accelerate test times.
ECPeriod: 500 * time.Millisecond,
ECPeriod: 100 * time.Millisecond,
ECDelayMultiplier: 1.0,
BaseDecisionBackoffTable: []float64{1.3, 1.69, 2.2, 2.86, 3.71, 4.83, 6.27, 8.16, 10.6, 13.79, 15.},
BaseDecisionBackoffTable: []float64{1., 1.2},
},
}

Expand Down
2 changes: 1 addition & 1 deletion test/honest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func TestHonest_Disagreement(t *testing.T) {
t.Parallel()
maxParticipantCases := 10
if testing.Short() {
// Reduce max participants to 1
// Reduce max participants to 5
maxParticipantCases = 5
}

Expand Down
3 changes: 2 additions & 1 deletion test/multi_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

// TestHonestMultiInstance_Agreement tests for multiple chained instances of the protocol with no adversaries.
func TestHonestMultiInstance_Agreement(t *testing.T) {
SkipInRaceMode(t)
t.Parallel()
const (
instanceCount = 4000
Expand Down Expand Up @@ -86,7 +87,7 @@ func FuzzHonestMultiInstance_SyncAgreement(f *testing.F) {

func FuzzHonestMultiInstance_AsyncAgreement(f *testing.F) {
const (
instanceCount = 4000
instanceCount = 2000
honestCount = 4
)
f.Add(-7)
Expand Down
4 changes: 4 additions & 0 deletions test/spam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ import (
)

func TestSpamAdversary(t *testing.T) {
SkipInRaceMode(t)
t.Parallel()
const (
instanceCount = 2000
maxRounds = 30
)
honestCounts := []int{3, 4, 5, 6, 7, 8, 9}
if testing.Short() {
honestCounts = []int{3, 4, 5}
}
tests := []struct {
name string
maxLookaheadRounds uint64
Expand Down
9 changes: 9 additions & 0 deletions test/util_norace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build !race
// +build !race

package test

import "testing"

func SkipInRaceMode(*testing.T) {
}
11 changes: 11 additions & 0 deletions test/util_race_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//go:build race
// +build race

package test

import "testing"

func SkipInRaceMode(t *testing.T) {
t.Helper()
t.Skip("skipping in -race mode")
}

0 comments on commit 217e335

Please sign in to comment.