Skip to content

Commit

Permalink
go/consensus/tendermint: Correctly propagate errors
Browse files Browse the repository at this point in the history
Not propagating the state unavailable error could lead to corruption when the
database becomes unavailable (e.g., due to running out of space or file
descriptors).
  • Loading branch information
kostko committed Jul 3, 2021
1 parent e85fcca commit f58415f
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changelog/4110.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
go/consensus/tendermint: Correctly propagate errors

Not propagating the state unavailable error could lead to corruption when the
database becomes unavailable (e.g., due to running out of space or file
descriptors).
15 changes: 9 additions & 6 deletions go/consensus/tendermint/apps/staking/proposing_rewards.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,28 @@ import (
abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api"
registryState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/registry/state"
stakingState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/staking/state"
registry "github.com/oasisprotocol/oasis-core/go/registry/api"
staking "github.com/oasisprotocol/oasis-core/go/staking/api"
)

func (app *stakingApplication) resolveEntityIDFromProposer(
ctx *abciAPI.Context,
regState *registryState.MutableState,
request types.RequestBeginBlock,
) *signature.PublicKey {
var proposingEntity *signature.PublicKey
) (*signature.PublicKey, error) {
proposerNode, err := regState.NodeByConsensusAddress(ctx, request.Header.ProposerAddress)
if err != nil {
switch err {
case nil:
case registry.ErrNoSuchNode:
ctx.Logger().Warn("failed to get proposer node",
"err", err,
"address", hex.EncodeToString(request.Header.ProposerAddress),
)
} else {
proposingEntity = &proposerNode.EntityID
return nil, nil
default:
return nil, err
}
return proposingEntity
return &proposerNode.EntityID, nil
}

func (app *stakingApplication) rewardBlockProposing(
Expand Down
18 changes: 12 additions & 6 deletions go/consensus/tendermint/apps/staking/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,35 @@ func (app *stakingApplication) BeginBlock(ctx *api.Context, request types.Reques
stakeState := stakingState.NewMutableState(ctx.State())

// Look up the proposer's entity.
proposingEntity := app.resolveEntityIDFromProposer(ctx, regState, request)
proposingEntity, err := app.resolveEntityIDFromProposer(ctx, regState, request)
if err != nil {
return fmt.Errorf("failed to resolve proposer entity ID: %w", err)
}

// Go through all voters of the previous block and resolve entities.
// numEligibleValidators is how many total validators are in the validator set, while
// votingEntities is from the validators which actually voted.
numEligibleValidators := len(request.GetLastCommitInfo().Votes)
votingEntities := app.resolveEntityIDsFromVotes(ctx, regState, request.GetLastCommitInfo())
votingEntities, err := app.resolveEntityIDsFromVotes(ctx, regState, request.GetLastCommitInfo())
if err != nil {
return fmt.Errorf("failed to resolve entity IDs from votes: %w", err)
}

// Disburse fees from previous block.
if err := app.disburseFeesVQ(ctx, stakeState, proposingEntity, numEligibleValidators, votingEntities); err != nil {
if err = app.disburseFeesVQ(ctx, stakeState, proposingEntity, numEligibleValidators, votingEntities); err != nil {
return fmt.Errorf("disburse fees voters and next proposer: %w", err)
}

// Save block proposer for fee disbursements.
stakingState.SetBlockProposer(ctx, proposingEntity)

// Add rewards for proposer.
if err := app.rewardBlockProposing(ctx, stakeState, proposingEntity, numEligibleValidators, len(votingEntities)); err != nil {
if err = app.rewardBlockProposing(ctx, stakeState, proposingEntity, numEligibleValidators, len(votingEntities)); err != nil {
return fmt.Errorf("staking: block proposing reward: %w", err)
}

// Track signing for rewards.
if err := app.updateEpochSigning(ctx, stakeState, votingEntities); err != nil {
if err = app.updateEpochSigning(ctx, stakeState, votingEntities); err != nil {
return fmt.Errorf("staking: failed to update epoch signing info: %w", err)
}

Expand All @@ -101,7 +107,7 @@ func (app *stakingApplication) BeginBlock(ctx *api.Context, request types.Reques
continue
}

if err := onEvidenceByzantineConsensus(ctx, reason, evidence.Validator.Address, evidence.Height, evidence.Time, evidence.Validator.Power); err != nil {
if err = onEvidenceByzantineConsensus(ctx, reason, evidence.Validator.Address, evidence.Height, evidence.Time, evidence.Validator.Power); err != nil {
return err
}
}
Expand Down
15 changes: 12 additions & 3 deletions go/consensus/tendermint/apps/staking/votes.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import (
"github.com/oasisprotocol/oasis-core/go/common/crypto/signature"
abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api"
registryState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/registry/state"
registry "github.com/oasisprotocol/oasis-core/go/registry/api"
)

func (app *stakingApplication) resolveEntityIDsFromVotes(ctx *abciAPI.Context, regState *registryState.MutableState, lastCommitInfo types.LastCommitInfo) []signature.PublicKey {
func (app *stakingApplication) resolveEntityIDsFromVotes(
ctx *abciAPI.Context,
regState *registryState.MutableState,
lastCommitInfo types.LastCommitInfo,
) ([]signature.PublicKey, error) {
var entityIDs []signature.PublicKey
for _, a := range lastCommitInfo.Votes {
if !a.SignedLastBlock {
Expand All @@ -20,16 +25,20 @@ func (app *stakingApplication) resolveEntityIDsFromVotes(ctx *abciAPI.Context, r

// Map address to node/entity.
node, err := regState.NodeByConsensusAddress(ctx, valAddr)
if err != nil {
switch err {
case nil:
case registry.ErrNoSuchNode:
ctx.Logger().Warn("failed to get validator node",
"err", err,
"address", hex.EncodeToString(valAddr),
)
continue
default:
return nil, err
}

entityIDs = append(entityIDs, node.EntityID)
}

return entityIDs
return entityIDs, nil
}

0 comments on commit f58415f

Please sign in to comment.