github.com/celestiaorg/celestia-node@v0.15.0-beta.1/nodebuilder/tests/fraud_test.go (about) 1 //go:build fraud || integration 2 3 package tests 4 5 import ( 6 "context" 7 "testing" 8 "time" 9 10 "github.com/ipfs/go-datastore" 11 ds_sync "github.com/ipfs/go-datastore/sync" 12 "github.com/libp2p/go-libp2p/core/host" 13 "github.com/libp2p/go-libp2p/core/peer" 14 "github.com/stretchr/testify/require" 15 "github.com/tendermint/tendermint/types" 16 "go.uber.org/fx" 17 18 "github.com/celestiaorg/go-fraud" 19 20 "github.com/celestiaorg/celestia-node/header" 21 headerfraud "github.com/celestiaorg/celestia-node/header/headertest/fraud" 22 "github.com/celestiaorg/celestia-node/nodebuilder" 23 "github.com/celestiaorg/celestia-node/nodebuilder/core" 24 "github.com/celestiaorg/celestia-node/nodebuilder/node" 25 "github.com/celestiaorg/celestia-node/nodebuilder/tests/swamp" 26 "github.com/celestiaorg/celestia-node/share/eds" 27 "github.com/celestiaorg/celestia-node/share/eds/byzantine" 28 ) 29 30 /* 31 Test-Case: Full Node will propagate a fraud proof to the network, once ByzantineError will be received from sampling. 32 Pre-Requisites: 33 - CoreClient is started by swamp. 34 Steps: 35 1. Create a Bridge Node(BN) with broken extended header at height 10. 36 2. Start a BN. 37 3. Create a Full Node(FN) with a connection to BN as a trusted peer. 38 4. Start a FN. 39 5. Subscribe to a fraud proof and wait when it will be received. 40 6. Check FN is not synced to 15. 41 Note: 15 is not available because DASer/Syncer will be stopped 42 before reaching this height due to receiving a fraud proof. 43 Another note: this test disables share exchange to speed up test results. 44 7. Spawn a Light Node(LN) in order to sync a BEFP. 45 8. Ensure that the BEFP was received. 46 9. Try to start a Full Node(FN) that contains a BEFP in its store. 47 */ 48 func TestFraudProofHandling(t *testing.T) { 49 ctx, cancel := context.WithTimeout(context.Background(), swamp.DefaultTestTimeout) 50 t.Cleanup(cancel) 51 52 const ( 53 blocks = 15 54 blockSize = 4 55 blockTime = time.Second 56 ) 57 58 sw := swamp.NewSwamp(t, swamp.WithBlockTime(blockTime)) 59 fillDn := swamp.FillBlocks(ctx, sw.ClientContext, sw.Accounts, blockSize, blocks) 60 set, val := sw.Validators(t) 61 fMaker := headerfraud.NewFraudMaker(t, 10, []types.PrivValidator{val}, set) 62 63 storeCfg := eds.DefaultParameters() 64 ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) 65 edsStore, err := eds.NewStore(storeCfg, t.TempDir(), ds) 66 require.NoError(t, err) 67 require.NoError(t, edsStore.Start(ctx)) 68 t.Cleanup(func() { 69 _ = edsStore.Stop(ctx) 70 }) 71 72 cfg := nodebuilder.DefaultConfig(node.Bridge) 73 // 1. 74 bridge := sw.NewNodeWithConfig( 75 node.Bridge, 76 cfg, 77 core.WithHeaderConstructFn(fMaker.MakeExtendedHeader(16, edsStore)), 78 fx.Replace(edsStore), 79 ) 80 // 2. 81 err = bridge.Start(ctx) 82 require.NoError(t, err) 83 84 // 3. 85 cfg = nodebuilder.DefaultConfig(node.Full) 86 addrs, err := peer.AddrInfoToP2pAddrs(host.InfoFromHost(bridge.Host)) 87 require.NoError(t, err) 88 cfg.Header.TrustedPeers = append(cfg.Header.TrustedPeers, addrs[0].String()) 89 cfg.Share.UseShareExchange = false 90 store := nodebuilder.MockStore(t, cfg) 91 full := sw.NewNodeWithStore(node.Full, store) 92 93 // 4. 94 err = full.Start(ctx) 95 require.NoError(t, err) 96 97 fullClient := getAdminClient(ctx, full, t) 98 99 // 5. 100 subCtx, subCancel := context.WithCancel(ctx) 101 subscr, err := fullClient.Fraud.Subscribe(subCtx, byzantine.BadEncoding) 102 require.NoError(t, err) 103 select { 104 case p := <-subscr: 105 require.Equal(t, 10, int(p.Height())) 106 t.Log("Caught the proof....") 107 subCancel() 108 case <-ctx.Done(): 109 subCancel() 110 t.Fatal("full node did not receive a fraud proof in time") 111 } 112 113 getCtx, getCancel := context.WithTimeout(ctx, time.Second) 114 proofs, err := fullClient.Fraud.Get(getCtx, byzantine.BadEncoding) 115 getCancel() 116 117 require.NoError(t, err) 118 require.Len(t, proofs, 1) 119 require.True(t, proofs[0].Type() == byzantine.BadEncoding) 120 // This is an obscure way to check if the Syncer was stopped. 121 // If we cannot get a height header within a timeframe it means the syncer was stopped 122 // FIXME: Eventually, this should be a check on service registry managing and keeping 123 // lifecycles of each Module. 124 // 6. 125 // random height after befp.height 126 height := uint64(15) 127 // initial timeout is set to 5 sec, as we are targeting the height=15, 128 // blockTime=1 sec, expected befp.height=10 129 timeOut := blockTime * 5 130 // during befp validation the node can still receive headers and it mostly depends on 131 // the operating system or hardware(e.g. on macOS tests is working 100% time with a single 132 // height=15, and on the Linux VM sometimes the last height is 17-18). So, lets give a chance for 133 // our befp validator to check the fraud proof and stop the syncer. 134 for height < 20 { 135 syncCtx, syncCancel := context.WithTimeout(context.Background(), timeOut) 136 _, err = full.HeaderServ.WaitForHeight(syncCtx, height) 137 syncCancel() 138 if err != nil { 139 break 140 } 141 timeOut = blockTime 142 height++ 143 } 144 require.ErrorIs(t, err, context.DeadlineExceeded) 145 146 // 7. 147 cfg = nodebuilder.DefaultConfig(node.Light) 148 cfg.Header.TrustedPeers = append(cfg.Header.TrustedPeers, addrs[0].String()) 149 lnStore := nodebuilder.MockStore(t, cfg) 150 light := sw.NewNodeWithStore(node.Light, lnStore) 151 require.NoError(t, light.Start(ctx)) 152 lightClient := getAdminClient(ctx, light, t) 153 154 // 8. 155 subCtx, subCancel = context.WithCancel(ctx) 156 subscr, err = lightClient.Fraud.Subscribe(subCtx, byzantine.BadEncoding) 157 require.NoError(t, err) 158 select { 159 case p := <-subscr: 160 require.Equal(t, 10, int(p.Height())) 161 subCancel() 162 case <-ctx.Done(): 163 subCancel() 164 t.Fatal("light node did not receive a fraud proof in time") 165 } 166 167 // 9. 168 fN := sw.NewNodeWithStore(node.Full, store) 169 err = fN.Start(ctx) 170 var fpExist *fraud.ErrFraudExists[*header.ExtendedHeader] 171 require.ErrorAs(t, err, &fpExist) 172 173 sw.StopNode(ctx, bridge) 174 sw.StopNode(ctx, full) 175 sw.StopNode(ctx, light) 176 require.NoError(t, <-fillDn) 177 }