From d5b49b22f7c204a4136c01123f93529980befbd8 Mon Sep 17 00:00:00 2001 From: ptrus Date: Wed, 4 Mar 2020 10:47:22 +0100 Subject: [PATCH 1/2] txsource: delegation workload --- .changelog/2752.feature.md | 1 + .../cmd/debug/txsource/workload/delegation.go | 283 ++++++++++++++++++ .../debug/txsource/workload/registration.go | 2 +- .../cmd/debug/txsource/workload/workload.go | 1 + go/oasis-test-runner/scenario/e2e/txsource.go | 2 + .../txsource/staking-genesis.json | 1 + 6 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 .changelog/2752.feature.md create mode 100644 go/oasis-node/cmd/debug/txsource/workload/delegation.go diff --git a/.changelog/2752.feature.md b/.changelog/2752.feature.md new file mode 100644 index 00000000000..c43506934f6 --- /dev/null +++ b/.changelog/2752.feature.md @@ -0,0 +1 @@ +txsource: delegation workload diff --git a/go/oasis-node/cmd/debug/txsource/workload/delegation.go b/go/oasis-node/cmd/debug/txsource/workload/delegation.go new file mode 100644 index 00000000000..daead36006a --- /dev/null +++ b/go/oasis-node/cmd/debug/txsource/workload/delegation.go @@ -0,0 +1,283 @@ +package workload + +import ( + "context" + "fmt" + "math/rand" + "time" + + "google.golang.org/grpc" + + "github.com/oasislabs/oasis-core/go/common/crypto/signature" + memorySigner "github.com/oasislabs/oasis-core/go/common/crypto/signature/signers/memory" + "github.com/oasislabs/oasis-core/go/common/logging" + consensus "github.com/oasislabs/oasis-core/go/consensus/api" + "github.com/oasislabs/oasis-core/go/consensus/api/transaction" + runtimeClient "github.com/oasislabs/oasis-core/go/runtime/client/api" + staking "github.com/oasislabs/oasis-core/go/staking/api" +) + +const ( + // NameDelegation is the name of the delegation workload. + NameDelegation = "delegation" + + delegationNumAccounts = 10 + delegateAmount = 100 +) + +type delegation struct { + logger *logging.Logger + + accounts []struct { + signer signature.Signer + reckonedNonce uint64 + delegatedTo signature.PublicKey + debondEndTime uint64 + } + fundingAccount signature.Signer +} + +func (d *delegation) doEscrowTx(ctx context.Context, rng *rand.Rand, cnsc consensus.ClientBackend) error { + d.logger.Debug("escrow tx flow") + + // Get current epoch. + epoch, err := cnsc.GetEpoch(ctx, consensus.HeightLatest) + if err != nil { + return fmt.Errorf("GetEpoch: %w", err) + } + + // Select an account that has no active delegations nor debonding funds. + perm := rng.Perm(delegationNumAccounts) + fromPermIdx := -1 + var empty signature.PublicKey + for i := range d.accounts { + if d.accounts[perm[i]].delegatedTo == empty && d.accounts[perm[i]].debondEndTime < uint64(epoch) { + fromPermIdx = i + break + } + } + if fromPermIdx == -1 { + d.logger.Debug("all accounts already delegating or debonding, skipping delegation") + return nil + } + + // Select an account to delegate to. + toPermIdx := rng.Intn(delegationNumAccounts) + + // Remember index. + selectedIdx := perm[fromPermIdx] + + // Update local state. + d.accounts[selectedIdx].delegatedTo = d.accounts[perm[toPermIdx]].signer.Public() + + // Create escrow tx. + escrow := &staking.Escrow{ + Account: d.accounts[selectedIdx].delegatedTo, + } + if err = escrow.Tokens.FromInt64(delegateAmount); err != nil { + return fmt.Errorf("escrow amount error: %w", err) + } + + tx := staking.NewAddEscrowTx(d.accounts[selectedIdx].reckonedNonce, &transaction.Fee{}, escrow) + d.accounts[selectedIdx].reckonedNonce++ + + // Estimate gas. + gas, err := cnsc.EstimateGas(ctx, &consensus.EstimateGasRequest{ + Caller: d.accounts[selectedIdx].signer.Public(), + Transaction: tx, + }) + if err != nil { + return fmt.Errorf("failed to estimate gas: %w", err) + } + tx.Fee.Gas = gas + feeAmount := int64(gas) * gasPrice + if err = tx.Fee.Amount.FromInt64(feeAmount); err != nil { + return fmt.Errorf("fee amount from int64: %w", err) + } + + // Fund account to cover Escrow fees. + // We only do one escrow per account at a time, so `delegateAmount` + // funds (that are Escrowed) should already be in the balance. + fundAmount := int64(gas) * gasPrice // transaction costs + if err = transferFunds(ctx, d.logger, cnsc, d.fundingAccount, d.accounts[selectedIdx].signer.Public(), fundAmount); err != nil { + return fmt.Errorf("account funding failure: %w", err) + } + + // Sign transaction. + signedTx, err := transaction.Sign(d.accounts[selectedIdx].signer, tx) + if err != nil { + return fmt.Errorf("transaction.Sign: %w", err) + } + d.logger.Debug("submitting escrow transaction", + "from", d.accounts[selectedIdx].signer.Public(), + "to", d.accounts[selectedIdx].delegatedTo, + ) + + // Submit transaction. + if err = cnsc.SubmitTx(ctx, signedTx); err != nil { + return fmt.Errorf("cnsc.SubmitTx: %w", err) + } + + return nil +} + +func (d *delegation) doReclaimEscrowTx(ctx context.Context, rng *rand.Rand, cnsc consensus.ClientBackend, stakingClient staking.Backend) error { + d.logger.Debug("reclaim escrow tx") + + // Select an account that has active delegation. + perm := rng.Perm(delegationNumAccounts) + fromPermIdx := -1 + var empty signature.PublicKey + for i := range d.accounts { + if d.accounts[perm[i]].delegatedTo != empty { + fromPermIdx = i + break + } + } + if fromPermIdx == -1 { + d.logger.Debug("no accounts delegating, skipping reclaim") + return nil + } + selectedIdx := perm[fromPermIdx] + + // Query amount of delegated shares for the account. + delegations, err := stakingClient.Delegations(ctx, &staking.OwnerQuery{ + Height: consensus.HeightLatest, + Owner: d.accounts[selectedIdx].signer.Public(), + }) + if err != nil { + return fmt.Errorf("stakingClient.Delegations %s: %w", d.accounts[selectedIdx].signer.Public(), err) + } + delegation := delegations[d.accounts[selectedIdx].delegatedTo] + if delegation == nil { + d.logger.Error("missing expected delegation", + "delegator", d.accounts[selectedIdx].signer.Public(), + "account", d.accounts[selectedIdx].delegatedTo, + "delegations", delegations, + ) + return fmt.Errorf("missing expected delegation by account: %s in account: %s", + d.accounts[selectedIdx].signer.Public(), d.accounts[selectedIdx].delegatedTo) + } + + // Create ReclaimEscrow tx. + reclaim := &staking.ReclaimEscrow{ + Account: d.accounts[selectedIdx].delegatedTo, + Shares: delegation.Shares, + } + tx := staking.NewReclaimEscrowTx(d.accounts[selectedIdx].reckonedNonce, &transaction.Fee{}, reclaim) + d.accounts[selectedIdx].reckonedNonce++ + + // Estimate gas. + gas, err := cnsc.EstimateGas(ctx, &consensus.EstimateGasRequest{ + Caller: d.accounts[selectedIdx].signer.Public(), + Transaction: tx, + }) + if err != nil { + return fmt.Errorf("failed to estimate gas: %w", err) + } + tx.Fee.Gas = gas + feeAmount := int64(gas) * gasPrice + if err = tx.Fee.Amount.FromInt64(feeAmount); err != nil { + return fmt.Errorf("fee amount from int64: %w", err) + } + + // Fund account to cover reclaim escrow fees. + fundAmount := int64(gas) * gasPrice // transaction costs + if err = transferFunds(ctx, d.logger, cnsc, d.fundingAccount, d.accounts[selectedIdx].signer.Public(), fundAmount); err != nil { + return fmt.Errorf("account funding failure: %w", err) + } + + signedTx, err := transaction.Sign(d.accounts[selectedIdx].signer, tx) + if err != nil { + return fmt.Errorf("transaction.Sign: %w", err) + } + + d.logger.Debug("submitting reclaim escrow transaction", + "reclaim_from", d.accounts[selectedIdx].delegatedTo, + "account", d.accounts[selectedIdx].signer.Public(), + ) + + // Submit transaction. + if err = cnsc.SubmitTx(ctx, signedTx); err != nil { + return fmt.Errorf("cnsc.SubmitTx: %w", err) + } + + // Query debonding end epoch for the account. + var debondingDelegations map[signature.PublicKey][]*staking.DebondingDelegation + debondingDelegations, err = stakingClient.DebondingDelegations(ctx, &staking.OwnerQuery{ + Height: consensus.HeightLatest, + Owner: d.accounts[selectedIdx].signer.Public(), + }) + if err != nil { + return fmt.Errorf("stakingClient.Delegations %s: %w", d.accounts[selectedIdx].signer.Public(), err) + } + debondingDelegation := debondingDelegations[d.accounts[selectedIdx].delegatedTo] + if len(debondingDelegation) == 0 { + d.logger.Error("missing expected debonding delegation", + "delegator", d.accounts[selectedIdx].signer.Public(), + "account", d.accounts[selectedIdx].delegatedTo, + "debonding_delegations", debondingDelegation, + ) + return fmt.Errorf("missing expected debonding delegation by account: %s in account: %s", + d.accounts[selectedIdx].signer.Public(), d.accounts[selectedIdx].delegatedTo) + } + + // Update local state. + d.accounts[selectedIdx].delegatedTo = empty + d.accounts[selectedIdx].debondEndTime = uint64(debondingDelegation[0].DebondEndTime) + + return nil +} + +func (d *delegation) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient, fundingAccount signature.Signer) error { + var err error + ctx := context.Background() + + d.logger = logging.GetLogger("cmd/txsource/workload/delegation") + d.fundingAccount = fundingAccount + + fac := memorySigner.NewFactory() + d.accounts = make([]struct { + signer signature.Signer + reckonedNonce uint64 + delegatedTo signature.PublicKey + debondEndTime uint64 + }, delegationNumAccounts) + + for i := range d.accounts { + d.accounts[i].signer, err = fac.Generate(signature.SignerEntity, rng) + if err != nil { + return fmt.Errorf("memory signer factory Generate account %d: %w", i, err) + } + + // Fund the account with delegation amount. + // Funds for fee's will be transferred before making transactions. + if err = transferFunds(ctx, d.logger, cnsc, fundingAccount, d.accounts[i].signer.Public(), delegateAmount); err != nil { + return fmt.Errorf("account funding failure: %w", err) + } + } + + stakingClient := staking.NewStakingClient(conn) + + for { + switch rng.Intn(2) { + case 0: + if err = d.doEscrowTx(ctx, rng, cnsc); err != nil { + return err + } + case 1: + if err = d.doReclaimEscrowTx(ctx, rng, cnsc, stakingClient); err != nil { + return err + } + default: + return fmt.Errorf("unimplemented") + } + + select { + case <-time.After(1 * time.Second): + case <-gracefulExit.Done(): + d.logger.Debug("time's up") + return nil + } + } +} diff --git a/go/oasis-node/cmd/debug/txsource/workload/registration.go b/go/oasis-node/cmd/debug/txsource/workload/registration.go index 2365387e49a..c068315b69c 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/registration.go +++ b/go/oasis-node/cmd/debug/txsource/workload/registration.go @@ -357,10 +357,10 @@ func (r *registration) Run(gracefulExit context.Context, rng *rand.Rand, conn *g ) select { + case <-time.After(1 * time.Second): case <-gracefulExit.Done(): registryLogger.Debug("time's up") return nil - default: } } } diff --git a/go/oasis-node/cmd/debug/txsource/workload/workload.go b/go/oasis-node/cmd/debug/txsource/workload/workload.go index 853aa994506..0aff6f023f0 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/workload.go +++ b/go/oasis-node/cmd/debug/txsource/workload/workload.go @@ -126,4 +126,5 @@ var ByName = map[string]Workload{ NameOversized: oversized{}, NameRegistration: ®istration{}, NameParallel: parallel{}, + NameDelegation: &delegation{}, } diff --git a/go/oasis-test-runner/scenario/e2e/txsource.go b/go/oasis-test-runner/scenario/e2e/txsource.go index 42e7be08fed..1b305509a57 100644 --- a/go/oasis-test-runner/scenario/e2e/txsource.go +++ b/go/oasis-test-runner/scenario/e2e/txsource.go @@ -39,6 +39,7 @@ var TxSourceMultiShort scenario.Scenario = &txSourceImpl{ workload.NameOversized, workload.NameRegistration, workload.NameParallel, + workload.NameDelegation, }, timeLimit: timeLimitShort, livenessCheckInterval: livenessCheckInterval, @@ -52,6 +53,7 @@ var TxSourceMulti scenario.Scenario = &txSourceImpl{ workload.NameOversized, workload.NameRegistration, workload.NameParallel, + workload.NameDelegation, }, timeLimit: timeLimitLong, nodeRestartInterval: nodeRestartIntervalLong, diff --git a/tests/fixture-data/txsource/staking-genesis.json b/tests/fixture-data/txsource/staking-genesis.json index 551789a0d03..a90db5d40cf 100644 --- a/tests/fixture-data/txsource/staking-genesis.json +++ b/tests/fixture-data/txsource/staking-genesis.json @@ -1,5 +1,6 @@ { "params": { + "debonding_interval": 2, "gas_costs": { "transfer": 10, "burn": 10, From ecc2ac35698adb164cbfc32cc9737c94e101b1a2 Mon Sep 17 00:00:00 2001 From: ptrus Date: Tue, 10 Mar 2020 11:46:12 +0100 Subject: [PATCH 2/2] staking/state: add Debonding queries unit tests --- .../tendermint/apps/staking/state/state.go | 2 - .../apps/staking/state/state_test.go | 229 +++++++++++++----- 2 files changed, 168 insertions(+), 63 deletions(-) diff --git a/go/consensus/tendermint/apps/staking/state/state.go b/go/consensus/tendermint/apps/staking/state/state.go index cd927e9b432..08a5118df72 100644 --- a/go/consensus/tendermint/apps/staking/state/state.go +++ b/go/consensus/tendermint/apps/staking/state/state.go @@ -249,7 +249,6 @@ func (s *ImmutableState) DelegationsFor(delegatorID signature.PublicKey) (map[si if !delegationKeyFmt.Decode(key, &escrowID, &decDelegatorID) { return true } - // TODO: add unit test for DelegationsFor. if !decDelegatorID.Equal(delegatorID) { return false } @@ -310,7 +309,6 @@ func (s *ImmutableState) DebondingDelegationsFor(delegatorID signature.PublicKey if !debondingDelegationKeyFmt.Decode(key, &decDelegatorID, &escrowID) { return true } - // TODO: add unit test for DebondingDelegationsFor. if !decDelegatorID.Equal(delegatorID) { return false } diff --git a/go/consensus/tendermint/apps/staking/state/state_test.go b/go/consensus/tendermint/apps/staking/state/state_test.go index 996aef7adb4..1e007b0c598 100644 --- a/go/consensus/tendermint/apps/staking/state/state_test.go +++ b/go/consensus/tendermint/apps/staking/state/state_test.go @@ -14,11 +14,13 @@ import ( memorySigner "github.com/oasislabs/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasislabs/oasis-core/go/common/quantity" "github.com/oasislabs/oasis-core/go/consensus/tendermint/abci" + epochtime "github.com/oasislabs/oasis-core/go/epochtime/api" staking "github.com/oasislabs/oasis-core/go/staking/api" ) func mustInitQuantity(t *testing.T, i int64) (q quantity.Quantity) { - require.NoError(t, q.FromBigInt(big.NewInt(i)), "FromBigInt") + err := q.FromBigInt(big.NewInt(i)) + require.NoError(t, err, "FromBigInt") return } @@ -27,16 +29,105 @@ func mustInitQuantityP(t *testing.T, i int64) *quantity.Quantity { return &q } +func TestDelegationQueries(t *testing.T) { + numDelegatorAccounts := 5 + + require := require.New(t) + + db := dbm.NewMemDB() + tree := iavl.NewMutableTree(db, 128) + s := NewMutableState(tree) + + fac := memorySigner.NewFactory() + + // Generate escrow account. + escrowSigner, err := fac.Generate(signature.SignerEntity, rand.Reader) + require.NoError(err, "generating escrow signer") + escrowID := escrowSigner.Public() + + var escrowAccount staking.Account + s.SetAccount(escrowID, &escrowAccount) + + // Generate delegator accounts. + var delegatorIDs []signature.PublicKey + // Store expected delegations. + expectedDelegations := make(map[signature.PublicKey]map[signature.PublicKey]*staking.Delegation) + expectedDelegations[escrowID] = map[signature.PublicKey]*staking.Delegation{} + expectedDebDelegations := make(map[signature.PublicKey]map[signature.PublicKey][]*staking.DebondingDelegation) + expectedDebDelegations[escrowID] = map[signature.PublicKey][]*staking.DebondingDelegation{} + + for i := int64(1); i <= int64(numDelegatorAccounts); i++ { + signer, serr := fac.Generate(signature.SignerEntity, rand.Reader) + require.NoError(serr, "memory signer factory Generate account") + id := signer.Public() + + delegatorIDs = append(delegatorIDs, id) + + // Init account. + var account staking.Account + account.General.Nonce = uint64(i) + err = account.General.Balance.FromBigInt(big.NewInt(2 * i * 100)) + require.NoError(err, "initialize delegator account general balance") + + // Init delegation. + var del staking.Delegation + err = escrowAccount.Escrow.Active.Deposit(&del.Shares, &account.General.Balance, mustInitQuantityP(t, i*100)) + require.NoError(err, "active escrow deposit") + expectedDelegations[escrowID][id] = &del + + // Init debonding delegation. + var deb staking.DebondingDelegation + deb.DebondEndTime = epochtime.EpochTime(i) + err = escrowAccount.Escrow.Debonding.Deposit(&deb.Shares, &account.General.Balance, mustInitQuantityP(t, i*100)) + require.NoError(err, "debonding escrow deposit") + expectedDebDelegations[escrowID][id] = []*staking.DebondingDelegation{&deb} + + // Update state. + s.SetAccount(id, &account) + s.SetDelegation(id, escrowID, &del) + s.SetDebondingDelegation(id, escrowID, uint64(i), &deb) + } + + // Test delegation queries. + for _, id := range delegatorIDs { + accDelegations, derr := s.DelegationsFor(id) + require.NoError(derr, "DelegationsFor") + expectedDelegation := map[signature.PublicKey]*staking.Delegation{ + escrowID: expectedDelegations[escrowID][id], + } + require.EqualValues(expectedDelegation, accDelegations, "DelegationsFor account should match expected delegations") + } + delegations, err := s.Delegations() + require.NoError(err, "state.Delegations") + require.EqualValues(expectedDelegations, delegations, "Delegations should match expected delegations") + + // Test debonding delegation queries. + for _, id := range delegatorIDs { + accDebDelegations, derr := s.DebondingDelegationsFor(id) + require.NoError(derr, "DebondingDelegationsFor") + expectedDebDelegation := map[signature.PublicKey][]*staking.DebondingDelegation{ + escrowID: expectedDebDelegations[escrowID][id], + } + require.EqualValues(expectedDebDelegation, accDebDelegations, "DebondingDelegationsFor account should match expected") + } + debDelegations, err := s.DebondingDelegations() + require.NoError(err, "state.DebondingDelegations") + require.EqualValues(expectedDebDelegations, debDelegations, "DebondingDelegations should match expected") +} + func TestRewardAndSlash(t *testing.T) { + require := require.New(t) + delegatorSigner, err := memorySigner.NewSigner(rand.Reader) - require.NoError(t, err, "generating delegator signer") + require.NoError(err, "generating delegator signer") delegatorID := delegatorSigner.Public() delegatorAccount := &staking.Account{} delegatorAccount.General.Nonce = 10 - require.NoError(t, delegatorAccount.General.Balance.FromBigInt(big.NewInt(300)), "initialize delegator account general balance") + err = delegatorAccount.General.Balance.FromBigInt(big.NewInt(300)) + require.NoError(err, "initialize delegator account general balance") escrowSigner, err := memorySigner.NewSigner(rand.Reader) - require.NoError(t, err, "generating escrow signer") + require.NoError(err, "generating escrow signer") escrowID := escrowSigner.Public() escrowAccountOnly := []signature.PublicKey{escrowID} escrowAccount := &staking.Account{} @@ -55,19 +146,23 @@ func TestRewardAndSlash(t *testing.T) { }, }, } - require.NoError(t, escrowAccount.Escrow.CommissionSchedule.PruneAndValidateForGenesis(&staking.CommissionScheduleRules{ - RateChangeInterval: 10, - RateBoundLead: 30, - MaxRateSteps: 4, - MaxBoundSteps: 12, - }, 0), "commission schedule") + err = escrowAccount.Escrow.CommissionSchedule.PruneAndValidateForGenesis( + &staking.CommissionScheduleRules{ + RateChangeInterval: 10, + RateBoundLead: 30, + MaxRateSteps: 4, + MaxBoundSteps: 12, + }, 0) + require.NoError(err, "commission schedule") del := &staking.Delegation{} - require.NoError(t, escrowAccount.Escrow.Active.Deposit(&del.Shares, &delegatorAccount.General.Balance, mustInitQuantityP(t, 100)), "active escrow deposit") + err = escrowAccount.Escrow.Active.Deposit(&del.Shares, &delegatorAccount.General.Balance, mustInitQuantityP(t, 100)) + require.NoError(err, "active escrow deposit") - deb := &staking.DebondingDelegation{} + var deb staking.DebondingDelegation deb.DebondEndTime = 21 - require.NoError(t, escrowAccount.Escrow.Debonding.Deposit(&deb.Shares, &delegatorAccount.General.Balance, mustInitQuantityP(t, 100)), "debonding escrow deposit") + err = escrowAccount.Escrow.Debonding.Deposit(&deb.Shares, &delegatorAccount.General.Balance, mustInitQuantityP(t, 100)) + require.NoError(err, "debonding escrow deposit") db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, 128) @@ -97,107 +192,119 @@ func TestRewardAndSlash(t *testing.T) { s.SetAccount(delegatorID, delegatorAccount) s.SetAccount(escrowID, escrowAccount) s.SetDelegation(delegatorID, escrowID, del) - s.SetDebondingDelegation(delegatorID, escrowID, 1, deb) + s.SetDebondingDelegation(delegatorID, escrowID, 1, &deb) // Epoch 10 is during the first step. - require.NoError(t, s.AddRewards(10, mustInitQuantityP(t, 100), escrowAccountOnly), "add rewards epoch 10") + err = s.AddRewards(10, mustInitQuantityP(t, 100), escrowAccountOnly) + require.NoError(err, "add rewards epoch 10") // 100% gain. delegatorAccount = s.Account(delegatorID) - require.Equal(t, mustInitQuantity(t, 100), delegatorAccount.General.Balance, "reward first step - delegator general") + require.Equal(mustInitQuantity(t, 100), delegatorAccount.General.Balance, "reward first step - delegator general") escrowAccount = s.Account(escrowID) - require.Equal(t, mustInitQuantity(t, 200), escrowAccount.Escrow.Active.Balance, "reward first step - escrow active escrow") - require.Equal(t, mustInitQuantity(t, 100), escrowAccount.Escrow.Debonding.Balance, "reward first step - escrow debonding escrow") + require.Equal(mustInitQuantity(t, 200), escrowAccount.Escrow.Active.Balance, "reward first step - escrow active escrow") + require.Equal(mustInitQuantity(t, 100), escrowAccount.Escrow.Debonding.Balance, "reward first step - escrow debonding escrow") // Reward is 100 tokens, with 80 added to the pool and 20 deposited as commission. // We add to the pool first, so the delegation becomes 100 shares : 180 tokens. // Then we deposit the 20 for commission, which comes out to 11 shares. del = s.Delegation(delegatorID, escrowID) - require.Equal(t, mustInitQuantity(t, 100), del.Shares, "reward first step - delegation shares") + require.Equal(mustInitQuantity(t, 100), del.Shares, "reward first step - delegation shares") escrowSelfDel := s.Delegation(escrowID, escrowID) - require.Equal(t, mustInitQuantity(t, 11), escrowSelfDel.Shares, "reward first step - escrow self delegation shares") + require.Equal(mustInitQuantity(t, 11), escrowSelfDel.Shares, "reward first step - escrow self delegation shares") commonPool, err := s.CommonPool() - require.NoError(t, err, "load common pool") - require.Equal(t, mustInitQuantityP(t, 9900), commonPool, "reward first step - common pool") + require.NoError(err, "load common pool") + require.Equal(mustInitQuantityP(t, 9900), commonPool, "reward first step - common pool") // Epoch 30 is in the second step. - require.NoError(t, s.AddRewards(30, mustInitQuantityP(t, 100), escrowAccountOnly), "add rewards epoch 30") + require.NoError(s.AddRewards(30, mustInitQuantityP(t, 100), escrowAccountOnly), "add rewards epoch 30") // 50% gain. escrowAccount = s.Account(escrowID) - require.Equal(t, mustInitQuantity(t, 300), escrowAccount.Escrow.Active.Balance, "reward boundary epoch - escrow active escrow") + require.Equal(mustInitQuantity(t, 300), escrowAccount.Escrow.Active.Balance, "reward boundary epoch - escrow active escrow") commonPool, err = s.CommonPool() - require.NoError(t, err, "load common pool") - require.Equal(t, mustInitQuantityP(t, 9800), commonPool, "reward first step - common pool") + require.NoError(err, "load common pool") + require.Equal(mustInitQuantityP(t, 9800), commonPool, "reward first step - common pool") // Epoch 99 is after the end of the schedule - require.NoError(t, s.AddRewards(99, mustInitQuantityP(t, 100), escrowAccountOnly), "add rewards epoch 99") + err = s.AddRewards(99, mustInitQuantityP(t, 100), escrowAccountOnly) + require.NoError(err, "add rewards epoch 99") // No change. escrowAccount = s.Account(escrowID) - require.Equal(t, mustInitQuantity(t, 300), escrowAccount.Escrow.Active.Balance, "reward late epoch - escrow active escrow") + require.Equal(mustInitQuantity(t, 300), escrowAccount.Escrow.Active.Balance, "reward late epoch - escrow active escrow") slashedNonzero, err := s.SlashEscrow(abci.NewMockContext(abci.ContextDeliverTx, time.Now()), escrowID, mustInitQuantityP(t, 40)) - require.NoError(t, err, "slash escrow") - require.True(t, slashedNonzero, "slashed nonzero") + require.NoError(err, "slash escrow") + require.True(slashedNonzero, "slashed nonzero") // 40 token loss. delegatorAccount = s.Account(delegatorID) - require.Equal(t, mustInitQuantity(t, 100), delegatorAccount.General.Balance, "slash - delegator general") + require.Equal(mustInitQuantity(t, 100), delegatorAccount.General.Balance, "slash - delegator general") escrowAccount = s.Account(escrowID) - require.Equal(t, mustInitQuantity(t, 270), escrowAccount.Escrow.Active.Balance, "slash - escrow active escrow") - require.Equal(t, mustInitQuantity(t, 90), escrowAccount.Escrow.Debonding.Balance, "slash - escrow debonding escrow") + require.Equal(mustInitQuantity(t, 270), escrowAccount.Escrow.Active.Balance, "slash - escrow active escrow") + require.Equal(mustInitQuantity(t, 90), escrowAccount.Escrow.Debonding.Balance, "slash - escrow debonding escrow") commonPool, err = s.CommonPool() - require.NoError(t, err, "load common pool") - require.Equal(t, mustInitQuantityP(t, 9840), commonPool, "slash - common pool") + require.NoError(err, "load common pool") + require.Equal(mustInitQuantityP(t, 9840), commonPool, "slash - common pool") // Epoch 10 is during the first step. - require.NoError(t, s.AddRewardSingleAttenuated(10, mustInitQuantityP(t, 10), 5, 10, escrowID), "add attenuated rewards epoch 30") + err = s.AddRewardSingleAttenuated(10, mustInitQuantityP(t, 10), 5, 10, escrowID) + require.NoError(err, "add attenuated rewards epoch 30") // 5% gain. escrowAccount = s.Account(escrowID) - require.Equal(t, mustInitQuantity(t, 283), escrowAccount.Escrow.Active.Balance, "attenuated reward - escrow active escrow") + require.Equal(mustInitQuantity(t, 283), escrowAccount.Escrow.Active.Balance, "attenuated reward - escrow active escrow") commonPool, err = s.CommonPool() - require.NoError(t, err, "load common pool") - require.Equal(t, mustInitQuantityP(t, 9827), commonPool, "reward attenuated - common pool") + require.NoError(err, "load common pool") + require.Equal(mustInitQuantityP(t, 9827), commonPool, "reward attenuated - common pool") } func TestEpochSigning(t *testing.T) { + require := require.New(t) + db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, 128) s := NewMutableState(tree) es, err := s.EpochSigning() - require.NoError(t, err, "load epoch signing info") - require.Zero(t, es.Total, "empty epoch signing info total") - require.Empty(t, es.ByEntity, "empty epoch signing info by entity") + require.NoError(err, "load epoch signing info") + require.Zero(es.Total, "empty epoch signing info total") + require.Empty(es.ByEntity, "empty epoch signing info by entity") var truant, exact, perfect signature.PublicKey - require.NoError(t, truant.UnmarshalHex("1111111111111111111111111111111111111111111111111111111111111111"), "initializing 'truant' ID") - require.NoError(t, exact.UnmarshalHex("3333333333333333333333333333333333333333333333333333333333333333"), "initializing 'exact' ID") - require.NoError(t, perfect.UnmarshalHex("4444444444444444444444444444444444444444444444444444444444444444"), "initializing 'perfect' ID") + err = truant.UnmarshalHex("1111111111111111111111111111111111111111111111111111111111111111") + require.NoError(err, "initializing 'truant' ID") + err = exact.UnmarshalHex("3333333333333333333333333333333333333333333333333333333333333333") + require.NoError(err, "initializing 'exact' ID") + err = perfect.UnmarshalHex("4444444444444444444444444444444444444444444444444444444444444444") + require.NoError(err, "initializing 'perfect' ID") - require.NoError(t, es.Update([]signature.PublicKey{truant, exact, perfect}), "updating epoch signing info") - require.NoError(t, es.Update([]signature.PublicKey{exact, perfect}), "updating epoch signing info") - require.NoError(t, es.Update([]signature.PublicKey{exact, perfect}), "updating epoch signing info") - require.NoError(t, es.Update([]signature.PublicKey{perfect}), "updating epoch signing info") - require.EqualValues(t, 4, es.Total, "populated epoch signing info total") - require.Len(t, es.ByEntity, 3, "populated epoch signing info by entity") + err = es.Update([]signature.PublicKey{truant, exact, perfect}) + require.NoError(err, "updating epoch signing info") + err = es.Update([]signature.PublicKey{exact, perfect}) + require.NoError(err, "updating epoch signing info") + err = es.Update([]signature.PublicKey{exact, perfect}) + require.NoError(err, "updating epoch signing info") + err = es.Update([]signature.PublicKey{perfect}) + require.NoError(err, "updating epoch signing info") + require.EqualValues(4, es.Total, "populated epoch signing info total") + require.Len(es.ByEntity, 3, "populated epoch signing info by entity") s.SetEpochSigning(es) esRoundTrip, err := s.EpochSigning() - require.NoError(t, err, "load epoch signing info 2") - require.Equal(t, es, esRoundTrip, "epoch signing info round trip") + require.NoError(err, "load epoch signing info 2") + require.Equal(es, esRoundTrip, "epoch signing info round trip") eligibleEntities, err := es.EligibleEntities(3, 4) - require.NoError(t, err, "determining eligible entities") - require.Len(t, eligibleEntities, 2, "eligible entities") - require.NotContains(t, eligibleEntities, truant, "'truant' not eligible") - require.Contains(t, eligibleEntities, exact, "'exact' eligible") - require.Contains(t, eligibleEntities, perfect, "'perfect' eligible") + require.NoError(err, "determining eligible entities") + require.Len(eligibleEntities, 2, "eligible entities") + require.NotContains(eligibleEntities, truant, "'truant' not eligible") + require.Contains(eligibleEntities, exact, "'exact' eligible") + require.Contains(eligibleEntities, perfect, "'perfect' eligible") s.ClearEpochSigning() esClear, err := s.EpochSigning() - require.NoError(t, err, "load cleared epoch signing info") - require.Zero(t, esClear.Total, "cleared epoch signing info total") - require.Empty(t, esClear.ByEntity, "cleared epoch signing info by entity") + require.NoError(err, "load cleared epoch signing info") + require.Zero(esClear.Total, "cleared epoch signing info total") + require.Empty(esClear.ByEntity, "cleared epoch signing info by entity") }