Skip to content

Commit

Permalink
go/registry: Handle the old and busted node descriptor envelope
Browse files Browse the repository at this point in the history
> Crawling in my skin
> These wounds, they will not heal
  • Loading branch information
Yawning committed Jan 29, 2020
1 parent a48cd81 commit 314ad5b
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 42 deletions.
10 changes: 10 additions & 0 deletions go/consensus/tendermint/apps/registry/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ func (rq *registryQuerier) Genesis(ctx context.Context) (*registry.Genesis, erro
}

// We only want to keep the nodes that are validators.
//
// BUG: If the debonding period will apply to other nodes,
// then we need to basically persist everything.
validatorNodes := make([]*node.MultiSignedNode, 0)
for _, sn := range signedNodes {
var n node.Node
Expand All @@ -161,6 +164,13 @@ func (rq *registryQuerier) Genesis(ctx context.Context) (*registry.Genesis, erro
if n.HasRoles(node.RoleValidator) {
validatorNodes = append(validatorNodes, sn)
}

// We want to discard nodes that haven't bothered to re-register
// with the new multi-signed descriptor format.
if len(sn.MultiSigned.Signatures) < 2 {
// Too bad we can't log here. Oh well.
continue
}
}

nodeStatuses, err := rq.state.NodeStatuses()
Expand Down
28 changes: 19 additions & 9 deletions go/consensus/tendermint/apps/supplementarysanity/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,25 @@ func checkRegistry(state *iavl.MutableTree, now epochtime.EpochTime) error {
return fmt.Errorf("SanityCheckRuntimes: %w", err)
}

// Check nodes.
nodes, err := st.SignedNodes()
if err != nil {
return fmt.Errorf("SignedNodes: %w", err)
}
err = registry.SanityCheckNodes(logger, params, nodes, seenEntities, runtimeLookup, false, now)
if err != nil {
return fmt.Errorf("SanityCheckNodes: %w", err)
}
// Because expecting node operators to actually operate nodes is too
// much trouble, we can't run this check till everyone transitions
// to the new descriptor format, since there's currently no way to
// distinguish between nodes registered at genesis (old signature
// format) and nodes registered at runtime (MUST be new signature
// format).

/*
// Check nodes.
nodes, err := st.SignedNodes()
if err != nil {
return fmt.Errorf("SignedNodes: %w", err)
}
err = registry.SanityCheckNodes(logger, params, nodes, seenEntities, runtimeLookup, false, now)
if err != nil {
return fmt.Errorf("SanityCheckNodes: %w", err)
}
*/
_, _ = seenEntities, runtimeLookup

return nil
}
Expand Down
94 changes: 63 additions & 31 deletions go/registry/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,11 +378,35 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo
ctx = RegisterNodeSignatureContext
}

if err := sigNode.Open(ctx, &n); err != nil {
logger.Error("RegisterNode: invalid signature",
"signed_node", sigNode,
// Sigh. Instead of having people regenerate some files for our test
// environment, instead we're allowing a transition by disabling some
// signature validation. If it were up to me, regenerating things in
// an alpha environment is to be expected, but apparently "aren't we
// providing incenitives?" is insufficent incentive to get them to
// actually do something resembling work.
//
// Note: The entity signed case will fail to import, however as that
// functionality is gated behind DontBlameOasis, tough shit.
isOldNode := isOldNode(sigNode, isGenesis)

if !isOldNode {
if err := sigNode.Open(ctx, &n); err != nil {
logger.Error("RegisterNode: invalid signature",
"signed_node", sigNode,
)
return nil, nil, ErrInvalidSignature
}
} else {
if err := cbor.Unmarshal(sigNode.Blob, &n); err != nil {
logger.Error("RegisterNode: failed to unmarshal old-format descriptor",
"err", err,
"signed_node", sigNode,
)
return nil, nil, fmt.Errorf("%w: failed to unmarshal old-format descriptor", ErrInvalidArgument)
}
logger.Warn("RegisterNode: bypassing old genesis descriptor signature verification",
"node", n,
)
return nil, nil, ErrInvalidSignature
}

// This should never happen, unless there's a bug in the caller.
Expand Down Expand Up @@ -516,14 +540,16 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo
)
return nil, nil, fmt.Errorf("%w: invalid consensus ID", ErrInvalidArgument)
}
if !sigNode.MultiSigned.IsSignedBy(n.Consensus.ID) {
logger.Error("RegisterNode: not signed by consensus ID",
"signed_node", sigNode,
"node", n,
)
return nil, nil, fmt.Errorf("%w: registration not signed by consensus ID", ErrInvalidArgument)
if !isOldNode {
if !sigNode.MultiSigned.IsSignedBy(n.Consensus.ID) {
logger.Error("RegisterNode: not signed by consensus ID",
"signed_node", sigNode,
"node", n,
)
return nil, nil, fmt.Errorf("%w: registration not signed by consensus ID", ErrInvalidArgument)
}
expectedSigners = append(expectedSigners, n.Consensus.ID)
}
expectedSigners = append(expectedSigners, n.Consensus.ID)
consensusAddressRequired := n.HasRoles(ConsensusAddressRequiredRoles)
if err := verifyAddresses(params, consensusAddressRequired, n.Consensus.Addresses); err != nil {
addrs, _ := json.Marshal(n.Consensus.Addresses)
Expand Down Expand Up @@ -567,14 +593,16 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo
if err != nil {
return nil, nil, err
}
if !sigNode.MultiSigned.IsSignedBy(certPub) {
logger.Error("RegisterNode: not signed by TLS certificate key",
"signed_node", sigNode,
"node", n,
)
return nil, nil, fmt.Errorf("%w: registration not signed by TLS certificate key", ErrInvalidArgument)
if !isOldNode {
if !sigNode.MultiSigned.IsSignedBy(certPub) {
logger.Error("RegisterNode: not signed by TLS certificate key",
"signed_node", sigNode,
"node", n,
)
return nil, nil, fmt.Errorf("%w: registration not signed by TLS certificate key", ErrInvalidArgument)
}
expectedSigners = append(expectedSigners, certPub)
}
expectedSigners = append(expectedSigners, certPub)

// Validate P2PInfo.
if !n.P2P.ID.IsValid() {
Expand All @@ -583,14 +611,16 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo
)
return nil, nil, fmt.Errorf("%w: invalid P2P ID", ErrInvalidArgument)
}
if !sigNode.MultiSigned.IsSignedBy(n.P2P.ID) {
logger.Error("RegisterNode: not signed by P2P ID",
"signed_node", sigNode,
"node", n,
)
return nil, nil, fmt.Errorf("%w: registration not signed by P2P ID", ErrInvalidArgument)
if !isOldNode {
if !sigNode.MultiSigned.IsSignedBy(n.P2P.ID) {
logger.Error("RegisterNode: not signed by P2P ID",
"signed_node", sigNode,
"node", n,
)
return nil, nil, fmt.Errorf("%w: registration not signed by P2P ID", ErrInvalidArgument)
}
expectedSigners = append(expectedSigners, n.P2P.ID)
}
expectedSigners = append(expectedSigners, n.P2P.ID)
p2pAddressRequired := n.HasRoles(P2PAddressRequiredRoles)
if err = verifyAddresses(params, p2pAddressRequired, n.P2P.Addresses); err != nil {
addrs, _ := json.Marshal(n.P2P.Addresses)
Expand Down Expand Up @@ -662,12 +692,14 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo
}

// Ensure that only the expected signatures are present, and nothing more.
if !sigNode.MultiSigned.IsOnlySignedBy(expectedSigners) {
logger.Error("RegisterNode: unexpected number of signatures",
"signed_node", sigNode,
"node", n,
)
return nil, nil, fmt.Errorf("%w: unexpected number of signatures", ErrInvalidArgument)
if !isOldNode {
if !sigNode.MultiSigned.IsOnlySignedBy(expectedSigners) {
logger.Error("RegisterNode: unexpected number of signatures",
"signed_node", sigNode,
"node", n,
)
return nil, nil, fmt.Errorf("%w: unexpected number of signatures", ErrInvalidArgument)
}
}

return &n, runtimes, nil
Expand Down
19 changes: 17 additions & 2 deletions go/registry/api/sanity_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/oasislabs/oasis-core/go/common"
"github.com/oasislabs/oasis-core/go/common/cbor"
"github.com/oasislabs/oasis-core/go/common/crypto/hash"
"github.com/oasislabs/oasis-core/go/common/crypto/signature"
"github.com/oasislabs/oasis-core/go/common/entity"
Expand All @@ -14,6 +15,12 @@ import (
"github.com/oasislabs/oasis-core/go/oasis-node/cmd/common/flags"
)

func isOldNode(sigNode *node.MultiSignedNode, isGenesis bool) bool {
// Technically this will always have 1 signature after conversion,
// but every well-formed node will have more than 2 signatures.
return isGenesis && len(sigNode.MultiSigned.Signatures) < 2
}

// SanityCheck does basic sanity checking on the genesis state.
func (g *Genesis) SanityCheck(baseEpoch epochtime.EpochTime) error {
logger := logging.GetLogger("genesis/sanity-check")
Expand Down Expand Up @@ -109,10 +116,18 @@ func SanityCheckNodes(
}

for _, sn := range nodes {
isOldNode := isOldNode(sn, isGenesis)

// Open the node to get the referenced entity.
var n node.Node
if err := sn.Open(RegisterGenesisNodeSignatureContext, &n); err != nil {
return fmt.Errorf("registry: sanity check failed: unable to open signed node")
if isOldNode {
if err := sn.Open(RegisterGenesisNodeSignatureContext, &n); err != nil {
return fmt.Errorf("registry: sanity check failed: unable to open signed node")
}
} else {
if err := cbor.Unmarshal(sn.Blob, &n); err != nil {
return fmt.Errorf("registry: sanity check failed: unable to unmarshal old-format descriptor")
}
}
if !n.ID.IsValid() {
return fmt.Errorf("registry: sanity check failed: node ID %s is invalid", n.ID.String())
Expand Down

0 comments on commit 314ad5b

Please sign in to comment.