Skip to content

Commit 321e9cb

Browse files
authored
Add retire_node_code proposal (#1558)
1 parent 3d0d9bc commit 321e9cb

5 files changed

Lines changed: 82 additions & 0 deletions

File tree

python/ccf/proposal_generator.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,12 @@ def new_node_code(code_digest: str, **kwargs):
310310
return build_proposal("new_node_code", code_digest_bytes, **kwargs)
311311

312312

313+
@cli_proposal
314+
def retire_node_code(code_digest: str, **kwargs):
315+
code_digest_bytes = list(bytearray.fromhex(code_digest))
316+
return build_proposal("retire_node_code", code_digest_bytes, **kwargs)
317+
318+
313319
@cli_proposal
314320
def new_user_code(code_digest: str, **kwargs):
315321
code_digest_bytes = list(bytearray.fromhex(code_digest))

src/node/rpc/member_frontend.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,26 @@ namespace ccf
304304
return true;
305305
}
306306

307+
bool retire_code_id(
308+
kv::Tx& tx,
309+
const CodeDigest& code_id,
310+
CodeIDs& code_id_table,
311+
ObjectId proposal_id)
312+
{
313+
auto code_ids = tx.get_view(code_id_table);
314+
auto existing_code_id = code_ids->get(code_id);
315+
if (!existing_code_id)
316+
{
317+
LOG_FAIL_FMT(
318+
"Proposal {}: No such code id in table: {:02x}",
319+
proposal_id,
320+
fmt::join(code_id, ""));
321+
return false;
322+
}
323+
code_ids->put(code_id, CodeStatus::RETIRED);
324+
return true;
325+
}
326+
307327
//! Table of functions that proposal scripts can propose to invoke
308328
const std::unordered_map<
309329
std::string,
@@ -473,6 +493,15 @@ namespace ccf
473493
this->network.node_code_ids,
474494
proposal_id);
475495
}},
496+
// retire node code ID
497+
{"retire_node_code",
498+
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
499+
return this->retire_code_id(
500+
tx,
501+
args.get<CodeDigest>(),
502+
this->network.node_code_ids,
503+
proposal_id);
504+
}},
476505
// accept new user code ID
477506
{"new_user_code",
478507
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {

tests/code_update.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,41 @@ def run(args):
110110
assert new_node
111111
network.wait_for_node_commit_sync(args.consensus)
112112

113+
LOG.info("Remove first code id")
114+
network.consortium.retire_code(new_node, first_code_id)
115+
116+
with new_node.client() as uc:
117+
r = uc.get("/node/code")
118+
versions = sorted(r.body["versions"], key=lambda x: x["digest"])
119+
expected = sorted(
120+
[
121+
{"digest": first_code_id, "status": "RETIRED"},
122+
{"digest": new_code_id, "status": "ACCEPTED"},
123+
],
124+
key=lambda x: x["digest"],
125+
)
126+
assert versions == expected, versions
127+
128+
LOG.info(f"Adding a node with retired code id {first_code_id}")
129+
code_not_found_exception = None
130+
try:
131+
network.create_and_add_pending_node(
132+
args.package, "localhost", args, timeout=3
133+
)
134+
except infra.network.CodeIdRetired as err:
135+
code_not_found_exception = err
136+
137+
assert (
138+
code_not_found_exception is not None
139+
), f"Adding a node with unsupported code id {new_code_id} should fail"
140+
141+
LOG.info("Adding another node with the new code to the network")
142+
new_node = network.create_and_trust_node(
143+
args.patched_file_name, "localhost", args
144+
)
145+
assert new_node
146+
network.wait_for_node_commit_sync(args.consensus)
147+
113148

114149
if __name__ == "__main__":
115150

tests/infra/consortium.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,12 @@ def add_new_code(self, remote_node, new_code_id):
355355
proposal.vote_for = careful_vote
356356
return self.vote_using_majority(remote_node, proposal)
357357

358+
def retire_code(self, remote_node, code_id):
359+
proposal_body, careful_vote = self.make_proposal("retire_node_code", code_id)
360+
proposal = self.get_any_active_member().propose(remote_node, proposal_body)
361+
proposal.vote_for = careful_vote
362+
return self.vote_using_majority(remote_node, proposal)
363+
358364
def add_new_user_code(self, remote_node, new_code_id):
359365
proposal_body, careful_vote = self.make_proposal("new_user_code", new_code_id)
360366
proposal = self.get_any_active_member().propose(remote_node, proposal_body)

tests/infra/network.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ class CodeIdNotFound(Exception):
4747
pass
4848

4949

50+
class CodeIdRetired(Exception):
51+
pass
52+
53+
5054
class NodeShutdownError(Exception):
5155
pass
5256

@@ -473,6 +477,8 @@ def create_and_add_pending_node(
473477
for error in errors:
474478
if "CODE_ID_NOT_FOUND" in error:
475479
raise CodeIdNotFound from e
480+
if "CODE_ID_RETIRED" in error:
481+
raise CodeIdRetired from e
476482
raise
477483

478484
return new_node

0 commit comments

Comments
 (0)