Skip to content

Commit

Permalink
Optionally require a deposit for registering a runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko committed Feb 6, 2020
1 parent e321eb2 commit ae52c3f
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 21 deletions.
1 change: 1 addition & 0 deletions .changelog/2638.breaking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Optionally require a deposit for registering a runtime.
23 changes: 23 additions & 0 deletions go/consensus/tendermint/apps/keymanager/keymanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ func (app *keymanagerApplication) onEpochChange(ctx *abci.Context, epoch epochti
nodes, _ := regState.Nodes()
registry.SortNodeList(nodes)

params, err := regState.ConsensusParameters()
if err != nil {
return fmt.Errorf("failed to get consensus parameters: %w", err)
}

// Recalculate all the key manager statuses.
//
// Note: This assumes that once a runtime is registered, it never expires.
Expand All @@ -105,6 +110,24 @@ func (app *keymanagerApplication) onEpochChange(ctx *abci.Context, epoch epochti
continue
}

// Suspend the runtime in case the registering entity no longer has enough stake to cover
// the entity and runtime deposits.
if !params.DebugBypassStake {
if err = registryState.EnsureSufficientRuntimeStake(ctx, rt); err != nil {
ctx.Logger().Warn("insufficient stake for key manager runtime operation",
"err", err,
"entity_id", rt.EntityID,
)

// Suspend runtime.
if err := regState.SuspendRuntime(rt.ID); err != nil {
return err
}

continue
}
}

var forceEmit bool
oldStatus, err := state.Status(rt.ID)
switch err {
Expand Down
31 changes: 31 additions & 0 deletions go/consensus/tendermint/apps/registry/state/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package state

import (
"fmt"

"github.com/oasislabs/oasis-core/go/consensus/tendermint/abci"
stakingState "github.com/oasislabs/oasis-core/go/consensus/tendermint/apps/staking/state"
registry "github.com/oasislabs/oasis-core/go/registry/api"
staking "github.com/oasislabs/oasis-core/go/staking/api"
)

// EnsureSufficientRuntimeStake checks that the given entity has sufficient stake to operate a
// given runtime (with separate thresholds for compute and key manager runtimes).
func EnsureSufficientRuntimeStake(ctx *abci.Context, rt *registry.Runtime) error {
thresholds := []staking.ThresholdKind{
staking.KindEntity,
}
switch rt.Kind {
case registry.KindCompute:
thresholds = append(thresholds, staking.KindRuntimeCompute)
case registry.KindKeyManager:
thresholds = append(thresholds, staking.KindRuntimeKeyManager)
default:
ctx.Logger().Error("RegisterRuntime: unknown runtime kind",
"runtime_id", rt.ID,
"kind", rt.Kind,
)
return fmt.Errorf("registry: unknown runtime kind (%d)", rt.Kind)
}
return stakingState.EnsureSufficientStake(ctx, rt.EntityID, thresholds)
}
18 changes: 18 additions & 0 deletions go/consensus/tendermint/apps/registry/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,12 @@ func (app *registryApplication) registerNode( // nolint: gocyclo
// If a runtime was previously suspended and this node now paid maintenance
// fees for it, resume the runtime.
for _, rt := range paidRuntimes {
// Only resume a runtime if the entity has enough stake to avoid having the runtime be
// suspended again on the next epoch transition.
if err = registryState.EnsureSufficientRuntimeStake(ctx, rt); err != nil {
continue
}

err := state.ResumeRuntime(rt.ID)
switch err {
case nil:
Expand Down Expand Up @@ -549,6 +555,18 @@ func (app *registryApplication) registerRuntime(
return registry.ErrIncorrectTxSigner
}

if !params.DebugBypassStake {
// Make sure that the entity has enough stake for at least being an entity and having a
// runtime (separate thresholds for compute and key manager runtimes).
if err = registryState.EnsureSufficientRuntimeStake(ctx, rt); err != nil {
ctx.Logger().Error("RegisterRuntime: Insufficent stake",
"err", err,
"entity_id", rt.EntityID,
)
return err
}
}

// If TEE is required, check if runtime provided at least one enclave ID.
if rt.TEEHardware != node.TEEHardwareInvalid {
switch rt.TEEHardware {
Expand Down
16 changes: 15 additions & 1 deletion go/consensus/tendermint/apps/roothash/roothash.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,21 @@ func (app *rootHashApplication) onCommitteeChanged(ctx *abci.Context, epoch epoc

// If there are no committees for this runtime, suspend the runtime as this
// means that there is noone to pay the maintenance fees.
if empty && !params.DebugDoNotSuspendRuntimes {
//
// Also suspend the runtime in case the registering entity no longer has enough stake to
// cover the entity and runtime deposits (this check is skipped if the runtime would be
// suspended anyway due to nobody being there to pay maintenance fees).
sufficientStake := true
if !empty && !params.DebugBypassStake {
if err = registryState.EnsureSufficientRuntimeStake(ctx, rt); err != nil {
ctx.Logger().Warn("insufficient stake for runtime operation",
"err", err,
"entity_id", rt.EntityID,
)
sufficientStake = false
}
}
if (empty || !sufficientStake) && !params.DebugDoNotSuspendRuntimes {
if err := app.suspendUnpaidRuntime(ctx, rtState, regState); err != nil {
return err
}
Expand Down
22 changes: 18 additions & 4 deletions go/oasis-node/cmd/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ const (
cfgEpochTimeDebugMockBackend = "epochtime.debug.mock_backend"
cfgEpochTimeTendermintInterval = "epochtime.tendermint.interval"

// Roothash config flags.
cfgRoothashDebugDoNotSuspendRuntimes = "roothash.debug.do_not_suspend_runtimes"
cfgRoothashDebugBypassStake = "roothash.debug.bypass_stake" // nolint: gosec

// Tendermint config flags.
cfgConsensusTimeoutCommit = "consensus.tendermint.timeout_commit"
cfgConsensusSkipTimeoutCommit = "consensus.tendermint.skip_timeout_commit"
Expand Down Expand Up @@ -416,6 +420,8 @@ func AppendRootHashState(doc *genesis.Document, exports []string, l *logging.Log
RuntimeStates: make(map[common.Namespace]*registry.RuntimeGenesis),

Parameters: roothash.ConsensusParameters{
DebugDoNotSuspendRuntimes: viper.GetBool(cfgRoothashDebugDoNotSuspendRuntimes),
DebugBypassStake: viper.GetBool(cfgRoothashDebugBypassStake),
// TODO: Make these configurable.
GasCosts: roothash.DefaultGasCosts,
},
Expand Down Expand Up @@ -568,10 +574,12 @@ func AppendStakingState(doc *genesis.Document, state string, l *logging.Logger)
_ = sq.FromBigInt(big.NewInt(0))
stakingSt.Parameters.Thresholds =
map[staking.ThresholdKind]quantity.Quantity{
staking.KindEntity: sq,
staking.KindValidator: sq,
staking.KindCompute: sq,
staking.KindStorage: sq,
staking.KindEntity: sq,
staking.KindValidator: sq,
staking.KindCompute: sq,
staking.KindStorage: sq,
staking.KindRuntimeCompute: sq,
staking.KindRuntimeKeyManager: sq,
}
}
}
Expand Down Expand Up @@ -725,6 +733,12 @@ func init() {
initGenesisFlags.Int64(cfgEpochTimeTendermintInterval, 86400, "Epoch interval (in blocks)")
_ = initGenesisFlags.MarkHidden(cfgEpochTimeDebugMockBackend)

// Roothash config flags.
initGenesisFlags.Bool(cfgRoothashDebugDoNotSuspendRuntimes, false, "do not suspend runtimes (UNSAFE)")
initGenesisFlags.Bool(cfgRoothashDebugBypassStake, false, "bypass all roothash stake checks and operations (UNSAFE)")
_ = initGenesisFlags.MarkHidden(cfgRoothashDebugDoNotSuspendRuntimes)
_ = initGenesisFlags.MarkHidden(cfgRoothashDebugBypassStake)

// Tendermint config flags.
initGenesisFlags.Duration(cfgConsensusTimeoutCommit, 1*time.Second, "tendermint commit timeout")
initGenesisFlags.Bool(cfgConsensusSkipTimeoutCommit, false, "skip tendermint commit timeout")
Expand Down
6 changes: 5 additions & 1 deletion go/roothash/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ type ConsensusParameters struct {
// DebugDoNotSuspendRuntimes is true iff runtimes should not be suspended
// for lack of paying maintenance fees.
DebugDoNotSuspendRuntimes bool `json:"debug_do_not_suspend_runtimes,omitempty"`

// DebugBypassStake is true iff the roothash should bypass all of the staking
// related checks and operations.
DebugBypassStake bool `json:"debug_bypass_stake,omitempty"`
}

const (
Expand Down Expand Up @@ -210,7 +214,7 @@ func SanityCheckBlocks(blocks map[common.Namespace]*block.Block) error {

// SanityCheck does basic sanity checking on the genesis state.
func (g *Genesis) SanityCheck() error {
unsafeFlags := g.Parameters.DebugDoNotSuspendRuntimes
unsafeFlags := g.Parameters.DebugDoNotSuspendRuntimes || g.Parameters.DebugBypassStake
if unsafeFlags && !flags.DebugDontBlameOasis() {
return fmt.Errorf("roothash: sanity check failed: one or more unsafe debug flags set")
}
Expand Down
18 changes: 12 additions & 6 deletions go/staking/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,12 +325,14 @@ func (p *SharePool) Withdraw(tokenDst, shareSrc, shareAmount *quantity.Quantity)
type ThresholdKind int

const (
KindEntity ThresholdKind = 0
KindValidator ThresholdKind = 1
KindCompute ThresholdKind = 2
KindStorage ThresholdKind = 3

KindMax = KindStorage
KindEntity ThresholdKind = 0
KindValidator ThresholdKind = 1
KindCompute ThresholdKind = 2
KindStorage ThresholdKind = 3
KindRuntimeCompute ThresholdKind = 4
KindRuntimeKeyManager ThresholdKind = 5

KindMax = KindRuntimeKeyManager
)

// String returns the string representation of a ThresholdKind.
Expand All @@ -344,6 +346,10 @@ func (k ThresholdKind) String() string {
return "compute"
case KindStorage:
return "storage"
case KindRuntimeCompute:
return "compute runtime"
case KindRuntimeKeyManager:
return "key manager runtime"
default:
return "[unknown threshold kind]"
}
Expand Down
10 changes: 6 additions & 4 deletions go/staking/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ func TestConsensusParameters(t *testing.T) {

// Valid thresholds.
validThresholds := map[ThresholdKind]quantity.Quantity{
KindEntity: *quantity.NewQuantity(),
KindValidator: *quantity.NewQuantity(),
KindCompute: *quantity.NewQuantity(),
KindStorage: *quantity.NewQuantity(),
KindEntity: *quantity.NewQuantity(),
KindValidator: *quantity.NewQuantity(),
KindCompute: *quantity.NewQuantity(),
KindStorage: *quantity.NewQuantity(),
KindRuntimeCompute: *quantity.NewQuantity(),
KindRuntimeKeyManager: *quantity.NewQuantity(),
}
validThresholdsParams := ConsensusParameters{
Thresholds: validThresholds,
Expand Down
10 changes: 6 additions & 4 deletions go/staking/tests/debug/debug_stake.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ var (
Parameters: api.ConsensusParameters{
DebondingInterval: 1,
Thresholds: map[api.ThresholdKind]quantity.Quantity{
api.KindEntity: QtyFromInt(1),
api.KindValidator: QtyFromInt(2),
api.KindCompute: QtyFromInt(3),
api.KindStorage: QtyFromInt(4),
api.KindEntity: QtyFromInt(1),
api.KindValidator: QtyFromInt(2),
api.KindCompute: QtyFromInt(3),
api.KindStorage: QtyFromInt(4),
api.KindRuntimeCompute: QtyFromInt(5),
api.KindRuntimeKeyManager: QtyFromInt(6),
},
Slashing: map[api.SlashReason]api.Slash{
api.SlashDoubleSigning: api.Slash{
Expand Down
4 changes: 3 additions & 1 deletion tests/fixture-data/stake-cli/staking-genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"0": "0",
"1": "0",
"2": "0",
"3": "0"
"3": "0",
"4": "0",
"5": "0"
}
}
}

0 comments on commit ae52c3f

Please sign in to comment.