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 }