From f8809788d2f5449c7049ff3202448eaf060b080d Mon Sep 17 00:00:00 2001 From: Jernej Kos Date: Wed, 29 Jan 2020 11:20:57 +0100 Subject: [PATCH 1/3] go/genesis: Fix genesis tests and registry sanity checks --- .changelog/2589.internal.md | 1 + go/consensus/tendermint/tests/evidence.go | 2 +- .../tendermint/tests/genesis/genesis.go | 2 +- .../{tests/tester.go => genesis_test.go} | 145 +++++++++++++----- go/genesis/tests/{helpers => }/helpers.go | 4 +- go/keymanager/api/api.go | 5 +- go/oasis-node/cmd/node/node.go | 2 +- go/oasis-test-runner/oasis/oasis.go | 2 +- go/registry/api/api.go | 47 ++++-- go/registry/api/sanity_check.go | 30 +++- go/roothash/api/commitment/pool_test.go | 2 +- .../mkvs/urkel/interop/cmd/protocol_server.go | 2 +- go/storage/tests/tester.go | 2 +- 13 files changed, 179 insertions(+), 67 deletions(-) create mode 100644 .changelog/2589.internal.md rename go/genesis/{tests/tester.go => genesis_test.go} (83%) rename go/genesis/tests/{helpers => }/helpers.go (92%) diff --git a/.changelog/2589.internal.md b/.changelog/2589.internal.md new file mode 100644 index 00000000000..95c307a0525 --- /dev/null +++ b/.changelog/2589.internal.md @@ -0,0 +1 @@ +go/genesis: Fix genesis tests and registry sanity checks. \ No newline at end of file diff --git a/go/consensus/tendermint/tests/evidence.go b/go/consensus/tendermint/tests/evidence.go index 93bf34e2845..17baadf220d 100644 --- a/go/consensus/tendermint/tests/evidence.go +++ b/go/consensus/tendermint/tests/evidence.go @@ -12,7 +12,7 @@ import ( "github.com/oasislabs/oasis-core/go/common/identity" consensus "github.com/oasislabs/oasis-core/go/consensus/api" tmcrypto "github.com/oasislabs/oasis-core/go/consensus/tendermint/crypto" - genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests/helpers" + genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests" ) // MakeDoubleSignEvidence creates consensus evidence of double signing. diff --git a/go/consensus/tendermint/tests/genesis/genesis.go b/go/consensus/tendermint/tests/genesis/genesis.go index 1dececb5cf8..6ad2da00f38 100644 --- a/go/consensus/tendermint/tests/genesis/genesis.go +++ b/go/consensus/tendermint/tests/genesis/genesis.go @@ -14,7 +14,7 @@ import ( "github.com/oasislabs/oasis-core/go/consensus/tendermint/service" epochtime "github.com/oasislabs/oasis-core/go/epochtime/api" genesis "github.com/oasislabs/oasis-core/go/genesis/api" - genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests/helpers" + genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests" registry "github.com/oasislabs/oasis-core/go/registry/api" roothash "github.com/oasislabs/oasis-core/go/roothash/api" scheduler "github.com/oasislabs/oasis-core/go/scheduler/api" diff --git a/go/genesis/tests/tester.go b/go/genesis/genesis_test.go similarity index 83% rename from go/genesis/tests/tester.go rename to go/genesis/genesis_test.go index 6cdb06a2ce5..08a7e6f41c2 100644 --- a/go/genesis/tests/tester.go +++ b/go/genesis/genesis_test.go @@ -1,6 +1,7 @@ -package tests +package genesis import ( + "crypto/ed25519" "encoding/hex" "math" "testing" @@ -20,7 +21,7 @@ import ( tendermint "github.com/oasislabs/oasis-core/go/consensus/tendermint/api" epochtime "github.com/oasislabs/oasis-core/go/epochtime/api" genesis "github.com/oasislabs/oasis-core/go/genesis/api" - genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests/helpers" + genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests" keymanager "github.com/oasislabs/oasis-core/go/keymanager/api" cmdFlags "github.com/oasislabs/oasis-core/go/oasis-node/cmd/common/flags" registry "github.com/oasislabs/oasis-core/go/registry/api" @@ -45,9 +46,10 @@ var testDoc = &genesis.Document{ }, Registry: registry.Genesis{ Parameters: registry.ConsensusParameters{ - DebugAllowUnroutableAddresses: true, - DebugAllowRuntimeRegistration: true, - DebugBypassStake: true, + DebugAllowUnroutableAddresses: true, + DebugAllowRuntimeRegistration: true, + DebugBypassStake: true, + DebugAllowEntitySignedNodeRegistration: true, }, }, Scheduler: scheduler.Genesis{ @@ -85,11 +87,9 @@ func signRuntimeOrDie(signer signature.Signer, rt *registry.Runtime) *registry.S return signedRuntime } -func signNodeOrDie(signer signature.Signer, n *node.Node) *node.MultiSignedNode { +func signNodeOrDie(signers []signature.Signer, n *node.Node) *node.MultiSignedNode { signedNode, err := node.MultiSignNode( - []signature.Signer{ - signer, - }, + signers, registry.RegisterGenesisNodeSignatureContext, n, ) @@ -130,7 +130,7 @@ func TestGenesisChainContext(t *testing.T) { // on each run. stableDoc.Staking = staking.Genesis{} - require.Equal(t, "daba5eed9f82d37c76384f9f185dc0bfff60eb57a33b7d8955e265244e0a0a51", stableDoc.ChainContext()) + require.Equal(t, "7670bb121628f48b2b87ef61ac79671684b0255f86323a4583d3bed5500bb31b", stableDoc.ChainContext()) } func TestGenesisSanityCheck(t *testing.T) { @@ -140,12 +140,15 @@ func TestGenesisSanityCheck(t *testing.T) { // First, set up a few things we'll need in the tests below. signer := memorySigner.NewTestSigner("genesis sanity checks signer") signer2 := memorySigner.NewTestSigner("another genesis sanity checks signer") + nodeSigner := memorySigner.NewTestSigner("node genesis sanity checks signer") + nodeConsensusSigner := memorySigner.NewTestSigner("node consensus genesis sanity checks signer") + nodeP2PSigner := memorySigner.NewTestSigner("node P2P genesis sanity checks signer") validPK := signer.Public() var validNS common.Namespace _ = validNS.UnmarshalBinary(validPK[:]) invalidPK := hex2pk("c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a") - invalidNS := hex2ns("c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", true) + unknownPK := memorySigner.NewTestSigner("unknown genesis sanity checks signer").Public() signature.BuildPublicKeyBlacklist(true) @@ -162,10 +165,17 @@ func TestGenesisSanityCheck(t *testing.T) { } signedTestEntity := signEntityOrDie(signer, testEntity) - kmRuntimeID := hex2ns("0000000000000000000000000000000000000000000000000000000000000000", false) + kmRuntimeID := hex2ns("4000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", false) testKMRuntime := ®istry.Runtime{ ID: kmRuntimeID, Kind: registry.KindKeyManager, + AdmissionPolicy: registry.RuntimeAdmissionPolicy{ + EntityWhitelist: ®istry.EntityWhitelistRuntimeAdmissionPolicy{ + Entities: map[signature.PublicKey]bool{ + validPK: true, + }, + }, + }, } signedTestKMRuntime := signRuntimeOrDie(signer, testKMRuntime) @@ -183,31 +193,62 @@ func TestGenesisSanityCheck(t *testing.T) { RoundTimeout: 1 * time.Second, }, TxnScheduler: registry.TxnSchedulerParameters{ + GroupSize: 1, Algorithm: "batching", BatchFlushTimeout: 1 * time.Second, MaxBatchSize: 1, MaxBatchSizeBytes: 1, }, + Storage: registry.StorageParameters{ + GroupSize: 1, + }, + AdmissionPolicy: registry.RuntimeAdmissionPolicy{ + AnyNode: ®istry.AnyNodeRuntimeAdmissionPolicy{}, + }, } signedTestRuntime := signRuntimeOrDie(signer, testRuntime) - testNodeID := hex2pk("0000000000000000000000000000000000000000000000000000000000000010") dummyCert, err := tls.Generate("genesis sanity check dummy cert") if err != nil { panic(err) } + var testConsensusAddress node.ConsensusAddress + _ = testConsensusAddress.UnmarshalText([]byte("f29bb9979109fc2512b2ae8ed14531c459cdeb2cb73077440115b6c955a359f1@127.0.0.1:1234")) + var testAddress node.Address + _ = testAddress.UnmarshalText([]byte("127.0.0.1:1234")) testNode := &node.Node{ - ID: testNodeID, + ID: nodeSigner.Public(), EntityID: testEntity.ID, Expiration: 10, + Roles: node.RoleValidator, Committee: node.CommitteeInfo{ Certificate: dummyCert.Certificate[0], + Addresses: []node.CommitteeAddress{ + {Certificate: dummyCert.Certificate[0], Address: testAddress}, + }, + }, + P2P: node.P2PInfo{ + ID: nodeP2PSigner.Public(), + Addresses: []node.Address{testAddress}, }, Consensus: node.ConsensusInfo{ - ID: testNodeID, + ID: nodeConsensusSigner.Public(), + Addresses: []node.ConsensusAddress{testConsensusAddress}, }, } - signedTestNode := signNodeOrDie(signer, testNode) + nodeTLSSigner := memorySigner.NewFromRuntime(dummyCert.PrivateKey.(ed25519.PrivateKey)) + nodeSigners := []signature.Signer{ + nodeSigner, + nodeP2PSigner, + nodeTLSSigner, + nodeConsensusSigner, + } + signedTestNode := signNodeOrDie(nodeSigners, testNode) + entitySignedTestNode := signNodeOrDie(append([]signature.Signer{signer}, nodeSigners...), testNode) + + testDocCopy := *testDoc + testDoc := &testDocCopy + testDoc.Registry.Parameters.KeyManagerOperator = signer.Public() // Test genesis document should pass sanity check. require.NoError(testDoc.SanityCheck(), "test genesis document should be valid") @@ -231,10 +272,6 @@ func TestGenesisSanityCheck(t *testing.T) { require.Error(d.SanityCheck(), "halt epoch in the past should be invalid") // Test consensus genesis checks. - d = *testDoc - d.Consensus.Backend = "asdf" - require.Error(d.SanityCheck(), "invalid consensus backend should be rejected") - d = *testDoc d.Consensus.Parameters.TimeoutCommit = 0 d.Consensus.Parameters.SkipTimeoutCommit = false @@ -260,11 +297,22 @@ func TestGenesisSanityCheck(t *testing.T) { d.KeyManager = keymanager.Genesis{ Statuses: []*keymanager.Status{ { - ID: invalidNS, + ID: testRuntimeID, }, }, } - require.Error(d.SanityCheck(), "invalid keymanager ID should be rejected") + require.Error(d.SanityCheck(), "invalid keymanager runtime should be rejected") + + d = *testDoc + d.KeyManager = keymanager.Genesis{ + Statuses: []*keymanager.Status{ + { + ID: validNS, + Nodes: []signature.PublicKey{invalidPK}, + }, + }, + } + require.Error(d.SanityCheck(), "invalid keymanager node should be rejected") // Test roothash genesis checks. // First we define a helper function for calling the SanityCheck() on RuntimeStates. @@ -409,7 +457,7 @@ func TestGenesisSanityCheck(t *testing.T) { d = *testDoc te = *testEntity - te.Nodes = []signature.PublicKey{invalidPK} + te.Nodes = []signature.PublicKey{unknownPK} te.AllowEntitySignedNodes = false signedEntityWithBrokenNode := signEntityOrDie(signer, &te) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithBrokenNode} @@ -419,18 +467,18 @@ func TestGenesisSanityCheck(t *testing.T) { d = *testDoc te = *testEntity - te.Nodes = []signature.PublicKey{invalidPK} + te.Nodes = []signature.PublicKey{unknownPK} te.AllowEntitySignedNodes = true signedEntityWithBrokenNode = signEntityOrDie(signer, &te) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithBrokenNode} d.Registry.Runtimes = []*registry.SignedRuntime{} - d.Registry.Nodes = []*node.MultiSignedNode{signedTestNode} + d.Registry.Nodes = []*node.MultiSignedNode{entitySignedTestNode} require.NoError(d.SanityCheck(), "node not listed among controlling entity's nodes should still be accepted if the entity allows entity-signed nodes") d = *testDoc tn := *testNode - tn.EntityID = invalidPK - signedBrokenTestNode := signNodeOrDie(signer, &tn) + tn.EntityID = unknownPK + signedBrokenTestNode := signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedBrokenTestNode} @@ -456,16 +504,25 @@ func TestGenesisSanityCheck(t *testing.T) { d = *testDoc tn = *testNode tn.Roles = 1<<16 | 1<<17 - signedBrokenTestNode = signNodeOrDie(signer, &tn) + signedBrokenTestNode = signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedBrokenTestNode} require.Error(d.SanityCheck(), "node with any reserved role bits set should be rejected") + d = *testDoc + tn = *testNode + tn.Roles = 0 + signedBrokenTestNode = signNodeOrDie(nodeSigners, &tn) + d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} + d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime} + d.Registry.Nodes = []*node.MultiSignedNode{signedBrokenTestNode} + require.Error(d.SanityCheck(), "node without any role bits set should be rejected") + d = *testDoc tn = *testNode tn.Committee.Certificate = []byte{1, 2, 3} - signedBrokenTestNode = signNodeOrDie(signer, &tn) + signedBrokenTestNode = signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedBrokenTestNode} @@ -474,7 +531,7 @@ func TestGenesisSanityCheck(t *testing.T) { d = *testDoc tn = *testNode tn.Consensus.ID = invalidPK - signedBrokenTestNode = signNodeOrDie(signer, &tn) + signedBrokenTestNode = signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedBrokenTestNode} @@ -483,7 +540,7 @@ func TestGenesisSanityCheck(t *testing.T) { d = *testDoc tn = *testNode tn.Roles = node.RoleComputeWorker - signedBrokenTestNode = signNodeOrDie(signer, &tn) + signedBrokenTestNode = signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedBrokenTestNode} @@ -492,7 +549,7 @@ func TestGenesisSanityCheck(t *testing.T) { d = *testDoc tn = *testNode tn.Roles = node.RoleKeyManager - signedBrokenTestNode = signNodeOrDie(signer, &tn) + signedBrokenTestNode = signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedBrokenTestNode} @@ -506,7 +563,7 @@ func TestGenesisSanityCheck(t *testing.T) { ID: testKMRuntime.ID, }, } - signedKMTestNode := signNodeOrDie(signer, &tn) + signedKMTestNode := signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedKMTestNode} @@ -520,7 +577,7 @@ func TestGenesisSanityCheck(t *testing.T) { ID: testRuntime.ID, }, } - signedBrokenTestNode = signNodeOrDie(signer, &tn) + signedBrokenTestNode = signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedBrokenTestNode} @@ -534,7 +591,7 @@ func TestGenesisSanityCheck(t *testing.T) { ID: testRuntime.ID, }, } - signedBrokenTestNode = signNodeOrDie(signer, &tn) + signedBrokenTestNode = signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime, signedTestRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedBrokenTestNode} @@ -548,7 +605,7 @@ func TestGenesisSanityCheck(t *testing.T) { ID: testKMRuntime.ID, }, } - signedBrokenTestNode = signNodeOrDie(signer, &tn) + signedBrokenTestNode = signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime, signedTestRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedBrokenTestNode} @@ -562,12 +619,26 @@ func TestGenesisSanityCheck(t *testing.T) { ID: testRuntime.ID, }, } - signedComputeTestNode := signNodeOrDie(signer, &tn) + signedComputeTestNode := signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime, signedTestRuntime} d.Registry.Nodes = []*node.MultiSignedNode{signedComputeTestNode} require.NoError(d.SanityCheck(), "compute node with compute runtime should pass") + d = *testDoc + tn = *testNode + tn.Roles = node.RoleStorageWorker + tn.Runtimes = []*node.Runtime{ + &node.Runtime{ + ID: testRuntime.ID, + }, + } + signedStorageTestNode := signNodeOrDie(nodeSigners, &tn) + d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} + d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime, signedTestRuntime} + d.Registry.Nodes = []*node.MultiSignedNode{signedStorageTestNode} + require.NoError(d.SanityCheck(), "storage node with compute runtime should pass") + // Test staking genesis checks. // NOTE: There doesn't seem to be a way to generate invalid Quantities, so // we're just going to test the code that checks if things add up. diff --git a/go/genesis/tests/helpers/helpers.go b/go/genesis/tests/helpers.go similarity index 92% rename from go/genesis/tests/helpers/helpers.go rename to go/genesis/tests/helpers.go index 1e9e6e080c2..ba7ef28f366 100644 --- a/go/genesis/tests/helpers/helpers.go +++ b/go/genesis/tests/helpers.go @@ -1,5 +1,5 @@ -// Package helpers contains genesis test helpers. -package helpers +// Package tests contains genesis test helpers. +package tests import ( "github.com/oasislabs/oasis-core/go/common/crypto/hash" diff --git a/go/keymanager/api/api.go b/go/keymanager/api/api.go index 2d556958cdd..9133c6d0544 100644 --- a/go/keymanager/api/api.go +++ b/go/keymanager/api/api.go @@ -144,7 +144,10 @@ type Genesis struct { // SanityCheckStatuses examines the statuses table. func SanityCheckStatuses(statuses []*Status) error { for _, status := range statuses { - // TODO: Verify key manager runtime ID. + // Verify key manager runtime ID. + if !status.ID.IsKeyManager() { + return fmt.Errorf("keymanager: sanity check failed: key manager runtime ID %s is invalid", status.ID) + } // Verify currently active key manager node IDs. for _, node := range status.Nodes { diff --git a/go/oasis-node/cmd/node/node.go b/go/oasis-node/cmd/node/node.go index f95df5add15..10f3398f33e 100644 --- a/go/oasis-node/cmd/node/node.go +++ b/go/oasis-node/cmd/node/node.go @@ -31,7 +31,7 @@ import ( epochtime "github.com/oasislabs/oasis-core/go/epochtime/api" genesisAPI "github.com/oasislabs/oasis-core/go/genesis/api" genesisFile "github.com/oasislabs/oasis-core/go/genesis/file" - genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests/helpers" + genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests" "github.com/oasislabs/oasis-core/go/ias" iasAPI "github.com/oasislabs/oasis-core/go/ias/api" keymanagerAPI "github.com/oasislabs/oasis-core/go/keymanager/api" diff --git a/go/oasis-test-runner/oasis/oasis.go b/go/oasis-test-runner/oasis/oasis.go index 07ae5f3ca0a..f142d4c1f77 100644 --- a/go/oasis-test-runner/oasis/oasis.go +++ b/go/oasis-test-runner/oasis/oasis.go @@ -22,7 +22,7 @@ import ( "github.com/oasislabs/oasis-core/go/common/logging" "github.com/oasislabs/oasis-core/go/common/node" genesisFile "github.com/oasislabs/oasis-core/go/genesis/file" - genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests/helpers" + genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests" "github.com/oasislabs/oasis-core/go/oasis-node/cmd/common" "github.com/oasislabs/oasis-core/go/oasis-node/cmd/genesis" "github.com/oasislabs/oasis-core/go/oasis-test-runner/env" diff --git a/go/registry/api/api.go b/go/registry/api/api.go index b5d36c3d393..5e03851c61a 100644 --- a/go/registry/api/api.go +++ b/go/registry/api/api.go @@ -151,6 +151,13 @@ var ( node.RoleStorageWorker | node.RoleKeyManager + // ComputeRuntimeAllowedRoles are the Node roles that allow compute runtimes. + ComputeRuntimeAllowedRoles = node.RoleComputeWorker | + node.RoleStorageWorker + + // KeyManagerRuntimeAllowedRoles are the Node roles that allow key manager runtimes. + KeyManagerRuntimeAllowedRoles = node.RoleKeyManager + // ConsensusAddressRequiredRoles are the Node roles that require Consensus Address. ConsensusAddressRequiredRoles = node.RoleValidator @@ -505,6 +512,14 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo return nil, nil, err } + // Enforce what kinds of runtimes are allowed. + if regRt.Kind == KindKeyManager && !n.HasRoles(KeyManagerRuntimeAllowedRoles) { + return nil, nil, fmt.Errorf("%w: key manager runtime not allowed", ErrInvalidArgument) + } + if regRt.Kind == KindCompute && !n.HasRoles(ComputeRuntimeAllowedRoles) { + return nil, nil, fmt.Errorf("%w: compute runtime not allowed", ErrInvalidArgument) + } + runtimes = append(runtimes, regRt) } } @@ -998,6 +1013,22 @@ func VerifyRegisterRuntimeArgs( return nil, ErrInvalidArgument } + // Ensure there is at least one member of the compute group. + if rt.Executor.GroupSize == 0 { + logger.Error("RegisterRuntime: executor group size too small", + "runtime", rt, + ) + return nil, fmt.Errorf("%w: executor group too small", ErrInvalidArgument) + } + + // Ensure there is at least one member of the merge group. + if rt.Merge.GroupSize == 0 { + logger.Error("RegisterRuntime: merge group size too small", + "runtime", rt, + ) + return nil, fmt.Errorf("%w: merge group too small", ErrInvalidArgument) + } + // Ensure there is at least one member of the transaction scheduler group. if rt.TxnScheduler.GroupSize == 0 { logger.Error("RegisterRuntime: transaction scheduler group too small", @@ -1050,22 +1081,6 @@ func VerifyRegisterRuntimeArgs( return nil, err } - // Ensure there is at least one member of the compute group. - if rt.Executor.GroupSize == 0 { - logger.Error("RegisterRuntime: executor group size too small", - "runtime", rt, - ) - return nil, fmt.Errorf("%w: executor group too small", ErrInvalidArgument) - } - - // Ensure there is at least one member of the merge group. - if rt.Merge.GroupSize == 0 { - logger.Error("RegisterRuntime: merge group size too small", - "runtime", rt, - ) - return nil, fmt.Errorf("%w: merge group too small", ErrInvalidArgument) - } - // Ensure a valid TEE hardware is specified. if rt.TEEHardware >= node.TEEHardwareReserved { logger.Error("RegisterRuntime: invalid TEE hardware specified", diff --git a/go/registry/api/sanity_check.go b/go/registry/api/sanity_check.go index 08a3fe14985..e44881a06e9 100644 --- a/go/registry/api/sanity_check.go +++ b/go/registry/api/sanity_check.go @@ -68,7 +68,7 @@ func SanityCheckRuntimes(logger *logging.Logger, suspendedRuntimes []*SignedRuntime, isGenesis bool, ) (RuntimeLookup, error) { - + // First go through all runtimes and perform general sanity checks. seenRuntimes := []*Runtime{} for _, srt := range runtimes { rt, err := VerifyRegisterRuntimeArgs(params, logger, srt, isGenesis) @@ -87,7 +87,23 @@ func SanityCheckRuntimes(logger *logging.Logger, seenSuspendedRuntimes = append(seenSuspendedRuntimes, rt) } - return newSanityCheckRuntimeLookup(seenRuntimes, seenSuspendedRuntimes), nil + // Then build a runtime lookup table and re-check compute runtimes as those need to reference + // correct key manager runtimes when a key manager is configured. + lookup, err := newSanityCheckRuntimeLookup(seenRuntimes, seenSuspendedRuntimes) + if err != nil { + return nil, fmt.Errorf("runtime sanity check failed: %w", err) + } + for _, runtimes := range [][]*Runtime{seenRuntimes, seenSuspendedRuntimes} { + for _, rt := range runtimes { + if rt.Kind != KindCompute { + continue + } + if err := VerifyRegisterComputeRuntimeArgs(logger, rt, lookup); err != nil { + return nil, fmt.Errorf("compute runtime sanity check failed: %w", err) + } + } + } + return lookup, nil } // SanityCheckNodes examines the nodes table. @@ -154,16 +170,22 @@ type sanityCheckRuntimeLookup struct { suspendedRuntimes map[common.Namespace]*Runtime } -func newSanityCheckRuntimeLookup(runtimes []*Runtime, suspendedRuntimes []*Runtime) RuntimeLookup { +func newSanityCheckRuntimeLookup(runtimes []*Runtime, suspendedRuntimes []*Runtime) (RuntimeLookup, error) { rtsMap := make(map[common.Namespace]*Runtime) sRtsMap := make(map[common.Namespace]*Runtime) for _, rt := range runtimes { + if rtsMap[rt.ID] != nil { + return nil, fmt.Errorf("duplicate runtime: %s", rt.ID) + } rtsMap[rt.ID] = rt } for _, srt := range suspendedRuntimes { + if rtsMap[srt.ID] != nil || sRtsMap[srt.ID] != nil { + return nil, fmt.Errorf("duplicate (suspended) runtime: %s", srt.ID) + } sRtsMap[srt.ID] = srt } - return &sanityCheckRuntimeLookup{rtsMap, sRtsMap} + return &sanityCheckRuntimeLookup{rtsMap, sRtsMap}, nil } func (r *sanityCheckRuntimeLookup) Runtime(id common.Namespace) (*Runtime, error) { diff --git a/go/roothash/api/commitment/pool_test.go b/go/roothash/api/commitment/pool_test.go index fdf366a6254..faa4fe12839 100644 --- a/go/roothash/api/commitment/pool_test.go +++ b/go/roothash/api/commitment/pool_test.go @@ -14,7 +14,7 @@ import ( "github.com/oasislabs/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasislabs/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasislabs/oasis-core/go/common/node" - genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests/helpers" + genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests" registry "github.com/oasislabs/oasis-core/go/registry/api" "github.com/oasislabs/oasis-core/go/roothash/api/block" scheduler "github.com/oasislabs/oasis-core/go/scheduler/api" diff --git a/go/storage/mkvs/urkel/interop/cmd/protocol_server.go b/go/storage/mkvs/urkel/interop/cmd/protocol_server.go index 5aaac37af66..515401f1f21 100644 --- a/go/storage/mkvs/urkel/interop/cmd/protocol_server.go +++ b/go/storage/mkvs/urkel/interop/cmd/protocol_server.go @@ -9,7 +9,7 @@ import ( "github.com/oasislabs/oasis-core/go/common/grpc" "github.com/oasislabs/oasis-core/go/common/identity" "github.com/oasislabs/oasis-core/go/common/logging" - genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests/helpers" + genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests" "github.com/oasislabs/oasis-core/go/oasis-node/cmd/common/background" storage "github.com/oasislabs/oasis-core/go/storage/api" "github.com/oasislabs/oasis-core/go/storage/database" diff --git a/go/storage/tests/tester.go b/go/storage/tests/tester.go index 17254e0d911..39309969b4f 100644 --- a/go/storage/tests/tester.go +++ b/go/storage/tests/tester.go @@ -12,7 +12,7 @@ import ( "github.com/oasislabs/oasis-core/go/common" "github.com/oasislabs/oasis-core/go/common/crypto/hash" - genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests/helpers" + genesisTestHelpers "github.com/oasislabs/oasis-core/go/genesis/tests" "github.com/oasislabs/oasis-core/go/storage/api" "github.com/oasislabs/oasis-core/go/storage/mkvs/urkel" "github.com/oasislabs/oasis-core/go/storage/mkvs/urkel/writelog" From 8617ded7a3a9210db29deed9f3f0ba797ecf7a0c Mon Sep 17 00:00:00 2001 From: Jernej Kos Date: Wed, 29 Jan 2020 11:22:03 +0100 Subject: [PATCH 2/3] go/keymanager: Move Frame structure to enclaverpc package --- go/keymanager/api/api.go | 8 -------- go/keymanager/api/grpc.go | 4 ++-- go/runtime/enclaverpc/api/api.go | 13 ++++++++++++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/go/keymanager/api/api.go b/go/keymanager/api/api.go index 9133c6d0544..5a8a5203b47 100644 --- a/go/keymanager/api/api.go +++ b/go/keymanager/api/api.go @@ -183,14 +183,6 @@ func (g *Genesis) SanityCheck() error { return nil } -// Frame is the Go analog of the Rust RPC Frame defined in -// client/src/rpc/client.rs. -type Frame struct { - Session []byte `json:"session,omitempty"` - UntrustedPlaintext string `json:"untrusted_plaintext,omitempty"` - Payload []byte `json:"payload,omitempty"` -} - func init() { // Old `INSECURE_SIGNING_KEY_PKCS8`. var oldTestKey signature.PublicKey diff --git a/go/keymanager/api/grpc.go b/go/keymanager/api/grpc.go index 54c7b7b4d56..4e9c2cb38e9 100644 --- a/go/keymanager/api/grpc.go +++ b/go/keymanager/api/grpc.go @@ -32,9 +32,9 @@ var ( Service = enclaverpc.NewService(ModuleName, requestSkipPolicyCheck) ) -func payloadSkipPolicyCheck(data []byte) (bool, error) { +func payloadSkipPolicyCheck(data cbor.RawMessage) (bool, error) { // Unpack the payload, get method from Frame. - var f Frame + var f enclaverpc.Frame if err := cbor.Unmarshal(data, &f); err != nil { return false, fmt.Errorf("unable to unpack Frame: %w", err) } diff --git a/go/runtime/enclaverpc/api/api.go b/go/runtime/enclaverpc/api/api.go index bce034b119c..4a6ca3d737a 100644 --- a/go/runtime/enclaverpc/api/api.go +++ b/go/runtime/enclaverpc/api/api.go @@ -5,6 +5,7 @@ import ( "context" "github.com/oasislabs/oasis-core/go/common" + "github.com/oasislabs/oasis-core/go/common/cbor" ) // Transport is the EnclaveRPC transport interface. @@ -18,5 +19,15 @@ type CallEnclaveRequest struct { RuntimeID common.Namespace `json:"runtime_id"` Endpoint string `json:"endpoint"` - Payload []byte `json:"payload"` + // Payload is a CBOR-serialized Frame. + Payload cbor.RawMessage `json:"payload"` +} + +// Frame is an EnclaveRPC frame. +// +// It is the Go analog of the Rust RPC frame defined in client/src/rpc/client.rs. +type Frame struct { + Session []byte `json:"session,omitempty"` + UntrustedPlaintext string `json:"untrusted_plaintext,omitempty"` + Payload []byte `json:"payload,omitempty"` } From 77c0536a5214ee82574ab5af74cb18d94b06a367 Mon Sep 17 00:00:00 2001 From: Jernej Kos Date: Wed, 29 Jan 2020 12:57:18 +0100 Subject: [PATCH 3/3] go/common/grpc/policy: Snapshot access policies map on update Previously the underlying access policies map was passed directly, which led to corruption as the map is mutable. We now create a snapshot of the map before emitting it to the policy watcher. --- go/common/grpc/policy/policy.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/go/common/grpc/policy/policy.go b/go/common/grpc/policy/policy.go index 5f41a9f87e1..e2d068c46c2 100644 --- a/go/common/grpc/policy/policy.go +++ b/go/common/grpc/policy/policy.go @@ -100,7 +100,14 @@ func (c *DynamicRuntimePolicyChecker) SetAccessPolicy(policy accessctl.Policy, r c.accessPolicies[runtimeID] = policy if c.watcher != nil { - c.watcher.PolicyUpdated(c.service, c.accessPolicies) + // Create a snapshot of the access policies map. While each policy is immutable, the set of + // all policies can be mutated by the dynamic runtime policy checker. + policies := make(map[common.Namespace]accessctl.Policy, len(c.accessPolicies)) + for k, v := range c.accessPolicies { + policies[k] = v + } + + c.watcher.PolicyUpdated(c.service, policies) } }