Skip to content

Commit

Permalink
Merge pull request #2917 from oasislabs/kostko/feature/reg-dsc-versions
Browse files Browse the repository at this point in the history
Version signed entity, node and runtime descriptors
  • Loading branch information
kostko authored May 18, 2020
2 parents 11d17af + 8b99d3b commit 8a842aa
Show file tree
Hide file tree
Showing 23 changed files with 331 additions and 107 deletions.
9 changes: 9 additions & 0 deletions .changelog/2581.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Version signed entity, node and runtime descriptors

This introduces a DescriptorVersion field to all entity, node and runtime
descriptors to support future updates and handling of legacy descriptors at
genesis.

All new registrations only accept the latest version while initializing from
genesis is also allowed with an older version to support a dump/restore
upgrade.
44 changes: 42 additions & 2 deletions go/common/entity/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,24 @@ var (
_ prettyprint.PrettyPrinter = (*SignedEntity)(nil)
)

const (
// LatestEntityDescriptorVersion is the latest entity descriptor version that should be used for
// all new descriptors. Using earlier versions may be rejected.
LatestEntityDescriptorVersion = 1

// Minimum and maximum descriptor versions that are allowed.
minEntityDescriptorVersion = 0
maxEntityDescriptorVersion = LatestEntityDescriptorVersion
)

// Entity represents an entity that controls one or more Nodes and or
// services.
type Entity struct {
type Entity struct { // nolint: maligned
// DescriptorVersion is the entity descriptor version.
//
// It should be bumped whenever breaking changes are made to the descriptor.
DescriptorVersion uint16 `json:"v,omitempty"`

// ID is the public key identifying the entity.
ID signature.PublicKey `json:"id"`

Expand All @@ -44,6 +59,29 @@ type Entity struct {
AllowEntitySignedNodes bool `json:"allow_entity_signed_nodes"`
}

// ValidateBasic performs basic descriptor validity checks.
func (e *Entity) ValidateBasic(strictVersion bool) error {
switch strictVersion {
case true:
// Only the latest version is allowed.
if e.DescriptorVersion != LatestEntityDescriptorVersion {
return fmt.Errorf("invalid entity descriptor version (expected: %d got: %d)",
LatestEntityDescriptorVersion,
e.DescriptorVersion,
)
}
case false:
// A range of versions is allowed.
if e.DescriptorVersion < minEntityDescriptorVersion || e.DescriptorVersion > maxEntityDescriptorVersion {
return fmt.Errorf("invalid entity descriptor version (min: %d max: %d)",
minEntityDescriptorVersion,
maxEntityDescriptorVersion,
)
}
}
return nil
}

// String returns a string representation of itself.
func (e Entity) String() string {
return "<Entity id=" + e.ID.String() + ">"
Expand Down Expand Up @@ -109,7 +147,8 @@ func Generate(baseDir string, signerFactory signature.SignerFactory, template *E
return nil, nil, err
}
ent := &Entity{
ID: signer.Public(),
DescriptorVersion: LatestEntityDescriptorVersion,
ID: signer.Public(),
}
if template != nil {
ent.Nodes = template.Nodes
Expand Down Expand Up @@ -166,6 +205,7 @@ func SignEntity(signer signature.Signer, context signature.Context, entity *Enti
func init() {
testEntitySigner = memorySigner.NewTestSigner("ekiden test entity key seed")

testEntity.DescriptorVersion = LatestEntityDescriptorVersion
testEntity.ID = testEntitySigner.Public()
testEntity.AllowEntitySignedNodes = true
}
40 changes: 39 additions & 1 deletion go/common/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,23 @@ var (
_ prettyprint.PrettyPrinter = (*MultiSignedNode)(nil)
)

const (
// LatestNodeDescriptorVersion is the latest node descriptor version that should be used for all
// new descriptors. Using earlier versions may be rejected.
LatestNodeDescriptorVersion = 1

// Minimum and maximum descriptor versions that are allowed.
minNodeDescriptorVersion = 0
maxNodeDescriptorVersion = LatestNodeDescriptorVersion
)

// Node represents public connectivity information about an Oasis node.
type Node struct {
type Node struct { // nolint: maligned
// DescriptorVersion is the node descriptor version.
//
// It should be bumped whenever breaking changes are made to the descriptor.
DescriptorVersion uint16 `json:"v,omitempty"`

// ID is the public key identifying the node.
ID signature.PublicKey `json:"id"`

Expand Down Expand Up @@ -116,6 +131,29 @@ func (m RolesMask) String() string {
return strings.Join(ret, ",")
}

// ValidateBasic performs basic descriptor validity checks.
func (n *Node) ValidateBasic(strictVersion bool) error {
switch strictVersion {
case true:
// Only the latest version is allowed.
if n.DescriptorVersion != LatestNodeDescriptorVersion {
return fmt.Errorf("invalid node descriptor version (expected: %d got: %d)",
LatestNodeDescriptorVersion,
n.DescriptorVersion,
)
}
case false:
// A range of versions is allowed.
if n.DescriptorVersion < minNodeDescriptorVersion || n.DescriptorVersion > maxNodeDescriptorVersion {
return fmt.Errorf("invalid node descriptor version (min: %d max: %d)",
minNodeDescriptorVersion,
maxNodeDescriptorVersion,
)
}
}
return nil
}

// AddRoles adds the Node roles
func (n *Node) AddRoles(r RolesMask) {
n.Roles |= r
Expand Down
2 changes: 1 addition & 1 deletion go/consensus/tendermint/apps/keymanager/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (app *keymanagerApplication) InitChain(ctx *tmapi.Context, request types.Re
regSt := doc.Registry
rtMap := make(map[common.Namespace]*registry.Runtime)
for _, v := range regSt.Runtimes {
rt, err := registry.VerifyRegisterRuntimeArgs(&regSt.Parameters, ctx.Logger(), v, true)
rt, err := registry.VerifyRegisterRuntimeArgs(&regSt.Parameters, ctx.Logger(), v, true, false)
if err != nil {
ctx.Logger().Error("InitChain: Invalid runtime",
"err", err,
Expand Down
2 changes: 1 addition & 1 deletion go/consensus/tendermint/apps/registry/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (app *registryApplication) InitChain(ctx *abciAPI.Context, request types.Re
if v == nil {
return fmt.Errorf("registry: genesis runtime index %d is nil", i)
}
rt, err := registry.VerifyRegisterRuntimeArgs(&st.Parameters, ctx.Logger(), v, ctx.IsInitChain())
rt, err := registry.VerifyRegisterRuntimeArgs(&st.Parameters, ctx.Logger(), v, ctx.IsInitChain(), false)
if err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion go/consensus/tendermint/apps/registry/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ func TestNodeUpdate(t *testing.T) {

// Create a new node.
n := node.Node{
ID: nodeSigner.Public(),
DescriptorVersion: node.LatestNodeDescriptorVersion,
ID: nodeSigner.Public(),
P2P: node.P2PInfo{
ID: p2pSigner1.Public(),
},
Expand Down
5 changes: 3 additions & 2 deletions go/consensus/tendermint/apps/registry/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (app *registryApplication) registerEntity(
state *registryState.MutableState,
sigEnt *entity.SignedEntity,
) error {
ent, err := registry.VerifyRegisterEntityArgs(ctx.Logger(), sigEnt, ctx.IsInitChain())
ent, err := registry.VerifyRegisterEntityArgs(ctx.Logger(), sigEnt, ctx.IsInitChain(), false)
if err != nil {
return err
}
Expand Down Expand Up @@ -198,6 +198,7 @@ func (app *registryApplication) registerNode( // nolint: gocyclo
untrustedEntity,
ctx.Now(),
ctx.IsInitChain(),
false,
epoch,
state,
state,
Expand Down Expand Up @@ -503,7 +504,7 @@ func (app *registryApplication) registerRuntime( // nolint: gocyclo
return registry.ErrForbidden
}

rt, err := registry.VerifyRegisterRuntimeArgs(params, ctx.Logger(), sigRt, ctx.IsInitChain())
rt, err := registry.VerifyRegisterRuntimeArgs(params, ctx.Logger(), sigRt, ctx.IsInitChain(), false)
if err != nil {
return err
}
Expand Down
5 changes: 3 additions & 2 deletions go/consensus/tendermint/apps/staking/slashing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ func TestOnEvidenceDoubleSign(t *testing.T) {
// Add node.
nodeSigner := memorySigner.NewTestSigner("node test signer")
nod := &node.Node{
ID: nodeSigner.Public(),
EntityID: ent.ID,
DescriptorVersion: node.LatestNodeDescriptorVersion,
ID: nodeSigner.Public(),
EntityID: ent.ID,
Consensus: node.ConsensusInfo{
ID: consensusID,
},
Expand Down
26 changes: 15 additions & 11 deletions go/genesis/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,18 @@ func TestGenesisSanityCheck(t *testing.T) {
// Note that this test entity has no nodes by design, those will be added
// later by various tests.
testEntity := &entity.Entity{
DescriptorVersion: entity.LatestEntityDescriptorVersion,
ID: validPK,
AllowEntitySignedNodes: true,
}
signedTestEntity := signEntityOrDie(signer, testEntity)

kmRuntimeID := hex2ns("4000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", false)
testKMRuntime := &registry.Runtime{
ID: kmRuntimeID,
EntityID: testEntity.ID,
Kind: registry.KindKeyManager,
DescriptorVersion: registry.LatestRuntimeDescriptorVersion,
ID: kmRuntimeID,
EntityID: testEntity.ID,
Kind: registry.KindKeyManager,
AdmissionPolicy: registry.RuntimeAdmissionPolicy{
EntityWhitelist: &registry.EntityWhitelistRuntimeAdmissionPolicy{
Entities: map[signature.PublicKey]bool{
Expand All @@ -182,10 +184,11 @@ func TestGenesisSanityCheck(t *testing.T) {

testRuntimeID := hex2ns("0000000000000000000000000000000000000000000000000000000000000001", false)
testRuntime := &registry.Runtime{
ID: testRuntimeID,
EntityID: testEntity.ID,
Kind: registry.KindCompute,
KeyManager: &testKMRuntime.ID,
DescriptorVersion: registry.LatestRuntimeDescriptorVersion,
ID: testRuntimeID,
EntityID: testEntity.ID,
Kind: registry.KindCompute,
KeyManager: &testKMRuntime.ID,
Executor: registry.ExecutorParameters{
GroupSize: 1,
RoundTimeout: 1 * time.Second,
Expand Down Expand Up @@ -223,10 +226,11 @@ func TestGenesisSanityCheck(t *testing.T) {
var testAddress node.Address
_ = testAddress.UnmarshalText([]byte("127.0.0.1:1234"))
testNode := &node.Node{
ID: nodeSigner.Public(),
EntityID: testEntity.ID,
Expiration: 10,
Roles: node.RoleValidator,
DescriptorVersion: node.LatestNodeDescriptorVersion,
ID: nodeSigner.Public(),
EntityID: testEntity.ID,
Expiration: 10,
Roles: node.RoleValidator,
Committee: node.CommitteeInfo{
Certificate: dummyCert.Certificate[0],
Addresses: []node.CommitteeAddress{
Expand Down
7 changes: 4 additions & 3 deletions go/oasis-node/cmd/debug/byzantine/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ func registryRegisterNode(svc service.TendermintService, id *identity.Identity,
}

nodeDesc := &node.Node{
ID: id.NodeSigner.Public(),
EntityID: entityID,
Expiration: 1000,
DescriptorVersion: node.LatestNodeDescriptorVersion,
ID: id.NodeSigner.Public(),
EntityID: entityID,
Expiration: 1000,
Committee: node.CommitteeInfo{
Certificate: id.GetTLSCertificate().Certificate[0],
Addresses: committeeAddresses,
Expand Down
19 changes: 11 additions & 8 deletions go/oasis-node/cmd/debug/txsource/workload/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ type registration struct {

func getRuntime(entityID signature.PublicKey, id common.Namespace) *registry.Runtime {
rt := &registry.Runtime{
ID: id,
EntityID: entityID,
Kind: registry.KindCompute,
DescriptorVersion: registry.LatestRuntimeDescriptorVersion,
ID: id,
EntityID: entityID,
Kind: registry.KindCompute,
Executor: registry.ExecutorParameters{
GroupSize: 1,
RoundTimeout: 1 * time.Second,
Expand Down Expand Up @@ -94,10 +95,11 @@ func getNodeDesc(rng *rand.Rand, nodeIdentity *identity.Identity, entityID signa
}

nodeDesc := node.Node{
ID: nodeIdentity.NodeSigner.Public(),
EntityID: entityID,
Expiration: 0,
Roles: availableRoles[rng.Intn(len(availableRoles))],
DescriptorVersion: node.LatestNodeDescriptorVersion,
ID: nodeIdentity.NodeSigner.Public(),
EntityID: entityID,
Expiration: 0,
Roles: availableRoles[rng.Intn(len(availableRoles))],
Committee: node.CommitteeInfo{
Certificate: nodeIdentity.GetTLSCertificate().Certificate[0],
Addresses: []node.CommitteeAddress{
Expand Down Expand Up @@ -204,7 +206,8 @@ func (r *registration) Run( // nolint: gocyclo
}

ent := &entity.Entity{
ID: entityAccs[i].signer.Public(),
DescriptorVersion: entity.LatestEntityDescriptorVersion,
ID: entityAccs[i].signer.Public(),
}

// Generate entity node identities.
Expand Down
1 change: 1 addition & 0 deletions go/oasis-node/cmd/registry/entity/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ func loadOrGenerateEntity(dataDir string, generate bool) (*entity.Entity, signat

if generate {
template := &entity.Entity{
DescriptorVersion: entity.LatestEntityDescriptorVersion,
AllowEntitySignedNodes: viper.GetBool(cfgAllowEntitySignedNodes),
}

Expand Down
7 changes: 4 additions & 3 deletions go/oasis-node/cmd/registry/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,10 @@ func doInit(cmd *cobra.Command, args []string) { // nolint: gocyclo
}

n := &node.Node{
ID: nodeIdentity.NodeSigner.Public(),
EntityID: entityID,
Expiration: viper.GetUint64(CfgExpiration),
DescriptorVersion: node.LatestNodeDescriptorVersion,
ID: nodeIdentity.NodeSigner.Public(),
EntityID: entityID,
Expiration: viper.GetUint64(CfgExpiration),
Committee: node.CommitteeInfo{
Certificate: nodeIdentity.GetTLSCertificate().Certificate[0],
NextCertificate: nextCert,
Expand Down
11 changes: 6 additions & 5 deletions go/oasis-node/cmd/registry/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,11 +353,12 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) {
}

rt := &registry.Runtime{
ID: id,
EntityID: signer.Public(),
Genesis: gen,
Kind: kind,
TEEHardware: teeHardware,
DescriptorVersion: registry.LatestRuntimeDescriptorVersion,
ID: id,
EntityID: signer.Public(),
Genesis: gen,
Kind: kind,
TEEHardware: teeHardware,
Version: registry.VersionInfo{
Version: version.FromU64(viper.GetUint64(CfgVersion)),
},
Expand Down
1 change: 1 addition & 0 deletions go/oasis-node/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ var (
}

testRuntime = &registry.Runtime{
DescriptorVersion: registry.LatestRuntimeDescriptorVersion,
// ID: default value,
// EntityID: test entity,
Kind: registry.KindCompute,
Expand Down
19 changes: 10 additions & 9 deletions go/oasis-test-runner/oasis/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,16 @@ func (rt *Runtime) GetGenesisStatePath() string {
// NewRuntime provisions a new runtime and adds it to the network.
func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) {
descriptor := registry.Runtime{
ID: cfg.ID,
EntityID: cfg.Entity.entity.ID,
Kind: cfg.Kind,
TEEHardware: cfg.TEEHardware,
Executor: cfg.Executor,
Merge: cfg.Merge,
TxnScheduler: cfg.TxnScheduler,
Storage: cfg.Storage,
AdmissionPolicy: cfg.AdmissionPolicy,
DescriptorVersion: registry.LatestRuntimeDescriptorVersion,
ID: cfg.ID,
EntityID: cfg.Entity.entity.ID,
Kind: cfg.Kind,
TEEHardware: cfg.TEEHardware,
Executor: cfg.Executor,
Merge: cfg.Merge,
TxnScheduler: cfg.TxnScheduler,
Storage: cfg.Storage,
AdmissionPolicy: cfg.AdmissionPolicy,
}

rtDir, err := net.baseDir.NewSubDir("runtime-" + cfg.ID.String())
Expand Down
Loading

0 comments on commit 8a842aa

Please sign in to comment.