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  }