github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/staking/candidate_center_test.go (about)

     1  package staking
     2  
     3  import (
     4  	"math/big"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/iotexproject/iotex-core/action"
    11  	"github.com/iotexproject/iotex-core/action/protocol"
    12  	"github.com/iotexproject/iotex-core/pkg/unit"
    13  	"github.com/iotexproject/iotex-core/test/identityset"
    14  )
    15  
    16  // testEqual verifies m contains exactly the list
    17  func testEqual(m *CandidateCenter, l CandidateList) bool {
    18  	if m.All().Len() != len(l) {
    19  		return false
    20  	}
    21  	for _, v := range l {
    22  		d := m.GetByOwner(v.Owner)
    23  		if d == nil {
    24  			return false
    25  		}
    26  		if !v.Equal(d) {
    27  			return false
    28  		}
    29  
    30  		d = m.GetByName(v.Name)
    31  		if d == nil {
    32  			return false
    33  		}
    34  		if !v.Equal(d) {
    35  			return false
    36  		}
    37  
    38  		d = m.GetBySelfStakingIndex(v.SelfStakeBucketIdx)
    39  		if d == nil && v.isSelfStakeBucketSettled() {
    40  			return false
    41  		}
    42  		if d != nil && !v.Equal(d) {
    43  			return false
    44  		}
    45  	}
    46  	return true
    47  }
    48  
    49  // testEqualAllCommit validates candidate center
    50  // with original candidates = old, number of changed cand = change, and number of new cand = increase
    51  func testEqualAllCommit(r *require.Assertions, m *CandidateCenter, old CandidateList, change, increase int,
    52  ) (CandidateList, error) {
    53  	// capture all candidates
    54  	size := len(old)
    55  	list := m.All()
    56  	r.Equal(size+increase, len(list))
    57  	r.Equal(size+increase, m.Size())
    58  	all, err := list.toStateCandidateList()
    59  	r.NoError(err)
    60  
    61  	// number of changed cand = change
    62  	r.Equal(change, len(m.change.view()))
    63  	delta := m.Delta()
    64  	ser, err := delta.Serialize()
    65  	r.NoError(err)
    66  
    67  	// test abort the changes
    68  	r.NoError(m.SetDelta(nil))
    69  	r.Equal(size, m.Size())
    70  	// m equal to old list, not equal to current
    71  	r.True(testEqual(m, old))
    72  	r.False(testEqual(m, list))
    73  	r.Nil(m.Delta())
    74  
    75  	// test commit
    76  	r.NoError(delta.Deserialize(ser))
    77  	r.NoError(m.SetDelta(delta))
    78  	r.NoError(m.LegacyCommit())
    79  	r.NoError(m.LegacyCommit()) // commit is idempotent
    80  	r.Equal(size+increase, m.Size())
    81  	// m equal to current list, not equal to old
    82  	r.True(testEqual(m, list))
    83  	r.False(testEqual(m, old))
    84  
    85  	// after commit All() is the same
    86  	list = m.All()
    87  	r.Equal(size+increase, len(list))
    88  	r.Equal(size+increase, m.Size())
    89  	all1, err := list.toStateCandidateList()
    90  	r.NoError(err)
    91  	r.Equal(all, all1)
    92  	return list, nil
    93  }
    94  
    95  func TestCandCenter(t *testing.T) {
    96  	r := require.New(t)
    97  
    98  	m, err := NewCandidateCenter(nil)
    99  	r.NoError(err)
   100  	for i, v := range testCandidates {
   101  		r.NoError(m.Upsert(testCandidates[i].d))
   102  		r.True(m.ContainsName(v.d.Name))
   103  		r.Equal(v.d, m.GetByName(v.d.Name))
   104  	}
   105  	r.Equal(len(testCandidates), m.Size())
   106  
   107  	// test export changes and commit
   108  	list := m.Delta()
   109  	r.NotNil(list)
   110  	r.Equal(len(list), m.Size())
   111  	r.True(testEqual(m, list))
   112  	r.NoError(m.SetDelta(list))
   113  	r.NoError(m.LegacyCommit())
   114  	r.Equal(len(testCandidates), m.Size())
   115  	r.True(testEqual(m, list))
   116  	old := m.All()
   117  	r.True(testEqual(m, old))
   118  
   119  	// test existence
   120  	for _, v := range testCandidates {
   121  		r.True(m.ContainsName(v.d.Name))
   122  		r.True(m.ContainsOwner(v.d.Owner))
   123  		r.True(m.ContainsOperator(v.d.Operator))
   124  		r.True(m.ContainsSelfStakingBucket(v.d.SelfStakeBucketIdx))
   125  		r.Equal(v.d, m.GetByName(v.d.Name))
   126  		r.Equal(v.d, m.GetByOwner(v.d.Owner))
   127  		r.Equal(v.d, m.GetBySelfStakingIndex(v.d.SelfStakeBucketIdx))
   128  	}
   129  
   130  	testDeltas := []*Candidate{
   131  		&Candidate{
   132  			Owner:              identityset.Address(1),
   133  			Operator:           identityset.Address(7),
   134  			Reward:             identityset.Address(1),
   135  			Name:               "update1",
   136  			Votes:              big.NewInt(2),
   137  			SelfStakeBucketIdx: 1,
   138  			SelfStake:          unit.ConvertIotxToRau(1200000),
   139  		},
   140  		&Candidate{
   141  			Owner:              identityset.Address(2),
   142  			Operator:           identityset.Address(8),
   143  			Reward:             identityset.Address(1),
   144  			Name:               "test2",
   145  			Votes:              big.NewInt(3),
   146  			SelfStakeBucketIdx: 200,
   147  			SelfStake:          unit.ConvertIotxToRau(1200000),
   148  		},
   149  		&Candidate{
   150  			Owner:              identityset.Address(3),
   151  			Operator:           identityset.Address(6),
   152  			Reward:             identityset.Address(1),
   153  			Name:               "test3",
   154  			Votes:              big.NewInt(3),
   155  			SelfStakeBucketIdx: 3,
   156  			SelfStake:          unit.ConvertIotxToRau(1200000),
   157  		},
   158  		&Candidate{
   159  			Owner:              identityset.Address(4),
   160  			Operator:           identityset.Address(10),
   161  			Reward:             identityset.Address(1),
   162  			Name:               "test4",
   163  			Votes:              big.NewInt(1),
   164  			SelfStakeBucketIdx: 4,
   165  			SelfStake:          unit.ConvertIotxToRau(1100000),
   166  		},
   167  
   168  		&Candidate{
   169  			Owner:              identityset.Address(7),
   170  			Operator:           identityset.Address(1),
   171  			Reward:             identityset.Address(1),
   172  			Name:               "new1",
   173  			Votes:              big.NewInt(2),
   174  			SelfStakeBucketIdx: 6,
   175  			SelfStake:          unit.ConvertIotxToRau(1200000),
   176  		},
   177  		&Candidate{
   178  			Owner:              identityset.Address(8),
   179  			Operator:           identityset.Address(2),
   180  			Reward:             identityset.Address(1),
   181  			Name:               "new2",
   182  			Votes:              big.NewInt(3),
   183  			SelfStakeBucketIdx: 7,
   184  			SelfStake:          unit.ConvertIotxToRau(1200000),
   185  		},
   186  		&Candidate{
   187  			Owner:              identityset.Address(9),
   188  			Operator:           identityset.Address(3),
   189  			Reward:             identityset.Address(1),
   190  			Name:               "new3",
   191  			Votes:              big.NewInt(3),
   192  			SelfStakeBucketIdx: 8,
   193  			SelfStake:          unit.ConvertIotxToRau(1200000),
   194  		},
   195  		&Candidate{
   196  			Owner:              identityset.Address(10),
   197  			Operator:           identityset.Address(4),
   198  			Reward:             identityset.Address(1),
   199  			Name:               "new4",
   200  			Votes:              big.NewInt(1),
   201  			SelfStakeBucketIdx: 9,
   202  			SelfStake:          unit.ConvertIotxToRau(1200000),
   203  		},
   204  	}
   205  
   206  	for i := range testDeltas {
   207  		r.NoError(m.Upsert(testDeltas[i]))
   208  	}
   209  	list = m.All()
   210  	r.Equal(len(list), m.Size())
   211  	r.True(testEqual(m, list))
   212  	delta := m.Delta()
   213  	// 4 updates + 4 new
   214  	r.Equal(8, len(delta))
   215  	r.Equal(len(testCandidates)+4, m.Size())
   216  
   217  	// SetDelta using one's own Delta() does not change a thing
   218  	r.NoError(m.SetDelta(delta))
   219  	r.Equal(len(list), m.Size())
   220  	r.True(testEqual(m, list))
   221  	r.NoError(m.LegacyCommit())
   222  	r.Equal(len(list), m.Size())
   223  	r.True(testEqual(m, list))
   224  
   225  	// testDeltas[3] only updated self-stake
   226  	td3 := m.GetByName(testDeltas[3].Name)
   227  	r.Equal(td3, testDeltas[3])
   228  
   229  	// test update existing and add new
   230  	// each time pick 2 random candidates to update, and add 2 new cand
   231  	for i := 0; i < 10; i++ {
   232  		size := len(list)
   233  		u1 := time.Now().Nanosecond() / 1000 % size
   234  		u2 := (u1 + 1) % size
   235  		d1 := list[u1].Clone()
   236  		d2 := list[u2].Clone()
   237  		// save the name, operator and self-stake
   238  		name1 := d1.Name
   239  		name2 := d2.Name
   240  		op1 := d1.Operator
   241  		op2 := d2.Operator
   242  		self1 := d1.SelfStakeBucketIdx
   243  		self2 := d2.SelfStakeBucketIdx
   244  
   245  		// d1 conflict with existing
   246  		conflict := list[(u1+3)%size]
   247  		d1.Name = conflict.Name
   248  		r.Equal(action.ErrInvalidCanName, m.Upsert(d1))
   249  		d1.Name = name1
   250  		d1.Operator = conflict.Operator
   251  		r.Equal(ErrInvalidOperator, m.Upsert(d1))
   252  		d1.Operator = op1
   253  		d1.SelfStakeBucketIdx = conflict.SelfStakeBucketIdx
   254  		r.Equal(ErrInvalidSelfStkIndex, m.Upsert(d1))
   255  		d1.SelfStakeBucketIdx = self1
   256  
   257  		// no change yet, delta must be empty
   258  		r.Nil(m.Delta())
   259  
   260  		// upsert(d1)
   261  		d1.Operator = identityset.Address(13 + i*2) // identityset[13]+ don't conflict with existing
   262  		r.NoError(m.Upsert(d1))
   263  		d1.Name = d1.Operator.String()[:12]
   264  		r.NoError(m.Upsert(d1))
   265  
   266  		// d2 conflict d1
   267  		d2.Name = d1.Name
   268  		r.Equal(action.ErrInvalidCanName, m.Upsert(d2))
   269  		d2.Name = name2
   270  		d2.Operator = d1.Operator
   271  		r.Equal(ErrInvalidOperator, m.Upsert(d2))
   272  		d2.Operator = op2
   273  		d2.SelfStakeBucketIdx = d1.SelfStakeBucketIdx
   274  		r.Equal(ErrInvalidSelfStkIndex, m.Upsert(d2))
   275  		d2.SelfStakeBucketIdx = self2
   276  		// upsert(d2)
   277  		d2.Operator = identityset.Address(14 + i*2)
   278  		r.NoError(m.Upsert(d2))
   279  		d2.Name = d2.Operator.String()[:12]
   280  		r.NoError(m.Upsert(d2))
   281  
   282  		// new n1 conflict with existing
   283  		n1 := list[(u2+size/2)%size].Clone()
   284  		n1.Owner = identityset.Address(15 + i*2)
   285  		r.Equal(action.ErrInvalidCanName, m.Upsert(n1))
   286  		n1.Name = name1
   287  		r.Equal(ErrInvalidOperator, m.Upsert(n1))
   288  		n1.Operator = op1
   289  		r.Equal(ErrInvalidSelfStkIndex, m.Upsert(n1))
   290  		n1.SelfStakeBucketIdx = uint64(size)
   291  		// upsert(n1)
   292  		r.NoError(m.Upsert(n1))
   293  
   294  		// new n2 conflict with dirty d2
   295  		n2 := d2.Clone()
   296  		n2.Owner = identityset.Address(16 + i*2)
   297  		r.Equal(action.ErrInvalidCanName, m.Upsert(n2))
   298  		n2.Name = name2
   299  		r.Equal(ErrInvalidOperator, m.Upsert(n2))
   300  		n2.Operator = op2
   301  		r.Equal(ErrInvalidSelfStkIndex, m.Upsert(n2))
   302  		n2.SelfStakeBucketIdx = uint64(size + 1)
   303  		// upsert(n2)
   304  		r.NoError(m.Upsert(n2))
   305  
   306  		// verify conflict with n1
   307  		n2 = n1.Clone()
   308  		n2.Owner = identityset.Address(0)
   309  		r.Equal(action.ErrInvalidCanName, m.Upsert(n2))
   310  		n2.Name = "noconflict"
   311  		r.Equal(ErrInvalidOperator, m.Upsert(n2))
   312  		n2.Operator = identityset.Address(0)
   313  		r.Equal(ErrInvalidSelfStkIndex, m.Upsert(n2))
   314  
   315  		// upsert a candidate w/o change is fine
   316  		r.NoError(m.Upsert(conflict))
   317  
   318  		// there are 5 changes (2 dirty + 2 new + 1 w/o change)
   319  		var err error
   320  		list, err = testEqualAllCommit(r, m, list, 5, 2)
   321  		r.NoError(err)
   322  
   323  		// test candidate that does not exist
   324  		nonExistAddr := identityset.Address(0)
   325  		r.False(m.ContainsOwner(nonExistAddr))
   326  		r.False(m.ContainsOperator(nonExistAddr))
   327  		r.False(m.ContainsName("notexist"))
   328  		r.False(m.ContainsSelfStakingBucket(1000))
   329  	}
   330  }
   331  
   332  func TestFixAlias(t *testing.T) {
   333  	r := require.New(t)
   334  
   335  	dk := protocol.NewDock()
   336  	view := protocol.View{}
   337  
   338  	for _, hasAlias := range []bool{false, true} {
   339  		// add 6 candidates into cand center
   340  		m, err := NewCandidateCenter(nil)
   341  		r.NoError(err)
   342  		for i, v := range testCandidates {
   343  			r.NoError(m.Upsert(testCandidates[i].d))
   344  			r.True(m.ContainsName(v.d.Name))
   345  			r.True(m.ContainsOperator(v.d.Operator))
   346  			r.Equal(v.d, m.GetByName(v.d.Name))
   347  		}
   348  		if hasAlias {
   349  			r.NoError(m.LegacyCommit())
   350  		} else {
   351  			r.NoError(m.Commit())
   352  		}
   353  		r.NoError(view.Write(_protocolID, m))
   354  
   355  		// simulate handleCandidateUpdate: update name
   356  		center := candCenterFromNewCandidateStateManager(r, view, dk)
   357  		name := testCandidates[0].d.Name
   358  		nameAlias := center.GetByName(name)
   359  		nameAlias.Equal(testCandidates[0].d)
   360  		nameAlias.Name = "break"
   361  		{
   362  			r.NoError(center.Upsert(nameAlias))
   363  			delta := center.Delta()
   364  			r.Equal(1, len(delta))
   365  			r.NoError(dk.Load(_protocolID, _stakingCandCenter, &delta))
   366  		}
   367  
   368  		center = candCenterFromNewCandidateStateManager(r, view, dk)
   369  		n := center.GetByName("break")
   370  		n.Equal(nameAlias)
   371  		r.True(center.ContainsName("break"))
   372  		// old name does not exist
   373  		r.Nil(center.GetByName(name))
   374  		r.False(center.ContainsName(name))
   375  
   376  		// simulate handleCandidateUpdate: update operator
   377  		op := testCandidates[1].d.Operator
   378  		opAlias := testCandidates[1].d.Clone()
   379  		opAlias.Operator = identityset.Address(17)
   380  		r.True(center.ContainsOperator(op))
   381  		r.False(center.ContainsOperator(opAlias.Operator))
   382  		{
   383  			r.NoError(center.Upsert(opAlias))
   384  			delta := center.Delta()
   385  			r.Equal(2, len(delta))
   386  			r.NoError(dk.Load(_protocolID, _stakingCandCenter, &delta))
   387  		}
   388  
   389  		// verify cand center with name/op alias
   390  		center = candCenterFromNewCandidateStateManager(r, view, dk)
   391  		n = center.GetByName("break")
   392  		n.Equal(nameAlias)
   393  		r.True(center.ContainsName("break"))
   394  		// old name does not exist
   395  		r.Nil(center.GetByName(name))
   396  		r.False(center.ContainsName(name))
   397  		n = center.GetByOwner(testCandidates[1].d.Owner)
   398  		n.Equal(opAlias)
   399  		r.True(center.ContainsOperator(opAlias.Operator))
   400  		// old operator does not exist
   401  		r.False(center.ContainsOperator(op))
   402  
   403  		// cand center Commit()
   404  		{
   405  			if hasAlias {
   406  				r.NoError(center.LegacyCommit())
   407  			} else {
   408  				r.NoError(center.Commit())
   409  			}
   410  			r.NoError(view.Write(_protocolID, center))
   411  			dk.Reset()
   412  		}
   413  
   414  		// verify cand center after Commit()
   415  		center = candCenterFromNewCandidateStateManager(r, view, dk)
   416  		n = center.GetByName("break")
   417  		n.Equal(nameAlias)
   418  		n = center.GetByOwner(testCandidates[1].d.Owner)
   419  		n.Equal(opAlias)
   420  		r.True(center.ContainsOperator(opAlias.Operator))
   421  		if !hasAlias {
   422  			r.Nil(center.GetByName(name))
   423  			r.False(center.ContainsName(name))
   424  			r.False(center.ContainsOperator(op))
   425  		} else {
   426  			// alias still exist in name/operator map
   427  			n = center.GetByName(name)
   428  			n.Equal(testCandidates[0].d)
   429  			r.True(center.ContainsName(name))
   430  			r.True(center.ContainsOperator(op))
   431  		}
   432  	}
   433  }
   434  
   435  func TestMultipleNonStakingCandidate(t *testing.T) {
   436  	r := require.New(t)
   437  
   438  	candStaked := &Candidate{
   439  		Owner:              identityset.Address(1),
   440  		Operator:           identityset.Address(11),
   441  		Reward:             identityset.Address(3),
   442  		Name:               "self-staked",
   443  		Votes:              unit.ConvertIotxToRau(1200000),
   444  		SelfStakeBucketIdx: 1,
   445  		SelfStake:          unit.ConvertIotxToRau(1200000),
   446  	}
   447  	candNonStaked1 := &Candidate{
   448  		Owner:              identityset.Address(2),
   449  		Operator:           identityset.Address(12),
   450  		Reward:             identityset.Address(3),
   451  		Name:               "non-self-staked1",
   452  		Votes:              big.NewInt(0),
   453  		SelfStakeBucketIdx: candidateNoSelfStakeBucketIndex,
   454  		SelfStake:          big.NewInt(0),
   455  	}
   456  	candNonStaked2 := &Candidate{
   457  		Owner:              identityset.Address(3),
   458  		Operator:           identityset.Address(13),
   459  		Reward:             identityset.Address(3),
   460  		Name:               "non-self-staked2",
   461  		Votes:              big.NewInt(0),
   462  		SelfStakeBucketIdx: candidateNoSelfStakeBucketIndex,
   463  		SelfStake:          big.NewInt(0),
   464  	}
   465  	candNonStaked1ColOwner := &Candidate{
   466  		Owner:              identityset.Address(2),
   467  		Operator:           identityset.Address(22),
   468  		Reward:             identityset.Address(3),
   469  		Name:               "non-self-staked1-col-owner",
   470  		Votes:              big.NewInt(0),
   471  		SelfStakeBucketIdx: candidateNoSelfStakeBucketIndex,
   472  		SelfStake:          big.NewInt(0),
   473  	}
   474  	candNonStaked1ColOpt := &Candidate{
   475  		Owner:              identityset.Address(20),
   476  		Operator:           identityset.Address(12),
   477  		Reward:             identityset.Address(3),
   478  		Name:               "non-self-staked1-col-opt",
   479  		Votes:              big.NewInt(0),
   480  		SelfStakeBucketIdx: candidateNoSelfStakeBucketIndex,
   481  		SelfStake:          big.NewInt(0),
   482  	}
   483  	candNonStaked1ColName := &Candidate{
   484  		Owner:              identityset.Address(21),
   485  		Operator:           identityset.Address(23),
   486  		Reward:             identityset.Address(3),
   487  		Name:               "non-self-staked1",
   488  		Votes:              big.NewInt(0),
   489  		SelfStakeBucketIdx: candidateNoSelfStakeBucketIndex,
   490  		SelfStake:          big.NewInt(0),
   491  	}
   492  	candStakedColBucket := &Candidate{
   493  		Owner:              identityset.Address(4),
   494  		Operator:           identityset.Address(14),
   495  		Reward:             identityset.Address(3),
   496  		Name:               "self-staked-col-bucket",
   497  		Votes:              unit.ConvertIotxToRau(1200000),
   498  		SelfStakeBucketIdx: 1,
   499  		SelfStake:          unit.ConvertIotxToRau(1200000),
   500  	}
   501  
   502  	checkCandidates := func(candcenter *CandidateCenter, cands []*Candidate) {
   503  		r.True(testEqual(candcenter, CandidateList(cands)))
   504  		// commit
   505  		r.NoError(candcenter.Commit())
   506  		r.True(testEqual(candcenter, CandidateList(cands)))
   507  		// from state manager
   508  		dk := protocol.NewDock()
   509  		view := protocol.View{}
   510  		r.NoError(view.Write(_protocolID, candcenter))
   511  		dk.Reset()
   512  		candcenter = candCenterFromNewCandidateStateManager(r, view, dk)
   513  		r.True(testEqual(candcenter, CandidateList(cands)))
   514  	}
   515  	t.Run("nonstaked candidate not collision on bucket", func(t *testing.T) {
   516  		candcenter, err := NewCandidateCenter(nil)
   517  		r.NoError(err)
   518  		r.NoError(candcenter.Upsert(candNonStaked1))
   519  		r.NoError(candcenter.Upsert(candNonStaked2))
   520  		checkCandidates(candcenter, []*Candidate{candNonStaked1, candNonStaked2})
   521  	})
   522  	t.Run("staked candidate collision on bucket", func(t *testing.T) {
   523  		candcenter, err := NewCandidateCenter(nil)
   524  		r.NoError(err)
   525  		r.NoError(candcenter.Upsert(candStaked))
   526  		r.ErrorIs(candcenter.Upsert(candStakedColBucket), ErrInvalidSelfStkIndex)
   527  		checkCandidates(candcenter, []*Candidate{candStaked})
   528  	})
   529  	t.Run("nonstaked candidate collision on operator", func(t *testing.T) {
   530  		candcenter, err := NewCandidateCenter(nil)
   531  		r.NoError(err)
   532  		r.NoError(candcenter.Upsert(candNonStaked1))
   533  		r.ErrorIs(candcenter.Upsert(candNonStaked1ColOpt), ErrInvalidOperator)
   534  		checkCandidates(candcenter, []*Candidate{candNonStaked1})
   535  	})
   536  	t.Run("nonstaked candidate collision on name", func(t *testing.T) {
   537  		candcenter, err := NewCandidateCenter(nil)
   538  		r.NoError(err)
   539  		r.NoError(candcenter.Upsert(candNonStaked1))
   540  		r.ErrorIs(candcenter.Upsert(candNonStaked1ColName), action.ErrInvalidCanName)
   541  		checkCandidates(candcenter, []*Candidate{candNonStaked1})
   542  	})
   543  	t.Run("nonstaked candidate update on owner", func(t *testing.T) {
   544  		candcenter, err := NewCandidateCenter(nil)
   545  		r.NoError(err)
   546  		r.NoError(candcenter.Upsert(candNonStaked1))
   547  		r.NoError(candcenter.Upsert(candNonStaked1ColOwner))
   548  		checkCandidates(candcenter, []*Candidate{candNonStaked1ColOwner})
   549  	})
   550  	t.Run("change bucket", func(t *testing.T) {
   551  		candcenter, err := NewCandidateCenter(nil)
   552  		r.NoError(err)
   553  		r.NoError(candcenter.Upsert(candNonStaked1))
   554  		// settle self-stake bucket
   555  		candStaked := candNonStaked1.Clone()
   556  		candStaked.SelfStakeBucketIdx = 1
   557  		candStaked.SelfStake = unit.ConvertIotxToRau(1200000)
   558  		r.NoError(candcenter.Upsert(candStaked))
   559  		// change self-stake bucket
   560  		candUpdated := candStaked.Clone()
   561  		candUpdated.SelfStakeBucketIdx = 2
   562  		r.NoError(candcenter.Upsert(candUpdated))
   563  		checkCandidates(candcenter, []*Candidate{candUpdated})
   564  	})
   565  }
   566  
   567  func candCenterFromNewCandidateStateManager(r *require.Assertions, view protocol.View, dk protocol.Dock) *CandidateCenter {
   568  	// get cand center: csm.ConstructBaseView
   569  	v, err := view.Read(_protocolID)
   570  	r.NoError(err)
   571  	center := v.(*CandidateCenter).Base()
   572  	// get changes: csm.Sync()
   573  	delta := CandidateList{}
   574  	err = dk.Unload(_protocolID, _stakingCandCenter, &delta)
   575  	r.True(err == nil || err == protocol.ErrNoName)
   576  	r.NoError(center.SetDelta(delta))
   577  	return center
   578  }