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 }