Skip to content

Commit

Permalink
go/keymanager/churp: Allow nodes to apply for a new committee
Browse files Browse the repository at this point in the history
  • Loading branch information
peternose committed Feb 24, 2024
1 parent 8200b6d commit e3cfd0b
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .changelog/5551.feature.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
go/keymanager/churp: Add create and update methods
go/keymanager/churp: Allow key managers to create/update scheme
1 change: 1 addition & 0 deletions .changelog/5568.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go/keymanager/churp: Allow nodes to apply for a new committee
6 changes: 6 additions & 0 deletions go/consensus/cometbft/apps/keymanager/churp/ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ func (ext *churpExt) ExecuteTx(ctx *tmapi.Context, tx *transaction.Transaction)
return api.ErrInvalidArgument
}
return ext.update(ctx, &cfg)
case churp.MethodApply:
var reg churp.SignedApplicationRequest
if err := cbor.Unmarshal(tx.Body, &reg); err != nil {
return api.ErrInvalidArgument
}
return ext.apply(ctx, &reg)
default:
panic(fmt.Sprintf("keymanager: churp: invalid method: %s", tx.Method))
}
Expand Down
111 changes: 111 additions & 0 deletions go/consensus/cometbft/apps/keymanager/churp/txs.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
"fmt"

beacon "github.com/oasisprotocol/oasis-core/go/beacon/api"
"github.com/oasisprotocol/oasis-core/go/common/crypto/signature"
"github.com/oasisprotocol/oasis-core/go/common/node"
tmapi "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api"
churpState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/keymanager/churp/state"
"github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/keymanager/common"
registryState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/state"
"github.com/oasisprotocol/oasis-core/go/keymanager/churp"
)

Expand Down Expand Up @@ -192,6 +195,114 @@ func (ext *churpExt) update(ctx *tmapi.Context, req *churp.UpdateRequest) error
return nil
}

func (ext *churpExt) apply(ctx *tmapi.Context, req *churp.SignedApplicationRequest) error {
// Prepare states.
state := churpState.NewMutableState(ctx.State())
regState := registryState.NewMutableState(ctx.State())

// Ensure that the runtime exists and is a key manager.
kmRt, err := common.KeyManagerRuntime(ctx, req.Application.RuntimeID)
if err != nil {
return err
}

// Get the existing status.
status, err := state.Status(ctx, req.Application.RuntimeID, req.Application.ID)
if err != nil {
return fmt.Errorf("keymanager: churp: non-existing ID: %d", req.Application.ID)
}

// Allow applications one epoch before the next handoff.
now, err := ext.state.GetCurrentEpoch(ctx)
if err != nil {
return err
}

switch status.NextHandoff {
case churp.HandoffsDisabled:
return fmt.Errorf("keymanager: churp: handoffs disabled")
case now + 1:
default:
return fmt.Errorf("keymanager: churp: submissions closed")
}

if status.Round != req.Application.Round {
return fmt.Errorf("keymanager: churp: invalid round: got %d, expected %d", req.Application.Round, status.Round)
}

// Allow only one application per round, to ensure the node's
// verification matrix (commitment) doesn't change.
nodeID := ctx.TxSigner()
if _, ok := status.Applications[nodeID]; ok {
return fmt.Errorf("keymanager: churp: application already submitted")
}

// Verify the node.
n, err := regState.Node(ctx, nodeID)
if err != nil {
return err
}
if n.IsExpired(uint64(now)) {
return fmt.Errorf("keymanager: churp: node registration expired")
}
if !n.HasRoles(node.RoleKeyManager) {
return fmt.Errorf("keymanager: churp: node not key manager")
}

// Verify RAK signature.
nodeRt, err := common.NodeRuntime(n, kmRt.ID)
if err != nil {
return err
}
rak, err := common.RuntimeAttestationKey(nodeRt, kmRt)
if err != nil {
return fmt.Errorf("keymanager: churp: failed to fetch node's rak: %w", err)
}
if err = req.VerifyRAK(rak); err != nil {
return fmt.Errorf("keymanager: churp: invalid signature: %w", err)
}

if ctx.IsCheckOnly() {
return nil
}

// Charge gas for this operation.
kmParams, err := state.ConsensusParameters(ctx)
if err != nil {
return err
}
if err = ctx.Gas().UseGas(1, churp.GasOpApply, kmParams.GasCosts); err != nil {
return err
}

// Return early if simulating since this is just estimating gas.
if ctx.IsSimulation() {
return nil
}

// Ok, as far as we can tell the application is valid, apply it.
if status.Applications == nil {
status.Applications = make(map[signature.PublicKey]churp.Application)
}
status.Applications[nodeID] = churp.Application{
Checksum: req.Application.Checksum,
Reconstructed: false,
}

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
}

func (ext *churpExt) computeNextHandoff(ctx *tmapi.Context) (beacon.EpochTime, error) {
// The next handoff will start at the beginning of the next epoch,
// meaning that nodes need to send their applications until the end
Expand Down
Loading

0 comments on commit e3cfd0b

Please sign in to comment.