-
Notifications
You must be signed in to change notification settings - Fork 112
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5551 from oasisprotocol/peternose/feature/churp-c…
…reate go/keymanager/churp: Add create and update methods
- Loading branch information
Showing
18 changed files
with
1,729 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
go/keymanager/churp: Add create and update methods |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package churp | ||
|
||
import ( | ||
"fmt" | ||
|
||
beacon "github.com/oasisprotocol/oasis-core/go/beacon/api" | ||
tmapi "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api" | ||
churpState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/keymanager/churp/state" | ||
registryState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/state" | ||
"github.com/oasisprotocol/oasis-core/go/keymanager/churp" | ||
) | ||
|
||
func (ext *churpExt) onEpochChange(ctx *tmapi.Context, epoch beacon.EpochTime) error { | ||
// Query the runtime and node lists. | ||
state := churpState.NewMutableState(ctx.State()) | ||
regState := registryState.NewMutableState(ctx.State()) | ||
runtimes, _ := regState.Runtimes(ctx) | ||
|
||
for _, rt := range runtimes { | ||
statuses, err := state.Statuses(ctx, rt.ID) | ||
if err != nil { | ||
return fmt.Errorf("keymanager: churp: failed to fetch runtime statuses: %w", err) | ||
} | ||
|
||
for _, status := range statuses { | ||
if status.NextHandoff == churp.HandoffsDisabled { | ||
continue | ||
} | ||
|
||
switch epoch { | ||
case status.NextHandoff: | ||
// The epoch for the handoff just started, meaning that registrations | ||
// are now closed. If not enough nodes applied for the next committee, | ||
// we need to reset applications and start collecting again. | ||
minCommitteeSize := int(status.Threshold)*2 + 1 | ||
if len(status.Applications) >= minCommitteeSize { | ||
continue | ||
} | ||
case status.NextHandoff + 1: | ||
// Handoff ended. Not all nodes replicated the secret and confirmed it, | ||
// as otherwise the next handoff epoch would be updated. | ||
// Reset and start collecting again | ||
default: | ||
continue | ||
} | ||
|
||
// The handoff failed, so postpone the round to the next epoch, giving | ||
// nodes one epoch time to submit applications. | ||
status.Applications = nil | ||
status.Checksum = nil | ||
status.NextHandoff = epoch + 1 | ||
|
||
if err := state.SetStatus(ctx, status); err != nil { | ||
ctx.Logger().Error("keymanager: churp: failed to set status", | ||
"err", err, | ||
) | ||
return fmt.Errorf("keymanager: churp: failed to set status: %w", err) | ||
} | ||
|
||
ctx.EmitEvent(tmapi.NewEventBuilder(ext.appName).TypedAttribute(&churp.UpdateEvent{ | ||
Status: status, | ||
})) | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package churp | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/cometbft/cometbft/abci/types" | ||
|
||
"github.com/oasisprotocol/oasis-core/go/common/cbor" | ||
"github.com/oasisprotocol/oasis-core/go/consensus/api" | ||
"github.com/oasisprotocol/oasis-core/go/consensus/api/transaction" | ||
tmapi "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api" | ||
genesis "github.com/oasisprotocol/oasis-core/go/genesis/api" | ||
"github.com/oasisprotocol/oasis-core/go/keymanager/churp" | ||
) | ||
|
||
// Ensure that the CHURP extension implements the Extension interface. | ||
var _ tmapi.Extension = (*churpExt)(nil) | ||
|
||
type churpExt struct { | ||
appName string | ||
state tmapi.ApplicationState | ||
} | ||
|
||
// New creates a new CHRUP extension for the key manager application. | ||
func New(appName string) tmapi.Extension { | ||
return &churpExt{ | ||
appName: appName, | ||
} | ||
} | ||
|
||
// Methods implements api.Extension. | ||
func (ext *churpExt) Methods() []transaction.MethodName { | ||
return churp.Methods | ||
} | ||
|
||
// OnRegister implements api.Extension. | ||
func (ext *churpExt) OnRegister(state tmapi.ApplicationState, _ tmapi.MessageDispatcher) { | ||
ext.state = state | ||
} | ||
|
||
// ExecuteTx implements api.Extension. | ||
func (ext *churpExt) ExecuteTx(ctx *tmapi.Context, tx *transaction.Transaction) error { | ||
switch tx.Method { | ||
case churp.MethodCreate: | ||
var cfg churp.CreateRequest | ||
if err := cbor.Unmarshal(tx.Body, &cfg); err != nil { | ||
return api.ErrInvalidArgument | ||
} | ||
return ext.create(ctx, &cfg) | ||
case churp.MethodUpdate: | ||
var cfg churp.UpdateRequest | ||
if err := cbor.Unmarshal(tx.Body, &cfg); err != nil { | ||
return api.ErrInvalidArgument | ||
} | ||
return ext.update(ctx, &cfg) | ||
default: | ||
panic(fmt.Sprintf("keymanager: churp: invalid method: %s", tx.Method)) | ||
} | ||
} | ||
|
||
// BeginBlock implements api.Extension. | ||
func (ext *churpExt) BeginBlock(ctx *tmapi.Context) error { | ||
changed, epoch := ext.state.EpochChanged(ctx) | ||
if !changed { | ||
return nil | ||
} | ||
|
||
return ext.onEpochChange(ctx, epoch) | ||
} | ||
|
||
// EndBlock implements api.Extension. | ||
func (*churpExt) EndBlock(*tmapi.Context) error { | ||
return nil | ||
} | ||
|
||
func (ext *churpExt) InitChain(*tmapi.Context, types.RequestInitChain, *genesis.Document) error { | ||
return nil | ||
} |
167 changes: 167 additions & 0 deletions
167
go/consensus/cometbft/apps/keymanager/churp/state/state.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package state | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/oasisprotocol/oasis-core/go/common" | ||
"github.com/oasisprotocol/oasis-core/go/common/cbor" | ||
"github.com/oasisprotocol/oasis-core/go/common/keyformat" | ||
consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" | ||
abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api" | ||
"github.com/oasisprotocol/oasis-core/go/keymanager/churp" | ||
"github.com/oasisprotocol/oasis-core/go/storage/mkvs" | ||
) | ||
|
||
var ( | ||
// parametersKeyFmt is the consensus parameters key format. | ||
// | ||
// Value is CBOR-serialized churp.ConsensusParameters. | ||
parametersKeyFmt = consensus.KeyFormat.New(0x74) | ||
|
||
// statusKeyFmt is the status key format. | ||
// | ||
// Key format is: 0x75 <runtime-id> <churp-id>. | ||
// Value is CBOR-serialized churp.Status. | ||
statusKeyFmt = consensus.KeyFormat.New(0x75, keyformat.H(&common.Namespace{}), uint8(0)) | ||
) | ||
|
||
// ImmutableState is a immutable state wrapper. | ||
type ImmutableState struct { | ||
is *abciAPI.ImmutableState | ||
} | ||
|
||
// ConsensusParameters returns the consensus parameters. | ||
func (st *ImmutableState) ConsensusParameters(ctx context.Context) (*churp.ConsensusParameters, error) { | ||
raw, err := st.is.Get(ctx, parametersKeyFmt.Encode()) | ||
if err != nil { | ||
return nil, abciAPI.UnavailableStateError(err) | ||
} | ||
if raw == nil { | ||
return nil, fmt.Errorf("cometbft/keymanager/churp: expected consensus parameters to be present in app state") | ||
} | ||
|
||
var params churp.ConsensusParameters | ||
if err = cbor.Unmarshal(raw, ¶ms); err != nil { | ||
return nil, abciAPI.UnavailableStateError(err) | ||
} | ||
return ¶ms, nil | ||
} | ||
|
||
// Status returns the CHURP status for the specified runtime and CHURP instance. | ||
func (st *ImmutableState) Status(ctx context.Context, runtimeID common.Namespace, churpID uint8) (*churp.Status, error) { | ||
data, err := st.is.Get(ctx, statusKeyFmt.Encode(&runtimeID, churpID)) | ||
if err != nil { | ||
return nil, abciAPI.UnavailableStateError(err) | ||
} | ||
if data == nil { | ||
return nil, churp.ErrNoSuchStatus | ||
} | ||
|
||
var status churp.Status | ||
if err := cbor.Unmarshal(data, &status); err != nil { | ||
return nil, abciAPI.UnavailableStateError(err) | ||
} | ||
return &status, nil | ||
} | ||
|
||
// Statuses returns the CHURP statuses for the specified runtime. | ||
func (st *ImmutableState) Statuses(ctx context.Context, runtimeID common.Namespace) ([]*churp.Status, error) { | ||
it := st.is.NewIterator(ctx) | ||
defer it.Close() | ||
|
||
// We need to pre-hash the runtime ID, so we can compare it below. | ||
runtimeIDHash := keyformat.PreHashed(runtimeID.Hash()) | ||
|
||
var statuses []*churp.Status | ||
for it.Seek(statusKeyFmt.Encode(&runtimeID)); it.Valid(); it.Next() { | ||
var ( | ||
hash keyformat.PreHashed | ||
churpID uint8 | ||
) | ||
if !statusKeyFmt.Decode(it.Key(), &hash, &churpID) { | ||
break | ||
} | ||
if runtimeIDHash != hash { | ||
break | ||
} | ||
|
||
var status churp.Status | ||
if err := cbor.Unmarshal(it.Value(), &status); err != nil { | ||
return nil, abciAPI.UnavailableStateError(err) | ||
} | ||
statuses = append(statuses, &status) | ||
} | ||
if it.Err() != nil { | ||
return nil, abciAPI.UnavailableStateError(it.Err()) | ||
} | ||
|
||
return statuses, nil | ||
} | ||
|
||
// AllStatuses returns the CHURP statuses for all runtimes. | ||
func (st *ImmutableState) AllStatuses(ctx context.Context) ([]*churp.Status, error) { | ||
it := st.is.NewIterator(ctx) | ||
defer it.Close() | ||
|
||
var statuses []*churp.Status | ||
for it.Seek(statusKeyFmt.Encode()); it.Valid(); it.Next() { | ||
if !statusKeyFmt.Decode(it.Key()) { | ||
break | ||
} | ||
|
||
var status churp.Status | ||
if err := cbor.Unmarshal(it.Value(), &status); err != nil { | ||
return nil, abciAPI.UnavailableStateError(err) | ||
} | ||
statuses = append(statuses, &status) | ||
} | ||
if it.Err() != nil { | ||
return nil, abciAPI.UnavailableStateError(it.Err()) | ||
} | ||
|
||
return statuses, nil | ||
} | ||
|
||
// NewImmutableState creates a new immutable state wrapper. | ||
func NewImmutableState(ctx context.Context, state abciAPI.ApplicationQueryState, version int64) (*ImmutableState, error) { | ||
is, err := abciAPI.NewImmutableState(ctx, state, version) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &ImmutableState{is}, nil | ||
} | ||
|
||
// MutableState is a mutable state wrapper. | ||
type MutableState struct { | ||
*ImmutableState | ||
|
||
ms mkvs.KeyValueTree | ||
} | ||
|
||
// SetConsensusParameters updates the state using the provided consensus parameters. | ||
// | ||
// This method must only be called from InitChain or EndBlock contexts. | ||
func (st *MutableState) SetConsensusParameters(ctx context.Context, params *churp.ConsensusParameters) error { | ||
if err := st.is.CheckContextMode(ctx, []abciAPI.ContextMode{abciAPI.ContextInitChain, abciAPI.ContextEndBlock}); err != nil { | ||
return err | ||
} | ||
err := st.ms.Insert(ctx, parametersKeyFmt.Encode(), cbor.Marshal(params)) | ||
return abciAPI.UnavailableStateError(err) | ||
} | ||
|
||
// SetStatus updates the state using the provided CHURP status. | ||
func (st *MutableState) SetStatus(ctx context.Context, status *churp.Status) error { | ||
err := st.ms.Insert(ctx, statusKeyFmt.Encode(&status.RuntimeID, status.ID), cbor.Marshal(status)) | ||
return abciAPI.UnavailableStateError(err) | ||
} | ||
|
||
// NewMutableState creates a new mutable state wrapper. | ||
func NewMutableState(tree mkvs.KeyValueTree) *MutableState { | ||
return &MutableState{ | ||
ImmutableState: &ImmutableState{ | ||
&abciAPI.ImmutableState{ImmutableKeyValueTree: tree}, | ||
}, | ||
ms: tree, | ||
} | ||
} |
Oops, something went wrong.