code.vegaprotocol.io/vega@v0.79.0/core/pow/snapshot_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package pow 17 18 import ( 19 "bytes" 20 "context" 21 "testing" 22 "time" 23 24 "code.vegaprotocol.io/vega/core/blockchain/abci" 25 "code.vegaprotocol.io/vega/core/integration/stubs" 26 "code.vegaprotocol.io/vega/core/pow/mocks" 27 "code.vegaprotocol.io/vega/core/snapshot" 28 "code.vegaprotocol.io/vega/core/stats" 29 "code.vegaprotocol.io/vega/core/types" 30 vgcontext "code.vegaprotocol.io/vega/libs/context" 31 "code.vegaprotocol.io/vega/libs/crypto" 32 "code.vegaprotocol.io/vega/libs/num" 33 "code.vegaprotocol.io/vega/libs/proto" 34 vgtest "code.vegaprotocol.io/vega/libs/test" 35 "code.vegaprotocol.io/vega/logging" 36 "code.vegaprotocol.io/vega/paths" 37 snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1" 38 39 "github.com/golang/mock/gomock" 40 "github.com/stretchr/testify/require" 41 ) 42 43 func TestConversions(t *testing.T) { 44 p := &types.PayloadProofOfWork{ 45 BlockHeight: []uint64{100, 101, 102}, 46 BlockHash: []string{"94A9CB1532011081B013CCD8E6AAA832CAB1CBA603F0C5A093B14C4961E5E7F0", "DC911C0EA95545441F3E1182DD25D973764395A7E75CBDBC086F1C6F7075AED6", "2E4F2967AA904F9A952BB4813EC6BBB3730B9FFFEC44106B89F0A1958547733C"}, 47 HeightToTx: map[uint64][]string{100: {"1", "2"}, 101: {"3"}}, 48 HeightToTid: map[uint64][]string{100: {"100", "200"}, 101: {"300"}}, 49 } 50 51 pp := p.IntoProto() 52 require.Equal(t, 3, len(pp.ProofOfWork.BlockHeight)) 53 require.Equal(t, 3, len(pp.ProofOfWork.BlockHash)) 54 for i, v := range p.BlockHeight { 55 require.Equal(t, v, pp.ProofOfWork.BlockHeight[i]) 56 } 57 for i, v := range p.BlockHash { 58 require.Equal(t, v, pp.ProofOfWork.BlockHash[i]) 59 } 60 require.Equal(t, 2, len(pp.ProofOfWork.TxAtHeight)) 61 require.Equal(t, 2, len(pp.ProofOfWork.TidAtHeight)) 62 require.Equal(t, uint64(100), pp.ProofOfWork.TxAtHeight[0].Height) 63 require.Equal(t, uint64(101), pp.ProofOfWork.TxAtHeight[1].Height) 64 require.Equal(t, uint64(100), pp.ProofOfWork.TidAtHeight[0].Height) 65 require.Equal(t, uint64(101), pp.ProofOfWork.TidAtHeight[1].Height) 66 require.Equal(t, 2, len(pp.ProofOfWork.TxAtHeight[0].Transactions)) 67 require.Equal(t, 2, len(pp.ProofOfWork.TidAtHeight[0].Transactions)) 68 require.Equal(t, "1", pp.ProofOfWork.TxAtHeight[0].Transactions[0]) 69 require.Equal(t, "2", pp.ProofOfWork.TxAtHeight[0].Transactions[1]) 70 require.Equal(t, "3", pp.ProofOfWork.TxAtHeight[1].Transactions[0]) 71 require.Equal(t, "100", pp.ProofOfWork.TidAtHeight[0].Transactions[0]) 72 require.Equal(t, "200", pp.ProofOfWork.TidAtHeight[0].Transactions[1]) 73 require.Equal(t, "300", pp.ProofOfWork.TidAtHeight[1].Transactions[0]) 74 75 ppp := types.PayloadProofOfWorkFromProto(pp) 76 77 require.Equal(t, 3, len(ppp.BlockHeight)) 78 require.Equal(t, 3, len(ppp.BlockHash)) 79 for i, v := range ppp.BlockHeight { 80 require.Equal(t, v, pp.ProofOfWork.BlockHeight[i]) 81 } 82 for i, v := range ppp.BlockHash { 83 require.Equal(t, v, pp.ProofOfWork.BlockHash[i]) 84 } 85 require.Equal(t, 2, len(ppp.HeightToTx)) 86 require.Equal(t, 2, len(ppp.HeightToTid)) 87 require.Equal(t, 2, len(ppp.HeightToTx[100])) 88 require.Equal(t, 2, len(ppp.HeightToTid[100])) 89 require.Equal(t, "1", ppp.HeightToTx[100][0]) 90 require.Equal(t, "2", ppp.HeightToTx[100][1]) 91 require.Equal(t, "100", ppp.HeightToTid[100][0]) 92 require.Equal(t, "200", ppp.HeightToTid[100][1]) 93 require.Equal(t, 1, len(ppp.HeightToTx[101])) 94 require.Equal(t, 1, len(ppp.HeightToTid[101])) 95 require.Equal(t, "3", ppp.HeightToTx[101][0]) 96 require.Equal(t, "300", ppp.HeightToTid[101][0]) 97 } 98 99 func TestSnapshot(t *testing.T) { 100 ts := mocks.NewMockTimeService(gomock.NewController(t)) 101 ts.EXPECT().GetTimeNow().AnyTimes().Return(time.Now()) 102 e := New(logging.NewTestLogger(), NewDefaultConfig()) 103 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(100)) 104 e.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20)) 105 e.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3) 106 e.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(1)) 107 e.UpdateSpamPoWIncreasingDifficulty(context.Background(), num.NewUint(1)) 108 109 e.BeginBlock(100, "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", []abci.Tx{}) 110 111 // add a new set of configuration which becomes active at block 100 112 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(200)) 113 e.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(25)) 114 e.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3) 115 e.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(2)) 116 e.UpdateSpamPoWIncreasingDifficulty(context.Background(), num.NewUint(0)) 117 118 party := crypto.RandomHash() 119 120 txs := []abci.Tx{ 121 &testTx{txID: "1", party: party, blockHeight: 100, powTxID: "DFE522E234D67E6AE3F017859F898E576B3928EA57310B765398615A0D3FDE2F", powNonce: 424517}, 122 &testTx{txID: "2", party: party, blockHeight: 100, powTxID: "5B0E1EB96CCAC120E6D824A5F4C4007EABC59573B861BD84B1EF09DFB376DC84", powNonce: 4031737}, 123 &testTx{txID: "3", party: party, blockHeight: 100, powTxID: "94A9CB1532011081B013CCD8E6AAA832CAB1CBA603F0C5A093B14C4961E5E7F0", powNonce: 431336}, 124 } 125 126 e.BeginBlock(101, "2E289FB9CEF7234E2C08F34CCD66B330229067CE47E22F76EF0595B3ABA9968F", txs) 127 e.BeginBlock(102, "2E289FB9CEF7234E2C08F34CCD66B330229067CE47E22F76EF0595B3ABA9968F", []abci.Tx{}) 128 129 key := (&types.PayloadProofOfWork{}).Key() 130 state1, _, err := e.GetState(key) 131 require.NoError(t, err) 132 133 eLoaded := New(logging.NewTestLogger(), NewDefaultConfig()) 134 eLoaded.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(1)) 135 eLoaded.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20)) 136 eLoaded.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3) 137 eLoaded.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(1)) 138 139 pl := snapshotpb.Payload{} 140 require.NoError(t, proto.Unmarshal(state1, &pl)) 141 eLoaded.LoadState(context.Background(), types.PayloadFromProto(&pl)) 142 143 state2, _, err := eLoaded.GetState(key) 144 require.NoError(t, err) 145 require.True(t, bytes.Equal(state1, state2)) 146 147 require.Equal(t, 2, len(eLoaded.activeParams)) 148 } 149 150 func TestSnapshotViaEngine(t *testing.T) { 151 ctx := vgtest.VegaContext("chainid", 100) 152 ts := mocks.NewMockTimeService(gomock.NewController(t)) 153 ts.EXPECT().GetTimeNow().AnyTimes().Return(time.Now()) 154 powEngine1 := New(logging.NewTestLogger(), NewDefaultConfig()) 155 now := time.Now() 156 log := logging.NewTestLogger() 157 timeService := stubs.NewTimeStub() 158 timeService.SetTime(now) 159 statsData := stats.New(log, stats.NewDefaultConfig()) 160 config := snapshot.DefaultConfig() 161 vegaPath := paths.New(t.TempDir()) 162 snapshotEngine1, err := snapshot.NewEngine(vegaPath, config, log, timeService, statsData.Blockchain) 163 require.NoError(t, err) 164 snapshotEngine1CloseFn := vgtest.OnlyOnce(snapshotEngine1.Close) 165 defer snapshotEngine1CloseFn() 166 167 snapshotEngine1.AddProviders(powEngine1) 168 169 require.NoError(t, snapshotEngine1.Start(ctx)) 170 require.NoError(t, powEngine1.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(2))) 171 require.NoError(t, powEngine1.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20))) 172 require.NoError(t, powEngine1.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3)) 173 require.NoError(t, powEngine1.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(1))) 174 require.NoError(t, powEngine1.UpdateSpamPoWIncreasingDifficulty(context.Background(), num.NewUint(1))) 175 176 powEngine1.BeginBlock(99, "377EEAC9847D751A4FAFD3F2896E99C1A03363EBDA3036C33940CFE578E196D1", []abci.Tx{}) 177 powEngine1.BeginBlock(100, "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", []abci.Tx{}) 178 179 party := crypto.RandomHash() 180 181 txs := []abci.Tx{ 182 &testTx{txID: "1", party: party, blockHeight: 100, powTxID: "DFE522E234D67E6AE3F017859F898E576B3928EA57310B765398615A0D3FDE2F", powNonce: 424517}, 183 &testTx{txID: "2", party: party, blockHeight: 100, powTxID: "5B0E1EB96CCAC120E6D824A5F4C4007EABC59573B861BD84B1EF09DFB376DC84", powNonce: 4031737}, 184 &testTx{txID: "3", party: party, blockHeight: 100, powTxID: "94A9CB1532011081B013CCD8E6AAA832CAB1CBA603F0C5A093B14C4961E5E7F0", powNonce: 431336}, 185 // add another transaction from the same party with reduced difficulty but from another block 186 &testTx{txID: "4", party: party, blockHeight: 99, powTxID: "4633a4d29f543cdd9afe7555c352179063d1ead0c778d246fabfc4c6f8adf031", powNonce: 2646611}, 187 } 188 189 powEngine1.BeginBlock(101, "2E289FB9CEF7234E2C08F34CCD66B330229067CE47E22F76EF0595B3ABA9968F", txs) 190 powEngine1.BeginBlock(102, "2E289FB9CEF7234E2C08F34CCD66B330229067CE47E22F76EF0595B3ABA9968F", []abci.Tx{}) 191 192 require.NoError(t, powEngine1.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(2))) 193 require.NoError(t, powEngine1.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(25))) 194 require.NoError(t, powEngine1.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3)) 195 require.NoError(t, powEngine1.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(5))) 196 require.NoError(t, powEngine1.UpdateSpamPoWIncreasingDifficulty(context.Background(), num.NewUint(0))) 197 198 ctx = vgcontext.WithTraceID(vgcontext.WithBlockHeight(context.Background(), 102), "0xDEADBEEF") 199 ctx = vgcontext.WithChainID(ctx, "chainid") 200 hash1, err := snapshotEngine1.SnapshotNow(ctx) 201 require.NoError(t, err) 202 state1 := map[string][]byte{} 203 for _, key := range powEngine1.Keys() { 204 state, additionalProvider, err := powEngine1.GetState(key) 205 require.NoError(t, err) 206 require.Empty(t, additionalProvider) 207 state1[key] = state 208 } 209 210 snapshotEngine1CloseFn() 211 212 tsLoaded := mocks.NewMockTimeService(gomock.NewController(t)) 213 tsLoaded.EXPECT().GetTimeNow().AnyTimes().Return(time.Now()) 214 powEngine2 := New(logging.NewTestLogger(), NewDefaultConfig()) 215 timeServiceLoaded := stubs.NewTimeStub() 216 timeServiceLoaded.SetTime(now) 217 snapshotEngine2, err := snapshot.NewEngine(vegaPath, config, log, timeServiceLoaded, statsData.Blockchain) 218 require.NoError(t, err) 219 defer snapshotEngine2.Close() 220 221 snapshotEngine2.AddProviders(powEngine2) 222 223 // This triggers the state restoration from the local snapshot. 224 require.NoError(t, snapshotEngine2.Start(ctx)) 225 226 // Comparing the hash after restoration, to ensure it produces the same result. 227 hash2, _, _ := snapshotEngine2.Info() 228 require.Equal(t, hash1, hash2) 229 230 state2 := map[string][]byte{} 231 for _, key := range powEngine2.Keys() { 232 state, additionalProvider, err := powEngine2.GetState(key) 233 require.NoError(t, err) 234 require.Empty(t, additionalProvider) 235 state2[key] = state 236 } 237 238 for key := range state1 { 239 require.Equalf(t, state1[key], state2[key], "Key %q does not have the same data", key) 240 } 241 }