зеркало из https://github.com/microsoft/CCF.git
205 строки
6.7 KiB
Python
205 строки
6.7 KiB
Python
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
# Licensed under the Apache 2.0 License.
|
|
import infra.e2e_args
|
|
import infra.network
|
|
import infra.path
|
|
import infra.proc
|
|
import infra.utils
|
|
import suite.test_requirements as reqs
|
|
import os
|
|
from infra.checker import check_can_progress
|
|
|
|
|
|
from loguru import logger as LOG
|
|
|
|
|
|
@reqs.description("Verify node evidence")
|
|
def test_verify_quotes(network, args):
|
|
if args.enclave_type == "virtual":
|
|
LOG.warning("Skipping quote test with virtual enclave")
|
|
return network
|
|
|
|
LOG.info("Check the network is stable")
|
|
primary, _ = network.find_primary()
|
|
check_can_progress(primary)
|
|
|
|
for node in network.get_joined_nodes():
|
|
LOG.info(f"Verifying quote for node {node.node_id}")
|
|
cafile = os.path.join(network.common_dir, "service_cert.pem")
|
|
assert (
|
|
infra.proc.ccall(
|
|
"verify_quote.sh",
|
|
f"https://{node.get_public_rpc_host()}:{node.get_public_rpc_port()}",
|
|
"--cacert",
|
|
f"{cafile}",
|
|
log_output=True,
|
|
).returncode
|
|
== 0
|
|
), f"Quote verification for node {node.node_id} failed"
|
|
|
|
return network
|
|
|
|
|
|
@reqs.description("Node with bad code fails to join")
|
|
def test_add_node_with_bad_code(network, args):
|
|
if args.enclave_type == "virtual":
|
|
LOG.warning("Skipping test_add_node_with_bad_code with virtual enclave")
|
|
return network
|
|
|
|
replacement_package = (
|
|
"samples/apps/logging/liblogging"
|
|
if args.package == "libjs_generic"
|
|
else "libjs_generic"
|
|
)
|
|
|
|
new_code_id = infra.utils.get_code_id(
|
|
args.enclave_type, args.oe_binary, replacement_package
|
|
)
|
|
|
|
LOG.info(f"Adding a node with unsupported code id {new_code_id}")
|
|
code_not_found_exception = None
|
|
try:
|
|
new_node = network.create_node("local://localhost")
|
|
network.join_node(new_node, replacement_package, args, timeout=3)
|
|
except infra.network.CodeIdNotFound as err:
|
|
code_not_found_exception = err
|
|
|
|
assert (
|
|
code_not_found_exception is not None
|
|
), f"Adding a node with unsupported code id {new_code_id} should fail"
|
|
|
|
return network
|
|
|
|
|
|
def get_replacement_package(args):
|
|
return (
|
|
"samples/apps/logging/liblogging"
|
|
if args.package == "libjs_generic"
|
|
else "libjs_generic"
|
|
)
|
|
|
|
|
|
@reqs.description("Update all nodes code")
|
|
def test_update_all_nodes(network, args):
|
|
replacement_package = get_replacement_package(args)
|
|
|
|
primary, _ = network.find_nodes()
|
|
|
|
first_code_id = infra.utils.get_code_id(
|
|
args.enclave_type, args.oe_binary, args.package
|
|
)
|
|
new_code_id = infra.utils.get_code_id(
|
|
args.enclave_type, args.oe_binary, replacement_package
|
|
)
|
|
|
|
if args.enclave_type == "virtual":
|
|
# Pretend this was already present
|
|
network.consortium.add_new_code(primary, first_code_id)
|
|
|
|
LOG.info("Add new code id")
|
|
network.consortium.add_new_code(primary, new_code_id)
|
|
with primary.client() as uc:
|
|
r = uc.get("/node/code")
|
|
versions = sorted(r.body.json()["versions"], key=lambda x: x["digest"])
|
|
expected = sorted(
|
|
[
|
|
{"digest": first_code_id, "status": "AllowedToJoin"},
|
|
{"digest": new_code_id, "status": "AllowedToJoin"},
|
|
],
|
|
key=lambda x: x["digest"],
|
|
)
|
|
assert versions == expected, versions
|
|
|
|
LOG.info("Remove old code id")
|
|
network.consortium.retire_code(primary, first_code_id)
|
|
with primary.client() as uc:
|
|
r = uc.get("/node/code")
|
|
versions = sorted(r.body.json()["versions"], key=lambda x: x["digest"])
|
|
expected = sorted(
|
|
[
|
|
{"digest": new_code_id, "status": "AllowedToJoin"},
|
|
],
|
|
key=lambda x: x["digest"],
|
|
)
|
|
assert versions == expected, versions
|
|
|
|
old_nodes = network.nodes.copy()
|
|
|
|
LOG.info("Start fresh nodes running new code")
|
|
for _ in range(0, len(old_nodes)):
|
|
new_node = network.create_node("local://localhost")
|
|
network.join_node(new_node, replacement_package, args)
|
|
network.trust_node(new_node, args)
|
|
|
|
LOG.info("Retire original nodes running old code")
|
|
for node in old_nodes:
|
|
primary, _ = network.find_nodes()
|
|
network.retire_node(primary, node)
|
|
# Elections take (much) longer than a backup removal which is just
|
|
# a commit, so we need to adjust our timeout accordingly, hence this branch
|
|
if node.node_id == primary.node_id:
|
|
new_primary, _ = network.wait_for_new_primary(primary)
|
|
primary = new_primary
|
|
# See https://github.com/microsoft/CCF/issues/1713
|
|
check_can_progress(new_primary)
|
|
node.stop()
|
|
|
|
LOG.info("Check the network is still functional")
|
|
check_can_progress(new_node)
|
|
return network
|
|
|
|
|
|
@reqs.description("Adding a new code ID invalidates open proposals")
|
|
def test_proposal_invalidation(network, args):
|
|
primary, _ = network.find_nodes()
|
|
|
|
LOG.info("Create an open proposal")
|
|
pending_proposals = []
|
|
with primary.client(None, "member0") as c:
|
|
new_member_proposal, _, _ = network.consortium.generate_and_propose_new_member(
|
|
primary, curve=args.participants_curve
|
|
)
|
|
pending_proposals.append(new_member_proposal.proposal_id)
|
|
|
|
LOG.info("Add temporary code ID")
|
|
temp_code_id = infra.utils.get_code_id(
|
|
args.enclave_type, args.oe_binary, get_replacement_package(args)
|
|
)
|
|
network.consortium.add_new_code(primary, temp_code_id)
|
|
|
|
LOG.info("Confirm open proposals are dropped")
|
|
with primary.client(None, "member0") as c:
|
|
for proposal_id in pending_proposals:
|
|
r = c.get(f"/gov/proposals/{proposal_id}")
|
|
assert r.status_code == 200, r.body.text()
|
|
assert r.body.json()["state"] == "Dropped", r.body.json()
|
|
|
|
LOG.info("Remove temporary code ID")
|
|
network.consortium.retire_code(primary, temp_code_id)
|
|
|
|
return network
|
|
|
|
|
|
def run(args):
|
|
with infra.network.network(
|
|
args.nodes, args.binary_dir, args.debug_nodes, args.perf_nodes, pdb=args.pdb
|
|
) as network:
|
|
network.start_and_open(args)
|
|
|
|
test_verify_quotes(network, args)
|
|
test_add_node_with_bad_code(network, args)
|
|
# NB: Assumes the current nodes are still using args.package, so must run before test_proposal_invalidation
|
|
test_proposal_invalidation(network, args)
|
|
test_update_all_nodes(network, args)
|
|
|
|
# Run again at the end to confirm current nodes are acceptable
|
|
test_verify_quotes(network, args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
args = infra.e2e_args.cli_args()
|
|
|
|
args.package = "samples/apps/logging/liblogging"
|
|
args.nodes = infra.e2e_args.min_nodes(args, f=1)
|
|
run(args)
|