code.vegaprotocol.io/vega@v0.79.0/core/delegation/checkpoint_backcompatibility_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 delegation
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"testing"
    22  
    23  	"code.vegaprotocol.io/vega/core/types"
    24  	"code.vegaprotocol.io/vega/libs/num"
    25  	"code.vegaprotocol.io/vega/libs/proto"
    26  
    27  	"github.com/golang/mock/gomock"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestCheckpointBackwardCompatibility(t *testing.T) {
    32  	t.Run("checkpoint with delegation roundtrip", testCheckpointBridgeWithDelegation)
    33  	t.Run("checkpoint with undelegation roundtrip", testCheckpointBridgeWithUndelegation)
    34  	t.Run("checkpoint with new delegation roundtrip", testCheckpointBridgeWithNewDelegation)
    35  	t.Run("full checkpoint roundtrip", testCheckpointBridgeMultiPartyMultiNode)
    36  }
    37  
    38  func testCheckpointBridgeWithDelegation(t *testing.T) {
    39  	testEngine := getEngine(t)
    40  	testEngine.broker.EXPECT().SendBatch(gomock.Any()).Times(2)
    41  
    42  	active := []*types.DelegationEntry{{
    43  		Party:      "party1",
    44  		Node:       "node1",
    45  		Amount:     num.NewUint(50),
    46  		Undelegate: false,
    47  		EpochSeq:   100,
    48  	}}
    49  
    50  	pending := []*types.DelegationEntry{{
    51  		Party:      "party1",
    52  		Node:       "node1",
    53  		Amount:     num.NewUint(100),
    54  		Undelegate: false,
    55  		EpochSeq:   101,
    56  	}}
    57  
    58  	data := &types.DelegateCP{
    59  		Active:  active,
    60  		Pending: pending,
    61  		Auto:    []string{},
    62  	}
    63  	cp, _ := proto.Marshal(data.IntoProto())
    64  
    65  	testEngine.engine.Load(context.Background(), cp)
    66  	testEngine.engine.onEpochEvent(context.Background(), types.Epoch{Seq: 100})
    67  	require.Equal(t, num.NewUint(50), testEngine.engine.partyDelegationState["party1"].totalDelegated)
    68  	require.Equal(t, num.NewUint(50), testEngine.engine.partyDelegationState["party1"].nodeToAmount["node1"])
    69  	require.Equal(t, num.NewUint(150), testEngine.engine.nextPartyDelegationState["party1"].totalDelegated)
    70  	require.Equal(t, num.NewUint(150), testEngine.engine.nextPartyDelegationState["party1"].nodeToAmount["node1"])
    71  
    72  	// take a new checkpoint from the current state and make sure it matches the old one
    73  	cp2, _ := testEngine.engine.Checkpoint()
    74  	require.True(t, bytes.Equal(cp, cp2))
    75  }
    76  
    77  func testCheckpointBridgeWithUndelegation(t *testing.T) {
    78  	testEngine := getEngine(t)
    79  	testEngine.broker.EXPECT().SendBatch(gomock.Any()).Times(2)
    80  
    81  	active := []*types.DelegationEntry{{
    82  		Party:      "party1",
    83  		Node:       "node1",
    84  		Amount:     num.NewUint(150),
    85  		Undelegate: false,
    86  		EpochSeq:   100,
    87  	}}
    88  
    89  	pending := []*types.DelegationEntry{{
    90  		Party:      "party1",
    91  		Node:       "node1",
    92  		Amount:     num.NewUint(50),
    93  		Undelegate: true,
    94  		EpochSeq:   101,
    95  	}}
    96  
    97  	data := &types.DelegateCP{
    98  		Active:  active,
    99  		Pending: pending,
   100  		Auto:    []string{},
   101  	}
   102  	cp, _ := proto.Marshal(data.IntoProto())
   103  	testEngine.engine.onEpochEvent(context.Background(), types.Epoch{Seq: 100})
   104  	testEngine.engine.Load(context.Background(), cp)
   105  
   106  	require.Equal(t, num.NewUint(150), testEngine.engine.partyDelegationState["party1"].totalDelegated)
   107  	require.Equal(t, num.NewUint(150), testEngine.engine.partyDelegationState["party1"].nodeToAmount["node1"])
   108  	require.Equal(t, num.NewUint(100), testEngine.engine.nextPartyDelegationState["party1"].totalDelegated)
   109  	require.Equal(t, num.NewUint(100), testEngine.engine.nextPartyDelegationState["party1"].nodeToAmount["node1"])
   110  
   111  	// take a new checkpoint from the current state and make sure it matches the old one
   112  	cp2, _ := testEngine.engine.Checkpoint()
   113  	require.True(t, bytes.Equal(cp, cp2))
   114  }
   115  
   116  func testCheckpointBridgeWithNewDelegation(t *testing.T) {
   117  	testEngine := getEngine(t)
   118  	testEngine.broker.EXPECT().SendBatch(gomock.Any()).Times(2)
   119  
   120  	active := []*types.DelegationEntry{{
   121  		Party:      "party1",
   122  		Node:       "node1",
   123  		Amount:     num.NewUint(50),
   124  		Undelegate: false,
   125  		EpochSeq:   100,
   126  	}}
   127  
   128  	pending := []*types.DelegationEntry{
   129  		{
   130  			Party:      "party1",
   131  			Node:       "node1",
   132  			Amount:     num.NewUint(100),
   133  			Undelegate: false,
   134  			EpochSeq:   101,
   135  		},
   136  		{
   137  			Party:      "party1",
   138  			Node:       "node2",
   139  			Amount:     num.NewUint(120),
   140  			Undelegate: false,
   141  			EpochSeq:   101,
   142  		},
   143  	}
   144  
   145  	data := &types.DelegateCP{
   146  		Active:  active,
   147  		Pending: pending,
   148  		Auto:    []string{},
   149  	}
   150  	cp, _ := proto.Marshal(data.IntoProto())
   151  
   152  	testEngine.engine.onEpochEvent(context.Background(), types.Epoch{Seq: 100})
   153  	testEngine.engine.Load(context.Background(), cp)
   154  
   155  	// take a new checkpoint from the current state and make sure it matches the old one
   156  	cp2, _ := testEngine.engine.Checkpoint()
   157  	require.True(t, bytes.Equal(cp, cp2))
   158  
   159  	require.Equal(t, num.NewUint(50), testEngine.engine.partyDelegationState["party1"].totalDelegated)
   160  	require.Equal(t, num.NewUint(50), testEngine.engine.partyDelegationState["party1"].nodeToAmount["node1"])
   161  	require.Equal(t, 1, len(testEngine.engine.partyDelegationState["party1"].nodeToAmount))
   162  	require.Equal(t, num.NewUint(270), testEngine.engine.nextPartyDelegationState["party1"].totalDelegated)
   163  	require.Equal(t, num.NewUint(150), testEngine.engine.nextPartyDelegationState["party1"].nodeToAmount["node1"])
   164  	require.Equal(t, num.NewUint(120), testEngine.engine.nextPartyDelegationState["party1"].nodeToAmount["node2"])
   165  }
   166  
   167  func testCheckpointBridgeMultiPartyMultiNode(t *testing.T) {
   168  	testEngine := getEngine(t)
   169  	testEngine.broker.EXPECT().SendBatch(gomock.Any()).Times(2)
   170  
   171  	active := []*types.DelegationEntry{
   172  		{
   173  			Party:      "party1",
   174  			Node:       "node1",
   175  			Amount:     num.NewUint(10),
   176  			Undelegate: false,
   177  			EpochSeq:   100,
   178  		},
   179  		{
   180  			Party:      "party1",
   181  			Node:       "node2",
   182  			Amount:     num.NewUint(20),
   183  			Undelegate: false,
   184  			EpochSeq:   100,
   185  		},
   186  		{
   187  			Party:      "party2",
   188  			Node:       "node1",
   189  			Amount:     num.NewUint(30),
   190  			Undelegate: false,
   191  			EpochSeq:   100,
   192  		},
   193  		{
   194  			Party:      "party2",
   195  			Node:       "node3",
   196  			Amount:     num.NewUint(40),
   197  			Undelegate: false,
   198  			EpochSeq:   100,
   199  		},
   200  		{
   201  			Party:      "party3",
   202  			Node:       "node3",
   203  			Amount:     num.NewUint(50),
   204  			Undelegate: false,
   205  			EpochSeq:   100,
   206  		},
   207  		{
   208  			Party:      "party4",
   209  			Node:       "node4",
   210  			Amount:     num.NewUint(60),
   211  			Undelegate: false,
   212  			EpochSeq:   100,
   213  		},
   214  	}
   215  
   216  	// party1 undelegates all from node1 and moves it to node 3
   217  	// party2 delegates to node2
   218  	pending := []*types.DelegationEntry{
   219  		{
   220  			Party:      "party1",
   221  			Node:       "node1",
   222  			Amount:     num.NewUint(10),
   223  			Undelegate: true,
   224  			EpochSeq:   101,
   225  		},
   226  		{
   227  			Party:      "party1",
   228  			Node:       "node3",
   229  			Amount:     num.NewUint(10),
   230  			Undelegate: false,
   231  			EpochSeq:   101,
   232  		},
   233  		{
   234  			Party:      "party2",
   235  			Node:       "node2",
   236  			Amount:     num.NewUint(50),
   237  			Undelegate: false,
   238  			EpochSeq:   101,
   239  		},
   240  		{
   241  			Party:      "party3",
   242  			Node:       "node3",
   243  			Amount:     num.NewUint(50),
   244  			Undelegate: true,
   245  			EpochSeq:   101,
   246  		},
   247  		{
   248  			Party:      "party5",
   249  			Node:       "node5",
   250  			Amount:     num.NewUint(70),
   251  			Undelegate: false,
   252  			EpochSeq:   101,
   253  		},
   254  	}
   255  
   256  	data := &types.DelegateCP{
   257  		Active:  active,
   258  		Pending: pending,
   259  		Auto:    []string{},
   260  	}
   261  	cp, _ := proto.Marshal(data.IntoProto())
   262  
   263  	testEngine.engine.onEpochEvent(context.Background(), types.Epoch{Seq: 100})
   264  	testEngine.engine.Load(context.Background(), cp)
   265  
   266  	// take a new checkpoint from the current state and make sure it matches the old one
   267  	cp2, _ := testEngine.engine.Checkpoint()
   268  	require.True(t, bytes.Equal(cp, cp2))
   269  
   270  	require.Equal(t, 4, len(testEngine.engine.partyDelegationState))
   271  	require.Equal(t, num.NewUint(30), testEngine.engine.partyDelegationState["party1"].totalDelegated)
   272  	require.Equal(t, num.NewUint(70), testEngine.engine.partyDelegationState["party2"].totalDelegated)
   273  	require.Equal(t, num.NewUint(50), testEngine.engine.partyDelegationState["party3"].totalDelegated)
   274  	require.Equal(t, num.NewUint(60), testEngine.engine.partyDelegationState["party4"].totalDelegated)
   275  
   276  	require.Equal(t, num.NewUint(10), testEngine.engine.partyDelegationState["party1"].nodeToAmount["node1"])
   277  	require.Equal(t, num.NewUint(20), testEngine.engine.partyDelegationState["party1"].nodeToAmount["node2"])
   278  	require.Equal(t, num.NewUint(30), testEngine.engine.partyDelegationState["party2"].nodeToAmount["node1"])
   279  	require.Equal(t, num.NewUint(40), testEngine.engine.partyDelegationState["party2"].nodeToAmount["node3"])
   280  	require.Equal(t, num.NewUint(50), testEngine.engine.partyDelegationState["party3"].nodeToAmount["node3"])
   281  	require.Equal(t, num.NewUint(60), testEngine.engine.partyDelegationState["party4"].nodeToAmount["node4"])
   282  
   283  	// expect party3 to have been gone and party 5 to have been added
   284  	require.Equal(t, 4, len(testEngine.engine.nextPartyDelegationState))
   285  	// party1 moved nomination from node1 to node3 no change in total
   286  	require.Equal(t, num.NewUint(30), testEngine.engine.nextPartyDelegationState["party1"].totalDelegated)
   287  	// party2 added nomination for node 2
   288  	require.Equal(t, num.NewUint(120), testEngine.engine.nextPartyDelegationState["party2"].totalDelegated)
   289  	// party 4 did nothing
   290  	require.Equal(t, num.NewUint(60), testEngine.engine.nextPartyDelegationState["party4"].totalDelegated)
   291  	// party 5 joined with nomination to node5
   292  	require.Equal(t, num.NewUint(70), testEngine.engine.nextPartyDelegationState["party5"].totalDelegated)
   293  
   294  	require.Equal(t, 2, len(testEngine.engine.nextPartyDelegationState["party1"].nodeToAmount))
   295  	require.Equal(t, num.NewUint(10), testEngine.engine.nextPartyDelegationState["party1"].nodeToAmount["node3"])
   296  	require.Equal(t, num.NewUint(20), testEngine.engine.nextPartyDelegationState["party1"].nodeToAmount["node2"])
   297  	require.Equal(t, 3, len(testEngine.engine.nextPartyDelegationState["party2"].nodeToAmount))
   298  	require.Equal(t, num.NewUint(30), testEngine.engine.nextPartyDelegationState["party2"].nodeToAmount["node1"])
   299  	require.Equal(t, num.NewUint(50), testEngine.engine.nextPartyDelegationState["party2"].nodeToAmount["node2"])
   300  	require.Equal(t, num.NewUint(40), testEngine.engine.nextPartyDelegationState["party2"].nodeToAmount["node3"])
   301  	require.Equal(t, 1, len(testEngine.engine.nextPartyDelegationState["party4"].nodeToAmount))
   302  	require.Equal(t, num.NewUint(60), testEngine.engine.nextPartyDelegationState["party4"].nodeToAmount["node4"])
   303  	require.Equal(t, 1, len(testEngine.engine.nextPartyDelegationState["party5"].nodeToAmount))
   304  	require.Equal(t, num.NewUint(70), testEngine.engine.nextPartyDelegationState["party5"].nodeToAmount["node5"])
   305  }
   306  
   307  func TestCheckpointWithRedundantUndelegation(t *testing.T) {
   308  	testEngine := getEngine(t)
   309  
   310  	active := []*types.DelegationEntry{}
   311  	pending := []*types.DelegationEntry{{
   312  		Party:      "party1",
   313  		Node:       "node1",
   314  		Amount:     num.NewUint(50),
   315  		Undelegate: true,
   316  		EpochSeq:   101,
   317  	}}
   318  	data := &types.DelegateCP{
   319  		Active:  active,
   320  		Pending: pending,
   321  		Auto:    []string{},
   322  	}
   323  	cp, _ := proto.Marshal(data.IntoProto())
   324  	testEngine.engine.onEpochEvent(context.Background(), types.Epoch{Seq: 100})
   325  	testEngine.engine.Load(context.Background(), cp)
   326  }