From 71974e1440123cb6c52f8f0f48b26e27333c5287 Mon Sep 17 00:00:00 2001 From: Edgar Aroutiounian Date: Wed, 4 Mar 2020 17:58:48 -0800 Subject: [PATCH] Add delegation test, cover 18.3% (#2274) Add unit test for validator.go fix the format problem fixing the failing test some work adding tests for validator sanitychecks creating fresh validator wrapper for testing sanity check minor adding more tests to transactions --- staking/types/delegation_test.go | 82 ++++++ staking/types/transaction_test.go | 102 ++++++- staking/types/validator_test.go | 435 +++++++++++++++++++++++++++++- 3 files changed, 608 insertions(+), 11 deletions(-) create mode 100644 staking/types/delegation_test.go diff --git a/staking/types/delegation_test.go b/staking/types/delegation_test.go new file mode 100644 index 0000000000..d6111f4e7e --- /dev/null +++ b/staking/types/delegation_test.go @@ -0,0 +1,82 @@ +package types + +import ( + "math/big" + "testing" + + common "github.com/ethereum/go-ethereum/common" + common2 "github.com/harmony-one/harmony/internal/common" +) + +var ( + testAddr, _ = common2.Bech32ToAddress("one129r9pj3sk0re76f7zs3qz92rggmdgjhtwge62k") + delegatorAddr = common.Address(testAddr) + delegationAmt = big.NewInt(100000) + // create a new delegation: + delegation = NewDelegation(delegatorAddr, delegationAmt) +) + +func TestUndelegate(t *testing.T) { + epoch1 := big.NewInt(10) + amount1 := big.NewInt(1000) + delegation.Undelegate(epoch1, amount1) + + // check the undelegation's Amount + if delegation.Undelegations[0].Amount.Cmp(amount1) != 0 { + t.Errorf("undelegate failed, amount does not match") + } + // check the undelegation's Epoch + if delegation.Undelegations[0].Epoch.Cmp(epoch1) != 0 { + t.Errorf("undelegate failed, epoch does not match") + } + + epoch2 := big.NewInt(12) + amount2 := big.NewInt(2000) + delegation.Undelegate(epoch2, amount2) + + // check the number of undelegations + if len(delegation.Undelegations) != 2 { + t.Errorf("total number of undelegations should have been two") + } +} + +func TestTotalInUndelegation(t *testing.T) { + var totalAmount *big.Int = delegation.TotalInUndelegation() + + // check the total amount of undelegation + if totalAmount.Cmp(big.NewInt(3000)) != 0 { + t.Errorf("total undelegation amount is not correct") + } +} + +func TestDeleteEntry(t *testing.T) { + // add the third delegation + // Undelegations[]: 1000, 2000, 3000 + epoch3 := big.NewInt(15) + amount3 := big.NewInt(3000) + delegation.Undelegate(epoch3, amount3) + + // delete the second undelegation entry + // Undelegations[]: 1000, 3000 + deleteEpoch := big.NewInt(12) + delegation.DeleteEntry(deleteEpoch) + + // check if the Undelegtaions[1] == 3000 + if delegation.Undelegations[1].Amount.Cmp(big.NewInt(3000)) != 0 { + t.Errorf("deleting an undelegation entry fails, amount is not correct") + } +} + +func TestRemoveUnlockUndelegations(t *testing.T) { + lastEpochInCommitte := big.NewInt(16) + curEpoch := big.NewInt(24) + + epoch4 := big.NewInt(21) + amount4 := big.NewInt(4000) + delegation.Undelegate(epoch4, amount4) + + result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommitte) + if result.Cmp(big.NewInt(8000)) != 0 { + t.Errorf("removing an unlocked undelegation fails") + } +} diff --git a/staking/types/transaction_test.go b/staking/types/transaction_test.go index 4fc4447931..72400763fb 100644 --- a/staking/types/transaction_test.go +++ b/staking/types/transaction_test.go @@ -1,7 +1,8 @@ package types import ( - "fmt" + "bytes" + "errors" "math/big" "testing" @@ -19,6 +20,20 @@ var ( testBLSPubKey2 = "40379eed79ed82bebfb4310894fd33b6a3f8413a78dc4d43b98d0adc9ef69f3285df05eaab9f2ce5f7227f8cb920e809" ) +func createDelegate() (*StakingTransaction, error) { + dAddr, _ := common2.Bech32ToAddress(testAccount) + vAddr, _ := common2.Bech32ToAddress(testAccount) + stakePayloadMaker := func() (Directive, interface{}) { + return DirectiveDelegate, Delegate{ + DelegatorAddress: dAddr, + ValidatorAddress: vAddr, + Amount: big.NewInt(100), + } + } + gasPrice := big.NewInt(1) + return NewStakingTransaction(0, 21000, gasPrice, stakePayloadMaker) +} + func CreateTestNewTransaction() (*StakingTransaction, error) { dAddr, _ := common2.Bech32ToAddress(testAccount) @@ -96,9 +111,86 @@ func TestTransactionCopy(t *testing.T) { if cv1.CommissionRates.Rate.Equal(cv2.CommissionRates.Rate) { t.Errorf("CommissionRate should not be equal") } +} + +func TestHash(t *testing.T) { + stakingTx, err := CreateTestNewTransaction() + if err != nil { + t.Errorf("cannot create new staking transaction, %v\n", err) + } + hash := stakingTx.Hash() + if hash.String() == "" { + t.Errorf("cannot get hash of staking transaction, %v\n", err) + } + if stakingTx.Hash().String() != hash.String() { + t.Errorf("cannot set hash of staking transaction\n") + } +} + +func TestGasCost(t *testing.T) { + stakingTx, err := CreateTestNewTransaction() + if err != nil { + t.Errorf("cannot create validator staking transaction, %v\n", err) + } + if stakingTx.Gas() != 600000 { + t.Errorf("gas set incorrectly \n") + } + if stakingTx.GasPrice().Int64() != big.NewInt(1).Int64() { + t.Errorf("gas price set incorrectly \n") + } + cost, err := stakingTx.Cost() + if err != nil || cost.Int64() != 600100 { + t.Errorf("staking transaction cost is incorrect %v\n", err) + } + delegateTx, err := createDelegate() + if err != nil { + t.Errorf("cannot create delegate staking transaction, %v\n", err) + } + cost, err = delegateTx.Cost() + if err != nil || cost.Int64() != 21100 { + t.Errorf("staking transaction cost is incorrect %v\n", err) + } + dAddr, _ := common2.Bech32ToAddress(testAccount) + vAddr, _ := common2.Bech32ToAddress(testAccount) + delegateTx1, err := NewStakingTransaction(0, 21000, big.NewInt(1), func() (Directive, interface{}) { + return DirectiveCreateValidator, Delegate{ + DelegatorAddress: dAddr, + ValidatorAddress: vAddr, + Amount: big.NewInt(100), + } + }) + if _, err = delegateTx1.Cost(); err == nil { + t.Error("expected", errStakingTransactionTypeCastErr, "got", nil) + } +} + +func TestNonce(t *testing.T) { + stakingTx, err := CreateTestNewTransaction() + if err != nil { + t.Errorf("cannot create validator staking transaction, %v\n", err) + } + if stakingTx.Nonce() != 0 { + t.Error("incorrect nonce \n") + } +} - fmt.Println("cv1", cv1) - fmt.Println("cv2", cv2) - fmt.Println("cv1", cv1.Description) - fmt.Println("cv2", cv2.Description) +func TestData(t *testing.T) { + stakingTx, err := CreateTestNewTransaction() + if err != nil { + t.Errorf("cannot create validator staking transaction, %v\n", err) + } + encoded, err := stakingTx.RLPEncodeStakeMsg() + if err != nil { + t.Errorf("could not rlp encode staking tx %v\n", err) + } + if bytes.Compare(stakingTx.Data(), encoded) != 0 { + t.Error("RLPEncode and Data does not match \n") + } + if _, err = RLPDecodeStakeMsg(encoded, DirectiveCreateValidator); err != nil { + t.Errorf("could not rlp decode staking tx %v\n", err) + } + e := errors.New("rlp: expected input string or byte for common.Address, decoding into (types.Delegate).ValidatorAddress") + if _, err = RLPDecodeStakeMsg(encoded, DirectiveDelegate); err == nil { + t.Error("expected", e, "got", nil) + } } diff --git a/staking/types/validator_test.go b/staking/types/validator_test.go index 6a5c576e66..f8e579c9ba 100644 --- a/staking/types/validator_test.go +++ b/staking/types/validator_test.go @@ -1,18 +1,441 @@ package types import ( + "fmt" "math/big" + "strings" + "testing" - "github.com/ethereum/go-ethereum/common" + common "github.com/ethereum/go-ethereum/common" + "github.com/harmony-one/bls/ffi/go/bls" + "github.com/harmony-one/harmony/crypto/hash" + common2 "github.com/harmony-one/harmony/internal/common" + "github.com/harmony-one/harmony/internal/ctxerror" "github.com/harmony-one/harmony/numeric" + "github.com/harmony-one/harmony/shard" + "github.com/pkg/errors" ) -func CreateNewValidator() Validator { +var ( + testAddr1, _ = common2.Bech32ToAddress("one1pdv9lrdwl0rg5vglh4xtyrv3wjk3wsqket7zxy") + validatorAddr = common.Address(testAddr1) + desc = Description{ + Name: "john", + Identity: "john", + Website: "harmony.one.wen", + SecurityContact: "wenSecurity", + Details: "wenDetails", + } + blsPubKey = "ba41f49d70d40434110e32b269dc9b52879ca5fb2aee01c49311c45e008a4b6494c3bd9e6ef7954e39d25d023243b898" + blsPriKey = "0a8c69c12020a762e7087f52bacbe835b8a91728f7310a191e026001f753a00e" + slotPubKeys = setSlotPubKeys() + slotKeySigs = setSlotKeySigs() + + validator = createNewValidator() + wrapper = createNewValidatorWrapper(validator) + + delegationAmt1 = big.NewInt(1e18) + delegation1 = NewDelegation(delegatorAddr, delegationAmt1) + + longNameDesc = Description{ + Name: "WayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayneWayne1", + Identity: "wen", + Website: "harmony.one.wen", + SecurityContact: "wenSecurity", + Details: "wenDetails", + } + longIdentityDesc = Description{ + Name: "Wayne", + Identity: "wenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwe1", + Website: "harmony.one.wen", + SecurityContact: "wenSecurity", + Details: "wenDetails", + } + longWebsiteDesc = Description{ + Name: "Wayne", + Identity: "wen", + Website: "harmony.one.wenharmony.one.wenharmony.one.wenharmony.one.wenharmony.one.wenharmony.one.wenharmony.one.wenharmony.one.wenharmony.one.wenharmo1", + SecurityContact: "wenSecurity", + Details: "wenDetails", + } + longSecurityContactDesc = Description{ + Name: "Wayne", + Identity: "wen", + Website: "harmony.one.wen", + SecurityContact: "wenSecuritywenSecuritywenSecuritywenSecuritywenSecuritywenSecuritywenSecuritywenSecuritywenSecuritywenSecuritywenSecuritywenSecuritywenSecuri", + Details: "wenDetails", + } + longDetailsDesc = Description{ + Name: "Wayne", + Identity: "wen", + Website: "harmony.one.wen", + SecurityContact: "wenSecurity", + Details: "wenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswwenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetailswenDetails", + } +) + +// Using public keys to create slot for validator +func setSlotPubKeys() []shard.BlsPublicKey { + p := &bls.PublicKey{} + p.DeserializeHexStr(blsPubKey) + pub := shard.BlsPublicKey{} + pub.FromLibBLSPublicKey(p) + return []shard.BlsPublicKey{pub} +} + +// Using private keys to create sign slot for message.CreateValidator +func setSlotKeySigs() []shard.BLSSignature { + messageBytes := []byte(BlsVerificationStr) + privateKey := &bls.SecretKey{} + privateKey.DeserializeHexStr(blsPriKey) + msgHash := hash.Keccak256(messageBytes) + signature := privateKey.SignHash(msgHash[:]) + var sig shard.BLSSignature + copy(sig[:], signature.Serialize()) + return []shard.BLSSignature{sig} +} + +// create a new validator +func createNewValidator() Validator { cr := CommissionRates{Rate: numeric.OneDec(), MaxRate: numeric.OneDec(), MaxChangeRate: numeric.ZeroDec()} c := Commission{cr, big.NewInt(300)} - d := Description{Name: "SuperHero", Identity: "YouWillNotKnow", Website: "under_construction", Details: "N/A"} - v := Validator{Address: common.Address{}, SlotPubKeys: nil, - LastEpochInCommittee: big.NewInt(20), MinSelfDelegation: big.NewInt(7), - Active: false, Commission: c, Description: d} + d := Description{Name: "Wayne", Identity: "wen", Website: "harmony.one.wen", Details: "best"} + v := Validator{ + Address: validatorAddr, + SlotPubKeys: slotPubKeys, + LastEpochInCommittee: big.NewInt(20), + MinSelfDelegation: big.NewInt(1e18), + MaxTotalDelegation: big.NewInt(3e18), + Active: false, + Commission: c, + Description: d, + CreationHeight: big.NewInt(12306), + } return v } + +// create a new validator wrapper +func createNewValidatorWrapper(v Validator) ValidatorWrapper { + return ValidatorWrapper{ + Validator: v, + } +} + +// Test MarshalValidator +func TestMarshalValidator(t *testing.T) { + _, err := MarshalValidator(validator) + if err != nil { + t.Errorf("MarshalValidator failed") + } +} + +// Test UnmarshalValidator +func TestMarshalUnmarshalValidator(t *testing.T) { + tmp, _ := MarshalValidator(validator) + _, err := UnmarshalValidator(tmp) + if err != nil { + t.Errorf("UnmarshalValidator failed!") + } +} + +// Test Print Slot Public Keys +func TestPrintSlotPubKeys(t *testing.T) { + printSlotPubKeys(validator.SlotPubKeys) +} + +func TestTotalDelegation(t *testing.T) { + // add a delegation to validator + // delegation.Amount = 10000 + wrapper.Delegations = append(wrapper.Delegations, delegation1) + totalNum := wrapper.TotalDelegation() + + // check if the numebr is 10000 + if totalNum.Cmp(big.NewInt(1e18)) != 0 { + t.Errorf("TotalDelegation number is not right") + } +} + +// check the validator wrapper's sanity +func TestValidatorSanityCheck(t *testing.T) { + err := validator.SanityCheck() + if err != nil { + t.Error("expected", nil, "got", err) + } + + v := Validator{ + Address: validatorAddr, + } + if err := v.SanityCheck(); err != errNeedAtLeastOneSlotKey { + t.Error("expected", errNeedAtLeastOneSlotKey, "got", err) + } + + v.SlotPubKeys = setSlotPubKeys() + if err := v.SanityCheck(); err != errNilMinSelfDelegation { + t.Error("expected", errNilMinSelfDelegation, "got", err) + } + v.MinSelfDelegation = big.NewInt(1e18) + if err := v.SanityCheck(); err != errNilMaxTotalDelegation { + t.Error("expected", errNilMaxTotalDelegation, "got", err) + } + v.MinSelfDelegation = big.NewInt(1e17) + v.MaxTotalDelegation = big.NewInt(3e18) + e := errors.Wrapf( + errMinSelfDelegationTooSmall, + "delegation-given %s", v.MinSelfDelegation.String(), + ) + if err := v.SanityCheck(); err.Error() != e.Error() { + t.Error("expected", e, "got", err) + } + + v.MinSelfDelegation = big.NewInt(3e18) + v.MaxTotalDelegation = big.NewInt(1e18) + e = errors.Wrapf( + errInvalidMaxTotalDelegation, + "max-total-delegation %s min-self-delegation %s", + v.MaxTotalDelegation.String(), + v.MinSelfDelegation.String(), + ) + if err := v.SanityCheck(); err.Error() != e.Error() { + t.Error("expected", e, "got", err) + } + v.MinSelfDelegation = big.NewInt(1e18) + v.MaxTotalDelegation = big.NewInt(3e18) + minusOneDec, _ := numeric.NewDecFromStr("-1") + plusTwoDec, _ := numeric.NewDecFromStr("2") + cr := CommissionRates{Rate: minusOneDec, MaxRate: numeric.OneDec(), MaxChangeRate: numeric.ZeroDec()} + c := Commission{cr, big.NewInt(300)} + v.Commission = c + e = errors.Wrapf( + errInvalidCommissionRate, "rate:%s", v.Rate.String(), + ) + if err := v.SanityCheck(); err.Error() != e.Error() { + t.Error("expected", e, "got", err) + } + v.Commission.Rate = plusTwoDec + e = errors.Wrapf( + errInvalidCommissionRate, "rate:%s", v.Rate.String(), + ) + if err := v.SanityCheck(); err.Error() != e.Error() { + t.Error("expected", e, "got", err) + } + v.Commission.Rate = numeric.MustNewDecFromStr("0.5") + v.Commission.MaxRate = minusOneDec + e = errors.Wrapf( + errInvalidCommissionRate, "rate:%s", v.MaxRate.String(), + ) + if err := v.SanityCheck(); err.Error() != e.Error() { + t.Error("expected", e, "got", err) + } + v.Commission.MaxRate = plusTwoDec + e = errors.Wrapf( + errInvalidCommissionRate, "rate:%s", v.MaxRate.String(), + ) + if err := v.SanityCheck(); err.Error() != e.Error() { + t.Error("expected", e, "got", err) + } + v.Commission.MaxRate = numeric.MustNewDecFromStr("0.9") + v.Commission.MaxChangeRate = minusOneDec + e = errors.Wrapf( + errInvalidCommissionRate, "rate:%s", v.MaxChangeRate.String(), + ) + if err := v.SanityCheck(); err.Error() != e.Error() { + t.Error("expected", e, "got", err) + } + v.Commission.MaxChangeRate = plusTwoDec + e = errors.Wrapf( + errInvalidCommissionRate, "rate:%s", v.MaxChangeRate.String(), + ) + if err := v.SanityCheck(); err.Error() != e.Error() { + t.Error("expected", e, "got", err) + } + v.Commission.MaxChangeRate = numeric.MustNewDecFromStr("0.05") + v.Commission.MaxRate = numeric.MustNewDecFromStr("0.41") + e = errors.Wrapf( + errCommissionRateTooLarge, "rate:%s", v.MaxRate.String(), + ) + if err := v.SanityCheck(); err.Error() != e.Error() { + t.Error("expected", e, "got", err) + } + v.Commission.MaxRate = numeric.MustNewDecFromStr("0.51") + v.Commission.MaxChangeRate = numeric.MustNewDecFromStr("0.95") + e = errors.Wrapf( + errCommissionRateTooLarge, "rate:%s", v.MaxChangeRate.String(), + ) + if err := v.SanityCheck(); err.Error() != e.Error() { + t.Error("expected", e, "got", err) + } + v.Commission.MaxChangeRate = numeric.MustNewDecFromStr("0.05") + v.SlotPubKeys = append(v.SlotPubKeys, v.SlotPubKeys[0]) + if err := v.SanityCheck(); err != errDuplicateSlotKeys { + t.Error("expected", errDuplicateSlotKeys, "got", err) + } +} + +func TestValidatorWrapperSanityCheck(t *testing.T) { + // no delegation must fail + wrapper := createNewValidatorWrapper(createNewValidator()) + if err := wrapper.SanityCheck(); err == nil { + t.Error("expected", errInvalidSelfDelegation, "got", err) + } + + // valid self delegation must not fail + valDel := NewDelegation(validatorAddr, big.NewInt(1e18)) + wrapper.Delegations = []Delegation{valDel} + if err := wrapper.SanityCheck(); err != nil { + t.Errorf("validator wrapper SanityCheck failed: %s", err) + } + + // invalid self delegation must fail + valDel = NewDelegation(validatorAddr, big.NewInt(1e17)) + wrapper.Delegations = []Delegation{valDel} + if err := wrapper.SanityCheck(); err == nil { + t.Error("expected", errInvalidSelfDelegation, "got", err) + } +} + +func testEnsureLength(t *testing.T) { + // test name length > MaxNameLength + if _, err := longNameDesc.EnsureLength(); err == nil { + t.Error("expected", ctxerror.New("[EnsureLength] Exceed Maximum Length", "have", len(longNameDesc.Name), "maxNameLen", MaxNameLength), "got", err) + } + // test identity length > MaxIdentityLength + if _, err := longIdentityDesc.EnsureLength(); err == nil { + t.Error("expected", ctxerror.New("[EnsureLength] Exceed Maximum Length", "have", len(longIdentityDesc.Identity), "maxIdentityLen", MaxIdentityLength), "got", err) + } + // test website length > MaxWebsiteLength + if _, err := longWebsiteDesc.EnsureLength(); err == nil { + t.Error("expected", ctxerror.New("[EnsureLength] Exceed Maximum Length", "have", len(longWebsiteDesc.Website), "maxWebsiteLen", MaxWebsiteLength), "got", err) + } + // test security contact length > MaxSecurityContactLength + if _, err := longSecurityContactDesc.EnsureLength(); err == nil { + t.Error("expected", ctxerror.New("[EnsureLength] Exceed Maximum Length", "have", len(longSecurityContactDesc.SecurityContact), "maxSecurityContactLen", MaxSecurityContactLength), "got", err) + } + // test details length > MaxDetailsLength + if _, err := longDetailsDesc.EnsureLength(); err == nil { + t.Error("expected", ctxerror.New("[EnsureLength] Exceed Maximum Length", "have", len(longDetailsDesc.Details), "maxDetailsLen", MaxDetailsLength), "got", err) + } +} + +func TestEnsureLength(t *testing.T) { + _, err := validator.Description.EnsureLength() + if err != nil { + t.Error("expected", "nil", "got", err) + } + testEnsureLength(t) +} + +func TestUpdateDescription(t *testing.T) { + // create two description + d1 := Description{ + Name: "Wayne", + Identity: "wen", + Website: "harmony.one.wen", + SecurityContact: "wenSecurity", + Details: "wenDetails", + } + d2 := Description{ + Name: "John", + Identity: "jw", + Website: "harmony.one.john", + SecurityContact: "johnSecurity", + Details: "johnDetails", + } + d1, _ = UpdateDescription(d1, d2) + + // check whether update description function work? + if compareTwoDescription(d1, d2) { + t.Errorf("UpdateDescription failed") + } +} + +// compare two descriptions' items +func compareTwoDescription(d1, d2 Description) bool { + return (strings.Compare(d1.Name, d2.Name) != 0 && + strings.Compare(d1.Identity, d2.Identity) != 0 && + strings.Compare(d1.Website, d2.Website) != 0 && + strings.Compare(d1.SecurityContact, d2.SecurityContact) != 0 && + strings.Compare(d1.Details, d2.Details) != 0) +} + +// test get validator's address +func TestGetAddress(t *testing.T) { + if validator.GetAddress() != validator.Address { + t.Errorf("validator GetAddress failed") + } +} + +// test get validator's name +func TestGetName(t *testing.T) { + if strings.Compare(validator.GetName(), validator.Name) != 0 { + t.Errorf("validator GetName failed") + } +} + +// test get validator's commission Rate +func TestGetCommissionRate(t *testing.T) { + if validator.GetCommissionRate() != validator.Commission.Rate { + t.Errorf("validator GetCommissionRate failed") + } +} + +// test get validator's min self delegation +func TestGetMinSelfDelegation(t *testing.T) { + if validator.GetMinSelfDelegation().Cmp(validator.MinSelfDelegation) != 0 { + t.Errorf("validator GetMinSelfDelegation failed") + } +} + +func TestVerifyBLSKeys(t *testing.T) { + // test verify bls for valid single key/sig pair + val := CreateValidator{ + ValidatorAddress: validatorAddr, + Description: desc, + SlotPubKeys: slotPubKeys, + SlotKeySigs: slotKeySigs, + Amount: big.NewInt(1e18), + } + err := VerifyBLSKeys(val.SlotPubKeys, val.SlotKeySigs) + if err != nil { + t.Errorf("VerifyBLSKeys failed") + } + + // test verify bls for not matching single key/sig pair + + // test verify bls for not length matching multiple key/sig pairs + + // test verify bls for not order matching multiple key/sig pairs + + // test verify bls for empty key/sig pairs +} + +func TestCreateValidatorFromNewMsg(t *testing.T) { + v := CreateValidator{ + ValidatorAddress: validatorAddr, + Description: desc, + Amount: big.NewInt(1e18), + } + blockNum := big.NewInt(1000) + _, err := CreateValidatorFromNewMsg(&v, blockNum) + if err != nil { + t.Errorf("CreateValidatorFromNewMsg failed") + } +} + +func TestUpdateValidatorFromEditMsg(t *testing.T) { + ev := EditValidator{ + ValidatorAddress: validatorAddr, + Description: desc, + MinSelfDelegation: big.NewInt(2e18), + MaxTotalDelegation: big.NewInt(6e18), + } + UpdateValidatorFromEditMsg(&validator, &ev) + + if validator.MinSelfDelegation.Cmp(big.NewInt(2e18)) != 0 { + t.Errorf("UpdateValidatorFromEditMsg failed") + } +} + +func TestString(t *testing.T) { + // print out the string + fmt.Println(validator.String()) +}