github.com/klaytn/klaytn@v1.12.1/reward/staking_info_test.go (about) 1 // Copyright 2019 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The klaytn library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package reward 18 19 import ( 20 "encoding/json" 21 "math" 22 "reflect" 23 "testing" 24 25 "github.com/klaytn/klaytn/common" 26 "github.com/klaytn/klaytn/params" 27 "github.com/klaytn/klaytn/storage/database" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 ) 31 32 type stakingInfoTestCase struct { 33 stakingInfo *StakingInfo 34 expectedConsolidated *ConsolidatedStakingInfo 35 expectedAmounts map[common.Address]uint64 36 } 37 38 var stakingInfoTestCases = generateStakingInfoTestCases() 39 40 func generateStakingInfoTestCases() []stakingInfoTestCase { 41 var ( 42 n1 = common.HexToAddress("0x8aD8F547fa00f58A8c4fb3B671Ee5f1A75bA028a") 43 n2 = common.HexToAddress("0xB2AAda7943919e82143324296987f6091F3FDC9e") 44 n3 = common.HexToAddress("0xD95c70710f07A3DaF7ae11cFBa10c789da3564D0") 45 n4 = common.HexToAddress("0xC704765db1d21C2Ea6F7359dcB8FD5233DeD16b5") 46 47 s1 = common.HexToAddress("0x4dd324F9821485caE941640B32c3Bcf1fA6E93E6") 48 s2 = common.HexToAddress("0x0d5Df5086B5f86f748dFaed5779c3f862C075B1f") 49 s3 = common.HexToAddress("0xD3Ff05f00491571E86A3cc8b0c320aA76D7413A5") 50 s4 = common.HexToAddress("0x11EF8e61d10365c2ECAe0E95b5fFa9ed4D68d64f") 51 52 r1 = common.HexToAddress("0x241c793A9AD555f52f6C3a83afe6178408796ab2") 53 r2 = common.HexToAddress("0x79b427Fb77077A9716E08D049B0e8f36Abfc8E2E") 54 r3 = common.HexToAddress("0x62E47d858bf8513fc401886B94E33e7DCec2Bfb7") 55 r4 = common.HexToAddress("0xf275f9f4c0d375F9E3E50370f93b504A1e45dB09") 56 57 kcf = common.HexToAddress("0x136807B12327a8AfF9831F09617dA1B9D398cda2") 58 kff = common.HexToAddress("0x46bA8F7538CD0749e572b2631F9FB4Ce3653AFB8") 59 60 a0 uint64 = 0 61 aL uint64 = 1000000 // less than minstaking 62 aM uint64 = 2000000 // exactly minstaking (params.DefaultMinimumStake) 63 a1 uint64 = 10000000 // above minstaking. Using 1,2,4,8 to uniquely spot errors 64 a2 uint64 = 20000000 65 a3 uint64 = 40000000 66 a4 uint64 = 80000000 67 ) 68 if aM != params.DefaultMinimumStake.Uint64() { 69 panic("broken test assumption") 70 } 71 72 return []stakingInfoTestCase{ 73 // Empty 74 { 75 stakingInfo: newEmptyStakingInfo(0), 76 expectedConsolidated: &ConsolidatedStakingInfo{ 77 nodes: make([]consolidatedNode, 0), 78 nodeIndex: make(map[common.Address]int), 79 }, 80 expectedAmounts: make(map[common.Address]uint64), 81 }, 82 83 // 1 entry 84 { 85 stakingInfo: &StakingInfo{ 86 BlockNum: 86400, 87 CouncilNodeAddrs: []common.Address{n1}, 88 CouncilStakingAddrs: []common.Address{s1}, 89 CouncilRewardAddrs: []common.Address{r1}, 90 KCFAddr: kcf, 91 KFFAddr: kff, 92 UseGini: true, 93 Gini: 0.00, 94 CouncilStakingAmounts: []uint64{a1}, 95 }, 96 expectedConsolidated: &ConsolidatedStakingInfo{ 97 nodes: []consolidatedNode{ 98 {[]common.Address{n1}, []common.Address{s1}, r1, a1}, 99 }, 100 nodeIndex: map[common.Address]int{n1: 0}, 101 }, 102 expectedAmounts: map[common.Address]uint64{n1: a1}, 103 }, 104 105 // Ordinary 4-entry info 106 { 107 stakingInfo: &StakingInfo{ 108 BlockNum: 2 * 86400, 109 CouncilNodeAddrs: []common.Address{n1, n2, n3, n4}, 110 CouncilStakingAddrs: []common.Address{s1, s2, s3, s4}, 111 CouncilRewardAddrs: []common.Address{r1, r2, r3, r4}, 112 KCFAddr: kcf, 113 KFFAddr: kff, 114 UseGini: true, 115 Gini: 0.38, // Gini(10, 20, 40, 80) 116 CouncilStakingAmounts: []uint64{a1, a2, a3, a4}, 117 }, 118 expectedConsolidated: &ConsolidatedStakingInfo{ 119 nodes: []consolidatedNode{ 120 {[]common.Address{n1}, []common.Address{s1}, r1, a1}, 121 {[]common.Address{n2}, []common.Address{s2}, r2, a2}, 122 {[]common.Address{n3}, []common.Address{s3}, r3, a3}, 123 {[]common.Address{n4}, []common.Address{s4}, r4, a4}, 124 }, 125 nodeIndex: map[common.Address]int{n1: 0, n2: 1, n3: 2, n4: 3}, 126 }, 127 expectedAmounts: map[common.Address]uint64{n1: a1, n2: a2, n3: a3, n4: a4}, 128 }, 129 130 // 4-entry with common reward addrs 131 { 132 stakingInfo: &StakingInfo{ 133 BlockNum: 3 * 86400, 134 CouncilNodeAddrs: []common.Address{n1, n2, n3, n4}, 135 CouncilStakingAddrs: []common.Address{s1, s2, s3, s4}, 136 CouncilRewardAddrs: []common.Address{r1, r2, r1, r2}, // r1 and r2 used twice each 137 KCFAddr: kcf, 138 KFFAddr: kff, 139 UseGini: true, 140 Gini: 0.17, // Gini(50, 100) 141 CouncilStakingAmounts: []uint64{a1, a2, a3, a4}, 142 }, 143 expectedConsolidated: &ConsolidatedStakingInfo{ 144 nodes: []consolidatedNode{ 145 {[]common.Address{n1, n3}, []common.Address{s1, s3}, r1, a1 + a3}, // n1 & n3 146 {[]common.Address{n2, n4}, []common.Address{s2, s4}, r2, a2 + a4}, // n2 & n4 147 }, 148 nodeIndex: map[common.Address]int{n1: 0, n2: 1, n3: 0, n4: 1}, 149 }, 150 expectedAmounts: map[common.Address]uint64{n1: a1 + a3, n2: a2 + a4, n3: a1 + a3, n4: a2 + a4}, 151 }, 152 153 // 4-entry with less-than-minstaking amounts 154 { 155 stakingInfo: &StakingInfo{ 156 BlockNum: 4 * 86400, 157 CouncilNodeAddrs: []common.Address{n1, n2, n3, n4}, 158 CouncilStakingAddrs: []common.Address{s1, s2, s3, s4}, 159 CouncilRewardAddrs: []common.Address{r1, r2, r3, r4}, 160 KCFAddr: kcf, 161 KFFAddr: kff, 162 UseGini: true, 163 Gini: 0.41, // Gini(20, 2) 164 CouncilStakingAmounts: []uint64{a2, aM, aL, a0}, // aL and a0 should be ignored in Gini calculation 165 }, 166 expectedConsolidated: &ConsolidatedStakingInfo{ 167 nodes: []consolidatedNode{ 168 {[]common.Address{n1}, []common.Address{s1}, r1, a2}, 169 {[]common.Address{n2}, []common.Address{s2}, r2, aM}, 170 {[]common.Address{n3}, []common.Address{s3}, r3, aL}, 171 {[]common.Address{n4}, []common.Address{s4}, r4, a0}, 172 }, 173 nodeIndex: map[common.Address]int{n1: 0, n2: 1, n3: 2, n4: 3}, 174 }, 175 expectedAmounts: map[common.Address]uint64{n1: a2, n2: aM, n3: aL, n4: a0}, 176 }, 177 } 178 } 179 180 func TestStakingInfo_GetIndexByNodeAddress(t *testing.T) { 181 testdata := []common.Address{ 182 common.StringToAddress("0xB55e5986b972Be438b4A91d6e8726aA50AD55EDc"), 183 common.StringToAddress("0xaDfc427080B4a66b5a629cd633d48C5d734572cA"), 184 common.StringToAddress("0x994daB8EB6f3FaE044cC0c9a0AB1A038e136b0B6"), 185 common.StringToAddress("0xD527822212Fded72c5fE89f46281d5355BD58235"), 186 } 187 testCases := []struct { 188 address common.Address 189 index int 190 err error 191 }{ 192 {common.StringToAddress("0xB55e5986b972Be438b4A91d6e8726aA50AD55EDc"), 0, nil}, 193 {common.StringToAddress("0xaDfc427080B4a66b5a629cd633d48C5d734572cA"), 1, nil}, 194 {common.StringToAddress("0x994daB8EB6f3FaE044cC0c9a0AB1A038e136b0B6"), 2, nil}, 195 {common.StringToAddress("0xD527822212Fded72c5fE89f46281d5355BD58235"), 3, nil}, 196 {common.StringToAddress("0x027AbB8c9f952cfFf01B1707fF14E2CB5D439502"), AddrNotFoundInCouncilNodes, ErrAddrNotInStakingInfo}, 197 } 198 199 stakingInfo := newEmptyStakingInfo(0) 200 stakingInfo.CouncilNodeAddrs = testdata 201 202 for i := 0; i < len(testCases); i++ { 203 result, err := stakingInfo.GetIndexByNodeAddress(testCases[i].address) 204 assert.Equal(t, testCases[i].index, result) 205 assert.Equal(t, testCases[i].err, err) 206 } 207 } 208 209 func TestStakingInfo_GetStakingAmountByNodeId(t *testing.T) { 210 testdata := struct { 211 address []common.Address 212 stakingAmount []uint64 213 }{ 214 []common.Address{ 215 common.StringToAddress("0xB55e5986b972Be438b4A91d6e8726aA50AD55EDc"), 216 common.StringToAddress("0xaDfc427080B4a66b5a629cd633d48C5d734572cA"), 217 common.StringToAddress("0x994daB8EB6f3FaE044cC0c9a0AB1A038e136b0B6"), 218 common.StringToAddress("0xD527822212Fded72c5fE89f46281d5355BD58235"), 219 }, 220 []uint64{ 221 100, 200, 300, 400, 222 }, 223 } 224 testCases := []struct { 225 address common.Address 226 stakingAmount uint64 227 err error 228 }{ 229 {common.StringToAddress("0xB55e5986b972Be438b4A91d6e8726aA50AD55EDc"), 100, nil}, 230 {common.StringToAddress("0xaDfc427080B4a66b5a629cd633d48C5d734572cA"), 200, nil}, 231 {common.StringToAddress("0x994daB8EB6f3FaE044cC0c9a0AB1A038e136b0B6"), 300, nil}, 232 {common.StringToAddress("0xD527822212Fded72c5fE89f46281d5355BD58235"), 400, nil}, 233 {common.StringToAddress("0x027AbB8c9f952cfFf01B1707fF14E2CB5D439502"), 0, ErrAddrNotInStakingInfo}, 234 } 235 236 stakingInfo := newEmptyStakingInfo(0) 237 stakingInfo.CouncilNodeAddrs = testdata.address 238 stakingInfo.CouncilStakingAmounts = testdata.stakingAmount 239 240 for i := 0; i < len(testCases); i++ { 241 result, err := stakingInfo.GetStakingAmountByNodeId(testCases[i].address) 242 assert.Equal(t, testCases[i].stakingAmount, result) 243 assert.Equal(t, testCases[i].err, err) 244 } 245 } 246 247 func TestStakingInfo_String(t *testing.T) { 248 // No information loss in String() -> Unmarshal() round trip 249 for _, testcase := range stakingInfoTestCases { 250 resultStr := testcase.stakingInfo.String() 251 t.Logf("%s", resultStr) 252 253 resultByteArr := []byte(resultStr) 254 resultStakingInfo := &StakingInfo{} 255 err := json.Unmarshal(resultByteArr, resultStakingInfo) 256 assert.NoError(t, err) 257 258 assert.Equal(t, testcase.stakingInfo, resultStakingInfo) 259 } 260 } 261 262 func TestCalcGiniCoefficient(t *testing.T) { 263 testCase := []struct { 264 testdata []float64 265 result float64 266 }{ 267 {[]float64{1, 1, 1}, 0.0}, 268 {[]float64{0, 8, 0, 0, 0}, 0.8}, 269 {[]float64{5, 4, 3, 2, 1}, 0.27}, 270 } 271 272 for i := 0; i < len(testCase); i++ { 273 result := CalcGiniCoefficient(testCase[i].testdata) 274 assert.Equal(t, testCase[i].result, result) 275 } 276 } 277 278 func TestGiniReflectToExpectedCCO(t *testing.T) { 279 testCase := []struct { 280 ccoToken []float64 281 beforeReflected []float64 282 adjustment []float64 283 afterReflected []float64 284 }{ 285 { 286 []float64{ 287 66666667, 233333333, 5000000, 5000000, 5000000, 288 77777778, 5000000, 33333333, 20000000, 16666667, 289 10000000, 5000000, 5000000, 5000000, 5000000, 290 5000000, 5000000, 5000000, 5000000, 5000000, 291 5000000, 292 }, 293 []float64{13, 44, 1, 1, 1, 15, 1, 6, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 294 []float64{42612, 89426, 9202, 9202, 9202, 46682, 9202, 28275, 20900, 18762, 13868, 9202, 9202, 9202, 9202, 9202, 9202, 9202, 9202, 9202, 9202}, 295 []float64{11, 23, 2, 2, 2, 12, 2, 7, 5, 5, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, 296 }, 297 { 298 []float64{ 299 400000000, 233333333, 233333333, 150000000, 108333333, 300 83333333, 66666667, 33333333, 20000000, 16666667, 301 10000000, 5000000, 5000000, 5000000, 5000000, 302 5000000, 5000000, 5000000, 5000000, 5000000, 303 5000000, 304 }, 305 []float64{28, 17, 17, 11, 8, 6, 5, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 306 []float64{123020, 89426, 89426, 68853, 56793, 48627, 42612, 28275, 20900, 18762, 13868, 9202, 9202, 9202, 9202, 9202, 9202, 9202, 9202, 9202, 9202}, 307 []float64{18, 13, 13, 10, 8, 7, 6, 4, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 308 }, 309 } 310 for i := 0; i < len(testCase); i++ { 311 stakingInfo := newEmptyStakingInfo(uint64(1)) 312 313 weights := make([]float64, len(testCase[i].ccoToken)) 314 tokenListToCalcGini := make([]float64, len(testCase[i].ccoToken)) 315 totalAmount := 0.0 316 for j := 0; j < len(testCase[i].ccoToken); j++ { 317 totalAmount += float64(testCase[i].ccoToken[j]) 318 tokenListToCalcGini[j] = testCase[i].ccoToken[j] 319 } 320 321 for j := 0; j < len(testCase[i].ccoToken); j++ { 322 weights[j] = math.Round(float64(testCase[i].ccoToken[j]) * 100 / totalAmount) 323 if weights[j] < 1 { 324 weights[j] = 1 325 } 326 if weights[j] != testCase[i].beforeReflected[j] { 327 t.Errorf("normal weight is incorrect. result : %v expected : %v", weights[j], testCase[i].beforeReflected[j]) 328 } 329 } 330 331 stakingAmountsGiniReflected := make([]float64, len(testCase[i].ccoToken)) 332 totalAmountGiniReflected := 0.0 333 stakingInfo.Gini = CalcGiniCoefficient(tokenListToCalcGini) 334 335 for j := 0; j < len(stakingAmountsGiniReflected); j++ { 336 stakingAmountsGiniReflected[j] = math.Round(math.Pow(float64(testCase[i].ccoToken[j]), 1.0/(1+stakingInfo.Gini))) 337 totalAmountGiniReflected += stakingAmountsGiniReflected[j] 338 } 339 340 for j := 0; j < len(testCase[i].ccoToken); j++ { 341 if stakingAmountsGiniReflected[j] != testCase[i].adjustment[j] { 342 t.Errorf("staking amount reflected gini is different. result : %v expected : %v", stakingAmountsGiniReflected[j], testCase[i].adjustment[j]) 343 } 344 } 345 346 for j := 0; j < len(testCase[i].ccoToken); j++ { 347 stakingAmountsGiniReflected[j] = math.Round(stakingAmountsGiniReflected[j] * 100 / totalAmountGiniReflected) 348 if stakingAmountsGiniReflected[j] != testCase[i].afterReflected[j] { 349 t.Errorf("weight reflected gini is different. result : %v expected : %v", stakingAmountsGiniReflected[j], testCase[i].afterReflected[j]) 350 } 351 } 352 } 353 } 354 355 // TestStakingInfoJSON tests marshalling and unmarshalling StakingInfo 356 // StakingInfo is marshaled before storing to DB. 357 func TestStakingInfoJSON(t *testing.T) { 358 // No information loss in json.Marshal() -> json.Unmarshal() round trip 359 for _, testcase := range stakingInfoTestCases { 360 src := testcase.stakingInfo 361 362 s, err := json.Marshal(src) 363 require.Nil(t, err) 364 365 dst := new(StakingInfo) 366 err = json.Unmarshal(s, dst) 367 require.Nil(t, err) 368 369 assert.Equal(t, src, dst) 370 } 371 } 372 373 func TestConsolidatedStakingInfo(t *testing.T) { 374 for _, testcase := range stakingInfoTestCases { 375 expected := testcase.expectedConsolidated 376 c := testcase.stakingInfo.GetConsolidatedStakingInfo() 377 378 // Test ConsolidatedStakingInfo 379 assert.Equal(t, expected.nodes, c.nodes) 380 assert.Equal(t, expected.nodeIndex, c.nodeIndex) 381 382 // Test CalcGiniCoefficient() 383 expectedGini := testcase.stakingInfo.Gini 384 gini := c.CalcGiniCoefficientMinStake(2000000) 385 assert.Equal(t, expectedGini, gini) 386 387 // Test GetConsolidatedNode() 388 for addr, expectedAmount := range testcase.expectedAmounts { 389 node := c.GetConsolidatedNode(addr) 390 require.NotNil(t, node) 391 assert.Equal(t, expectedAmount, node.StakingAmount) 392 } 393 } 394 } 395 396 // oldStakingInfo is a legacy of StakingInfo providing backward-compatibility. 397 // Since json tags of StakingInfo were changed, a node may fail to unmarshal stored data without this. 398 // oldStakingInfo's field names are the same with StakingInfo's names, but json tag is different. 399 type oldStakingInfo struct { 400 BlockNum uint64 `json:"BlockNum"` 401 CouncilNodeAddrs []common.Address `json:"CouncilNodeAddrs"` 402 CouncilStakingAddrs []common.Address `json:"CouncilStakingAddrs"` 403 CouncilRewardAddrs []common.Address `json:"CouncilRewardAddrs"` 404 KCFAddr common.Address `json:"KIRAddr"` // KIRAddr -> KCFAddr from v1.10.2 405 KFFAddr common.Address `json:"PoCAddr"` // PoCAddr -> KFFAddr from v1.10.2 406 UseGini bool `json:"UseGini"` 407 Gini float64 `json:"Gini"` 408 CouncilStakingAmounts []uint64 `json:"CouncilStakingAmounts"` 409 } 410 411 var oldInfo = oldStakingInfo{ 412 2880, 413 []common.Address{ 414 common.HexToAddress("0x159ae5ccda31b77475c64d88d4499c86f77b7ecc"), 415 common.HexToAddress("0x181deb121304b0430d99328ff1a9122df9f09d7f"), 416 common.HexToAddress("0x324ec8f2681cd73642cc55057970540a1f4393e0"), 417 common.HexToAddress("0x11191029025d3fcd21001746f949b25c6e8435cc"), 418 }, 419 []common.Address{ 420 common.HexToAddress("0x70e051c46ea76b9af9977407bb32192319907f9e"), 421 common.HexToAddress("0xe4a0c3821a2711758306ed57c2f4900aa9ddbb3d"), 422 common.HexToAddress("0xf3ba3a33b3bf7cf2085890315b41cc788770feb3"), 423 common.HexToAddress("0x9285a85777d0ae7e12bee3ffd7842908b2295f45"), 424 }, 425 []common.Address{ 426 common.HexToAddress("0xd155d4277c99fa837c54a37a40a383f71a3d082a"), 427 common.HexToAddress("0x2b8cc0ca62537fa5e49dce197acc8a15d3c5d4a8"), 428 common.HexToAddress("0x7d892f470ecde693f52588dd0cfe46c3d26b6219"), 429 common.HexToAddress("0xa0f7354a0cef878246820b6caa19d2bdef74a0cc"), 430 }, 431 common.HexToAddress("0x673003e5f9a852d3dc85b83d16ef62d45497fb96"), 432 common.HexToAddress("0x576dc0c2afeb1661da3cf53a60e76dd4e32c7ab1"), 433 false, 434 -1, 435 []uint64{5000000, 5000000, 5000000, 5000000}, 436 } 437 438 var newInfo = StakingInfo{ 439 oldInfo.BlockNum, 440 []common.Address{ 441 common.HexToAddress("0x70e051c46ea76b9af9977407bb32192319907f9e"), 442 common.HexToAddress("0xe4a0c3821a2711758306ed57c2f4900aa9ddbb3d"), 443 common.HexToAddress("0xf3ba3a33b3bf7cf2085890315b41cc788770feb3"), 444 common.HexToAddress("0x11191029025d3fcd21001746f949b25c6e8435cc"), 445 }, 446 []common.Address{ 447 common.HexToAddress("0x7d892f470ecde693f52588dd0cfe46c3d26b6219"), 448 common.HexToAddress("0x2b8cc0ca62537fa5e49dce197acc8a15d3c5d4a8"), 449 common.HexToAddress("0xa0f7354a0cef878246820b6caa19d2bdef74a0cc"), 450 common.HexToAddress("0x576dc0c2afeb1661da3cf53a60e76dd4e32c7ab1"), 451 }, 452 []common.Address{ 453 common.HexToAddress("0xd155d4277c99fa837c54a37a40a383f71a3d082a"), 454 common.HexToAddress("0x159ae5ccda31b77475c64d88d4499c86f77b7ecc"), 455 common.HexToAddress("0x181deb121304b0430d99328ff1a9122df9f09d7f"), 456 common.HexToAddress("0x673003e5f9a852d3dc85b83d16ef62d45497fb96"), 457 }, 458 common.HexToAddress("0x324ec8f2681cd73642cc55057970540a1f4393e0"), 459 common.HexToAddress("0x9285a85777d0ae7e12bee3ffd7842908b2295f45"), 460 false, 461 0.3, 462 []uint64{15000000, 4000000, 25000000, 35000000}, 463 } 464 465 // TestGetStakingInfoFromDB tests whether the node can read oldStakingInfo and StakingInfo data or not. 466 func TestGetStakingInfoFromDB(t *testing.T) { 467 oldStakingManager := GetStakingManager() 468 defer SetTestStakingManager(oldStakingManager) 469 470 for _, info := range []interface{}{oldInfo, newInfo} { 471 // reset database 472 SetTestStakingManagerWithDB(database.NewMemoryDBManager()) 473 474 infoBytes, err := json.Marshal(info) 475 if err != nil { 476 t.Fatal(err) 477 } 478 479 stakingManager.stakingInfoDB.WriteStakingInfo(oldInfo.BlockNum, infoBytes) 480 retrievedInfo, err := getStakingInfoFromDB(oldInfo.BlockNum) 481 if err != nil { 482 t.Fatal(err) 483 } 484 485 vInfo := reflect.ValueOf(info) 486 vRetriedInfo := reflect.ValueOf(*retrievedInfo) 487 488 assert.Equal(t, vInfo.NumField(), vRetriedInfo.NumField()) 489 for i := 0; i < vInfo.NumField(); i++ { 490 assert.Equal(t, vInfo.Field(i).Interface(), vRetriedInfo.Field(i).Interface()) 491 } 492 } 493 } 494 495 // TestStakingInfo_MarshalJSON tests marshal/unmarshal staking info data. 496 func TestStakingInfo_MarshalJSON(t *testing.T) { 497 // old marshalled data, new unmarshal method 498 { 499 oldInfoByte, err := json.Marshal(oldInfo) 500 if err != nil { 501 t.Fatal(err) 502 } 503 504 var unmarshalled StakingInfo 505 if err := json.Unmarshal(oldInfoByte, &unmarshalled); err != nil { 506 t.Fatal(err) 507 } 508 checkStakingInfoValues(t, oldInfo, unmarshalled) 509 } 510 511 // new marshalled data, old unmarshal method 512 { 513 newInfoByte, err := json.Marshal(newInfo) 514 if err != nil { 515 t.Fatal(err) 516 } 517 518 var unmarshalled oldStakingInfo 519 if err := json.Unmarshal(newInfoByte, &unmarshalled); err != nil { 520 t.Fatal(err) 521 } 522 checkStakingInfoValues(t, newInfo, unmarshalled) 523 } 524 525 // new marshalled data, new unmarshal method 526 { 527 newInfoByte, err := json.Marshal(newInfo) 528 if err != nil { 529 t.Fatal(err) 530 } 531 532 var unmarshalled StakingInfo 533 if err := json.Unmarshal(newInfoByte, &unmarshalled); err != nil { 534 t.Fatal(err) 535 } 536 checkStakingInfoValues(t, newInfo, unmarshalled) 537 } 538 } 539 540 func checkStakingInfoValues(t *testing.T, info interface{}, stakingInfo interface{}) { 541 vOld := reflect.ValueOf(info) 542 vNew := reflect.ValueOf(stakingInfo) 543 assert.Equal(t, vOld.NumField(), vNew.NumField()) 544 545 for i := 0; i < vOld.NumField(); i++ { 546 field := reflect.TypeOf(info).Field(i).Name 547 assert.Equal(t, vOld.FieldByName(field).Interface(), vNew.FieldByName(field).Interface()) 548 } 549 }