code.vegaprotocol.io/vega@v0.79.0/core/validators/erc20multisig/topology_snapshot_state_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 erc20multisig_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/hex"
    22  	"testing"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/core/types"
    26  	"code.vegaprotocol.io/vega/core/validators"
    27  	"code.vegaprotocol.io/vega/libs/crypto"
    28  	"code.vegaprotocol.io/vega/libs/proto"
    29  	snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    30  
    31  	"github.com/golang/mock/gomock"
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  func TestERC20TopologySnapshotEmpty(t *testing.T) {
    37  	top := getTestTopology(t)
    38  	defer top.ctrl.Finish()
    39  
    40  	top.OnTick(context.Background(), time.Unix(10, 0))
    41  	// first set the threshold and 1 validator
    42  
    43  	// Let's create threshold
    44  	// first assert we have no threshold
    45  	assert.Equal(t, top.GetThreshold(), uint32(0))
    46  
    47  	stateVerified, _, err := top.GetState((&types.PayloadERC20MultiSigTopologyVerified{}).Key())
    48  	assert.NoError(t, err)
    49  	assert.NotNil(t, stateVerified)
    50  
    51  	snap := &snapshotpb.Payload{}
    52  	err = proto.Unmarshal(stateVerified, snap)
    53  	require.Nil(t, err)
    54  
    55  	snapTop := getTestTopology(t)
    56  	defer snapTop.ctrl.Finish()
    57  
    58  	snapTop.LoadState(context.Background(), types.PayloadFromProto(snap))
    59  	state2, _, err := snapTop.GetState((&types.PayloadERC20MultiSigTopologyVerified{}).Key())
    60  	assert.NoError(t, err)
    61  	assert.NotNil(t, state2)
    62  	assert.True(t, bytes.Equal(stateVerified, state2))
    63  }
    64  
    65  func TestERC20TopologySnapshot(t *testing.T) {
    66  	top := getTestTopology(t)
    67  	defer top.ctrl.Finish()
    68  
    69  	top.ocv.EXPECT().GetMultiSigAddress().AnyTimes()
    70  	top.ethEventSource.EXPECT().UpdateContractBlock(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
    71  	top.OnTick(context.Background(), time.Unix(10, 0))
    72  	// first set the threshold and 1 validator
    73  
    74  	// Let's create threshold
    75  	// first assert we have no threshold
    76  	assert.Equal(t, top.GetThreshold(), uint32(0))
    77  
    78  	thresholdEvent1 := types.SignerThresholdSetEvent{
    79  		Threshold:   666,
    80  		BlockNumber: 10,
    81  		LogIndex:    11,
    82  		TxHash:      "0xacbde",
    83  		ID:          "someid",
    84  		Nonce:       "123",
    85  		BlockTime:   123456789,
    86  	}
    87  
    88  	var cb func(interface{}, bool)
    89  	var res validators.Resource
    90  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
    91  		cb = f
    92  		res = r
    93  		return nil
    94  	})
    95  
    96  	assert.NoError(t, top.ProcessThresholdEvent(&thresholdEvent1))
    97  
    98  	// now we can call the callback
    99  	cb(res, true)
   100  
   101  	// now we can update the time
   102  	top.broker.EXPECT().Send(gomock.Any()).Times(1)
   103  	top.OnTick(context.Background(), time.Unix(11, 0))
   104  	assert.Equal(t, top.GetThreshold(), uint32(666))
   105  
   106  	// now the signer
   107  
   108  	// first assert we have no signers
   109  	assert.Len(t, top.GetSigners(), 0)
   110  
   111  	signerEvent1 := types.SignerEvent{
   112  		BlockNumber: 10,
   113  		LogIndex:    11,
   114  		TxHash:      "0xacbde",
   115  		ID:          "someid",
   116  		Address:     "0xe3133A829FB11c3ad86A992D6576ec7705B105e5",
   117  		Nonce:       "123",
   118  		BlockTime:   123456789,
   119  		Kind:        types.SignerEventKindAdded,
   120  	}
   121  
   122  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   123  		cb = f
   124  		res = r
   125  		return nil
   126  	})
   127  
   128  	assert.NoError(t, top.ProcessSignerEvent(&signerEvent1))
   129  
   130  	// now we can call the callback
   131  	cb(res, true)
   132  
   133  	// now we can update the time
   134  	top.broker.EXPECT().Send(gomock.Any()).Times(1)
   135  	top.OnTick(context.Background(), time.Unix(12, 0))
   136  
   137  	t.Run("ensure the signer list is updated", func(t *testing.T) {
   138  		signers := top.GetSigners()
   139  		assert.Len(t, signers, 1)
   140  		assert.Equal(t, "0xe3133A829FB11c3ad86A992D6576ec7705B105e5", signers[0])
   141  	})
   142  
   143  	t.Run("check if our party IsSigner", func(t *testing.T) {
   144  		assert.True(t, top.IsSigner("0xe3133A829FB11c3ad86A992D6576ec7705B105e5"))
   145  	})
   146  
   147  	t.Run("check excess signers", func(t *testing.T) {
   148  		okAddresses := []string{"0xe3133A829FB11c3ad86A992D6576ec7705B105e5"}
   149  		koAddresses := []string{}
   150  
   151  		assert.True(t, top.ExcessSigners(koAddresses))
   152  		assert.False(t, top.ExcessSigners(okAddresses))
   153  	})
   154  
   155  	// now we will add some pending ones
   156  
   157  	thresholdEvent2 := types.SignerThresholdSetEvent{
   158  		Threshold:   500,
   159  		BlockNumber: 100,
   160  		LogIndex:    1,
   161  		TxHash:      "0xacbde2",
   162  		ID:          "someidthreshold2",
   163  		Nonce:       "1234",
   164  		BlockTime:   123456790,
   165  	}
   166  
   167  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   168  		return nil
   169  	})
   170  
   171  	assert.NoError(t, top.ProcessThresholdEvent(&thresholdEvent2))
   172  
   173  	signerEvent2 := types.SignerEvent{
   174  		BlockNumber: 101,
   175  		LogIndex:    19,
   176  		TxHash:      "0xacbde3",
   177  		ID:          "someid3",
   178  		Address:     "0xe82EfC4187705655C9b484dFFA25f240e8A6B0BA",
   179  		Nonce:       "1239",
   180  		BlockTime:   123456800,
   181  		Kind:        types.SignerEventKindAdded,
   182  	}
   183  
   184  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   185  		return nil
   186  	})
   187  
   188  	assert.NoError(t, top.ProcessSignerEvent(&signerEvent2))
   189  
   190  	// now we can snapshot
   191  	stateVerified, _, err := top.GetState((&types.PayloadERC20MultiSigTopologyVerified{}).Key())
   192  	assert.NoError(t, err)
   193  	assert.NotNil(t, stateVerified)
   194  	statePending, _, err := top.GetState((&types.PayloadERC20MultiSigTopologyPending{}).Key())
   195  	assert.NoError(t, err)
   196  	assert.NotNil(t, statePending)
   197  
   198  	// now instantiate a new one, and load the stuff
   199  	top2 := getTestTopology(t)
   200  	defer top2.ctrl.Finish()
   201  	top2.ocv.EXPECT().GetMultiSigAddress().AnyTimes()
   202  	top2.ethEventSource.EXPECT().UpdateContractBlock(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   203  
   204  	snap := &snapshotpb.Payload{}
   205  	err = proto.Unmarshal(stateVerified, snap)
   206  	require.NoError(t, err)
   207  
   208  	_, err = top2.LoadState(context.Background(), types.PayloadFromProto(snap))
   209  	assert.NoError(t, err)
   210  
   211  	ress := []validators.Resource{}
   212  	cbs := []func(interface{}, bool){}
   213  	// we should have 2 resources being restored
   214  	top2.witness.EXPECT().RestoreResource(gomock.Any(), gomock.Any()).Times(2).DoAndReturn(
   215  		func(res validators.Resource, f func(interface{}, bool)) error {
   216  			ress = append(ress, res)
   217  			cbs = append(cbs, f)
   218  			return nil
   219  		})
   220  
   221  	snap2 := &snapshotpb.Payload{}
   222  	err = proto.Unmarshal(statePending, snap2)
   223  	require.NoError(t, err)
   224  
   225  	_, err = top2.LoadState(context.Background(), types.PayloadFromProto(snap2))
   226  	assert.NoError(t, err)
   227  
   228  	// we should have had 2 callbacks
   229  	assert.Len(t, ress, 2)
   230  	assert.Len(t, cbs, 2)
   231  
   232  	// for now we still should have 2 pending, and 2 non pending
   233  	// we can compare states, they should be the same
   234  	tStateVerified, _, err := top.GetState((&types.PayloadERC20MultiSigTopologyVerified{}).Key())
   235  	assert.NoError(t, err)
   236  	assert.Equal(t,
   237  		hex.EncodeToString(crypto.Hash(tStateVerified)),
   238  		"159295749d4eb7646839c438de9004dca3f859c548117d249b6686b4ba1a4736",
   239  	)
   240  	tStatePending, _, err := top.GetState((&types.PayloadERC20MultiSigTopologyPending{}).Key())
   241  	assert.NoError(t, err)
   242  	assert.Equal(t,
   243  		hex.EncodeToString(crypto.Hash(tStatePending)),
   244  		"13ed814a71110dba6fbc88cd27c4efb25895d1ceb0434a270d96b835249f2a6d",
   245  	)
   246  
   247  	t2StateVerified, _, err := top2.GetState((&types.PayloadERC20MultiSigTopologyVerified{}).Key())
   248  	assert.NoError(t, err)
   249  	assert.Equal(t,
   250  		hex.EncodeToString(crypto.Hash(t2StateVerified)),
   251  		"159295749d4eb7646839c438de9004dca3f859c548117d249b6686b4ba1a4736",
   252  	)
   253  	t2StatePending, _, err := top2.GetState((&types.PayloadERC20MultiSigTopologyPending{}).Key())
   254  	assert.NoError(t, err)
   255  	assert.Equal(t,
   256  		hex.EncodeToString(crypto.Hash(t2StatePending)),
   257  		"13ed814a71110dba6fbc88cd27c4efb25895d1ceb0434a270d96b835249f2a6d",
   258  	)
   259  
   260  	assert.Equal(t, top2.GetThreshold(), uint32(666))
   261  	signers2 := top2.GetSigners()
   262  	assert.Equal(t, signers2[0], "0xe3133A829FB11c3ad86A992D6576ec7705B105e5")
   263  	assert.Len(t, signers2, 1)
   264  
   265  	// now let's call the callbacks, and move time
   266  	cbs[0](ress[0], true)
   267  	cbs[1](ress[1], true)
   268  
   269  	top2.broker.EXPECT().Send(gomock.Any()).Times(2)
   270  	top2.OnTick(context.Background(), time.Unix(20, 0))
   271  
   272  	// now we assert the changes
   273  	assert.Equal(t, top2.GetThreshold(), uint32(500))
   274  	signers3 := top2.GetSigners()
   275  	assert.Equal(t, signers3[0], "0xe3133A829FB11c3ad86A992D6576ec7705B105e5")
   276  	assert.Equal(t, signers3[1], "0xe82EfC4187705655C9b484dFFA25f240e8A6B0BA")
   277  	assert.Len(t, signers3, 2)
   278  
   279  	// now let's just check the hash
   280  	t2StateVerifiedLast, _, err := top2.GetState((&types.PayloadERC20MultiSigTopologyVerified{}).Key())
   281  	assert.NoError(t, err)
   282  	assert.Equal(t,
   283  		hex.EncodeToString(crypto.Hash(t2StateVerifiedLast)),
   284  		"0c8256dcccd2d72a664fedec2e9d36a995e1b81bcfdd4ce492c5360519fa1ccc",
   285  	)
   286  	t2StatePendingLast, _, err := top2.GetState((&types.PayloadERC20MultiSigTopologyPending{}).Key())
   287  	assert.NoError(t, err)
   288  	assert.Equal(t,
   289  		hex.EncodeToString(crypto.Hash(t2StatePendingLast)),
   290  		"74b4ccedd16267f6e93d3416a14cc142e528518bb3bcc30cfa9884705045f197",
   291  	)
   292  }
   293  
   294  func TestERC20TopologySnapshotAddRemoveSigner(t *testing.T) {
   295  	top := getTestTopology(t)
   296  	defer top.ctrl.Finish()
   297  
   298  	top.ocv.EXPECT().GetMultiSigAddress().AnyTimes()
   299  	top.ethEventSource.EXPECT().UpdateContractBlock(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   300  	top.OnTick(context.Background(), time.Unix(10, 0))
   301  
   302  	var cb func(interface{}, bool)
   303  	var res validators.Resource
   304  	// first assert we have no signers
   305  	assert.Len(t, top.GetSigners(), 0)
   306  
   307  	signerEvent1 := types.SignerEvent{
   308  		BlockNumber: 10,
   309  		LogIndex:    11,
   310  		TxHash:      "0xacbde",
   311  		ID:          "someid",
   312  		Address:     "0xe3133A829FB11c3ad86A992D6576ec7705B105e5",
   313  		Nonce:       "123",
   314  		BlockTime:   123456789,
   315  		Kind:        types.SignerEventKindAdded,
   316  	}
   317  
   318  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   319  		cb = f
   320  		res = r
   321  		return nil
   322  	})
   323  
   324  	assert.NoError(t, top.ProcessSignerEvent(&signerEvent1))
   325  
   326  	// now we can call the callback
   327  	cb(res, true)
   328  
   329  	// now we can update the time
   330  	top.broker.EXPECT().Send(gomock.Any()).Times(1)
   331  	top.OnTick(context.Background(), time.Unix(12, 0))
   332  
   333  	// Now we have a signer
   334  	t.Run("ensure the signer list is updated", func(t *testing.T) {
   335  		signers := top.GetSigners()
   336  		assert.Len(t, signers, 1)
   337  		assert.Equal(t, "0xe3133A829FB11c3ad86A992D6576ec7705B105e5", signers[0])
   338  	})
   339  
   340  	signerEvent2 := types.SignerEvent{
   341  		BlockNumber: 11,
   342  		LogIndex:    12,
   343  		TxHash:      "0xacbde",
   344  		ID:          "someid",
   345  		Address:     "0xe3133A829FB11c3ad86A992D6576ec7705B105e5",
   346  		Nonce:       "123",
   347  		BlockTime:   123456789,
   348  		Kind:        types.SignerEventKindRemoved,
   349  	}
   350  
   351  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   352  		return nil
   353  	})
   354  
   355  	assert.NoError(t, top.ProcessSignerEvent(&signerEvent2))
   356  
   357  	// now we can call the callback
   358  	cb(res, true)
   359  
   360  	// now we can update the time
   361  	top.broker.EXPECT().Send(gomock.Any()).Times(1)
   362  	top.OnTick(context.Background(), time.Unix(15, 0))
   363  
   364  	// Now we have no signer, but some seen events
   365  	t.Run("ensure the signer has been removed", func(t *testing.T) {
   366  		signers := top.GetSigners()
   367  		require.Len(t, signers, 0)
   368  	})
   369  
   370  	// now we can snapshot
   371  	stateVerified, _, err := top.GetState((&types.PayloadERC20MultiSigTopologyVerified{}).Key())
   372  	assert.NoError(t, err)
   373  	assert.NotNil(t, stateVerified)
   374  
   375  	// now instantiate a new one, and load the stuff
   376  	top2 := getTestTopology(t)
   377  	defer top2.ctrl.Finish()
   378  	top2.ocv.EXPECT().GetMultiSigAddress().AnyTimes()
   379  	top2.ethEventSource.EXPECT().UpdateContractBlock(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   380  
   381  	snap := &snapshotpb.Payload{}
   382  	err = proto.Unmarshal(stateVerified, snap)
   383  	require.NoError(t, err)
   384  
   385  	_, err = top2.LoadState(context.Background(), types.PayloadFromProto(snap))
   386  	assert.NoError(t, err)
   387  
   388  	// no signers because they were all removed
   389  	signers2 := top2.GetSigners()
   390  	assert.Len(t, signers2, 0)
   391  
   392  	// take a checkpoint to be sure that addressesPerEvents were restored properly
   393  	b1, err := top.Checkpoint()
   394  	require.NoError(t, err)
   395  
   396  	b2, err := top2.Checkpoint()
   397  	require.NoError(t, err)
   398  
   399  	require.Equal(t, b1, b2)
   400  }