44 "context"
55 "fmt"
66 "math/big"
7- "os "
7+ "sync/atomic "
88 "testing"
99 "time"
1010
@@ -14,7 +14,10 @@ import (
1414 "github.com/filecoin-project/go-f3/gpbft"
1515 "github.com/filecoin-project/go-f3/manifest"
1616 "github.com/filecoin-project/go-f3/sim/signing"
17- leveldb "github.com/ipfs/go-ds-leveldb"
17+
18+ "github.com/ipfs/go-datastore"
19+ "github.com/ipfs/go-datastore/failstore"
20+ ds_sync "github.com/ipfs/go-datastore/sync"
1821 pubsub "github.com/libp2p/go-libp2p-pubsub"
1922 "github.com/libp2p/go-libp2p/core/host"
2023 "github.com/libp2p/go-libp2p/core/peer"
@@ -79,6 +82,37 @@ func TestPauseResumeCatchup(t *testing.T) {
7982 env .waitForInstanceNumber (node0failInstance + 3 , 30 * time .Second , false )
8083}
8184
85+ func TestFailRecover (t * testing.T ) {
86+ env := newTestEnvironment (t , 2 , false )
87+
88+ // Make it possible to fail a single write for node 0.
89+ var failDsWrite atomic.Bool
90+ dsFailureFunc := func (op string ) error {
91+ if failDsWrite .Load () {
92+ switch op {
93+ case "put" , "batch-put" :
94+ failDsWrite .Store (false )
95+ return xerrors .Errorf ("FAILURE!" )
96+ }
97+ }
98+ return nil
99+ }
100+
101+ env .injectDatastoreFailures (0 , dsFailureFunc )
102+
103+ env .connectAll ()
104+ env .start ()
105+ env .waitForInstanceNumber (1 , 10 * time .Second , true )
106+
107+ // Inject a single write failure. This should prevent us from storing a single decision
108+ // decision.
109+ failDsWrite .Store (true )
110+
111+ // We should proceed anyways (catching up via the certificate exchange protocol).
112+ oldInstance := env .nodes [0 ].currentGpbftInstance ()
113+ env .waitForInstanceNumber (oldInstance + 3 , 10 * time .Second , true )
114+ }
115+
82116func TestDynamicManifest_WithoutChanges (t * testing.T ) {
83117 env := newTestEnvironment (t , 2 , true )
84118
@@ -182,9 +216,10 @@ var base manifest.Manifest = manifest.Manifest{
182216}
183217
184218type testNode struct {
185- e * testEnv
186- h host.Host
187- f3 * f3.F3
219+ e * testEnv
220+ h host.Host
221+ f3 * f3.F3
222+ dsErrFunc func (string ) error
188223}
189224
190225func (n * testNode ) currentGpbftInstance () uint64 {
@@ -486,20 +521,19 @@ func (e *testEnv) newF3Instance(id int, manifestServer peer.ID) (*testNode, erro
486521 return nil , xerrors .Errorf ("creating libp2p host: %w" , err )
487522 }
488523
524+ n := & testNode {e : e , h : h }
525+
489526 ps , err := pubsub .NewGossipSub (e .testCtx , h )
490527 if err != nil {
491528 return nil , xerrors .Errorf ("creating gossipsub: %w" , err )
492529 }
493530
494- tmpdir , err := os .MkdirTemp ("" , "f3-*" )
495- if err != nil {
496- return nil , xerrors .Errorf ("creating temp dir: %w" , err )
497- }
498-
499- ds , err := leveldb .NewDatastore (tmpdir , nil )
500- if err != nil {
501- return nil , xerrors .Errorf ("creating a datastore: %w" , err )
502- }
531+ ds := ds_sync .MutexWrap (failstore .NewFailstore (datastore .NewMapDatastore (), func (s string ) error {
532+ if n .dsErrFunc != nil {
533+ return (n .dsErrFunc )(s )
534+ }
535+ return nil
536+ }))
503537
504538 m := e .manifest // copy because we mutate this
505539 var mprovider manifest.ManifestProvider
@@ -511,16 +545,20 @@ func (e *testEnv) newF3Instance(id int, manifestServer peer.ID) (*testNode, erro
511545
512546 e .signingBackend .Allow (int (id ))
513547
514- module , err : = f3 .New (e .testCtx , mprovider , ds , h , ps , e .signingBackend , e .ec )
548+ n . f3 , err = f3 .New (e .testCtx , mprovider , ds , h , ps , e .signingBackend , e .ec )
515549 if err != nil {
516550 return nil , xerrors .Errorf ("creating module: %w" , err )
517551 }
518552
519553 e .errgrp .Go (func () error {
520- return runMessageSubscription (e .testCtx , module , gpbft .ActorID (id ), e .signingBackend )
554+ return runMessageSubscription (e .testCtx , n . f3 , gpbft .ActorID (id ), e .signingBackend )
521555 })
522556
523- return & testNode {e : e , h : h , f3 : module }, nil
557+ return n , nil
558+ }
559+
560+ func (e * testEnv ) injectDatastoreFailures (i int , fn func (op string ) error ) {
561+ e .nodes [i ].dsErrFunc = fn
524562}
525563
526564// TODO: This code is copy-pasta from cmd/f3/run.go, consider taking it out into a shared testing lib.
0 commit comments